Evaluate remaining red/green time for upcoming intersection
This commit is contained in:
parent
933106b7ea
commit
7463adb038
12 changed files with 2979 additions and 103 deletions
123
Cargo.lock
generated
123
Cargo.lock
generated
|
|
@ -17,6 +17,56 @@ version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c583acf993cf4245c4acb0a2cc2ab1f9cc097de73411bb6d3647ff6af2b1013d"
|
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]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.102"
|
version = "1.0.102"
|
||||||
|
|
@ -29,6 +79,12 @@ version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "assert_float_eq"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd9777fd8234cccd34019d9e26d70c8247f9baf24c8fdc0b56c548cf943af2ed"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|
@ -164,6 +220,12 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-default"
|
name = "const-default"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
|
@ -633,6 +695,29 @@ dependencies = [
|
||||||
"syn 2.0.117",
|
"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]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
|
@ -1011,6 +1096,7 @@ dependencies = [
|
||||||
name = "esp32-c_its-companion"
|
name = "esp32-c_its-companion"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"assert_float_eq",
|
||||||
"c-its-parser",
|
"c-its-parser",
|
||||||
"chrono",
|
"chrono",
|
||||||
"critical-section",
|
"critical-section",
|
||||||
|
|
@ -1020,6 +1106,7 @@ dependencies = [
|
||||||
"embassy_gps",
|
"embassy_gps",
|
||||||
"embedded-io 0.7.1",
|
"embedded-io 0.7.1",
|
||||||
"embedded-io-async 0.7.0",
|
"embedded-io-async 0.7.0",
|
||||||
|
"env_logger",
|
||||||
"esp-alloc",
|
"esp-alloc",
|
||||||
"esp-backtrace",
|
"esp-backtrace",
|
||||||
"esp-bootloader-esp-idf",
|
"esp-bootloader-esp-idf",
|
||||||
|
|
@ -1027,7 +1114,9 @@ dependencies = [
|
||||||
"esp-println",
|
"esp-println",
|
||||||
"esp-radio",
|
"esp-radio",
|
||||||
"esp-rtos",
|
"esp-rtos",
|
||||||
|
"geo-types",
|
||||||
"log",
|
"log",
|
||||||
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1333,6 +1422,12 @@ dependencies = [
|
||||||
"syn 2.0.117",
|
"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]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
|
|
@ -1594,6 +1689,12 @@ version = "1.21.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
|
|
@ -1757,6 +1858,18 @@ dependencies = [
|
||||||
"uuid",
|
"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]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
|
|
@ -1770,9 +1883,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.10"
|
version = "0.8.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "riscv"
|
name = "riscv"
|
||||||
|
|
@ -2202,6 +2315,12 @@ version = "0.2.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.23.1"
|
version = "1.23.1"
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ cam = ["c-its-parser/cam"]
|
||||||
denm = ["c-its-parser/denm"]
|
denm = ["c-its-parser/denm"]
|
||||||
# evaluate SPAT information
|
# evaluate SPAT information
|
||||||
spat = ["c-its-parser/spatem", "c-its-parser/mapem"]
|
spat = ["c-its-parser/spatem", "c-its-parser/mapem"]
|
||||||
|
# enable some debug printing (without enabling debug logging of the ESP RTOS)
|
||||||
|
spat_debug = []
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
@ -86,6 +88,12 @@ c-its-parser = { version = "2.2.2", default-features = false, features = [
|
||||||
] }
|
] }
|
||||||
chrono = { version = "0.4.44", default-features = false, features = ["alloc"] }
|
chrono = { version = "0.4.44", default-features = false, features = ["alloc"] }
|
||||||
crossbeam-queue = { version = "0.3.12", 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 }
|
||||||
|
num-traits = { version = "0.2.19", default-features = false, features = ["libm"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
assert_float_eq = "1.2.0"
|
||||||
|
env_logger = "0.11.10"
|
||||||
|
|
||||||
|
|
||||||
# For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html
|
# For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html
|
||||||
|
|
|
||||||
36
Readme.md
36
Readme.md
|
|
@ -2,6 +2,29 @@
|
||||||
|
|
||||||
Displays different C-ITS receive-only use cases for cheap.
|
Displays different C-ITS receive-only use cases for cheap.
|
||||||
|
|
||||||
|
## Hardware Requirements
|
||||||
|
The code is currently tailored to:
|
||||||
|
- Seeed XIAO ESP32-C5 (8MB PSRAM)
|
||||||
|
- Seeed XIAO L67K (connected on GPIO12 (RX), GPIO11 (TX), GPIO1 (wakeup) and GPIO25 (reset))
|
||||||
|
TODO: only enable in `spat` feature?
|
||||||
|
- TODO: display (when using `spat` feature)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Toolchain Installation
|
### Toolchain Installation
|
||||||
|
|
@ -24,10 +47,23 @@ rustup component add --toolchain nightly rustfmt
|
||||||
```
|
```
|
||||||
|
|
||||||
### Build and Flash
|
### Build and Flash
|
||||||
|
In default configuration only signal phases (SPAT) data is displayed for the upcoming intersection.
|
||||||
|
Additional features are available though 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
|
||||||
|
- `denm`: Enable DENMs
|
||||||
|
- `cam`: Enable CAMs
|
||||||
|
|
||||||
Connect your ESP32-C5 and run:
|
Connect your ESP32-C5 and run:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
# default configuration
|
||||||
cargo run --release
|
cargo run --release
|
||||||
|
|
||||||
|
# for e.g. also enable DENM feature
|
||||||
|
cargo run --release -F denm
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run Unit Tests on Host
|
### Run Unit Tests on Host
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,551 @@
|
||||||
//! Application Logic
|
//! Application Logic
|
||||||
|
|
||||||
|
#[cfg(feature = "spat")]
|
||||||
|
use alloc::rc::Rc;
|
||||||
|
#[cfg(feature = "spat")]
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "riscv32", feature = "spat"))]
|
||||||
|
use esp_println::println;
|
||||||
|
#[cfg(feature = "spat")]
|
||||||
|
use log::{debug, error};
|
||||||
|
use log::{info, warn};
|
||||||
|
|
||||||
|
#[cfg(feature = "spat")]
|
||||||
|
use crate::geo_alg;
|
||||||
|
|
||||||
|
pub mod v2x;
|
||||||
|
|
||||||
#[cfg(feature = "cam")]
|
#[cfg(feature = "cam")]
|
||||||
pub mod cam;
|
pub mod cam;
|
||||||
#[cfg(feature = "denm")]
|
#[cfg(feature = "denm")]
|
||||||
pub mod denm;
|
pub mod denm;
|
||||||
#[cfg(feature = "spat")]
|
#[cfg(feature = "spat")]
|
||||||
pub mod tlc;
|
pub mod tlc;
|
||||||
|
|
||||||
|
#[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 = 10.;
|
||||||
|
|
||||||
|
pub struct State {
|
||||||
|
initialized: bool,
|
||||||
|
time: chrono::NaiveDateTime,
|
||||||
|
|
||||||
|
position: geo_types::Point,
|
||||||
|
heading_deg: Option<f32>,
|
||||||
|
speed_mps: Option<f32>,
|
||||||
|
|
||||||
|
#[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, 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(),
|
||||||
|
position: geo_types::Point::default(),
|
||||||
|
heading_deg: None,
|
||||||
|
speed_mps: None,
|
||||||
|
#[cfg(feature = "spat")]
|
||||||
|
last_prune: chrono::NaiveDateTime::default(),
|
||||||
|
#[cfg(feature = "spat")]
|
||||||
|
tlc_cache: tlc::Data::new(chrono::Duration::seconds(20)),
|
||||||
|
#[cfg(feature = "spat")]
|
||||||
|
glosa_state: GlosaState::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "esp")]
|
||||||
|
pub fn update_with_gpsfix(&mut self, fix: &embassy_gps::types::GpsFix) {
|
||||||
|
self.position = geo_types::Point::new(fix.longitude_deg, fix.latitude_deg);
|
||||||
|
|
||||||
|
if let Some(heading) = fix.true_course_deg {
|
||||||
|
self.heading_deg = Some(heading);
|
||||||
|
} else {
|
||||||
|
warn!("State: Course missing from GpsFix");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(speed) = fix.speed_over_ground {
|
||||||
|
self.speed_mps = Some(speed);
|
||||||
|
} else {
|
||||||
|
warn!("State: Speed missing from GpsFix");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(time) = fix.get_timestamp() {
|
||||||
|
self.time = time;
|
||||||
|
|
||||||
|
if !self.initialized {
|
||||||
|
info!("GNSS fix: {}", self.print_fix());
|
||||||
|
}
|
||||||
|
self.initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_fix(&self) -> alloc::string::String {
|
||||||
|
alloc::format!(
|
||||||
|
"{:?}, {:.4} N, {:.4} E, {:.0}°, {:.2} m/s",
|
||||||
|
self.time,
|
||||||
|
self.position.y(),
|
||||||
|
self.position.x(),
|
||||||
|
self.heading_deg.unwrap_or_default(),
|
||||||
|
self.speed_mps.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// only update "our" intersection
|
||||||
|
if let GlosaState::Locked(data) = &self.glosa_state
|
||||||
|
&& etsi
|
||||||
|
.spat
|
||||||
|
.intersections
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.find(|i| i.id.id.0 == data.intersection_id)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
self.tlc_cache.handle_spatem(self.time, etsi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "spat")]
|
||||||
|
pub fn run_glosa(&mut self) {
|
||||||
|
let Some(own_heading) = self.heading_deg else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let state_update = match &self.glosa_state {
|
||||||
|
// search for upcoming intersection
|
||||||
|
GlosaState::Idle => {
|
||||||
|
debug!(
|
||||||
|
"GLOSA: Searching with {} known intersections",
|
||||||
|
self.tlc_cache.known_intersections().len()
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(search_result) =
|
||||||
|
Self::find_intersection(&self.position, own_heading, &self.tlc_cache)
|
||||||
|
{
|
||||||
|
info!(
|
||||||
|
"GLOSA: Approach has signal groups: {:?}",
|
||||||
|
search_result.signal_groups
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(GlosaState::Locked(search_result))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GlosaState::Locked(info) => {
|
||||||
|
debug!(
|
||||||
|
"GLOSA: Re-evaluating intersection {:4} (at lane {:3})",
|
||||||
|
info.intersection_id, info.lane_id
|
||||||
|
);
|
||||||
|
|
||||||
|
// determine if intersection is left
|
||||||
|
let intersection = info.intersection.borrow();
|
||||||
|
if Self::is_intersection_left(
|
||||||
|
&intersection,
|
||||||
|
info.lane_id,
|
||||||
|
info.approach_id,
|
||||||
|
&self.position,
|
||||||
|
) {
|
||||||
|
info!("GLOSA: Intersection was left -> back to idle");
|
||||||
|
Some(GlosaState::Idle)
|
||||||
|
} 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
|
||||||
|
for sig in approach_sig_grps {
|
||||||
|
if let Some(phase) = sig.phase_to_str()
|
||||||
|
&& let Some(manv) = sig.maneuvers()
|
||||||
|
{
|
||||||
|
use core::ops::Sub;
|
||||||
|
|
||||||
|
let min_end = sig.min_end_time();
|
||||||
|
let dur = min_end.sub(self.time.and_utc()).as_seconds_f32();
|
||||||
|
println!(
|
||||||
|
"GLOSA: {manv} {phase} for {dur:.1} s (until {})",
|
||||||
|
sig.min_end_time()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None // keep state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// can't update the state from within the match statement, so do it here
|
||||||
|
if let Some(new) = state_update {
|
||||||
|
self.glosa_state = new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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_near, angle_near) = GLOSA_SEARCH_ANGLE_PARAMS[0];
|
||||||
|
let (dist_far, angle_far) = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::init_test_env_logger;
|
||||||
|
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.heading_deg = Some(own_heading);
|
||||||
|
|
||||||
|
state.position = own_pos_infront;
|
||||||
|
state.run_glosa();
|
||||||
|
assert!(matches!(state.glosa_state, GlosaState::Idle));
|
||||||
|
|
||||||
|
state.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.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.position = own_pos_passed;
|
||||||
|
state.run_glosa();
|
||||||
|
assert!(matches!(state.glosa_state, GlosaState::Idle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,107 @@
|
||||||
//! C-ITS Traffic Light Data (MAPEM/ SPATEM) Evaluation
|
//! C-ITS Traffic Light Data (MAPEM/ SPATEM) Evaluation/ Cache
|
||||||
|
|
||||||
use c_its_parser::standards::mapem_2_2_1::mapem_pdu_descriptions;
|
use alloc::rc::Rc;
|
||||||
use c_its_parser::standards::spatem_2_2_1::spatem_pdu_descriptions;
|
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;
|
use chrono::Datelike;
|
||||||
#[cfg(target_arch = "riscv32")]
|
#[cfg(all(target_arch = "riscv32", feature = "spat_debug"))]
|
||||||
use esp_println::println;
|
use esp_println::println;
|
||||||
|
|
||||||
pub fn handle_mapem(etsi: &mapem_pdu_descriptions::MAPEM) {
|
use super::v2x::map;
|
||||||
// TODO: proof of concept only -- replace with actual evaluation
|
use crate::cache::Cache;
|
||||||
|
|
||||||
|
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 {
|
if let Some(map) = &etsi.map.intersections {
|
||||||
for int in &map.0 {
|
for geom in &map.0 {
|
||||||
println!("got MAPEM for intersection {}", int.id.id.0);
|
let int_id = geom.id.id.0;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_spatem(etsi: &spatem_pdu_descriptions::SPATEM, now: &chrono::NaiveDateTime) {
|
if self.intersections.contains(&int_id) {
|
||||||
// TODO: proof of concept only -- replace with actual evaluation
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_spatem(&mut self, time_now: chrono::NaiveDateTime, etsi: &SPATEM) {
|
||||||
for int in &etsi.spat.intersections.0 {
|
for int in &etsi.spat.intersections.0 {
|
||||||
println!("got SPATEM for intersection {}", int.id.id.0);
|
let int_id = int.id.id.0;
|
||||||
|
|
||||||
if let (Some(moy), Some(second)) = (&int.moy, &int.time_stamp) {
|
if self.intersections.contains(&int_id) {
|
||||||
let year = now.year();
|
// update with SPAT data
|
||||||
|
|
||||||
let ref_time = c_its_parser::time_utils::time_from_moy_and_dsecond(moy, second, year);
|
// we can ignore the result, since we're sure the item exists
|
||||||
println!("- ref time: {ref_time:?}");
|
let _ = self.intersections.update(time_now, int_id, |i| {
|
||||||
|
if i.is_complete() {
|
||||||
for state in &int.states.0 {
|
// add SPAT information to intersection
|
||||||
let sig_grp = state.signal_group.0;
|
i.add_spat(int, time_now.year());
|
||||||
|
|
||||||
if let Some(event) = state.state_time_speed.0.first() {
|
|
||||||
if let Some(timing) = &event.timing {
|
|
||||||
let min_end_time = timing.min_end_time.to_datetime_from_moy(moy, year);
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"- sig grp. {sig_grp:03}: until {min_end_time:?} at {:?}",
|
|
||||||
event.event_state
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"- sig grp. {sig_grp:03}: {:?} until unknown",
|
|
||||||
event.event_state
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
println!("- sig grp. {sig_grp:03}: no events");
|
// drop message since we don't have (full) MAP yet
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1019
src/applogic/v2x/map.rs
Normal file
1019
src/applogic/v2x/map.rs
Normal file
File diff suppressed because it is too large
Load diff
4
src/applogic/v2x/mod.rs
Normal file
4
src/applogic/v2x/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
//! Types for V2X data handling
|
||||||
|
|
||||||
|
#[cfg(feature = "spat")]
|
||||||
|
pub mod map;
|
||||||
221
src/cache.rs
Normal file
221
src/cache.rs
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
//! 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.items.iter().position(|i| i.data.borrow().key() == 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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
Normal file
446
src/geo_alg.rs
Normal file
|
|
@ -0,0 +1,446 @@
|
||||||
|
//! 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/main.rs
78
src/main.rs
|
|
@ -23,65 +23,13 @@ use log::{debug, error, info, warn};
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
mod applogic;
|
mod applogic;
|
||||||
|
#[cfg(feature = "spat")]
|
||||||
|
mod cache;
|
||||||
|
mod geo_alg;
|
||||||
mod radio;
|
mod radio;
|
||||||
|
|
||||||
const WIFI_CHANNEL: radio::Channel = 180;
|
const WIFI_CHANNEL: radio::Channel = 180;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
struct State {
|
|
||||||
initialized: bool,
|
|
||||||
time: chrono::NaiveDateTime,
|
|
||||||
|
|
||||||
lat_deg: f32,
|
|
||||||
lon_deg: f32,
|
|
||||||
heading_deg: Option<f32>,
|
|
||||||
speed_mps: Option<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
fn update_with_gpsfix(&mut self, fix: &embassy_gps::types::GpsFix) {
|
|
||||||
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
|
|
||||||
let lat_deg = fix.latitude_deg as f32;
|
|
||||||
#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
|
|
||||||
let lon_deg = fix.longitude_deg as f32;
|
|
||||||
|
|
||||||
self.lat_deg = lat_deg;
|
|
||||||
self.lon_deg = lon_deg;
|
|
||||||
|
|
||||||
if let Some(heading) = fix.true_course_deg {
|
|
||||||
self.heading_deg = Some(heading);
|
|
||||||
} else {
|
|
||||||
warn!("State: Course missing from GpsFix");
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(speed) = fix.speed_over_ground {
|
|
||||||
self.speed_mps = Some(speed);
|
|
||||||
} else {
|
|
||||||
warn!("State: Speed missing from GpsFix");
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(time) = fix.get_timestamp() {
|
|
||||||
self.time = time;
|
|
||||||
|
|
||||||
if !self.initialized {
|
|
||||||
info!("GNSS fix: {}", self.print_fix());
|
|
||||||
}
|
|
||||||
self.initialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_fix(&self) -> alloc::string::String {
|
|
||||||
alloc::format!(
|
|
||||||
"{:?}, {:.4} N, {:.4} E, {:.0}°, {:.2} m/s",
|
|
||||||
self.time,
|
|
||||||
self.lat_deg,
|
|
||||||
self.lon_deg,
|
|
||||||
self.heading_deg.unwrap_or_default(),
|
|
||||||
self.speed_mps.unwrap_or_default(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This creates a default app-descriptor required by the esp-idf bootloader.
|
// This creates a default app-descriptor required by the esp-idf bootloader.
|
||||||
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
|
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
|
||||||
esp_bootloader_esp_idf::esp_app_desc!();
|
esp_bootloader_esp_idf::esp_app_desc!();
|
||||||
|
|
@ -157,7 +105,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||||
);
|
);
|
||||||
info!("GNSS task started, waiting for GNSS fix...");
|
info!("GNSS task started, waiting for GNSS fix...");
|
||||||
|
|
||||||
let mut state = State::default();
|
let mut state = applogic::State::new();
|
||||||
loop {
|
loop {
|
||||||
embassy_time::Timer::after(embassy_time::Duration::from_millis(5)).await;
|
embassy_time::Timer::after(embassy_time::Duration::from_millis(5)).await;
|
||||||
|
|
||||||
|
|
@ -167,9 +115,12 @@ async fn main(spawner: Spawner) -> ! {
|
||||||
if let Some(fix) = gnss_update_ref.replace(None) {
|
if let Some(fix) = gnss_update_ref.replace(None) {
|
||||||
state.update_with_gpsfix(&fix);
|
state.update_with_gpsfix(&fix);
|
||||||
|
|
||||||
if let Some(time) = fix.get_timestamp() {
|
if fix.get_timestamp().is_some() {
|
||||||
state.time = time;
|
info!("{}", state.print_fix());
|
||||||
info!("{}", state.print_fix()); // TODO: degrate to debug later?
|
|
||||||
|
// run GLOSA algorithm
|
||||||
|
#[cfg(feature = "spat")]
|
||||||
|
state.run_glosa();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -193,15 +144,15 @@ async fn main(spawner: Spawner) -> ! {
|
||||||
geonetworking: _,
|
geonetworking: _,
|
||||||
transport: _,
|
transport: _,
|
||||||
etsi,
|
etsi,
|
||||||
} => applogic::tlc::handle_mapem(&etsi),
|
} => state.handle_mapem(&etsi),
|
||||||
#[cfg(feature = "spat")]
|
#[cfg(feature = "spat")]
|
||||||
ItsMessage::Spatem {
|
ItsMessage::Spatem {
|
||||||
geonetworking: _,
|
geonetworking: _,
|
||||||
transport: _,
|
transport: _,
|
||||||
etsi,
|
etsi,
|
||||||
} => {
|
} => {
|
||||||
if state.initialized {
|
if state.initialized() {
|
||||||
applogic::tlc::handle_spatem(&etsi, &state.time);
|
state.handle_spatem(&etsi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,6 +186,9 @@ async fn main(spawner: Spawner) -> ! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// maintenance
|
||||||
|
state.prune();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
13
src/test.rs
13
src/test.rs
|
|
@ -1,10 +1,23 @@
|
||||||
#![cfg(not(target_arch = "riscv32"))]
|
#![cfg(not(target_arch = "riscv32"))]
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
mod applogic;
|
mod applogic;
|
||||||
mod cache;
|
mod cache;
|
||||||
|
mod geo_alg;
|
||||||
|
mod testdata;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
// env_logger::init();
|
||||||
// dummy only
|
// 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();
|
||||||
|
}
|
||||||
|
|
|
||||||
457
src/testdata.rs
Normal file
457
src/testdata.rs
Normal file
|
|
@ -0,0 +1,457 @@
|
||||||
|
//! 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,
|
||||||
|
];
|
||||||
Loading…
Add table
Add a link
Reference in a new issue