Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bfccf7f22 | ||
| db4dffa7b3 |
46 changed files with 270 additions and 34875 deletions
|
|
@ -1,2 +1,2 @@
|
|||
stack-size-threshold = 1024
|
||||
doc-valid-idents = ["WiFi", "GeoNetworking", "QoS", ".."]
|
||||
doc-valid-idents = ["WiFi", ".."]
|
||||
|
|
|
|||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -24,7 +24,3 @@ target/
|
|||
|
||||
# Ignore .DS_Store file in mac
|
||||
**/.DS_Store
|
||||
|
||||
# Ignore KiCad backups and lock files
|
||||
hardware/c_its-companion/c_its-companion-backups/
|
||||
*.kicad_pro.lck
|
||||
|
|
|
|||
612
Cargo.lock
generated
612
Cargo.lock
generated
|
|
@ -17,92 +17,24 @@ version = "0.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c583acf993cf4245c4acb0a2cc2ab1f9cc097de73411bb6d3647ff6af2b1013d"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary-int"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "993a810118f8f37e9c4411c86f1c4c940a09a7ab34b7bf2d88d06f50c553fab7"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "assert_float_eq"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd9777fd8234cccd34019d9e26d70c8247f9baf24c8fdc0b56c548cf943af2ed"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "az"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
|
|
@ -137,9 +69,9 @@ checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
|
|||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.1.1"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
|
|
@ -166,29 +98,6 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bt-hci"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "211713d2e9fb4793ce4360a712c0764264aff6be48932ccf02ca2a331c0436a9"
|
||||
dependencies = [
|
||||
"btuuid",
|
||||
"embassy-sync 0.8.0",
|
||||
"embedded-io 0.7.1",
|
||||
"embedded-io-async 0.7.0",
|
||||
"futures-intrusive",
|
||||
"heapless 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "btuuid"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5f48f1e9b0aad0a4f05d17bdeae0fa20ff798e272a03a6940ca27ad9c5a6ae7"
|
||||
dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.20.2"
|
||||
|
|
@ -215,9 +124,8 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
|||
|
||||
[[package]]
|
||||
name = "c-its-parser"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e94d9990b20498f621f164f23ae198c4365115eb4191ae106352ec74d4a4110"
|
||||
version = "2.1.0"
|
||||
source = "git+https://github.com/consider-it/C-ITS-Parser.git?branch=feat%2Frasn-tweaks#f3208f5d2d1e14265809c652ce62d0668d1f3af6"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"etherparse",
|
||||
|
|
@ -255,27 +163,12 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "const-default"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cordyceps"
|
||||
version = "0.3.4"
|
||||
|
|
@ -624,21 +517,6 @@ dependencies = [
|
|||
"heapless 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embassy_gps"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/jbeyerstedt/embassy_gps.git?branch=feat%2Frework-gpsfix#75f1a67a69372234330c0c9618a953c415380b99"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"embassy-futures",
|
||||
"embassy-time",
|
||||
"embedded-io-async 0.7.0",
|
||||
"esp-hal",
|
||||
"heapless 0.9.2",
|
||||
"log",
|
||||
"nmea",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-can"
|
||||
version = "0.4.1"
|
||||
|
|
@ -648,29 +526,6 @@ dependencies = [
|
|||
"nb 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-graphics"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e8da660bb0c829b34a56a965490597f82a55e767b91f9543be80ce8ccb416fe"
|
||||
dependencies = [
|
||||
"az",
|
||||
"byteorder",
|
||||
"embedded-graphics-core",
|
||||
"float-cmp",
|
||||
"micromath",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-graphics-core"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95743bef3ff70fcba3930246c4e6872882bbea0dcc6da2ca860112e0cd4bd09f"
|
||||
dependencies = [
|
||||
"az",
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.7"
|
||||
|
|
@ -696,16 +551,6 @@ dependencies = [
|
|||
"embedded-hal 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal-bus"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "513e0b3a8fb7d3013a8ae17a834283f170deaf7d0eeab0a7c1a36ad4dd356d22"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"embedded-hal 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
|
|
@ -772,45 +617,12 @@ dependencies = [
|
|||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"jiff",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "esp-alloc"
|
||||
version = "0.10.0"
|
||||
|
|
@ -988,7 +800,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "23fbff98b06a96b6ce3791ecec5c668524052a068e23aacd23afe17ddba844ce"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"bt-hci",
|
||||
"cfg-if",
|
||||
"docsplay",
|
||||
"document-features",
|
||||
|
|
@ -1184,22 +995,13 @@ dependencies = [
|
|||
name = "esp32-c_its-companion"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"assert_float_eq",
|
||||
"bt-hci",
|
||||
"c-its-parser",
|
||||
"chrono",
|
||||
"critical-section",
|
||||
"crossbeam-queue",
|
||||
"embassy-executor",
|
||||
"embassy-futures",
|
||||
"embassy-sync 0.7.2",
|
||||
"embassy-time",
|
||||
"embassy_gps",
|
||||
"embedded-graphics",
|
||||
"embedded-hal-bus",
|
||||
"embedded-io 0.7.1",
|
||||
"embedded-io-async 0.7.0",
|
||||
"env_logger",
|
||||
"esp-alloc",
|
||||
"esp-backtrace",
|
||||
"esp-bootloader-esp-idf",
|
||||
|
|
@ -1207,29 +1009,7 @@ dependencies = [
|
|||
"esp-println",
|
||||
"esp-radio",
|
||||
"esp-rtos",
|
||||
"esp32-cits-core",
|
||||
"geo-types",
|
||||
"log",
|
||||
"mipidsi",
|
||||
"num-traits",
|
||||
"profont",
|
||||
"prost",
|
||||
"prost-build",
|
||||
"static_cell",
|
||||
"trouble-host",
|
||||
"ublox",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "esp32-cits-core"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.hamburg.ccc.de/jtbx/esp32-cits-core?branch=main#a6d0ea6ca2da130fde2b84060a34ef45e772a459"
|
||||
dependencies = [
|
||||
"c-its-parser",
|
||||
"chrono",
|
||||
"esp-hal",
|
||||
"esp-radio",
|
||||
"geo-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1297,40 +1077,19 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "etherparse"
|
||||
version = "0.21.0"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17304d06addb3283cdc4bd528e42dd95e73c8ee2d6492ffce415e93660885449"
|
||||
checksum = "3ac016aaf11dfe643edcd088a166234bfcb72e7f06691abfcf21e9af524037f4"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
|
@ -1358,52 +1117,12 @@ version = "2.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-intrusive"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.32"
|
||||
|
|
@ -1423,10 +1142,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1471,11 +1188,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "geonetworking"
|
||||
version = "0.4.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6da6fde770112587d5f5f7af439546e35bd35aa77a9e3e47e9968056783fe50d"
|
||||
checksum = "ea4f48e7b1cdf91ad77ec1e4c5617aee3666a929288f4a3f11e473b67d3a6314"
|
||||
dependencies = [
|
||||
"arbitrary-int",
|
||||
"bitvec",
|
||||
"bitvec-nom2",
|
||||
"bytes",
|
||||
"nom",
|
||||
"num",
|
||||
"num-traits",
|
||||
|
|
@ -1596,12 +1315,6 @@ dependencies = [
|
|||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
|
|
@ -1641,18 +1354,6 @@ dependencies = [
|
|||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-util",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
|
|
@ -1683,27 +1384,12 @@ version = "0.10.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
|
|
@ -1738,35 +1424,12 @@ version = "2.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "micromath"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "mipidsi"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790ebd28bd67addbccf41b1c0c188c26bb9f5bdcd91d4d6da9bd558e20d97a1d"
|
||||
dependencies = [
|
||||
"embedded-graphics-core",
|
||||
"embedded-hal 1.0.0",
|
||||
"heapless 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multimap"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "0.1.3"
|
||||
|
|
@ -1782,20 +1445,6 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||
|
||||
[[package]]
|
||||
name = "nmea"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2086c773d18da556c05ca235596d163c00379027189bafe2209ae40ebd19717c"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"heapless 0.8.0",
|
||||
"nom",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
|
|
@ -1913,29 +1562,12 @@ version = "1.21.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
|
|
@ -2006,66 +1638,6 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "profont"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "016681370a9dd6e7ddb4c1a959922fd59dc45e5ebaa5ff5b13090267898ced34"
|
||||
dependencies = [
|
||||
"embedded-graphics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-build"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"itertools",
|
||||
"log",
|
||||
"multimap",
|
||||
"petgraph",
|
||||
"prettyplease",
|
||||
"prost",
|
||||
"prost-types",
|
||||
"regex",
|
||||
"syn 2.0.117",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-derive"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a"
|
||||
dependencies = [
|
||||
"prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
|
|
@ -2108,8 +1680,7 @@ checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
|
|||
[[package]]
|
||||
name = "rasn"
|
||||
version = "0.28.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88235e585f2f3fc26ad809c79a84dd77e6a7203eb7d12e5bcaab89fc036d3d90"
|
||||
source = "git+https://github.com/consider-it/rasn.git?branch=feat%2Ffeature-flags#2f7833af4339cac3be1775c2cd262e735f5e8ad0"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"bitvec-nom2",
|
||||
|
|
@ -2123,7 +1694,6 @@ dependencies = [
|
|||
"num-traits",
|
||||
"once_cell",
|
||||
"rasn-derive",
|
||||
"serde_json",
|
||||
"snafu",
|
||||
"xml-no-std",
|
||||
]
|
||||
|
|
@ -2131,8 +1701,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "rasn-derive"
|
||||
version = "0.28.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11dcd4dab09ceadedb3e2bedf340deb583f0e25bba9ad966b5655c2fb62b1392"
|
||||
source = "git+https://github.com/consider-it/rasn.git?branch=feat%2Ffeature-flags#2f7833af4339cac3be1775c2cd262e735f5e8ad0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"rasn-derive-impl",
|
||||
|
|
@ -2142,8 +1711,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "rasn-derive-impl"
|
||||
version = "0.28.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f121c276d3f2fba8caabc870288de3f81abdb0bb94d5510651e58e1e00a7b9e7"
|
||||
source = "git+https://github.com/consider-it/rasn.git?branch=feat%2Ffeature-flags#2f7833af4339cac3be1775c2cd262e735f5e8ad0"
|
||||
dependencies = [
|
||||
"either",
|
||||
"itertools",
|
||||
|
|
@ -2153,18 +1721,6 @@ dependencies = [
|
|||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
|
|
@ -2178,9 +1734,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.11"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "riscv"
|
||||
|
|
@ -2254,19 +1810,6 @@ dependencies = [
|
|||
"svgbobdoc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
|
|
@ -2285,12 +1828,6 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[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.28"
|
||||
|
|
@ -2368,12 +1905,6 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
|
|
@ -2422,15 +1953,6 @@ version = "1.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "static_cell"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23"
|
||||
dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
|
|
@ -2499,18 +2021,6 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
|
|
@ -2620,68 +2130,12 @@ dependencies = [
|
|||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trouble-host"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1df7817cead4b83dfbeeaa59736ecbc97c30b21dc3bfc0cc2ce8a6687f70b37a"
|
||||
dependencies = [
|
||||
"bt-hci",
|
||||
"embassy-futures",
|
||||
"embassy-sync 0.7.2",
|
||||
"embassy-time",
|
||||
"embedded-io 0.7.1",
|
||||
"futures",
|
||||
"heapless 0.9.2",
|
||||
"rand_core 0.6.4",
|
||||
"static_cell",
|
||||
"trouble-host-macros",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trouble-host-macros"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d809ae05f510cf8f1d863749281eb28e049d9ae4ad52d92ea7a0ac068a0e17c"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"darling 0.20.11",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
|
||||
|
||||
[[package]]
|
||||
name = "ublox"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bcc96bd937066775fb87034e1dbf6d7165a8975c74ddec1105e31b4a050538b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"chrono",
|
||||
"num-traits",
|
||||
"ublox_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ublox_derive"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5eda7d1c1151a40ae85c7c065a19959909573535034e18e1015b738993cd2c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ufmt-write"
|
||||
version = "0.1.0"
|
||||
|
|
@ -2694,12 +2148,6 @@ version = "1.0.24"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
|
|
@ -2718,12 +2166,6 @@ version = "0.2.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.23.1"
|
||||
|
|
@ -2731,8 +2173,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3038,26 +2478,6 @@ dependencies = [
|
|||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bce33a6288fa3f072a8c2c7d0f2fdbb90e28298f0135c1f99b96c3db2efcc60b"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd425244944f4ab65ccff928e7323354c5a018c75838362fdce749dfad2ee1e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
|
|
|
|||
118
Cargo.toml
118
Cargo.toml
|
|
@ -3,134 +3,56 @@ edition = "2024"
|
|||
name = "esp32-c_its-companion"
|
||||
rust-version = "1.88"
|
||||
version = "0.1.0"
|
||||
default-run = "esp32-c_its-companion"
|
||||
|
||||
[[bin]]
|
||||
name = "esp32-c_its-companion"
|
||||
path = "./src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "test"
|
||||
path = "./src/test.rs"
|
||||
required-features = ["std", "cam"]
|
||||
|
||||
[features]
|
||||
default = ["esp", "spat", "gnss_ubx", "screen"]
|
||||
|
||||
# build for ESP (needed to build test for `host-tuple`)
|
||||
esp = [
|
||||
"dep:esp-hal",
|
||||
"dep:esp-rtos",
|
||||
"dep:esp-bootloader-esp-idf",
|
||||
"dep:embassy-executor",
|
||||
"dep:embassy-futures",
|
||||
"dep:embassy-sync",
|
||||
"dep:embassy-time",
|
||||
"dep:embedded-io",
|
||||
"dep:embedded-io-async",
|
||||
"dep:esp-alloc",
|
||||
"dep:esp-backtrace",
|
||||
"dep:esp-println",
|
||||
"dep:esp-radio",
|
||||
"dep:bt-hci",
|
||||
"esp32-cits-core/esp32c5",
|
||||
"esp32-cits-core/log-04",
|
||||
]
|
||||
std = ["c-its-parser/std", "esp32-cits-core/std"]
|
||||
|
||||
# enable u-blox GNSS module
|
||||
gnss_ubx = ["_gnss", "_uart", "dep:ublox"]
|
||||
# enable L67K GNSS module
|
||||
gnss_l67k = ["_gnss", "dep:embassy_gps"]
|
||||
# echo CAM data on serial
|
||||
cam = ["c-its-parser/cam"]
|
||||
# send CAM autonomously
|
||||
cam_tx = ["c-its-parser/cam"]
|
||||
# echo DENM events on serial and BLE
|
||||
denm = ["c-its-parser/denm"]
|
||||
# evaluate SPAT information
|
||||
spat = ["c-its-parser/spatem", "c-its-parser/mapem"]
|
||||
# enable some debug printing (without enabling debug logging of the ESP RTOS)
|
||||
spat_debug = []
|
||||
# enable ST7789 based display
|
||||
screen = ["dep:mipidsi", "dep:embedded-graphics", "dep:embedded-hal-bus", "dep:profont"]
|
||||
# enable I/O via UART
|
||||
uart = ["_uart", "_gnss"]
|
||||
# enable I/O via BLE
|
||||
ble = ["_gnss"]
|
||||
|
||||
# internal feature, that any UART I/O is enabled
|
||||
_uart = []
|
||||
# internal feature, that any GNSS source is enabled
|
||||
_gnss = []
|
||||
|
||||
[dependencies]
|
||||
esp-hal = { version = "~1.1.0", optional = true, features = ["esp32c5", "log-04", "unstable"] }
|
||||
esp-hal = { version = "~1.1.0", features = ["esp32c5", "log-04", "unstable"] }
|
||||
|
||||
esp-rtos = { version = "0.3.0", optional = true, features = [
|
||||
esp-rtos = { version = "0.3.0", features = [
|
||||
"embassy",
|
||||
"esp-alloc",
|
||||
"esp-radio",
|
||||
"embassy",
|
||||
"esp32c5",
|
||||
"log-04",
|
||||
] }
|
||||
|
||||
esp-bootloader-esp-idf = { version = "0.5.0", optional = true, features = ["esp32c5", "log-04"] }
|
||||
esp-bootloader-esp-idf = { version = "0.5.0", features = ["esp32c5", "log-04"] }
|
||||
log = "0.4.27"
|
||||
|
||||
critical-section = "1.2.0"
|
||||
bt-hci = { version = "0.8.0", optional = true }
|
||||
embassy-executor = { version = "0.10.0", optional = true, features = ["log"] }
|
||||
embassy-futures = { version = "0.1.2", optional = true }
|
||||
embassy-sync = { version = "0.7", optional = true }
|
||||
embassy-time = { version = "0.5.0", optional = true, features = ["log"] }
|
||||
embedded-io = { version = "0.7.1", optional = true }
|
||||
embedded-io-async = { version = "0.7.0", optional = true }
|
||||
esp-alloc = { version = "0.10.0", optional = true }
|
||||
esp-backtrace = { version = "0.19.0", optional = true, features = [
|
||||
embassy-executor = { version = "0.10.0", features = ["log"] }
|
||||
embassy-time = { version = "0.5.0", features = ["log"] }
|
||||
embedded-io = "0.7.1"
|
||||
embedded-io-async = "0.7.0"
|
||||
esp-alloc = "0.10.0"
|
||||
esp-backtrace = { version = "0.19.0", features = [
|
||||
"esp32c5",
|
||||
"panic-handler",
|
||||
"println",
|
||||
] }
|
||||
esp-println = { version = "0.17.0", optional = true, default-features = false, features = ["colors", "critical-section", "jtag-serial", "esp32c5", "log-04"] }
|
||||
esp-radio = { version = "0.18.0", optional = true, features = [
|
||||
esp-println = { version = "0.17.0", features = ["esp32c5", "log-04"] }
|
||||
esp-radio = { version = "0.18.0", features = [
|
||||
"esp-alloc",
|
||||
"esp32c5",
|
||||
"log-04",
|
||||
"unstable",
|
||||
"wifi",
|
||||
"sniffer",
|
||||
"ble",
|
||||
] }
|
||||
trouble-host = { version = "0.6.0", features = ["gatt", "central", "derive", "default-packet-pool"] }
|
||||
static_cell = "2"
|
||||
|
||||
embassy_gps = { version = "0.1.0", optional = true, default-features = false, features = ["esp", "log-04"], git = "https://github.com/jbeyerstedt/embassy_gps.git", branch = "feat/rework-gpsfix" }
|
||||
esp32-cits-core = { version = "0.1.0", default-features = false, git = "https://git.hamburg.ccc.de/jtbx/esp32-cits-core", branch = "main" }
|
||||
embedded-hal-bus = { version = "0.3.0", optional = true }
|
||||
embedded-graphics = { version = "0.8.2", optional = true }
|
||||
# needs newer GN with removed bitvec dependency
|
||||
c-its-parser = { version = "2.4.0", default-features = false, features = [
|
||||
c-its-parser = { version = "2.1.0", git = "https://github.com/consider-it/C-ITS-Parser.git", branch = "feat/rasn-tweaks", default-features = false, features = [
|
||||
"time",
|
||||
"geo",
|
||||
"libm",
|
||||
"transport",
|
||||
"mapem",
|
||||
"spatem",
|
||||
"cam", # TODO: for initial setup only
|
||||
] }
|
||||
chrono = { version = "0.4.44", default-features = false, features = ["alloc"] }
|
||||
crossbeam-queue = { version = "0.3.12", default-features = false, features = ["alloc"] }
|
||||
geo-types = { version = "0.7.19", default-features = false }
|
||||
mipidsi = { version = "0.10.0", optional = true }
|
||||
num-traits = { version = "0.2.19", default-features = false, features = ["libm"] }
|
||||
profont = { version = "0.7.0", optional = true }
|
||||
prost = { version = "0.14", default-features = false, features = ["derive"] }
|
||||
ublox = { version = "0.10", optional = true, default-features = false, features = ["alloc", "ubx_proto27"] }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_float_eq = "1.2.0"
|
||||
env_logger = "0.11.10"
|
||||
|
||||
[build-dependencies]
|
||||
prost-build = "0.14"
|
||||
|
||||
|
||||
# For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html
|
||||
|
|
@ -146,6 +68,14 @@ lto = 'fat'
|
|||
opt-level = 's'
|
||||
strip = "debuginfo"
|
||||
|
||||
## cargo build --release && espflash save-image --chip esp32c5 target/riscv32imac-unknown-none-elf/release/esp32-c_its-companion out.bin
|
||||
# default rasn, debug 2, strip default: 1,904,496 byte
|
||||
# default rasn, debug 2, strip true: 1,904,592 byte
|
||||
# default rasn, debug default, strip default: 1,905,312 byte
|
||||
# only UPER, debug 2, strip default: 1,229,840 byte
|
||||
# only UPER, debug 2, strip true: 1,229,760 byte
|
||||
# only UPER, debug default, strip default: 1,231,136 byte
|
||||
|
||||
[profile.release-dbg]
|
||||
inherits = "release"
|
||||
strip = false
|
||||
|
|
|
|||
94
Readme.md
94
Readme.md
|
|
@ -1,68 +1,7 @@
|
|||
# ESP32-C5 C-ITS Companion/ Visualizer
|
||||
# ESP32-C5 C-ITS Visualizer
|
||||
|
||||
Displays different C-ITS receive-only use cases for cheap.
|
||||
|
||||
## Hardware Requirements
|
||||
The code is currently tailored to:
|
||||
- Seeed XIAO ESP32-C5 (8MB PSRAM)
|
||||
- U-Blox M10Q GNSS (connected on GPIO12 (RX), GPIO11 (TX), and GPIO25 (reset))
|
||||
or Seeed XIAO L67K (connected on GPIO12 (RX), GPIO11 (TX), GPIO1 (wakeup) and GPIO25 (reset))
|
||||
only available with `gnss_ubx` or `gnss_l67k` feature (which is included in `spat` feature)
|
||||
- Some ST7789 240*280px screen (connected on GPIO8 (SCL/ SCK), GPIO10 (SDA/ MOSI), GPIO7 (reset), GPIO23 (DC), GPIO24 (CS), GPIO0 (BL))
|
||||
only available with `screen` feature (which is included in `spat` feature)
|
||||
|
||||
## Features
|
||||
|
||||
### GNSS
|
||||
Enable u-blox or L67K GNSS module and in main application logic/ state.
|
||||
|
||||
### SPAT
|
||||
Takes the current position and heading from the GNSS receiver and displays the signal phases of the upcoming intersection.
|
||||
The upcoming intersection is selected when the own position is "inside" some ingress approach of the intersection.
|
||||
The display will show the current phase and remaining time for all signal groups of the bike-able lanes in the approach.
|
||||
|
||||
### DENM
|
||||
TODO: Not implemented yet!
|
||||
|
||||
Shall "publish" the most relevant information of a DENMs via BLE
|
||||
|
||||
### CAM
|
||||
Just prints a line for each received CAM.
|
||||
|
||||
### CAM-TX
|
||||
Generates and transmits a CAM when a GNSS update was received.
|
||||
Depends on the `gnss` feature.
|
||||
|
||||
Note: In germany, using the 5875 to 5915 MHz frequency band is only permitted for C-ITS use cases which implement transmit power reduction measures with at least 30 dB reduction range (or equivalent measures) and have a maximum EIRP of 33 dBm.
|
||||
This is based on EU regulation 2014/53/EU, so it applies to other EU countries in similar ways.
|
||||
The relevant harmonized standard is ETSI EN 302 571 which mandates (excerpt of most relevant points):
|
||||
|
||||
- a maximum power spectral density of 23 dBm/Mhz EIRP
|
||||
- transmit power control (TCP) in a range from 3 dBm up to the maximum output power of the equipment
|
||||
* used for CEN DSRC and HDR DSRC interference mitigation by implementing measures from ETSI TS 102 792
|
||||
+ Mode A or B: Output power less than 10 dBm EIRP and less than -65 or -45 dBm/MHz EIRP in CEN DSRC band -> **needs to be measured**
|
||||
+ Mode C: Maximum transmission time of less than 1 ms and at least a few 100 ms between transmission (realistically) -> **can't be ensured with big messages**
|
||||
+ Mode D: "any" realistically possible transmission time, but at least 500 ms between transmissions for ~5 ITS stations/ 1000ms for 10 ITS stations -> **safe to assume when we only send CAMs at 1 Hz**
|
||||
+ But modes C and D also need to:
|
||||
- use protected zone information from CAM messages
|
||||
- use a protected zone database, e.g. from www.asecap-pzdb.com
|
||||
* used for dynamic congestion control (DCC)
|
||||
+ mandatory mechanism described in ETSI EN 302 571, chapter 4.2.10
|
||||
|
||||
This is **not** implemented in this application!
|
||||
**So legally, you're not allowed to send with this.**
|
||||
|
||||
### UART
|
||||
Enable UART I/O protocol according to [docs/uart-protocol.md](./docs/uart-protocol.md).
|
||||
|
||||
### BLE
|
||||
Enable BLE I/O protocol according to [docs/ble-protocol.md](./docs/ble-protocol.md).
|
||||
Enabling `denm` feature or `cam` feature is recommended, otherwise no data will be received.
|
||||
|
||||
Note: When changing the set of supported messages on the BLE interface, you may need to turn bluetooth off and on again on your client device.
|
||||
Otherwise the client may not re-scan the service's characteristics and still use the previous set of characteristics.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### Toolchain Installation
|
||||
|
|
@ -77,7 +16,7 @@ rustup target add riscv32imac-unknown-none-elf
|
|||
cargo install espflash --locked
|
||||
```
|
||||
|
||||
(Project was setup using esp-generate with the options `--chip esp32c5 -o esp32c5-wroom-1-psram -o alloc -o log -o unstable-hal -o wifi -o esp-backtrace -o embassy -o ble-trouble`.)
|
||||
(Project was setup using esp-generate with the options `--chip esp32c5 -o esp32c5-wroom-1-psram -o alloc -o log -o unstable-hal -o wifi -o esp-backtrace -o embassy`.)
|
||||
|
||||
To use the proper rustfmt features, enable the nightly toolchain:
|
||||
```shell
|
||||
|
|
@ -85,37 +24,8 @@ rustup component add --toolchain nightly rustfmt
|
|||
```
|
||||
|
||||
### Build and Flash
|
||||
In default configuration only signal phases (SPAT) data is displayed for the upcoming intersection.
|
||||
Additional features are available through feature flags:
|
||||
|
||||
- `esp`: Run on ESP (required as feature flag to enable unit tests on host)
|
||||
- `std`: Run on system with `std` library
|
||||
- `spat`: Enable signal phase (and intersection map) data
|
||||
- `spat_debug`: Enable additional debug information for the SPAT/ GLOSA use case
|
||||
- `denm`: Enable DENMs
|
||||
- `cam`: Enable CAMs
|
||||
- `cam_tx`: Enable CAM generation and transmission (needs time and position source)
|
||||
|
||||
Officially supported feature combinations are:
|
||||
- `esp,spat,gnss_ubx,screen` (default features, "standalone")
|
||||
- `esp,spat,gnss_ubx,screen,cam_tx` ("standalone with CAM")
|
||||
- `esp,spat,gnss_ubx,screen,cam_tx,cam,denm`
|
||||
- `esp,uart,cam,denm` ("smartphone companion with UART")
|
||||
- `esp,ble,cam,denm,cam_tx` ("smartphone companion with BLE")
|
||||
|
||||
Connect your ESP32-C5 and run:
|
||||
|
||||
```
|
||||
# default configuration
|
||||
cargo run --release
|
||||
|
||||
# for e.g. also enable DENM feature
|
||||
cargo run --release -F denm
|
||||
```
|
||||
|
||||
### Run Unit Tests on Host
|
||||
The unit tests in this project are meant to be run on the host:
|
||||
|
||||
```
|
||||
cargo test --target host-tuple --bin test --no-default-features -F std,spat
|
||||
```
|
||||
|
|
|
|||
24
build.rs
24
build.rs
|
|
@ -1,23 +1,7 @@
|
|||
use std::env;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
|
||||
if target_arch == "riscv32" {
|
||||
linker_be_nice();
|
||||
// make sure linkall.x is the last linker script (otherwise might cause problems with flip-link)
|
||||
println!("cargo:rustc-link-arg=-Tlinkall.x");
|
||||
}
|
||||
|
||||
let descriptor_path =
|
||||
std::path::PathBuf::from(env::var("OUT_DIR").unwrap()).join("proto_descriptor.bin");
|
||||
|
||||
prost_build::Config::new()
|
||||
.file_descriptor_set_path(&descriptor_path)
|
||||
.compile_well_known_types()
|
||||
.compile_protos(&["docs/c_its-messages.proto"], &["docs/"])?;
|
||||
|
||||
Ok(())
|
||||
fn main() {
|
||||
linker_be_nice();
|
||||
// make sure linkall.x is the last linker script (otherwise might cause problems with flip-link)
|
||||
println!("cargo:rustc-link-arg=-Tlinkall.x");
|
||||
}
|
||||
|
||||
fn linker_be_nice() {
|
||||
|
|
|
|||
|
|
@ -1,124 +0,0 @@
|
|||
# Bluetooth LE Protocol
|
||||
|
||||
Some data is made available via BLE so that a smartphone app can be used to display it in other use cases.
|
||||
|
||||
## General
|
||||
|
||||
The BLE protocol contains some standardized services, but can also transport custom services.
|
||||
Each service is compromised of a set of characteristics which can be read or clients can even subscribe for changes.
|
||||
|
||||
Sadly we can't use any of the standardized service/ profiles to transmit V2X data.
|
||||
Even notifications about DENMs can't be transmitted since the Alert Notification Profile is only meant for the companion device to query notifications from the phone, but not the other way around.
|
||||
|
||||
So we will only use the Device Information Service to announce ourselves and use a custom service for everything else.
|
||||
|
||||
## V2X BLE Service
|
||||
|
||||
V2X data is exposed via the service UUID `c0b70000-d4f4-4000-ada8-f99a02ee315c` (randomly generated UUID, with byte 3 and 4 set to zero) using the following characteristics:
|
||||
|
||||
UUID | Type | Description
|
||||
------------------------------------- | --------------- | -----------
|
||||
c0b70001-d4f4-4001-ada8-f99a02ee315c | `PositionState` | Set position from external source
|
||||
c0b70001-d4f4-4002-ada8-f99a02ee315c | `RawNotify` | Raw message notification
|
||||
c0b70001-d4f4-4003-ada8-f99a02ee315c | `RawMsgRx` | Raw CAM message (de-duplicated/ rate-limited)
|
||||
c0b70001-d4f4-4004-ada8-f99a02ee315c | `CamEvent` | Latest received CAM
|
||||
c0b70001-d4f4-4005-ada8-f99a02ee315c | `RawMsgRx` | Raw DENM message (de-duplicated/ rate-limited)
|
||||
c0b70001-d4f4-4006-ada8-f99a02ee315c | `DenmEvent` | Latest received DENM
|
||||
c0b70001-d4f4-4007-ada8-f99a02ee315c | `RawMsgRx` | Raw MAPEM message (de-duplicated/ rate-limited)
|
||||
c0b70001-d4f4-4008-ada8-f99a02ee315c | `RawMsgRx` | Raw SPATEM message (de-duplicated/ rate-limited)
|
||||
|
||||
Since a modern BLE notification can only fit up tp 251 bytes of payload, the raw messages can only be polled.
|
||||
Please subscribe to `c0b70001-d4f4-4002-ada8-f99a02ee315c` and use the message ID to determine from which service to read the incoming value.
|
||||
|
||||
## Message Format
|
||||
|
||||
Messages are encoded according to the [Protocol Buffers](https://protobuf.dev/) specification since it provides decent binary size and has parser libraries for many programming languages.
|
||||
All data types are defined in one `.proto` file: [c_its-messages.proto](./c_its-messages.proto).
|
||||
|
||||
### Position State Input
|
||||
|
||||
When there's no GNSS receiver connected to the ESP, position, heading and speed information can be set using a `PositionState` message (see [c_its-messages.proto](./c_its-messages.proto)).
|
||||
|
||||
Maximum message size (assuming realistic value ranges):
|
||||
|
||||
field name | tag size | (max.) data size
|
||||
----------------- | -------- | ---------
|
||||
timestamp_ms | 1 | 8 (I64)
|
||||
position | 1 | 1 (LEN(10)) + sub-fields below
|
||||
\+ latitude_deg | 1 | 4 (I32)
|
||||
\+ longitude_deg | 1 | 4 (I32)
|
||||
heading | 1 | 2 (VARINT(3600))
|
||||
speed | 1 | 2 (VARINT(~163 m/s))
|
||||
**SUM** | 6 + | 21 = 27 byte
|
||||
|
||||
|
||||
### DENM Event
|
||||
|
||||
Each received DENM will be "published" as a `DenmEvent` message (see [c_its-messages.proto](./c_its-messages.proto)).
|
||||
Repetitions of the same message (e.g. no changes to the validity timeframe and location) may be dropped by the BLE server.
|
||||
|
||||
Maximum message size (assuming realistic value ranges):
|
||||
|
||||
field name | tag size | (max.) data size
|
||||
----------------- | -------- | ---------
|
||||
timestamp | 1 | 8 (I64)
|
||||
validity_end_ts | 1 | 8 (I64)
|
||||
station_id | 1 | 8 (I64)
|
||||
seq_num | 1 | 3 (VARINT(65535))
|
||||
event_position | 1 | 1 (LEN(10)) + sub-fields below
|
||||
\+ latitude_deg | 1 | 4 (I32)
|
||||
\+ longitude_deg | 1 | 4 (I32)
|
||||
event_heading_deg | 1 | 1 (VARINT(360))
|
||||
direction | 1 | 1 (VARINT(3))
|
||||
cause_code | 1 | 1 (VARINT(127))
|
||||
sub_cause_code | 1 | 1 (VARINT(127))
|
||||
**SUM** | 11 + | 40 = 51 byte
|
||||
|
||||
Note: Cause code and sub-cause code are technically 0..255 in the ASN.1 spec, but that's mainly to ensure backwards-compatibility of the CHOICE with the old wire format.
|
||||
Realistically only cause codes up to 100 are defined and common sub-cause codes use values up to 22.
|
||||
|
||||
### CAM Event
|
||||
|
||||
Each received (low-frequency) CAM will be "published" as a `CamEvent` message (see [c_its-messages.proto](./c_its-messages.proto)).
|
||||
|
||||
Maximum message size (assuming realistic value ranges):
|
||||
|
||||
field name | tag size | (max.) data size
|
||||
--------------------- | -------- | ---------
|
||||
station_id | 1 | 8 (I64)
|
||||
station_type | 1 | 1 (VARINT(15))
|
||||
position | 1 | 1 (LEN(10)) + sub-fields below
|
||||
\+ latitude_deg | 1 | 4 (I32)
|
||||
\+ longitude_deg | 1 | 4 (I32)
|
||||
vehicle_role | 1 | 1 (VARINT(15))
|
||||
vehicle_data | 1 | 1 (LEN(11)) + sub-fields below
|
||||
\+ heading_deg | 1 | 2 (VARINT(360))
|
||||
\+ speed | 1 | 2 (VARINT(~163 m/s))
|
||||
\+ vehicle_length_dm | 1 | 2 (VARINT(1023))
|
||||
\+ vehicle_width_dm | 1 | 1 (VARINT(62))
|
||||
**SUM** | 11 + | 27 = 38 byte
|
||||
|
||||
### Raw Received Message
|
||||
|
||||
Received V2X messages will be "published" as a `RawMsgRx` message (see [c_its-messages.proto](./c_its-messages.proto)).
|
||||
Repetitions of the same message should be dropped by the BLE server.
|
||||
Consecutive messages with no relevant changes may be dropped by the BLE server while keeping some minimal publishing rate, e.g.:
|
||||
- only publish MAPEMs every 10 seconds (since content doesn't change, but client may be restarted after first MAPEM from an intersection was received)
|
||||
- drop high-frequency CAMs (only publish "full" CAMs which include the low-frequency container)
|
||||
|
||||
Maximum message size (assuming realistic value ranges):
|
||||
|
||||
field name | tag size | (max.) data size
|
||||
--------------------- | -------- | ---------
|
||||
msg_type | 1 | 1 (VARINT(21))
|
||||
payload | 1 | 2+1394 (LEN(1394) + payload)
|
||||
**SUM** | 2 + | 1397 = 1399 byte
|
||||
|
||||
### Raw Message Notification
|
||||
|
||||
Maximum message size (assuming realistic value ranges):
|
||||
|
||||
field name | tag size | (max.) data size
|
||||
--------------------- | -------- | ---------
|
||||
msg_type | 1 | 1 (VARINT(21))
|
||||
**SUM** | 1 + | 1 = 2 byte
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
syntax = "proto2";
|
||||
|
||||
package c_its_io;
|
||||
|
||||
// ------------------------------
|
||||
// Data Types
|
||||
// ------------------------------
|
||||
|
||||
message Position {
|
||||
required float latitude_deg = 1;
|
||||
required float longitude_deg = 2;
|
||||
}
|
||||
|
||||
enum TrafficDir {
|
||||
// for all directions of traffic
|
||||
ALL_DIRECTIONS = 0;
|
||||
// for the direction of traffic according to the reference direction, and the portion of traffic upstream of the reference position
|
||||
UPSTREAM = 1;
|
||||
// for the direction of traffic according to the reference direction, and the portion of traffic downstream of the reference position
|
||||
DOWNSTREAM = 2;
|
||||
// for the direction of traffic opposite to the reference direction
|
||||
OPPOSITE = 3;
|
||||
}
|
||||
|
||||
message VehicleContainer {
|
||||
optional uint32 heading_deg = 1; // vehicle heading in degrees, if not "unavailable"
|
||||
optional uint32 speed = 2; // vehicle speed in 1/100 m/s, if not "unavailable"
|
||||
optional uint32 vehicle_length_dm = 3; // vehicle length in 1/10 m, if not "unavailable"
|
||||
optional uint32 vehicle_width_dm = 4; // vehicle width in 1/10 m, if not "unavailable"
|
||||
}
|
||||
|
||||
|
||||
// GeoNetworking Destination Area Shape
|
||||
enum GNAreaShape {
|
||||
ETSI_AREASHAPE_CIRCLE = 0;
|
||||
ETSI_AREASHAPE_RECTANGLE = 1;
|
||||
ETSI_AREASHAPE_ELLIPSIS = 2;
|
||||
}
|
||||
|
||||
// GeoNetworking Destination Area/ Source Position
|
||||
message ITSPosition {
|
||||
required int32 latitude = 1; // Latitude in 1/10th of micro degrees
|
||||
required int32 longitude = 2; // Longitude in 1/10th of micro degrees
|
||||
}
|
||||
|
||||
// GeoNetworking Destination Area
|
||||
message GNArea {
|
||||
optional GNAreaShape area = 1;
|
||||
optional ITSPosition position = 2;
|
||||
optional int32 distA = 3; // Distance A of the geometric shape in meters
|
||||
optional int32 distB = 4; // Distance B of the geometric shape in meters, mandatory for rectangle and ellipsis
|
||||
optional int32 angle = 5; // Angle of the geometric shape in degrees, mandatory for rectangle and ellipsis
|
||||
}
|
||||
|
||||
// GeoNetworking Transport
|
||||
enum GNTransport {
|
||||
GN_TRANSPORT_GAC = 3; // Geographically-Scoped Anycast
|
||||
GN_TRANSPORT_GBC = 4; // Geographically-Scoped broadcast
|
||||
GN_TRANSPORT_TSB = 5; // Topologically-scoped broadcast
|
||||
GN_TRANSPORT_SHB = 7; // Single hop broadcast
|
||||
}
|
||||
|
||||
message RawMsgTx {
|
||||
required bytes payload = 1; // as UPER
|
||||
required uint32 message_type = 2; // ITS `MessageId` integer value
|
||||
|
||||
required GNTransport gnTransport = 3;
|
||||
required GNArea gnArea = 4;
|
||||
required uint32 hopLimit = 5;
|
||||
|
||||
optional uint32 stationType = 6; // for source position vector
|
||||
}
|
||||
|
||||
message RawNotify {
|
||||
required uint32 message_type = 1; // ITS `MessageId` integer value
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Messages
|
||||
// ------------------------------
|
||||
|
||||
message PositionState {
|
||||
required sfixed64 timestamp_ms = 1; // current UNIX time in milliseconds
|
||||
required Position position = 2;
|
||||
optional uint32 heading = 3; // heading in 1/10 deg
|
||||
optional uint32 speed = 4; // speed in 1/100 m/s
|
||||
}
|
||||
|
||||
message DenmEvent {
|
||||
required fixed64 timestamp = 1; // DENM reference time as UNIX time (in seconds)
|
||||
required fixed64 validity_end_ts = 2; // UNIX time when event validity ends (from denm.management.validityDuration)
|
||||
required fixed32 station_id = 3; // unique ID of the event when combined with `seq_num`
|
||||
required uint32 seq_num = 4; // unique ID of the event when combined with `station_id`
|
||||
optional Position event_position = 5; // event location (from denm.management.eventPosition)
|
||||
optional uint32 event_heading_deg = 6; // event heading (from denm.location.eventPositionHeading)
|
||||
optional TrafficDir direction = 7; // relevance traffic direction (from denm.management.trafficDirection)
|
||||
optional uint32 cause_code = 8;
|
||||
optional uint32 sub_cause_code = 9;
|
||||
}
|
||||
|
||||
message CamEvent {
|
||||
required fixed32 station_id = 1; // Station ID of the vehicle
|
||||
required uint32 station_type = 2; // ITS `StationType`
|
||||
required Position position = 3; // vehicle position
|
||||
optional uint32 vehicle_role = 4; // ITS `VehicleRole`
|
||||
optional VehicleContainer vehicle_data = 5; // speed, etc unless CAM contains an RSU container
|
||||
}
|
||||
|
||||
message RawMsgRx {
|
||||
required uint32 msg_type = 1; // ITS message type (`MessageId` from PDU header)
|
||||
required bytes payload = 2; // UPER message with Geonetworking and BTP headers
|
||||
}
|
||||
|
||||
message SerialOutputMsg {
|
||||
oneof payload {
|
||||
DenmEvent denm = 1;
|
||||
RawMsgRx rx_msg = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message SerialInputMsg {
|
||||
oneof payload {
|
||||
PositionState position = 1;
|
||||
RawMsgTx tx_msg = 2;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# UART Protocol
|
||||
|
||||
If enabled using the `TODO` feature flag, all received messages are transmitted on UART using the protocol described in this document.
|
||||
The serial communication can also be used to send V2X data.
|
||||
|
||||
## Message Format
|
||||
|
||||
The UART communication is using a custom framing protocol to encapsulate binary messages.
|
||||
The payload is:
|
||||
|
||||
- a `SerialOutputMsg` for transmissions from the ESP to the host or
|
||||
- a `SerialInputMsg` for transmissions from the host to the ESP
|
||||
|
||||
in binary [Proto](https://protobuf.dev/) serialization according to the [c_its-messages.proto](./c_its-messages.proto) definition.
|
||||
|
||||
Each transmission:
|
||||
- starts with a Start of Frame (SOF) sequence: `0x56, 0x32, 0x58, 0x2B` ("V2X+")
|
||||
- followed by a 16-bit big-endian length field
|
||||
- followed by the payload
|
||||
- followed by an End of Frame (EOF) sequence `0x0d, 0x0a`
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
#!/bin/bash
|
||||
## Build/ Test/ Clippy all supported feature permutations
|
||||
set -eu
|
||||
|
||||
mode=${1:-help}
|
||||
wasm_target="--chrome --headless"
|
||||
extended=${2:-false}
|
||||
|
||||
|
||||
features_base=(
|
||||
uart,cam,denm
|
||||
ble,cam,denm,cam_tx
|
||||
uart,ble,cam,denm
|
||||
spat,gnss_ubx,screen
|
||||
spat,gnss_ubx,screen,cam_tx
|
||||
)
|
||||
features_more=(
|
||||
spat,gnss_ubx,screen,ble
|
||||
spat,gnss_ubx,screen,ble,cam_tx
|
||||
spat,gnss_ubx,screen,cam,denm
|
||||
spat,gnss_ubx,screen,cam_tx,cam,denm
|
||||
)
|
||||
features_test=(
|
||||
spat,cam,denm,uart
|
||||
)
|
||||
|
||||
|
||||
runBuild () {
|
||||
for feat in "${features[@]}"; do
|
||||
echo "cargo build for $feat ..."
|
||||
cargo build --release --no-default-features -F esp,$feat
|
||||
done
|
||||
}
|
||||
|
||||
runTest () {
|
||||
for feat in "${features_test[@]}"; do
|
||||
echo "cargo test for $feat ..."
|
||||
cargo test --target host-tuple --bin test --no-default-features -F std,$feat
|
||||
done
|
||||
}
|
||||
|
||||
runClippy () {
|
||||
for feat in "${features[@]}"; do
|
||||
echo "clippy for $feat ..."
|
||||
cargo clippy --no-default-features -F esp,$feat -- -Wclippy::pedantic
|
||||
done
|
||||
}
|
||||
|
||||
if [ $extended == "--more" ]; then
|
||||
echo "running with extended feature list"
|
||||
features=( "${features_base[@]}" "${features_more[@]}" )
|
||||
else
|
||||
echo "running standard feature list"
|
||||
features=( "${features_base[@]}" )
|
||||
fi
|
||||
|
||||
case $mode in
|
||||
build)
|
||||
runBuild
|
||||
;;
|
||||
clippy)
|
||||
runClippy
|
||||
;;
|
||||
test)
|
||||
runTest
|
||||
;;
|
||||
all)
|
||||
runBuild
|
||||
runClippy
|
||||
runTest
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [build | clippy | test | all] [--more]"
|
||||
esac
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 145 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 142 KiB |
File diff suppressed because it is too large
Load diff
|
|
@ -1,138 +0,0 @@
|
|||
{
|
||||
"board": {
|
||||
"active_layer": 0,
|
||||
"active_layer_preset": "",
|
||||
"auto_track_width": true,
|
||||
"hidden_netclasses": [],
|
||||
"hidden_nets": [],
|
||||
"high_contrast_mode": 0,
|
||||
"net_color_mode": 1,
|
||||
"opacity": {
|
||||
"images": 0.6,
|
||||
"pads": 1.0,
|
||||
"shapes": 1.0,
|
||||
"tracks": 1.0,
|
||||
"vias": 1.0,
|
||||
"zones": 0.6
|
||||
},
|
||||
"prototype_zone_fills": false,
|
||||
"selection_filter": {
|
||||
"dimensions": true,
|
||||
"footprints": true,
|
||||
"graphics": true,
|
||||
"keepouts": true,
|
||||
"lockedItems": false,
|
||||
"otherItems": true,
|
||||
"pads": true,
|
||||
"text": true,
|
||||
"tracks": true,
|
||||
"vias": true,
|
||||
"zones": true
|
||||
},
|
||||
"visible_items": [
|
||||
"vias",
|
||||
"footprint_text",
|
||||
"footprint_anchors",
|
||||
"ratsnest",
|
||||
"grid",
|
||||
"footprints_front",
|
||||
"footprints_back",
|
||||
"footprint_values",
|
||||
"footprint_references",
|
||||
"tracks",
|
||||
"drc_errors",
|
||||
"drawing_sheet",
|
||||
"bitmaps",
|
||||
"pads",
|
||||
"zones",
|
||||
"drc_warnings",
|
||||
"drc_exclusions",
|
||||
"locked_item_shadows",
|
||||
"conflict_shadows",
|
||||
"shapes",
|
||||
"board_outline_area",
|
||||
"ly_points"
|
||||
],
|
||||
"visible_layers": "ffffffff_ffffffff_ffffffff_ffffffff",
|
||||
"zone_display_mode": 0
|
||||
},
|
||||
"git": {
|
||||
"integration_disabled": false,
|
||||
"repo_type": "",
|
||||
"repo_username": "",
|
||||
"ssh_key": ""
|
||||
},
|
||||
"meta": {
|
||||
"filename": "c_its-companion.kicad_prl",
|
||||
"version": 5
|
||||
},
|
||||
"net_inspector_panel": {
|
||||
"col_hidden": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"col_order": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9
|
||||
],
|
||||
"col_widths": [
|
||||
156,
|
||||
141,
|
||||
103,
|
||||
71,
|
||||
103,
|
||||
103,
|
||||
103,
|
||||
74,
|
||||
103,
|
||||
103
|
||||
],
|
||||
"custom_group_rules": [],
|
||||
"expanded_rows": [],
|
||||
"filter_by_net_name": true,
|
||||
"filter_by_netclass": true,
|
||||
"filter_text": "",
|
||||
"group_by_constraint": false,
|
||||
"group_by_netclass": false,
|
||||
"show_time_domain_details": false,
|
||||
"show_unconnected_nets": false,
|
||||
"show_zero_pad_nets": false,
|
||||
"sort_ascending": true,
|
||||
"sorting_column": 0
|
||||
},
|
||||
"open_jobsets": [],
|
||||
"project": {
|
||||
"files": []
|
||||
},
|
||||
"schematic": {
|
||||
"hierarchy_collapsed": [],
|
||||
"selection_filter": {
|
||||
"graphics": true,
|
||||
"images": true,
|
||||
"labels": true,
|
||||
"lockedItems": false,
|
||||
"otherItems": true,
|
||||
"pins": true,
|
||||
"ruleAreas": true,
|
||||
"symbols": true,
|
||||
"text": true,
|
||||
"wires": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,744 +0,0 @@
|
|||
{
|
||||
"board": {
|
||||
"3dviewports": [
|
||||
{
|
||||
"name": "ISO-back",
|
||||
"ww": 1.0,
|
||||
"wx": 23.768348693847656,
|
||||
"wy": 1.82621169090271,
|
||||
"wz": -13.010103225708008,
|
||||
"xw": 0.0,
|
||||
"xx": -0.6424655318260193,
|
||||
"xy": 0.5901128053665161,
|
||||
"xz": -0.4888812303543091,
|
||||
"yw": 0.0,
|
||||
"yx": 0.7663145065307617,
|
||||
"yy": 0.49455612897872925,
|
||||
"yz": -0.41009292006492615,
|
||||
"zw": 0.0,
|
||||
"zx": -0.00022190232994034886,
|
||||
"zy": -0.638107419013977,
|
||||
"zz": -0.7699472904205322
|
||||
},
|
||||
{
|
||||
"name": "ISO-front",
|
||||
"ww": 1.0,
|
||||
"wx": 4.608643054962158,
|
||||
"wy": 18.730253219604492,
|
||||
"wz": -27.170215606689453,
|
||||
"xw": 0.0,
|
||||
"xx": 0.6427879333496094,
|
||||
"xy": -0.5868241190910339,
|
||||
"xz": 0.49240362644195557,
|
||||
"yw": 0.0,
|
||||
"yx": 0.7660443186759949,
|
||||
"yy": 0.4924042820930481,
|
||||
"yz": -0.4131760001182556,
|
||||
"zw": 0.0,
|
||||
"zx": -2.1759660739917308e-08,
|
||||
"zy": 0.6427874565124512,
|
||||
"zz": 0.7660446763038635
|
||||
}
|
||||
],
|
||||
"design_settings": {
|
||||
"defaults": {
|
||||
"apply_defaults_to_fp_barcodes": false,
|
||||
"apply_defaults_to_fp_dimensions": false,
|
||||
"apply_defaults_to_fp_fields": false,
|
||||
"apply_defaults_to_fp_shapes": false,
|
||||
"apply_defaults_to_fp_text": false,
|
||||
"board_outline_line_width": 0.05,
|
||||
"copper_line_width": 0.2,
|
||||
"copper_text_italic": false,
|
||||
"copper_text_size_h": 1.5,
|
||||
"copper_text_size_v": 1.5,
|
||||
"copper_text_thickness": 0.3,
|
||||
"copper_text_upright": false,
|
||||
"courtyard_line_width": 0.05,
|
||||
"dimension_precision": 4,
|
||||
"dimension_units": 3,
|
||||
"dimensions": {
|
||||
"arrow_length": 1270000,
|
||||
"extension_offset": 500000,
|
||||
"keep_text_aligned": true,
|
||||
"suppress_zeroes": true,
|
||||
"text_position": 0,
|
||||
"units_format": 0
|
||||
},
|
||||
"fab_line_width": 0.1,
|
||||
"fab_text_italic": false,
|
||||
"fab_text_size_h": 1.0,
|
||||
"fab_text_size_v": 1.0,
|
||||
"fab_text_thickness": 0.15,
|
||||
"fab_text_upright": false,
|
||||
"other_line_width": 0.1,
|
||||
"other_text_italic": false,
|
||||
"other_text_size_h": 1.0,
|
||||
"other_text_size_v": 1.0,
|
||||
"other_text_thickness": 0.15,
|
||||
"other_text_upright": false,
|
||||
"pads": {
|
||||
"drill": 0.8,
|
||||
"height": 1.27,
|
||||
"width": 1.27
|
||||
},
|
||||
"silk_line_width": 0.1,
|
||||
"silk_text_italic": false,
|
||||
"silk_text_size_h": 1.0,
|
||||
"silk_text_size_v": 1.0,
|
||||
"silk_text_thickness": 0.1,
|
||||
"silk_text_upright": false,
|
||||
"zones": {
|
||||
"border_display_style": 2,
|
||||
"border_hatch_pitch": 0.5,
|
||||
"corner_radius": 0.0,
|
||||
"corner_smoothing": 0,
|
||||
"fill_mode": 0,
|
||||
"hatch_gap": 1.5,
|
||||
"hatch_orientation": 0.0,
|
||||
"hatch_smoothing_level": 0,
|
||||
"hatch_smoothing_value": 0.1,
|
||||
"hatch_thickness": 1.0,
|
||||
"min_clearance": 0.5,
|
||||
"min_island_area": 10.0,
|
||||
"min_thickness": 0.25,
|
||||
"pad_connection": 1,
|
||||
"remove_islands": 0,
|
||||
"thermal_relief_gap": 0.5,
|
||||
"thermal_relief_spoke_width": 0.5
|
||||
}
|
||||
},
|
||||
"diff_pair_dimensions": [
|
||||
{
|
||||
"gap": 0.0,
|
||||
"via_gap": 0.0,
|
||||
"width": 0.0
|
||||
}
|
||||
],
|
||||
"drc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 2
|
||||
},
|
||||
"rule_severities": {
|
||||
"annular_width": "error",
|
||||
"clearance": "error",
|
||||
"connection_width": "warning",
|
||||
"copper_edge_clearance": "error",
|
||||
"copper_sliver": "warning",
|
||||
"courtyards_overlap": "error",
|
||||
"creepage": "error",
|
||||
"diff_pair_gap_out_of_range": "error",
|
||||
"diff_pair_uncoupled_length_too_long": "error",
|
||||
"drill_out_of_range": "error",
|
||||
"duplicate_footprints": "warning",
|
||||
"extra_footprint": "warning",
|
||||
"footprint": "error",
|
||||
"footprint_filters_mismatch": "ignore",
|
||||
"footprint_symbol_field_mismatch": "warning",
|
||||
"footprint_symbol_mismatch": "warning",
|
||||
"footprint_type_mismatch": "ignore",
|
||||
"hole_clearance": "error",
|
||||
"hole_to_hole": "warning",
|
||||
"holes_co_located": "warning",
|
||||
"invalid_outline": "error",
|
||||
"isolated_copper": "warning",
|
||||
"item_on_disabled_layer": "error",
|
||||
"items_not_allowed": "error",
|
||||
"length_out_of_range": "error",
|
||||
"lib_footprint_issues": "warning",
|
||||
"lib_footprint_mismatch": "warning",
|
||||
"malformed_courtyard": "error",
|
||||
"microvia_drill_out_of_range": "error",
|
||||
"mirrored_text_on_front_layer": "warning",
|
||||
"missing_courtyard": "ignore",
|
||||
"missing_footprint": "warning",
|
||||
"missing_tuning_profile": "warning",
|
||||
"net_conflict": "warning",
|
||||
"nonmirrored_text_on_back_layer": "warning",
|
||||
"npth_inside_courtyard": "error",
|
||||
"padstack": "warning",
|
||||
"pth_inside_courtyard": "error",
|
||||
"shorting_items": "error",
|
||||
"silk_edge_clearance": "warning",
|
||||
"silk_over_copper": "warning",
|
||||
"silk_overlap": "warning",
|
||||
"skew_out_of_range": "error",
|
||||
"solder_mask_bridge": "error",
|
||||
"starved_thermal": "error",
|
||||
"text_height": "warning",
|
||||
"text_on_edge_cuts": "error",
|
||||
"text_thickness": "warning",
|
||||
"through_hole_pad_without_hole": "error",
|
||||
"too_many_vias": "error",
|
||||
"track_angle": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_not_centered_on_via": "ignore",
|
||||
"track_on_post_machined_layer": "error",
|
||||
"track_segment_length": "error",
|
||||
"track_width": "error",
|
||||
"tracks_crossing": "error",
|
||||
"tuning_profile_track_geometries": "ignore",
|
||||
"unconnected_items": "error",
|
||||
"unresolved_variable": "error",
|
||||
"via_dangling": "warning",
|
||||
"zones_intersect": "error"
|
||||
},
|
||||
"rules": {
|
||||
"max_error": 0.005,
|
||||
"min_clearance": 0.0,
|
||||
"min_connection": 0.0,
|
||||
"min_copper_edge_clearance": 0.5,
|
||||
"min_groove_width": 0.0,
|
||||
"min_hole_clearance": 0.25,
|
||||
"min_hole_to_hole": 0.25,
|
||||
"min_microvia_diameter": 0.2,
|
||||
"min_microvia_drill": 0.1,
|
||||
"min_resolved_spokes": 2,
|
||||
"min_silk_clearance": 0.0,
|
||||
"min_text_height": 0.8,
|
||||
"min_text_thickness": 0.08,
|
||||
"min_through_hole_diameter": 0.3,
|
||||
"min_track_width": 0.2,
|
||||
"min_via_annular_width": 0.1,
|
||||
"min_via_diameter": 0.5,
|
||||
"solder_mask_to_copper_clearance": 0.005,
|
||||
"use_height_for_length_calcs": true
|
||||
},
|
||||
"teardrop_options": [
|
||||
{
|
||||
"td_onpthpad": true,
|
||||
"td_onroundshapesonly": false,
|
||||
"td_onsmdpad": true,
|
||||
"td_ontrackend": false,
|
||||
"td_onvia": true
|
||||
}
|
||||
],
|
||||
"teardrop_parameters": [
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_round_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_rect_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_track_end",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
}
|
||||
],
|
||||
"track_widths": [
|
||||
0.0
|
||||
],
|
||||
"tuning_pattern_settings": {
|
||||
"diff_pair_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 1.0
|
||||
},
|
||||
"diff_pair_skew_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
},
|
||||
"single_track_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
}
|
||||
},
|
||||
"via_dimensions": [
|
||||
{
|
||||
"diameter": 0.0,
|
||||
"drill": 0.0
|
||||
}
|
||||
],
|
||||
"zones_allow_external_fillets": false
|
||||
},
|
||||
"ipc2581": {
|
||||
"bom_rev": "",
|
||||
"dist": "",
|
||||
"distpn": "",
|
||||
"internal_id": "",
|
||||
"mfg": "",
|
||||
"mpn": "",
|
||||
"sch_revision": "v0.1"
|
||||
},
|
||||
"layer_pairs": [],
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"component_class_settings": {
|
||||
"assignments": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"sheet_component_classes": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
"erc": {
|
||||
"erc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"pin_map": [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
]
|
||||
],
|
||||
"rule_severities": {
|
||||
"bus_definition_conflict": "error",
|
||||
"bus_entry_needed": "error",
|
||||
"bus_to_bus_conflict": "error",
|
||||
"bus_to_net_conflict": "error",
|
||||
"different_unit_footprint": "error",
|
||||
"different_unit_net": "error",
|
||||
"duplicate_reference": "error",
|
||||
"duplicate_sheet_names": "error",
|
||||
"endpoint_off_grid": "warning",
|
||||
"extra_units": "error",
|
||||
"field_name_whitespace": "warning",
|
||||
"footprint_filter": "ignore",
|
||||
"footprint_link_issues": "warning",
|
||||
"four_way_junction": "ignore",
|
||||
"ground_pin_not_ground": "warning",
|
||||
"hier_label_mismatch": "error",
|
||||
"isolated_pin_label": "warning",
|
||||
"label_dangling": "error",
|
||||
"label_multiple_wires": "warning",
|
||||
"lib_symbol_issues": "warning",
|
||||
"lib_symbol_mismatch": "warning",
|
||||
"missing_bidi_pin": "warning",
|
||||
"missing_input_pin": "warning",
|
||||
"missing_power_pin": "error",
|
||||
"missing_unit": "warning",
|
||||
"multiple_net_names": "warning",
|
||||
"net_not_bus_member": "warning",
|
||||
"no_connect_connected": "warning",
|
||||
"no_connect_dangling": "warning",
|
||||
"pin_not_connected": "error",
|
||||
"pin_not_driven": "error",
|
||||
"pin_to_pin": "warning",
|
||||
"power_pin_not_driven": "error",
|
||||
"same_local_global_label": "warning",
|
||||
"similar_label_and_power": "warning",
|
||||
"similar_labels": "warning",
|
||||
"similar_power": "warning",
|
||||
"simulation_model_issue": "ignore",
|
||||
"single_global_label": "ignore",
|
||||
"stacked_pin_name": "warning",
|
||||
"unannotated": "error",
|
||||
"unconnected_wire_endpoint": "warning",
|
||||
"undefined_netclass": "error",
|
||||
"unit_value_mismatch": "error",
|
||||
"unresolved_variable": "error",
|
||||
"wire_dangling": "error"
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"pinned_footprint_libs": [],
|
||||
"pinned_symbol_libs": []
|
||||
},
|
||||
"meta": {
|
||||
"filename": "c_its-companion.kicad_pro",
|
||||
"version": 3
|
||||
},
|
||||
"net_settings": {
|
||||
"classes": [
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 0.2,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_via_gap": 0.25,
|
||||
"diff_pair_width": 0.2,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Default",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"priority": 2147483647,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.2,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.6,
|
||||
"via_drill": 0.3,
|
||||
"wire_width": 6
|
||||
},
|
||||
{
|
||||
"name": "Power",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"priority": 0,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.5,
|
||||
"tuning_profile": ""
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 5
|
||||
},
|
||||
"net_colors": null,
|
||||
"netclass_assignments": null,
|
||||
"netclass_patterns": [
|
||||
{
|
||||
"netclass": "Power",
|
||||
"pattern": "GND"
|
||||
},
|
||||
{
|
||||
"netclass": "Power",
|
||||
"pattern": "+3V3"
|
||||
},
|
||||
{
|
||||
"netclass": "Power",
|
||||
"pattern": "/BAT*"
|
||||
},
|
||||
{
|
||||
"netclass": "Power",
|
||||
"pattern": "/V_*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pcbnew": {
|
||||
"last_paths": {
|
||||
"idf": "",
|
||||
"netlist": "",
|
||||
"plot": "",
|
||||
"specctra_dsn": "",
|
||||
"vrml": ""
|
||||
},
|
||||
"page_layout_descr_file": ""
|
||||
},
|
||||
"schematic": {
|
||||
"annotate_start_num": 0,
|
||||
"annotation": {
|
||||
"method": 0,
|
||||
"sort_order": 0
|
||||
},
|
||||
"bom_export_filename": "${PROJECTNAME}.csv",
|
||||
"bom_fmt_presets": [],
|
||||
"bom_fmt_settings": {
|
||||
"field_delimiter": ",",
|
||||
"keep_line_breaks": false,
|
||||
"keep_tabs": false,
|
||||
"name": "CSV",
|
||||
"ref_delimiter": ",",
|
||||
"ref_range_delimiter": "",
|
||||
"string_delimiter": "\""
|
||||
},
|
||||
"bom_presets": [],
|
||||
"bom_settings": {
|
||||
"exclude_dnp": false,
|
||||
"fields_ordered": [
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Reference",
|
||||
"name": "Reference",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Qty",
|
||||
"name": "${QUANTITY}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Value",
|
||||
"name": "Value",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "DNP",
|
||||
"name": "${DNP}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Exclude from BOM",
|
||||
"name": "${EXCLUDE_FROM_BOM}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Exclude from Board",
|
||||
"name": "${EXCLUDE_FROM_BOARD}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Footprint",
|
||||
"name": "Footprint",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Datasheet",
|
||||
"name": "Datasheet",
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"filter_string": "",
|
||||
"group_symbols": true,
|
||||
"include_excluded_from_bom": true,
|
||||
"name": "Default Editing",
|
||||
"sort_asc": true,
|
||||
"sort_field": "Reference"
|
||||
},
|
||||
"bus_aliases": {},
|
||||
"connection_grid_size": 50.0,
|
||||
"drawing": {
|
||||
"dashed_lines_dash_length_ratio": 12.0,
|
||||
"dashed_lines_gap_length_ratio": 3.0,
|
||||
"default_line_thickness": 6.0,
|
||||
"default_text_size": 50.0,
|
||||
"field_names": [],
|
||||
"hop_over_size_choice": 0,
|
||||
"intersheets_ref_own_page": false,
|
||||
"intersheets_ref_prefix": "",
|
||||
"intersheets_ref_short": false,
|
||||
"intersheets_ref_show": false,
|
||||
"intersheets_ref_suffix": "",
|
||||
"junction_size_choice": 3,
|
||||
"label_size_ratio": 0.375,
|
||||
"operating_point_overlay_i_precision": 3,
|
||||
"operating_point_overlay_i_range": "~A",
|
||||
"operating_point_overlay_v_precision": 3,
|
||||
"operating_point_overlay_v_range": "~V",
|
||||
"overbar_offset_ratio": 1.23,
|
||||
"pin_symbol_size": 25.0,
|
||||
"text_offset_ratio": 0.15
|
||||
},
|
||||
"legacy_lib_dir": "",
|
||||
"legacy_lib_list": [],
|
||||
"meta": {
|
||||
"version": 1
|
||||
},
|
||||
"page_layout_descr_file": "",
|
||||
"plot_directory": "",
|
||||
"reuse_designators": true,
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0,
|
||||
"top_level_sheets": [
|
||||
{
|
||||
"filename": "c_its-companion.kicad_sch",
|
||||
"name": "Root",
|
||||
"uuid": "9ad468b0-d7e4-4d4a-b652-c4a81a952e30"
|
||||
}
|
||||
],
|
||||
"used_designators": "C1,#PWR1-10,J1-2,SW1,TP1,U1-2,#FLG1-3",
|
||||
"variants": []
|
||||
},
|
||||
"sheets": [
|
||||
[
|
||||
"9ad468b0-d7e4-4d4a-b652-c4a81a952e30",
|
||||
"Root"
|
||||
]
|
||||
],
|
||||
"text_variables": {},
|
||||
"tuning_profiles": {
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"tuning_profiles_impedance_geometric": []
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1 +0,0 @@
|
|||
{"ARCHIVE_NAME": "", "EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false, "OPEN BROWSER": true, "BACKUP_OPT": true}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
(fp_lib_table
|
||||
(version 7)
|
||||
(lib (name "library") (type "KiCad") (uri "${KIPRJMOD}/library") (options "") (descr ""))
|
||||
)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,542 +0,0 @@
|
|||
(footprint "XIAO-ESP32-C5-DIP"
|
||||
(version 20241229)
|
||||
(generator "pcbnew")
|
||||
(generator_version "9.0")
|
||||
(layer "F.Cu")
|
||||
(property "Reference" "REF**"
|
||||
(at -0.275 -10.275 0)
|
||||
(unlocked yes)
|
||||
(layer "F.SilkS")
|
||||
(uuid "7bab359d-5e5b-4dff-9db4-e7c8562ce635")
|
||||
(effects
|
||||
(font
|
||||
(size 0.635 0.635)
|
||||
(thickness 0.1016)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Value" "XIAO-ESP32-C5-DIP"
|
||||
(at 0 0 0)
|
||||
(unlocked yes)
|
||||
(layer "F.Fab")
|
||||
(uuid "79cabf23-4ebc-4d37-9172-bb2cf4ea975f")
|
||||
(effects
|
||||
(font
|
||||
(size 0.635 0.635)
|
||||
(thickness 0.1016)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 0 0 0)
|
||||
(unlocked yes)
|
||||
(layer "F.Fab")
|
||||
(hide yes)
|
||||
(uuid "663dc686-104e-47e1-9669-e1228477fa6f")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Description" ""
|
||||
(at 0 0 0)
|
||||
(unlocked yes)
|
||||
(layer "F.Fab")
|
||||
(hide yes)
|
||||
(uuid "a25b2a4a-94ce-41bd-8341-b98204aa9142")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(attr smd)
|
||||
(fp_line
|
||||
(start -10.414 -6.985)
|
||||
(end -10.414 6.985)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "e213e2f5-659f-4cdb-b390-b284746621aa")
|
||||
)
|
||||
(fp_line
|
||||
(start -8.509 -8.89)
|
||||
(end 8.636 -8.89)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "f58afa0e-63f9-4096-9d17-7cf8e5de7759")
|
||||
)
|
||||
(fp_line
|
||||
(start -8.509 8.89)
|
||||
(end 8.636 8.89)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "9c6409b2-5f1b-4299-85bf-d07c96f13a2a")
|
||||
)
|
||||
(fp_line
|
||||
(start 10.541 -4.495)
|
||||
(end 11.551272 -4.491272)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "4c45fdff-98df-401f-bf6d-768af24b67a9")
|
||||
)
|
||||
(fp_line
|
||||
(start 10.541 6.985)
|
||||
(end 10.541 -6.985)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "eb7ba2c6-3729-4940-9c6b-4ac369cf3075")
|
||||
)
|
||||
(fp_line
|
||||
(start 10.541 6.985)
|
||||
(end 10.541 -6.985)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "6f680559-bbef-46de-9ef5-efda7c2411e9")
|
||||
)
|
||||
(fp_line
|
||||
(start 11.551 4.504)
|
||||
(end 10.541 4.504)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "eddbec16-11c2-42ad-90d6-191cfc991c10")
|
||||
)
|
||||
(fp_line
|
||||
(start 12.051 -3.991272)
|
||||
(end 12.051 4.004)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "e973f8d1-81df-437d-bffe-4f734e1ac18e")
|
||||
)
|
||||
(fp_arc
|
||||
(start -10.414 -6.985)
|
||||
(mid -9.856038 -8.332038)
|
||||
(end -8.509 -8.89)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "88e5d6ea-6b70-4830-9817-980fd2b32f79")
|
||||
)
|
||||
(fp_arc
|
||||
(start -8.509 8.89)
|
||||
(mid -9.856038 8.332038)
|
||||
(end -10.414 6.985)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "f108f05c-d5f6-4de1-bc6f-4c31ef53e967")
|
||||
)
|
||||
(fp_arc
|
||||
(start 8.636 -8.89)
|
||||
(mid 9.983038 -8.332038)
|
||||
(end 10.541 -6.985)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "c6870bc4-897a-485a-b951-3feda1bdd32e")
|
||||
)
|
||||
(fp_arc
|
||||
(start 10.541 6.985)
|
||||
(mid 9.983038 8.332038)
|
||||
(end 8.636 8.89)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "e1d8f660-1495-43db-8a6b-7fdc7dc42e35")
|
||||
)
|
||||
(fp_arc
|
||||
(start 11.551272 -4.491272)
|
||||
(mid 11.904644 -4.344724)
|
||||
(end 12.051 -3.991272)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type default)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "a9b2ba2a-ad25-4191-9070-ebc0333a9101")
|
||||
)
|
||||
(fp_arc
|
||||
(start 12.051 4.004)
|
||||
(mid 11.904553 4.357553)
|
||||
(end 11.551 4.504)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type default)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "45e349ac-e285-4bed-a05b-ae5a80336af0")
|
||||
)
|
||||
(fp_circle
|
||||
(center 9.37 -7.857)
|
||||
(end 9.624 -7.857)
|
||||
(stroke
|
||||
(width 0.5)
|
||||
(type solid)
|
||||
)
|
||||
(fill yes)
|
||||
(layer "F.SilkS")
|
||||
(uuid "6839324b-c75a-49ba-8c0c-16e2191166eb")
|
||||
)
|
||||
(fp_circle
|
||||
(center 10.25 -9)
|
||||
(end 10.504 -9)
|
||||
(stroke
|
||||
(width 0.5)
|
||||
(type solid)
|
||||
)
|
||||
(fill yes)
|
||||
(layer "F.SilkS")
|
||||
(uuid "6e5f4857-2ad9-4493-8234-6091a6821232")
|
||||
)
|
||||
(fp_rect
|
||||
(start -10.425 -8.9)
|
||||
(end 10.55 8.9)
|
||||
(stroke
|
||||
(width 0.05)
|
||||
(type default)
|
||||
)
|
||||
(fill no)
|
||||
(layer "F.CrtYd")
|
||||
(uuid "b53697bc-d105-40e2-af66-708b16a7f26c")
|
||||
)
|
||||
(fp_rect
|
||||
(start -10.425 -8.9)
|
||||
(end 10.55 8.9)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(fill no)
|
||||
(layer "F.Fab")
|
||||
(uuid "a45a327e-f036-4877-8d89-39f4f4b0c715")
|
||||
)
|
||||
(fp_circle
|
||||
(center 9.376 -7.854)
|
||||
(end 9.63 -7.854)
|
||||
(stroke
|
||||
(width 0.5)
|
||||
(type solid)
|
||||
)
|
||||
(fill yes)
|
||||
(layer "F.Fab")
|
||||
(uuid "12a1812e-f866-498e-9d6e-50e427087f34")
|
||||
)
|
||||
(pad "1" smd roundrect
|
||||
(at 7.62 -8.455 90)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "82bbfb55-8baa-4218-ac89-ab07d82ef7f8")
|
||||
)
|
||||
(pad "1" thru_hole circle
|
||||
(at 7.62 -7.62 90)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "f2fffd4f-460c-4b4d-b954-811e935ed1d6")
|
||||
)
|
||||
(pad "2" smd roundrect
|
||||
(at 5.08 -8.455 90)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "18ef9914-2d15-494c-9020-2334abcc1460")
|
||||
)
|
||||
(pad "2" thru_hole circle
|
||||
(at 5.08 -7.62 90)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "4732cc3f-e641-4870-a9b3-ae13729c7bba")
|
||||
)
|
||||
(pad "3" smd roundrect
|
||||
(at 2.54 -8.455 90)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "15338a55-8514-4218-a2c1-2cae003104c3")
|
||||
)
|
||||
(pad "3" thru_hole circle
|
||||
(at 2.54 -7.62 90)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "0cef5e55-d8ff-4394-9cc1-cc318608b0d9")
|
||||
)
|
||||
(pad "4" smd roundrect
|
||||
(at 0 -8.455 90)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "191a259d-5773-4a80-91de-baa69611fab8")
|
||||
)
|
||||
(pad "4" thru_hole circle
|
||||
(at 0 -7.62 90)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "200b8da4-9395-48a5-b7ac-92a6a147c215")
|
||||
)
|
||||
(pad "5" smd roundrect
|
||||
(at -2.54 -8.455 90)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "52012467-1c46-4a8e-aa82-6f6ce69bd2ca")
|
||||
)
|
||||
(pad "5" thru_hole circle
|
||||
(at -2.54 -7.62 90)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "2b50ccce-3634-47d1-bf7d-a3db4f3f9a9f")
|
||||
)
|
||||
(pad "6" smd roundrect
|
||||
(at -5.08 -8.455 90)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "04aeaf95-142a-40f6-97a3-04453fc50824")
|
||||
)
|
||||
(pad "6" thru_hole circle
|
||||
(at -5.08 -7.62 90)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "7636de9d-d8e5-49c9-9fff-0cd76515690d")
|
||||
)
|
||||
(pad "7" smd roundrect
|
||||
(at -7.62 -8.455 90)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "e997fd0b-233a-4656-afad-cb2f8b9d87c9")
|
||||
)
|
||||
(pad "7" thru_hole circle
|
||||
(at -7.62 -7.62 90)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "9d2c0d0c-05dd-477e-8384-b14eec83fc2e")
|
||||
)
|
||||
(pad "8" thru_hole circle
|
||||
(at -7.62 7.62 270)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "1e50c66f-49b8-4b03-9889-8cc6ca45ad8e")
|
||||
)
|
||||
(pad "8" smd roundrect
|
||||
(at -7.62 8.455 270)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "e7b72803-b5f0-433c-8313-83247355fb43")
|
||||
)
|
||||
(pad "9" thru_hole circle
|
||||
(at -5.08 7.62 270)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "808bf9dc-1c26-4a77-93c9-e4c6d5eccbb2")
|
||||
)
|
||||
(pad "9" smd roundrect
|
||||
(at -5.08 8.455 270)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "532de3b6-bd30-47c5-b70e-0bca6abbd997")
|
||||
)
|
||||
(pad "10" thru_hole circle
|
||||
(at -2.54 7.62 270)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "a792a2ac-537b-4136-8f92-b81f7ce8f753")
|
||||
)
|
||||
(pad "10" smd roundrect
|
||||
(at -2.54 8.455 270)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "6e3f5c75-6143-468e-bb07-d89020c03f59")
|
||||
)
|
||||
(pad "11" thru_hole circle
|
||||
(at 0 7.62 270)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "fb78046a-9b91-4893-b010-827957cc07be")
|
||||
)
|
||||
(pad "11" smd roundrect
|
||||
(at 0 8.455 270)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "5249f425-bece-432d-8076-28e1b8b386e3")
|
||||
)
|
||||
(pad "12" thru_hole circle
|
||||
(at 2.54 7.62 270)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "0b8ccc97-5100-4dcf-8500-e99789ee0b4c")
|
||||
)
|
||||
(pad "12" smd roundrect
|
||||
(at 2.54 8.455 270)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "82f1bbd1-708b-4d2d-9113-7ba3c2313670")
|
||||
)
|
||||
(pad "13" thru_hole circle
|
||||
(at 5.08 7.62 270)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "03882061-01af-484b-b620-9e92a3daaf68")
|
||||
)
|
||||
(pad "13" smd roundrect
|
||||
(at 5.08 8.455 270)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "954f51d1-3d75-42fb-a02a-7389b30718d9")
|
||||
)
|
||||
(pad "14" thru_hole circle
|
||||
(at 7.62 7.62 270)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "c6d1af47-da13-4409-9ab1-55d358cc57eb")
|
||||
)
|
||||
(pad "14" smd roundrect
|
||||
(at 7.62 8.455 270)
|
||||
(size 2.432 1.524)
|
||||
(layers "F.Cu" "F.Mask")
|
||||
(roundrect_rratio 0.1)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "6b725fd4-d6f0-46bf-b3e0-7ec07cc05622")
|
||||
)
|
||||
(embedded_fonts no)
|
||||
(model "${AMZPATH}/3dmodels/AMz3D.3dshapes/XIAO-nRF52840 v15.step"
|
||||
(hide yes)
|
||||
(offset
|
||||
(xyz -1.778 -6.096 0.381)
|
||||
)
|
||||
(scale
|
||||
(xyz 1 1 1)
|
||||
)
|
||||
(rotate
|
||||
(xyz -90 0 0)
|
||||
)
|
||||
)
|
||||
(model "${AMZPATH}/3dmodels/AMz3D.3dshapes/Seeeduino XIAO SAMD21 v4.step"
|
||||
(offset
|
||||
(xyz 0 0 0)
|
||||
)
|
||||
(scale
|
||||
(xyz 1 1 1)
|
||||
)
|
||||
(rotate
|
||||
(xyz 0 0 0)
|
||||
)
|
||||
)
|
||||
(model "${AMZPATH}/3dmodels/AMz3D.3dshapes/Seeed Studio XIAO RP2040 v26.step"
|
||||
(hide yes)
|
||||
(offset
|
||||
(xyz 0 0 0)
|
||||
)
|
||||
(scale
|
||||
(xyz 1 1 1)
|
||||
)
|
||||
(rotate
|
||||
(xyz 0 0 90)
|
||||
)
|
||||
)
|
||||
(model "${AMZPATH}/3dmodels/AMz3D.3dshapes/Seeed Studio XIAO-ESP32-C3.step"
|
||||
(hide yes)
|
||||
(offset
|
||||
(xyz -1.7018 -6.096 0)
|
||||
)
|
||||
(scale
|
||||
(xyz 1 1 1)
|
||||
)
|
||||
(rotate
|
||||
(xyz -90 0 0)
|
||||
)
|
||||
)
|
||||
(model "${AMZPATH}/3dmodels/AMz3D.3dshapes/XIAO-ESP32S3 v2.step"
|
||||
(hide yes)
|
||||
(offset
|
||||
(xyz -1.778 -6.1722 0)
|
||||
)
|
||||
(scale
|
||||
(xyz 1 1 1)
|
||||
)
|
||||
(rotate
|
||||
(xyz -90 0 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -1,558 +0,0 @@
|
|||
(footprint "XIAO-ESP32-C5-SMD-DIP"
|
||||
(version 20260206)
|
||||
(generator "pcbnew")
|
||||
(generator_version "10.0")
|
||||
(layer "F.Cu")
|
||||
(property "Reference" "REF**"
|
||||
(at 7.312 -21.336 0)
|
||||
(layer "F.SilkS")
|
||||
(uuid "95a6896c-2eab-4f28-adc5-7fd64550efd1")
|
||||
(effects
|
||||
(font
|
||||
(size 0.78232 0.78232)
|
||||
(thickness 0.10668)
|
||||
)
|
||||
(justify left bottom)
|
||||
)
|
||||
)
|
||||
(property "Value" "Val**"
|
||||
(at 8.89 -10.795 0)
|
||||
(layer "F.Fab")
|
||||
(uuid "ead571aa-8f25-4413-9dd8-3ecea3d8b1b5")
|
||||
(effects
|
||||
(font
|
||||
(size 0.536448 0.536448)
|
||||
(thickness 0.073152)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 0 0 0)
|
||||
(unlocked yes)
|
||||
(layer "F.Fab")
|
||||
(hide yes)
|
||||
(uuid "e0e614f2-f99b-42a7-87d5-6d40f74c047a")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Description" ""
|
||||
(at 0 0 0)
|
||||
(unlocked yes)
|
||||
(layer "F.Fab")
|
||||
(hide yes)
|
||||
(uuid "074d8848-2ba7-4146-8c49-a0cf65e2aa82")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(duplicate_pad_numbers_are_jumpers no)
|
||||
(fp_line
|
||||
(start 0 -1.987)
|
||||
(end 0 -19.132)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "e72c3c07-5bc8-4f5f-97cd-36412308e250")
|
||||
)
|
||||
(fp_line
|
||||
(start 1.905 -0.082)
|
||||
(end 15.875 -0.082)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "e3e0ab25-f18a-4068-92e8-92a17b4caf05")
|
||||
)
|
||||
(fp_line
|
||||
(start 4.595 -21.037)
|
||||
(end 4.598728 -22.047272)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "4838598f-afa6-445e-b237-7544ae7e9968")
|
||||
)
|
||||
(fp_line
|
||||
(start 5.098728 -22.547)
|
||||
(end 13.094 -22.547)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "101b5417-f570-447c-b9f6-42639e5eff3c")
|
||||
)
|
||||
(fp_line
|
||||
(start 13.594 -22.047)
|
||||
(end 13.594 -21.037)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "37ee6532-c85a-4899-82db-921e02cce4af")
|
||||
)
|
||||
(fp_line
|
||||
(start 15.875 -21.037)
|
||||
(end 1.905 -21.037)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "f94b2798-d11f-47d7-8f2c-02478417b6f1")
|
||||
)
|
||||
(fp_line
|
||||
(start 17.78 -1.987)
|
||||
(end 17.78 -19.132)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "8c3282cc-86c2-4cf3-a698-856916df5f40")
|
||||
)
|
||||
(fp_arc
|
||||
(start 0 -19.136)
|
||||
(mid 0.557962 -20.483038)
|
||||
(end 1.905 -21.041)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "2a1b788b-cdcf-4a49-acac-d187cf344ffc")
|
||||
)
|
||||
(fp_arc
|
||||
(start 1.905 -0.082)
|
||||
(mid 0.557962 -0.639962)
|
||||
(end 0 -1.987)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "677c2a8e-0212-4651-99b4-af42d29d0bd1")
|
||||
)
|
||||
(fp_arc
|
||||
(start 4.598728 -22.047272)
|
||||
(mid 4.745276 -22.400644)
|
||||
(end 5.098728 -22.547)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type default)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "b38fc005-51b4-4609-b7e3-78320e82da0d")
|
||||
)
|
||||
(fp_arc
|
||||
(start 13.094 -22.547)
|
||||
(mid 13.447553 -22.400553)
|
||||
(end 13.594 -22.047)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type default)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "46a43512-01fe-440e-b1e8-31a50057cbf8")
|
||||
)
|
||||
(fp_arc
|
||||
(start 15.875 -21.037)
|
||||
(mid 17.222038 -20.479038)
|
||||
(end 17.78 -19.132)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "f77dffb2-d331-43db-9018-069e9fa47c57")
|
||||
)
|
||||
(fp_arc
|
||||
(start 17.78 -1.987)
|
||||
(mid 17.222038 -0.639962)
|
||||
(end 15.875 -0.082)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "535b0a70-da0d-4557-a5dd-d25a51ee5468")
|
||||
)
|
||||
(fp_circle
|
||||
(center -0.21 -20.621)
|
||||
(end -0.21 -20.875)
|
||||
(stroke
|
||||
(width 0.5)
|
||||
(type solid)
|
||||
)
|
||||
(fill yes)
|
||||
(layer "F.SilkS")
|
||||
(uuid "c4f8c64e-4c5d-41c1-8b14-aca8fcf77968")
|
||||
)
|
||||
(fp_circle
|
||||
(center 1.064 -19.904)
|
||||
(end 1.064 -20.158)
|
||||
(stroke
|
||||
(width 0.5)
|
||||
(type solid)
|
||||
)
|
||||
(fill yes)
|
||||
(layer "F.SilkS")
|
||||
(uuid "163ea809-1cd6-43c9-968e-14c093715603")
|
||||
)
|
||||
(fp_rect
|
||||
(start 17.8 0)
|
||||
(end 0 -21)
|
||||
(stroke
|
||||
(width 0.05)
|
||||
(type default)
|
||||
)
|
||||
(fill no)
|
||||
(layer "F.CrtYd")
|
||||
(uuid "477d102d-f2d0-4f67-ad93-9f60da51bd76")
|
||||
)
|
||||
(fp_rect
|
||||
(start 0 -21)
|
||||
(end 17.8 0)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(fill no)
|
||||
(layer "F.Fab")
|
||||
(uuid "e7107e2f-485b-49f6-9f5d-2537ab0ef458")
|
||||
)
|
||||
(fp_circle
|
||||
(center 1.084 -19.9)
|
||||
(end 1.084 -20.154)
|
||||
(stroke
|
||||
(width 0.5)
|
||||
(type solid)
|
||||
)
|
||||
(fill yes)
|
||||
(layer "F.Fab")
|
||||
(uuid "089122fe-193e-4cef-a9b8-54d37cb7fa99")
|
||||
)
|
||||
(pad "1" smd roundrect
|
||||
(at 0.835 -18.12)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "351f5f05-8bc3-4b53-9daf-3207bd71876b")
|
||||
)
|
||||
(pad "1" thru_hole circle
|
||||
(at 1.26 -18.14 180)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "6a16c3b2-8d4e-4efe-a84e-dbc418c7fe79")
|
||||
)
|
||||
(pad "2" smd roundrect
|
||||
(at 0.835 -15.58)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "cfdee4aa-d600-484f-9a5c-2062a00bde1d")
|
||||
)
|
||||
(pad "2" thru_hole circle
|
||||
(at 1.26 -15.6 180)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "69193d02-4e31-4519-a90d-949c3f48e92c")
|
||||
)
|
||||
(pad "3" smd roundrect
|
||||
(at 0.835 -13.04)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "52a69e1c-edab-4079-a491-6bcb12907f61")
|
||||
)
|
||||
(pad "3" thru_hole circle
|
||||
(at 1.26 -13.06 180)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "a3aed707-7e8b-4b1b-ba96-06cfb9154fcb")
|
||||
)
|
||||
(pad "4" smd roundrect
|
||||
(at 0.835 -10.5)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "1e864c46-432c-460c-af69-13a1c9d0fd2f")
|
||||
)
|
||||
(pad "4" thru_hole circle
|
||||
(at 1.26 -10.52 180)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "eee65312-1942-42a1-a13a-2a28a114ec95")
|
||||
)
|
||||
(pad "5" smd roundrect
|
||||
(at 0.835 -7.96)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "da854e17-c3b3-4a83-9d47-a379e4d7efa5")
|
||||
)
|
||||
(pad "5" thru_hole circle
|
||||
(at 1.26 -7.98 180)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "febdd29f-f361-4b8e-b2b5-7502d3e5fdea")
|
||||
)
|
||||
(pad "6" smd roundrect
|
||||
(at 0.835 -5.42)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "3fde7f90-983d-44c1-939e-47d36175373f")
|
||||
)
|
||||
(pad "6" thru_hole circle
|
||||
(at 1.26 -5.44 180)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "323ef0a5-3407-4ac3-b76f-8ac19e044d1e")
|
||||
)
|
||||
(pad "7" smd roundrect
|
||||
(at 0.835 -2.88)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "ebfd6bef-ea17-4635-877b-58b57c784dec")
|
||||
)
|
||||
(pad "7" thru_hole circle
|
||||
(at 1.26 -2.9 180)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "2b179783-65b0-4d7c-850b-74c9fa3a7a64")
|
||||
)
|
||||
(pad "8" thru_hole circle
|
||||
(at 16.5 -2.9)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "5cbc8aa6-fdfd-49e6-b018-da1d4ff342aa")
|
||||
)
|
||||
(pad "8" smd roundrect
|
||||
(at 17 -2.88)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "19eb14d0-5713-47b7-86f2-4bccede8ba54")
|
||||
)
|
||||
(pad "9" thru_hole circle
|
||||
(at 16.5 -5.44)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "e7f197b5-1fc8-47f6-9f49-60ffd6fa2335")
|
||||
)
|
||||
(pad "9" smd roundrect
|
||||
(at 17 -5.42)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "4c3b83bc-a000-4a70-887c-6b775bc40ee1")
|
||||
)
|
||||
(pad "10" thru_hole circle
|
||||
(at 16.5 -7.98)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "4df7fd17-71d5-4f8f-a3f2-68c6537a767f")
|
||||
)
|
||||
(pad "10" smd roundrect
|
||||
(at 17 -7.96)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "223d5c33-d2a3-4398-8d3f-544bd6345a86")
|
||||
)
|
||||
(pad "11" thru_hole circle
|
||||
(at 16.5 -10.52)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "dad67976-a604-485a-bb37-2d67ea771b2c")
|
||||
)
|
||||
(pad "11" smd roundrect
|
||||
(at 17 -10.5)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "1f04f1d2-ce82-4256-b122-36f63f189efe")
|
||||
)
|
||||
(pad "12" thru_hole circle
|
||||
(at 16.5 -13.06)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "c55d522b-9f8b-4fa4-afaa-880f3b1c9c5f")
|
||||
)
|
||||
(pad "12" smd roundrect
|
||||
(at 17 -13.04)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "31818d88-bbc0-4c22-b4ed-d6884ef71ebb")
|
||||
)
|
||||
(pad "13" thru_hole circle
|
||||
(at 16.5 -15.6)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "e46165bc-b289-44b0-b7f6-6759bba3d4b8")
|
||||
)
|
||||
(pad "13" smd roundrect
|
||||
(at 17 -15.58)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "3fbf02f8-81da-4fba-bc95-9e47b9abaa05")
|
||||
)
|
||||
(pad "14" thru_hole circle
|
||||
(at 16.5 -18.14)
|
||||
(size 1.524 1.524)
|
||||
(drill 0.889)
|
||||
(layers "*.Cu" "*.Mask")
|
||||
(remove_unused_layers no)
|
||||
(uuid "71ca3093-fd4e-46c3-9980-6d986d711719")
|
||||
)
|
||||
(pad "14" smd roundrect
|
||||
(at 17 -18.12)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "ce7a6ff3-5c3a-4077-bf86-beedc24debb3")
|
||||
)
|
||||
(pad "15" smd circle
|
||||
(at 7.6166 -19.1415)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "7273149b-9aea-4a23-9b4e-db1e0d6c76fc")
|
||||
)
|
||||
(pad "16" smd circle
|
||||
(at 10.1566 -19.1415)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "0aef11c3-14ad-4cb5-9b05-95a892988ce8")
|
||||
)
|
||||
(pad "17" smd circle
|
||||
(at 7.6166 -16.6015)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "158a5cbb-af86-4fa6-8d58-6831485085c5")
|
||||
)
|
||||
(pad "18" smd circle
|
||||
(at 10.1566 -16.6015)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "3b7bcd8a-c97c-46f6-9faa-4e525d03d6e8")
|
||||
)
|
||||
(pad "19" smd circle
|
||||
(at 7.6166 -14.0615)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "ad4ba963-211d-4878-a4cb-255a615c14de")
|
||||
)
|
||||
(pad "20" smd circle
|
||||
(at 10.1566 -14.0615)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "8c7e088d-0846-4d7d-9143-5ce3fdb0c4fd")
|
||||
)
|
||||
(pad "21" smd circle
|
||||
(at 7.6166 -11.5215)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "c6bbe198-c8c7-4b83-a36e-6a6fdeb3b94f")
|
||||
)
|
||||
(pad "22" smd circle
|
||||
(at 10.064 -11.5783)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "4f1e574b-3b71-4a23-b765-d779726fd3c6")
|
||||
)
|
||||
(pad "23" smd roundrect
|
||||
(at 7.3086 -4.9683 270)
|
||||
(size 2.5 1.1)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "08d345bf-1b32-499f-94bb-44332eb5115d")
|
||||
)
|
||||
(pad "24" smd roundrect
|
||||
(at 9.8486 -4.9683 270)
|
||||
(size 2.5 1.1)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "8b3ca1bb-eee0-4c3d-8f3b-eefea4a68bbb")
|
||||
)
|
||||
(pad "25" smd roundrect
|
||||
(at 9.949 -8.412 270)
|
||||
(size 3.1 1.9)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "45570478-e499-45ab-bb97-539cabc4e177")
|
||||
)
|
||||
(embedded_fonts no)
|
||||
)
|
||||
|
|
@ -1,445 +0,0 @@
|
|||
(footprint "XIAO-ESP32-C5-SMD"
|
||||
(version 20241229)
|
||||
(generator "pcbnew")
|
||||
(generator_version "9.0")
|
||||
(layer "F.Cu")
|
||||
(property "Reference" "REF**"
|
||||
(at 7.312 -21.336 0)
|
||||
(layer "F.SilkS")
|
||||
(uuid "a9f5f5dd-597c-45dd-b31d-a35c3963eed2")
|
||||
(effects
|
||||
(font
|
||||
(size 0.78232 0.78232)
|
||||
(thickness 0.10668)
|
||||
)
|
||||
(justify left bottom)
|
||||
)
|
||||
)
|
||||
(property "Value" "Val**"
|
||||
(at 8.89 -10.795 0)
|
||||
(layer "F.Fab")
|
||||
(uuid "2d66aca7-e0b3-44df-838e-be285890792c")
|
||||
(effects
|
||||
(font
|
||||
(size 0.536448 0.536448)
|
||||
(thickness 0.073152)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Datasheet" ""
|
||||
(at 0 0 0)
|
||||
(unlocked yes)
|
||||
(layer "F.Fab")
|
||||
(hide yes)
|
||||
(uuid "5173b428-b87b-497c-9fd1-0d444c2ced85")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(property "Description" ""
|
||||
(at 0 0 0)
|
||||
(unlocked yes)
|
||||
(layer "F.Fab")
|
||||
(hide yes)
|
||||
(uuid "ea7181c4-fdca-44f1-ad15-d0627f50a68e")
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(fp_line
|
||||
(start 0 -1.987)
|
||||
(end 0 -19.132)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "df0486b4-d04f-4201-a301-beede6e15862")
|
||||
)
|
||||
(fp_line
|
||||
(start 1.905 -0.082)
|
||||
(end 15.875 -0.082)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "f54abd8d-62ed-4b05-8e37-f29acdadc0f8")
|
||||
)
|
||||
(fp_line
|
||||
(start 4.595 -21.037)
|
||||
(end 4.598728 -22.047272)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "fead6daa-b9f4-4bd1-a131-b5d2cd3184de")
|
||||
)
|
||||
(fp_line
|
||||
(start 5.098728 -22.547)
|
||||
(end 13.094 -22.547)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "b90ae92d-7c1a-4689-bf64-c3528a3ea3ee")
|
||||
)
|
||||
(fp_line
|
||||
(start 13.594 -22.047)
|
||||
(end 13.594 -21.037)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "b540fbcc-8e06-44d7-95f6-8b142e4c90fb")
|
||||
)
|
||||
(fp_line
|
||||
(start 15.875 -21.037)
|
||||
(end 1.905 -21.037)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "2b80542c-5088-4f89-ae32-3400cf894ca1")
|
||||
)
|
||||
(fp_line
|
||||
(start 17.78 -1.987)
|
||||
(end 17.78 -19.132)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "4cd8add0-8a8c-468d-ba6f-b86787c288af")
|
||||
)
|
||||
(fp_arc
|
||||
(start 0 -19.136)
|
||||
(mid 0.557962 -20.483038)
|
||||
(end 1.905 -21.041)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "4d9bf7a5-4ab4-48bb-b8ac-0a5ce4523642")
|
||||
)
|
||||
(fp_arc
|
||||
(start 1.905 -0.082)
|
||||
(mid 0.557962 -0.639962)
|
||||
(end 0 -1.987)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "d09661c8-1ad3-40e9-8323-9dabc8f3f98b")
|
||||
)
|
||||
(fp_arc
|
||||
(start 4.598728 -22.047272)
|
||||
(mid 4.745276 -22.400644)
|
||||
(end 5.098728 -22.547)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type default)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "7a58f637-14d7-4804-b127-f1d029b5d1e6")
|
||||
)
|
||||
(fp_arc
|
||||
(start 13.094 -22.547)
|
||||
(mid 13.447553 -22.400553)
|
||||
(end 13.594 -22.047)
|
||||
(stroke
|
||||
(width 0.127)
|
||||
(type default)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "d0bc8663-987f-4e2e-b464-096a547b822f")
|
||||
)
|
||||
(fp_arc
|
||||
(start 15.875 -21.037)
|
||||
(mid 17.222038 -20.479038)
|
||||
(end 17.78 -19.132)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "77004ea5-53a4-40ab-b35c-7bdd22107e34")
|
||||
)
|
||||
(fp_arc
|
||||
(start 17.78 -1.987)
|
||||
(mid 17.222038 -0.639962)
|
||||
(end 15.875 -0.082)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type solid)
|
||||
)
|
||||
(layer "F.SilkS")
|
||||
(uuid "94189dc1-14b5-4f17-b9d3-0163ed99e583")
|
||||
)
|
||||
(fp_circle
|
||||
(center -0.21 -20.621)
|
||||
(end -0.21 -20.875)
|
||||
(stroke
|
||||
(width 0.5)
|
||||
(type solid)
|
||||
)
|
||||
(fill yes)
|
||||
(layer "F.SilkS")
|
||||
(uuid "dc606cc5-10bc-4635-81d1-fa27b1e6b4e2")
|
||||
)
|
||||
(fp_circle
|
||||
(center 1.064 -19.904)
|
||||
(end 1.064 -20.158)
|
||||
(stroke
|
||||
(width 0.5)
|
||||
(type solid)
|
||||
)
|
||||
(fill yes)
|
||||
(layer "F.SilkS")
|
||||
(uuid "2b5e2829-6d01-441f-9983-fc4a273ff483")
|
||||
)
|
||||
(fp_rect
|
||||
(start 17.8 0)
|
||||
(end 0 -21)
|
||||
(stroke
|
||||
(width 0.05)
|
||||
(type default)
|
||||
)
|
||||
(fill no)
|
||||
(layer "F.CrtYd")
|
||||
(uuid "e428eb56-00a2-4249-bb91-d2e5823b2899")
|
||||
)
|
||||
(fp_rect
|
||||
(start 0 -21)
|
||||
(end 17.8 0)
|
||||
(stroke
|
||||
(width 0.1)
|
||||
(type default)
|
||||
)
|
||||
(fill no)
|
||||
(layer "F.Fab")
|
||||
(uuid "2133d927-559d-4615-ab92-732772c02d94")
|
||||
)
|
||||
(fp_circle
|
||||
(center 1.084 -19.9)
|
||||
(end 1.084 -20.154)
|
||||
(stroke
|
||||
(width 0.5)
|
||||
(type solid)
|
||||
)
|
||||
(fill yes)
|
||||
(layer "F.Fab")
|
||||
(uuid "50e04a65-8ff2-4d1f-be98-2db48101e90a")
|
||||
)
|
||||
(pad "1" smd roundrect
|
||||
(at 0.835 -18.12)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "c68f6b12-dcf5-46b1-b159-2eef9023b0ac")
|
||||
)
|
||||
(pad "2" smd roundrect
|
||||
(at 0.835 -15.58)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "525b09fc-9099-4274-98c3-45f84d0591ab")
|
||||
)
|
||||
(pad "3" smd roundrect
|
||||
(at 0.835 -13.04)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "99cc76a7-0a78-4868-871b-c214ecac5525")
|
||||
)
|
||||
(pad "4" smd roundrect
|
||||
(at 0.835 -10.5)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "8038b67b-b3bf-4a3e-b790-40857963c957")
|
||||
)
|
||||
(pad "5" smd roundrect
|
||||
(at 0.835 -7.96)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "84711cb2-d222-464e-bcd5-f76af75cc612")
|
||||
)
|
||||
(pad "6" smd roundrect
|
||||
(at 0.835 -5.42)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "aabcfc3b-fa72-4e48-95d1-d53474158cc0")
|
||||
)
|
||||
(pad "7" smd roundrect
|
||||
(at 0.835 -2.88)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "3e086061-1f3f-4b1e-9354-920126d40249")
|
||||
)
|
||||
(pad "8" smd roundrect
|
||||
(at 17 -2.88)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "974a7e29-b5d3-475c-b481-2bc14f5b3495")
|
||||
)
|
||||
(pad "9" smd roundrect
|
||||
(at 17 -5.42)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "a553cc0b-22bc-49aa-9fb7-680c812e8abd")
|
||||
)
|
||||
(pad "10" smd roundrect
|
||||
(at 17 -7.96)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "b650936d-c436-4d93-abb7-7957add5cfb1")
|
||||
)
|
||||
(pad "11" smd roundrect
|
||||
(at 17 -10.5)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "177728f1-7b02-41a6-9de4-bdaee546f6d9")
|
||||
)
|
||||
(pad "12" smd roundrect
|
||||
(at 17 -13.04)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "eb6539ef-4d84-4ae5-bc4e-237dae21b9ef")
|
||||
)
|
||||
(pad "13" smd roundrect
|
||||
(at 17 -15.58)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "c8744fbe-93c9-4c54-84bc-435357de679f")
|
||||
)
|
||||
(pad "14" smd roundrect
|
||||
(at 17 -18.12)
|
||||
(size 2.75 2)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(solder_mask_margin 0.0508)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "fd579d63-bbef-4609-9252-aad3f944a314")
|
||||
)
|
||||
(pad "15" smd circle
|
||||
(at 7.6166 -19.1415)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "ad890579-1e55-4b71-9d59-080a59598896")
|
||||
)
|
||||
(pad "16" smd circle
|
||||
(at 10.1566 -19.1415)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "25d6fdae-01ac-4ca4-8702-55badc8bcbc8")
|
||||
)
|
||||
(pad "17" smd circle
|
||||
(at 7.6166 -16.6015)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "e73ae295-736f-4acf-8ee1-0c120ebcebea")
|
||||
)
|
||||
(pad "18" smd circle
|
||||
(at 10.1566 -16.6015)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "7adfba87-f861-42a5-bb73-c3f9fac53180")
|
||||
)
|
||||
(pad "19" smd circle
|
||||
(at 7.6166 -14.0615)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "619dc2a5-519d-48ac-9c78-8c438df2a14d")
|
||||
)
|
||||
(pad "20" smd circle
|
||||
(at 10.1566 -14.0615)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "aa8b72d0-17bf-47d4-94b1-df9199638a29")
|
||||
)
|
||||
(pad "21" smd circle
|
||||
(at 7.6166 -11.5215)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "8539b5cc-db2c-4fa4-9ead-b842a1885293")
|
||||
)
|
||||
(pad "22" smd circle
|
||||
(at 10.064 -11.5783)
|
||||
(size 1.7 1.7)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(uuid "313edcfe-ad25-4ea7-b786-d8290dfb479a")
|
||||
)
|
||||
(pad "23" smd roundrect
|
||||
(at 7.3086 -4.9683 270)
|
||||
(size 2.5 1.1)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "144d3c3d-6207-433c-9e61-4f75722af865")
|
||||
)
|
||||
(pad "24" smd roundrect
|
||||
(at 9.8486 -4.9683 270)
|
||||
(size 2.5 1.1)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "3780a4ef-348c-4241-b04b-2cb4f7127fa2")
|
||||
)
|
||||
(pad "25" smd roundrect
|
||||
(at 9.949 -8.412 270)
|
||||
(size 3.1 1.9)
|
||||
(layers "F.Cu" "F.Mask" "F.Paste")
|
||||
(roundrect_rratio 0.25)
|
||||
(thermal_bridge_angle 45)
|
||||
(uuid "00be3957-5a21-4898-8292-59f5f447623f")
|
||||
)
|
||||
(embedded_fonts no)
|
||||
)
|
||||
Binary file not shown.
|
|
@ -1,4 +0,0 @@
|
|||
(sym_lib_table
|
||||
(version 7)
|
||||
(lib (name "Seeed_Studio_XIAO_Series") (type "KiCad") (uri "${KIPRJMOD}/library/Seeed_Studio_XIAO_Series.kicad_sym") (options "") (descr ""))
|
||||
)
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
## Builds common config for smartphone connected device
|
||||
## -> receive CAM and DENMs via BLE and send CAMs based on position data received from BLE
|
||||
|
||||
cargo run --release --no-default-features -F esp,cam,denm,ble,cam_tx
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
## Builds common config for smartphone connected device
|
||||
## -> only receive CAM and DENMs via UART and BLE
|
||||
|
||||
cargo run --release --no-default-features -F esp,cam,denm,uart,ble
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
## Builds common config for smartphone connected device
|
||||
## -> only receive CAM and DENMs via UART
|
||||
|
||||
cargo run --release --no-default-features -F esp,cam,denm,uart
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
## Builds common config for standalone device with automatic CAM transmission
|
||||
## -> enable SPAT/ GLOSA with GNSS and a screen + CAM transmission
|
||||
|
||||
cargo run --release -F cam_tx
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
## Builds common config for standalone device
|
||||
## -> enable SPAT/ GLOSA with GNSS and a screen
|
||||
|
||||
cargo run --release
|
||||
|
|
@ -7,32 +7,23 @@ pub fn handle_cam(etsi: &cam_pdu_descriptions::CAM) {
|
|||
// only POC output
|
||||
match &etsi.cam.cam_parameters.high_frequency_container {
|
||||
HighFrequencyContainer::basicVehicleContainerHighFrequency(obu) => {
|
||||
let station_id = etsi.header.station_id.0;
|
||||
let lat_deg = etsi
|
||||
.cam
|
||||
.cam_parameters
|
||||
.basic_container
|
||||
.reference_position
|
||||
.latitude
|
||||
.as_deg();
|
||||
let lon_deg = etsi
|
||||
.cam
|
||||
.cam_parameters
|
||||
.basic_container
|
||||
.reference_position
|
||||
.longitude
|
||||
.as_deg();
|
||||
|
||||
if obu.speed.speed_value.is_unavailable() {
|
||||
info!(
|
||||
"Got CAM from {station_id} at {lat_deg:.3}°N, {lon_deg:.3}°E, speed unavailable",
|
||||
);
|
||||
} else {
|
||||
info!(
|
||||
"Got CAM from {station_id} at {lat_deg:.3}°N, {lon_deg:.3}°E, speed {:.2} km/h",
|
||||
obu.speed.speed_value.as_kmh()
|
||||
);
|
||||
}
|
||||
info!(
|
||||
"Got CAM from {} at {:.3}°N, {:.3}°E, speed {:.2} km/h",
|
||||
etsi.header.station_id.0,
|
||||
etsi.cam
|
||||
.cam_parameters
|
||||
.basic_container
|
||||
.reference_position
|
||||
.latitude
|
||||
.as_deg(),
|
||||
etsi.cam
|
||||
.cam_parameters
|
||||
.basic_container
|
||||
.reference_position
|
||||
.longitude
|
||||
.as_deg(),
|
||||
obu.speed.speed_value.as_kmh()
|
||||
);
|
||||
}
|
||||
HighFrequencyContainer::rsuContainerHighFrequency(rsu) => {
|
||||
info!(
|
||||
|
|
|
|||
|
|
@ -1,465 +0,0 @@
|
|||
//! C-ITS CAM Transmission
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use c_its_parser::gn as geonetworking;
|
||||
use c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions;
|
||||
use c_its_parser::standards::cdd_1_3_1_1::its_container;
|
||||
use c_its_parser::standards::extensions;
|
||||
|
||||
const PH_MAX_LENGHT_M: f32 = 500.;
|
||||
const PH_MAX_ITEMS: usize = 23;
|
||||
const PH_MIN_DIST_M: f32 = 20.;
|
||||
|
||||
/// Stand-alone CAM generator
|
||||
///
|
||||
/// Generates CAM messages from time and [`PosVel`] updates
|
||||
///
|
||||
/// # Send a message
|
||||
/// ```
|
||||
/// use esp32_cits_core::tx::GnItsPayload as _;
|
||||
///
|
||||
/// let (mut seq_no, mac_bytes) = {
|
||||
/// // unwrap is fine since [`esp_hal::efuse::MacAddress`] is just an `[u8; 6]`
|
||||
/// let mac_bytes = mac.as_bytes().try_into().unwrap();
|
||||
///
|
||||
/// (0, mac_bytes)
|
||||
/// };
|
||||
///
|
||||
/// let mut cam = {
|
||||
/// let station_id = make_station_id(mac);
|
||||
/// info!("V2X Station ID: {station_id}");
|
||||
///
|
||||
/// applogic::cam_tx::CamState::new(
|
||||
/// station_id,
|
||||
/// OWN_STATION_TYPE,
|
||||
/// OWN_VEHICLE_LENGTH,
|
||||
/// OWN_VEHICLE_WIDTH,
|
||||
/// )
|
||||
/// };
|
||||
///
|
||||
/// cam.update(time, fix.clone().into());
|
||||
/// match cam.encode_packet(mac_bytes, pos_vel, time, GN_IS_MOBILE, &mut seq_no) {
|
||||
/// Ok(data) => {
|
||||
/// if let Err(err) =
|
||||
/// radio::send_80211_bcast_frame(&mut wlan_iface, &mac_bytes.clone(), &data)
|
||||
/// {
|
||||
/// warn!("Failed to send 802.11 frame: {err}");
|
||||
/// }
|
||||
/// }
|
||||
/// Err(err) => warn!("Failed to create CAM: {err}"),
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct CamState {
|
||||
station_id: u32,
|
||||
station_type: its_container::StationType,
|
||||
|
||||
vehicle_length_dm: u16, // vehicle length is in 10cm steps
|
||||
vehicle_width_dm: u8, // vehicle width is in 10cm steps
|
||||
|
||||
time: chrono::DateTime<chrono::Utc>,
|
||||
pos_state: esp32_cits_core::PosVel,
|
||||
|
||||
/// database for path history, oldest item at the back of the vector
|
||||
path_history: Vec<PathPoint>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
struct PathPoint {
|
||||
pos: geo_types::Point,
|
||||
time: chrono::DateTime<chrono::Utc>,
|
||||
/// distance to previous point. Ignore on last point!
|
||||
dist_m: f32,
|
||||
}
|
||||
|
||||
impl PathPoint {
|
||||
fn new_from_pos(pos: &esp32_cits_core::PosVel, time: chrono::DateTime<chrono::Utc>) -> Self {
|
||||
Self {
|
||||
pos: pos.position,
|
||||
time,
|
||||
dist_m: 0.,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_from_pos_with_dist(
|
||||
pos: &esp32_cits_core::PosVel,
|
||||
time: chrono::DateTime<chrono::Utc>,
|
||||
dist_m: f32,
|
||||
) -> Self {
|
||||
Self {
|
||||
pos: pos.position,
|
||||
time,
|
||||
dist_m,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CamState {
|
||||
pub fn new(
|
||||
station_id: u32,
|
||||
station_type: c_its_parser::standards::extensions::ItsStationType,
|
||||
vehicle_length_dm: u16,
|
||||
vehicle_width_dm: u8,
|
||||
) -> Self {
|
||||
Self {
|
||||
station_id,
|
||||
station_type: station_type.into(),
|
||||
vehicle_length_dm,
|
||||
vehicle_width_dm,
|
||||
time: chrono::DateTime::<chrono::Utc>::default(),
|
||||
pos_state: esp32_cits_core::PosVel::default(),
|
||||
path_history: Vec::with_capacity(PH_MAX_ITEMS + 1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, time: chrono::DateTime<chrono::Utc>, pos: esp32_cits_core::PosVel) {
|
||||
self.time = time;
|
||||
self.pos_state = pos;
|
||||
|
||||
// Update path history
|
||||
Self::update_ph(&mut self.path_history, time, &self.pos_state);
|
||||
}
|
||||
|
||||
// this is a function, not a method for easier testing
|
||||
fn update_ph(
|
||||
path_history: &mut Vec<PathPoint>,
|
||||
time: chrono::DateTime<chrono::Utc>,
|
||||
pos: &esp32_cits_core::PosVel,
|
||||
) {
|
||||
// Update path history
|
||||
if path_history.is_empty() {
|
||||
// prime with current position if empty
|
||||
path_history.push(PathPoint::new_from_pos(pos, time));
|
||||
} else {
|
||||
// unwrap is fine since we checked for non-empty before
|
||||
let prev = path_history.first().unwrap();
|
||||
|
||||
// only add a point, if we moved more than PH_MIN_DIST_M
|
||||
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
|
||||
let dist = crate::geo_alg::haversine_dist(&prev.pos, &pos.position) as f32;
|
||||
if dist > PH_MIN_DIST_M {
|
||||
// new items shall be at the front of the vector
|
||||
path_history.insert(0, PathPoint::new_from_pos_with_dist(pos, time, dist));
|
||||
}
|
||||
}
|
||||
|
||||
// check length (PH_MAX_LENGHT_M and PH_MAX_ITEMS points max)
|
||||
if path_history.len() > PH_MAX_ITEMS {
|
||||
let _ = path_history.pop();
|
||||
}
|
||||
|
||||
let (_, cutoff_idx) =
|
||||
path_history
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold((0., None), |(len, cutoff_idx), (idx, item)| {
|
||||
if cutoff_idx.is_none() {
|
||||
let new_len = len + item.dist_m;
|
||||
let new_cutoff_idx = if len > PH_MAX_LENGHT_M {
|
||||
Some(idx)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
(new_len, new_cutoff_idx)
|
||||
} else {
|
||||
(len, cutoff_idx)
|
||||
}
|
||||
});
|
||||
if let Some(idx) = cutoff_idx {
|
||||
path_history.drain(idx..);
|
||||
}
|
||||
}
|
||||
|
||||
fn make_ph(&self) -> Vec<its_container::PathPoint> {
|
||||
let mut ph = alloc::vec![];
|
||||
|
||||
self.path_history.iter().fold(
|
||||
PathPoint::new_from_pos(&self.pos_state, self.time),
|
||||
|prev_point, pt| {
|
||||
// calculate delta position and append to `ph`
|
||||
|
||||
let delta_time = pt.time - prev_point.time;
|
||||
let delta_lat = pt.pos.y() - prev_point.pos.y();
|
||||
let delta_lon = pt.pos.x() - prev_point.pos.x();
|
||||
|
||||
// delta time is in 1/100 second / 10 ms steps
|
||||
let path_delta_time = its_container::PathDeltaTime(
|
||||
(delta_time.num_milliseconds().abs() / 10)
|
||||
.min(65_535) // clamp to max value
|
||||
.into(),
|
||||
);
|
||||
let delta_latitude = its_container::DeltaLatitude::from_deg(delta_lat);
|
||||
let delta_longitude = its_container::DeltaLongitude::from_deg(delta_lon);
|
||||
let delta_altitude = its_container::DeltaAltitude::unavailable();
|
||||
|
||||
let ppoint = its_container::PathPoint::new(
|
||||
its_container::DeltaReferencePosition::new(
|
||||
delta_latitude,
|
||||
delta_longitude,
|
||||
delta_altitude,
|
||||
),
|
||||
Some(path_delta_time),
|
||||
);
|
||||
|
||||
ph.push(ppoint);
|
||||
|
||||
*pt
|
||||
},
|
||||
);
|
||||
|
||||
ph
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn make_cam(&self) -> Result<cam_pdu_descriptions::CAM, alloc::string::String> {
|
||||
let header = its_container::ItsPduHeader::new(
|
||||
2,
|
||||
extensions::ItsMessageId::Cam.as_u8(),
|
||||
its_container::StationID(self.station_id),
|
||||
);
|
||||
|
||||
let generation_delta_time = {
|
||||
let its_timestamp = c_its_parser::time_utils::TimestampIts::from(self.time);
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let ts_mod = (its_timestamp.0 % 65_536) as u16;
|
||||
|
||||
cam_pdu_descriptions::GenerationDeltaTime(ts_mod)
|
||||
};
|
||||
|
||||
let heading = match self.pos_state.heading_deg {
|
||||
Some(value) => its_container::Heading {
|
||||
heading_value: its_container::HeadingValue::from_deg(value)
|
||||
.map_err(|err| alloc::format!("Failed to make HeadingValue: {err}"))?,
|
||||
heading_confidence: its_container::HeadingConfidence(127), // unavailable for now
|
||||
},
|
||||
None => its_container::Heading {
|
||||
heading_value: its_container::HeadingValue(0),
|
||||
heading_confidence: its_container::HeadingConfidence(127), // unavailable for now
|
||||
},
|
||||
};
|
||||
let speed = match self.pos_state.speed_mps {
|
||||
Some(value) => its_container::Speed {
|
||||
speed_value: its_container::SpeedValue::from_mps(value)
|
||||
.map_err(|err| alloc::format!("Failed to make SpeedValue: {err}"))?,
|
||||
speed_confidence: its_container::SpeedConfidence(127), // unavailable for now
|
||||
},
|
||||
None => its_container::Speed {
|
||||
speed_value: its_container::SpeedValue(0),
|
||||
speed_confidence: its_container::SpeedConfidence(127), // unavailable for now
|
||||
},
|
||||
};
|
||||
let reference_position = its_container::ReferencePosition {
|
||||
latitude: its_container::Latitude::from_deg(self.pos_state.position.y()),
|
||||
longitude: its_container::Longitude::from_deg(self.pos_state.position.x()),
|
||||
position_confidence_ellipse: its_container::PosConfidenceEllipse {
|
||||
semi_major_confidence: its_container::SemiAxisLength(4095), // unavailable for now
|
||||
semi_minor_confidence: its_container::SemiAxisLength(4095), // unavailable for now
|
||||
semi_major_orientation: its_container::HeadingValue::unavailable(),
|
||||
},
|
||||
altitude: its_container::Altitude {
|
||||
altitude_value: its_container::AltitudeValue(800_001), // unavailable for now
|
||||
altitude_confidence: its_container::AltitudeConfidence::unavailable,
|
||||
},
|
||||
};
|
||||
|
||||
let basic_container = cam_pdu_descriptions::BasicContainer::new(
|
||||
self.station_type.clone(),
|
||||
reference_position,
|
||||
);
|
||||
let high_frequency_container =
|
||||
cam_pdu_descriptions::HighFrequencyContainer::basicVehicleContainerHighFrequency(
|
||||
cam_pdu_descriptions::BasicVehicleContainerHighFrequency {
|
||||
heading,
|
||||
speed,
|
||||
drive_direction: its_container::DriveDirection::unavailable,
|
||||
vehicle_length: its_container::VehicleLength {
|
||||
vehicle_length_value: its_container::VehicleLengthValue(
|
||||
self.vehicle_length_dm,
|
||||
),
|
||||
vehicle_length_confidence_indication:
|
||||
its_container::VehicleLengthConfidenceIndication::unavailable,
|
||||
},
|
||||
vehicle_width: its_container::VehicleWidth(self.vehicle_width_dm),
|
||||
longitudinal_acceleration: its_container::LongitudinalAcceleration {
|
||||
longitudinal_acceleration_value:
|
||||
its_container::LongitudinalAccelerationValue::unavailable(),
|
||||
longitudinal_acceleration_confidence: its_container::AccelerationConfidence(
|
||||
102, // unavailable
|
||||
),
|
||||
},
|
||||
curvature: its_container::Curvature {
|
||||
curvature_value: its_container::CurvatureValue::unavailable(),
|
||||
curvature_confidence: its_container::CurvatureConfidence::unavailable,
|
||||
},
|
||||
curvature_calculation_mode:
|
||||
its_container::CurvatureCalculationMode::unavailable,
|
||||
yaw_rate: its_container::YawRate {
|
||||
yaw_rate_value: its_container::YawRateValue::unavailable(),
|
||||
yaw_rate_confidence: its_container::YawRateConfidence::unavailable,
|
||||
},
|
||||
acceleration_control: None,
|
||||
lane_position: None,
|
||||
steering_wheel_angle: None,
|
||||
lateral_acceleration: None,
|
||||
vertical_acceleration: None,
|
||||
performance_class: None,
|
||||
cen_dsrc_tolling_zone: None,
|
||||
},
|
||||
);
|
||||
let low_frequency_container =
|
||||
cam_pdu_descriptions::LowFrequencyContainer::basicVehicleContainerLowFrequency(
|
||||
cam_pdu_descriptions::BasicVehicleContainerLowFrequency {
|
||||
vehicle_role: its_container::VehicleRole::default,
|
||||
exterior_lights: its_container::ExteriorLights::default(),
|
||||
path_history: its_container::PathHistory(self.make_ph()),
|
||||
},
|
||||
);
|
||||
|
||||
let cam_parameters = cam_pdu_descriptions::CamParameters::new(
|
||||
basic_container,
|
||||
high_frequency_container,
|
||||
Some(low_frequency_container),
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(cam_pdu_descriptions::CAM::new(
|
||||
header,
|
||||
cam_pdu_descriptions::CoopAwareness::new(generation_delta_time, cam_parameters),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl esp32_cits_core::tx::GnItsPayload for CamState {
|
||||
fn make_eh(
|
||||
&self,
|
||||
address: [u8; 6],
|
||||
own_position: &esp32_cits_core::PosVel,
|
||||
time: chrono::DateTime<chrono::Utc>,
|
||||
_seq_no: &mut u16,
|
||||
) -> Result<
|
||||
(
|
||||
geonetworking::en302636_4_1::ExtendedHeader,
|
||||
Option<geonetworking::en302636_4_1::AreaType>,
|
||||
u8,
|
||||
),
|
||||
alloc::string::String,
|
||||
> {
|
||||
let station_type = geonetworking::en302636_4_1::StationType::try_from(self.station_type.0)
|
||||
.unwrap_or_default();
|
||||
|
||||
// CAM always uses SHB, hop-limit 1
|
||||
Ok((
|
||||
esp32_cits_core::tx::gn::make_shb_eh(address, station_type, own_position, time)?,
|
||||
None,
|
||||
1,
|
||||
))
|
||||
}
|
||||
|
||||
fn make_payload(
|
||||
&self,
|
||||
) -> Result<(Vec<u8>, c_its_parser::standards::extensions::ItsMessageId), alloc::string::String>
|
||||
{
|
||||
let msg = self.make_cam()?;
|
||||
let uper = msg.encode_to_uper()?;
|
||||
Ok((uper, c_its_parser::standards::extensions::ItsMessageId::Cam))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::vec;
|
||||
|
||||
use super::*;
|
||||
use crate::init_test_env_logger;
|
||||
|
||||
fn posstate_from_pos(position: geo_types::Point) -> esp32_cits_core::PosVel {
|
||||
esp32_cits_core::PosVel {
|
||||
position,
|
||||
heading_deg: None,
|
||||
speed_mps: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cam_path_history_update() {
|
||||
init_test_env_logger();
|
||||
|
||||
let time = chrono::DateTime::from_timestamp(0, 0).unwrap();
|
||||
let p1 = geo_types::Point::new(10., 53.5);
|
||||
let p2 = geo_types::Point::new(10., 53.5 + 0.000045); // ~5m further (< PH_MIN_DIST_M)
|
||||
let p3 = geo_types::Point::new(10., 53.5 + 0.00027); // ~30m further (> PH_MIN_DIST_M)
|
||||
|
||||
let ps1 = posstate_from_pos(p1);
|
||||
let pp1 = PathPoint::new_from_pos(&ps1, time);
|
||||
let ps2 = posstate_from_pos(p2);
|
||||
let pp2 = PathPoint::new_from_pos(&ps2, time);
|
||||
let ps3 = posstate_from_pos(p3);
|
||||
let pp3 = PathPoint::new_from_pos_with_dist(&ps3, time, 30.02267);
|
||||
|
||||
let mut path_history: Vec<PathPoint> = vec![];
|
||||
|
||||
// add first point, expect one point in PH
|
||||
CamState::update_ph(&mut path_history, time, &posstate_from_pos(p1));
|
||||
assert_eq!(1, path_history.len());
|
||||
assert_eq!(&pp1, path_history.first().unwrap());
|
||||
|
||||
// add insignificant point, expect still one point in PH
|
||||
CamState::update_ph(&mut path_history, time, &posstate_from_pos(p2));
|
||||
assert_eq!(1, path_history.len());
|
||||
assert_eq!(&pp1, path_history.first().unwrap());
|
||||
|
||||
// add significantly moved point, expect [pp3, pp1] vector
|
||||
CamState::update_ph(&mut path_history, time, &posstate_from_pos(p3));
|
||||
assert_eq!(2, path_history.len());
|
||||
assert_eq!(&pp3, path_history.first().unwrap());
|
||||
assert_eq!(&pp1, path_history.last().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cam_path_history_max_len() {
|
||||
init_test_env_logger();
|
||||
|
||||
let time = chrono::DateTime::from_timestamp(0, 0).unwrap();
|
||||
let p1 = geo_types::Point::new(10., 53.5);
|
||||
let p2 = geo_types::Point::new(10., 53.5 + 0.00023); // at ~25m from p1 (> PH_MIN_DIST_M)
|
||||
let p3 = geo_types::Point::new(10., 53.5 + 0.00445); // at ~495m from p1 (slightly less than PH_MAX_LENGHT_M)
|
||||
let p4 = geo_types::Point::new(10., 53.5 + 0.00477); // at ~530m from p1 (slightly more than PH_MAX_LENGHT_M)
|
||||
|
||||
let ps1 = posstate_from_pos(p1);
|
||||
let pp1 = PathPoint::new_from_pos(&ps1, time);
|
||||
let ps2 = posstate_from_pos(p2);
|
||||
let pp2 = PathPoint::new_from_pos(&ps2, time);
|
||||
let ps3 = posstate_from_pos(p3);
|
||||
let pp3 = PathPoint::new_from_pos(&ps3, time);
|
||||
let ps4 = posstate_from_pos(p4);
|
||||
let pp4 = PathPoint::new_from_pos(&ps4, time);
|
||||
|
||||
let mut path_history: Vec<PathPoint> = vec![];
|
||||
|
||||
// prime with p3, p4 (should give a length of ~470m)
|
||||
CamState::update_ph(&mut path_history, time, &posstate_from_pos(p4));
|
||||
CamState::update_ph(&mut path_history, time, &posstate_from_pos(p3));
|
||||
assert_eq!(2, path_history.len());
|
||||
|
||||
// Add p2, should drop p4 from vector because of > PH_MAX_LENGHT_M
|
||||
CamState::update_ph(&mut path_history, time, &posstate_from_pos(p2));
|
||||
assert_eq!(2, path_history.len());
|
||||
assert_eq!(p2, path_history.get(0).unwrap().pos);
|
||||
assert_eq!(469.24323, path_history.get(0).unwrap().dist_m);
|
||||
assert_eq!(p3, path_history.get(1).unwrap().pos);
|
||||
assert_eq!(35.582424, path_history.get(1).unwrap().dist_m);
|
||||
|
||||
// Add p1, should not drop anything from vector because still < PH_MAX_LENGHT_M
|
||||
CamState::update_ph(&mut path_history, time, &posstate_from_pos(p1));
|
||||
assert_eq!(3, path_history.len());
|
||||
assert_eq!(p1, path_history.get(0).unwrap().pos);
|
||||
assert_eq!(25.57487, path_history.get(0).unwrap().dist_m);
|
||||
assert_eq!(p2, path_history.get(1).unwrap().pos);
|
||||
assert_eq!(469.24323, path_history.get(1).unwrap().dist_m);
|
||||
assert_eq!(p3, path_history.get(2).unwrap().pos);
|
||||
assert_eq!(35.582424, path_history.get(2).unwrap().dist_m);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
//! C-ITS DENM Evaluation
|
||||
|
||||
use c_its_parser::standards::denm_2_2_1::denm_pdu_description;
|
||||
use log::info;
|
||||
|
||||
pub fn handle_denm(etsi: &denm_pdu_description::DENM) {
|
||||
let mgmt = &etsi.denm.management;
|
||||
|
||||
if mgmt.termination.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let (Some(sit), Some(loc)) = (&etsi.denm.situation, &etsi.denm.location) {
|
||||
// let timestamp: chrono::DateTime<chrono::Utc> = mgmt.reference_time.clone().into();
|
||||
let end_time = chrono::DateTime::<chrono::Utc>::from(mgmt.reference_time.clone())
|
||||
+ chrono::Duration::seconds(mgmt.validity_duration.0.into());
|
||||
let station_id = &mgmt.action_id.originating_station_id.0;
|
||||
let seq_num = mgmt.action_id.sequence_number.0;
|
||||
|
||||
let lat_deg = mgmt.event_position.latitude.as_deg();
|
||||
let lon_deg = mgmt.event_position.longitude.as_deg();
|
||||
|
||||
let heading = loc
|
||||
.event_position_heading
|
||||
.as_ref()
|
||||
.map(|v| v.value.as_deg());
|
||||
|
||||
// let direction = mgmt.traffic_direction;
|
||||
|
||||
let (cc, scc) = sit.event_type.cc_and_scc.to_u8_tuple();
|
||||
|
||||
if let Some(hdg) = heading {
|
||||
info!(
|
||||
"DENN: {station_id}/{seq_num} CC {cc}/{scc} at {lat_deg:.3}°N, {lon_deg:.3}°E, hdg {hdg:.1}° until {end_time:?}"
|
||||
);
|
||||
} else {
|
||||
info!(
|
||||
"DENN: {station_id}/{seq_num} CC {cc}/{scc} at {lat_deg:.3}°N, {lon_deg:.3}°E until {end_time:?}"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
info!("DENM: One of situation or location container not available");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,746 +1,4 @@
|
|||
//! Application Logic
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
use alloc::rc::Rc;
|
||||
#[cfg(feature = "spat")]
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use log::info;
|
||||
#[cfg(feature = "spat")]
|
||||
use log::warn;
|
||||
#[cfg(feature = "spat")]
|
||||
use log::{debug, error};
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
use crate::{applogic::v2x::map, geo_alg};
|
||||
|
||||
pub mod v2x;
|
||||
|
||||
#[cfg(feature = "cam")]
|
||||
pub mod cam;
|
||||
#[cfg(any(feature = "cam_tx", test))]
|
||||
pub mod cam_tx;
|
||||
#[cfg(feature = "denm")]
|
||||
pub mod denm;
|
||||
#[cfg(feature = "spat")]
|
||||
pub mod tlc;
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
const TLC_CACHE_LIFETIME: chrono::Duration = chrono::Duration::minutes(1);
|
||||
#[cfg(feature = "spat")]
|
||||
const PRUNE_INTERVAL: chrono::Duration = chrono::Duration::seconds(5);
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
const GLOSA_SEARCH_DIST_M: f32 = 400.;
|
||||
#[cfg(feature = "spat")]
|
||||
const GLOSA_SEARCH_ANGLE_PARAMS: [(f32, f32); 2] = [(400., 45.), (50., 180.)]; // far and near (distance, angle) tuple
|
||||
#[cfg(feature = "spat")]
|
||||
const GLOSA_LANE_HEADING_DIFF_DEG: f32 = 35.;
|
||||
#[cfg(feature = "spat")]
|
||||
const GLOSA_AT_LANE_OFFSET_M: f64 = 10.;
|
||||
#[cfg(feature = "spat")]
|
||||
const GLOSA_LANE_LEFT_OFFSET_M: f64 = 20.;
|
||||
#[cfg(feature = "spat")]
|
||||
const GLOSA_STOP_LINE_PASSED_M: f64 = 5.;
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct State {
|
||||
initialized: bool,
|
||||
time: chrono::NaiveDateTime,
|
||||
|
||||
#[allow(unused)]
|
||||
pos: esp32_cits_core::PosVel,
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
last_prune: chrono::NaiveDateTime,
|
||||
#[cfg(feature = "spat")]
|
||||
tlc_cache: tlc::Data,
|
||||
|
||||
// GLOSA
|
||||
#[cfg(feature = "spat")]
|
||||
#[allow(clippy::struct_field_names)]
|
||||
glosa_state: GlosaState,
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
#[derive(Debug)]
|
||||
pub enum GlosaOutput {
|
||||
Error(alloc::string::String),
|
||||
Searching(GlosaSearching),
|
||||
Found(GlosaFound),
|
||||
Locked(GlosaSignalInfo),
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct GlosaSearching {
|
||||
pub known_intersection_ids: Vec<u16>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct GlosaFound {
|
||||
pub intersection_id: u16,
|
||||
pub approach_id: Option<u8>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
impl From<&TlData> for GlosaFound {
|
||||
fn from(value: &TlData) -> Self {
|
||||
Self {
|
||||
intersection_id: value.intersection_id,
|
||||
approach_id: value.approach_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct GlosaSignalInfo {
|
||||
pub intersection_id: u16,
|
||||
pub signal_groups: Vec<SignalGroupData>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct SignalGroupData {
|
||||
pub phase: SignalPhase,
|
||||
pub maneuver: map::Maneuvers,
|
||||
pub timing: Option<TimingData>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
#[allow(clippy::struct_field_names)]
|
||||
pub struct TimingData {
|
||||
pub min_end_sec: i16,
|
||||
pub likely_end_sec: Option<i16>,
|
||||
pub max_end_sec: Option<i16>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
impl TimingData {
|
||||
pub fn new(value: &map::SignalGroup, now: chrono::DateTime<chrono::Utc>) -> Option<Self> {
|
||||
let min = value
|
||||
.min_end_time()
|
||||
.map(|val| Self::time_to_duration_sec(now, val));
|
||||
let likely_end_sec = value
|
||||
.likely_time()
|
||||
.map(|val| Self::time_to_duration_sec(now, val));
|
||||
let max_end_sec = value
|
||||
.max_end_time()
|
||||
.map(|val| Self::time_to_duration_sec(now, val));
|
||||
|
||||
min.map(|min_end_sec| Self {
|
||||
min_end_sec,
|
||||
likely_end_sec,
|
||||
max_end_sec,
|
||||
})
|
||||
}
|
||||
|
||||
fn time_to_duration_sec(
|
||||
now: chrono::DateTime<chrono::Utc>,
|
||||
time_point: chrono::DateTime<chrono::Utc>,
|
||||
) -> i16 {
|
||||
use core::ops::Sub as _;
|
||||
|
||||
use num_traits::Float as _;
|
||||
|
||||
let dur = time_point.sub(now).as_seconds_f32();
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let duration_sec = dur.round() as i16;
|
||||
|
||||
duration_sec
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub enum SignalPhase {
|
||||
#[default]
|
||||
Unknown,
|
||||
Red,
|
||||
RedYellow,
|
||||
Green,
|
||||
Yellow,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
pub struct GnssFix {
|
||||
pub time: Option<chrono::NaiveDateTime>,
|
||||
pub latitude_deg: f64,
|
||||
pub longitude_deg: f64,
|
||||
pub heading_deg: Option<f32>,
|
||||
pub speed_mps: Option<f32>,
|
||||
}
|
||||
|
||||
impl From<&GnssFix> for esp32_cits_core::PosVel {
|
||||
fn from(value: &GnssFix) -> Self {
|
||||
let position = geo_types::Point::new(value.longitude_deg, value.latitude_deg);
|
||||
|
||||
Self {
|
||||
position,
|
||||
heading_deg: value.heading_deg,
|
||||
speed_mps: value.speed_mps,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<GnssFix> for esp32_cits_core::PosVel {
|
||||
fn from(value: GnssFix) -> Self {
|
||||
(&value).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
impl core::fmt::Display for SignalPhase {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
SignalPhase::Unknown => write!(f, "__"),
|
||||
SignalPhase::Red => write!(f, "RE"),
|
||||
SignalPhase::RedYellow => write!(f, "RY"),
|
||||
SignalPhase::Green => write!(f, "GN"),
|
||||
SignalPhase::Yellow => write!(f, "YE"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
impl From<c_its_parser::standards::dsrc_2_2_1::etsi_its_dsrc::MovementPhaseState> for SignalPhase {
|
||||
fn from(value: c_its_parser::standards::dsrc_2_2_1::etsi_its_dsrc::MovementPhaseState) -> Self {
|
||||
use c_its_parser::standards::dsrc_2_2_1::etsi_its_dsrc::MovementPhaseState;
|
||||
|
||||
match value {
|
||||
MovementPhaseState::stop_And_Remain => SignalPhase::Red,
|
||||
MovementPhaseState::pre_Movement => SignalPhase::RedYellow,
|
||||
MovementPhaseState::permissive_Movement_Allowed
|
||||
| MovementPhaseState::protected_Movement_Allowed => SignalPhase::Green,
|
||||
MovementPhaseState::permissive_clearance | MovementPhaseState::protected_clearance => {
|
||||
SignalPhase::Yellow
|
||||
}
|
||||
_ => SignalPhase::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
#[derive(Debug, Default)]
|
||||
enum GlosaState {
|
||||
#[default]
|
||||
Idle,
|
||||
Locked(TlData),
|
||||
}
|
||||
|
||||
/// Where we are and pre-processed information about the current intersection
|
||||
#[cfg(feature = "spat")]
|
||||
#[derive(Debug)]
|
||||
struct TlData {
|
||||
intersection_id: u16, // mostly for debug printing
|
||||
intersection: Rc<core::cell::RefCell<v2x::map::Intersection>>,
|
||||
|
||||
lane_id: u8,
|
||||
approach_id: Option<u8>,
|
||||
|
||||
signal_groups: Vec<u8>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
initialized: false,
|
||||
time: chrono::NaiveDateTime::default(),
|
||||
pos: esp32_cits_core::PosVel::default(),
|
||||
#[cfg(feature = "spat")]
|
||||
last_prune: chrono::NaiveDateTime::default(),
|
||||
#[cfg(feature = "spat")]
|
||||
tlc_cache: tlc::Data::new(TLC_CACHE_LIFETIME),
|
||||
#[cfg(feature = "spat")]
|
||||
glosa_state: GlosaState::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[cfg(feature = "esp")]
|
||||
pub fn update_with_gpsfix(&mut self, fix: &GnssFix) {
|
||||
self.pos = fix.into();
|
||||
if let Some(time) = fix.time {
|
||||
self.time = time;
|
||||
|
||||
if !self.initialized {
|
||||
info!("GNSS fix: {}", self.print_fix());
|
||||
}
|
||||
self.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn print_fix(&self) -> alloc::string::String {
|
||||
alloc::format!(
|
||||
"{:?}, {:.4} N, {:.4} E, {:.0}°, {:.2} m/s",
|
||||
self.time,
|
||||
self.pos.position.y(),
|
||||
self.pos.position.x(),
|
||||
self.pos.heading_deg.unwrap_or_default(),
|
||||
self.pos.speed_mps.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn prune(&mut self) {
|
||||
#[cfg(feature = "spat")]
|
||||
if self.last_prune + PRUNE_INTERVAL < self.time {
|
||||
self.tlc_cache.prune(self.time);
|
||||
|
||||
info!("Known int.: {:?}", self.tlc_cache.known_intersections());
|
||||
|
||||
self.last_prune = self.time;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
pub fn handle_mapem(&mut self, etsi: &tlc::MAPEM) {
|
||||
self.tlc_cache.handle_mapem(self.time, etsi);
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
pub fn handle_spatem(&mut self, etsi: &tlc::SPATEM) {
|
||||
self.tlc_cache.handle_spatem(self.time, etsi);
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn run_glosa(&mut self) -> GlosaOutput {
|
||||
let Some(own_heading) = self.pos.heading_deg else {
|
||||
use alloc::string::ToString;
|
||||
|
||||
return GlosaOutput::Error("Heading unknown".to_string());
|
||||
};
|
||||
|
||||
let known_intersection_ids = self.tlc_cache.known_intersections();
|
||||
let (state_update, output_data) = match &self.glosa_state {
|
||||
// search for upcoming intersection
|
||||
GlosaState::Idle => {
|
||||
debug!(
|
||||
"GLOSA: Searching with {} known intersections",
|
||||
known_intersection_ids.len()
|
||||
);
|
||||
|
||||
if let Some(search_result) =
|
||||
Self::find_intersection(&self.pos.position, own_heading, &self.tlc_cache)
|
||||
{
|
||||
info!(
|
||||
"GLOSA: Approach has signal groups: {:?}",
|
||||
search_result.signal_groups
|
||||
);
|
||||
|
||||
let glosa_found_data = (&search_result).into();
|
||||
(
|
||||
Some(GlosaState::Locked(search_result)),
|
||||
GlosaOutput::Found(glosa_found_data),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
None,
|
||||
GlosaOutput::Searching(GlosaSearching {
|
||||
known_intersection_ids,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
GlosaState::Locked(info) => {
|
||||
debug!(
|
||||
"GLOSA: Re-evaluating intersection {:4} (at lane {:3})",
|
||||
info.intersection_id, info.lane_id
|
||||
);
|
||||
|
||||
// ensure that "our" intersection is kept in TLC cache
|
||||
if self
|
||||
.tlc_cache
|
||||
.keep_intersection(info.intersection_id, self.time)
|
||||
.is_err()
|
||||
{
|
||||
info!("GLOSA: Intersection not known any more");
|
||||
(
|
||||
Some(GlosaState::Idle),
|
||||
GlosaOutput::Searching(GlosaSearching {
|
||||
known_intersection_ids,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
// determine if intersection is left
|
||||
let intersection = info.intersection.borrow();
|
||||
if Self::is_intersection_left(
|
||||
&intersection,
|
||||
info.lane_id,
|
||||
info.approach_id,
|
||||
&self.pos.position,
|
||||
) {
|
||||
info!("GLOSA: Intersection was left -> back to idle");
|
||||
(
|
||||
Some(GlosaState::Idle),
|
||||
GlosaOutput::Searching(GlosaSearching {
|
||||
known_intersection_ids,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
// get signal groups which are in our approach
|
||||
let mut approach_sig_grps = intersection
|
||||
.signal_groups()
|
||||
.iter()
|
||||
.filter(|i| {
|
||||
info.signal_groups
|
||||
.iter()
|
||||
.find(|sg_id| **sg_id == i.id())
|
||||
.is_some()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// sort signal groups by maneuver
|
||||
approach_sig_grps.sort_by_key(|v| v.maneuvers());
|
||||
|
||||
// display signal phases
|
||||
let sig_grps = approach_sig_grps
|
||||
.iter()
|
||||
.filter_map(|sig| {
|
||||
if let Some(maneuver) = sig.maneuvers() {
|
||||
let phase = sig
|
||||
.phase()
|
||||
.map(core::convert::Into::into)
|
||||
.unwrap_or_default();
|
||||
let timing = TimingData::new(sig, self.time.and_utc());
|
||||
|
||||
Some(SignalGroupData {
|
||||
phase,
|
||||
maneuver,
|
||||
timing,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
(
|
||||
None,
|
||||
GlosaOutput::Locked(GlosaSignalInfo {
|
||||
intersection_id: intersection.id(),
|
||||
signal_groups: sig_grps,
|
||||
}),
|
||||
) // keep state, return signal groups
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// can't update the state from within the match statement, so do it here
|
||||
if let Some(new) = state_update {
|
||||
self.glosa_state = new;
|
||||
}
|
||||
|
||||
output_data
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn find_intersection(
|
||||
own_pos: &geo_types::Point,
|
||||
own_heading: f32,
|
||||
tlc_cache: &tlc::Data,
|
||||
) -> Option<TlData> {
|
||||
// filter known intersections by distance (and bearing)
|
||||
let upcoming_intersections = tlc_cache.intersections_matching(|i| {
|
||||
let int_pos = i.center();
|
||||
|
||||
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
|
||||
let distance = geo_alg::haversine_dist(own_pos, &int_pos) as f32;
|
||||
let bearing = geo_alg::haversine_bearing(own_pos, &int_pos);
|
||||
let bearing_diff = geo_alg::deg_180(own_heading - bearing);
|
||||
|
||||
distance < GLOSA_SEARCH_DIST_M && bearing_diff.abs() < Self::get_search_angle(distance)
|
||||
});
|
||||
|
||||
info!(
|
||||
"GLOSA: upcoming int. count: {}",
|
||||
upcoming_intersections.len()
|
||||
);
|
||||
|
||||
// determine relevant approach
|
||||
for int in upcoming_intersections {
|
||||
let intersection = int.borrow();
|
||||
let lanes = intersection.ingress_lanes();
|
||||
|
||||
let own_pos_cart = geo_alg::lonlat_to_dcart(&intersection.ref_point(), own_pos);
|
||||
|
||||
// find lanes with matching heading
|
||||
let relevant_lanes = lanes
|
||||
.iter()
|
||||
.filter(|i| {
|
||||
let lane_heading = i.get_heading();
|
||||
let heading_diff = geo_alg::deg_180(own_heading - lane_heading);
|
||||
|
||||
debug!(
|
||||
" - Lane {:4}/{:03} has heading {lane_heading:4.0} -> diff {heading_diff:4.0}°",
|
||||
intersection.id(),
|
||||
i.id()
|
||||
);
|
||||
|
||||
heading_diff.abs() < GLOSA_LANE_HEADING_DIFF_DEG
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
info!(
|
||||
"GLOSA: Int {:4} has {} relevant lanes",
|
||||
intersection.id(),
|
||||
relevant_lanes.len()
|
||||
);
|
||||
for lane in &relevant_lanes {
|
||||
info!(
|
||||
" - lane {:03} from appr. {:?}, usages {:?}",
|
||||
lane.id(),
|
||||
lane.approach_id(),
|
||||
lane.usages()
|
||||
);
|
||||
}
|
||||
|
||||
// determine in which lane/ approach we are
|
||||
let current_lane = relevant_lanes.iter().find(|lane| {
|
||||
#[cfg(feature = "spat_debug")]
|
||||
info!("GLOSA: Searching for current lane");
|
||||
match geo_alg::LinePosition::for_ingress_lane(own_pos_cart, lane.nodes()) {
|
||||
Ok(pos) => {
|
||||
#[cfg(feature = "spat_debug")]
|
||||
info!(
|
||||
" - Lane {:4}/{:03} {:?} with {:.0} m offset",
|
||||
intersection.id(),
|
||||
lane.id(),
|
||||
pos.semantic,
|
||||
pos.perpendicular_dist,
|
||||
);
|
||||
|
||||
if pos.perpendicular_dist < GLOSA_AT_LANE_OFFSET_M
|
||||
&& pos.semantic == geo_alg::LinePositionType::InBetween
|
||||
{
|
||||
info!(
|
||||
"GLOSA: At lane {:4}/{:03} -> locking intersection",
|
||||
intersection.id(),
|
||||
lane.id()
|
||||
);
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Lane {:04}: Failed to determine position: {err}", lane.id());
|
||||
false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(lane) = current_lane {
|
||||
// we found some lane which we are "inside" -> lane search can be aborted here
|
||||
// but we should extract some information which is needed for future processing first
|
||||
|
||||
// collect all signal groups from the current approach
|
||||
let mut signal_groups = if let Some(own_approach) = lane.approach_id() {
|
||||
let approach_lanes = relevant_lanes.iter().filter(|lane| {
|
||||
if let Some(approach) = lane.approach_id() {
|
||||
approach == own_approach
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
approach_lanes
|
||||
.flat_map(|lane| {
|
||||
lane.connections()
|
||||
.iter()
|
||||
.filter_map(v2x::map::Connection::signal_group)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
error!(
|
||||
"GLOSA: Lane has no approach ID, can't determine all possibly relevant signal groups"
|
||||
);
|
||||
// just fall back to only the current lane's signal groups
|
||||
lane.connections()
|
||||
.iter()
|
||||
.filter_map(v2x::map::Connection::signal_group)
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
signal_groups.sort_unstable();
|
||||
signal_groups.dedup();
|
||||
|
||||
return Some(TlData {
|
||||
intersection_id: intersection.id(),
|
||||
intersection: int.clone(),
|
||||
lane_id: lane.id(),
|
||||
approach_id: lane.approach_id(),
|
||||
signal_groups,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
fn get_search_angle(distance: f32) -> f32 {
|
||||
let (dist_far, angle_far) = GLOSA_SEARCH_ANGLE_PARAMS[0];
|
||||
let (dist_near, angle_near) = GLOSA_SEARCH_ANGLE_PARAMS[1];
|
||||
|
||||
if distance > dist_far {
|
||||
angle_far
|
||||
} else if distance < dist_near {
|
||||
angle_near
|
||||
} else {
|
||||
// interpolate angle from near to far
|
||||
let diff_dist = dist_far - dist_near;
|
||||
let diff_angle = angle_far - angle_near;
|
||||
|
||||
angle_near + (diff_angle / diff_dist) * (distance - dist_near)
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if intersection was left
|
||||
///
|
||||
/// TODO: return if left via stop line or otherwise
|
||||
#[cfg(feature = "spat")]
|
||||
fn is_intersection_left(
|
||||
intersection: &v2x::map::Intersection,
|
||||
lane_id: u8,
|
||||
approach_id: Option<u8>,
|
||||
own_pos: &geo_types::Point,
|
||||
) -> bool {
|
||||
// get all lanes from the current approach
|
||||
let lanes = if let Some(approach) = approach_id {
|
||||
intersection.approach_lanes(approach)
|
||||
} else if let Some(lane) = intersection.lane(lane_id) {
|
||||
alloc::vec![lane]
|
||||
} else {
|
||||
alloc::vec![]
|
||||
};
|
||||
|
||||
// find minimal distance to any lane where we are "inside"
|
||||
let own_pos_cart = geo_alg::lonlat_to_dcart(&intersection.ref_point(), own_pos);
|
||||
let min_perp_dist = lanes.iter().fold(f64::MAX, |min_dist, i| {
|
||||
match geo_alg::LinePosition::for_ingress_lane(own_pos_cart, i.nodes()) {
|
||||
Ok(pos) => {
|
||||
// check if we left the lane via the stop line, but only count stop line as passed after X meters
|
||||
if pos.semantic == geo_alg::LinePositionType::InBetween
|
||||
|| (pos.semantic == geo_alg::LinePositionType::BeforeStart
|
||||
&& pos.parallel_dist.abs() < GLOSA_STOP_LINE_PASSED_M)
|
||||
{
|
||||
// still at the lane -> update min_dist
|
||||
pos.perpendicular_dist
|
||||
} else {
|
||||
// not at the lane -> take old min_dist
|
||||
min_dist
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Lane {:04}: Failed to determine position: {err}", i.id());
|
||||
min_dist
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
min_perp_dist > GLOSA_LANE_LEFT_OFFSET_M
|
||||
}
|
||||
|
||||
// -- getters --
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn initialized(&self) -> bool {
|
||||
self.initialized
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn time(&self) -> chrono::prelude::NaiveDateTime {
|
||||
self.time
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn pos(&self) -> &esp32_cits_core::PosVel {
|
||||
&self.pos
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::init_test_env_logger;
|
||||
#[cfg(feature = "spat")]
|
||||
use crate::testdata::{MAPEM_HH_560, MAPEM_HH_1328};
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
#[test]
|
||||
fn glosa() {
|
||||
init_test_env_logger();
|
||||
|
||||
// prime TLC cache
|
||||
let mut state = State::new();
|
||||
let time_now = chrono::NaiveDateTime::default();
|
||||
|
||||
let etsi = if let c_its_parser::ItsMessage::Mapem {
|
||||
geonetworking: _,
|
||||
transport: _,
|
||||
etsi,
|
||||
} =
|
||||
c_its_parser::de::decode(MAPEM_HH_1328, c_its_parser::Headers::None).unwrap()
|
||||
{
|
||||
etsi
|
||||
} else {
|
||||
panic!("Unexpected C-ITS message")
|
||||
};
|
||||
state.tlc_cache.handle_mapem(time_now, &etsi);
|
||||
|
||||
let etsi = if let c_its_parser::ItsMessage::Mapem {
|
||||
geonetworking: _,
|
||||
transport: _,
|
||||
etsi,
|
||||
} =
|
||||
c_its_parser::de::decode(MAPEM_HH_560, c_its_parser::Headers::None).unwrap()
|
||||
{
|
||||
etsi
|
||||
} else {
|
||||
panic!("Unexpected C-ITS message")
|
||||
};
|
||||
state.tlc_cache.handle_mapem(time_now, &etsi);
|
||||
|
||||
// test GLOSA
|
||||
let own_pos_infront = geo_types::Point::new(9.9752864, 53.5564028);
|
||||
let own_pos_inside = geo_types::Point::new(9.9760764, 53.5563621);
|
||||
let own_pos_passed = geo_types::Point::new(9.977217, 53.5560602);
|
||||
let own_heading = 90.;
|
||||
|
||||
state.pos.heading_deg = Some(own_heading);
|
||||
|
||||
state.pos.position = own_pos_infront;
|
||||
state.run_glosa();
|
||||
assert!(matches!(state.glosa_state, GlosaState::Idle));
|
||||
|
||||
state.pos.position = own_pos_inside;
|
||||
state.run_glosa();
|
||||
assert!(matches!(state.glosa_state, GlosaState::Locked(_)));
|
||||
if let GlosaState::Locked(info) = &state.glosa_state {
|
||||
assert_eq!(1328, info.intersection_id);
|
||||
assert!(info.lane_id == 1 || info.lane_id == 2);
|
||||
}
|
||||
|
||||
state.pos.position = own_pos_inside;
|
||||
state.run_glosa();
|
||||
assert!(matches!(state.glosa_state, GlosaState::Locked(_)));
|
||||
if let GlosaState::Locked(info) = &state.glosa_state {
|
||||
assert_eq!(1328, info.intersection_id);
|
||||
assert!(info.lane_id == 1 || info.lane_id == 2);
|
||||
}
|
||||
|
||||
state.pos.position = own_pos_passed;
|
||||
state.run_glosa();
|
||||
assert!(matches!(state.glosa_state, GlosaState::Idle));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,117 +1,22 @@
|
|||
//! C-ITS Traffic Light Data (MAPEM/ SPATEM) Evaluation/ Cache
|
||||
//! C-ITS Traffic Light Data (MAPEM/ SPATEM) Evaluation
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub use c_its_parser::standards::mapem_2_2_1::mapem_pdu_descriptions::MAPEM;
|
||||
pub use c_its_parser::standards::spatem_2_2_1::spatem_pdu_descriptions::SPATEM;
|
||||
use chrono::Datelike;
|
||||
#[cfg(all(target_arch = "riscv32", feature = "spat_debug"))]
|
||||
use c_its_parser::standards::{
|
||||
mapem_2_2_1::mapem_pdu_descriptions, spatem_2_2_1::spatem_pdu_descriptions,
|
||||
};
|
||||
use esp_println::println;
|
||||
|
||||
use super::v2x::map;
|
||||
use crate::cache::{Cache, CacheError};
|
||||
|
||||
pub struct Data {
|
||||
intersections: Cache<u16, map::Intersection>,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub fn new(cache_lifetime: chrono::Duration) -> Self {
|
||||
Self {
|
||||
intersections: Cache::<u16, map::Intersection>::new(cache_lifetime),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn known_intersections(&self) -> Vec<u16> {
|
||||
self.intersections.keys()
|
||||
}
|
||||
|
||||
pub fn intersections_matching<P>(
|
||||
&self,
|
||||
predicate: P,
|
||||
) -> Vec<Rc<core::cell::RefCell<map::Intersection>>>
|
||||
where
|
||||
P: Fn(&map::Intersection) -> bool,
|
||||
{
|
||||
self.intersections.get_filtered(predicate)
|
||||
}
|
||||
|
||||
pub fn handle_mapem(&mut self, time_now: chrono::NaiveDateTime, etsi: &MAPEM) {
|
||||
let layer_id = etsi.map.layer_id.as_ref().map(|v| v.0);
|
||||
if let Some(map) = &etsi.map.intersections {
|
||||
for geom in &map.0 {
|
||||
let int_id = geom.id.id.0;
|
||||
|
||||
if self.intersections.contains(&int_id) {
|
||||
// update with new map layers
|
||||
|
||||
// we can ignore the result, since we're sure the item exists
|
||||
let _ = self.intersections.update(time_now, int_id, |i| {
|
||||
if !i.is_complete() {
|
||||
i.add_layer(layer_id, geom);
|
||||
|
||||
#[cfg(feature = "spat_debug")]
|
||||
if i.is_complete() {
|
||||
Self::pretty_print_intersection(&i);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// create new intersection
|
||||
let intersection = map::Intersection::new(layer_id, geom);
|
||||
|
||||
#[cfg(feature = "spat_debug")]
|
||||
if intersection.is_complete() {
|
||||
Self::pretty_print_intersection(&intersection);
|
||||
}
|
||||
|
||||
// we can ignore the result, since we're sure the item does not exist
|
||||
let _ = self.intersections.insert(time_now, intersection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat_debug")]
|
||||
fn pretty_print_intersection(intersection: &map::Intersection) {
|
||||
println!("TLC: {intersection}");
|
||||
println!("TLC: Signal Groups");
|
||||
for grp in intersection.signal_groups() {
|
||||
println!(" {grp}");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prune(&mut self, time_now: chrono::NaiveDateTime) {
|
||||
self.intersections.prune(time_now);
|
||||
}
|
||||
|
||||
/// Bumps the timestamp of a certain intersection to keep it cached
|
||||
pub fn keep_intersection(
|
||||
&mut self,
|
||||
intersection_id: u16,
|
||||
time_now: chrono::NaiveDateTime,
|
||||
) -> Result<(), CacheError<u16>> {
|
||||
self.intersections.update(time_now, intersection_id, |_| {})
|
||||
}
|
||||
|
||||
pub fn handle_spatem(&mut self, time_now: chrono::NaiveDateTime, etsi: &SPATEM) {
|
||||
for int in &etsi.spat.intersections.0 {
|
||||
let int_id = int.id.id.0;
|
||||
|
||||
if self.intersections.contains(&int_id) {
|
||||
// update with SPAT data
|
||||
|
||||
// we can ignore the result, since we're sure the item exists
|
||||
let _ = self.intersections.update(time_now, int_id, |i| {
|
||||
if i.is_complete() {
|
||||
// add SPAT information to intersection
|
||||
i.add_spat(int, time_now.year());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// drop message since we don't have (full) MAP yet
|
||||
}
|
||||
pub fn handle_mapem(etsi: &mapem_pdu_descriptions::MAPEM) {
|
||||
// TODO: proof of concept only -- replace with actual evaluation
|
||||
if let Some(map) = &etsi.map.intersections {
|
||||
for int in &map.0 {
|
||||
println!("got MAPEM for intersection {}", int.id.id.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_spatem(etsi: &spatem_pdu_descriptions::SPATEM) {
|
||||
// TODO: proof of concept only -- replace with actual evaluation
|
||||
for int in &etsi.spat.intersections.0 {
|
||||
println!("got SPATEM for intersection {}", int.id.id.0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +0,0 @@
|
|||
//! Types for V2X data handling
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
pub mod map;
|
||||
416
src/ble.rs
416
src/ble.rs
|
|
@ -1,416 +0,0 @@
|
|||
#![allow(
|
||||
clippy::needless_borrows_for_generic_args,
|
||||
reason = "gatt_service creates false positives"
|
||||
)]
|
||||
|
||||
use core::cell::RefCell;
|
||||
|
||||
use critical_section::Mutex;
|
||||
use crossbeam_queue::ArrayQueue;
|
||||
use esp_backtrace as _;
|
||||
use log::{info, warn};
|
||||
use trouble_host::prelude::*;
|
||||
|
||||
use crate::io;
|
||||
|
||||
pub const CONTROLLER_SLOTS: usize = 20;
|
||||
const CONNECTIONS_MAX: usize = 1;
|
||||
const L2CAP_CHANNELS_MAX: usize = 1;
|
||||
|
||||
pub static CLIENT_CONNECTED: Mutex<RefCell<bool>> = Mutex::new(RefCell::new(false));
|
||||
pub static TX_DATA: Mutex<RefCell<Option<ArrayQueue<BleSendable>>>> =
|
||||
Mutex::new(RefCell::new(None));
|
||||
pub static RX_DATA: Mutex<RefCell<Option<io::msg::PositionState>>> = Mutex::new(RefCell::new(None));
|
||||
|
||||
pub enum BleSendable {
|
||||
#[cfg(feature = "cam")]
|
||||
CamRaw(io::msg::RawMsgRx),
|
||||
#[cfg(feature = "cam")]
|
||||
Cam(io::msg::CamEvent),
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
DenmRaw(io::msg::RawMsgRx),
|
||||
#[cfg(feature = "denm")]
|
||||
Denm(io::msg::DenmEvent),
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
MapemRaw(io::msg::RawMsgRx),
|
||||
#[cfg(feature = "spat")]
|
||||
SpatemRaw(io::msg::RawMsgRx),
|
||||
}
|
||||
|
||||
#[gatt_server]
|
||||
struct Server {
|
||||
c_its_events: CITSEvents,
|
||||
}
|
||||
|
||||
#[gatt_service(uuid = "c0b70000-d4f4-4000-ada8-f99a02ee315c")]
|
||||
struct CITSEvents {
|
||||
#[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "Position", read, value = "PositionState proto msg", type = &'static str)]
|
||||
#[characteristic(uuid = "c0b70001-d4f4-4001-ada8-f99a02ee315c", write)]
|
||||
position: [u8; 27],
|
||||
|
||||
#[cfg(any(feature = "cam", feature = "denm", feature = "spat"))]
|
||||
#[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "Raw_Notify", read, value = "Raw Message Notification", type = &'static str)]
|
||||
#[characteristic(uuid = "c0b70001-d4f4-4002-ada8-f99a02ee315c", read, notify, value = [0u8; io::msg::RawNotify::PROTO_SIZE])]
|
||||
raw_notify: [u8; io::msg::RawNotify::PROTO_SIZE],
|
||||
|
||||
#[cfg(feature = "cam")]
|
||||
// #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "CAM", read, value = "RawMsgRx proto msg", type = &'static str)]
|
||||
#[characteristic(uuid = "c0b70001-d4f4-4003-ada8-f99a02ee315c", read, value = [0u8; io::msg::RawMsgRx::PROTO_SIZE])]
|
||||
cam_raw: [u8; io::msg::RawMsgRx::PROTO_SIZE],
|
||||
#[cfg(feature = "cam")]
|
||||
#[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "CAM_Event", read, value = "CamEvent proto msg", type = &'static str)]
|
||||
#[characteristic(uuid = "c0b70001-d4f4-4004-ada8-f99a02ee315c", read, notify, value = [0u8; io::msg::CamEvent::PROTO_SIZE])]
|
||||
cam_event: [u8; io::msg::CamEvent::PROTO_SIZE],
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
// #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "DENM", read, value = "RawMsgRx proto msg", type = &'static str)]
|
||||
#[characteristic(uuid = "c0b70001-d4f4-4005-ada8-f99a02ee315c", read, value = [0u8; io::msg::RawMsgRx::PROTO_SIZE])]
|
||||
denm_raw: [u8; io::msg::RawMsgRx::PROTO_SIZE],
|
||||
#[cfg(feature = "denm")]
|
||||
#[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "DENM_Event", read, value = "DenmEvent proto msg", type = &'static str)]
|
||||
#[characteristic(uuid = "c0b70001-d4f4-4006-ada8-f99a02ee315c", read, notify, value = [0u8; io::msg::DenmEvent::PROTO_SIZE])]
|
||||
denm_event: [u8; io::msg::DenmEvent::PROTO_SIZE],
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
// #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "MAPEM", read, value = "RawMsgRx proto msg", type = &'static str)]
|
||||
#[characteristic(uuid = "c0b70001-d4f4-4007-ada8-f99a02ee315c", read, value = [0u8; io::msg::RawMsgRx::PROTO_SIZE])]
|
||||
mapem_raw: [u8; io::msg::RawMsgRx::PROTO_SIZE],
|
||||
#[cfg(feature = "spat")]
|
||||
// #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "SPATEM", read, value = "RawMsgRx proto msg", type = &'static str)]
|
||||
#[characteristic(uuid = "c0b70001-d4f4-4008-ada8-f99a02ee315c", read, value = [0u8; io::msg::RawMsgRx::PROTO_SIZE])]
|
||||
spatem_raw: [u8; io::msg::RawMsgRx::PROTO_SIZE],
|
||||
}
|
||||
|
||||
/// Creates the BLE controller
|
||||
pub fn make_controller(
|
||||
bt_peripheral: esp_hal::peripherals::BT<'static>,
|
||||
) -> bt_hci::controller::ExternalController<
|
||||
esp_radio::ble::controller::BleConnector<'static>,
|
||||
CONTROLLER_SLOTS,
|
||||
> {
|
||||
critical_section::with(|cs| {
|
||||
TX_DATA.borrow(cs).replace(Some(ArrayQueue::new(2)));
|
||||
});
|
||||
|
||||
let transport = esp_radio::ble::controller::BleConnector::new(
|
||||
bt_peripheral,
|
||||
esp_radio::ble::Config::default(),
|
||||
)
|
||||
.unwrap();
|
||||
bt_hci::controller::ExternalController::<_, { CONTROLLER_SLOTS }>::new(transport)
|
||||
}
|
||||
|
||||
/// Run the BLE stack.
|
||||
#[embassy_executor::task]
|
||||
pub async fn run(
|
||||
controller: bt_hci::controller::ExternalController<
|
||||
esp_radio::ble::controller::BleConnector<'static>,
|
||||
CONTROLLER_SLOTS,
|
||||
>,
|
||||
name: alloc::string::String,
|
||||
) {
|
||||
let mut resources: HostResources<DefaultPacketPool, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX> =
|
||||
HostResources::new();
|
||||
let stack = trouble_host::new(controller, &mut resources);
|
||||
let Host {
|
||||
mut peripheral,
|
||||
runner,
|
||||
..
|
||||
} = stack.build();
|
||||
|
||||
let mut features = alloc::vec![];
|
||||
if cfg!(feature = "cam") {
|
||||
features.push("cam");
|
||||
}
|
||||
if cfg!(feature = "denm") {
|
||||
features.push("denm");
|
||||
}
|
||||
if cfg!(feature = "spat") {
|
||||
features.push("mapem");
|
||||
features.push("spatem");
|
||||
}
|
||||
info!(
|
||||
"Starting advertising and GATT service for {}",
|
||||
features.join(",")
|
||||
);
|
||||
let server = Server::new_with_config(GapConfig::Peripheral(PeripheralConfig {
|
||||
name: &name,
|
||||
appearance: &appearance::UNKNOWN,
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let _ = embassy_futures::join::join(ble_task(runner), async {
|
||||
loop {
|
||||
match advertise(&mut peripheral, &server).await {
|
||||
Ok(conn) => {
|
||||
info!("BLE: New client connected");
|
||||
|
||||
// set up tasks when the connection is established to a central, so they don't run when no one is connected.
|
||||
let a = gatt_events_task(&server, &conn);
|
||||
let b = publishing_task(&server, &conn);
|
||||
|
||||
// run until any task ends (usually because the connection has been closed), then return to advertising state.
|
||||
embassy_futures::select::select(a, b).await;
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let value_ref = CLIENT_CONNECTED.borrow(cs);
|
||||
let _ = value_ref.replace(false);
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("BLE ADV: fatal error: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn update(payload: BleSendable) {
|
||||
critical_section::with(|cs| {
|
||||
// only add to queue when someone is connected
|
||||
if *CLIENT_CONNECTED.borrow(cs).borrow() {
|
||||
let queue_rc = TX_DATA.borrow(cs).borrow();
|
||||
// unwrap is fine b/c we stored something in it before
|
||||
let queue = queue_rc.as_ref().unwrap();
|
||||
|
||||
if queue.push(payload).is_err() {
|
||||
log::error!("BLE TX queue is full");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Create an advertiser to use to connect to a BLE Central, and wait for it to connect.
|
||||
async fn advertise<'values, 'server, C: Controller>(
|
||||
peripheral: &mut Peripheral<'values, C, DefaultPacketPool>,
|
||||
server: &'server Server<'values>,
|
||||
) -> Result<GattConnection<'values, 'server, DefaultPacketPool>, BleHostError<C::Error>> {
|
||||
let mut advertiser_data = [0; 35];
|
||||
let service_uuid = Uuid::from(0xc0b7_0000_d4f4_4000_ada8_f99a_02ee_315c_u128)
|
||||
.as_raw()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let len = AdStructure::encode_slice(
|
||||
&[
|
||||
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
|
||||
AdStructure::ServiceUuids128(&[service_uuid]),
|
||||
],
|
||||
&mut advertiser_data[..],
|
||||
)?;
|
||||
|
||||
let advertiser = peripheral
|
||||
.advertise(
|
||||
&trouble_host::advertise::AdvertisementParameters {
|
||||
timeout: Some(embassy_time::Duration::from_secs(2)),
|
||||
..Default::default()
|
||||
},
|
||||
Advertisement::ConnectableScannableUndirected {
|
||||
adv_data: &advertiser_data[..len],
|
||||
scan_data: &[],
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
// info!("[adv] advertising {len} bytes");
|
||||
let conn = advertiser.accept().await?.with_attribute_server(server)?;
|
||||
// info!("[adv] connection established");
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
async fn ble_task<C: Controller, P: PacketPool>(mut runner: Runner<'_, C, P>) {
|
||||
loop {
|
||||
if let Err(e) = runner.run().await {
|
||||
panic!("BLE Task: fatal error {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Stream Events until the connection closes.
|
||||
///
|
||||
/// This function will handle the GATT events and process them.
|
||||
/// This is how we interact with read and write requests.
|
||||
async fn gatt_events_task<P: PacketPool>(
|
||||
server: &Server<'_>,
|
||||
conn: &GattConnection<'_, '_, P>,
|
||||
) -> Result<(), Error> {
|
||||
let reason = loop {
|
||||
match conn.next().await {
|
||||
GattConnectionEvent::Disconnected { reason } => break reason,
|
||||
GattConnectionEvent::Gatt { event } => {
|
||||
match &event {
|
||||
GattEvent::Write(event)
|
||||
if event.handle() == server.c_its_events.position.handle =>
|
||||
{
|
||||
use prost::Message as _;
|
||||
|
||||
// parse and send to main thread
|
||||
match io::msg::PositionState::decode(event.data()) {
|
||||
Ok(data) => {
|
||||
critical_section::with(|cs| {
|
||||
let rx_data_ref = RX_DATA.borrow(cs);
|
||||
let _ = rx_data_ref.replace(Some(data));
|
||||
});
|
||||
}
|
||||
Err(err) => log::error!("BLE: Failed to parse input: {err:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// This step is also performed at drop(), but writing it explicitly is necessary
|
||||
// in order to ensure reply is sent.
|
||||
match event.accept() {
|
||||
Ok(reply) => reply.send().await,
|
||||
Err(e) => warn!("BLE: error sending response: {e:?}"),
|
||||
}
|
||||
}
|
||||
_ => {} // ignore other Gatt Connection Events
|
||||
}
|
||||
};
|
||||
|
||||
info!("BLE: disconnected b/c {reason:?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
async fn publishing_task<P: PacketPool>(server: &Server<'_>, conn: &GattConnection<'_, '_, P>) {
|
||||
critical_section::with(|cs| {
|
||||
let value_ref = CLIENT_CONNECTED.borrow(cs);
|
||||
let _ = value_ref.replace(true);
|
||||
});
|
||||
|
||||
loop {
|
||||
embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await;
|
||||
|
||||
let mut new_data = None;
|
||||
critical_section::with(|cs| {
|
||||
let queue_rc = TX_DATA.borrow(cs).borrow();
|
||||
// unwrap is fine b/c we stored something in it before
|
||||
let queue = queue_rc.as_ref().unwrap();
|
||||
|
||||
if let Some(data) = queue.pop() {
|
||||
new_data = Some(data);
|
||||
}
|
||||
});
|
||||
|
||||
match new_data {
|
||||
#[cfg(feature = "cam")]
|
||||
Some(BleSendable::Cam(cam_event)) => match cam_event.serialize_ble_proto() {
|
||||
Ok(value) => {
|
||||
if let Err(err) = server.c_its_events.cam_event.notify(conn, &value).await {
|
||||
warn!("BLE: failed notifying connection of new CAM event: {err:?}");
|
||||
}
|
||||
}
|
||||
Err(err) => warn!("BLE: CAM event {err}"),
|
||||
},
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
Some(BleSendable::Denm(denm_event)) => match denm_event.serialize_ble_proto() {
|
||||
Ok(value) => {
|
||||
if let Err(err) = server.c_its_events.denm_event.notify(conn, &value).await {
|
||||
warn!("BLE: failed notifying connection of new DENM event: {err:?}");
|
||||
}
|
||||
}
|
||||
Err(err) => warn!("BLE: DENM event {err}"),
|
||||
},
|
||||
|
||||
#[cfg(feature = "cam")]
|
||||
Some(BleSendable::CamRaw(raw_msg)) => match raw_msg.serialize_ble_proto() {
|
||||
Ok(value) => {
|
||||
if let Err(err) = server.c_its_events.cam_raw.set(server, &value) {
|
||||
warn!("BLE: failed setting new raw CAM value: {err:?}");
|
||||
}
|
||||
|
||||
if let Err(err) = send_raw_notify(
|
||||
server,
|
||||
conn,
|
||||
c_its_parser::standards::extensions::ItsMessageId::Cam,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("BLE: {err}");
|
||||
}
|
||||
}
|
||||
Err(err) => warn!("BLE: Raw CAM too big: {err}"),
|
||||
},
|
||||
#[cfg(feature = "denm")]
|
||||
Some(BleSendable::DenmRaw(raw_msg)) => match raw_msg.serialize_ble_proto() {
|
||||
Ok(value) => {
|
||||
if let Err(err) = server.c_its_events.denm_raw.set(server, &value) {
|
||||
warn!("BLE: failed setting new raw DENM value: {err:?}");
|
||||
}
|
||||
|
||||
if let Err(err) = send_raw_notify(
|
||||
server,
|
||||
conn,
|
||||
c_its_parser::standards::extensions::ItsMessageId::Denm,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("BLE: {err}");
|
||||
}
|
||||
}
|
||||
Err(err) => warn!("BLE: Raw DENM {err}"),
|
||||
},
|
||||
#[cfg(feature = "spat")]
|
||||
Some(BleSendable::MapemRaw(raw_msg)) => match raw_msg.serialize_ble_proto() {
|
||||
Ok(value) => {
|
||||
if let Err(err) = server.c_its_events.mapem_raw.set(server, &value) {
|
||||
warn!("BLE: failed setting new raw MAPEM value: {err:?}");
|
||||
}
|
||||
|
||||
if let Err(err) = send_raw_notify(
|
||||
server,
|
||||
conn,
|
||||
c_its_parser::standards::extensions::ItsMessageId::Mapem,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("BLE: {err}");
|
||||
}
|
||||
}
|
||||
Err(err) => warn!("BLE: Raw MAPEM {err}"),
|
||||
},
|
||||
#[cfg(feature = "spat")]
|
||||
Some(BleSendable::SpatemRaw(raw_msg)) => match raw_msg.serialize_ble_proto() {
|
||||
Ok(value) => {
|
||||
if let Err(err) = server.c_its_events.spatem_raw.set(server, &value) {
|
||||
warn!("BLE: failed setting new raw SPATEM value: {err:?}");
|
||||
}
|
||||
|
||||
if let Err(err) = send_raw_notify(
|
||||
server,
|
||||
conn,
|
||||
c_its_parser::standards::extensions::ItsMessageId::Spatem,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("BLE: {err}");
|
||||
}
|
||||
}
|
||||
Err(err) => warn!("BLE: Raw SPATEM {err}"),
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_raw_notify<P: PacketPool>(
|
||||
server: &Server<'_>,
|
||||
conn: &GattConnection<'_, '_, P>,
|
||||
msg_id: c_its_parser::standards::extensions::ItsMessageId,
|
||||
) -> Result<(), alloc::string::String> {
|
||||
let notify_buf = io::msg::RawNotify::from(msg_id)
|
||||
.serialize_ble_proto()
|
||||
.expect("BLE: Raw notify too big");
|
||||
|
||||
server
|
||||
.c_its_events
|
||||
.raw_notify
|
||||
.notify(conn, ¬ify_buf)
|
||||
.await
|
||||
.map_err(|err| alloc::format!("failed notifying connection of new raw {msg_id:?}: {err:?}"))
|
||||
}
|
||||
258
src/cache.rs
258
src/cache.rs
|
|
@ -1,258 +0,0 @@
|
|||
//! Data Cache
|
||||
//!
|
||||
//! Store items for some time
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use alloc::vec::Vec;
|
||||
use core::ops::Sub;
|
||||
|
||||
pub trait Cachable<K>
|
||||
where
|
||||
K: core::cmp::PartialEq,
|
||||
{
|
||||
fn key(&self) -> K;
|
||||
}
|
||||
|
||||
pub trait Initable<T, K>
|
||||
where
|
||||
T: Cachable<K>,
|
||||
K: core::cmp::PartialEq,
|
||||
{
|
||||
fn init(key: K) -> T;
|
||||
}
|
||||
|
||||
struct CacheItem<K, T>
|
||||
where
|
||||
T: Cachable<K>,
|
||||
K: core::cmp::PartialEq,
|
||||
{
|
||||
timestamp: chrono::NaiveDateTime,
|
||||
data: Rc<core::cell::RefCell<T>>,
|
||||
phantom: core::marker::PhantomData<K>, // rust wants this
|
||||
}
|
||||
|
||||
impl<K, T> core::fmt::Display for CacheItem<K, T>
|
||||
where
|
||||
T: Cachable<K>,
|
||||
K: core::cmp::PartialEq + core::fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"CacheItem({}, {:?})",
|
||||
self.data.borrow().key(),
|
||||
self.timestamp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, T> CacheItem<K, T>
|
||||
where
|
||||
T: Cachable<K>,
|
||||
K: core::cmp::PartialEq,
|
||||
{
|
||||
fn new(time_now: chrono::NaiveDateTime, data: T) -> Self {
|
||||
Self {
|
||||
timestamp: time_now,
|
||||
data: Rc::new(core::cell::RefCell::new(data)),
|
||||
phantom: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CacheError<K>
|
||||
where
|
||||
K: core::fmt::Display,
|
||||
{
|
||||
KeyNotFound(K),
|
||||
InsertKeyAlreadyPresent(K),
|
||||
}
|
||||
|
||||
impl<K> core::fmt::Display for CacheError<K>
|
||||
where
|
||||
K: core::fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
CacheError::KeyNotFound(key) => write!(f, "Key '{key}' doesn't exist in cache"),
|
||||
CacheError::InsertKeyAlreadyPresent(key) => {
|
||||
write!(f, "Insert failed b/c key '{key}' already exists in cache")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Item cache
|
||||
///
|
||||
/// Stores items for a certain time.
|
||||
/// Items need to implement `Cachable` so that an itentifier (key) can be retreived from them.
|
||||
pub struct Cache<K, T>
|
||||
where
|
||||
T: Cachable<K>,
|
||||
K: core::cmp::PartialEq,
|
||||
{
|
||||
lifetime: chrono::Duration,
|
||||
items: Vec<CacheItem<K, T>>,
|
||||
}
|
||||
|
||||
impl<K, T> core::fmt::Display for Cache<K, T>
|
||||
where
|
||||
T: Cachable<K>,
|
||||
K: core::cmp::PartialEq + core::fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "Cache({}, {} items)", self.lifetime, self.items.len())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl<K, T> Cache<K, T>
|
||||
where
|
||||
T: Cachable<K> + Clone,
|
||||
K: core::cmp::PartialEq + core::fmt::Display,
|
||||
{
|
||||
/// Creates a new cache with a certain item lifetime
|
||||
pub const fn new(lifetime: chrono::Duration) -> Self {
|
||||
Self {
|
||||
lifetime,
|
||||
items: alloc::vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if a key is in the cache
|
||||
pub fn contains(&mut self, key: &K) -> bool {
|
||||
self.items
|
||||
.iter()
|
||||
.find(|i| i.data.borrow().key() == *key)
|
||||
.is_some()
|
||||
}
|
||||
|
||||
/// Determines the index of a certain key
|
||||
fn find(&mut self, key: &K) -> Option<usize> {
|
||||
self.items
|
||||
.iter()
|
||||
.position(|i| i.data.borrow().key() == *key)
|
||||
}
|
||||
|
||||
/// Retrieves all known keys
|
||||
pub fn keys(&self) -> Vec<K> {
|
||||
self.items.iter().map(|i| i.data.borrow().key()).collect()
|
||||
}
|
||||
|
||||
/// Adds a new item to the cache
|
||||
///
|
||||
/// Call [`Self::update`] to update the contents of the cached item
|
||||
/// or just to bump the timestamp.
|
||||
///
|
||||
/// # Errors
|
||||
/// Fails if the specified key already exist
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
time_now: chrono::NaiveDateTime,
|
||||
data: T,
|
||||
) -> Result<(), CacheError<K>> {
|
||||
let key = data.key();
|
||||
if self.contains(&key) {
|
||||
Err(CacheError::InsertKeyAlreadyPresent(key))
|
||||
} else {
|
||||
self.items.push(CacheItem::new(time_now, data));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves an item from the cache
|
||||
///
|
||||
/// Returns `None` if no item exists for this key
|
||||
pub fn get(&mut self, key: &K) -> Option<Rc<core::cell::RefCell<T>>> {
|
||||
self.items.iter().find_map(|i| {
|
||||
if i.data.borrow().key() == *key {
|
||||
Some(i.data.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieves items from the cache which match the predicate
|
||||
pub fn get_filtered<P>(&self, predicate: P) -> Vec<Rc<core::cell::RefCell<T>>>
|
||||
where
|
||||
P: Fn(&T) -> bool,
|
||||
{
|
||||
self.items
|
||||
.iter()
|
||||
.filter_map(|i| {
|
||||
let data = i.data.borrow();
|
||||
if predicate(&data) {
|
||||
Some(i.data.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Updates an item in the cache using the provided function
|
||||
///
|
||||
/// This can also be used to just bump the timestamp of the cached item by providing an empty closure.
|
||||
///
|
||||
/// # Errors
|
||||
/// Fails if the specified key does not exist in the cache
|
||||
pub fn update<F>(
|
||||
&mut self,
|
||||
time_now: chrono::NaiveDateTime,
|
||||
key: K,
|
||||
func: F,
|
||||
) -> Result<(), CacheError<K>>
|
||||
where
|
||||
F: Fn(&mut T),
|
||||
K: core::fmt::Display,
|
||||
{
|
||||
// find item
|
||||
let Some(pos) = self.find(&key) else {
|
||||
return Err(CacheError::KeyNotFound(key));
|
||||
};
|
||||
|
||||
// unwrap is fine since we fetched the position before
|
||||
let item = self.items.get_mut(pos).unwrap();
|
||||
|
||||
let mut data = item.data.borrow_mut();
|
||||
func(&mut data);
|
||||
|
||||
item.timestamp = time_now;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updates an item in the cache using the provided function or create a new item with default values
|
||||
pub fn update_or_init<F>(&mut self, time_now: chrono::NaiveDateTime, key: K, mut func: F)
|
||||
where
|
||||
F: FnMut(&mut T),
|
||||
K: core::fmt::Display,
|
||||
T: Initable<T, K>,
|
||||
{
|
||||
if let Some(pos) = self.find(&key) {
|
||||
// update existsting item
|
||||
// unwrap is fine since we fetched the position before
|
||||
let item = self.items.get_mut(pos).unwrap();
|
||||
|
||||
let mut data = item.data.borrow_mut();
|
||||
func(&mut data);
|
||||
|
||||
item.timestamp = time_now;
|
||||
} else {
|
||||
// insert new item
|
||||
self.items.push(CacheItem::new(time_now, T::init(key)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Cleans items which timed out from the cache
|
||||
///
|
||||
/// Call this regularly to prune old items.
|
||||
pub fn prune(&mut self, time_now: chrono::NaiveDateTime) {
|
||||
self.items.retain(|i| {
|
||||
let age = time_now.sub(i.timestamp);
|
||||
age < self.lifetime
|
||||
});
|
||||
}
|
||||
}
|
||||
446
src/geo_alg.rs
446
src/geo_alg.rs
|
|
@ -1,446 +0,0 @@
|
|||
//! Geo Algorithms
|
||||
//!
|
||||
//! Since the `geo` crate is not `no_std`, we need to implement the most important algorithms on our own
|
||||
|
||||
use alloc::string::{String, ToString};
|
||||
use core::ops::{Mul, Sub};
|
||||
|
||||
use log::warn;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use num_traits::float::Float;
|
||||
|
||||
const EARTH_RADIUS: f64 = 6_371_008.8; // mean radius of GRS80 ellipsoid
|
||||
const EARTH_CIRCUMFERENCE: f64 = 40_030_228.884; // calculated from `EARTH_RADIUS * 2 * PI`
|
||||
|
||||
/// Haversine distance between two geo coordinates (in meters)
|
||||
#[allow(unused)]
|
||||
pub fn haversine_dist(a: &geo_types::Point, b: &geo_types::Point) -> f64 {
|
||||
let theta1 = a.y().to_radians();
|
||||
let theta2 = b.y().to_radians();
|
||||
let delta_theta = (b.y() - a.y()).to_radians();
|
||||
let delta_lambda = (b.x() - a.x()).to_radians();
|
||||
let a = (delta_theta / 2.).sin().powi(2)
|
||||
+ theta1.cos() * theta2.cos() * (delta_lambda / 2.).sin().powi(2);
|
||||
let c = 2. * a.sqrt().asin();
|
||||
EARTH_RADIUS * c
|
||||
}
|
||||
|
||||
/// Haversine bearing from point `a` to point `b`
|
||||
#[allow(unused)]
|
||||
pub fn haversine_bearing(a: &geo_types::Point, b: &geo_types::Point) -> f32 {
|
||||
let (lng_a, lat_a) = (a.x().to_radians(), a.y().to_radians());
|
||||
let (lng_b, lat_b) = (b.x().to_radians(), b.y().to_radians());
|
||||
let delta_lng = lng_b - lng_a;
|
||||
|
||||
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
|
||||
let s = (lat_b.cos() * delta_lng.sin()) as f32;
|
||||
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
|
||||
let c = (lat_a.cos() * lat_b.sin() - lat_a.sin() * lat_b.cos() * delta_lng.cos()) as f32;
|
||||
|
||||
let degrees = f32::atan2(s, c).to_degrees();
|
||||
(degrees + 360.0) % 360.0
|
||||
}
|
||||
|
||||
/// Cartesian distance between to X/Y coordinates (in unit of the coordinates)
|
||||
#[allow(unused)]
|
||||
pub fn cartesian_dist(a: geo_types::Point, b: geo_types::Point) -> f64 {
|
||||
let vector = b.sub(a);
|
||||
cartesian_norm(vector)
|
||||
}
|
||||
|
||||
/// Cartesian bearing from X/Y point `a` to X/Y point `b`
|
||||
#[allow(unused)]
|
||||
pub fn cartesian_bearing(a: geo_types::Point, b: geo_types::Point) -> f64 {
|
||||
let vector = b.sub(a);
|
||||
cartesian_theta(vector)
|
||||
}
|
||||
|
||||
/// Cartesian vector norm (length)
|
||||
#[allow(unused)]
|
||||
pub fn cartesian_norm(vec: geo_types::Point) -> f64 {
|
||||
f64::sqrt(vec.x() * vec.x() + vec.y() * vec.y())
|
||||
}
|
||||
|
||||
/// Cartesian vector direction (polar coordinate angle)
|
||||
#[allow(unused)]
|
||||
pub fn cartesian_theta(vec: geo_types::Point) -> f64 {
|
||||
let theta_rad = f64::atan2(vec.y(), vec.x());
|
||||
theta_rad * 180. / core::f64::consts::PI
|
||||
}
|
||||
|
||||
/// Normalizes an angle (in degrees) to (-180, 180] interval
|
||||
#[allow(unused)]
|
||||
pub fn deg_180(angle_deg: f32) -> f32 {
|
||||
let full_circle_deg = 360.0;
|
||||
|
||||
let angle = angle_deg % full_circle_deg;
|
||||
if angle > full_circle_deg / 2. {
|
||||
return angle - full_circle_deg;
|
||||
}
|
||||
if angle <= -full_circle_deg / 2. {
|
||||
return angle + full_circle_deg;
|
||||
}
|
||||
angle
|
||||
}
|
||||
|
||||
/// Normalizes an angle (in degrees) to [0, 360) interval
|
||||
#[allow(unused)]
|
||||
pub fn deg_360(angle_deg: f32) -> f32 {
|
||||
let full_circle_deg = 360.0;
|
||||
|
||||
let angle = angle_deg % full_circle_deg;
|
||||
if (angle < 0.) {
|
||||
return angle + full_circle_deg;
|
||||
}
|
||||
angle
|
||||
}
|
||||
|
||||
/// Converts a geo-position (lon/lat) to cartesian coordinates relative to a reference positions
|
||||
///
|
||||
/// Both `ref_pos` and `position` are expected to be lon/lat in degrees.
|
||||
/// The output will be in a local X/Y coordinate system with X pointing north and Y pointing east.
|
||||
#[allow(unused)]
|
||||
pub fn lonlat_to_dcart(
|
||||
ref_pos: &geo_types::Point,
|
||||
position: &geo_types::Point,
|
||||
) -> geo_types::Point {
|
||||
let pos_diff = position.sub(*ref_pos);
|
||||
let dlat_deg = pos_diff.y();
|
||||
let dlon_deg = pos_diff.x();
|
||||
|
||||
// latitude (north/south) degree per meter is independent from longitude
|
||||
let dx = dlat_deg / 360. * EARTH_CIRCUMFERENCE;
|
||||
|
||||
// longitude (east/west) degree per meter has a different value depending on the latitude
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let ref_lat_rad = ref_pos.to_radians().y();
|
||||
let dy = dlon_deg / 360. * (EARTH_CIRCUMFERENCE * ref_lat_rad.cos());
|
||||
|
||||
geo_types::Point::new(dx, dy)
|
||||
}
|
||||
|
||||
/// Converts a X/Y coordinate (NED relative to a reference position) to a geo-position
|
||||
///
|
||||
/// The `ref_pos` is expected to be lon/lat in degrees and the output will be the same.
|
||||
/// The `position` is expected to be in a local X/Y coordinate system with X pointing north and Y pointing east.
|
||||
#[allow(unused)]
|
||||
pub fn dcart_to_lonlat(
|
||||
ref_pos: &geo_types::Point,
|
||||
position: &geo_types::Point,
|
||||
) -> geo_types::Point {
|
||||
// note that we're using NED while c-its-parser uses ITS-G5 style ENU X/Y coordinates
|
||||
// so we need to swap X and Y coordinates
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let dx = position.y() as f32;
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let dy = position.x() as f32;
|
||||
|
||||
c_its_parser::geo_utils::point_from_dxy(dx, dy, ref_pos)
|
||||
}
|
||||
|
||||
/// Describes how a position is related to a line segment
|
||||
#[derive(Debug, Default)]
|
||||
#[allow(unused)]
|
||||
pub struct LinePosition {
|
||||
/// Own position relative to the line segment
|
||||
pub semantic: LinePositionType,
|
||||
/// Distance to center line (always positive)
|
||||
pub parallel_dist: f64,
|
||||
/// Distance to first node (negative if `BeforeStart`)
|
||||
pub perpendicular_dist: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[allow(unused)]
|
||||
pub enum LinePositionType {
|
||||
#[default]
|
||||
Undefined,
|
||||
BeforeStart,
|
||||
InBetween,
|
||||
AfterEnd,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl LinePosition {
|
||||
pub fn new(parallel_dist: f64, perpendicular_dist: f64, after_end: bool) -> Self {
|
||||
let semantic = if after_end {
|
||||
LinePositionType::AfterEnd
|
||||
} else if parallel_dist < 0. {
|
||||
LinePositionType::BeforeStart
|
||||
} else {
|
||||
LinePositionType::InBetween
|
||||
};
|
||||
Self {
|
||||
semantic,
|
||||
parallel_dist,
|
||||
perpendicular_dist,
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines `point` position relative to a line segment (in X/Y coordinates)
|
||||
///
|
||||
/// The `ls` shall consist of 2 points!
|
||||
///
|
||||
/// # Algorithm
|
||||
/// The line segment can be converted to a vector which describes an infinite line.
|
||||
/// So we can calculate the (perpendicular) projection of the point on that line:
|
||||
/// ```text
|
||||
/// [Case A] (point) | [Case B] (point)
|
||||
/// | | |
|
||||
/// | | |
|
||||
/// (node1)---(node2)-----(q) | (node1)------(q)------(node2)
|
||||
/// ```
|
||||
///
|
||||
/// This will already give us the perpendicular and parallel distance.
|
||||
/// Since we came from a finite line segment and want to know if the point is bejond the segment,
|
||||
/// we also need to compare the parallel distance with the length of the vector.
|
||||
/// So both case are the same mathematically.
|
||||
///
|
||||
/// # Errors
|
||||
/// Fails if points are the same
|
||||
pub fn for_ls(point: geo_types::Point, line: &geo_types::Line) -> Result<Self, String> {
|
||||
let node1 = line.start_point();
|
||||
let node2 = line.end_point();
|
||||
|
||||
let line_vec = node2.sub(node1);
|
||||
let line_vec_length = cartesian_norm(line_vec);
|
||||
|
||||
// input validation
|
||||
if line_vec_length < f64::EPSILON {
|
||||
return Err("Points are the same".to_string());
|
||||
}
|
||||
|
||||
// calculate projection of `p` onto the line
|
||||
// ```math
|
||||
// # n needs to be a unit vector
|
||||
// n = (node2 - node1) / |node2 - node1|
|
||||
// # vector from node1 to the point
|
||||
// p = point - node1
|
||||
// # projection is the unit-vector multipled by the dot-product of p and n
|
||||
// q = (p {dot} n) * n
|
||||
// # perpendicular length is calculated from the vector q->p
|
||||
// QP = q - p
|
||||
// ```
|
||||
let p = point.sub(node1);
|
||||
let n = line_vec.mul(1. / line_vec_length);
|
||||
let q = n.mul(p.dot(n));
|
||||
let qp_length = cartesian_norm(q.sub(p));
|
||||
|
||||
// calculate parallel distance of `q` to `node1`.
|
||||
// This can be achieved by the dot-product since `n` is a unit vector and
|
||||
// the dot-product of two paralle vectors is the same as the multiplication of the vector norms.
|
||||
let q_dist = n.dot(q);
|
||||
|
||||
// generate output
|
||||
let after_end = q_dist > line_vec_length;
|
||||
let line_pos = Self::new(q_dist, qp_length, after_end);
|
||||
|
||||
Ok(line_pos)
|
||||
}
|
||||
|
||||
/// Determines `point` position relative to nodes of an ingress `Lane`
|
||||
///
|
||||
/// Keep in mind, that the lane's node list starts at the stop line and is extending away from the intersection.
|
||||
/// But we are driving the other way around!
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns a human-readable string if position can't be determined
|
||||
pub fn for_ingress_lane(
|
||||
point: geo_types::Point,
|
||||
nodes: &geo_types::LineString,
|
||||
) -> Result<Self, String> {
|
||||
let mut result = Self::default();
|
||||
|
||||
let mut lines = nodes.lines();
|
||||
|
||||
// check, that the stop line is not behind us
|
||||
let line = lines
|
||||
.next()
|
||||
.ok_or("Lane has less than two nodes".to_string())?;
|
||||
|
||||
let pos = Self::for_ls(point, &line)?;
|
||||
if pos.semantic == LinePositionType::BeforeStart {
|
||||
// we have crossed the stop line -> abort search for position inside the lane
|
||||
return Ok(pos);
|
||||
}
|
||||
if pos.semantic == LinePositionType::InBetween {
|
||||
// we're at this segment -> search finished
|
||||
return Ok(pos);
|
||||
}
|
||||
// otherwise: continue with next segment (and store this segment's length)
|
||||
let mut lane_length = cartesian_norm(line.delta().into());
|
||||
let mut last_pos = LinePosition::default();
|
||||
|
||||
// continue with remaining lines
|
||||
for line in lines {
|
||||
let line_len = cartesian_norm(line.delta().into());
|
||||
|
||||
// ignored line, if points are too close <10cm
|
||||
if line_len < 0.1 {
|
||||
warn!("Lane nodes are just {line_len:.1} m apart -> ignoring");
|
||||
continue;
|
||||
}
|
||||
|
||||
let pos = Self::for_ls(point, &line)?;
|
||||
match pos.semantic {
|
||||
LinePositionType::BeforeStart => {
|
||||
// we're at the outer arc of the lane right at the border between line segments
|
||||
// -> set parallel distance to zero and finish search
|
||||
return Ok(LinePosition::new(
|
||||
lane_length,
|
||||
pos.perpendicular_dist,
|
||||
false,
|
||||
));
|
||||
}
|
||||
LinePositionType::InBetween => {
|
||||
// we're at this segment
|
||||
// -> add segment's distance to accumulated lane length and finish search
|
||||
return Ok(LinePosition::new(
|
||||
lane_length + pos.parallel_dist,
|
||||
pos.perpendicular_dist,
|
||||
false,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
// continue searching, add segment's distance to lane length accumulator
|
||||
lane_length += line_len;
|
||||
last_pos = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we came this far, we're after the lane's end (actually the start in driving direction)
|
||||
// -> add parallel distance to accumulated lane length
|
||||
Ok(LinePosition::new(
|
||||
lane_length + last_pos.parallel_dist,
|
||||
last_pos.perpendicular_dist,
|
||||
true,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::geo_alg::{LinePosition, cartesian_bearing};
|
||||
|
||||
#[test]
|
||||
fn test_cart_bearing() {
|
||||
let start = geo_types::Point::new(0., 0.);
|
||||
|
||||
assert_float_eq::assert_float_absolute_eq!(
|
||||
0.,
|
||||
cartesian_bearing(start, geo_types::Point::new(10., 0.))
|
||||
);
|
||||
|
||||
assert_float_eq::assert_float_absolute_eq!(
|
||||
90.,
|
||||
cartesian_bearing(start, geo_types::Point::new(0., 10.))
|
||||
);
|
||||
|
||||
assert_float_eq::assert_float_absolute_eq!(
|
||||
45.,
|
||||
cartesian_bearing(start, geo_types::Point::new(10., 10.))
|
||||
);
|
||||
|
||||
assert_float_eq::assert_float_absolute_eq!(
|
||||
135.,
|
||||
cartesian_bearing(start, geo_types::Point::new(-10., 10.))
|
||||
);
|
||||
|
||||
assert_float_eq::assert_float_absolute_eq!(
|
||||
-135.,
|
||||
cartesian_bearing(start, geo_types::Point::new(-10., -10.))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linepos_for_ls_validation() {
|
||||
// identical nodes shall result in an error
|
||||
|
||||
let point = geo_types::Point::new(0., 0.);
|
||||
let node1 = geo_types::Point::new(0., 0.);
|
||||
let node2 = geo_types::Point::new(10., -5.);
|
||||
|
||||
let ls = geo_types::Line::new(node1, node1);
|
||||
assert!(LinePosition::for_ls(point, &ls).is_err());
|
||||
|
||||
let ls = geo_types::Line::new(node2, node2);
|
||||
assert!(LinePosition::for_ls(point, &ls).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linepos_for_ls_test() {
|
||||
use crate::geo_alg::LinePositionType;
|
||||
|
||||
// test on X axis
|
||||
{
|
||||
let node1 = geo_types::Point::new(0., 0.);
|
||||
let node2 = geo_types::Point::new(5., 0.);
|
||||
|
||||
let ls = geo_types::Line::new(node1, node2);
|
||||
|
||||
// test some positions
|
||||
let test_tuples = vec![
|
||||
(node1.x() - 1000., LinePositionType::BeforeStart),
|
||||
(node1.x() - 1., LinePositionType::BeforeStart),
|
||||
(node1.x(), LinePositionType::InBetween),
|
||||
(node1.x() + 1., LinePositionType::InBetween),
|
||||
(node2.x() - 1., LinePositionType::InBetween),
|
||||
(node2.x(), LinePositionType::InBetween),
|
||||
(node2.x() + 1., LinePositionType::AfterEnd),
|
||||
(node2.x() + 1000., LinePositionType::AfterEnd),
|
||||
];
|
||||
let y_values = vec![(node1.y() - 1.), (node1.y()), (node1.y() + 1.)];
|
||||
|
||||
// y position should only affect the perpendicular distance
|
||||
for y in &y_values {
|
||||
// x position affects the position type and parallel distance
|
||||
for (x, semantic) in &test_tuples {
|
||||
let point = geo_types::Point::new(*x, *y);
|
||||
|
||||
let result = LinePosition::for_ls(point, &ls).unwrap();
|
||||
|
||||
assert_eq!(semantic, &result.semantic);
|
||||
assert_eq!((y - node1.y()).abs(), result.perpendicular_dist);
|
||||
assert_eq!(x - node1.x(), result.parallel_dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test on Y axis
|
||||
{
|
||||
let node1 = geo_types::Point::new(3., 5.);
|
||||
let node2 = geo_types::Point::new(3., 100.);
|
||||
|
||||
let ls = geo_types::Line::new(node1, node2);
|
||||
|
||||
// test some positions
|
||||
let test_tuples = vec![
|
||||
(node1.y() - 1000., LinePositionType::BeforeStart),
|
||||
(node1.y() - 1., LinePositionType::BeforeStart),
|
||||
(node1.y(), LinePositionType::InBetween),
|
||||
(node1.y() + 1., LinePositionType::InBetween),
|
||||
(node2.y() - 1., LinePositionType::InBetween),
|
||||
(node2.y(), LinePositionType::InBetween),
|
||||
(node2.y() + 1., LinePositionType::AfterEnd),
|
||||
(node2.y() + 1000., LinePositionType::AfterEnd),
|
||||
];
|
||||
let x_values = vec![(node1.x() - 1.), (node1.x()), (node1.x() + 1.)];
|
||||
|
||||
// x position should only affect the perpendicular distance
|
||||
for x in &x_values {
|
||||
// y position affects the position type and parallel distance
|
||||
for (y, semantic) in &test_tuples {
|
||||
let point = geo_types::Point::new(*x, *y);
|
||||
|
||||
let result = LinePosition::for_ls(point, &ls).unwrap();
|
||||
|
||||
assert_eq!(semantic, &result.semantic);
|
||||
assert_eq!((x - node1.x()).abs(), result.perpendicular_dist);
|
||||
assert_eq!(y - node1.y(), result.parallel_dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
849
src/io.rs
849
src/io.rs
|
|
@ -1,849 +0,0 @@
|
|||
//! UART and BLE I/O Protocol
|
||||
|
||||
pub mod msg {
|
||||
#![allow(clippy::all, clippy::pedantic, clippy::nursery, dead_code)]
|
||||
include!(concat!(env!("OUT_DIR"), "/c_its_io.rs"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
use c_its_parser::gn as geonetworking;
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
impl From<msg::RawMsgRx> for msg::SerialOutputMsg {
|
||||
fn from(value: msg::RawMsgRx) -> Self {
|
||||
use msg::serial_output_msg::Payload;
|
||||
|
||||
Self {
|
||||
payload: Some(Payload::RxMsg(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
impl From<msg::DenmEvent> for msg::SerialOutputMsg {
|
||||
fn from(value: msg::DenmEvent) -> Self {
|
||||
use msg::serial_output_msg::Payload;
|
||||
|
||||
Self {
|
||||
payload: Some(Payload::Denm(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl msg::RawMsgRx {
|
||||
#[cfg(feature = "uart")]
|
||||
pub fn serialize_uart_proto(&self) -> alloc::vec::Vec<u8> {
|
||||
serialize_serial_msg(&Into::<msg::SerialOutputMsg>::into(self.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
impl msg::DenmEvent {
|
||||
#[cfg(feature = "ble")]
|
||||
pub const PROTO_SIZE: usize = 51;
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
pub fn serialize_ble_proto(&self) -> Result<[u8; Self::PROTO_SIZE], alloc::string::String> {
|
||||
use prost::Message as _;
|
||||
|
||||
let mut data = [0u8; Self::PROTO_SIZE];
|
||||
|
||||
let mut mut_buf = &mut data[..];
|
||||
if let Err(err) = self.encode(&mut mut_buf) {
|
||||
Err(alloc::format!("Buffer too small: {err:?}"))
|
||||
} else {
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cam")]
|
||||
impl msg::CamEvent {
|
||||
#[cfg(feature = "ble")]
|
||||
pub const PROTO_SIZE: usize = 38;
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
pub fn serialize_ble_proto(&self) -> Result<[u8; Self::PROTO_SIZE], alloc::string::String> {
|
||||
use prost::Message as _;
|
||||
|
||||
let mut data = [0u8; Self::PROTO_SIZE];
|
||||
|
||||
let mut mut_buf = &mut data[..];
|
||||
if let Err(err) = self.encode(&mut mut_buf) {
|
||||
Err(alloc::format!("Buffer too small: {err:?}"))
|
||||
} else {
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "cam", feature = "denm", feature = "spat"))]
|
||||
impl msg::RawMsgRx {
|
||||
#[cfg(feature = "ble")]
|
||||
pub const PROTO_SIZE: usize = 1400;
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
pub fn serialize_ble_proto(&self) -> Result<[u8; Self::PROTO_SIZE], alloc::string::String> {
|
||||
use prost::Message as _;
|
||||
|
||||
let mut data = [0u8; Self::PROTO_SIZE];
|
||||
|
||||
let mut mut_buf = &mut data[..];
|
||||
if let Err(err) = self.encode(&mut mut_buf) {
|
||||
Err(alloc::format!("Buffer too small: {err:?}"))
|
||||
} else {
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "ble",
|
||||
any(feature = "cam", feature = "denm", feature = "spat")
|
||||
))]
|
||||
impl From<c_its_parser::standards::extensions::ItsMessageId> for msg::RawNotify {
|
||||
fn from(value: c_its_parser::standards::extensions::ItsMessageId) -> Self {
|
||||
let message_type = value.as_u8().into();
|
||||
Self { message_type }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "cam", feature = "denm", feature = "spat"))]
|
||||
impl msg::RawNotify {
|
||||
#[cfg(feature = "ble")]
|
||||
pub const PROTO_SIZE: usize = 2;
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
pub fn serialize_ble_proto(self) -> Result<[u8; Self::PROTO_SIZE], alloc::string::String> {
|
||||
use prost::Message as _;
|
||||
|
||||
let mut data = [0u8; Self::PROTO_SIZE];
|
||||
|
||||
let mut mut_buf = &mut data[..];
|
||||
if let Err(err) = self.encode(&mut mut_buf) {
|
||||
Err(alloc::format!("Buffer too small: {err:?}"))
|
||||
} else {
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Parser {
|
||||
buffer: alloc::vec::Vec<u8>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
impl Parser {
|
||||
const SOF_LEN: u16 = 4;
|
||||
const SOF1: u8 = 0x56;
|
||||
const SOF2: u8 = 0x32;
|
||||
const SOF3: u8 = 0x58;
|
||||
const SOF4: u8 = 0x2B;
|
||||
const EOF_LEN: u16 = 2;
|
||||
#[allow(unused)]
|
||||
const EOF1: u8 = 0x0d;
|
||||
#[allow(unused)]
|
||||
const EOF2: u8 = 0x0a;
|
||||
const LENGTH_LEN: u16 = 2;
|
||||
|
||||
/// Parses incoming data and return list of messages
|
||||
pub fn parse(&mut self, input: &[u8]) -> alloc::vec::Vec<msg::SerialInputMsg> {
|
||||
use prost::Message as _;
|
||||
|
||||
// combine with existing data
|
||||
self.buffer.append(&mut input.to_vec());
|
||||
|
||||
// find start of frame and parse
|
||||
let mut output = alloc::vec![];
|
||||
while let Some(pos) = Self::find_sof(&self.buffer) {
|
||||
// drop everything before the SOF
|
||||
self.buffer.drain(..pos);
|
||||
|
||||
// get length field and check buffer size
|
||||
if let Some(len_bytes) = self
|
||||
.buffer
|
||||
.get(usize::from(Self::SOF_LEN)..usize::from(Self::SOF_LEN + Self::LENGTH_LEN))
|
||||
{
|
||||
// unwrap is fine since we queried 2 bytes before, so we can also convert to 2-byte slice
|
||||
let msg_size = u16::from_be_bytes(len_bytes.try_into().unwrap());
|
||||
|
||||
let required_buf_size = Self::SOF_LEN + Self::LENGTH_LEN + msg_size + Self::EOF_LEN;
|
||||
if let Some(payload) = self.buffer.get(
|
||||
usize::from(Self::SOF_LEN + Self::LENGTH_LEN)
|
||||
..usize::from(required_buf_size - 2),
|
||||
) {
|
||||
match msg::SerialInputMsg::decode(payload) {
|
||||
Ok(data) => {
|
||||
output.push(data);
|
||||
}
|
||||
Err(err) => log::error!("Failed to parse input: {err:?}"),
|
||||
}
|
||||
|
||||
// consume buffer
|
||||
self.buffer.drain(..usize::from(required_buf_size));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
/// Returns the start index of the SOF sequence, if present
|
||||
fn find_sof(input: &[u8]) -> Option<usize> {
|
||||
// search for SOF
|
||||
if let Some(sof1_pos) = input.iter().position(|i| *i == Self::SOF1) {
|
||||
// drop everything before sof1_pos and check if remainder continues with SOF sequence
|
||||
let (_, msg) = input.split_at(sof1_pos);
|
||||
if msg.starts_with(&[Self::SOF1, Self::SOF2, Self::SOF3, Self::SOF4]) {
|
||||
// found the SOF sequence
|
||||
return Some(sof1_pos);
|
||||
}
|
||||
}
|
||||
|
||||
// if we reach this position, SOF sequence wasn't found
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
fn serialize_serial_msg<T>(msg: &T) -> alloc::vec::Vec<u8>
|
||||
where
|
||||
T: prost::Message,
|
||||
{
|
||||
let mut msg_buf = msg.encode_to_vec();
|
||||
|
||||
let sof = [0x56u8, 0x32, 0x58, 0x2B];
|
||||
let eof = [0x0du8, 0x0a];
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let len: [u8; 2] = (msg_buf.len() as u16).to_be_bytes();
|
||||
|
||||
let mut out = sof.to_vec();
|
||||
out.extend_from_slice(&len);
|
||||
out.append(&mut msg_buf);
|
||||
out.extend_from_slice(&eof);
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "uart", feature = "ble"))]
|
||||
impl From<msg::PositionState> for crate::applogic::GnssFix {
|
||||
fn from(value: msg::PositionState) -> Self {
|
||||
let time =
|
||||
chrono::DateTime::from_timestamp_millis(value.timestamp_ms).map(|v| v.naive_utc());
|
||||
|
||||
let latitude_deg = value.position.latitude_deg.into();
|
||||
let longitude_deg = value.position.longitude_deg.into();
|
||||
let heading_deg = value.heading.map(|v| {
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let val = (v as f32) / 10.;
|
||||
val
|
||||
});
|
||||
let speed_mps = value.speed.map(|v| {
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let val = (v as f32) / 100.;
|
||||
val
|
||||
});
|
||||
|
||||
Self {
|
||||
time,
|
||||
latitude_deg,
|
||||
longitude_deg,
|
||||
heading_deg,
|
||||
speed_mps,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
impl From<msg::PositionState> for esp32_cits_core::PosVel {
|
||||
fn from(value: msg::PositionState) -> Self {
|
||||
let position = geo_types::Point::new(
|
||||
value.position.longitude_deg.into(),
|
||||
value.position.latitude_deg.into(),
|
||||
);
|
||||
let heading_deg = value.heading.map(|v| {
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let val = (v as f32) / 10.;
|
||||
val
|
||||
});
|
||||
let speed_mps = value.speed.map(|v| {
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let val = (v as f32) / 100.;
|
||||
val
|
||||
});
|
||||
|
||||
Self {
|
||||
position,
|
||||
heading_deg,
|
||||
speed_mps,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
impl From<msg::ItsPosition> for esp32_cits_core::PosVel {
|
||||
fn from(value: msg::ItsPosition) -> Self {
|
||||
let latitude_deg = f64::from(value.latitude) / 10_000_000.;
|
||||
let longitude_deg = f64::from(value.longitude) / 10_000_000.;
|
||||
let position = geo_types::Point::new(longitude_deg, latitude_deg);
|
||||
|
||||
Self {
|
||||
position,
|
||||
heading_deg: None,
|
||||
speed_mps: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
impl esp32_cits_core::tx::GnItsPayload for msg::RawMsgTx {
|
||||
fn make_eh(
|
||||
&self,
|
||||
address: [u8; 6],
|
||||
own_position: &esp32_cits_core::PosVel,
|
||||
time: chrono::DateTime<chrono::Utc>,
|
||||
seq_no: &mut u16,
|
||||
) -> Result<
|
||||
(
|
||||
c_its_parser::gn::en302636_4_1::ExtendedHeader,
|
||||
Option<c_its_parser::gn::en302636_4_1::AreaType>,
|
||||
u8,
|
||||
),
|
||||
alloc::string::String,
|
||||
> {
|
||||
use alloc::string::ToString as _;
|
||||
|
||||
let station_type = self
|
||||
.station_type
|
||||
.and_then(|v| {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let raw_val = v as u8;
|
||||
geonetworking::en302636_4_1::StationType::try_from(raw_val).ok()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let hop_limit = self.hop_limit as u8;
|
||||
|
||||
Ok(match self.gn_transport() {
|
||||
msg::GnTransport::Gac => {
|
||||
if self.gn_area.position.is_none() {
|
||||
return Err("Failed to create GAC: gnArea.position missing".to_string());
|
||||
}
|
||||
|
||||
*seq_no += 1;
|
||||
|
||||
let (atype, geoanycast) = Self::make_geobcast(
|
||||
address,
|
||||
station_type,
|
||||
own_position,
|
||||
time,
|
||||
self.gn_area,
|
||||
*seq_no,
|
||||
)?;
|
||||
let eh = geonetworking::en302636_4_1::ExtendedHeader::GAC(geoanycast);
|
||||
(eh, Some(atype), hop_limit)
|
||||
}
|
||||
msg::GnTransport::Gbc => {
|
||||
if self.gn_area.position.is_none() {
|
||||
return Err("Failed to create GAC: gnArea.position missing".to_string());
|
||||
}
|
||||
|
||||
*seq_no += 1;
|
||||
|
||||
let (atype, geoanycast) = Self::make_geobcast(
|
||||
address,
|
||||
station_type,
|
||||
own_position,
|
||||
time,
|
||||
self.gn_area,
|
||||
*seq_no,
|
||||
)?;
|
||||
let eh = geonetworking::en302636_4_1::ExtendedHeader::GBC(geoanycast);
|
||||
(eh, Some(atype), hop_limit)
|
||||
}
|
||||
msg::GnTransport::Tsb => {
|
||||
*seq_no += 1;
|
||||
|
||||
(
|
||||
esp32_cits_core::tx::gn::make_tsb_eh(
|
||||
address,
|
||||
station_type,
|
||||
own_position,
|
||||
time,
|
||||
*seq_no,
|
||||
)?,
|
||||
None,
|
||||
hop_limit,
|
||||
)
|
||||
}
|
||||
msg::GnTransport::Shb => (
|
||||
esp32_cits_core::tx::gn::make_shb_eh(address, station_type, own_position, time)?,
|
||||
None,
|
||||
1,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn make_payload(
|
||||
&self,
|
||||
) -> Result<
|
||||
(
|
||||
alloc::vec::Vec<u8>,
|
||||
c_its_parser::standards::extensions::ItsMessageId,
|
||||
),
|
||||
alloc::string::String,
|
||||
> {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let message_id = (self.message_type as u8).try_into()?;
|
||||
Ok((self.payload.clone(), message_id))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
impl msg::RawMsgTx {
|
||||
fn make_geobcast(
|
||||
address: [u8; 6],
|
||||
station_type: geonetworking::en302636_4_1::StationType,
|
||||
own_position: &esp32_cits_core::PosVel,
|
||||
time: chrono::DateTime<chrono::Utc>,
|
||||
gn_area: msg::GnArea,
|
||||
sequence_number: u16,
|
||||
) -> Result<
|
||||
(
|
||||
c_its_parser::gn::en302636_4_1::AreaType,
|
||||
geonetworking::en302636_4_1::GeoBroadcast,
|
||||
),
|
||||
alloc::string::String,
|
||||
> {
|
||||
let source_position_vector =
|
||||
esp32_cits_core::tx::gn::make_lpv(address, station_type, own_position, time)?;
|
||||
|
||||
let (atype, dist_a, dist_b, angle) = match gn_area.area() {
|
||||
msg::GnAreaShape::EtsiAreashapeCircle => (
|
||||
c_its_parser::gn::en302636_4_1::AreaType::Circular,
|
||||
gn_area.dist_a,
|
||||
None,
|
||||
None,
|
||||
),
|
||||
msg::GnAreaShape::EtsiAreashapeRectangle => (
|
||||
c_its_parser::gn::en302636_4_1::AreaType::Rectangular,
|
||||
gn_area.dist_a,
|
||||
gn_area.dist_b,
|
||||
gn_area.angle,
|
||||
),
|
||||
msg::GnAreaShape::EtsiAreashapeEllipsis => (
|
||||
c_its_parser::gn::en302636_4_1::AreaType::Ellipsoidal,
|
||||
gn_area.dist_a,
|
||||
gn_area.dist_b,
|
||||
gn_area.angle,
|
||||
),
|
||||
};
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let latitude_deg = own_position.position.y() as f32;
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let longitude_deg = own_position.position.x() as f32;
|
||||
let distance_a = dist_a
|
||||
.map(|v| {
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
let val = v as u16;
|
||||
val
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let distance_b = dist_b
|
||||
.map(|v| {
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
let val = v as u16;
|
||||
val
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let angle = angle
|
||||
.map(|v| {
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
let val = v as u16;
|
||||
val
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let gac = geonetworking::en302636_4_1::GeoAnycast::try_from_values(
|
||||
sequence_number,
|
||||
source_position_vector,
|
||||
latitude_deg,
|
||||
longitude_deg,
|
||||
distance_a,
|
||||
distance_b,
|
||||
angle,
|
||||
)
|
||||
.map_err(|err| alloc::format!("{err}"))?;
|
||||
|
||||
Ok((atype, gac))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cam")]
|
||||
impl From<&alloc::boxed::Box<c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions::CAM>>
|
||||
for msg::CamEvent
|
||||
{
|
||||
fn from(
|
||||
value: &alloc::boxed::Box<c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions::CAM>,
|
||||
) -> Self {
|
||||
use c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions;
|
||||
|
||||
let station_id = value.header.station_id.0;
|
||||
let station_type = u32::from(value.cam.cam_parameters.basic_container.station_type.0);
|
||||
let position = value
|
||||
.cam
|
||||
.cam_parameters
|
||||
.basic_container
|
||||
.reference_position
|
||||
.clone()
|
||||
.into();
|
||||
|
||||
let vehicle_role = value
|
||||
.cam
|
||||
.cam_parameters
|
||||
.low_frequency_container
|
||||
.as_ref()
|
||||
.and_then(|v| match v {
|
||||
cam_pdu_descriptions::LowFrequencyContainer::basicVehicleContainerLowFrequency(
|
||||
basic_vehicle_container_low_frequency,
|
||||
) => Some(basic_vehicle_container_low_frequency.vehicle_role as u32),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let vehicle_data = match &value.cam.cam_parameters.high_frequency_container {
|
||||
cam_pdu_descriptions::HighFrequencyContainer::basicVehicleContainerHighFrequency(
|
||||
basic_vehicle_container_high_frequency,
|
||||
) => Some(basic_vehicle_container_high_frequency.into()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Self {
|
||||
station_id,
|
||||
station_type,
|
||||
position,
|
||||
vehicle_role,
|
||||
vehicle_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cam")]
|
||||
impl From<&c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions::BasicVehicleContainerHighFrequency> for msg::VehicleContainer {
|
||||
fn from(value: &c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions::BasicVehicleContainerHighFrequency) -> Self {
|
||||
let heading_deg = if value.heading.heading_value.is_unavailable() {
|
||||
None
|
||||
} else {
|
||||
Some(u32::from(value.heading.heading_value.0))
|
||||
};
|
||||
let speed = if value.speed.speed_value.is_unavailable() {
|
||||
None
|
||||
} else {
|
||||
Some(u32::from(value.speed.speed_value.0))
|
||||
};
|
||||
let vehicle_length_dm = if value.vehicle_length.vehicle_length_value.is_unavailable() {
|
||||
None
|
||||
} else {
|
||||
Some(u32::from(value.vehicle_length.vehicle_length_value.0))
|
||||
};
|
||||
let vehicle_width_dm = if value.vehicle_width.is_unavailable() {
|
||||
None
|
||||
} else {
|
||||
Some(u32::from(value.vehicle_width.0))
|
||||
};
|
||||
|
||||
|
||||
Self { heading_deg, speed, vehicle_length_dm, vehicle_width_dm }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
impl From<&alloc::boxed::Box<c_its_parser::standards::denm_2_2_1::denm_pdu_description::DENM>>
|
||||
for msg::DenmEvent
|
||||
{
|
||||
fn from(
|
||||
value: &alloc::boxed::Box<c_its_parser::standards::denm_2_2_1::denm_pdu_description::DENM>,
|
||||
) -> Self {
|
||||
let denm_mgmt = &value.denm.management;
|
||||
|
||||
let station_id = value.header.station_id.0; // TODO: or from action ID?
|
||||
|
||||
let ref_time = chrono::DateTime::<chrono::Utc>::from(denm_mgmt.reference_time.clone());
|
||||
let end_time = ref_time + chrono::Duration::seconds(denm_mgmt.validity_duration.0.into());
|
||||
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
let timestamp = ref_time.timestamp_millis() as u64;
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
let validity_end_ts = end_time.timestamp_millis() as u64;
|
||||
let seq_num = denm_mgmt.action_id.sequence_number.0.into();
|
||||
|
||||
let event_position = Some(denm_mgmt.event_position.clone().into());
|
||||
let event_heading_deg = value
|
||||
.denm
|
||||
.location
|
||||
.as_ref()
|
||||
.and_then(|v| v.event_position_heading.clone())
|
||||
.map(|v| {
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
let conv = v.value.as_deg() as u32;
|
||||
conv
|
||||
});
|
||||
let direction = denm_mgmt
|
||||
.traffic_direction
|
||||
.map(|v| msg::TrafficDir::from(v) as i32);
|
||||
let cc_tuple = value
|
||||
.denm
|
||||
.situation
|
||||
.as_ref()
|
||||
.map(|v| v.event_type.cc_and_scc.to_u8_tuple());
|
||||
let cause_code = cc_tuple.map(|v| v.0.into());
|
||||
let sub_cause_code = cc_tuple.map(|v| v.1.into());
|
||||
|
||||
Self {
|
||||
timestamp,
|
||||
validity_end_ts,
|
||||
station_id,
|
||||
seq_num,
|
||||
event_position,
|
||||
event_heading_deg,
|
||||
direction,
|
||||
cause_code,
|
||||
sub_cause_code,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cam")]
|
||||
impl From<c_its_parser::standards::cdd_1_3_1_1::its_container::ReferencePosition>
|
||||
for msg::Position
|
||||
{
|
||||
fn from(value: c_its_parser::standards::cdd_1_3_1_1::its_container::ReferencePosition) -> Self {
|
||||
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
|
||||
let latitude_deg = value.latitude.as_deg() as f32;
|
||||
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
|
||||
let longitude_deg = value.longitude.as_deg() as f32;
|
||||
|
||||
Self {
|
||||
latitude_deg,
|
||||
longitude_deg,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
impl From<c_its_parser::standards::cdd_2_2_1::etsi_its_cdd::ReferencePosition> for msg::Position {
|
||||
fn from(value: c_its_parser::standards::cdd_2_2_1::etsi_its_cdd::ReferencePosition) -> Self {
|
||||
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
|
||||
let latitude_deg = value.latitude.as_deg() as f32;
|
||||
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
|
||||
let longitude_deg = value.longitude.as_deg() as f32;
|
||||
|
||||
Self {
|
||||
latitude_deg,
|
||||
longitude_deg,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
impl From<c_its_parser::standards::cdd_2_2_1::etsi_its_cdd::TrafficDirection> for msg::TrafficDir {
|
||||
fn from(value: c_its_parser::standards::cdd_2_2_1::etsi_its_cdd::TrafficDirection) -> Self {
|
||||
use c_its_parser::standards::cdd_2_2_1::etsi_its_cdd::TrafficDirection;
|
||||
|
||||
match value {
|
||||
TrafficDirection::allTrafficDirections => Self::AllDirections,
|
||||
TrafficDirection::sameAsReferenceDirection_upstreamOfReferencePosition => {
|
||||
Self::Upstream
|
||||
}
|
||||
TrafficDirection::sameAsReferenceDirection_downstreamOfReferencePosition => {
|
||||
Self::Downstream
|
||||
}
|
||||
TrafficDirection::oppositeToReferenceDirection => Self::Opposite,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "cam", feature = "denm", feature = "spat"))]
|
||||
impl TryFrom<&c_its_parser::ItsMessage<'_>> for msg::RawMsgRx {
|
||||
type Error = alloc::string::String;
|
||||
|
||||
fn try_from(value: &c_its_parser::ItsMessage<'_>) -> Result<Self, Self::Error> {
|
||||
let msg_id = c_its_parser::standards::extensions::ItsMessageId::from(value);
|
||||
|
||||
let payload = match value {
|
||||
#[cfg(feature = "denm")]
|
||||
c_its_parser::ItsMessage::DenmV1 {
|
||||
geonetworking: _,
|
||||
transport: _,
|
||||
etsi,
|
||||
} => etsi.encode_to_uper(),
|
||||
#[cfg(feature = "denm")]
|
||||
c_its_parser::ItsMessage::DenmV2 {
|
||||
geonetworking: _,
|
||||
transport: _,
|
||||
etsi,
|
||||
} => etsi.encode_to_uper(),
|
||||
#[cfg(any(feature = "cam", feature = "cam_tx"))]
|
||||
c_its_parser::ItsMessage::Cam {
|
||||
geonetworking: _,
|
||||
transport: _,
|
||||
etsi,
|
||||
} => etsi.encode_to_uper(),
|
||||
#[cfg(feature = "spat")]
|
||||
c_its_parser::ItsMessage::Spatem {
|
||||
geonetworking: _,
|
||||
transport: _,
|
||||
etsi,
|
||||
} => etsi.encode_to_uper(),
|
||||
#[cfg(feature = "spat")]
|
||||
c_its_parser::ItsMessage::Mapem {
|
||||
geonetworking: _,
|
||||
transport: _,
|
||||
etsi,
|
||||
} => etsi.encode_to_uper(),
|
||||
}?;
|
||||
|
||||
Ok(Self {
|
||||
msg_type: msg_id.as_u8().into(),
|
||||
payload,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::init_test_env_logger;
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
impl From<msg::RawMsgTx> for msg::SerialInputMsg {
|
||||
fn from(value: msg::RawMsgTx) -> Self {
|
||||
use msg::serial_input_msg::Payload;
|
||||
|
||||
Self {
|
||||
payload: Some(Payload::TxMsg(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
impl From<msg::PositionState> for msg::SerialInputMsg {
|
||||
fn from(value: msg::PositionState) -> Self {
|
||||
use msg::serial_input_msg::Payload;
|
||||
|
||||
Self {
|
||||
payload: Some(Payload::Position(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// manual example data:
|
||||
// SerialInputMsg(RawMsgTx): 5632582b0040123e0a340202c173256817284029d5b3400f1feaf01ffffffc23b7743e00000fc0007e8138250737feebfff600000dffff7ffff1ce40400010011807220028020d0a
|
||||
// SerialInputMsg(PositionState): 5632582b00170a1509a0bbe2a29f010000120a0d00002842150000b8410d0a
|
||||
// PositionState: 09a0bbe2a29f010000120a0d00002842150000b841
|
||||
|
||||
#[cfg(feature = "uart")]
|
||||
#[test]
|
||||
fn uart_parser() {
|
||||
// generate some test UART input messages
|
||||
let raw_tx = msg::RawMsgTx {
|
||||
payload: crate::testdata::CAM_SMALL.to_vec(),
|
||||
message_type: 1,
|
||||
gn_transport: msg::GnTransport::Shb.into(),
|
||||
gn_area: msg::GnArea {
|
||||
area: None,
|
||||
position: None,
|
||||
dist_a: None,
|
||||
dist_b: None,
|
||||
angle: None,
|
||||
},
|
||||
hop_limit: 2,
|
||||
station_type: None,
|
||||
};
|
||||
let raw_tx_buf = serialize_serial_msg::<msg::SerialInputMsg>(&raw_tx.clone().into());
|
||||
|
||||
let pos_state = msg::PositionState {
|
||||
timestamp_ms: 1785144196000,
|
||||
position: msg::Position {
|
||||
latitude_deg: 42.,
|
||||
longitude_deg: 23.,
|
||||
},
|
||||
heading: None,
|
||||
speed: None,
|
||||
};
|
||||
let pos_state_buf = serialize_serial_msg::<msg::SerialInputMsg>(&pos_state.clone().into());
|
||||
|
||||
let mut parser = Parser::default();
|
||||
|
||||
// feed individual complete messages to parser
|
||||
let output = parser.parse(&raw_tx_buf);
|
||||
assert_eq!(1, output.len());
|
||||
assert_eq!(
|
||||
&msg::SerialInputMsg::from(raw_tx.clone()),
|
||||
output.get(0).unwrap()
|
||||
);
|
||||
|
||||
let output = parser.parse(&pos_state_buf);
|
||||
assert_eq!(1, output.len());
|
||||
assert_eq!(
|
||||
&msg::SerialInputMsg::from(pos_state.clone()),
|
||||
output.get(0).unwrap()
|
||||
);
|
||||
|
||||
// feed multiple complete messages to parser
|
||||
let mut combined_buf = pos_state_buf.clone();
|
||||
combined_buf.extend_from_slice(&raw_tx_buf);
|
||||
|
||||
let output = parser.parse(&combined_buf);
|
||||
assert_eq!(2, output.len());
|
||||
assert_eq!(
|
||||
&msg::SerialInputMsg::from(pos_state.clone()),
|
||||
output.get(0).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
&msg::SerialInputMsg::from(raw_tx.clone()),
|
||||
output.get(1).unwrap()
|
||||
);
|
||||
|
||||
// feed partial messages to parser
|
||||
let buf1 = &raw_tx_buf[0..3];
|
||||
let buf2 = &raw_tx_buf[3..];
|
||||
|
||||
let output = parser.parse(buf1);
|
||||
assert_eq!(0, output.len());
|
||||
|
||||
let output = parser.parse(buf2);
|
||||
assert_eq!(1, output.len());
|
||||
assert_eq!(
|
||||
&msg::SerialInputMsg::from(raw_tx.clone()),
|
||||
output.get(0).unwrap()
|
||||
);
|
||||
|
||||
// feed messages with garbage in between
|
||||
let mut buf1 = vec![0xabu8, 0xcd]; // prepend some garbage
|
||||
buf1.extend_from_slice(&raw_tx_buf[0..3]);
|
||||
let mut buf2 = (&raw_tx_buf[3..]).to_vec();
|
||||
buf2.extend_from_slice(&[0xfeu8, 0x42]); // append some garbage
|
||||
buf2.extend_from_slice(&pos_state_buf); // append second message
|
||||
buf2.extend_from_slice(&[0x11u8, 0x22]); // append some more garbage
|
||||
|
||||
let output = parser.parse(&buf1);
|
||||
assert_eq!(0, output.len());
|
||||
|
||||
let output = parser.parse(&buf2);
|
||||
assert_eq!(2, output.len());
|
||||
assert_eq!(
|
||||
&msg::SerialInputMsg::from(raw_tx.clone()),
|
||||
output.get(0).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
&msg::SerialInputMsg::from(pos_state.clone()),
|
||||
output.get(1).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
984
src/main.rs
984
src/main.rs
File diff suppressed because it is too large
Load diff
98
src/radio.rs
Normal file
98
src/radio.rs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
//! IEEE 802.11p C-ITS Capture with ESP32-C5
|
||||
|
||||
// -----------------------------
|
||||
// missing interface definitions
|
||||
// -----------------------------
|
||||
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
|
||||
use esp_radio::wifi::sniffer;
|
||||
|
||||
pub type Channel = core::ffi::c_int;
|
||||
|
||||
unsafe extern "C" {
|
||||
/// Changes the 802.11 PHY channel
|
||||
///
|
||||
/// C function signature: `void phy_change_channel(int,int,int,int)`
|
||||
///
|
||||
/// Options:
|
||||
/// - `primary`: Channel frequency in MHz
|
||||
/// - `ignored1`: Ignored (set to 1)
|
||||
/// - `ignored2`: Ignored (set to 0)
|
||||
/// - `ht_mode`: Probably HT mode
|
||||
unsafe fn phy_change_channel(
|
||||
primary: Channel,
|
||||
ignored1: core::ffi::c_int,
|
||||
ignored2: core::ffi::c_int,
|
||||
ht_mode: core::ffi::c_int,
|
||||
) -> core::ffi::c_int;
|
||||
|
||||
/// Enables 802.11p mode on the ESP32-C5
|
||||
///
|
||||
/// C function signature: `void phy_11p_set(int,int)`
|
||||
///
|
||||
/// Options:
|
||||
/// - `enable`: Boolean option to enable 802.11p mode
|
||||
/// - `zero`: Unknown, but need to be zero
|
||||
unsafe fn phy_11p_set(enable: u8, zero: u8);
|
||||
}
|
||||
|
||||
/// Enum values for `wifi_promiscuous_pkt_type_t` used in `esp_radio::wifi::sniffer::PromiscuousPkt::frame_type`
|
||||
///
|
||||
/// Original value is actually `core::ffi::c_uint`, but that can't be used in `#[repr()]` attributes,
|
||||
/// so we're using `u8` as the smallest suitable type here that can be casted to a bigger type.
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum PromiscuousPktType {
|
||||
/// Management frame, indicates 'buf' argument is `wifi_promiscuous_pkt_t`
|
||||
Mgmt = 0,
|
||||
/// Control frame, indicates 'buf' argument is `wifi_promiscuous_pkt_t`
|
||||
Ctrl = 1,
|
||||
/// Data frame, indicates 'buf' argument is `wifi_promiscuous_pkt_t`
|
||||
Data = 2,
|
||||
/// Other type, such as MIMO etc. 'buf' argument is `wifi_promiscuous_pkt_t` but the payload is zero length.
|
||||
Misc = 3,
|
||||
}
|
||||
|
||||
impl PromiscuousPktType {
|
||||
#[allow(dead_code)]
|
||||
pub fn as_repr(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
|
||||
/// Converts to underlying type of `wifi_promiscuous_pkt_type_t` (used by `esp_radio::wifi::sniffer::PromiscuousPkt::frame_type`)
|
||||
pub fn as_frame_type(self) -> core::ffi::c_uint {
|
||||
core::ffi::c_uint::from(self.as_repr())
|
||||
}
|
||||
|
||||
/// Determines if
|
||||
pub fn matches(self, frame_type: core::ffi::c_uint) -> bool {
|
||||
self.as_frame_type() == frame_type
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure WiFi Sniffer to a certain 802.11p channel
|
||||
pub fn setup_wifi_sniffer(
|
||||
channel: Channel,
|
||||
sniffer: &mut sniffer::Sniffer,
|
||||
callback: fn(sniffer::PromiscuousPkt<'_>),
|
||||
) -> Result<(), String> {
|
||||
sniffer
|
||||
.set_promiscuous_mode(true)
|
||||
.map_err(|err| format!("Failed to enable promiscuous mode: {err:?}"))?;
|
||||
|
||||
sniffer.set_receive_cb(callback);
|
||||
|
||||
// convert channel number to frequency in MHz
|
||||
let primary = 5000 + (channel * 5);
|
||||
|
||||
unsafe {
|
||||
phy_11p_set(1, 0);
|
||||
match phy_change_channel(primary, 1, 0, 0) {
|
||||
0 => Ok(()),
|
||||
ret => Err(format!("Failed to change channel: {ret}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
663
src/screen.rs
663
src/screen.rs
|
|
@ -1,663 +0,0 @@
|
|||
//! Screen Output
|
||||
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics::{geometry, mono_font, pixelcolor, primitives, text};
|
||||
|
||||
use crate::applogic;
|
||||
|
||||
pub struct DisplaySize {
|
||||
width: u16,
|
||||
height: u16,
|
||||
offset_top: i32,
|
||||
corner_radius: i32,
|
||||
}
|
||||
|
||||
pub struct Handler<D>
|
||||
where
|
||||
D: DrawTarget<Color = pixelcolor::Rgb565>,
|
||||
{
|
||||
target: D,
|
||||
size: DisplaySize,
|
||||
|
||||
// internal state
|
||||
prev_state: ScreenState,
|
||||
hb_state: bool,
|
||||
gnss_hb_state: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub enum ScreenState {
|
||||
#[default]
|
||||
Uninitialized,
|
||||
/// aka. "no GNSS fix" or "splash screen"
|
||||
Initial,
|
||||
Error(alloc::string::String),
|
||||
#[cfg(feature = "spat")]
|
||||
GlosaSearching(applogic::GlosaSearching),
|
||||
#[cfg(feature = "spat")]
|
||||
GlosaFound(applogic::GlosaFound),
|
||||
#[cfg(feature = "spat")]
|
||||
GlosaLocked(applogic::GlosaSignalInfo),
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
impl From<applogic::GlosaOutput> for ScreenState {
|
||||
fn from(value: applogic::GlosaOutput) -> Self {
|
||||
match value {
|
||||
applogic::GlosaOutput::Searching(glosa_searching) => {
|
||||
Self::GlosaSearching(glosa_searching)
|
||||
}
|
||||
applogic::GlosaOutput::Found(glosa_found) => Self::GlosaFound(glosa_found),
|
||||
applogic::GlosaOutput::Locked(glosa_signal_info) => {
|
||||
Self::GlosaLocked(glosa_signal_info)
|
||||
}
|
||||
applogic::GlosaOutput::Error(err_str) => Self::Error(err_str),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Handler<D>
|
||||
where
|
||||
D: DrawTarget<Color = pixelcolor::Rgb565>,
|
||||
{
|
||||
const COLOR_BG: pixelcolor::Rgb565 = pixelcolor::Rgb565::BLACK;
|
||||
const COLOR_FG: pixelcolor::Rgb565 = pixelcolor::Rgb565::WHITE;
|
||||
|
||||
const BK_STYLE: primitives::PrimitiveStyle<pixelcolor::Rgb565> =
|
||||
primitives::PrimitiveStyleBuilder::new()
|
||||
.fill_color(Self::COLOR_BG)
|
||||
.build();
|
||||
const RD_STYLE: primitives::PrimitiveStyle<pixelcolor::Rgb565> =
|
||||
primitives::PrimitiveStyleBuilder::new()
|
||||
.fill_color(pixelcolor::Rgb565::CSS_RED)
|
||||
.build();
|
||||
const YE_STYLE: primitives::PrimitiveStyle<pixelcolor::Rgb565> =
|
||||
primitives::PrimitiveStyleBuilder::new()
|
||||
.fill_color(pixelcolor::Rgb565::new(31, 55, 0))
|
||||
.build();
|
||||
const GN_STYLE: primitives::PrimitiveStyle<pixelcolor::Rgb565> =
|
||||
primitives::PrimitiveStyleBuilder::new()
|
||||
.fill_color(pixelcolor::Rgb565::new(10, 50, 0))
|
||||
.build();
|
||||
const BLANK_SIGNAL_STYLE: primitives::PrimitiveStyle<pixelcolor::Rgb565> =
|
||||
primitives::PrimitiveStyleBuilder::new()
|
||||
.fill_color(pixelcolor::Rgb565::CSS_GRAY)
|
||||
.build();
|
||||
|
||||
const DEFAULT_TEXT_STYLE: mono_font::MonoTextStyle<'static, pixelcolor::Rgb565> =
|
||||
mono_font::MonoTextStyle::new(&profont::PROFONT_24_POINT, Self::COLOR_FG);
|
||||
const MID_TEXT_STYLE: mono_font::MonoTextStyle<'static, pixelcolor::Rgb565> =
|
||||
mono_font::MonoTextStyle::new(&profont::PROFONT_18_POINT, Self::COLOR_FG);
|
||||
const SMALL_TEXT_STYLE: mono_font::MonoTextStyle<'static, pixelcolor::Rgb565> =
|
||||
mono_font::MonoTextStyle::new(&profont::PROFONT_14_POINT, Self::COLOR_FG);
|
||||
|
||||
const BIG_HEIGHT_THLD: u16 = 172;
|
||||
|
||||
pub fn new(target: D, display_size: DisplaySize) -> Self {
|
||||
Self {
|
||||
target,
|
||||
size: display_size,
|
||||
prev_state: ScreenState::Uninitialized,
|
||||
hb_state: false,
|
||||
gnss_hb_state: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, state: ScreenState) -> Result<(), D::Error> {
|
||||
if self.prev_state != state {
|
||||
match &state {
|
||||
ScreenState::Uninitialized | ScreenState::Initial => self.draw_slash_screen()?,
|
||||
ScreenState::GlosaSearching(_glosa_searching) => self.draw_glosa_searching()?,
|
||||
ScreenState::GlosaFound(glosa_found) => self.draw_glosa_found(glosa_found)?,
|
||||
ScreenState::GlosaLocked(glosa_data) => {
|
||||
let prev_glosa_data =
|
||||
if let ScreenState::GlosaLocked(prev_glosa_data) = &self.prev_state {
|
||||
Some(prev_glosa_data.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.draw_glosa(glosa_data, prev_glosa_data)?;
|
||||
}
|
||||
ScreenState::Error(msg) => self.draw_message(msg)?,
|
||||
}
|
||||
}
|
||||
|
||||
// Update indicator/ heartbeat
|
||||
{
|
||||
let update_ind_dia = 10;
|
||||
let circle_x_pos =
|
||||
i32::from(self.size.width) - update_ind_dia - self.size.corner_radius;
|
||||
|
||||
text::Text::with_alignment(
|
||||
"HB",
|
||||
geometry::Point::new(circle_x_pos - 5, 10 + self.size.offset_top),
|
||||
Self::SMALL_TEXT_STYLE,
|
||||
text::Alignment::Right,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
primitives::Circle::new(
|
||||
geometry::Point::new(circle_x_pos, 2 + self.size.offset_top),
|
||||
update_ind_dia.cast_unsigned(),
|
||||
)
|
||||
.into_styled(if self.hb_state {
|
||||
Self::GN_STYLE
|
||||
} else {
|
||||
Self::BK_STYLE
|
||||
})
|
||||
.draw(&mut self.target)?;
|
||||
}
|
||||
|
||||
self.prev_state = state;
|
||||
self.hb_state = !self.hb_state;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// on big screns: uses full screen width and height from 250 to 280 pixels
|
||||
pub fn update_gnss(
|
||||
&mut self,
|
||||
pvt: &ublox::nav_pvt::proto27::NavPvtRef,
|
||||
) -> Result<(), D::Error> {
|
||||
use alloc::string::ToString as _;
|
||||
|
||||
// this only applies to big screens
|
||||
if !self.is_big_height() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let num_sat = pvt.num_satellites();
|
||||
let fix_str = match pvt.fix_type() {
|
||||
ublox::GnssFixType::NoFix
|
||||
| ublox::GnssFixType::DeadReckoningOnly
|
||||
| ublox::GnssFixType::TimeOnlyFix => {
|
||||
alloc::format!("{num_sat:2} sat")
|
||||
}
|
||||
ublox::GnssFixType::Fix2D => "2D fix".to_string(),
|
||||
ublox::GnssFixType::Fix3D | ublox::GnssFixType::GPSPlusDeadReckoning => {
|
||||
"3D fix".to_string()
|
||||
}
|
||||
_ => " ".to_string(),
|
||||
};
|
||||
|
||||
// clear area and write information
|
||||
// note: text position is x baseline, not top left corner
|
||||
primitives::Rectangle::new(
|
||||
geometry::Point::new(0, 250 + self.size.offset_top),
|
||||
geometry::Size::new(self.size.width.into(), 20),
|
||||
)
|
||||
.into_styled(Self::BK_STYLE)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
let colon = if self.gnss_hb_state { ":" } else { " " };
|
||||
let message = alloc::format!(
|
||||
"{fix_str}{colon} {:2.0}km/h, {:3.0}°",
|
||||
pvt.ground_speed_2d() * 3.6,
|
||||
pvt.heading_motion()
|
||||
);
|
||||
text::Text::with_alignment(
|
||||
&message,
|
||||
geometry::Point::new(self.size.corner_radius / 2, 265 + self.size.offset_top),
|
||||
Self::SMALL_TEXT_STYLE,
|
||||
text::Alignment::Left,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
self.gnss_hb_state = !self.gnss_hb_state;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear(&mut self) -> Result<(), D::Error>
|
||||
where
|
||||
D: DrawTarget<Color = pixelcolor::Rgb565>,
|
||||
{
|
||||
primitives::Rectangle::new(
|
||||
geometry::Point::new(0, 0),
|
||||
geometry::Size::new(self.size.width.into(), self.size.height.into()),
|
||||
)
|
||||
.into_styled(Self::BK_STYLE)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_big_height(&self) -> bool {
|
||||
self.size.height > Self::BIG_HEIGHT_THLD
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn test_img(&mut self) -> Result<(), D::Error> {
|
||||
mipidsi::TestImage::new().draw(&mut self.target)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn draw_message(&mut self, msg: &str) -> Result<(), D::Error> {
|
||||
// clear whole display and draw text
|
||||
self.clear()?;
|
||||
|
||||
text::Text::with_alignment(
|
||||
msg,
|
||||
geometry::Point::new(i32::from(self.size.width) / 2, 40 + self.size.offset_top),
|
||||
Self::DEFAULT_TEXT_STYLE,
|
||||
text::Alignment::Center,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn draw_glosa_searching(&mut self) -> Result<(), D::Error> {
|
||||
// clear whole display and draw text
|
||||
self.clear()?;
|
||||
|
||||
text::Text::with_alignment(
|
||||
"Searching...",
|
||||
geometry::Point::new(i32::from(self.size.width) / 2, 130 + self.size.offset_top),
|
||||
Self::DEFAULT_TEXT_STYLE,
|
||||
text::Alignment::Center,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn draw_glosa_found(&mut self, data: &applogic::GlosaFound) -> Result<(), D::Error> {
|
||||
// clear whole display and draw text
|
||||
self.clear()?;
|
||||
|
||||
let text = if let Some(appr) = data.approach_id {
|
||||
alloc::format!("Int. {}\nApproach {appr}", data.intersection_id)
|
||||
} else {
|
||||
alloc::format!("Int. {}\nno approach ID available", data.intersection_id)
|
||||
};
|
||||
|
||||
text::Text::with_alignment(
|
||||
&text,
|
||||
geometry::Point::new(i32::from(self.size.width) / 2, 130 + self.size.offset_top),
|
||||
Self::DEFAULT_TEXT_STYLE,
|
||||
text::Alignment::Center,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// uses full screen width and height
|
||||
// - from 0 to 105+67 = 172 pixels for small screens
|
||||
// - from 0 to 105+67+20 = 192 pixels for big screens
|
||||
#[allow(unused)]
|
||||
fn draw_glosa(
|
||||
&mut self,
|
||||
data: &applogic::GlosaSignalInfo,
|
||||
prev_data: Option<applogic::GlosaSignalInfo>,
|
||||
) -> Result<(), D::Error> {
|
||||
let num_signals = data.signal_groups.len();
|
||||
|
||||
let is_big_height = self.is_big_height();
|
||||
|
||||
if let Some(prev) = prev_data
|
||||
&& prev.signal_groups.len() != num_signals
|
||||
{
|
||||
// clear everything if number of signals has changed
|
||||
self.clear();
|
||||
} else {
|
||||
// only clear maneuver and timing area otherwise
|
||||
let sig_total_height = 67 + if is_big_height { 20 } else { 0 };
|
||||
primitives::Rectangle::new(
|
||||
geometry::Point::new(0, 105 + self.size.offset_top),
|
||||
geometry::Size::new(self.size.width.into(), sig_total_height),
|
||||
)
|
||||
.into_styled(Self::BK_STYLE)
|
||||
.draw(&mut self.target)?;
|
||||
}
|
||||
|
||||
// do nothing more when no signals
|
||||
if num_signals == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// draw intersection ID
|
||||
text::Text::new(
|
||||
&alloc::format!("#{}", data.intersection_id),
|
||||
geometry::Point::new(self.size.corner_radius + 5, 10 + self.size.offset_top),
|
||||
Self::SMALL_TEXT_STYLE,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
|
||||
let sig_width = i32::from(self.size.width) / (num_signals as i32);
|
||||
|
||||
for (idx, sig) in data.signal_groups.iter().enumerate() {
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
|
||||
let offset = (idx as i32) * sig_width;
|
||||
|
||||
let circle_dia = 50;
|
||||
let circle_margin = (sig_width - circle_dia) / 2;
|
||||
let centerline = offset + (sig_width / 2);
|
||||
|
||||
// draw phase color
|
||||
let style = match sig.phase {
|
||||
applogic::SignalPhase::Unknown => Self::BLANK_SIGNAL_STYLE,
|
||||
applogic::SignalPhase::Red => Self::RD_STYLE,
|
||||
applogic::SignalPhase::Green => Self::GN_STYLE,
|
||||
applogic::SignalPhase::RedYellow | applogic::SignalPhase::Yellow => Self::YE_STYLE,
|
||||
};
|
||||
primitives::Circle::new(
|
||||
geometry::Point::new(offset + circle_margin, 15 + self.size.offset_top),
|
||||
circle_dia.cast_unsigned(),
|
||||
)
|
||||
.into_styled(style)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
// TODO: draw lane type?
|
||||
|
||||
// draw maneuver
|
||||
text::Text::with_alignment(
|
||||
&maneuver_to_str(sig.maneuver),
|
||||
geometry::Point::new(centerline, 130 + self.size.offset_top),
|
||||
Self::DEFAULT_TEXT_STYLE,
|
||||
text::Alignment::Center,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
// draw timing indicator(s)
|
||||
if let Some(timing) = &sig.timing {
|
||||
if let (Some(likely), Some(max)) = (timing.likely_end_sec, timing.max_end_sec) {
|
||||
text::Text::with_alignment(
|
||||
&alloc::format!("{likely}s"),
|
||||
geometry::Point::new(centerline, 160 + self.size.offset_top),
|
||||
Self::DEFAULT_TEXT_STYLE,
|
||||
text::Alignment::Center,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
// only for big screen
|
||||
if is_big_height {
|
||||
// TODO: show as diff to likely time?
|
||||
text::Text::with_alignment(
|
||||
&alloc::format!("{}-{max}", timing.min_end_sec),
|
||||
geometry::Point::new(centerline, 185 + self.size.offset_top),
|
||||
Self::MID_TEXT_STYLE,
|
||||
text::Alignment::Center,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
}
|
||||
} else {
|
||||
text::Text::with_alignment(
|
||||
&alloc::format!("{}s", timing.min_end_sec),
|
||||
geometry::Point::new(centerline, 160 + self.size.offset_top),
|
||||
Self::DEFAULT_TEXT_STYLE,
|
||||
text::Alignment::Center,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if is_big_height {
|
||||
// // TODO: Add ETA
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn draw_slash_screen(&mut self) -> Result<(), D::Error> {
|
||||
let text_style = mono_font::MonoTextStyle::new(&profont::PROFONT_18_POINT, Self::COLOR_FG);
|
||||
|
||||
self.clear()?;
|
||||
|
||||
let sig_width = i32::from(self.size.width) / 3;
|
||||
let circle_dia = 50;
|
||||
let circle_margin = (sig_width - circle_dia) / 2;
|
||||
|
||||
for (idx, style) in [Self::RD_STYLE, Self::YE_STYLE, Self::GN_STYLE]
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
|
||||
let offset = (idx as i32) * sig_width;
|
||||
|
||||
primitives::Circle::new(
|
||||
geometry::Point::new(offset + circle_margin, 15 + self.size.offset_top),
|
||||
circle_dia.cast_unsigned(),
|
||||
)
|
||||
.into_styled(*style)
|
||||
.draw(&mut self.target)?;
|
||||
}
|
||||
|
||||
// draw welcome text
|
||||
let centerline = i32::from(self.size.width) / 2;
|
||||
text::Text::with_alignment(
|
||||
"C-ITS Signal Phase\nWaiting for GNSS...",
|
||||
geometry::Point::new(centerline, 130 + self.size.offset_top),
|
||||
text_style,
|
||||
text::Alignment::Center,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn maneuver_to_str(value: applogic::v2x::map::Maneuvers) -> alloc::string::String {
|
||||
alloc::format!(
|
||||
"{}{}{}",
|
||||
if value.left_allowed { "<" } else { " " },
|
||||
if value.staight_allowed { "^" } else { " " },
|
||||
if value.right_allowed { ">" } else { " " },
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates an [`mipidsi::Display`], width, height tuple for an ST7789 172*320px screen in landscape orientation
|
||||
///
|
||||
/// # Errors
|
||||
/// Human-readable fatal errors
|
||||
#[allow(unused, clippy::too_many_arguments, clippy::type_complexity)]
|
||||
pub fn make_small_display<IoScl, IoSda, IoRes, IoDc, IoCs, IoBl, Spi>(
|
||||
scl: IoScl,
|
||||
sda: IoSda,
|
||||
res: IoRes,
|
||||
dc: IoDc,
|
||||
cs: IoCs,
|
||||
bl: IoBl,
|
||||
spi: Spi,
|
||||
spi_buffer: &mut [u8],
|
||||
) -> Result<
|
||||
(
|
||||
mipidsi::Display<
|
||||
mipidsi::interface::SpiInterface<
|
||||
'_,
|
||||
embedded_hal_bus::spi::ExclusiveDevice<
|
||||
esp_hal::spi::master::Spi<'_, esp_hal::Blocking>,
|
||||
esp_hal::gpio::Output<'_>,
|
||||
embedded_hal_bus::spi::NoDelay,
|
||||
>,
|
||||
esp_hal::gpio::Output<'_>,
|
||||
>,
|
||||
mipidsi::models::ST7789,
|
||||
esp_hal::gpio::Output<'_>,
|
||||
>,
|
||||
DisplaySize,
|
||||
),
|
||||
alloc::string::String,
|
||||
>
|
||||
where
|
||||
IoScl: esp_hal::gpio::interconnect::PeripheralOutput<'static>,
|
||||
IoSda: esp_hal::gpio::interconnect::PeripheralOutput<'static>,
|
||||
IoRes:
|
||||
esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static,
|
||||
IoDc:
|
||||
esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static,
|
||||
IoCs:
|
||||
esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static,
|
||||
IoBl:
|
||||
esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static,
|
||||
Spi: esp_hal::spi::master::Instance + 'static,
|
||||
{
|
||||
const DISPLAY_HIDDEN_X: u16 = 34; // somehow this display has 34 px of invisible space on the left (in native orientation)
|
||||
|
||||
let size = DisplaySize {
|
||||
width: 320,
|
||||
height: 172,
|
||||
offset_top: 0,
|
||||
corner_radius: 10,
|
||||
};
|
||||
|
||||
let (spi, cs) = setup_st7789_spi(scl, sda, cs, spi)
|
||||
.map_err(|err| alloc::format!("Failed to build SPI config: {err}"))?;
|
||||
|
||||
let output_config = esp_hal::gpio::OutputConfig::default();
|
||||
let dc_output = esp_hal::gpio::Output::new(dc, esp_hal::gpio::Level::Low, output_config);
|
||||
let rst_output = esp_hal::gpio::Output::new(res, esp_hal::gpio::Level::High, output_config);
|
||||
let _ = esp_hal::gpio::Output::new(bl, esp_hal::gpio::Level::High, output_config); // enable backlight
|
||||
|
||||
let mut display_delay = esp_hal::delay::Delay::new();
|
||||
|
||||
let spi_device = embedded_hal_bus::spi::ExclusiveDevice::new_no_delay(spi, cs)
|
||||
.map_err(|err| alloc::format!("Failed to initialize SPI device: {err}"))?;
|
||||
|
||||
// Note: height and width are swapped b/c of 90° display rotation
|
||||
let display = mipidsi::Builder::new(
|
||||
mipidsi::models::ST7789,
|
||||
mipidsi::interface::SpiInterface::new(spi_device, dc_output, spi_buffer),
|
||||
)
|
||||
.display_size(size.height + DISPLAY_HIDDEN_X, size.width)
|
||||
.reset_pin(rst_output)
|
||||
.invert_colors(mipidsi::options::ColorInversion::Inverted)
|
||||
.orientation(mipidsi::options::Orientation::new().rotate(mipidsi::options::Rotation::Deg90))
|
||||
.init(&mut display_delay)
|
||||
.map_err(|err| alloc::format!("Failed to initialize display: {err:?}"))?;
|
||||
|
||||
Ok((display, size))
|
||||
}
|
||||
|
||||
/// Creates an [`mipidsi::Display`], width, height tuple for an ST7789 240*280px screen in portrait orientation
|
||||
///
|
||||
/// # Errors
|
||||
/// Human-readable fatal errors
|
||||
#[allow(unused, clippy::too_many_arguments, clippy::type_complexity)]
|
||||
pub fn make_large_display<IoScl, IoSda, IoRes, IoDc, IoCs, IoBl, Spi>(
|
||||
scl: IoScl,
|
||||
sda: IoSda,
|
||||
res: IoRes,
|
||||
dc: IoDc,
|
||||
cs: IoCs,
|
||||
bl: IoBl,
|
||||
spi: Spi,
|
||||
spi_buffer: &mut [u8],
|
||||
) -> Result<
|
||||
(
|
||||
mipidsi::Display<
|
||||
mipidsi::interface::SpiInterface<
|
||||
'_,
|
||||
embedded_hal_bus::spi::ExclusiveDevice<
|
||||
esp_hal::spi::master::Spi<'_, esp_hal::Blocking>,
|
||||
esp_hal::gpio::Output<'_>,
|
||||
embedded_hal_bus::spi::NoDelay,
|
||||
>,
|
||||
esp_hal::gpio::Output<'_>,
|
||||
>,
|
||||
mipidsi::models::ST7789,
|
||||
esp_hal::gpio::Output<'_>,
|
||||
>,
|
||||
DisplaySize,
|
||||
),
|
||||
alloc::string::String,
|
||||
>
|
||||
where
|
||||
IoScl: esp_hal::gpio::interconnect::PeripheralOutput<'static>,
|
||||
IoSda: esp_hal::gpio::interconnect::PeripheralOutput<'static>,
|
||||
IoRes:
|
||||
esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static,
|
||||
IoDc:
|
||||
esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static,
|
||||
IoCs:
|
||||
esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static,
|
||||
IoBl:
|
||||
esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static,
|
||||
Spi: esp_hal::spi::master::Instance + 'static,
|
||||
{
|
||||
const DISPLAY_HIDDEN_Y: u16 = 20; // somehow this display has 20 px of invisible space on the top (in native orientation)
|
||||
|
||||
let size = DisplaySize {
|
||||
width: 240,
|
||||
height: 280 + DISPLAY_HIDDEN_Y,
|
||||
offset_top: DISPLAY_HIDDEN_Y.into(),
|
||||
corner_radius: 30,
|
||||
};
|
||||
|
||||
let (spi, cs) = setup_st7789_spi(scl, sda, cs, spi)
|
||||
.map_err(|err| alloc::format!("Failed to build SPI config: {err}"))?;
|
||||
|
||||
let output_config = esp_hal::gpio::OutputConfig::default();
|
||||
let dc_output = esp_hal::gpio::Output::new(dc, esp_hal::gpio::Level::Low, output_config);
|
||||
let rst_output = esp_hal::gpio::Output::new(res, esp_hal::gpio::Level::High, output_config);
|
||||
let _ = esp_hal::gpio::Output::new(bl, esp_hal::gpio::Level::High, output_config); // enable backlight
|
||||
|
||||
let mut display_delay = esp_hal::delay::Delay::new();
|
||||
|
||||
let spi_device = embedded_hal_bus::spi::ExclusiveDevice::new_no_delay(spi, cs)
|
||||
.map_err(|err| alloc::format!("Failed to initialize SPI device: {err}"))?;
|
||||
|
||||
let display = mipidsi::Builder::new(
|
||||
mipidsi::models::ST7789,
|
||||
mipidsi::interface::SpiInterface::new(spi_device, dc_output, spi_buffer),
|
||||
)
|
||||
.display_size(size.width, size.height + DISPLAY_HIDDEN_Y)
|
||||
.reset_pin(rst_output)
|
||||
.invert_colors(mipidsi::options::ColorInversion::Inverted)
|
||||
.orientation(mipidsi::options::Orientation::new().rotate(mipidsi::options::Rotation::Deg0))
|
||||
.init(&mut display_delay)
|
||||
.map_err(|err| alloc::format!("Failed to initialize display: {err:?}"))?;
|
||||
|
||||
Ok((display, size))
|
||||
}
|
||||
|
||||
/// Builds the SPI interface for the ST7789 display
|
||||
///
|
||||
/// Returns (spi master, chip-select) tuple
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns SPI config error when SPI setup failed
|
||||
fn setup_st7789_spi<IoScl, IoSda, IoCs, Spi>(
|
||||
scl: IoScl,
|
||||
sda: IoSda,
|
||||
cs: IoCs,
|
||||
spi: Spi,
|
||||
) -> Result<
|
||||
(
|
||||
esp_hal::spi::master::Spi<'static, esp_hal::Blocking>,
|
||||
esp_hal::gpio::Output<'static>,
|
||||
),
|
||||
esp_hal::spi::master::ConfigError,
|
||||
>
|
||||
where
|
||||
IoScl: esp_hal::gpio::interconnect::PeripheralOutput<'static>,
|
||||
IoSda: esp_hal::gpio::interconnect::PeripheralOutput<'static>,
|
||||
IoCs:
|
||||
esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static,
|
||||
Spi: esp_hal::spi::master::Instance + 'static,
|
||||
{
|
||||
let spi_config = esp_hal::spi::master::Config::default()
|
||||
.with_frequency(esp_hal::time::Rate::from_mhz(40))
|
||||
.with_mode(esp_hal::spi::Mode::_0);
|
||||
let spi: esp_hal::spi::master::Spi<'_, esp_hal::Blocking> =
|
||||
esp_hal::spi::master::Spi::new(spi, spi_config)?
|
||||
.with_sck(scl)
|
||||
.with_mosi(sda);
|
||||
|
||||
let cs_output = esp_hal::gpio::Output::new(
|
||||
cs,
|
||||
esp_hal::gpio::Level::High,
|
||||
esp_hal::gpio::OutputConfig::default(),
|
||||
);
|
||||
|
||||
Ok((spi, cs_output))
|
||||
}
|
||||
24
src/test.rs
24
src/test.rs
|
|
@ -1,24 +0,0 @@
|
|||
#![cfg(not(target_arch = "riscv32"))]
|
||||
#![allow(unused)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod applogic;
|
||||
mod cache;
|
||||
mod geo_alg;
|
||||
mod io;
|
||||
mod testdata;
|
||||
|
||||
fn main() {
|
||||
// env_logger::init();
|
||||
// dummy only
|
||||
}
|
||||
|
||||
/// Initializes the env-logger to be used inside tests
|
||||
fn init_test_env_logger() {
|
||||
let _ = env_logger::builder()
|
||||
.format_timestamp(None)
|
||||
.filter_level(log::LevelFilter::Debug)
|
||||
.is_test(true)
|
||||
.try_init();
|
||||
}
|
||||
465
src/testdata.rs
465
src/testdata.rs
|
|
@ -1,465 +0,0 @@
|
|||
//! Central provider for V2X test messages
|
||||
//!
|
||||
//! All messages are without any headers (so they start with the ItsPduHeader)
|
||||
|
||||
#![cfg(all(not(target_arch = "riscv32"), any(feature = "spat", feature = "cam")))]
|
||||
#![allow(unused)]
|
||||
|
||||
/// Intersection "Sievekingplatz-Südfahrbahn/Ziviljustizgebäude"
|
||||
///
|
||||
/// Pedestrian crossing over a one-way street (going from west to east)
|
||||
///
|
||||
/// Possible start point (heading 90° to 135°):
|
||||
/// - in front: `geo_types::Point::new(9.9752864, 53.5564028)`
|
||||
/// - at lane: `geo_types::Point::new(9.9760764, 53.5563621)`
|
||||
/// - passed: `geo_types::Point::new(9.977217, 53.5560602)`
|
||||
#[cfg(feature = "spat")]
|
||||
pub const MAPEM_HH_1328: &[u8] = &[
|
||||
0x2, 0x5, 0x0, 0x12, 0x19, 0x8c, 0x8, 0x0, 0x3, 0x9, 0xcd, 0x83, 0x42, 0xfc, 0x9a, 0x94, 0xef,
|
||||
0xb1, 0x67, 0x71, 0x8b, 0x36, 0x4e, 0x2e, 0x31, 0x60, 0xb9, 0xa4, 0x0, 0xc, 0x14, 0xc0, 0x20,
|
||||
0xab, 0x21, 0xda, 0x5c, 0xe2, 0x78, 0x72, 0x74, 0x5, 0x14, 0x18, 0x90, 0x4, 0x50, 0x80, 0x0,
|
||||
0x0, 0x85, 0xc, 0x5c, 0xa8, 0xa0, 0x0, 0x80, 0x23, 0xc1, 0x42, 0x7e, 0xa8, 0x58, 0x46, 0x1d,
|
||||
0xa0, 0x11, 0xef, 0x4d, 0xf0, 0x45, 0xa5, 0x56, 0x8, 0x2d, 0xa6, 0x2, 0x30, 0xc9, 0x26, 0x11,
|
||||
0xb7, 0xc5, 0x90, 0x84, 0xca, 0x16, 0xc1, 0x60, 0x70, 0x0, 0x2, 0x2, 0x20, 0x6, 0x44, 0x40,
|
||||
0x0, 0x0, 0x0, 0x8f, 0x5, 0xf8, 0x83, 0xed, 0x8c, 0x19, 0x20, 0x10, 0xa1, 0x0, 0x0, 0x1, 0xa,
|
||||
0x23, 0x2a, 0x63, 0x40, 0x1, 0x9, 0xb9, 0xa7, 0x40, 0x25, 0xec, 0x21, 0x17, 0xad, 0x3c, 0x8,
|
||||
0xef, 0x66, 0xfc, 0x47, 0xa1, 0x2e, 0x82, 0x39, 0x99, 0x2b, 0x11, 0xd2, 0xc7, 0x8, 0x8f, 0x32,
|
||||
0x18, 0x4, 0x70, 0x50, 0x8c, 0x2b, 0x5, 0x80, 0x0, 0x10, 0x25, 0x82, 0x40, 0x0, 0x8, 0x18,
|
||||
0x80, 0x29, 0x11, 0x0, 0x0, 0x0, 0x1, 0x17, 0x14, 0x80, 0x3f, 0x70, 0xf8, 0x88, 0x2, 0x11,
|
||||
0x10, 0x0, 0x0, 0x0, 0x11, 0x23, 0x38, 0x83, 0xe5, 0x8c, 0x90, 0x20, 0x33, 0x2, 0x4, 0x0, 0x0,
|
||||
0x1, 0x43, 0xd6, 0xe2, 0x8, 0x0, 0x20, 0x72, 0x5a, 0xd0, 0x50, 0x38, 0x10, 0x20, 0x20, 0x3b,
|
||||
0x2, 0x4, 0x0, 0x0, 0x1, 0x20, 0x23, 0x20, 0xa0, 0x0, 0x82, 0x37, 0x94, 0xc1, 0x40, 0xc0, 0x40,
|
||||
0xa0,
|
||||
];
|
||||
|
||||
/// Intersection "Johannisbollwerk/Ditmar-Koel-Straße"
|
||||
///
|
||||
/// Has a very complex geometry with multiple stop lines after each other.
|
||||
/// Main traffic direction is east-west, has separate bike lanes.
|
||||
///
|
||||
/// Possible start point (heading 90°): TODO
|
||||
#[cfg(feature = "spat")]
|
||||
pub const MAPEM_HH_2053: &[u8] = &[
|
||||
0x2, 0x5, 0x0, 0x12, 0x43, 0x84, 0x8, 0x0, 0x3, 0x9, 0xcd, 0x83, 0x42, 0xfc, 0x9a, 0x94, 0xef,
|
||||
0xb2, 0x61, 0x71, 0x93, 0x6, 0xac, 0xee, 0x31, 0x66, 0xb9, 0x94, 0x0, 0xc, 0x20, 0x14, 0x10,
|
||||
0xab, 0x1e, 0x9c, 0x96, 0xe2, 0x77, 0x51, 0x1e, 0x5, 0x14, 0xc8, 0x90, 0x1c, 0xd0, 0x88, 0x0,
|
||||
0x0, 0x15, 0x8f, 0x5d, 0xb6, 0x88, 0x0, 0x20, 0xc, 0x8e, 0xb0, 0x1a, 0x4a, 0xb8, 0x2c, 0xa,
|
||||
0x0, 0x0, 0x40, 0x44, 0x0, 0x84, 0x88, 0x80, 0x0, 0x1, 0x9, 0xdd, 0xc0, 0x41, 0xdd, 0x54, 0xb0,
|
||||
0x59, 0xe9, 0x39, 0x4, 0x80, 0x64, 0x84, 0x0, 0x0, 0x0, 0xaa, 0x39, 0xc9, 0x5d, 0x0, 0x4, 0x5c,
|
||||
0x8c, 0x91, 0x84, 0xd8, 0x75, 0x3d, 0x21, 0x58, 0x34, 0x0, 0x3, 0x1, 0x2c, 0x8, 0x80, 0x0,
|
||||
0x81, 0x4, 0x1, 0x90, 0x88, 0x0, 0x0, 0x1, 0x18, 0xe6, 0x61, 0xa8, 0x13, 0xaf, 0x1d, 0xc0,
|
||||
0x98, 0xc4, 0x44, 0x80, 0x84, 0x84, 0x0, 0x0, 0x0, 0xaa, 0x11, 0x44, 0xe9, 0x0, 0x4, 0x5c,
|
||||
0x8c, 0x91, 0x84, 0xd8, 0xdd, 0x3c, 0x50, 0x58, 0x2c, 0x0, 0x3, 0x1, 0x88, 0x2, 0xa1, 0x10,
|
||||
0x0, 0x0, 0x2, 0x31, 0xa3, 0x3e, 0x34, 0x27, 0x7a, 0x38, 0x81, 0x38, 0x8a, 0xc9, 0x0, 0x45,
|
||||
0x8, 0x0, 0x0, 0x1, 0x48, 0x1a, 0xd6, 0x68, 0x0, 0x22, 0xcf, 0xe9, 0xc7, 0x1e, 0x92, 0xe7,
|
||||
0x46, 0x2b, 0x6, 0x80, 0x0, 0x30, 0x55, 0x82, 0xc0, 0x0, 0x18, 0x31, 0x21, 0x34, 0x21, 0x0,
|
||||
0x0, 0x0, 0x2b, 0x4c, 0x89, 0xc, 0x90, 0x0, 0x44, 0x3d, 0xb5, 0x5a, 0x12, 0x39, 0x48, 0xc1,
|
||||
0x65, 0x10, 0x0, 0x26, 0xe, 0x20, 0x50, 0xe4, 0x40, 0x0, 0x0, 0x8, 0xda, 0x2, 0x25, 0x30, 0xe3,
|
||||
0x85, 0x9c, 0x1e, 0x1e, 0x92, 0x89, 0x9, 0x61, 0x8, 0x0, 0x0, 0x1, 0x5a, 0x3e, 0x86, 0x18,
|
||||
0x80, 0x2, 0x21, 0xcb, 0xaa, 0x80, 0x94, 0x8a, 0x1c, 0xb, 0x29, 0x80, 0x1, 0x30, 0x81, 0x2,
|
||||
0x97, 0x22, 0x0, 0x0, 0x0, 0x46, 0xc5, 0xb0, 0x9a, 0x7, 0xc, 0x2d, 0xe0, 0xf3, 0x54, 0x88,
|
||||
0x48, 0x4f, 0x8, 0x4, 0x20, 0x0, 0x0, 0x15, 0xa2, 0x28, 0x44, 0x88, 0x80, 0x2b, 0x50, 0x87,
|
||||
0x5e, 0xa8, 0xc2, 0x1f, 0xaa, 0x0, 0x2c, 0xaa, 0x0, 0x4, 0xc2, 0x44, 0xa, 0x9c, 0x80, 0x84,
|
||||
0x0, 0x0, 0x2, 0xb5, 0xec, 0x1, 0x40, 0x11, 0x6a, 0xe, 0x75, 0x55, 0xc1, 0xdb, 0x69, 0xc4,
|
||||
0x90, 0x7d, 0x90, 0x8, 0x40, 0x0, 0x0, 0x6d, 0x5e, 0x40, 0x79, 0xb, 0x44, 0x1, 0x54, 0x46,
|
||||
0x7e, 0x8, 0x37, 0x88, 0x6f, 0x23, 0xd0, 0x44, 0x91, 0x34, 0x45, 0x5f, 0x6f, 0x8e, 0x65, 0x5,
|
||||
0x90, 0x40, 0x0, 0x48, 0x50, 0x81, 0x2, 0x90, 0x10, 0x80, 0x0, 0x0, 0x58, 0x59, 0x12, 0xe1,
|
||||
0x80, 0x8a, 0x88, 0x37, 0x26, 0x78, 0x1c, 0xf3, 0x20, 0x48, 0x2c, 0xc8, 0x40, 0x0, 0x0, 0x1b,
|
||||
0x57, 0x80, 0x1e, 0x86, 0x10, 0x0, 0x46, 0x70, 0x58, 0x42, 0x88, 0xa4, 0x23, 0xcc, 0x44, 0x4f,
|
||||
0x38, 0x44, 0x0, 0xae, 0xe2, 0x21, 0x58, 0xa4, 0x0, 0x4, 0x85, 0xac, 0x90, 0x80, 0x4, 0x83,
|
||||
0x84, 0x5, 0x14, 0x88, 0x0, 0x0, 0x1, 0x21, 0x96, 0x6b, 0xd8, 0x3, 0x6f, 0x67, 0xc1, 0xaa,
|
||||
0x35, 0x24, 0x82, 0xec, 0x84, 0x0, 0x0, 0x1, 0xb5, 0x77, 0xb1, 0xed, 0xd9, 0x0, 0x4, 0x67,
|
||||
0x6c, 0x83, 0xd0, 0x8b, 0x2a, 0x32, 0x44, 0x1c, 0x54, 0x6a, 0x40, 0x5c, 0xad, 0xb3, 0x15, 0x8a,
|
||||
0xc0, 0x0, 0x48, 0x62, 0xcb, 0xd0, 0x0, 0x44, 0x34, 0x40, 0x55, 0x48, 0x80, 0x0, 0x0, 0x12,
|
||||
0x1b, 0xf0, 0xc6, 0x68, 0x2d, 0xd7, 0x8, 0x19, 0xe3, 0x4c, 0x20, 0x5f, 0x44, 0x44, 0x0, 0x0,
|
||||
0x9, 0xf, 0x23, 0x6c, 0x98, 0x15, 0xac, 0xb6, 0xa, 0x62, 0x52, 0x10, 0x24, 0x92, 0x20, 0x0,
|
||||
0x0, 0x4, 0x84, 0x6b, 0x2a, 0xd6, 0x1c, 0x8a, 0xcf, 0x3, 0x90, 0xdc, 0xc9, 0x21, 0x1c, 0xa0,
|
||||
0x10, 0x80, 0x0, 0x0, 0x58, 0x4a, 0x82, 0xe0, 0xc8, 0x80, 0x2b, 0x50, 0x19, 0x7, 0x6c, 0x8,
|
||||
0x4, 0x82, 0xb, 0x2f, 0x20, 0x1, 0x60, 0xf2, 0x41, 0x25, 0x42, 0x0, 0x0, 0x1, 0x98, 0x22, 0xb3,
|
||||
0x96, 0xe8, 0x0, 0x20, 0x80, 0xac, 0x80, 0x73, 0x37, 0x90, 0x75, 0x54, 0xbe, 0xf, 0xdd, 0x81,
|
||||
0x60, 0xb2, 0xe0, 0x20, 0x6c, 0x59, 0x42, 0x2c, 0xb7, 0xd5, 0x0, 0x10, 0x96, 0x28, 0x20, 0x1,
|
||||
0x42, 0xb, 0x2f, 0x20, 0x0, 0xb1, 0x15, 0x8a, 0x88, 0x0, 0x50, 0xa9, 0x20, 0x9a, 0xa0, 0x10,
|
||||
0x80, 0x0, 0x0, 0xd8, 0x5, 0xcc, 0x2e, 0xe8, 0x80, 0x2a, 0x88, 0x75, 0xc4, 0x84, 0xf, 0x81,
|
||||
0x92, 0xc1, 0xf5, 0x11, 0x1c, 0x59, 0x84, 0xf8, 0x40, 0xb1, 0xa8, 0x0, 0xc, 0x12, 0x10, 0x1a,
|
||||
0x62, 0x2, 0x10, 0x0, 0x0, 0xb, 0x57, 0x61, 0xa0, 0x42, 0x1, 0x15, 0x11, 0x1e, 0x53, 0xdb,
|
||||
0x88, 0xc7, 0x21, 0xa0, 0x90, 0x41, 0x50, 0x80, 0x0, 0x0, 0x36, 0x1, 0xc7, 0x3, 0xea, 0x0, 0x8,
|
||||
0x75, 0x7c, 0x79, 0xf, 0xfb, 0x95, 0xe1, 0xef, 0x70, 0xd0, 0x59, 0xd6, 0xf5, 0xe0, 0xb1, 0x98,
|
||||
0x0, 0xc, 0x13, 0x10, 0x19, 0x62, 0x20, 0x0, 0x0, 0x4, 0xab, 0xb4, 0x4f, 0xff, 0x82, 0x3b,
|
||||
0x97, 0xb6, 0x11, 0x77, 0xc1, 0x99, 0x20, 0x8a, 0xa1, 0x0, 0x0, 0x0, 0x8c, 0x4, 0x11, 0xf4,
|
||||
0xc4, 0x0, 0x10, 0xec, 0x88, 0xfe, 0x2c, 0x2c, 0x89, 0xe0, 0xf0, 0x38, 0x5e, 0xac, 0x74, 0x7c,
|
||||
0x24, 0x0, 0x40, 0x4f, 0x6f, 0xc8, 0x2c, 0x62, 0x0, 0x3, 0x5, 0x4, 0x6, 0x18, 0x88, 0x0, 0x0,
|
||||
0x1, 0x2a, 0xed, 0xc3, 0xf5, 0xc0, 0x8e, 0xa1, 0xed, 0x84, 0x5b, 0xd0, 0x66, 0x8, 0x10, 0xc0,
|
||||
0x81, 0x0, 0x0, 0x0, 0x53, 0x6a, 0x8a, 0x3a, 0x0, 0x8, 0x1d, 0x86, 0x94, 0x14, 0x12, 0xe, 0x2c,
|
||||
0x8, 0x12, 0xc0, 0x81, 0x0, 0x0, 0x0, 0x53, 0xeb, 0x9c, 0x8a, 0x0, 0x8, 0x22, 0x89, 0x6c, 0x14,
|
||||
0x10, 0xe, 0x2e, 0x8, 0x14, 0x80, 0x42, 0x0, 0x0, 0x0, 0x52, 0xdd, 0x8e, 0xf2, 0x0, 0x8, 0x20,
|
||||
0xd6, 0x74, 0x14, 0x16, 0x10, 0x30, 0x0, 0x16, 0x40, 0x42, 0x0, 0x0, 0x0, 0x49, 0x4e, 0x21,
|
||||
0x48, 0x0, 0x20, 0x7c, 0xe6, 0x30, 0x40, 0xc6, 0x4, 0x8, 0x0, 0x0, 0x2, 0x82, 0xce, 0x9a, 0x10,
|
||||
0x0, 0x40, 0xd4, 0x45, 0xe0, 0xa0, 0xd0, 0x41, 0x90, 0x40, 0xd6, 0x4, 0x8, 0x0, 0x0, 0x2, 0x46,
|
||||
0x74, 0x3c, 0x40, 0x1, 0x4, 0xb0, 0xe8, 0x82, 0x83, 0x1, 0x6, 0x81, 0x3, 0x90, 0x8, 0x40, 0x0,
|
||||
0x0, 0x9, 0xc, 0xcb, 0xe9, 0x0, 0x4, 0x12, 0xd3, 0xaa, 0xa, 0xf, 0x5, 0x1b, 0x0, 0xf, 0x20,
|
||||
0x21, 0x0, 0x0, 0x0, 0x2c, 0x7b, 0xf1, 0xcf, 0x40, 0x1, 0x3, 0x3b, 0x7, 0x2, 0xd, 0xa0, 0x20,
|
||||
0x40, 0x0, 0x0, 0x1a, 0xbd, 0xaf, 0x2, 0xfa, 0x80, 0x2, 0x7, 0xca, 0x63, 0x5, 0xe, 0x6, 0x8e,
|
||||
0x0, 0xe, 0x10, 0x20, 0x40, 0x0, 0x0, 0x1a, 0xbe, 0x96, 0xfc, 0xb4, 0x80, 0x2, 0x8, 0x39, 0x9d,
|
||||
0x4, 0x1d, 0x40, 0x40, 0x80, 0x0, 0x0, 0x35, 0x7e, 0x35, 0xf0, 0x9d, 0x0, 0x4, 0xf, 0x8c, 0xc6,
|
||||
0xa, 0x1e, 0xe, 0x1d, 0x0, 0x1e, 0x20, 0x40, 0x80, 0x0, 0x0, 0x30, 0x1, 0x86, 0x20, 0x50, 0x0,
|
||||
0x41, 0x7, 0xb3, 0xa0, 0x84, 0x2c, 0x8, 0x10, 0x0, 0x0, 0x6, 0x9, 0xca, 0xb9, 0xea, 0x0, 0x8,
|
||||
0x19, 0xd8, 0x30, 0x14, 0x44, 0x2e, 0x3c, 0x8, 0x44, 0xc0, 0x81, 0x0, 0x0, 0x0, 0x60, 0xfe,
|
||||
0xcb, 0x6e, 0xa0, 0x0, 0x82, 0x63, 0x7d, 0x1, 0x44, 0x22, 0xe3, 0xe0, 0x85, 0x6c, 0x8, 0x10,
|
||||
0x0, 0x0, 0x5, 0xaf, 0x58, 0x78, 0xc8, 0x0, 0x20, 0x8b, 0x65, 0x90, 0x51, 0x60, 0xa1, 0x0,
|
||||
0x21, 0x63, 0x2, 0x4, 0x0, 0x0, 0x1, 0x6a, 0x5a, 0x6, 0xe2, 0x0, 0x8, 0x1d, 0x36, 0x9c, 0x14,
|
||||
0x56, 0x28, 0x42, 0x8, 0x5a, 0xc0, 0x42, 0x0, 0x0, 0x0, 0x5a, 0x4c, 0xc2, 0xc6, 0x80, 0x2, 0x7,
|
||||
0x45, 0xa8, 0x5, 0x17, 0xa, 0x91, 0x2, 0x17, 0x30, 0x10, 0x80, 0x0, 0x0, 0x16, 0xac, 0x12,
|
||||
0x2b, 0x20, 0x0, 0x82, 0x2f, 0x96, 0x1, 0x45, 0xa2, 0xa4, 0x60, 0x86, 0x8, 0x8, 0x10, 0x0, 0x0,
|
||||
0x6, 0x0, 0x3c, 0xc4, 0x5a, 0x0, 0x8, 0x20, 0xe6, 0x74, 0x14, 0x62, 0x1e, 0x48, 0x0, 0x62,
|
||||
0x40, 0x81, 0x0, 0x0, 0x0, 0x6a, 0xfc, 0xd3, 0xe1, 0x5a, 0x0, 0x8, 0x1f, 0x29, 0x8c, 0x10,
|
||||
0xc9, 0x1, 0x2, 0x0, 0x0, 0x0, 0xd5, 0xf4, 0xa7, 0xe6, 0xa4, 0x0, 0x10, 0x41, 0xac, 0xe8, 0x28,
|
||||
0xcc, 0x40, 0x94, 0x0, 0xcc, 0x81, 0x2, 0x0, 0x0, 0x0, 0xd5, 0xed, 0xe8, 0x18, 0xd4, 0x0, 0x10,
|
||||
0x3e, 0x73, 0x18,
|
||||
];
|
||||
|
||||
/// Intersection "Feldstraße/Glacischaussee"
|
||||
///
|
||||
/// Has a very complex geometry with multiple stop lines after each other.
|
||||
/// Main traffic direction is east-west, has separate bike lanes.
|
||||
///
|
||||
/// Possible start point (heading 90°): TODO
|
||||
#[cfg(feature = "spat")]
|
||||
pub const MAPEM_HH_560: &[u8] = &[
|
||||
0x2, 0x5, 0x0, 0x1, 0x61, 0x2b, 0x8, 0x0, 0x3, 0x9, 0x4d, 0x83, 0x42, 0xfc, 0x9a, 0x94, 0xef,
|
||||
0xb0, 0x6b, 0x7d, 0xab, 0x66, 0x17, 0xd8, 0xb3, 0x5c, 0xda, 0x0, 0x6, 0x4, 0x60, 0x18, 0x55,
|
||||
0x91, 0x1, 0x73, 0x71, 0x3b, 0xe2, 0xcb, 0x2, 0x58, 0x56, 0x48, 0x1c, 0x28, 0x44, 0x0, 0x0,
|
||||
0x32, 0xbb, 0xbd, 0x9a, 0x91, 0x0, 0x61, 0x90, 0x6e, 0x15, 0x40, 0x3c, 0x5c, 0xac, 0x39, 0x63,
|
||||
0x28, 0x8e, 0xcc, 0x98, 0x51, 0xe, 0x55, 0xe1, 0x2, 0xd0, 0x3d, 0xd2, 0xe, 0x4, 0xe8, 0x22,
|
||||
0xb0, 0xa2, 0x0, 0x1, 0x7, 0x58, 0x49, 0x0, 0x0, 0x84, 0x8, 0x5, 0x21, 0x11, 0x0, 0x0, 0x2,
|
||||
0x21, 0x1f, 0x51, 0xf0, 0x9e, 0xd9, 0x70, 0x12, 0xab, 0x42, 0xc4, 0x2, 0x50, 0x8c, 0x80, 0x0,
|
||||
0x1, 0x10, 0x5e, 0xa0, 0x20, 0x4f, 0x4c, 0xba, 0x9, 0x52, 0xa2, 0xe4, 0x81, 0x8, 0x84, 0x40,
|
||||
0x0, 0x2, 0xad, 0x36, 0xa3, 0x6c, 0x40, 0x1, 0x2, 0x93, 0x4b, 0x4, 0x38, 0xd7, 0x80, 0x2, 0x16,
|
||||
0x50, 0x1, 0xff, 0x2c, 0x0, 0x2f, 0xd6, 0x13, 0xb, 0x6f, 0x1, 0x60, 0x84, 0x0, 0x82, 0x2, 0x20,
|
||||
0x8, 0x64, 0x44, 0x0, 0x0, 0x9, 0x1b, 0x9, 0x2c, 0xd0, 0x4, 0xf0, 0x82, 0x25, 0x1c, 0x3f, 0xd2,
|
||||
0x40, 0xd1, 0x42, 0x20, 0x0, 0x1, 0xd7, 0xa0, 0xe6, 0xac, 0xa2, 0x0, 0xc3, 0x21, 0xc1, 0xfa,
|
||||
0xd8, 0x58, 0xf5, 0xb9, 0x2, 0xc1, 0x7d, 0x4e, 0xe, 0x68, 0xf1, 0x3, 0xa9, 0x26, 0x13, 0x81,
|
||||
0x8c, 0xef, 0x60, 0xdb, 0x75, 0xa0, 0xea, 0xdd, 0xa4, 0xb, 0x4, 0x80, 0x0, 0x20, 0x22, 0x40,
|
||||
0xb1, 0x42, 0x20, 0x0, 0x1, 0x97, 0xce, 0x86, 0x7c, 0x22, 0x0, 0xc3, 0x20, 0xcd, 0x78, 0x81,
|
||||
0x63, 0xde, 0xe5, 0xb, 0x8, 0xf5, 0x74, 0x39, 0xef, 0xd1, 0x8b, 0x2f, 0x7e, 0x90, 0x55, 0x57,
|
||||
0x82, 0x41, 0xca, 0x7d, 0xdc, 0x16, 0x22, 0x80, 0x0, 0x61, 0xc2, 0x2, 0x24, 0x44, 0x40, 0x0,
|
||||
0x0, 0xb3, 0x4b, 0x65, 0x8e, 0x81, 0x21, 0x90, 0xe0, 0x57, 0xc6, 0xb, 0xe9, 0xef, 0x24, 0xc,
|
||||
0x14, 0x22, 0x0, 0x0, 0x19, 0x7b, 0x78, 0x69, 0x4a, 0x20, 0xc, 0x32, 0x1b, 0x4b, 0x87, 0x85,
|
||||
0x8f, 0x7b, 0x92, 0x2c, 0x1d, 0xd5, 0x50, 0xe7, 0x2f, 0x2a, 0x2c, 0xbb, 0xfa, 0x11, 0x64, 0x6f,
|
||||
0x83, 0x2, 0x81, 0xb1, 0xc1, 0x62, 0x48, 0x0, 0x6, 0x1e, 0x20, 0x24, 0x44, 0x44, 0x0, 0x0, 0xb,
|
||||
0x34, 0xa5, 0x54, 0x3c, 0x12, 0x19, 0x7, 0xee, 0xf2, 0x2, 0x99, 0x7b, 0xc9, 0x1, 0x51, 0x8,
|
||||
0x80, 0x0, 0x7, 0x51, 0x5d, 0x86, 0x5a, 0x20, 0xc, 0x32, 0x0, 0x62, 0x8c, 0x11, 0xe2, 0x4c,
|
||||
0xd0, 0x42, 0x65, 0xb3, 0x9, 0x3a, 0xaa, 0xc1, 0xa, 0xb8, 0x18, 0x26, 0x2, 0xb0, 0x88, 0xc6,
|
||||
0x67, 0xb8, 0x23, 0x96, 0x95, 0x82, 0xc3, 0xd0, 0x0, 0x10, 0x24, 0x40, 0x3c, 0x48, 0x88, 0x0,
|
||||
0x0, 0x11, 0xfc, 0x8d, 0xab, 0xa0, 0xcd, 0x78, 0x81, 0x5f, 0x56, 0x9b, 0x12, 0x3, 0x22, 0x11,
|
||||
0x0, 0x0, 0x14, 0xb7, 0x45, 0x19, 0x21, 0x0, 0x4, 0x44, 0x19, 0x3b, 0xa2, 0x3b, 0x49, 0xa0,
|
||||
0x8, 0x40, 0xb7, 0x1, 0x26, 0x15, 0x6c, 0x47, 0x6f, 0x49, 0x42, 0x3d, 0xba, 0x89, 0x11, 0x52,
|
||||
0x56, 0x78, 0xce, 0xf9, 0x3c, 0xc1, 0xb, 0x1d, 0x3a, 0x88, 0x77, 0xa8, 0x3c, 0x44, 0xc5, 0x39,
|
||||
0x80, 0xb1, 0x18, 0x0, 0x5, 0xa, 0x24, 0x7, 0x44, 0x22, 0x0, 0x0, 0x29, 0x6d, 0xa7, 0x28, 0xda,
|
||||
0x0, 0x8, 0x89, 0xf6, 0x6a, 0x44, 0x71, 0x53, 0x50, 0x10, 0xc3, 0x6d, 0x82, 0x46, 0x2c, 0xa8,
|
||||
0x8e, 0xa2, 0x94, 0x84, 0x7a, 0x94, 0xca, 0x22, 0xbf, 0xae, 0x81, 0x9e, 0x3e, 0x78, 0x82, 0x17,
|
||||
0x2a, 0x6f, 0x10, 0xf2, 0xd0, 0x60, 0x89, 0x96, 0x72, 0xc1, 0x62, 0x50, 0x0, 0xa, 0x16, 0x48,
|
||||
0x6, 0x68, 0x44, 0x0, 0x0, 0x33, 0x24, 0x13, 0x3d, 0xf1, 0x10, 0x6, 0x19, 0x1, 0x56, 0x4a, 0x4,
|
||||
0x84, 0xc, 0x80, 0x99, 0x12, 0x30, 0x5, 0x81, 0x2c, 0x9, 0x6, 0x1, 0xc2, 0x15, 0xd0, 0xa5, 0x9,
|
||||
0x18, 0x0, 0x22, 0xb1, 0x22, 0x0, 0x6, 0x3, 0x58, 0x89, 0x0, 0x3, 0x2, 0x12, 0x0, 0x9a, 0x11,
|
||||
0x0, 0x0, 0xe, 0xc8, 0x3b, 0x8f, 0xac, 0x44, 0x1, 0x86, 0x42, 0x88, 0xf, 0xb0, 0x51, 0xa1,
|
||||
0xff, 0x9, 0x20, 0xc, 0x41, 0x26, 0x60, 0xd8, 0x28, 0x4c, 0x97, 0x5, 0x39, 0x1b, 0x91, 0x2d,
|
||||
0xc1, 0x75, 0x5, 0x30, 0x9c, 0x50, 0x58, 0x4a, 0x0, 0x3, 0x82, 0x92, 0x1, 0x1a, 0x11, 0x0, 0x0,
|
||||
0xc, 0xc8, 0x9b, 0x4f, 0x8f, 0x44, 0x1, 0x86, 0x42, 0x5f, 0x8a, 0x48, 0x3, 0xb0, 0x1c, 0x24,
|
||||
0xa8, 0x37, 0x88, 0xd9, 0xc5, 0xe4, 0x69, 0x7b, 0x38, 0x49, 0xf, 0x9b, 0x5, 0xdc, 0xac, 0xcd,
|
||||
0x2a, 0x16, 0x20, 0xb0, 0xf8, 0x0, 0x8, 0x6, 0x24, 0x10, 0x24, 0x12, 0x0, 0x0, 0x15, 0x9a,
|
||||
0x65, 0xaf, 0x56, 0x88, 0x3, 0xc, 0x83, 0xf2, 0x7b, 0x5, 0xb9, 0xe, 0xc6, 0x2e, 0x1, 0x6b,
|
||||
0xe1, 0x6c, 0xf3, 0x28, 0xb, 0x63, 0x56, 0xec, 0x5c, 0x8a, 0x98, 0x20, 0xb0, 0x98, 0x0, 0x9,
|
||||
0xc, 0x24, 0x2d, 0x44, 0x2, 0x10, 0x0, 0x0, 0x3a, 0xd9, 0xca, 0x42, 0xc4, 0x40, 0x16, 0x20,
|
||||
0x0, 0x34, 0xec, 0x3, 0xe, 0x65, 0x1, 0x15, 0x3e, 0x84, 0x3a, 0xd6, 0xe1, 0x3, 0x15, 0xb9,
|
||||
0x8c, 0xa6, 0x16, 0x3d, 0x0, 0xab, 0x7d, 0x80, 0x1, 0xc3, 0x81, 0x65, 0xd0, 0x0, 0xa, 0x1a,
|
||||
0x20, 0x5c, 0x44, 0x4, 0x20, 0x0, 0x0, 0x11, 0xe4, 0x58, 0x4a, 0x20, 0xfc, 0x9e, 0xc0, 0x66,
|
||||
0xaf, 0xa0, 0x20, 0x9b, 0x2, 0x4, 0x0, 0x0, 0x1, 0x7e, 0x98, 0x22, 0x8a, 0x0, 0x8, 0x1d, 0x6,
|
||||
0xa0, 0x4, 0x28, 0x2, 0x8, 0x28, 0xc0, 0x81, 0x0, 0x0, 0x0, 0x66, 0x20, 0x8c, 0x7b, 0x20, 0x0,
|
||||
0x82, 0x26, 0x97, 0x40, 0x42, 0x60, 0x40, 0x82, 0xac, 0x8, 0x10, 0x0, 0x0, 0x5, 0xb9, 0x28,
|
||||
0x6f, 0xe8, 0x0, 0x20, 0x71, 0x1b, 0x0, 0x10, 0xb0, 0x18, 0x20, 0xb3, 0x2, 0x4, 0x0, 0x0, 0x1,
|
||||
0x56, 0xc2, 0x4b, 0x68, 0x0, 0x20, 0x8f, 0x25, 0x0, 0x10, 0xa8, 0x20, 0x20, 0xbb, 0x2, 0x4,
|
||||
0x0, 0x0, 0x1, 0x7e, 0x63, 0x57, 0xea, 0x0, 0x8, 0x26, 0x37, 0xcc, 0x4, 0x30, 0xa, 0x8, 0x30,
|
||||
0xc0, 0x81, 0x0, 0x0, 0x0, 0x5e, 0x5, 0x57, 0xac, 0x80, 0x2, 0x6, 0x76, 0xd, 0x1, 0xb, 0x83,
|
||||
0x2, 0xc, 0xb0, 0x20, 0x40, 0x0, 0x0, 0x13, 0xeb, 0xc2, 0x9a, 0x0, 0x8, 0x22, 0x56, 0x8c, 0x4,
|
||||
0x34, 0xe, 0x8, 0x34, 0xc0, 0x81, 0x0, 0x0, 0x0, 0x4e, 0xf3, 0x45, 0x28, 0x0, 0x20, 0x76, 0xe5,
|
||||
0xd0, 0x10, 0xc8, 0x40, 0x20, 0xdb, 0x2, 0x4, 0x0, 0x0, 0x1, 0x6f, 0xd8, 0xd, 0x32, 0x0, 0x8,
|
||||
0x19, 0xd8, 0x34, 0x4, 0x38, 0x12, 0x8, 0x38, 0xc0, 0x81, 0x0, 0x0, 0x0, 0x5d, 0x90, 0xc1,
|
||||
0x90, 0x80, 0x2, 0x9, 0x8d, 0xf3, 0x1, 0xd, 0x85, 0x2, 0xf, 0xa0, 0x10, 0x80, 0x0, 0x0, 0x15,
|
||||
0xa2, 0x24, 0x2e, 0x80, 0x2, 0x8, 0xee, 0x50, 0x1, 0x10, 0x5, 0x80, 0x10, 0x10, 0x10, 0x80,
|
||||
0x0, 0x0, 0x17, 0x0, 0x81, 0x9d, 0xa0, 0x0, 0x81, 0xc8, 0x6b, 0x41, 0x8, 0x50, 0x8, 0x40, 0x0,
|
||||
0x0, 0xb, 0x8a, 0xd8, 0xd8, 0x50, 0x0, 0x40, 0xcf, 0x42, 0x0, 0x22, 0x20, 0xc0, 0x2, 0x22, 0x2,
|
||||
0x10, 0x0, 0x0, 0x2, 0xef, 0x9c, 0x24, 0xe4, 0x0, 0x10, 0x4c, 0x4f, 0x80, 0x21, 0x1a, 0x1, 0x8,
|
||||
0x0, 0x0, 0x1, 0x5a, 0xd2, 0xde, 0x48, 0x0, 0x20, 0x80, 0x26, 0x40, 0x11, 0x20, 0x68, 0x1,
|
||||
0x21, 0x1, 0x8, 0x0, 0x0, 0x1, 0x5a, 0xce, 0x9a, 0x68, 0x0, 0x20, 0x80, 0x19, 0xc0, 0x42, 0x54,
|
||||
0x2, 0x10, 0x0, 0x0, 0x2, 0xa4, 0x6c, 0xcc, 0x10, 0x0, 0x41, 0x2a, 0x39, 0x40, 0x22, 0x60,
|
||||
0xe0, 0x2, 0x62, 0x2, 0x10, 0x0, 0x0, 0x2, 0x47, 0x69, 0x47, 0x40, 0x1, 0x3, 0x49, 0x13, 0x2,
|
||||
0x13, 0xa0, 0x10, 0x80, 0x0, 0x0, 0x17, 0xe0, 0x4, 0x82, 0x20, 0x0, 0x82, 0x63, 0x7d, 0x0,
|
||||
0x45, 0x1, 0xe0, 0x5, 0x4, 0x4, 0x20, 0x0, 0x0, 0x5, 0x6c, 0xd8, 0xff, 0xa0, 0x0, 0x81, 0x9d,
|
||||
0x83, 0x1, 0xa, 0x58, 0x10, 0x20, 0x0, 0x0, 0xb, 0x60, 0x30, 0x70, 0x10, 0x0, 0x41, 0x31, 0x42,
|
||||
0x0, 0x22, 0xa1, 0x0, 0x42, 0xa6, 0x4, 0x8, 0x0, 0x0, 0x2, 0xd3, 0xa2, 0x16, 0x24, 0x0, 0x10,
|
||||
0x33, 0xcf, 0x80, 0x8, 0xa4, 0x44, 0x10, 0xad, 0x0, 0x84, 0x0, 0x0, 0x0, 0xbd, 0xe7, 0x8f,
|
||||
0xdd, 0x0, 0x4, 0xe, 0x8b, 0x50, 0x2, 0x2c, 0x12, 0x0, 0x2c, 0x20, 0x21, 0x0, 0x0, 0x0, 0x2f,
|
||||
0xe0, 0x89, 0xbb, 0x40, 0x1, 0x4, 0x71, 0x29, 0x80,
|
||||
];
|
||||
|
||||
/// Intersection "TODO"
|
||||
///
|
||||
/// 3 layers
|
||||
#[cfg(feature = "spat")]
|
||||
pub const MAPEM_HH_2349_L31: &[u8] = &[
|
||||
0x2, 0x5, 0x0, 0x12, 0x25, 0xf4, 0x18, 0x0, 0x3e, 0x6, 0xd, 0x91, 0x21, 0x93, 0x3b, 0x8c, 0x99,
|
||||
0xb4, 0x73, 0x71, 0x8b, 0x6, 0xe, 0x60, 0x0, 0x61, 0x25, 0xa1, 0x5, 0x58, 0xef, 0x69, 0x97,
|
||||
0x13, 0xed, 0x54, 0x20, 0x28, 0xa3, 0x68, 0x4, 0x49, 0xc6, 0xeb, 0xcb, 0xa2, 0x4, 0xf1, 0xba,
|
||||
0xf2, 0xe8, 0x9d, 0x6, 0x9d, 0xa8, 0x20, 0xf2, 0xda, 0x81, 0x99, 0x2, 0x4, 0x8, 0x0, 0x0, 0xab,
|
||||
0x72, 0xf, 0xe5, 0xc0, 0x7d, 0xa6, 0x34, 0x41, 0xf4, 0xe3, 0x75, 0xe5, 0xd1, 0x2, 0x78, 0xdd,
|
||||
0x79, 0x74, 0x4e, 0x83, 0x4e, 0xd4, 0x10, 0x79, 0x6d, 0x40, 0xcd, 0x1, 0x2, 0x4, 0x0, 0x0,
|
||||
0x55, 0xb8, 0x57, 0xf1, 0x50, 0x3e, 0x73, 0x18, 0x28, 0x80, 0x3c, 0x51, 0x10, 0x6d, 0x38, 0xdd,
|
||||
0x79, 0x74, 0x40, 0x9e, 0x37, 0x5e, 0x5d, 0x13, 0xa0, 0xd3, 0xb5, 0x4, 0x1e, 0x5b, 0x50, 0x32,
|
||||
0x40, 0x40, 0x81, 0x0, 0x0, 0x10, 0x8c, 0x36, 0xc6, 0x1, 0x31, 0xc1, 0xc0, 0xa1, 0xc0, 0x91,
|
||||
0x24, 0x1, 0xe4, 0xe3, 0x75, 0xe5, 0xd1, 0x2, 0x78, 0xdd, 0x79, 0x74, 0x4e, 0x83, 0x4e, 0xd4,
|
||||
0x10, 0x79, 0x6d, 0x40, 0xcc, 0x81, 0x2, 0x4, 0x0, 0x0, 0x55, 0xaf, 0x28, 0x32, 0xd0, 0x3e,
|
||||
0x73, 0x1a, 0x0, 0xd2, 0x71, 0xba, 0xf2, 0xe8, 0x81, 0x3c, 0x6e, 0xbc, 0xba, 0x27, 0x41, 0xa7,
|
||||
0x6a, 0x8, 0x3c, 0xb6, 0xa0, 0x64, 0x40, 0x81, 0x2, 0x0, 0x0, 0x20, 0x21, 0xcd, 0x23, 0x81,
|
||||
0x9c, 0x7e, 0x51, 0x7, 0x53, 0x8d, 0xd7, 0x97, 0x44, 0x9, 0xe3, 0x75, 0xe5, 0xd1, 0x3a, 0xd,
|
||||
0x3b, 0x50, 0x41, 0xe5, 0xb5, 0x3, 0x34, 0x4, 0x8, 0x10, 0x0, 0x1, 0x56, 0xd5, 0x20, 0xe, 0xc1,
|
||||
0x6, 0xb3, 0xa0, 0xa1, 0xe0, 0xe1, 0x34, 0x41, 0x94, 0xe3, 0x75, 0xe5, 0xd1, 0x2, 0x78, 0xdd,
|
||||
0x79, 0x74, 0x4e, 0x83, 0x4e, 0xd4, 0x10, 0x79, 0x6d, 0x40, 0xc9, 0x1, 0x2, 0x4, 0x0, 0x0,
|
||||
0x40, 0xe9, 0x9a, 0x78, 0x4, 0xc9, 0x3, 0x82, 0x86, 0x83, 0x4, 0x50, 0x6, 0x13, 0x8d, 0xd7,
|
||||
0x97, 0x44, 0x9, 0xe3, 0x75, 0xe5, 0xd1, 0x3a, 0xd, 0x3b, 0x50, 0x41, 0xe5, 0xb5, 0x3, 0x22,
|
||||
0x2, 0x8, 0x10, 0x0, 0x1, 0x8, 0xf2, 0x70, 0xe4, 0x13, 0x1c, 0x22, 0x88, 0x2e, 0x9c, 0x6e,
|
||||
0xbc, 0xba, 0x20, 0x4f, 0x1b, 0xaf, 0x2e, 0x89, 0xd0, 0x69, 0xda, 0x82, 0xf, 0x2d, 0xa8, 0x19,
|
||||
0x20, 0x10, 0x40, 0x80, 0x0, 0x8, 0x2f, 0xb3, 0x77, 0x0, 0x67, 0x5e, 0xf0, 0x50, 0xc0, 0x58,
|
||||
0x82, 0x0, 0xb2, 0x71, 0xba, 0xf2, 0xe8, 0x81, 0x3c, 0x6e, 0xbc, 0xba, 0x27, 0x41, 0xa7, 0x6a,
|
||||
0x8, 0x3c, 0xb6, 0xa0, 0x64, 0x40, 0x41, 0x2, 0x0, 0x0, 0x20, 0x73, 0x2d, 0xbe, 0x82, 0x64,
|
||||
0x81, 0x51, 0x5, 0x53, 0x8d, 0xd7, 0x97, 0x44, 0x9, 0xe3, 0x75, 0xe5, 0xd1, 0x3a, 0xd, 0x3b,
|
||||
0x50, 0x41, 0xe5, 0xb5, 0x3, 0x24, 0x2, 0x8, 0x10, 0x0, 0x1, 0x0, 0x69, 0x6d, 0x60, 0xc, 0xe3,
|
||||
0xf6, 0xa, 0x16, 0xa, 0xf, 0x40, 0x14, 0x4e, 0x37, 0x5e, 0x5d, 0x10, 0x27, 0x8d, 0xd7, 0x97,
|
||||
0x44, 0xe8, 0x34, 0xed, 0x41, 0x7, 0x96, 0xd4, 0xc, 0x88, 0x10, 0x20, 0x40, 0x0, 0x4, 0xe,
|
||||
0xb5, 0xa7, 0x50, 0x4c, 0x90, 0x2b, 0x20, 0xa, 0xd1, 0xb0, 0xe8, 0xe5, 0xcf, 0xa7, 0x2c, 0xba,
|
||||
0x73, 0x65, 0xdc, 0x81, 0x8a, 0xd, 0x3b, 0x50, 0x41, 0xe5, 0xb5, 0x3, 0x11, 0x40, 0x20, 0x0,
|
||||
0x2, 0x88, 0x69, 0x7c, 0x9a, 0x43, 0xec, 0xa4, 0x90, 0x1, 0x0, 0xc0, 0x0, 0xd1, 0xe3, 0x90,
|
||||
0x68, 0xf2, 0x0, 0x34, 0x59, 0x18, 0x1a, 0x1c, 0x98, 0x8, 0x2, 0x0, 0x16, 0x86, 0xc9, 0xe0,
|
||||
0x31, 0x99, 0x20, 0x18, 0xbc, 0x9e, 0x9c, 0x5b, 0x29, 0x10, 0x1, 0x41, 0x61, 0x90, 0x0, 0x2,
|
||||
0x2, 0x80, 0x20, 0x9c, 0x6e, 0xbc, 0xba, 0x20, 0x4f, 0x1b, 0xaf, 0x2e, 0x89, 0xd0, 0x69, 0xda,
|
||||
0x82, 0xf, 0x2d, 0xa8, 0x19, 0x90, 0x20, 0x40, 0x80, 0x0, 0xa, 0xb6, 0xbd, 0x0, 0x6c, 0x8,
|
||||
0x35, 0x9d, 0x50, 0xc, 0x5a, 0x36, 0x1d, 0x1c, 0xb9, 0xf4, 0xe5, 0x97, 0x4e, 0x6c, 0xbb, 0x90,
|
||||
0x34, 0x41, 0xa7, 0x6a, 0x8, 0x3c, 0xb6, 0xa0, 0x66, 0x64, 0x4, 0x0, 0x0, 0x9, 0x55, 0x73,
|
||||
0x60, 0x6b, 0xc2, 0x13, 0x5c, 0x90, 0x86, 0xc9, 0xce, 0x59, 0x0, 0x96, 0x8d, 0x87, 0x47, 0x2e,
|
||||
0x7d, 0x39, 0x65, 0xd3, 0x9b, 0x2e, 0xe4, 0xc, 0x90, 0x69, 0xda, 0x82, 0xf, 0x2d, 0xa8, 0x18,
|
||||
0x8a, 0x10, 0x0, 0x0, 0xe, 0x43, 0x56, 0xe3, 0x87, 0x1f, 0xbb, 0xe5, 0x46, 0x1c, 0x3, 0x18,
|
||||
0x41, 0xff, 0x4d, 0xe0, 0x80, 0x20, 0x0, 0xff, 0xa9, 0xd8, 0xd, 0x6e, 0x53, 0x6, 0xb3, 0x2d,
|
||||
0xa7, 0x58, 0xcb, 0xa4, 0x0, 0x50, 0x58, 0x5c, 0x0, 0x0, 0x81, 0x20, 0x9, 0x27, 0x1b, 0xaf,
|
||||
0x2e, 0x88, 0x13, 0xc6, 0xeb, 0xcb, 0xa2, 0x74, 0x1a, 0x76, 0xa0, 0x83, 0xcb, 0x6a, 0x6, 0x44,
|
||||
0x8, 0x10, 0x20, 0x0, 0x2, 0x11, 0x7e, 0xd9, 0x90, 0x26, 0x38, 0x31, 0x0, 0x81, 0x38, 0xdd,
|
||||
0x79, 0x74, 0x40, 0x9e, 0x37, 0x5e, 0x5d, 0x13, 0xa0, 0xd3, 0xb5, 0x4, 0x1e, 0x5b, 0x50, 0x33,
|
||||
0x20, 0x40, 0x81, 0x0, 0x0, 0x15, 0x6f, 0x79, 0xf1, 0x88, 0x10, 0x6b, 0x3a, 0x80, 0x38, 0x9c,
|
||||
0x6e, 0xbc, 0xba, 0x20, 0x4f, 0x1b, 0xaf, 0x2e, 0x89, 0xd0, 0x69, 0xda, 0x82, 0xf, 0x2d, 0xa8,
|
||||
0x19, 0x10, 0x20, 0x40, 0x80, 0x0, 0x8, 0x31, 0x1b, 0x57, 0x40, 0x67, 0x5f, 0x25, 0x0, 0xb5,
|
||||
0xa3, 0x61, 0xd1, 0xcb, 0x9f, 0x4e, 0x59, 0x74, 0xe6, 0xcb, 0xb9, 0x3, 0x34, 0x1a, 0x76, 0xa0,
|
||||
0x83, 0xcb, 0x6a, 0x6, 0x66, 0x44, 0x0, 0x0, 0x0, 0x95, 0x57, 0xe2, 0x0, 0xfc, 0x46, 0x24,
|
||||
0xec, 0x62, 0x2e, 0xc7, 0x57, 0x44, 0x21, 0x4e, 0x37, 0x5e, 0x5d, 0x10, 0x27, 0x8d, 0xd7, 0x97,
|
||||
0x44, 0xe8, 0x34, 0xed, 0x41, 0x7, 0x96, 0xd4, 0xc, 0xd0, 0x10, 0x20, 0x40, 0x0, 0x5, 0x5b,
|
||||
0xd9, 0x7c, 0x72, 0x4, 0x14, 0xce, 0x82, 0x88, 0x83, 0x45, 0x54, 0x2, 0x16, 0x8d, 0x87, 0x47,
|
||||
0x2e, 0x7d, 0x39, 0x65, 0xd3, 0x9b, 0x2e, 0xe4, 0xc, 0xd0, 0x69, 0xda, 0x82, 0xf, 0x2d, 0xa8,
|
||||
0x19, 0x11, 0x10, 0x0, 0x0, 0x2, 0x40, 0xab, 0x99, 0x11, 0xc, 0x3d, 0x3b, 0x22, 0x87, 0x13,
|
||||
0xc5, 0x44, 0xd, 0x4e, 0x37, 0x5e, 0x5d, 0x10, 0x27, 0x8d, 0xd7, 0x97, 0x44, 0xe8, 0x34, 0xed,
|
||||
0x41, 0x7, 0x96, 0xd4, 0xc, 0x58, 0x10, 0x20, 0x40, 0x0, 0x4, 0x2e, 0x8a, 0x53, 0x20, 0x3e,
|
||||
0xb3, 0x18, 0x28, 0x38, 0x14, 0x29, 0x10, 0x39, 0x38, 0xdd, 0x79, 0x74, 0x40, 0x9e, 0x37, 0x5e,
|
||||
0x5d, 0x13, 0xa0, 0xd3, 0xb5, 0x4, 0x1e, 0x5b, 0x50, 0x31, 0x60, 0x40, 0x81, 0x0, 0x0, 0x10,
|
||||
0xc6, 0x17, 0xad, 0x41, 0x5, 0xb3, 0xa0, 0xa0, 0xd0, 0x50, 0xb4, 0x40, 0xf4, 0xe3, 0x75, 0xe5,
|
||||
0xd1, 0x2, 0x78, 0xdd, 0x79, 0x74, 0x4e, 0x83, 0x4e, 0xd4, 0x10, 0x79, 0x6d, 0x40, 0xcd, 0x1,
|
||||
0x2, 0x4, 0x0, 0x0, 0x55, 0xaf, 0xc8, 0x33, 0x20, 0x3e, 0x73, 0x18, 0x28, 0x40, 0x34, 0x31,
|
||||
0x90, 0xd, 0x68, 0xd8, 0x74, 0x72, 0xe7, 0xd3, 0x96, 0x5d, 0x39, 0xb2, 0xee, 0x40, 0xcd, 0x6,
|
||||
0x9d, 0xa8, 0x20, 0xf2, 0xda, 0x81, 0x88, 0xa1, 0x0, 0x0, 0x2, 0x44, 0x36, 0x2e, 0x23, 0x61,
|
||||
0x79, 0xf4, 0x9a, 0x83, 0xcf, 0x94, 0x1, 0xe7, 0x4a, 0xc0, 0xf3, 0x65, 0xc0, 0x79, 0x73, 0x10,
|
||||
0xf5, 0xf5, 0x26, 0x64, 0x46, 0x30, 0x58, 0x23, 0x6, 0x9, 0x32, 0x18, 0x4, 0x8b, 0x13, 0x82,
|
||||
0x3b, 0x8d, 0x1, 0x18, 0x48, 0x0, 0x96, 0x69, 0x30, 0x40, 0x10, 0x0, 0x23, 0x9, 0x0, 0x11,
|
||||
0xdc, 0x6a, 0x9, 0x12, 0x2a, 0x4, 0x97, 0xe, 0x2, 0x0, 0x80, 0x51, 0x18, 0x41, 0xa8, 0x0, 0xa0,
|
||||
0xb0, 0x84, 0x0, 0x2, 0x3, 0x44, 0x11, 0x4e, 0x37, 0x5e, 0x5d, 0x10, 0x27, 0x8d, 0xd7, 0x97,
|
||||
0x44, 0xe8, 0x34, 0xed, 0x41, 0x7, 0x96, 0xd4, 0xc, 0x90, 0x10, 0x20, 0x40, 0x0, 0x4, 0x18,
|
||||
0x69, 0xad, 0xe0, 0x33, 0xaf, 0xa0, 0x28, 0x48, 0x20, 0x35, 0x10, 0x4d, 0x38, 0xdd, 0x79, 0x74,
|
||||
0x40, 0x9e, 0x37, 0x5e, 0x5d, 0x13, 0xa0, 0xd3, 0xb5, 0x4, 0x1e, 0x5b, 0x50, 0x32, 0x40, 0x40,
|
||||
0x81, 0x0, 0x0, 0x10, 0x10, 0xa6, 0x95, 0x80, 0xce, 0x3f, 0x60, 0xa1, 0x40, 0x90, 0xe0,
|
||||
];
|
||||
#[cfg(feature = "spat")]
|
||||
pub const MAPEM_HH_2349_L32: &[u8] = &[
|
||||
0x2, 0x5, 0x0, 0x12, 0x25, 0xf4, 0x18, 0x0, 0x40, 0x6, 0xd, 0x91, 0x21, 0x93, 0x3b, 0x8c, 0x99,
|
||||
0xb4, 0x73, 0x71, 0x8b, 0x6, 0xe, 0x60, 0x0, 0x61, 0x25, 0xa1, 0x5, 0x58, 0xef, 0x69, 0x97,
|
||||
0x13, 0xed, 0x54, 0x20, 0x28, 0xa3, 0x48, 0x84, 0x29, 0xc6, 0xeb, 0xcb, 0xa2, 0x4, 0xf1, 0xba,
|
||||
0xf2, 0xe8, 0x9d, 0x6, 0x9d, 0xa8, 0x20, 0xf2, 0xda, 0x81, 0x9a, 0x2, 0x4, 0x8, 0x0, 0x0, 0xab,
|
||||
0x7b, 0x2f, 0x8e, 0x40, 0x82, 0x99, 0xd0, 0x51, 0x10, 0x68, 0xaa, 0x0, 0xf2, 0x71, 0xba, 0xf2,
|
||||
0xe8, 0x81, 0x3c, 0x6e, 0xbc, 0xba, 0x27, 0x41, 0xa7, 0x6a, 0x8, 0x3c, 0xb6, 0xa0, 0x66, 0x40,
|
||||
0x81, 0x2, 0x0, 0x0, 0x2a, 0xd7, 0x94, 0x19, 0x68, 0x1f, 0x39, 0x8d, 0x0, 0x71, 0x38, 0xdd,
|
||||
0x79, 0x74, 0x40, 0x9e, 0x37, 0x5e, 0x5d, 0x13, 0xa0, 0xd3, 0xb5, 0x4, 0x1e, 0x5b, 0x50, 0x32,
|
||||
0x20, 0x40, 0x81, 0x0, 0x0, 0x10, 0x62, 0x36, 0xae, 0x80, 0xce, 0xbe, 0x48, 0x3, 0x49, 0xc6,
|
||||
0xeb, 0xcb, 0xa2, 0x4, 0xf1, 0xba, 0xf2, 0xe8, 0x9d, 0x6, 0x9d, 0xa8, 0x20, 0xf2, 0xda, 0x81,
|
||||
0x91, 0x2, 0x4, 0x8, 0x0, 0x0, 0x80, 0x87, 0x34, 0x8e, 0x6, 0x71, 0xf9, 0x44, 0x19, 0x4e, 0x37,
|
||||
0x5e, 0x5d, 0x10, 0x27, 0x8d, 0xd7, 0x97, 0x44, 0xe8, 0x34, 0xed, 0x41, 0x7, 0x96, 0xd4, 0xc,
|
||||
0x90, 0x10, 0x20, 0x40, 0x0, 0x4, 0xe, 0x99, 0xa7, 0x80, 0x4c, 0x90, 0x38, 0x28, 0x68, 0x30,
|
||||
0x45, 0x0, 0x61, 0x38, 0xdd, 0x79, 0x74, 0x40, 0x9e, 0x37, 0x5e, 0x5d, 0x13, 0xa0, 0xd3, 0xb5,
|
||||
0x4, 0x1e, 0x5b, 0x50, 0x32, 0x20, 0x20, 0x81, 0x0, 0x0, 0x10, 0x8f, 0x27, 0xe, 0x41, 0x31,
|
||||
0xc2, 0x28, 0x82, 0xe9, 0xc6, 0xeb, 0xcb, 0xa2, 0x4, 0xf1, 0xba, 0xf2, 0xe8, 0x9d, 0x6, 0x9d,
|
||||
0xa8, 0x20, 0xf2, 0xda, 0x81, 0x92, 0x1, 0x4, 0x8, 0x0, 0x0, 0x82, 0xfb, 0x37, 0x70, 0x6, 0x75,
|
||||
0xef, 0x5, 0xc, 0x5, 0x88, 0x20, 0xb, 0x27, 0x1b, 0xaf, 0x2e, 0x88, 0x13, 0xc6, 0xeb, 0xcb,
|
||||
0xa2, 0x74, 0x1a, 0x76, 0xa0, 0x83, 0xcb, 0x6a, 0x6, 0x44, 0x4, 0x10, 0x20, 0x0, 0x2, 0x7,
|
||||
0x32, 0xdb, 0xe8, 0x26, 0x48, 0x15, 0x10, 0x55, 0x38, 0xdd, 0x79, 0x74, 0x40, 0x9e, 0x37, 0x5e,
|
||||
0x5d, 0x13, 0xa0, 0xd3, 0xb5, 0x4, 0x1e, 0x5b, 0x50, 0x32, 0x40, 0x20, 0x81, 0x0, 0x0, 0x10,
|
||||
0x6, 0x96, 0xd6, 0x0, 0xce, 0x3f, 0x60, 0xa1, 0x60, 0xa0, 0xf5, 0x0, 0xb5, 0xa3, 0x61, 0xd1,
|
||||
0xcb, 0x9f, 0x4e, 0x59, 0x74, 0xe6, 0xcb, 0xb9, 0x3, 0x34, 0x1a, 0x76, 0xa0, 0x83, 0xcb, 0x6a,
|
||||
0x6, 0x66, 0x44, 0x0, 0x0, 0x0, 0x95, 0x57, 0xe2, 0x0, 0xfc, 0x46, 0x24, 0xec, 0x62, 0x2e,
|
||||
0xc7, 0x57, 0x40, 0x12, 0x4e, 0x37, 0x5e, 0x5d, 0x10, 0x27, 0x8d, 0xd7, 0x97, 0x44, 0xe8, 0x34,
|
||||
0xed, 0x41, 0x7, 0x96, 0xd4, 0xc, 0x88, 0x10, 0x20, 0x40, 0x0, 0x4, 0x22, 0xfd, 0xb3, 0x20,
|
||||
0x4c, 0x70, 0x62, 0x20, 0xea, 0x71, 0xba, 0xf2, 0xe8, 0x81, 0x3c, 0x6e, 0xbc, 0xba, 0x27, 0x41,
|
||||
0xa7, 0x6a, 0x8, 0x3c, 0xb6, 0xa0, 0x66, 0x80, 0x81, 0x2, 0x0, 0x0, 0x2a, 0xda, 0xa4, 0x1,
|
||||
0xd8, 0x20, 0xd6, 0x74, 0x14, 0x3c, 0x1c, 0x26, 0xa0, 0xa, 0xb4, 0x6c, 0x3a, 0x39, 0x73, 0xe9,
|
||||
0xcb, 0x2e, 0x9c, 0xd9, 0x77, 0x20, 0x6a, 0x83, 0x4e, 0xd4, 0x10, 0x79, 0x6d, 0x40, 0xc4, 0x48,
|
||||
0x80, 0x0, 0x0, 0x11, 0x81, 0x6e, 0x2, 0x81, 0xcc, 0x71, 0x88, 0x5c, 0x25, 0x20, 0x4c, 0x80,
|
||||
0xeb, 0x46, 0xc3, 0xa3, 0x97, 0x3e, 0x9c, 0xb2, 0xe9, 0xcd, 0x97, 0x72, 0x6, 0x28, 0x34, 0xed,
|
||||
0x41, 0x7, 0x96, 0xd4, 0xc, 0x89, 0x8, 0x0, 0x0, 0xf, 0x21, 0x6, 0xc, 0xfb, 0xa, 0x3e, 0x44,
|
||||
0xf0, 0x10, 0xc, 0x0, 0x18, 0xb2, 0x5d, 0x83, 0x13, 0xcb, 0xa8, 0xa2, 0x58, 0xba, 0x11, 0x16,
|
||||
0x8e, 0xa0, 0x8c, 0x18, 0x20, 0x7, 0xfc, 0x10, 0x5a, 0x76, 0x7, 0xd1, 0x3b, 0xb, 0xa1, 0x1b,
|
||||
0x40, 0x80, 0x20, 0x0, 0x3f, 0xc, 0xe8, 0x1f, 0xe6, 0x78, 0x10, 0x23, 0x3a, 0x8, 0x25, 0x9e,
|
||||
0x44, 0x6a, 0x3b, 0x20, 0x2, 0x8a, 0xc1, 0x48, 0x0, 0x10, 0x1d, 0x60, 0x84, 0x0, 0x8, 0x10,
|
||||
0x80, 0x44, 0x9c, 0x6e, 0xbc, 0xba, 0x20, 0x4f, 0x1b, 0xaf, 0x2e, 0x89, 0xd0, 0x69, 0xda, 0x82,
|
||||
0xf, 0x2d, 0xa8, 0x19, 0x90, 0x20, 0x40, 0x80, 0x0, 0xa, 0xb7, 0x20, 0xfe, 0x5c, 0x7, 0xda,
|
||||
0x63, 0x44, 0x11, 0x4e, 0x37, 0x5e, 0x5d, 0x10, 0x27, 0x8d, 0xd7, 0x97, 0x44, 0xe8, 0x34, 0xed,
|
||||
0x41, 0x7, 0x96, 0xd4, 0xc, 0x90, 0x10, 0x20, 0x40, 0x0, 0x4, 0x18, 0x69, 0xad, 0xe0, 0x33,
|
||||
0xaf, 0xa0, 0x28, 0x48, 0x20, 0x35, 0x10, 0x6d, 0x38, 0xdd, 0x79, 0x74, 0x40, 0x9e, 0x37, 0x5e,
|
||||
0x5d, 0x13, 0xa0, 0xd3, 0xb5, 0x4, 0x1e, 0x5b, 0x50, 0x32, 0x40, 0x40, 0x81, 0x0, 0x0, 0x10,
|
||||
0x8c, 0x36, 0xc6, 0x1, 0x31, 0xc1, 0xc0, 0xa1, 0xc0, 0x91, 0x25, 0x0, 0x45, 0xa3, 0x61, 0xd1,
|
||||
0xcb, 0x9f, 0x4e, 0x59, 0x74, 0xe6, 0xcb, 0xb9, 0x3, 0x44, 0x1a, 0x76, 0xa0, 0x83, 0xcb, 0x6a,
|
||||
0x6, 0x22, 0x44, 0x0, 0x0, 0x0, 0x8c, 0x3, 0xf1, 0x64, 0xf, 0xed, 0x92, 0xe2, 0xc5, 0xf8, 0xba,
|
||||
0x64, 0x6, 0x5a, 0x36, 0x1d, 0x1c, 0xb9, 0xf4, 0xe5, 0x97, 0x4e, 0x6c, 0xbb, 0x90, 0x32, 0x41,
|
||||
0xa7, 0x6a, 0x8, 0x3c, 0xb6, 0xa0, 0x64, 0x48, 0x40, 0x0, 0x0, 0x29, 0x6, 0xee, 0x66, 0xf4,
|
||||
0x91, 0x7b, 0x93, 0xc8, 0x8, 0x6, 0x0, 0x4c, 0x7f, 0x2, 0x68, 0x0, 0xa1, 0x8e, 0x0, 0x44, 0x51,
|
||||
0xa4, 0x3, 0xe5, 0x88, 0x34, 0x38, 0x3, 0x5, 0x85, 0xa0, 0x0, 0x38, 0x4a, 0x20, 0x6a, 0x71,
|
||||
0xba, 0xf2, 0xe8, 0x81, 0x3c, 0x6e, 0xbc, 0xba, 0x27, 0x41, 0xa7, 0x6a, 0x8, 0x3c, 0xb6, 0xa0,
|
||||
0x62, 0xc0, 0x81, 0x2, 0x0, 0x0, 0x21, 0x74, 0x52, 0x99, 0x1, 0xf5, 0x98, 0xc1, 0x41, 0xc0,
|
||||
0xa1, 0x48, 0x2, 0x89, 0xc6, 0xeb, 0xcb, 0xa2, 0x4, 0xf1, 0xba, 0xf2, 0xe8, 0x9d, 0x6, 0x9d,
|
||||
0xa8, 0x20, 0xf2, 0xda, 0x81, 0x91, 0x2, 0x4, 0x8, 0x0, 0x0, 0x81, 0xd6, 0xb4, 0xea, 0x9, 0x92,
|
||||
0x5, 0x44, 0x1f, 0x4e, 0x37, 0x5e, 0x5d, 0x10, 0x27, 0x8d, 0xd7, 0x97, 0x44, 0xe8, 0x34, 0xed,
|
||||
0x41, 0x7, 0x96, 0xd4, 0xc, 0xd0, 0x10, 0x20, 0x40, 0x0, 0x5, 0x5b, 0x85, 0x7f, 0x15, 0x3,
|
||||
0xe7, 0x31, 0x82, 0x88, 0x3, 0xc5, 0x11, 0x3, 0x93, 0x8d, 0xd7, 0x97, 0x44, 0x9, 0xe3, 0x75,
|
||||
0xe5, 0xd1, 0x3a, 0xd, 0x3b, 0x50, 0x41, 0xe5, 0xb5, 0x3, 0x16, 0x4, 0x8, 0x10, 0x0, 0x1, 0xc,
|
||||
0x61, 0x7a, 0xd4, 0x10, 0x5b, 0x3a, 0xa, 0xd, 0x5, 0xb, 0x40, 0x20, 0x4e, 0x37, 0x5e, 0x5d,
|
||||
0x10, 0x27, 0x8d, 0xd7, 0x97, 0x44, 0xe8, 0x34, 0xed, 0x41, 0x7, 0x96, 0xd4, 0xc, 0xc8, 0x10,
|
||||
0x20, 0x40, 0x0, 0x5, 0x5b, 0xde, 0x7c, 0x62, 0x4, 0x1a, 0xce, 0xa2, 0x7, 0xa7, 0x1b, 0xaf,
|
||||
0x2e, 0x88, 0x13, 0xc6, 0xeb, 0xcb, 0xa2, 0x74, 0x1a, 0x76, 0xa0, 0x83, 0xcb, 0x6a, 0x6, 0x68,
|
||||
0x8, 0x10, 0x20, 0x0, 0x2, 0xad, 0x7e, 0x41, 0x99, 0x1, 0xf3, 0x98, 0xc1, 0x42, 0x1, 0xa1,
|
||||
0x88, 0x2, 0x9, 0xc6, 0xeb, 0xcb, 0xa2, 0x4, 0xf1, 0xba, 0xf2, 0xe8, 0x9d, 0x6, 0x9d, 0xa8,
|
||||
0x20, 0xf2, 0xda, 0x81, 0x99, 0x2, 0x4, 0x8, 0x0, 0x0, 0xab, 0x6b, 0xd0, 0x6, 0xc0, 0x83, 0x59,
|
||||
0xd4, 0x41, 0x34, 0xe3, 0x75, 0xe5, 0xd1, 0x2, 0x78, 0xdd, 0x79, 0x74, 0x4e, 0x83, 0x4e, 0xd4,
|
||||
0x10, 0x79, 0x6d, 0x40, 0xc9, 0x1, 0x2, 0x4, 0x0, 0x0, 0x40, 0x42, 0x9a, 0x56, 0x3, 0x38, 0xfd,
|
||||
0x82, 0x85, 0x2, 0x43, 0x80,
|
||||
];
|
||||
#[cfg(feature = "spat")]
|
||||
pub const MAPEM_HH_2349_L33: &[u8] = &[
|
||||
0x2, 0x5, 0x0, 0x12, 0x25, 0xf4, 0x18, 0x0, 0x42, 0x6, 0xd, 0x91, 0x21, 0x93, 0x3b, 0x8c, 0x99,
|
||||
0xb4, 0x73, 0x71, 0x8b, 0x6, 0xe, 0x60, 0x0, 0x61, 0x25, 0xa1, 0x5, 0x58, 0xef, 0x69, 0x97,
|
||||
0x13, 0xed, 0x54, 0x20, 0x28, 0xa3, 0x48, 0x84, 0x29, 0xc6, 0xeb, 0xcb, 0xa2, 0x4, 0xf1, 0xba,
|
||||
0xf2, 0xe8, 0x9d, 0x6, 0x9d, 0xa8, 0x20, 0xf2, 0xda, 0x81, 0x9a, 0x2, 0x4, 0x8, 0x0, 0x0, 0xab,
|
||||
0x7b, 0x2f, 0x8e, 0x40, 0x82, 0x99, 0xd0, 0x51, 0x10, 0x68, 0xaa, 0x0, 0xf2, 0x71, 0xba, 0xf2,
|
||||
0xe8, 0x81, 0x3c, 0x6e, 0xbc, 0xba, 0x27, 0x41, 0xa7, 0x6a, 0x8, 0x3c, 0xb6, 0xa0, 0x66, 0x40,
|
||||
0x81, 0x2, 0x0, 0x0, 0x2a, 0xd7, 0x94, 0x19, 0x68, 0x1f, 0x39, 0x8d, 0x10, 0x75, 0x38, 0xdd,
|
||||
0x79, 0x74, 0x40, 0x9e, 0x37, 0x5e, 0x5d, 0x13, 0xa0, 0xd3, 0xb5, 0x4, 0x1e, 0x5b, 0x50, 0x33,
|
||||
0x40, 0x40, 0x81, 0x0, 0x0, 0x15, 0x6d, 0x52, 0x0, 0xec, 0x10, 0x6b, 0x3a, 0xa, 0x1e, 0xe,
|
||||
0x13, 0x44, 0x1b, 0x4e, 0x37, 0x5e, 0x5d, 0x10, 0x27, 0x8d, 0xd7, 0x97, 0x44, 0xe8, 0x34, 0xed,
|
||||
0x41, 0x7, 0x96, 0xd4, 0xc, 0x90, 0x10, 0x20, 0x40, 0x0, 0x4, 0x23, 0xd, 0xb1, 0x80, 0x4c,
|
||||
0x70, 0x70, 0x28, 0x70, 0x24, 0x49, 0x0, 0x69, 0x38, 0xdd, 0x79, 0x74, 0x40, 0x9e, 0x37, 0x5e,
|
||||
0x5d, 0x13, 0xa0, 0xd3, 0xb5, 0x4, 0x1e, 0x5b, 0x50, 0x32, 0x20, 0x40, 0x81, 0x0, 0x0, 0x10,
|
||||
0x10, 0xe6, 0x91, 0xc0, 0xce, 0x3f, 0x28, 0x83, 0x29, 0xc6, 0xeb, 0xcb, 0xa2, 0x4, 0xf1, 0xba,
|
||||
0xf2, 0xe8, 0x9d, 0x6, 0x9d, 0xa8, 0x20, 0xf2, 0xda, 0x81, 0x92, 0x2, 0x4, 0x8, 0x0, 0x0, 0x81,
|
||||
0xd3, 0x34, 0xf0, 0x9, 0x92, 0x7, 0x5, 0xd, 0x6, 0x8, 0xa2, 0xb, 0xa7, 0x1b, 0xaf, 0x2e, 0x88,
|
||||
0x13, 0xc6, 0xeb, 0xcb, 0xa2, 0x74, 0x1a, 0x76, 0xa0, 0x83, 0xcb, 0x6a, 0x6, 0x48, 0x4, 0x10,
|
||||
0x20, 0x0, 0x2, 0xb, 0xec, 0xdd, 0xc0, 0x19, 0xd7, 0xbc, 0x14, 0x30, 0x16, 0x20, 0x80, 0x2c,
|
||||
0x9c, 0x6e, 0xbc, 0xba, 0x20, 0x4f, 0x1b, 0xaf, 0x2e, 0x89, 0xd0, 0x69, 0xda, 0x82, 0xf, 0x2d,
|
||||
0xa8, 0x19, 0x10, 0x10, 0x40, 0x80, 0x0, 0x8, 0x1c, 0xcb, 0x6f, 0xa0, 0x99, 0x20, 0x54, 0x41,
|
||||
0x54, 0xe3, 0x75, 0xe5, 0xd1, 0x2, 0x78, 0xdd, 0x79, 0x74, 0x4e, 0x83, 0x4e, 0xd4, 0x10, 0x79,
|
||||
0x6d, 0x40, 0xc9, 0x0, 0x82, 0x4, 0x0, 0x0, 0x40, 0x1a, 0x5b, 0x58, 0x3, 0x38, 0xfd, 0x82,
|
||||
0x85, 0x82, 0x83, 0xd0, 0x7, 0x13, 0x8d, 0xd7, 0x97, 0x44, 0x9, 0xe3, 0x75, 0xe5, 0xd1, 0x3a,
|
||||
0xd, 0x3b, 0x50, 0x41, 0xe5, 0xb5, 0x3, 0x22, 0x4, 0x8, 0x10, 0x0, 0x1, 0x6, 0x23, 0x6a, 0xe8,
|
||||
0xc, 0xeb, 0xe4, 0xc8, 0x12, 0xb4, 0x6c, 0x3a, 0x39, 0x73, 0xe9, 0xcb, 0x2e, 0x9c, 0xd9, 0x77,
|
||||
0x20, 0x64, 0x83, 0x4e, 0xd4, 0x10, 0x79, 0x6d, 0x40, 0xcc, 0xd0, 0x80, 0x0, 0x0, 0x12, 0xad,
|
||||
0x1e, 0x3f, 0x1c, 0x4, 0xed, 0x3b, 0xc2, 0xa7, 0xc3, 0x3c, 0xe1, 0x82, 0xc1, 0x20, 0x0, 0xc,
|
||||
0x11, 0x40, 0x21, 0x68, 0xd8, 0x74, 0x72, 0xe7, 0xd3, 0x96, 0x5d, 0x39, 0xb2, 0xee, 0x40, 0xcd,
|
||||
0x6, 0x9d, 0xa8, 0x20, 0xf2, 0xda, 0x81, 0x91, 0x11, 0x0, 0x0, 0x0, 0x24, 0xa, 0xb9, 0x91,
|
||||
0x10, 0xc3, 0xd3, 0xb2, 0x28, 0x71, 0x3c, 0x54, 0x41, 0x34, 0xe3, 0x75, 0xe5, 0xd1, 0x2, 0x78,
|
||||
0xdd, 0x79, 0x74, 0x4e, 0x83, 0x4e, 0xd4, 0x10, 0x79, 0x6d, 0x40, 0xc9, 0x1, 0x2, 0x4, 0x0,
|
||||
0x0, 0x40, 0x42, 0x9a, 0x56, 0x3, 0x38, 0xfd, 0x82, 0x85, 0x2, 0x43, 0x90, 0x6, 0x13, 0x8d,
|
||||
0xd7, 0x97, 0x44, 0x9, 0xe3, 0x75, 0xe5, 0xd1, 0x3a, 0xd, 0x3b, 0x50, 0x41, 0xe5, 0xb5, 0x3,
|
||||
0x22, 0x2, 0x8, 0x10, 0x0, 0x1, 0x8, 0xf2, 0x70, 0xe4, 0x13, 0x1c, 0x22, 0xa0, 0x8, 0xb4, 0x6c,
|
||||
0x3a, 0x39, 0x73, 0xe9, 0xcb, 0x2e, 0x9c, 0xd9, 0x77, 0x20, 0x68, 0x83, 0x4e, 0xd4, 0x10, 0x79,
|
||||
0x6d, 0x40, 0xc4, 0x48, 0x80, 0x0, 0x0, 0x11, 0x80, 0x7e, 0x2c, 0x81, 0xfd, 0xb2, 0x5c, 0x58,
|
||||
0xbf, 0x17, 0x48, 0x2, 0x49, 0xc6, 0xeb, 0xcb, 0xa2, 0x4, 0xf1, 0xba, 0xf2, 0xe8, 0x9d, 0x6,
|
||||
0x9d, 0xa8, 0x20, 0xf2, 0xda, 0x81, 0x91, 0x2, 0x4, 0x8, 0x0, 0x0, 0x84, 0x5f, 0xb6, 0x64, 0x9,
|
||||
0x8e, 0xc, 0x44, 0xd, 0x4e, 0x37, 0x5e, 0x5d, 0x10, 0x27, 0x8d, 0xd7, 0x97, 0x44, 0xe8, 0x34,
|
||||
0xed, 0x41, 0x7, 0x96, 0xd4, 0xc, 0x58, 0x10, 0x20, 0x40, 0x0, 0x4, 0x2e, 0x8a, 0x53, 0x20,
|
||||
0x3e, 0xb3, 0x18, 0x28, 0x38, 0x14, 0x29, 0x0, 0x51, 0x38, 0xdd, 0x79, 0x74, 0x40, 0x9e, 0x37,
|
||||
0x5e, 0x5d, 0x13, 0xa0, 0xd3, 0xb5, 0x4, 0x1e, 0x5b, 0x50, 0x32, 0x20, 0x40, 0x81, 0x0, 0x0,
|
||||
0x10, 0x3a, 0xd6, 0x9d, 0x41, 0x32, 0x40, 0xa8, 0x83, 0xe9, 0xc6, 0xeb, 0xcb, 0xa2, 0x4, 0xf1,
|
||||
0xba, 0xf2, 0xe8, 0x9d, 0x6, 0x9d, 0xa8, 0x20, 0xf2, 0xda, 0x81, 0x9a, 0x2, 0x4, 0x8, 0x0, 0x0,
|
||||
0xab, 0x70, 0xaf, 0xe2, 0xa0, 0x7c, 0xe6, 0x30, 0x51, 0x0, 0x78, 0xa2, 0x20, 0x72, 0x71, 0xba,
|
||||
0xf2, 0xe8, 0x81, 0x3c, 0x6e, 0xbc, 0xba, 0x27, 0x41, 0xa7, 0x6a, 0x8, 0x3c, 0xb6, 0xa0, 0x62,
|
||||
0xc0, 0x81, 0x2, 0x0, 0x0, 0x21, 0x8c, 0x2f, 0x5a, 0x82, 0xb, 0x67, 0x41, 0x41, 0xa0, 0xa1,
|
||||
0x68, 0x4, 0x9, 0xc6, 0xeb, 0xcb, 0xa2, 0x4, 0xf1, 0xba, 0xf2, 0xe8, 0x9d, 0x6, 0x9d, 0xa8,
|
||||
0x20, 0xf2, 0xda, 0x81, 0x99, 0x2, 0x4, 0x8, 0x0, 0x0, 0xab, 0x7b, 0xcf, 0x8c, 0x40, 0x83,
|
||||
0x59, 0xd4, 0x40, 0xf4, 0xe3, 0x75, 0xe5, 0xd1, 0x2, 0x78, 0xdd, 0x79, 0x74, 0x4e, 0x83, 0x4e,
|
||||
0xd4, 0x10, 0x79, 0x6d, 0x40, 0xcd, 0x1, 0x2, 0x4, 0x0, 0x0, 0x55, 0xaf, 0xc8, 0x33, 0x20,
|
||||
0x3e, 0x73, 0x18, 0x28, 0x40, 0x34, 0x31, 0x90, 0x29, 0x68, 0xd8, 0x74, 0x72, 0xe7, 0xd3, 0x96,
|
||||
0x5d, 0x39, 0xb2, 0xee, 0x40, 0xc5, 0x6, 0x9d, 0xa8, 0x20, 0xf2, 0xda, 0x81, 0x99, 0xa1, 0x0,
|
||||
0x0, 0x0, 0x45, 0x5a, 0x6e, 0x7c, 0xf8, 0x9, 0xda, 0x77, 0x80, 0x80, 0x60, 0x2, 0xa7, 0xbf,
|
||||
0xbc, 0xf1, 0xa, 0xc1, 0x60, 0x0, 0xc, 0x15, 0x61, 0x4, 0x0, 0xc, 0xc, 0x80, 0x20, 0x9c, 0x6e,
|
||||
0xbc, 0xba, 0x20, 0x4f, 0x1b, 0xaf, 0x2e, 0x89, 0xd0, 0x69, 0xda, 0x82, 0xf, 0x2d, 0xa8, 0x19,
|
||||
0x90, 0x20, 0x40, 0x80, 0x0, 0xa, 0xb6, 0xbd, 0x0, 0x6c, 0x8, 0x35, 0x9d, 0x40, 0x22, 0x4e,
|
||||
0x37, 0x5e, 0x5d, 0x10, 0x27, 0x8d, 0xd7, 0x97, 0x44, 0xe8, 0x34, 0xed, 0x41, 0x7, 0x96, 0xd4,
|
||||
0xc, 0xc8, 0x10, 0x20, 0x40, 0x0, 0x5, 0x5b, 0x90, 0x7f, 0x2e, 0x3, 0xed, 0x31, 0xa8, 0x2,
|
||||
0xad, 0x1b, 0xe, 0x8e, 0x5c, 0xfa, 0x72, 0xcb, 0xa7, 0x36, 0x5d, 0xc8, 0x1a, 0xa0, 0xd3, 0xb5,
|
||||
0x4, 0x1e, 0x5b, 0x50, 0x31, 0x12, 0x20, 0x0, 0x0, 0x4, 0x60, 0x5b, 0x80, 0xa0, 0x73, 0x1c,
|
||||
0x62, 0x17, 0x9, 0x48, 0x12, 0x20, 0x8a, 0x71, 0xba, 0xf2, 0xe8, 0x81, 0x3c, 0x6e, 0xbc, 0xba,
|
||||
0x27, 0x41, 0xa7, 0x6a, 0x8, 0x3c, 0xb6, 0xa0, 0x64, 0x80, 0x81, 0x2, 0x0, 0x0, 0x20, 0xc3,
|
||||
0x4d, 0x6f, 0x1, 0x9d, 0x7d, 0x1, 0x42, 0x41, 0x1, 0xa0,
|
||||
];
|
||||
|
||||
// More data:
|
||||
// - 34 (Neuer Pferdemarkt/Neuer Kamp): 2 layers, quite complex bike crossings
|
||||
|
||||
/// Just some random SPATEM
|
||||
#[cfg(feature = "spat")]
|
||||
pub const SPATEM: &[u8] = &[
|
||||
0x02, 0x04, 0x00, 0x00, 0x30, 0x16, 0x00, 0x38, 0x4a, 0x6c, 0x1a, 0x17, 0xe4, 0xd4, 0xa7, 0x7d,
|
||||
0x93, 0x5b, 0x8c, 0x9a, 0xb0, 0x63, 0x71, 0x92, 0xe6, 0x50, 0x00, 0x30, 0x9c, 0x50, 0x40, 0x00,
|
||||
0x0e, 0xa7, 0x1d, 0x4f, 0xf0, 0x10, 0x00, 0x22, 0x8c, 0xe2, 0xf9, 0x44, 0xff, 0x63, 0x5b, 0x63,
|
||||
0x48, 0x71, 0xad, 0xb1, 0xae, 0xa1, 0xae, 0xa7, 0x80, 0x10, 0xa1, 0xb8, 0xbe, 0x29, 0x40, 0xa0,
|
||||
0xd6, 0xd8, 0xd1, 0x5c, 0x6b, 0x6c, 0x6c, 0x0c, 0x6c, 0x0d, 0xe0, 0x06, 0x28, 0x0e, 0x2d, 0xfa,
|
||||
0x68, 0xf6, 0x35, 0xb6, 0x34, 0x57, 0x1a, 0xdb, 0x1b, 0x03, 0x1b, 0x03, 0x78, 0x02, 0x0a, 0x1b,
|
||||
0x8d, 0x8b, 0x8d, 0xc0, 0x0d, 0xa7, 0x0d, 0x11, 0xc6, 0xd3, 0x86, 0xd4, 0xc6, 0xd4, 0xde, 0x00,
|
||||
0xa2, 0x8a, 0xe3, 0x5f, 0x23, 0x6c, 0x43, 0x66, 0x03, 0x43, 0x71, 0xb3, 0x01, 0xbb, 0x71, 0xba,
|
||||
0xd1, 0x80, 0x30, 0xa0, 0x38, 0xaf, 0x79, 0x86, 0x50, 0xd6, 0xd8, 0xd1, 0x5c, 0x6b, 0x6c, 0x6c,
|
||||
0x0c, 0x6c, 0x0c, 0x60, 0x0e, 0x28, 0xce, 0x35, 0xfc, 0x36, 0xce, 0x36, 0x6a, 0x34, 0x87, 0x1b,
|
||||
0x35, 0x1b, 0x44, 0x1b, 0x44, 0x78, 0x04, 0x0a, 0x1b, 0x8d, 0x8b, 0x8d, 0xc0, 0x0d, 0xa7, 0x0d,
|
||||
0x15, 0xc6, 0xd3, 0x86, 0xe3, 0xc6, 0xe1, 0x46, 0x01, 0x22, 0x80, 0xe2, 0xdf, 0xa5, 0xcd, 0x03,
|
||||
0x5b, 0x63, 0x45, 0x71, 0xad, 0xb1, 0xb1, 0xc1, 0xb0, 0xd1, 0x80,
|
||||
];
|
||||
|
||||
/// Some random CAM
|
||||
#[cfg(feature = "cam")]
|
||||
pub const CAM: &[u8] = &[
|
||||
0x02, 0x02, 0xde, 0x14, 0x0c, 0xe5, 0xc7, 0xc0, 0x40, 0x5a, 0xb2, 0x3d, 0x82, 0xce, 0x27, 0x81,
|
||||
0xe9, 0xa2, 0x78, 0x27, 0x4b, 0xc6, 0x33, 0xfa, 0x54, 0x58, 0x7c, 0xa0, 0xa2, 0x7e, 0x83, 0x02,
|
||||
0x96, 0x8a, 0x97, 0x33, 0xff, 0x82, 0x00, 0x1a, 0x10, 0x3f, 0xe0, 0x14, 0x39, 0x80, 0x10, 0x6e,
|
||||
0x00, 0x75, 0x80, 0x11, 0x58, 0xce, 0x00, 0x02, 0xf0, 0x3a, 0xdc, 0x08, 0xc4, 0xc8, 0x00, 0x01,
|
||||
0x57, 0x81, 0xd6, 0x20, 0x46, 0x96, 0x33, 0x80, 0x0a, 0xbc, 0x0e, 0xdb, 0x02, 0x39, 0x31, 0x9c,
|
||||
0x00, 0x55, 0xe0, 0x75, 0x08, 0x11, 0x85, 0x90, 0x00, 0x02, 0xaf, 0x03, 0xa0, 0xc0, 0x91, 0x2c,
|
||||
0x80, 0x00, 0x16, 0x78, 0x1c, 0x9e, 0x05, 0x65, 0x64, 0x00, 0x00, 0xc3, 0xc0, 0xe0, 0x90, 0x2d,
|
||||
0xbb, 0x19, 0xc0, 0x06, 0xde, 0x05, 0x8d, 0x81, 0x02, 0x18, 0xce, 0x00, 0x35, 0xf0, 0x15, 0x5c,
|
||||
0x00, 0x06, 0xc6, 0x70, 0x00, 0xdf, 0x80, 0x8d, 0x5f, 0xde, 0x66, 0x27, 0x00, 0x07, 0x3c, 0x04,
|
||||
0x76, 0xfd, 0x67, 0x31, 0x9c, 0x00, 0x58, 0x60, 0x41, 0x37, 0xd3, 0x15, 0x89, 0xc0, 0x06, 0xdc,
|
||||
];
|
||||
|
||||
#[cfg(any(feature = "cam_tx", feature = "uart"))]
|
||||
pub const CAM_SMALL: &[u8] = &[
|
||||
0x2, 0x2, 0xc1, 0x73, 0x25, 0x68, 0x17, 0x28, 0x40, 0x29, 0xd5, 0xb3, 0x40, 0xf, 0x1f, 0xea,
|
||||
0xf0, 0x1f, 0xff, 0xff, 0xfc, 0x23, 0xb7, 0x74, 0x3e, 0x0, 0x0, 0xf, 0xc0, 0x0, 0x7e, 0x81,
|
||||
0x38, 0x25, 0x7, 0x37, 0xfe, 0xeb, 0xff, 0xf6, 0x0, 0x0, 0xd, 0xff, 0xff, 0x7f, 0xff, 0xf1,
|
||||
0xce, 0x40, 0x40, 0x0,
|
||||
];
|
||||
106
src/ubx.rs
106
src/ubx.rs
|
|
@ -1,106 +0,0 @@
|
|||
//! u-blox helpers
|
||||
#![allow(unused)]
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub fn pretty_print_mon_ver(packet: &ublox::mon_ver::MonVerRef) -> alloc::string::String {
|
||||
alloc::format!(
|
||||
"UBX-MON-VER: SW version: {} HW version: {}; Extensions: {:?}",
|
||||
packet.software_version(),
|
||||
packet.hardware_version(),
|
||||
packet.extension().collect::<Vec<&str>>()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn pretty_print_mon_comms(packet: &ublox::mon_comms::MonCommsRef) -> alloc::string::String {
|
||||
alloc::format!(
|
||||
"UBX-MON-COMMS: {} ports ({:?}), {} tx err, info {:?}",
|
||||
packet.n_ports(),
|
||||
packet.prot_ids(),
|
||||
packet.tx_errors(),
|
||||
packet
|
||||
.ports()
|
||||
.map(|v| alloc::format!("{v:?}"))
|
||||
.collect::<Vec<_>>()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn pretty_print_mon_rf(packet: &ublox::mon_rf::MonRfRef) -> alloc::string::String {
|
||||
alloc::format!(
|
||||
"UBX-MON-RF: {:?}",
|
||||
packet
|
||||
.blocks()
|
||||
.map(|v| alloc::format!("{v:?}"))
|
||||
.collect::<Vec<_>>()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn pretty_print_nav_sat(packet: &ublox::nav_sat::NavSatRef) -> alloc::string::String {
|
||||
// sort by quality flags
|
||||
let mut sat_infos = packet.svs().collect::<Vec<_>>();
|
||||
sat_infos.sort_unstable_by_key(|v| v.flags_raw() & 0x7);
|
||||
sat_infos.reverse();
|
||||
|
||||
let sat_info_strings = sat_infos
|
||||
.iter()
|
||||
.map(|v| {
|
||||
alloc::format!(
|
||||
"- {}/{:3}: E {:3.0}, A {:3.0}, CNO {:2.1} dBHz, {:?}, {:?}",
|
||||
v.gnss_id(),
|
||||
v.sv_id(),
|
||||
v.elev(),
|
||||
v.azim(),
|
||||
v.cno(),
|
||||
v.flags().quality_ind(),
|
||||
v.flags().health(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
alloc::format!(
|
||||
"UBX-NAV-SAT: num {:?}\n{}",
|
||||
packet.num_svs(),
|
||||
sat_info_strings.join("\n")
|
||||
)
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&ublox::nav_pvt::proto27::NavPvtRef<'a>> for crate::applogic::GnssFix {
|
||||
type Error = alloc::string::String;
|
||||
|
||||
fn try_from(value: &ublox::nav_pvt::proto27::NavPvtRef<'a>) -> Result<Self, Self::Error> {
|
||||
let has_time = value.fix_type() == ublox::GnssFixType::Fix3D
|
||||
|| value.fix_type() == ublox::GnssFixType::GPSPlusDeadReckoning
|
||||
|| value.fix_type() == ublox::GnssFixType::TimeOnlyFix;
|
||||
let has_posvel = value.fix_type() == ublox::GnssFixType::Fix3D
|
||||
|| value.fix_type() == ublox::GnssFixType::GPSPlusDeadReckoning;
|
||||
|
||||
if !has_posvel || !has_time {
|
||||
return Err(alloc::format!(
|
||||
"NAV-PVT without valid fix: {:?}",
|
||||
value.fix_type()
|
||||
));
|
||||
}
|
||||
|
||||
let pos: ublox::PositionLLA = value.into();
|
||||
let vel: ublox::Velocity = value.into();
|
||||
|
||||
let time: chrono::DateTime<chrono::Utc> = value
|
||||
.try_into()
|
||||
.map_err(|err| alloc::format!("Failed to convert time: {err}"))?;
|
||||
|
||||
let latitude_deg = pos.lat;
|
||||
let longitude_deg = pos.lon;
|
||||
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
|
||||
let heading_deg = vel.heading as f32;
|
||||
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
|
||||
let speed_mps = vel.speed as f32;
|
||||
|
||||
Ok(Self {
|
||||
time: Some(time.naive_utc()),
|
||||
latitude_deg,
|
||||
longitude_deg,
|
||||
heading_deg: Some(heading_deg),
|
||||
speed_mps: Some(speed_mps),
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue