├── .github
├── dependabot.yml
└── workflows
│ ├── coverage.yml
│ └── main.yml
├── .gitignore
├── .rustfmt.toml
├── CHANGELOG.md
├── Cargo.toml
├── LICENSE-MIT
├── README.md
├── asterix-derive
├── Cargo.toml
└── src
│ └── lib.rs
├── src
├── custom_read_write.rs
├── data_item.rs
├── fourty_eight.rs
├── fspec.rs
├── lib.rs
├── modifier.rs
├── thirty_four.rs
└── types.rs
└── tests
└── public_api.rs
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: github-actions
4 | directory: /
5 | schedule:
6 | interval: daily
7 |
8 | - package-ecosystem: cargo
9 | directory: /
10 | schedule:
11 | interval: daily
12 |
--------------------------------------------------------------------------------
/.github/workflows/coverage.yml:
--------------------------------------------------------------------------------
1 | name: Coverage
2 |
3 | on: [pull_request, push]
4 |
5 | permissions:
6 | contents: read
7 |
8 | jobs:
9 | coverage:
10 | runs-on: ubuntu-latest
11 | env:
12 | CARGO_TERM_COLOR: always
13 | steps:
14 | - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
15 | - name: Install Rust
16 | run: rustup update stable
17 | - name: Install cargo-llvm-cov
18 | uses: taiki-e/install-action@f23140dab9d53af716ede5c5e74661a2a8de049c # cargo-llvm-cov
19 | - name: Generate code coverage
20 | run: cargo llvm-cov --workspace --codecov --output-path codecov.json
21 | - name: Upload coverage to Codecov
22 | uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
23 | with:
24 | files: codecov.json
25 | fail_ci_if_error: true
26 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - master
5 | pull_request:
6 | branches:
7 | - master
8 | schedule: [cron: "40 1 * * *"]
9 |
10 | name: ci
11 |
12 | jobs:
13 | # build, test all supported targets
14 | build-test-stable:
15 | runs-on: ubuntu-latest
16 | strategy:
17 | matrix:
18 | targets:
19 | - x86_64-unknown-linux-gnu
20 | toolchain:
21 | - stable
22 | # msrv
23 | - 1.70.0
24 |
25 | steps:
26 | - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
27 | - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # master
28 | with:
29 | toolchain: ${{ matrix.toolchain }}
30 | target: ${{ matrix.targets }}
31 | - uses: Swatinem/rust-cache@3cf7f8cc28d1b4e7d01e3783be10a97d55d483c8 # v2.7.1
32 | - run: cargo build --workspace --target ${{ matrix.targets }}
33 | - run: cargo test --workspace --target ${{ matrix.targets }}
34 |
35 | # fmt and clippy on stable builds
36 | fmt-clippy-stable:
37 | runs-on: ubuntu-latest
38 |
39 | steps:
40 | - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
41 | - uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248 # master
42 | with:
43 | toolchain: stable
44 | target: x86_64-unknown-linux-gnu
45 | components: rustfmt, clippy
46 | - uses: Swatinem/rust-cache@3cf7f8cc28d1b4e7d01e3783be10a97d55d483c8 # v2.7.1
47 | - run: cargo fmt --all --check
48 | - run: cargo clippy --workspace -- -D warnings
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # Added info from idea editor
17 | .idea/
18 |
--------------------------------------------------------------------------------
/.rustfmt.toml:
--------------------------------------------------------------------------------
1 | use_small_heuristics = "Max"
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [Unreleased]
8 |
9 | ## [0.4.0] - 2024-02-09
10 | - Add support for the following 48/030 packets:
11 | - `WrongDFReplyFormatDetected`
12 | - `TransponderAnomalyMs`
13 | - `TransponderAnomalySI`
14 | - `PotentialICConflict`
15 | - `ICConflictDetectionPossible`
16 | - `AMGAllocated(u16)`
17 | - `Manufacture(u16)`
18 |
19 | ## [0.3.1] - 2023-10-14
20 | - Update deku to 0.16
21 | - Update syn to 2.0
22 | - Improve use of syn NestedMeta for loading fspec
23 |
24 | ## [0.3.0] - 2022-11-22
25 | - Update deku to 0.15.0
26 | - Improve deku usage
27 | - [deku-derive] improve rust AST parsing
28 | - use assert_hex for better testing panic display
29 |
30 | ## [0.2.6] - 2020-01-13
31 | - Update deku to 0.10.0
32 | - Use deku attributes for loading fspec
33 | - Other code improvements
34 |
35 | ## [0.2.5] - 2020-11-06
36 | - Update/Use deku 0.9.1
37 |
38 | ## [0.2.4] - 2020-10-03
39 | - Update lib.rs example
40 | - Add CI
41 |
42 | ## [0.2.3] - 2020-10-03
43 | - Update deku: 0.8.0. Gives some speed improvements with less allocation for writing.
44 |
45 | ## [0.2.2] - 2020-09-27
46 | - Introduce asterix-derive(UpdateFspec): Automatic update of Fspec from data_items generation.
47 | - Cat34: Complete
48 |
49 | ## [0.2.1] - 2020-08-30
50 | - Cat48: Add TrackQuality
51 | - Cat48: Add WarningErrorConditionsTargetClass
52 | - Cat48: Add Mode3ACodeConfidenceIndicator
53 | - Cat48: Add ModeCCodeAndConfidenceIndicator
54 | - Cat48: Add HeightMeasuredBy3dRadar
55 | - Cat48: Add RadialDopplerSpeed
56 | - Cat48: Add ACASResolutionAdvisoryReport
57 | - Cat48: Add Mode1CodeOctalRepresentation
58 | - Cat48: Add Mode2CodeOctalRepresentation
59 | - Cat48: Add Mode1CodeConfidenceIndicator
60 | - Cat48: Add Mode2CodeConfidenceIndicator
61 | - Cat34: Add AntennaRotationSpeed
62 | - Update Data Item docs
63 |
64 | ## [0.2.0] - 2020-08-21
65 | - add AsterixPacket::finalize() for updating the packet to the correct fspec and len after
66 | messages are added
67 |
68 | ## [0.1.1] - 2020-08-16
69 | - Add License file (MIT) and prepare Cargo.toml file for release
70 |
71 | ## [0.1.0] - 2020-08-16
72 | - Initial Release for most of CAT048 and CAT034
73 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "asterix"
3 | version = "0.4.0"
4 | authors = ["wcampbell"]
5 | edition = "2021"
6 | categories = ["encoding", "parsing"]
7 | keywords = ["asterix", "radar", "aviation", "network", "protocol"]
8 | license = "MIT"
9 | readme = "README.md"
10 | description = "Encode/Decode for ASTERIX protocol using the deku library"
11 | repository = "https://github.com/wcampbell0x2a/asterix-rs"
12 | rust-version = "1.70.0"
13 |
14 | [workspace]
15 | members = [
16 | "asterix-derive",
17 | ]
18 |
19 | [dependencies]
20 | deku = "0.16"
21 | asterix-derive = { version = "^0.4", path = "asterix-derive" }
22 | assert_hex = "0.4"
23 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | Permission is hereby granted, free of charge, to any
2 | person obtaining a copy of this software and associated
3 | documentation files (the "Software"), to deal in the
4 | Software without restriction, including without
5 | limitation the rights to use, copy, modify, merge,
6 | publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software
8 | is furnished to do so, subject to the following
9 | conditions:
10 |
11 | The above copyright notice and this permission notice
12 | shall be included in all copies or substantial portions
13 | of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Asterix Encode/Decode Library
2 |
3 | [
](https://github.com/wcampbell0x2a/asterix-rs)
4 | [
](https://crates.io/crates/asterix)
5 | [
](https://docs.rs/asterix)
6 | [
](https://github.com/wcampbell0x2a/asterix-rs/actions?query=branch%3Amaster)
7 | [
](https://app.codecov.io/gh/wcampbell0x2a/asterix-rs)
8 |
9 | Currently supported:
10 | - CAT048
11 | - CAT034
12 |
13 | ## Usage
14 | *Compiler support: requires rustc 1.70+*
15 |
16 | Add the following to your `Cargo.toml` file:
17 | ```toml
18 | [dependencies]
19 | asterix = "v0.4.0"
20 | ```
21 |
22 | ## Changelog
23 |
24 | See [CHANGELOG.md](https://github.com/wcampbell0x2a/asterix-rs/blob/master/CHANGELOG.md)
25 |
--------------------------------------------------------------------------------
/asterix-derive/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "asterix-derive"
3 | version = "0.4.0"
4 | authors = ["wcampbell "]
5 | edition = "2021"
6 | description = "proc-macro convenience with the deku library, for updating ASTERIX FSPECs"
7 | readme = "../README.md"
8 | license = "MIT"
9 |
10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
11 |
12 | [dependencies]
13 | syn = {version = "2.0", features = ["full"]}#, features = ["extra-traits"]}
14 | quote = "1.0"
15 | proc-macro2 = "1.0"
16 |
17 | [lib]
18 | proc-macro = true
19 |
--------------------------------------------------------------------------------
/asterix-derive/src/lib.rs:
--------------------------------------------------------------------------------
1 | use proc_macro::TokenStream;
2 | use quote::quote;
3 | use syn::{parse_macro_input, Data, DeriveInput, Fields};
4 |
5 | /// Generate syntax for updating the fspec from the deku style syntax that would be found
6 | /// in a ASTERIX category
7 | ///
8 | /// This is mostly a `hack` in the true sense of the word. Although it works pretty well for
9 | /// the well defined deku derives.
10 | ///
11 | /// Input:
12 | /// ```rust, ignore
13 | /// use asterix::data_item::*;
14 | /// #[deku(skip, cond = "is_fspec(DataSourceIdentifier::FRN_48, fspec, 0)")]
15 | /// pub data_source_identifier: Option,
16 | /// ```
17 | ///
18 | /// General Output:
19 | /// ```rust, ignore
20 | /// use asterix::data_item::*;
21 | /// if self.data_source_identifier.is_some() {
22 | /// fspec[0] |= DataSourceIdentifier::FRN_48;
23 | /// }
24 | ///
25 | /// ```
26 | ///
27 | /// There are a few parts that are pre-pended and appended to the end of the above statements, with
28 | /// generation for the vec and cleaning up the fspec.
29 | #[proc_macro_derive(UpdateFspec)]
30 | #[doc(hidden)]
31 | pub fn derive_answer_fn(input: TokenStream) -> TokenStream {
32 | // Parse the input tokens into a syntax tree
33 | let input = parse_macro_input!(input as DeriveInput);
34 |
35 | let name = &input.ident; // struct name
36 |
37 | // (self.name, fspec_num, FRN)
38 | let mut data_items: Vec<(String, String, String)> = vec![];
39 |
40 | if let Data::Struct(s) = input.data {
41 | if let Fields::Named(f) = s.fields {
42 | for field in f.named.iter() {
43 | let ident = field.ident.as_ref().unwrap(); // they are 'named' fields
44 | // check if is first 'fspec' field in struct, skip
45 | if ident == "fspec" {
46 | continue;
47 | }
48 | for attr in &field.attrs {
49 | // check if doc ident, we don't need that one
50 | if !attr.path().is_ident("deku") {
51 | continue;
52 | }
53 | // ident should be 'deku' at this point
54 | // pulling out the `TokenStream` from `Meta::List` and parsing
55 | attr.parse_nested_meta(|meta| {
56 | if meta.path.is_ident("cond") {
57 | let value = meta.value()?; // this parses the `=`
58 | let token: syn::LitStr = value.parse()?; // this parses `"is_fspec(...)"`
59 | let fn_call = token.parse::().unwrap();
60 | let frn = if let syn::Expr::Path(attrs) = &fn_call.args[0] {
61 | format!(
62 | "{}::{}",
63 | attrs.path.segments[0].ident, attrs.path.segments[1].ident,
64 | )
65 | } else {
66 | unreachable!()
67 | };
68 |
69 | let fspec_num = if let syn::Expr::Lit(lit) = &fn_call.args[2] {
70 | if let syn::Lit::Int(int) = &lit.lit {
71 | int.to_string()
72 | } else {
73 | unreachable!();
74 | }
75 | } else {
76 | unreachable!();
77 | };
78 | data_items.push((
79 | ident.to_string(),
80 | fspec_num.to_string(),
81 | frn.to_string(),
82 | ));
83 | }
84 | Ok(())
85 | })
86 | .expect("Error parsing nested meta");
87 | }
88 | }
89 | }
90 | }
91 |
92 | let mut quotes = quote! {};
93 |
94 | for data_item in data_items {
95 | let ident = syn::Ident::new(&data_item.0.to_string(), proc_macro2::Span::call_site());
96 | let fspec_num = data_item.1.parse::().unwrap();
97 | let frn = data_item.2;
98 | let frn = syn::parse_str::(&frn).unwrap();
99 | quotes = quote! {
100 | #quotes
101 | if self.#ident.is_some() {
102 | fspec[#fspec_num] |= #frn;
103 | }
104 | }
105 | }
106 |
107 | let expanded = quote! {
108 | impl #name {
109 | pub fn update_fspec(&mut self) {
110 | let mut fspec = vec![0x00; 10];
111 | #quotes
112 | trim_fspec(&mut fspec);
113 | add_fx(&mut fspec);
114 | self.fspec = fspec;
115 | }
116 | }
117 | };
118 | // Hand the output tokens back to the compiler
119 | TokenStream::from(expanded) // also could be 'expanded.into()'
120 | }
121 |
--------------------------------------------------------------------------------
/src/custom_read_write.rs:
--------------------------------------------------------------------------------
1 | use deku::bitvec::{BitSlice, BitVec, Msb0};
2 | /// Several helpers for deku reading of certain types into certain types
3 | use deku::prelude::*;
4 |
5 | #[allow(dead_code)]
6 | pub(crate) enum Op {
7 | Multiply,
8 | Divide,
9 | Add,
10 | Subtract,
11 | }
12 |
13 | impl Op {
14 | pub(crate) fn calculate(&self, value: f32, modifier: f32) -> f32 {
15 | match self {
16 | Op::Multiply => value * modifier,
17 | Op::Divide => value / modifier,
18 | Op::Add => value + modifier,
19 | Op::Subtract => value - modifier,
20 | }
21 | }
22 | }
23 |
24 | pub(crate) mod read {
25 | use super::*;
26 |
27 | /// Read in big-endian bits to u32, multiply by f32, return f32
28 | pub(crate) fn bits_to_f32(
29 | rest: &BitSlice,
30 | bits: usize,
31 | modifier: f32,
32 | modifier_op: Op,
33 | ) -> Result<(&BitSlice, f32), DekuError> {
34 | let (rest, value) = u32::read(rest, (deku::ctx::Endian::Big, deku::ctx::BitSize(bits)))?;
35 | Ok(op(rest, value as f32, modifier, modifier_op))
36 | }
37 |
38 | /// Read in big-endian bits to i16, multiply by f32, return f32
39 | pub(crate) fn bits_i16_to_f32(
40 | rest: &BitSlice,
41 | bits: usize,
42 | modifier: f32,
43 | modifier_op: Op,
44 | ) -> Result<(&BitSlice, f32), DekuError> {
45 | let (rest, value) = i16::read(rest, (deku::ctx::Endian::Big, deku::ctx::BitSize(bits)))?;
46 | Ok(op(rest, f32::from(value), modifier, modifier_op))
47 | }
48 |
49 | pub(crate) fn op(
50 | rest: &BitSlice,
51 | value: f32,
52 | modifier: f32,
53 | modifier_op: Op,
54 | ) -> (&BitSlice, f32) {
55 | (rest, modifier_op.calculate(value, modifier))
56 | }
57 |
58 | /// Read in big-endian bits, multiply by f32, return Some(f32)
59 | pub(crate) fn bits_to_optionf32(
60 | rest: &BitSlice,
61 | bits: usize,
62 | modifier: f32,
63 | modifier_op: Op,
64 | ) -> Result<(&BitSlice, Option), DekuError> {
65 | bits_to_f32(rest, bits, modifier, modifier_op).map(|(rest, f)| (rest, Some(f)))
66 | }
67 | }
68 |
69 | pub mod write {
70 | use super::*;
71 |
72 | pub(crate) fn f32_u32(
73 | value: &f32,
74 | bits: usize,
75 | modifier: f32,
76 | modifier_op: Op,
77 | output: &mut BitVec,
78 | ) -> Result<(), DekuError> {
79 | // TODO this should be function for this and the other one
80 | let value = modifier_op.calculate(*value, modifier);
81 | (value as u32).write(output, (deku::ctx::Endian::Big, deku::ctx::BitSize(bits)))
82 | }
83 |
84 | pub(crate) fn f32_optionu32(
85 | value: &Option,
86 | bits: usize,
87 | modifier: f32,
88 | modifier_op: Op,
89 | output: &mut BitVec,
90 | ) -> Result<(), DekuError> {
91 | value.map_or(Ok(()), |value| f32_u32(&value, bits, modifier, modifier_op, output))
92 | }
93 |
94 | pub(crate) fn f32_i32(
95 | value: &f32,
96 | bits: usize,
97 | modifier: f32,
98 | modifier_op: Op,
99 | output: &mut BitVec,
100 | ) -> Result<(), DekuError> {
101 | let value = modifier_op.calculate(*value, modifier);
102 | (value as i32).write(output, (deku::ctx::Endian::Big, deku::ctx::BitSize(bits)))
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/data_item.rs:
--------------------------------------------------------------------------------
1 | //! Defined Data Items that are used for formal parsing of data structs in categories
2 |
3 | use crate::custom_read_write::{read, write, Op};
4 | use crate::fspec::is_fspec;
5 | use crate::modifier;
6 | use crate::types::{
7 | DataFilterTYP, MessageCounterTYP, AIC, ANT, ARC, CDM, CHAB, CLU, CNF, CODE, COM, D, DLF, DOU,
8 | ERR, FOEFRI, FX, G, GHO, L, MAH, ME, MI, MSC, MSSC, MTYPE, NOGO, OVL, POL, RAB, RAD, RDP, RDPC,
9 | RDPR, RED, SCF, SI, SIM, SPI, STAT, STC, SUP, TCC, TRE, TST, TSV, TYP, V, XPP,
10 | };
11 | use deku::bitvec::{BitSlice, BitVec, Msb0};
12 | use deku::prelude::*;
13 |
14 | const RHO_MODIFIER: f32 = 1.0 / 256.0;
15 | const THETA_MODIFIER: f32 = 360.0 / 65536.0;
16 |
17 | /// Identification of the radar station from which the data is received
18 | ///
19 | /// Data Item I048/010
20 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
21 | #[deku(ctx = "_: deku::ctx::Endian")]
22 | pub struct DataSourceIdentifier {
23 | /// System Area Code
24 | pub sac: u8,
25 | /// System Identification Code
26 | pub sic: u8,
27 | }
28 |
29 | impl DataSourceIdentifier {
30 | pub const FRN_34: u8 = 0b1000_0000;
31 | pub const FRN_48: u8 = 0b1000_0000;
32 | }
33 |
34 | /// Absolute time stamping expressed as Co-ordinated Universal Time (UTC)
35 | ///
36 | /// Data Item I048/140
37 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
38 | #[deku(ctx = "_: deku::ctx::Endian")]
39 | pub struct TimeOfDay {
40 | #[deku(
41 | reader = "read::bits_to_f32(deku::rest, 24, Self::MODIFIER, Op::Divide)",
42 | writer = "write::f32_u32(&self.time, 24, Self::MODIFIER, Op::Multiply, deku::output)"
43 | )]
44 | pub time: f32,
45 | }
46 |
47 | impl TimeOfDay {
48 | pub const FRN_34: u8 = 0b10_0000;
49 | pub const FRN_48: u8 = 0b100_0000;
50 | const MODIFIER: f32 = 128.0;
51 | }
52 |
53 | /// Type and properties of the target report
54 | ///
55 | /// Data Item I048/020
56 | ///
57 | /// TODO: This can extend with FX bit.
58 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
59 | #[deku(ctx = "_: deku::ctx::Endian")]
60 | pub struct TargetReportDescriptor {
61 | pub typ: TYP,
62 | pub sim: SIM,
63 | pub rdp: RDP,
64 | pub spi: SPI,
65 | pub rab: RAB,
66 | pub fx1: FX,
67 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent")]
68 | pub tst: Option,
69 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent")]
70 | pub err: Option,
71 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent")]
72 | pub xpp: Option,
73 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent")]
74 | pub me: Option,
75 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent")]
76 | pub mi: Option,
77 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent")]
78 | pub foe_fri: Option,
79 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent")]
80 | pub fx2: Option,
81 | }
82 |
83 | impl TargetReportDescriptor {
84 | pub const FRN_48: u8 = 0b10_0000;
85 | }
86 |
87 | /// Measured position of an aircraft in local polar co-ordinates
88 | ///
89 | /// Data Item I048/040
90 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
91 | #[deku(ctx = "_: deku::ctx::Endian")]
92 | pub struct MeasuredPositionInPolarCoordinates {
93 | #[deku(
94 | reader = "read::bits_to_f32(deku::rest, 16, RHO_MODIFIER, Op::Multiply)",
95 | writer = "write::f32_u32(&self.rho, 16, RHO_MODIFIER, Op::Divide, deku::output)"
96 | )]
97 | pub rho: f32,
98 | #[deku(
99 | reader = "read::bits_to_f32(deku::rest, 16, THETA_MODIFIER, Op::Multiply)",
100 | writer = "write::f32_u32(&self.theta, 16, THETA_MODIFIER, Op::Divide, deku::output)"
101 | )]
102 | pub theta: f32,
103 | }
104 |
105 | impl MeasuredPositionInPolarCoordinates {
106 | pub const FRN_48: u8 = 0b1_0000;
107 | }
108 |
109 | /// Mode-3/A code converted into octal representation
110 | ///
111 | /// Data Item I048/070
112 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
113 | #[deku(ctx = "_: deku::ctx::Endian")]
114 | pub struct Mode3ACodeInOctalRepresentation {
115 | pub v: V,
116 | pub g: G,
117 | pub l: L,
118 | #[deku(bits = "1")]
119 | pub reserved: u8,
120 | /// Mode-3/A reply in octal representation
121 | #[deku(bits = "12", endian = "big")]
122 | pub reply: u16,
123 | }
124 |
125 | impl Mode3ACodeInOctalRepresentation {
126 | pub const FRN_48: u8 = 0b1000;
127 | }
128 |
129 | /// Flight Level converted into binary representation
130 | ///
131 | /// Data Item I048/090
132 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
133 | #[deku(ctx = "_: deku::ctx::Endian")]
134 | pub struct FlightLevelInBinaryRepresentation {
135 | pub v: V,
136 | pub g: G,
137 | #[deku(
138 | reader = "Self::read(deku::rest)",
139 | writer = "Self::write(&self.flight_level, deku::output)"
140 | )]
141 | pub flight_level: u16,
142 | }
143 |
144 | impl FlightLevelInBinaryRepresentation {
145 | pub const FRN_48: u8 = 0b100;
146 | const CTX: (deku::ctx::Endian, deku::ctx::BitSize) =
147 | (deku::ctx::Endian::Big, deku::ctx::BitSize(14_usize));
148 |
149 | fn read(rest: &BitSlice) -> Result<(&BitSlice, u16), DekuError> {
150 | u16::read(rest, Self::CTX).map(|(rest, value)| (rest, value / 4))
151 | }
152 |
153 | fn write(flight_level: &u16, output: &mut BitVec) -> Result<(), DekuError> {
154 | let value = *flight_level * 4;
155 | value.write(output, Self::CTX)
156 | }
157 | }
158 |
159 | /// Aircraft address (24-bits Mode S address) assigned uniquely to
160 | /// each aircraft
161 | ///
162 | /// Data Item I048/220
163 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
164 | #[deku(ctx = "_: deku::ctx::Endian")]
165 | pub struct AircraftAddress {
166 | #[deku(bytes = "3", endian = "big")]
167 | pub address: u32,
168 | }
169 |
170 | impl AircraftAddress {
171 | pub const FRN_48: u8 = 0b1000_0000;
172 | }
173 |
174 | /// Aircraft identification (in 8 characters) obtained from an aircraft
175 | /// equipped with a Mode S transponder
176 | ///
177 | /// Data Item I048/240
178 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
179 | #[deku(ctx = "_: deku::ctx::Endian")]
180 | pub struct AircraftIdentification {
181 | /// IA5 char array
182 | #[deku(
183 | reader = "Self::read(deku::rest)",
184 | writer = "Self::write(&self.identification, deku::output)"
185 | )]
186 | pub identification: String,
187 | }
188 |
189 | impl AircraftIdentification {
190 | pub const FRN_48: u8 = 0b100_0000;
191 | /// Read and convert to String
192 | fn read(rest: &BitSlice) -> Result<(&BitSlice, String), DekuError> {
193 | let (rest, one) = u8::read(rest, (deku::ctx::Endian::Big, deku::ctx::BitSize(6_usize)))?;
194 | let (rest, two) = u8::read(rest, (deku::ctx::Endian::Big, deku::ctx::BitSize(6_usize)))?;
195 | let (rest, three) = u8::read(rest, (deku::ctx::Endian::Big, deku::ctx::BitSize(6_usize)))?;
196 | let (rest, four) = u8::read(rest, (deku::ctx::Endian::Big, deku::ctx::BitSize(6_usize)))?;
197 | let (rest, five) = u8::read(rest, (deku::ctx::Endian::Big, deku::ctx::BitSize(6_usize)))?;
198 | let (rest, six) = u8::read(rest, (deku::ctx::Endian::Big, deku::ctx::BitSize(6_usize)))?;
199 | let (rest, seven) = u8::read(rest, (deku::ctx::Endian::Big, deku::ctx::BitSize(6_usize)))?;
200 | let (rest, _) = u8::read(rest, (deku::ctx::Endian::Big, deku::ctx::BitSize(6_usize)))?;
201 | let value = format!(
202 | "{}{}{}{}{}{}{}",
203 | Self::asterix_char_to_ascii(one) as char,
204 | Self::asterix_char_to_ascii(two) as char,
205 | Self::asterix_char_to_ascii(three) as char,
206 | Self::asterix_char_to_ascii(four) as char,
207 | Self::asterix_char_to_ascii(five) as char,
208 | Self::asterix_char_to_ascii(six) as char,
209 | Self::asterix_char_to_ascii(seven) as char
210 | );
211 | Ok((rest, value))
212 | }
213 |
214 | /// Parse from String to u8 and write
215 | fn write(field_a: &str, output: &mut BitVec) -> Result<(), DekuError> {
216 | for c in field_a.chars() {
217 | Self::asterix_ascii_to_ia5_char(c as u8)
218 | .write(output, (deku::ctx::Endian::Big, deku::ctx::BitSize(6_usize)))?;
219 | }
220 | 0_u8.write(output, (deku::ctx::Endian::Big, deku::ctx::BitSize(6_usize)))
221 | }
222 |
223 | const IA5_ALPHA: u8 = 0x01;
224 | const IA5_SPACE: u8 = 0x20;
225 | const IA5_DIGIT: u8 = 0x30;
226 | const ASC_DIGIT: u8 = b'0';
227 | const ASC_ALPHA: u8 = b'A';
228 | const ASC_SPACE: u8 = b' ';
229 | const ASC_ERROR: u8 = b'?';
230 |
231 | /// parse into ascii from IA5 char array
232 | const fn asterix_char_to_ascii(code: u8) -> u8 {
233 | // space
234 | if code == Self::IA5_SPACE {
235 | Self::ASC_SPACE
236 | }
237 | // digit
238 | else if Self::IA5_DIGIT <= code && code < Self::IA5_DIGIT + 10 {
239 | Self::ASC_DIGIT + (code - Self::IA5_DIGIT)
240 | }
241 | // letter
242 | else if Self::IA5_ALPHA <= code && code < Self::IA5_ALPHA + 26 {
243 | Self::ASC_ALPHA + (code - Self::IA5_ALPHA)
244 | } else {
245 | Self::ASC_ERROR
246 | }
247 | }
248 |
249 | /// parse from IA5 char as u8 to u8 value
250 | const fn asterix_ascii_to_ia5_char(code: u8) -> u8 {
251 | // space
252 | if code == Self::ASC_SPACE {
253 | Self::IA5_SPACE
254 | }
255 | // digit
256 | else if Self::ASC_DIGIT <= code && code < Self::ASC_DIGIT + 10 {
257 | Self::IA5_DIGIT + (code - Self::ASC_DIGIT)
258 | }
259 | // letter
260 | else if Self::ASC_ALPHA <= code && code < Self::ASC_ALPHA + 26 {
261 | Self::IA5_ALPHA + (code - Self::ASC_ALPHA)
262 | } else {
263 | Self::ASC_ERROR
264 | }
265 | }
266 | }
267 |
268 | /// Mode S Comm B data as extracted from the aircraft
269 | /// transponder
270 | ///
271 | /// Data Item I048/250, Mode S MB Data
272 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
273 | #[deku(ctx = "_: deku::ctx::Endian")]
274 | pub struct ModeSMBData {
275 | #[deku(update = "self.mb_data.len()")]
276 | pub count: u8,
277 | #[deku(count = "count")]
278 | pub mb_data: Vec,
279 | #[deku(bits = "4")]
280 | pub bds1: u8,
281 | #[deku(bits = "4")]
282 | pub bds2: u8,
283 | }
284 |
285 | impl ModeSMBData {
286 | pub const FRN_48: u8 = 0b10_0000;
287 | }
288 |
289 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
290 | pub struct MBData {
291 | #[deku(count = "7")]
292 | pub data: Vec,
293 | }
294 |
295 | /// An integer value representing a unique reference to a track
296 | /// record within a particular track file
297 | ///
298 | /// Data Item I048/161
299 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
300 | #[deku(ctx = "_: deku::ctx::Endian")]
301 | pub struct TrackNumber {
302 | #[deku(bits = "4")]
303 | pub reserved: u8,
304 | #[deku(bits = "12", endian = "big")]
305 | pub number: u16,
306 | }
307 |
308 | impl TrackNumber {
309 | pub const FRN_48: u8 = 0b1_0000;
310 | }
311 |
312 | /// Calculated position of an aircraft in Cartesian co-ordinates
313 | ///
314 | /// Data Item I048/042
315 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
316 | #[deku(ctx = "_: deku::ctx::Endian")]
317 | pub struct CalculatedPositionCartesianCorr {
318 | #[deku(
319 | reader = "read::bits_i16_to_f32(deku::rest, 16, Self::MODIFIER, Op::Multiply)",
320 | writer = "write::f32_i32(&self.x, 16, Self::MODIFIER, Op::Divide, deku::output)"
321 | )]
322 | pub x: f32,
323 | #[deku(
324 | reader = "read::bits_i16_to_f32(deku::rest, 16, Self::MODIFIER, Op::Multiply)",
325 | writer = "write::f32_i32(&self.y, 16, Self::MODIFIER, Op::Divide, deku::output)"
326 | )]
327 | pub y: f32,
328 | }
329 |
330 | impl CalculatedPositionCartesianCorr {
331 | pub const FRN_48: u8 = 0b1000;
332 | const MODIFIER: f32 = 1.0 / 128.0;
333 | }
334 |
335 | /// Calculated track velocity expressed in polar co-ordinates
336 | ///
337 | /// Data Item I048/200
338 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
339 | #[deku(ctx = "_: deku::ctx::Endian")]
340 | pub struct CalculatedTrackVelocity {
341 | #[deku(
342 | reader = "read::bits_to_f32(deku::rest, 16, modifier::groundspeed(), Op::Multiply)",
343 | writer = "write::f32_u32(&self.groundspeed, 16, modifier::groundspeed(), Op::Divide, deku::output)"
344 | )]
345 | pub groundspeed: f32,
346 | #[deku(
347 | reader = "read::bits_to_f32(deku::rest, 16, modifier::heading1(), Op::Multiply)",
348 | writer = "write::f32_u32(&self.heading, 16, modifier::heading1(), Op::Divide, deku::output)"
349 | )]
350 | pub heading: f32,
351 | }
352 |
353 | impl CalculatedTrackVelocity {
354 | pub const FRN_48: u8 = 0b100;
355 | }
356 |
357 | /// Status of monoradar track (PSR and/or SSR updated)
358 | ///
359 | /// Data Item I048/170
360 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
361 | #[deku(ctx = "_: deku::ctx::Endian")]
362 | pub struct TrackStatus {
363 | pub cnf: CNF,
364 | pub rad: RAD,
365 | pub dou: DOU,
366 | pub mah: MAH,
367 | pub cdm: CDM,
368 | pub fx1: FX,
369 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent")]
370 | pub tre: Option,
371 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent")]
372 | pub gho: Option,
373 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent")]
374 | pub sup: Option,
375 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent")]
376 | pub tcc: Option,
377 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent", bits = "3")]
378 | pub reserved: Option,
379 | #[deku(skip, cond = "*fx1 != FX::ExtensionIntoFirstExtent")]
380 | pub fx2: Option,
381 | }
382 |
383 | impl TrackStatus {
384 | pub const FRN_48: u8 = 0b10;
385 | }
386 |
387 | /// Track quality in the form of a vector of standard deviations
388 | ///
389 | /// Data Item I048/210
390 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
391 | #[deku(ctx = "_: deku::ctx::Endian")]
392 | pub struct TrackQuality {
393 | #[deku(
394 | reader = "read::bits_to_f32(deku::rest, 8, Self::MODIFIER, Op::Divide)",
395 | writer = "write::f32_u32(&self.horizontal_stddev, 8, Self::MODIFIER, Op::Multiply, deku::output)"
396 | )]
397 | pub horizontal_stddev: f32,
398 | #[deku(
399 | reader = "read::bits_to_f32(deku::rest, 8, Self::MODIFIER, Op::Divide)",
400 | writer = "write::f32_u32(&self.vertical_stddev, 8, Self::MODIFIER, Op::Multiply, deku::output)"
401 | )]
402 | pub vertical_stddev: f32,
403 | #[deku(
404 | reader = "read::bits_to_f32(deku::rest, 8, modifier::groundspeed(), Op::Multiply)",
405 | writer = "write::f32_u32(&self.groundspeed_stddev, 8, modifier::groundspeed(), Op::Divide, deku::output)"
406 | )]
407 | pub groundspeed_stddev: f32,
408 | #[deku(
409 | reader = "read::bits_to_f32(deku::rest, 8, modifier::heading2(), Op::Multiply)",
410 | writer = "write::f32_u32(&self.heading_stddev, 8, modifier::heading2(), Op::Divide, deku::output)"
411 | )]
412 | pub heading_stddev: f32,
413 | }
414 |
415 | impl TrackQuality {
416 | pub const FRN_48: u8 = 0b1000_0000;
417 | const MODIFIER: f32 = 1.0 / 128.0;
418 | }
419 |
420 | /// Communications capability of the transponder, capability of the onboard ACAS equipment and
421 | /// flight status
422 | ///
423 | /// Data Item I048/230
424 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
425 | #[deku(ctx = "_: deku::ctx::Endian")]
426 | pub struct CommunicationsCapabilityFlightStatus {
427 | pub com: COM,
428 | pub stat: STAT,
429 | pub si: SI,
430 | #[deku(bits = "1")]
431 | pub reserved: u8,
432 | pub mssc: MSSC,
433 | pub arc: ARC,
434 | pub aic: AIC,
435 | #[deku(bits = "1")]
436 | pub b1a: u8,
437 | #[deku(bits = "4")]
438 | pub b1b: u8,
439 | }
440 |
441 | impl CommunicationsCapabilityFlightStatus {
442 | pub const FRN_48: u8 = 0b10;
443 | }
444 |
445 | /// Additional information on the quality of the target report
446 | ///
447 | /// Data Item I048/130
448 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
449 | #[deku(ctx = "_: deku::ctx::Endian")]
450 | pub struct RadarPlotCharacteristics {
451 | #[deku(until = "|b: &u8| *b & 0b0000_0001 == 0")]
452 | pub fspec: Vec,
453 | #[deku(
454 | skip,
455 | cond = "is_fspec(0b1000_0000, fspec, 0)",
456 | reader = "read::bits_to_optionf32(deku::rest, 8, Self::runlength_modifier(), Op::Multiply)",
457 | writer = "write::f32_optionu32(&self.srl, 8, Self::runlength_modifier(), Op::Divide, deku::output)"
458 | )]
459 | pub srl: Option,
460 | #[deku(skip, cond = "is_fspec(0b100_0000, fspec, 0)")]
461 | pub srr: Option,
462 | #[deku(skip, cond = "is_fspec(0b10_0000, fspec, 0)")]
463 | pub sam: Option,
464 | #[deku(
465 | skip,
466 | cond = "is_fspec(0b1_0000, fspec, 0)",
467 | reader = "read::bits_to_optionf32(deku::rest, 8, Self::runlength_modifier(), Op::Multiply)",
468 | writer = "write::f32_optionu32(&self.prl, 8, Self::runlength_modifier(), Op::Divide, deku::output)"
469 | )]
470 | pub prl: Option,
471 | #[deku(skip, cond = "is_fspec(0b1000, fspec, 0)")]
472 | pub pam: Option,
473 | #[deku(
474 | skip,
475 | cond = "is_fspec(0b100, fspec, 0)",
476 | reader = "read::bits_to_optionf32(deku::rest, 8, Self::nm_modifier(), Op::Multiply)",
477 | writer = "write::f32_optionu32(&self.rpd, 8, Self::nm_modifier(), Op::Divide, deku::output)"
478 | )]
479 | pub rpd: Option,
480 | #[deku(
481 | skip,
482 | cond = "is_fspec(0b100, fspec, 0)",
483 | reader = "read::bits_to_optionf32(deku::rest, 8, Self::apd_modifier(), Op::Multiply)",
484 | writer = "write::f32_optionu32(&self.apd, 8, Self::apd_modifier(), Op::Divide, deku::output)"
485 | )]
486 | pub apd: Option,
487 | }
488 |
489 | impl RadarPlotCharacteristics {
490 | pub const FRN_48: u8 = 0b10;
491 |
492 | fn runlength_modifier() -> f32 {
493 | 360.0 / f32::from(2_u16.pow(13))
494 | }
495 |
496 | fn nm_modifier() -> f32 {
497 | 1.0 / 256.0
498 | }
499 |
500 | fn apd_modifier() -> f32 {
501 | 360.0 / f32::from(2_u16.pow(14))
502 | }
503 | }
504 |
505 | /// This Data Item allows for a more convenient handling of the
506 | /// messages at the receiver side by further defining the type of
507 | /// transaction
508 | ///
509 | /// Data Item I034/000
510 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
511 | #[deku(ctx = "_: deku::ctx::Endian")]
512 | pub struct MessageType {
513 | pub t: MTYPE,
514 | }
515 |
516 | impl MessageType {
517 | pub const FRN_34: u8 = 0b100_0000;
518 | }
519 |
520 | /// Eight most significant bits of the antenna azimuth defining a
521 | /// particular azimuth sector
522 | ///
523 | /// Data Item I034/020
524 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
525 | #[deku(ctx = "_: deku::ctx::Endian")]
526 | pub struct SectorNumber {
527 | #[deku(reader = "Self::read(deku::rest)", writer = "Self::write(&self.num, deku::output)")]
528 | pub num: u16,
529 | }
530 |
531 | impl SectorNumber {
532 | pub const FRN_34: u8 = 0b1_0000;
533 | pub const FRN_48: u8 = 0b1_0000;
534 | const CTX: (deku::ctx::Endian, deku::ctx::BitSize) =
535 | (deku::ctx::Endian::Big, deku::ctx::BitSize(8_usize));
536 |
537 | fn modifier() -> f32 {
538 | 360.0 / 2_f32.powi(8)
539 | }
540 |
541 | fn read(rest: &BitSlice) -> Result<(&BitSlice, u16), DekuError> {
542 | u16::read(rest, Self::CTX)
543 | .map(|(rest, value)| (rest, (f32::from(value) * Self::modifier()) as u16))
544 | }
545 |
546 | fn write(num: &u16, output: &mut BitVec) -> Result<(), DekuError> {
547 | let value = (f32::from(*num) / Self::modifier()) as u8;
548 | value.write(output, Self::CTX)
549 | }
550 | }
551 |
552 | /// Warning/error conditions detected by a radar station for the target
553 | /// report involved. Target Classification information for the target
554 | /// involved
555 | ///
556 | /// Data Item I048/030
557 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
558 | #[deku(ctx = "_: deku::ctx::Endian")]
559 | pub struct WarningErrorConditionsTargetClass {
560 | #[deku(until = "|codefx: &CodeFx| codefx.fx == FX::EndOfDataItem")]
561 | pub codefxs: Vec,
562 | }
563 |
564 | impl WarningErrorConditionsTargetClass {
565 | pub const FRN_48: u8 = 0b100_0000;
566 | }
567 |
568 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
569 | pub struct CodeFx {
570 | pub code: CODE,
571 | pub fx: FX,
572 | }
573 |
574 | /// Confidence level for each bit of a Mode-3/A reply as provided
575 | /// by a monopulse SSR station
576 | ///
577 | /// Data Item I048/080
578 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
579 | #[deku(ctx = "_: deku::ctx::Endian")]
580 | pub struct Mode3ACodeConfidenceIndicator {
581 | #[deku(bits = "4", endian = "big")]
582 | pub reserved: u8,
583 | #[deku(bits = "12", endian = "big")]
584 | pub confidence: u16,
585 | }
586 |
587 | impl Mode3ACodeConfidenceIndicator {
588 | pub const FRN_48: u8 = 0b10_0000;
589 | }
590 |
591 | /// Mode-C height in Gray notation as received from the
592 | /// transponder together with the confidence level for each reply bit
593 | /// as provided by a MSSR/Mode S station
594 | ///
595 | /// Data Item I048/100
596 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
597 | #[deku(ctx = "_: deku::ctx::Endian")]
598 | pub struct ModeCCodeAndConfidenceIndicator {
599 | pub v: V,
600 | pub g: G,
601 | #[deku(bits = "2", endian = "big")]
602 | pub reserved0: u8,
603 | #[deku(bits = "12", endian = "big")]
604 | pub mode_c_gray_notation: u16,
605 | #[deku(bits = "4", endian = "big")]
606 | pub reserved1: u8,
607 | #[deku(bits = "12", endian = "big")]
608 | pub confidence: u16,
609 | }
610 |
611 | impl ModeCCodeAndConfidenceIndicator {
612 | pub const FRN_48: u8 = 0b1_0000;
613 | }
614 |
615 | /// Height of a target as measured by a 3D radar. The height shall
616 | /// use mean sea level as the zero reference level
617 | ///
618 | /// Data Item I048/110
619 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
620 | #[deku(ctx = "_: deku::ctx::Endian")]
621 | pub struct HeightMeasuredBy3dRadar {
622 | #[deku(bits = "2", endian = "big")]
623 | pub reserved: u8,
624 | #[deku(reader = "Self::read(deku::rest)", writer = "Self::write(&self.height, deku::output)")]
625 | pub height: i32,
626 | }
627 |
628 | impl HeightMeasuredBy3dRadar {
629 | pub const FRN_48: u8 = 0b1000;
630 | const CTX: (deku::ctx::Endian, deku::ctx::BitSize) =
631 | (deku::ctx::Endian::Big, deku::ctx::BitSize(14_usize));
632 | pub const MODIFIER: i32 = 25;
633 |
634 | fn read(rest: &BitSlice) -> Result<(&BitSlice, i32), DekuError> {
635 | i32::read(rest, Self::CTX).map(|(rest, value)| (rest, value * Self::MODIFIER))
636 | }
637 |
638 | fn write(height: &i32, output: &mut BitVec) -> Result<(), DekuError> {
639 | let value = height / Self::MODIFIER;
640 | value.write(output, Self::CTX)
641 | }
642 | }
643 |
644 | /// Information on the Doppler Speed of the target report
645 | ///
646 | /// Data Item I048/120
647 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
648 | #[deku(ctx = "_: deku::ctx::Endian")]
649 | pub struct RadialDopplerSpeed {
650 | #[deku(bits = "1", endian = "big")]
651 | pub cal: u8,
652 | #[deku(bits = "1", endian = "big")]
653 | pub rds: u8,
654 | #[deku(bits = "6", endian = "big")]
655 | pub spare: u8,
656 | #[deku(skip, endian = "big", cond = "*cal == 0")]
657 | pub calculated_doppler_speed: Option,
658 | #[deku(skip, endian = "big", cond = "*rds == 0")]
659 | pub raw_doppler_speed: Option,
660 | }
661 |
662 | impl RadialDopplerSpeed {
663 | pub const FRN_48: u8 = 0b100;
664 | }
665 |
666 | /// Subfield of `HeightMeasuredBy3dRadar`
667 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
668 | #[deku(ctx = "_: deku::ctx::Endian")]
669 | pub struct CalculatedDopplerSpeed {
670 | pub d: D,
671 | #[deku(bits = "5", endian = "big")]
672 | pub spare: u8,
673 | #[deku(bits = "10", endian = "big")]
674 | pub cal: u16,
675 | }
676 |
677 | /// Subfield of `HeightMeasuredBy3dRadar`
678 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
679 | #[deku(ctx = "_: deku::ctx::Endian")]
680 | pub struct RawDopplerSpeed {
681 | /// Repetition Factor
682 | #[deku(endian = "big")]
683 | pub rep: u8,
684 | /// Doppler Speed: 1 m/sec
685 | #[deku(endian = "big")]
686 | pub dop: u16,
687 | /// Ambiquity Range: 1 m/sec
688 | #[deku(endian = "big")]
689 | pub amb: u16,
690 | /// Transmitter Frequency: 1 Mhz
691 | #[deku(endian = "big")]
692 | pub frq: u16,
693 | }
694 |
695 | /// Currently active Resolution Advisory (RA), if any, generated by the
696 | /// ACAS associated with the transponder transmitting the report and
697 | /// threat identity data
698 | ///
699 | /// Data Item I048/260
700 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
701 | #[deku(ctx = "_: deku::ctx::Endian")]
702 | pub struct ACASResolutionAdvisoryReport {
703 | pub mb_data: [u8; 7],
704 | }
705 |
706 | impl ACASResolutionAdvisoryReport {
707 | pub const FRN_48: u8 = 0b1000_0000;
708 | }
709 |
710 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
711 | #[deku(ctx = "_: deku::ctx::Endian")]
712 | pub struct Mode1CodeOctalRepresentation {
713 | pub v: V,
714 | pub g: G,
715 | pub l: L,
716 | #[deku(bits = "5", endian = "big")]
717 | pub data: u8,
718 | }
719 |
720 | impl Mode1CodeOctalRepresentation {
721 | pub const FRN_48: u8 = 0b100_0000;
722 | }
723 |
724 | /// Reply to Mode-2 interrogation
725 | ///
726 | /// Data Item I048/050
727 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
728 | #[deku(ctx = "_: deku::ctx::Endian")]
729 | pub struct Mode2CodeOctalRepresentation {
730 | pub v: V,
731 | pub g: G,
732 | pub l: L,
733 | #[deku(bits = "1", endian = "big")]
734 | pub spare: u8,
735 | #[deku(bits = "12", endian = "big")]
736 | pub data: u16,
737 | }
738 |
739 | impl Mode2CodeOctalRepresentation {
740 | pub const FRN_48: u8 = 0b10_0000;
741 | }
742 |
743 | /// Confidence level for each bit of a Mode-1 reply as provided by
744 | /// a monopulse SSR station
745 | ///
746 | /// Data Item I048/065
747 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
748 | #[deku(ctx = "_: deku::ctx::Endian")]
749 | pub struct Mode1CodeConfidenceIndicator {
750 | #[deku(bits = "3", endian = "big")]
751 | pub spare: u8,
752 | #[deku(bits = "5", endian = "big")]
753 | pub data: u8,
754 | }
755 |
756 | impl Mode1CodeConfidenceIndicator {
757 | pub const FRN_48: u8 = 0b1_0000;
758 | }
759 |
760 | /// Confidence level for each bit of a Mode-2 reply as provided by
761 | /// a monopulse SSR station
762 | ///
763 | /// Data Item I048/060
764 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
765 | #[deku(ctx = "_: deku::ctx::Endian")]
766 | pub struct Mode2CodeConfidenceIndicator {
767 | #[deku(bits = "4", endian = "big")]
768 | pub spare: u8,
769 | #[deku(bits = "12", endian = "big")]
770 | pub data: u16,
771 | }
772 |
773 | impl Mode2CodeConfidenceIndicator {
774 | pub const FRN_48: u8 = 0b1000;
775 | }
776 |
777 | /// Antenna rotation period as measured between two consecutive
778 | /// North crossings or as averaged during a period of time
779 | ///
780 | /// Data Item I034/041
781 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
782 | #[deku(ctx = "_: deku::ctx::Endian")]
783 | pub struct AntennaRotationSpeed {
784 | #[deku(
785 | reader = "read::bits_to_f32(deku::rest, 16, Self::MODIFIER, Op::Divide)",
786 | writer = "write::f32_u32(&self.period, 16, Self::MODIFIER, Op::Multiply, deku::output)"
787 | )]
788 | pub period: f32,
789 | }
790 |
791 | impl AntennaRotationSpeed {
792 | pub const FRN_34: u8 = 0b1000;
793 | const MODIFIER: f32 = 128.0;
794 | }
795 |
796 | /// Information concerning the configuration and status of a System
797 | ///
798 | /// Data Item I034/050
799 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
800 | #[deku(ctx = "_: deku::ctx::Endian")]
801 | pub struct SystemConfigurationAndStatus {
802 | #[deku(bits = "1")]
803 | pub com_bit: u8,
804 | #[deku(bits = "2")]
805 | pub spare_bit0: u8,
806 | #[deku(bits = "1")]
807 | pub psr_bit: u8,
808 | #[deku(bits = "1")]
809 | pub ssr_bit: u8,
810 | #[deku(bits = "1")]
811 | pub mds_bit: u8,
812 | #[deku(bits = "1")]
813 | pub spare_bit1: u8,
814 | pub fx_bit: FX,
815 | #[deku(skip, cond = "*com_bit != 1")]
816 | pub com: Option,
817 | #[deku(skip, cond = "*psr_bit != 1")]
818 | pub psr: Option,
819 | #[deku(skip, cond = "*ssr_bit != 1")]
820 | pub ssr: Option,
821 | #[deku(skip, cond = "*mds_bit != 1")]
822 | pub mds: Option,
823 | }
824 |
825 | impl SystemConfigurationAndStatus {
826 | pub const FRN_34: u8 = 0b0100;
827 | }
828 |
829 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
830 | pub struct ComSubField {
831 | pub nogo: NOGO,
832 | pub rdpc: RDPC,
833 | pub rdpr: RDPR,
834 | pub ovl_rdp: OVL,
835 | pub olv_xmt: OVL,
836 | pub msc: MSC,
837 | pub tsv: TSV,
838 | #[deku(bits = "1")]
839 | pub spare: u8,
840 | }
841 |
842 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
843 | pub struct Sensor {
844 | pub ant: ANT,
845 | pub chab: CHAB,
846 | pub ovl: OVL,
847 | pub msc: MSC,
848 | #[deku(bits = "3")]
849 | pub spare: u8,
850 | }
851 |
852 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
853 | pub struct MdsSubField {
854 | pub ant: ANT,
855 | pub chab: CHAB,
856 | pub ovl_sur: OVL,
857 | pub msc: MSC,
858 | pub scf: SCF,
859 | pub dlf: DLF,
860 | pub ovl_scf: OVL,
861 | pub ovl_dlf: OVL,
862 | #[deku(bits = "7")]
863 | pub spare: u8,
864 | }
865 |
866 | /// Status concerning the processing options, in use during the last antenna revolution,
867 | /// for the various Sensors, composing the System
868 | ///
869 | /// Data Item I034/060
870 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
871 | #[deku(ctx = "_: deku::ctx::Endian")]
872 | pub struct SystemProcessingMode {
873 | #[deku(bits = "1")]
874 | pub com_bit: u8,
875 | #[deku(bits = "2")]
876 | pub spare_bit0: u8,
877 | #[deku(bits = "1")]
878 | pub psr_bit: u8,
879 | #[deku(bits = "1")]
880 | pub ssr_bit: u8,
881 | #[deku(bits = "1")]
882 | pub mds_bit: u8,
883 | #[deku(bits = "1")]
884 | pub spare_bit1: u8,
885 | pub fx_bit: FX,
886 | #[deku(skip, cond = "*com_bit != 1")]
887 | pub com: Option,
888 | #[deku(skip, cond = "*psr_bit != 1")]
889 | pub psr: Option,
890 | #[deku(skip, cond = "*ssr_bit != 1")]
891 | pub ssr: Option,
892 | #[deku(skip, cond = "*mds_bit != 1")]
893 | pub mds: Option,
894 | }
895 |
896 | impl SystemProcessingMode {
897 | pub const FRN_34: u8 = 0b0010;
898 | }
899 |
900 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
901 | pub struct ComSubField2 {
902 | #[deku(bits = "1")]
903 | pub spare0: u8,
904 | pub red_rdp: RED,
905 | pub red_xmt: RED,
906 | #[deku(bits = "1")]
907 | pub spare1: u8,
908 | }
909 |
910 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
911 | pub struct PsrSubField {
912 | pub pol: POL,
913 | pub red_rad: RED,
914 | pub stc: STC,
915 | #[deku(bits = "2")]
916 | pub spare: u8,
917 | }
918 |
919 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
920 | pub struct SsrSubField {
921 | pub red_rad: RED,
922 | #[deku(bits = "5")]
923 | pub spare: u8,
924 | }
925 |
926 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
927 | pub struct MdsSubField2 {
928 | pub red_rad: RED,
929 | pub clu: CLU,
930 | #[deku(bits = "4")]
931 | pub spare: u8,
932 | }
933 |
934 | /// Message Count values, according the various types of messages,
935 | /// for the last completed antenna revolution, counted between two
936 | /// North crossings
937 | ///
938 | /// Data Item I034/070
939 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
940 | #[deku(ctx = "_: deku::ctx::Endian")]
941 | pub struct MessageCountValues {
942 | #[deku(update = "self.counters.len()")]
943 | pub count: u8,
944 | #[deku(count = "count")]
945 | pub counters: Vec,
946 | }
947 |
948 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
949 | pub struct MessageCounter {
950 | pub typ: MessageCounterTYP,
951 | #[deku(bits = "11")]
952 | pub counter: u16,
953 | }
954 |
955 | impl MessageCountValues {
956 | pub const FRN_34: u8 = 0b1000_0000;
957 | }
958 |
959 | /// Geographical window defined in polar co-ordinates.
960 | ///
961 | /// Data Item I034/100
962 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
963 | #[deku(ctx = "_: deku::ctx::Endian")]
964 | pub struct GenericPolarWindow {
965 | #[deku(
966 | reader = "read::bits_to_f32(deku::rest, 16, RHO_MODIFIER, Op::Multiply)",
967 | writer = "write::f32_u32(&self.rho_start, 16, RHO_MODIFIER, Op::Divide, deku::output)"
968 | )]
969 | pub rho_start: f32,
970 | #[deku(
971 | reader = "read::bits_to_f32(deku::rest, 16, RHO_MODIFIER, Op::Multiply)",
972 | writer = "write::f32_u32(&self.rho_end, 16, RHO_MODIFIER, Op::Divide, deku::output)"
973 | )]
974 | pub rho_end: f32,
975 | #[deku(
976 | reader = "read::bits_to_f32(deku::rest, 16, THETA_MODIFIER, Op::Multiply)",
977 | writer = "write::f32_u32(&self.theta_start, 16, THETA_MODIFIER, Op::Divide, deku::output)"
978 | )]
979 | pub theta_start: f32,
980 | #[deku(
981 | reader = "read::bits_to_f32(deku::rest, 16, THETA_MODIFIER, Op::Multiply)",
982 | writer = "write::f32_u32(&self.theta_end, 16, THETA_MODIFIER, Op::Divide, deku::output)"
983 | )]
984 | pub theta_end: f32,
985 | }
986 |
987 | impl GenericPolarWindow {
988 | pub const FRN_34: u8 = 0b0100_0000;
989 | }
990 |
991 | /// Data Filter, which allows suppression of individual data types.
992 | ///
993 | /// Data Item I034/110
994 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
995 | #[deku(ctx = "_: deku::ctx::Endian")]
996 | pub struct DataFilter {
997 | pub typ: DataFilterTYP,
998 | }
999 |
1000 | impl DataFilter {
1001 | pub const FRN_34: u8 = 0b0010_0000;
1002 | }
1003 |
1004 | /// 3D-Position of Data Source in WGS 84 Co-ordinates
1005 | ///
1006 | /// Data Item I034/120
1007 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
1008 | #[deku(ctx = "_: deku::ctx::Endian")]
1009 | pub struct ThreeDPositionOfDataSource {
1010 | pub height_of_wgs_84: u16,
1011 | #[deku(
1012 | reader = "read::bits_to_f32(deku::rest, 24, Self::WGS_MODIFIER, Op::Multiply)",
1013 | writer = "write::f32_u32(&self.latitude_in_wgs_84, 24, Self::WGS_MODIFIER, Op::Divide, deku::output)"
1014 | )]
1015 | pub latitude_in_wgs_84: f32,
1016 | #[deku(
1017 | reader = "read::bits_to_f32(deku::rest, 24, Self::WGS_MODIFIER, Op::Multiply)",
1018 | writer = "write::f32_u32(&self.longitude_in_wgs_84, 24, Self::WGS_MODIFIER, Op::Divide, deku::output)"
1019 | )]
1020 | pub longitude_in_wgs_84: f32,
1021 | }
1022 |
1023 | impl ThreeDPositionOfDataSource {
1024 | pub const WGS_MODIFIER: f32 = 180.0 / 8_388_608.0;
1025 | pub const FRN_34: u8 = 0b0001_0000;
1026 | }
1027 |
1028 | /// Averaged difference in range and in azimuth for the primary target
1029 | /// position with respect to the SSR target position as calculated by
1030 | /// the radar station
1031 | ///
1032 | /// Data Item I034/090
1033 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
1034 | #[deku(ctx = "_: deku::ctx::Endian")]
1035 | pub struct CollimationError {
1036 | #[deku(
1037 | reader = "read::bits_to_f32(deku::rest, 8, Self::MODIFIER, Op::Multiply)",
1038 | writer = "write::f32_u32(&self.range_error, 8, Self::MODIFIER, Op::Divide, deku::output)"
1039 | )]
1040 | pub range_error: f32,
1041 | #[deku(
1042 | reader = "read::bits_to_f32(deku::rest, 8, Self::AZIMUTH_MODIFIER, Op::Multiply)",
1043 | writer = "write::f32_u32(&self.azimuth_error, 8, Self::AZIMUTH_MODIFIER, Op::Divide, deku::output)"
1044 | )]
1045 | pub azimuth_error: f32,
1046 | }
1047 |
1048 | impl CollimationError {
1049 | pub const MODIFIER: f32 = 1.0 / 128.0;
1050 | // TODO #![feature(const_int_pow)]
1051 | //pub const AZIMUTH_MODIFIER: f32 = 360.0 / f32::from(2_u16.pow(14));
1052 | pub const AZIMUTH_MODIFIER: f32 = 360.0 / 16384.0;
1053 | pub const FRN_34: u8 = 0b0000_1000;
1054 | }
1055 |
1056 | #[cfg(test)]
1057 | mod tests {
1058 | use super::*;
1059 | // tests from https://github.com/wireshark/wireshark/blob/master/test/suite_dissectors/group_asterix.py
1060 |
1061 | #[test]
1062 | fn tod_140() {
1063 | let mut input = BitSlice::from_slice(&[0xa8, 0xbf, 0xff]);
1064 | let item = TimeOfDay::read(&mut input, deku::ctx::Endian::Big).unwrap().1;
1065 | assert_eq!(item.time, 86_399.99);
1066 | }
1067 |
1068 | #[test]
1069 | fn target_report_descriptor_020() {
1070 | let mut input = BitSlice::from_slice(&[
1071 | 0xe0 | 0x08 | 0x04 | 0x02 | 0x01,
1072 | 0x80 | 0x40 | 0x20 | 0x10 | 0x08 | 0x06,
1073 | ]);
1074 | let item = TargetReportDescriptor::read(&mut input, deku::ctx::Endian::Big).unwrap().1;
1075 | assert_eq!(item.typ, TYP::ModeSRollCallPlusPSR);
1076 | assert_eq!(item.sim, SIM::ActualTargetReport);
1077 | assert_eq!(item.rdp, RDP::ReportFromRDPChain2);
1078 | assert_eq!(item.spi, SPI::SpecialPositionIdentification);
1079 | assert_eq!(item.rab, RAB::ReportFromFieldMonitor);
1080 | assert_eq!(item.fx1, FX::ExtensionIntoFirstExtent);
1081 | assert_eq!(item.tst, Some(TST::TestTargetReport));
1082 | assert_eq!(item.err, Some(ERR::ExtendedRangePresent));
1083 | assert_eq!(item.xpp, Some(XPP::XPulsePresent));
1084 | assert_eq!(item.me, Some(ME::MilitaryEmergency));
1085 | assert_eq!(item.mi, Some(MI::MilitaryIdentification));
1086 | assert_eq!(item.foe_fri, Some(FOEFRI::NoReply));
1087 | }
1088 | }
1089 |
--------------------------------------------------------------------------------
/src/fourty_eight.rs:
--------------------------------------------------------------------------------
1 | use crate::data_item::{
2 | ACASResolutionAdvisoryReport, AircraftAddress, AircraftIdentification,
3 | CalculatedPositionCartesianCorr, CalculatedTrackVelocity, CommunicationsCapabilityFlightStatus,
4 | DataSourceIdentifier, FlightLevelInBinaryRepresentation, HeightMeasuredBy3dRadar,
5 | MeasuredPositionInPolarCoordinates, Mode1CodeConfidenceIndicator, Mode1CodeOctalRepresentation,
6 | Mode2CodeConfidenceIndicator, Mode2CodeOctalRepresentation, Mode3ACodeConfidenceIndicator,
7 | Mode3ACodeInOctalRepresentation, ModeCCodeAndConfidenceIndicator, ModeSMBData,
8 | RadarPlotCharacteristics, RadialDopplerSpeed, TargetReportDescriptor, TimeOfDay, TrackNumber,
9 | TrackQuality, TrackStatus, WarningErrorConditionsTargetClass,
10 | };
11 | use crate::fspec::{add_fx, is_fspec, trim_fspec};
12 | use crate::FSPEC_IDENT;
13 | use asterix_derive::UpdateFspec;
14 | use deku::prelude::*;
15 |
16 | /// Transmission of Monoradar Target Reports
17 | #[derive(Debug, Default, PartialEq, DekuRead, DekuWrite, UpdateFspec)]
18 | #[deku(endian = "big")]
19 | pub struct Cat48 {
20 | #[deku(until = "|b: &u8| *b & FSPEC_IDENT == 0")]
21 | pub fspec: Vec,
22 | /// FRN 1
23 | #[deku(skip, cond = "is_fspec(DataSourceIdentifier::FRN_48, fspec, 0)")]
24 | pub data_source_identifier: Option,
25 | /// FRN 2
26 | #[deku(skip, cond = "is_fspec(TimeOfDay::FRN_48, fspec, 0)")]
27 | pub time_of_day: Option,
28 | /// FRN 3
29 | #[deku(skip, cond = "is_fspec(TargetReportDescriptor::FRN_48, fspec, 0)")]
30 | pub target_report_descriptor: Option,
31 | /// FRN 4
32 | #[deku(skip, cond = "is_fspec(MeasuredPositionInPolarCoordinates::FRN_48, fspec, 0)")]
33 | pub measured_position_in_polar_coordinates: Option,
34 | /// FRN 5
35 | #[deku(skip, cond = "is_fspec(Mode3ACodeInOctalRepresentation::FRN_48, fspec, 0)")]
36 | pub mode_3_a_code_in_octal_representation: Option,
37 | /// FRN 6
38 | #[deku(skip, cond = "is_fspec(FlightLevelInBinaryRepresentation::FRN_48, fspec, 0)")]
39 | pub flight_level_in_binary_repre: Option,
40 | /// FRN 7
41 | #[deku(skip, cond = "is_fspec(RadarPlotCharacteristics::FRN_48, fspec, 0)")]
42 | pub radar_plot_characteristics: Option,
43 | /// FRN 8
44 | #[deku(skip, cond = "is_fspec(AircraftAddress::FRN_48, fspec, 1)")]
45 | pub aircraft_address: Option,
46 | /// FRN 9
47 | #[deku(skip, cond = "is_fspec(AircraftIdentification::FRN_48, fspec, 1)")]
48 | pub aircraft_identification: Option,
49 | /// FRN 10
50 | #[deku(skip, cond = "is_fspec(ModeSMBData::FRN_48, fspec, 1)")]
51 | pub mode_smb_data: Option,
52 | /// FRN 11
53 | #[deku(skip, cond = "is_fspec(TrackNumber::FRN_48, fspec, 1)")]
54 | pub track_number: Option,
55 | /// FRN 12
56 | #[deku(skip, cond = "is_fspec(CalculatedPositionCartesianCorr::FRN_48, fspec, 1)")]
57 | pub calculated_position_cartesian_coor: Option,
58 | /// FRN 13
59 | #[deku(skip, cond = "is_fspec(CalculatedTrackVelocity::FRN_48, fspec, 1)")]
60 | pub calculated_track_velocity: Option,
61 | /// FRN 14
62 | #[deku(skip, cond = "is_fspec(TrackStatus::FRN_48, fspec, 1)")]
63 | pub track_status: Option,
64 | /// FRN 15
65 | #[deku(skip, cond = "is_fspec(TrackQuality::FRN_48, fspec, 2)")]
66 | pub track_quality: Option,
67 | /// FRN 16
68 | #[deku(skip, cond = "is_fspec(WarningErrorConditionsTargetClass::FRN_48, fspec, 2)")]
69 | pub warning_error_con_target_class: Option,
70 | /// FRN 17
71 | #[deku(skip, cond = "is_fspec(Mode3ACodeConfidenceIndicator::FRN_48, fspec, 2)")]
72 | pub mode3a_code_confidence_indicator: Option,
73 | /// FRN 18
74 | #[deku(skip, cond = "is_fspec(ModeCCodeAndConfidenceIndicator::FRN_48, fspec, 2)")]
75 | pub modec_code_and_confidence_indicator: Option,
76 | /// FRN 19
77 | #[deku(skip, cond = "is_fspec(HeightMeasuredBy3dRadar::FRN_48, fspec, 2)")]
78 | pub height_measured_by_3d_radar: Option,
79 | /// FRN 20
80 | #[deku(skip, cond = "is_fspec(RadialDopplerSpeed::FRN_48, fspec, 2)")]
81 | pub radial_doppler_speed: Option,
82 | /// FRN 21
83 | #[deku(skip, cond = "is_fspec(CommunicationsCapabilityFlightStatus::FRN_48, fspec, 2)")]
84 | pub communications_capability_flight_status: Option,
85 | /// FRN 22
86 | #[deku(skip, cond = "is_fspec(ACASResolutionAdvisoryReport::FRN_48, fspec, 3)")]
87 | pub acas_resolution_advisory_report: Option,
88 | /// FRN 23
89 | #[deku(skip, cond = "is_fspec(Mode1CodeOctalRepresentation::FRN_48, fspec, 3)")]
90 | pub mode_1_code_octal_representation: Option,
91 | /// FRN 24
92 | #[deku(skip, cond = "is_fspec(Mode2CodeOctalRepresentation::FRN_48, fspec, 3)")]
93 | pub mode_2_code_octal_representation: Option,
94 | /// FRN 25
95 | #[deku(skip, cond = "is_fspec(Mode1CodeConfidenceIndicator::FRN_48, fspec, 3)")]
96 | pub mode_1_code_confidence: Option,
97 | /// FRN 26
98 | #[deku(skip, cond = "is_fspec(Mode2CodeConfidenceIndicator::FRN_48, fspec, 3)")]
99 | pub mode_2_code_confidence: Option,
100 | // FRN 27: Special Purpose Field
101 | // FRN 28: Reserved Expansion Field
102 | }
103 |
--------------------------------------------------------------------------------
/src/fspec.rs:
--------------------------------------------------------------------------------
1 | use crate::FSPEC_IDENT;
2 |
3 | /// Usage in cond for checking if dataitem is to be read, by checking the fspec for the data item
4 | pub fn is_fspec(dataitem_fspec: u8, fspec: &[u8], pos: usize) -> bool {
5 | if pos < fspec.len() {
6 | dataitem_fspec & fspec[pos] != dataitem_fspec
7 | } else {
8 | true
9 | }
10 | }
11 |
12 | /// Remove trailing empty fspec entries
13 | pub fn trim_fspec(fspec: &mut Vec) {
14 | // - find last item in fspec that isn't 00...
15 | let mut remove_indicies = vec![];
16 | for (n, f) in fspec.iter().rev().enumerate() {
17 | if *f != 0x00 {
18 | break;
19 | }
20 | remove_indicies.push(fspec.len() - n);
21 | }
22 | for i in &remove_indicies {
23 | fspec.remove(*i - 1);
24 | }
25 | }
26 |
27 | /// Add FX bits
28 | pub fn add_fx(fspec: &mut [u8]) {
29 | let fspec_len = fspec.len();
30 | for f in fspec.iter_mut().take(fspec_len - 1) {
31 | *f |= FSPEC_IDENT
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | //! Encode/Decode for ASTERIX protocol using the deku library
2 | //!
3 | //! # Creating an Asterix packet
4 | //! There are currently two ways of creating an AsterixPacket:
5 | //!
6 | //! ## From `&[u8]`
7 | //! ```
8 | //! use deku::prelude::*;
9 | //! use asterix::*;
10 | //! use asterix::data_item::*;
11 | //!
12 | //! let bytes = &[0x22, 0x00, 0x0b, 0xf0, 0x19, 0x0d, 0x02, 0x35, 0x6d, 0xfa, 0x60];
13 | //! let (_, mut packet) = AsterixPacket::from_bytes((bytes, 0)).unwrap();
14 | //! ```
15 | //!
16 | //! ## Packet Creation
17 | //! Create an CAT34 Asterix packet.
18 | //!
19 | //! ```rust
20 | //! use deku::prelude::*;
21 | //! use asterix::*;
22 | //! use asterix::data_item::*;
23 | //! use asterix::types::*;
24 | //!
25 | //! let mut thirty_eight = Cat34::default();
26 | //! thirty_eight.data_source_identifier = Some(DataSourceIdentifier { sac: 25, sic: 13 });
27 | //! thirty_eight.message_type = Some(MessageType {
28 | //! t: MTYPE::SectorCrossing,
29 | //! });
30 | //! thirty_eight.time_of_day = Some(TimeOfDay { time: 27355.953 });
31 | //! thirty_eight.sector_number = Some(SectorNumber { num: 135 });
32 | //!
33 | //! let mut packet = AsterixPacket::default();
34 | //! packet.category = 34;
35 | //! packet.messages = vec![asterix::AsterixMessage::Cat34(thirty_eight)];
36 | //! ```
37 | //!
38 | //! # Encoding Packets
39 | //! ```rust
40 | //! use deku::prelude::*;
41 | //! use asterix::*;
42 | //! use asterix::data_item::*;
43 | //!
44 | //! // Create / Mutate a packet
45 | //! let mut packet = AsterixPacket::default();
46 | //!
47 | //! // finalize(): Updates fspec for all packet messages, as well as setting the length as per
48 | //! // the protocol.
49 | //! packet.finalize().unwrap();
50 | //!
51 | //! // serialize
52 | //! packet.to_bytes().unwrap();
53 | //! ```
54 |
55 | use deku::bitvec::{BitVec, Msb0};
56 | use deku::prelude::*;
57 |
58 | pub mod types;
59 |
60 | mod custom_read_write;
61 | mod modifier;
62 |
63 | mod fourty_eight;
64 | pub use fourty_eight::Cat48;
65 |
66 | mod thirty_four;
67 | pub use thirty_four::Cat34;
68 |
69 | pub mod data_item;
70 | mod fspec;
71 |
72 | /// Size of category + length in bytes
73 | const ASTERIX_HEADER_SIZE: u16 = 3;
74 |
75 | const FSPEC_IDENT: u8 = 0b0000_0001;
76 |
77 | #[derive(Debug, Default, PartialEq, DekuRead, DekuWrite)]
78 | #[deku(endian = "big")]
79 | pub struct AsterixPacket {
80 | /// Category of all `messages`
81 | pub category: u8,
82 | /// Total length of `AsterixPacket`
83 | #[deku(update = "Self::update_len(&mut self.messages)")]
84 | pub length: u16,
85 | /// Asterix Messages
86 | #[deku(bytes_read = "length - ASTERIX_HEADER_SIZE", ctx = "*category")]
87 | pub messages: Vec,
88 | }
89 |
90 | impl AsterixPacket {
91 | /// Update fspec and len
92 | pub fn finalize(&mut self) -> Result<(), DekuError> {
93 | for message in &mut self.messages {
94 | message.update_fspec();
95 | }
96 | self.update()
97 | }
98 |
99 | /// Read all messages and return byte len
100 | fn update_len(messages: &mut [AsterixMessage]) -> u16 {
101 | let mut len: u16 = 0;
102 | for message in messages.iter_mut() {
103 | let mut bits: BitVec = BitVec::new();
104 | message.write(&mut bits, (deku::ctx::Endian::Big, 0)).unwrap();
105 | len += (bits.len() / 8) as u16 + ASTERIX_HEADER_SIZE
106 | }
107 | len
108 | }
109 | }
110 |
111 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
112 | #[deku(id = "category", ctx = "_: deku::ctx::Endian, category: u8")]
113 | /// Union of Asterix categories
114 | pub enum AsterixMessage {
115 | #[deku(id = "34")]
116 | Cat34(Cat34),
117 | #[deku(id = "48")]
118 | Cat48(Cat48),
119 | }
120 |
121 | impl AsterixMessage {
122 | /// Call `update_fpsec` of internal type
123 | pub fn update_fspec(&mut self) {
124 | match self {
125 | Self::Cat34(c) => c.update_fspec(),
126 | Self::Cat48(c) => c.update_fspec(),
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/modifier.rs:
--------------------------------------------------------------------------------
1 | pub fn groundspeed() -> f32 {
2 | 2_f32.powi(-14)
3 | }
4 |
5 | pub fn heading1() -> f32 {
6 | 360.0 / 2_f32.powi(16)
7 | }
8 |
9 | pub fn heading2() -> f32 {
10 | 360.0 / 2_f32.powi(12)
11 | }
12 |
--------------------------------------------------------------------------------
/src/thirty_four.rs:
--------------------------------------------------------------------------------
1 | use crate::data_item::{
2 | AntennaRotationSpeed, CollimationError, DataFilter, DataSourceIdentifier, GenericPolarWindow,
3 | MessageCountValues, MessageType, SectorNumber, SystemConfigurationAndStatus,
4 | SystemProcessingMode, ThreeDPositionOfDataSource, TimeOfDay,
5 | };
6 | use crate::fspec::{add_fx, is_fspec, trim_fspec};
7 | use crate::FSPEC_IDENT;
8 | use asterix_derive::UpdateFspec;
9 | use deku::prelude::*;
10 |
11 | /// Transmission of Monoradar Service Messages
12 | #[derive(Debug, Default, PartialEq, DekuRead, DekuWrite, UpdateFspec)]
13 | #[deku(endian = "big")]
14 | pub struct Cat34 {
15 | #[deku(until = "|b: &u8| *b & FSPEC_IDENT == 0")]
16 | pub fspec: Vec,
17 | /// FRN 1
18 | #[deku(skip, cond = "is_fspec(DataSourceIdentifier::FRN_34, fspec, 0)")]
19 | pub data_source_identifier: Option,
20 | /// FRN 2
21 | #[deku(skip, cond = "is_fspec(MessageType::FRN_34, fspec, 0)")]
22 | pub message_type: Option,
23 | /// FRN 3
24 | #[deku(skip, cond = "is_fspec(TimeOfDay::FRN_34, fspec, 0)")]
25 | pub time_of_day: Option,
26 | /// FRN 4
27 | #[deku(skip, cond = "is_fspec(SectorNumber::FRN_34, fspec, 0)")]
28 | pub sector_number: Option,
29 | /// FRN 5
30 | #[deku(skip, cond = "is_fspec(AntennaRotationSpeed::FRN_34, fspec, 0)")]
31 | pub antenna_rotation_speed: Option,
32 | /// FRN 6
33 | #[deku(skip, cond = "is_fspec(SystemConfigurationAndStatus::FRN_34, fspec, 0)")]
34 | pub system_configuration_and_status: Option,
35 | /// FRN 7
36 | #[deku(skip, cond = "is_fspec(SystemProcessingMode::FRN_34, fspec, 0)")]
37 | pub system_processing_mode: Option,
38 | /// FRN 8
39 | #[deku(skip, cond = "is_fspec(MessageCountValues::FRN_34, fspec, 1)")]
40 | pub message_count_values: Option,
41 | /// FRN 9
42 | #[deku(skip, cond = "is_fspec(GenericPolarWindow::FRN_34, fspec, 1)")]
43 | pub generic_polar_window: Option,
44 | /// FRN 10
45 | #[deku(skip, cond = "is_fspec(DataFilter::FRN_34, fspec, 1)")]
46 | pub data_filter: Option,
47 | /// FRN 11
48 | #[deku(skip, cond = "is_fspec(ThreeDPositionOfDataSource::FRN_34, fspec, 1)")]
49 | pub three_d_position_of_data_source: Option,
50 | /// FRN 12
51 | #[deku(skip, cond = "is_fspec(CollimationError::FRN_34, fspec, 1)")]
52 | pub collimation_error: Option,
53 | // FRN 13: Reserved Expansion Field
54 | // FRN 14: Special Purpose Field
55 | }
56 |
--------------------------------------------------------------------------------
/src/types.rs:
--------------------------------------------------------------------------------
1 | //! Enums used for providing common meaning for bits in a `data_item`
2 |
3 | use deku::prelude::*;
4 |
5 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
6 | #[deku(type = "u8", bits = "3")]
7 | pub enum TYP {
8 | NoDetection = 0x00,
9 | SinglePSRDetection = 0x01,
10 | SingleSSRDetection = 0x02,
11 | SSRPlusPSRDetection = 0x03,
12 | SingleModeSAllCall = 0x04,
13 | SingleModeSRollCall = 0x05,
14 | ModeSAllCallPlusPSR = 0x06,
15 | ModeSRollCallPlusPSR = 0x07,
16 | }
17 |
18 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
19 | #[deku(type = "u8", bits = "1")]
20 | pub enum SIM {
21 | ActualTargetReport = 0x00,
22 | SimulatedTargetReport = 0x01,
23 | }
24 |
25 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
26 | #[deku(type = "u8", bits = "1")]
27 | pub enum RDP {
28 | ReportFromRDPChain1 = 0x00,
29 | ReportFromRDPChain2 = 0x01,
30 | }
31 |
32 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
33 | #[deku(type = "u8", bits = "1")]
34 | pub enum SPI {
35 | AbsenceOfSPI = 0x00,
36 | SpecialPositionIdentification = 0x01,
37 | }
38 |
39 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
40 | #[deku(type = "u8", bits = "1")]
41 | pub enum RAB {
42 | ReportFromAircraftTransponder = 0x00,
43 | ReportFromFieldMonitor = 0x01,
44 | }
45 |
46 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
47 | #[deku(type = "u8", bits = "1")]
48 | pub enum FX {
49 | EndOfDataItem = 0x00,
50 | ExtensionIntoFirstExtent = 0x01,
51 | }
52 |
53 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
54 | #[deku(type = "u8", bits = "1")]
55 | pub enum V {
56 | CodeValidated = 0x00,
57 | CodeNotValidated = 0x01,
58 | }
59 |
60 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
61 | #[deku(type = "u8", bits = "1")]
62 | pub enum G {
63 | Default = 0x00,
64 | GarbledCode = 0x01,
65 | }
66 |
67 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
68 | #[deku(type = "u8", bits = "1")]
69 | pub enum L {
70 | Mode3CodeDerivedFromTheReplyOfTheTransponder = 0x00,
71 | Mode3CodeNotExtractedDuringTheLastScan = 0x01,
72 | }
73 |
74 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
75 | #[deku(type = "u8", bits = "1")]
76 | pub enum CNF {
77 | ConfirmedTrack = 0x00,
78 | TentativeTrack = 0x01,
79 | }
80 |
81 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
82 | #[deku(type = "u8", bits = "2")]
83 | pub enum RAD {
84 | CombinedTrack = 0x00,
85 | PSRTrack = 0x01,
86 | SSRModeSTrack = 0x02,
87 | Invalid = 0x03,
88 | }
89 |
90 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
91 | #[deku(type = "u8", bits = "1")]
92 | pub enum DOU {
93 | NormalConfidence = 0x00,
94 | LowConfidence = 0x01,
95 | }
96 |
97 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
98 | #[deku(type = "u8", bits = "1")]
99 | pub enum MAH {
100 | NoHorizontalManSensed = 0x00,
101 | HorizontalManSensed = 0x01,
102 | }
103 |
104 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
105 | #[deku(type = "u8", bits = "2")]
106 | pub enum CDM {
107 | Maintaining = 0x00,
108 | Climbing = 0x01,
109 | Descending = 0x02,
110 | Unknown = 0x03,
111 | }
112 |
113 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
114 | #[deku(type = "u8", bits = "1")]
115 | pub enum TRE {
116 | TrackStillAlive = 0x00,
117 | EndOfTrackLifetime = 0x01,
118 | }
119 |
120 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
121 | #[deku(type = "u8", bits = "1")]
122 | pub enum GHO {
123 | TrueTargetTrack = 0x00,
124 | GhostTargetTrack = 0x01,
125 | }
126 |
127 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
128 | #[deku(type = "u8", bits = "1")]
129 | pub enum SUP {
130 | No = 0x00,
131 | Yes = 0x01,
132 | }
133 |
134 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
135 | #[deku(type = "u8", bits = "1")]
136 | pub enum TCC {
137 | RadarPlanePlotTransformation = 0x00,
138 | SlantRangePlotTransformation = 0x01,
139 | }
140 |
141 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
142 | #[deku(type = "u8", bits = "3")]
143 | pub enum COM {
144 | NoCommunicationsSurveillanceOnly = 0x00,
145 | CommACommB = 0x01,
146 | CommACommBUplinkELM = 0x02,
147 | CommACommBUplinkELMDownlinkELM = 0x03,
148 | Top5TransponderCapability = 0x04,
149 | #[deku(id_pat = "0x05..=0x07")]
150 | NoAssigned,
151 | }
152 |
153 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
154 | #[deku(type = "u8", bits = "3")]
155 | pub enum STAT {
156 | NoAlertNoSPIAircraftAirborne = 0x00,
157 | NoAlertNoSPIAircraftOnGround = 0x01,
158 | AlertNoSPIAircraftAirborne = 0x02,
159 | AlertNoSPIAircraftOnGround = 0x03,
160 | AlertSPIAircraftAirborneOrOnGround = 0x04,
161 | NoAlertSPIAircraftAirborneOrOnGround = 0x05,
162 | NotAssigned = 0x06,
163 | Unknown = 0x07,
164 | }
165 |
166 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
167 | #[deku(type = "u8", bits = "1")]
168 | pub enum SI {
169 | SICodeCapable = 0x00,
170 | IICodeCapable = 0x01,
171 | }
172 |
173 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
174 | #[deku(type = "u8", bits = "1")]
175 | pub enum MSSC {
176 | No = 0x00,
177 | Yes = 0x01,
178 | }
179 |
180 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
181 | #[deku(type = "u8", bits = "1")]
182 | pub enum ARC {
183 | Resolution100ft = 0x00,
184 | Resolution25ft = 0x01,
185 | }
186 |
187 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
188 | #[deku(type = "u8", bits = "1")]
189 | pub enum AIC {
190 | No = 0x00,
191 | Yes = 0x01,
192 | }
193 |
194 | #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
195 | #[deku(type = "u8", bits = "8")]
196 | pub enum MTYPE {
197 | NorthMarker = 0x01,
198 | SectorCrossing = 0x02,
199 | GeographicaFiltering = 0x03,
200 | JammingStrobe = 0x04,
201 | }
202 |
203 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
204 | #[deku(type = "u16", bits = "7")]
205 | pub enum CODE {
206 | #[deku(id = "0")]
207 | NotDefined,
208 | #[deku(id = "1")]
209 | MultipathReply,
210 | #[deku(id = "2")]
211 | ReplySidelobeInterrogationReception,
212 | #[deku(id = "3")]
213 | SplitPlot,
214 | #[deku(id = "4")]
215 | SecondTimeAroundReply,
216 | #[deku(id = "5")]
217 | Angel,
218 | #[deku(id = "6")]
219 | SlowMovingTarget,
220 | #[deku(id = "7")]
221 | FixedPSRPlot,
222 | #[deku(id = "8")]
223 | SlowPSRPlot,
224 | #[deku(id = "9")]
225 | LowQualityPSRPlot,
226 | #[deku(id = "10")]
227 | PhantomSSRPlot,
228 | #[deku(id = "11")]
229 | NonMatchingMode3ACode,
230 | #[deku(id = "12")]
231 | ModeCCodeModeSAbnormal,
232 | #[deku(id = "13")]
233 | TargetInClutter,
234 | #[deku(id = "14")]
235 | MaximumDopplerREsponseInZeroFilter,
236 | #[deku(id = "15")]
237 | TransponderAnomalyDetected,
238 | #[deku(id = "16")]
239 | DuplicatedOrIllegalModeSAircraftAddress,
240 | #[deku(id = "17")]
241 | ModeSErrorCorrectionApplied,
242 | #[deku(id = "18")]
243 | UndecodableModeCSCode,
244 | #[deku(id = "19")]
245 | Birds,
246 | #[deku(id = "20")]
247 | FlockOfBirds,
248 | #[deku(id = "21")]
249 | Mode1PresentOriginalReply,
250 | #[deku(id = "22")]
251 | Mode2PresentOriginalReply,
252 | #[deku(id = "23")]
253 | PlotCausedByWindTurbine,
254 | #[deku(id = "24")]
255 | Helicopter,
256 | #[deku(id = "25")]
257 | MaxiumumNumberInterrogationsSurveillance,
258 | #[deku(id = "26")]
259 | MaxiumumNumberInterrogationsBDS,
260 | #[deku(id = "27")]
261 | BDSOverlayIncoherence,
262 | #[deku(id = "28")]
263 | PotentialBDSSwapDetected,
264 | #[deku(id = "29")]
265 | TrackUpdateZenithalGap,
266 | #[deku(id = "30")]
267 | ModeSTrackReAquired,
268 | #[deku(id = "31")]
269 | DuplicatedMode5PairNoPinDetected,
270 | #[deku(id = "32")]
271 | WrongDFReplyFormatDetected,
272 | #[deku(id = "33")]
273 | TransponderAnomalyMs,
274 | #[deku(id = "34")]
275 | TransponderAnomalySI,
276 | #[deku(id = "35")]
277 | PotentialICConflict,
278 | #[deku(id = "36")]
279 | ICConflictDetectionPossible,
280 | #[deku(id_pat = "37..=63")]
281 | AMGAllocated(u16),
282 | #[deku(id_pat = "64..=127")]
283 | Manufacturer(u16),
284 | }
285 |
286 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
287 | #[deku(type = "u8", bits = "1")]
288 | pub enum D {
289 | Valid = 0,
290 | Doubtful = 1,
291 | }
292 |
293 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
294 | #[deku(type = "u8", bits = "1")]
295 | /// Operational Release Status of the System
296 | pub enum NOGO {
297 | SystemIsReleasedForOperationalUse = 0,
298 | OperationalUseOfSystemIsInhibited = 1,
299 | }
300 |
301 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
302 | #[deku(type = "u8", bits = "1")]
303 | /// Radar Data Processor Chain Selection Status
304 | pub enum RDPC {
305 | RDPC1Selected = 0,
306 | RDPC2Selected = 1,
307 | }
308 |
309 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
310 | #[deku(type = "u8", bits = "1")]
311 | /// Event to signal a reset/restart of the selected Radar Data Processor Chain,
312 | /// i.e. expect a new assignment of track numbers
313 | pub enum RDPR {
314 | DefaultSituation = 0,
315 | ResetOfRDPC = 1,
316 | }
317 |
318 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
319 | #[deku(type = "u8", bits = "1")]
320 | /// Monitoring System Connected Status
321 | pub enum MSC {
322 | MonitoringSystemConnected = 0,
323 | MonitoringSystemDisconnected = 1,
324 | }
325 |
326 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
327 | #[deku(type = "u8", bits = "1")]
328 | /// Time Source Validity
329 | pub enum TSV {
330 | Valid = 0,
331 | Invalid = 1,
332 | }
333 |
334 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
335 | #[deku(type = "u8", bits = "1")]
336 | /// Selected Antenna
337 | pub enum ANT {
338 | Antenna1 = 0,
339 | Antenna2 = 1,
340 | }
341 |
342 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
343 | #[deku(type = "u8", bits = "2")]
344 | /// Channel A/B Selection Status
345 | pub enum CHAB {
346 | NoChannelSelected = 0b00,
347 | ChannelAOnlySelected = 0b01,
348 | ChannelBOnlySelected = 0b10,
349 | DiversityMode = 0b11,
350 | }
351 |
352 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
353 | #[deku(type = "u8", bits = "1")]
354 | /// Overload Condition
355 | pub enum OVL {
356 | NoOverload = 0,
357 | Overload = 1,
358 | }
359 |
360 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
361 | #[deku(type = "u8", bits = "1")]
362 | /// Channel A/B selection status for Surveillance Co-ordination Function
363 | pub enum SCF {
364 | ChannelAInUse = 0,
365 | ChannelBInUse = 1,
366 | }
367 |
368 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
369 | #[deku(type = "u8", bits = "1")]
370 | /// Channel A/B selection status for Data Link Function
371 | pub enum DLF {
372 | ChannelAInUse = 0,
373 | ChannelBInUse = 1,
374 | }
375 |
376 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
377 | #[deku(type = "u8", bits = "3")]
378 | pub enum RED {
379 | NoReductionActive = 0b000,
380 | ReductionStep1Active = 0b001,
381 | ReductionStep2Active = 0b010,
382 | ReductionStep3Active = 0b011,
383 | ReductionStep4Active = 0b100,
384 | ReductionStep5Active = 0b101,
385 | ReductionStep6Active = 0b110,
386 | ReductionStep7Active = 0b111,
387 | }
388 |
389 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
390 | #[deku(type = "u8", bits = "1")]
391 | pub enum POL {
392 | LinearPolarization = 0,
393 | CircularPolarization = 1,
394 | }
395 |
396 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
397 | #[deku(type = "u8", bits = "1")]
398 | pub enum STC {
399 | STCMap1 = 0b00,
400 | STCMap2 = 0b01,
401 | STCMap3 = 0b10,
402 | STCMap4 = 0b11,
403 | }
404 |
405 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
406 | #[deku(type = "u8", bits = "1")]
407 | pub enum CLU {
408 | Autonomous = 0,
409 | #[deku(id = "1")]
410 | NotAutonomous = 1,
411 | }
412 |
413 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
414 | #[deku(type = "u8", bits = "5")]
415 | pub enum MessageCounterTYP {
416 | NoDetection = 0,
417 | SinglePSRTargetReports = 1,
418 | SingleSSRTargetReports = 2,
419 | SSRPSRTargetReports = 3,
420 | SingleAllCallTargetReports = 4,
421 | SingleRollCallTargetReports = 5,
422 | AllCallPSRModeSTargetReports = 6,
423 | RollCallPSRModeSTargetReports = 7,
424 | FilterForWeatherData = 8,
425 | FilterForJammingStrobe = 9,
426 | FilterPSRData = 10,
427 | FilterSSRModeSData = 11,
428 | FilterSSRModeSPSRData = 12,
429 | FilterForEnhancedSuveillanceData = 13,
430 | FilterForPSREnhancedSurveillance = 14,
431 | FilterForPSREnhancedSurveillancePlusSSRModeSDataNotInAreaOfPrimeInterest = 15,
432 | FilterForPSREnhancedSurveillancePlusAllSSRModeSData = 16,
433 | }
434 |
435 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
436 | #[deku(type = "u8", bits = "8")]
437 | pub enum DataFilterTYP {
438 | InvalidValue = 0,
439 | FilterWeatherData = 1,
440 | FilterJammingStrobe = 2,
441 | FilterPSRData = 3,
442 | FilterSSRModeSData = 4,
443 | FilterSSRModeSPSRData = 5,
444 | EnhancedSurveillanceData = 6,
445 | FilterPSREnhancedSurveillanceData = 7,
446 | FilterPSREnhancedSurveillanceSSRModeSDataNotInAreaOfPrimeInterest = 8,
447 | FilterPSREnhancedSurveillanceAllSSRModeSData = 9,
448 | }
449 |
450 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
451 | #[deku(type = "u8", bits = "1")]
452 | pub enum TST {
453 | RealTargetReport = 0,
454 | TestTargetReport = 1,
455 | }
456 |
457 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
458 | #[deku(type = "u8", bits = "1")]
459 | pub enum ERR {
460 | NoExtendedRange = 0,
461 | ExtendedRangePresent = 1,
462 | }
463 |
464 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
465 | #[deku(type = "u8", bits = "1")]
466 | pub enum XPP {
467 | NoXPulsePresent = 0,
468 | XPulsePresent = 1,
469 | }
470 |
471 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
472 | #[deku(type = "u8", bits = "1")]
473 | pub enum ME {
474 | NoMilitaryEmergency = 0,
475 | MilitaryEmergency = 1,
476 | }
477 |
478 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
479 | #[deku(type = "u8", bits = "1")]
480 | pub enum MI {
481 | NoMilitaryIdentification = 0,
482 | MilitaryIdentification = 1,
483 | }
484 |
485 | #[derive(Debug, PartialEq, Clone, Copy, DekuRead, DekuWrite)]
486 | #[deku(type = "u8", bits = "2")]
487 | pub enum FOEFRI {
488 | NoMode4Interrogation = 0b00,
489 | FriendlyTarget = 0b01,
490 | UnknownTarget = 0b10,
491 | NoReply = 0b11,
492 | }
493 |
--------------------------------------------------------------------------------
/tests/public_api.rs:
--------------------------------------------------------------------------------
1 | use assert_hex::assert_eq_hex;
2 | use asterix::data_item::{
3 | CodeFx, DataSourceIdentifier, HeightMeasuredBy3dRadar, MBData, MessageType,
4 | Mode3ACodeConfidenceIndicator, ModeCCodeAndConfidenceIndicator, SectorNumber, TimeOfDay,
5 | TrackQuality, WarningErrorConditionsTargetClass,
6 | };
7 | use asterix::types::{
8 | AIC, ARC, CDM, CNF, CODE, COM, DOU, FX, G, GHO, L, MAH, MSSC, MTYPE, RAB, RAD, RDP, SI, SIM,
9 | SPI, STAT, SUP, TCC, TRE, TYP, V,
10 | };
11 | use asterix::{AsterixMessage, AsterixPacket, Cat34, Cat48};
12 | use deku::{DekuContainerRead, DekuContainerWrite};
13 |
14 | #[test]
15 | fn it_works() {
16 | let bytes = vec![
17 | 0x30, 0x00, 0x30, 0xfd, 0xf7, 0x02, 0x19, 0xc9, 0x35, 0x6d, 0x4d, 0xa0, 0xc5, 0xaf, 0xf1,
18 | 0xe0, 0x02, 0x00, 0x05, 0x28, 0x3c, 0x66, 0x0c, 0x10, 0xc2, 0x36, 0xd4, 0x18,
19 | //0x20 in wireshark, but last 6 bits don't matter and will 0x00 by writer
20 | 0x00, 0x01, 0xc0, 0x78, 0x00, 0x31, 0xbc, 0x00, 0x00, 0x40, 0x0d, 0xeb, 0x07, 0xb9, 0x58,
21 | 0x2e, 0x41, 0x00, 0x20, 0xf5,
22 | ];
23 | let (_, mut packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
24 |
25 | assert_eq!(packet.category, 48);
26 | assert_eq!(packet.length, 48);
27 |
28 | // TODO check NONE values after more are implemented
29 | if let AsterixMessage::Cat48(ref mut message) = packet.messages[0] {
30 | assert_eq_hex!(message.fspec, &[0xfd, 0xf7, 0x02]);
31 |
32 | let data_source_identifier = message.data_source_identifier.as_ref().unwrap();
33 | assert_eq!(data_source_identifier.sac, 25);
34 | assert_eq!(data_source_identifier.sic, 201);
35 |
36 | let time_of_day = message.time_of_day.as_ref().unwrap();
37 | assert_eq!(time_of_day.time, 27354.602);
38 |
39 | let target_report_descriptor = message.target_report_descriptor.as_ref().unwrap();
40 | assert_eq!(target_report_descriptor.typ, TYP::SingleModeSRollCall);
41 | assert_eq!(target_report_descriptor.sim, SIM::ActualTargetReport);
42 | assert_eq!(target_report_descriptor.rdp, RDP::ReportFromRDPChain1);
43 | assert_eq!(target_report_descriptor.spi, SPI::AbsenceOfSPI);
44 | assert_eq!(target_report_descriptor.rab, RAB::ReportFromAircraftTransponder);
45 | assert_eq!(target_report_descriptor.fx1, FX::EndOfDataItem);
46 |
47 | let measured_position_in_polar_coordinates =
48 | message.measured_position_in_polar_coordinates.as_ref().unwrap();
49 | assert_eq!(measured_position_in_polar_coordinates.rho, 197.6836);
50 | assert_eq!(measured_position_in_polar_coordinates.theta, 340.13672);
51 |
52 | let mode_3_a_code_in_octal_representation =
53 | message.mode_3_a_code_in_octal_representation.as_ref().unwrap();
54 | assert_eq!(mode_3_a_code_in_octal_representation.v, V::CodeValidated);
55 | assert_eq!(mode_3_a_code_in_octal_representation.g, G::Default);
56 | assert_eq!(
57 | mode_3_a_code_in_octal_representation.l,
58 | L::Mode3CodeDerivedFromTheReplyOfTheTransponder
59 | );
60 | //TODO add squawk?
61 |
62 | let flight_level_in_binary_repre = message.flight_level_in_binary_repre.as_ref().unwrap();
63 | assert_eq!(flight_level_in_binary_repre.v, V::CodeValidated);
64 | assert_eq!(flight_level_in_binary_repre.g, G::Default);
65 | assert_eq!(flight_level_in_binary_repre.flight_level, 330);
66 |
67 | let aircraft_address = message.aircraft_address.as_ref().unwrap();
68 | assert_eq!(aircraft_address.address, 0x003c_660c);
69 |
70 | let aircraft_identification = message.aircraft_identification.as_ref().unwrap();
71 | assert_eq!(aircraft_identification.identification, "DLH65A ");
72 |
73 | let mode_smb_data = message.mode_smb_data.as_ref().unwrap();
74 | assert_eq!(mode_smb_data.count, 1);
75 | assert_eq_hex!(
76 | mode_smb_data.mb_data,
77 | vec![MBData { data: [0xc0, 0x78, 0x00, 0x31, 0xbc, 0x00, 0x00].to_vec() }]
78 | );
79 | // TODO assert BDS1
80 | // TODO assert BDS2
81 |
82 | let track_number = message.track_number.as_ref().unwrap();
83 | assert_eq!(track_number.number, 3563);
84 |
85 | let calculated_track_velocity = message.calculated_track_velocity.as_ref().unwrap();
86 | assert_eq!(calculated_track_velocity.groundspeed, 0.120_666_504);
87 | assert_eq!(calculated_track_velocity.heading, 124.002_686);
88 |
89 | let track_status = message.track_status.as_ref().unwrap();
90 | assert_eq!(track_status.cnf, CNF::ConfirmedTrack);
91 | assert_eq!(track_status.rad, RAD::SSRModeSTrack);
92 | assert_eq!(track_status.dou, DOU::NormalConfidence);
93 | assert_eq!(track_status.mah, MAH::NoHorizontalManSensed);
94 | assert_eq!(track_status.cdm, CDM::Maintaining);
95 | assert_eq!(track_status.fx1, FX::ExtensionIntoFirstExtent);
96 | assert_eq!(track_status.tre, Some(TRE::TrackStillAlive));
97 | assert_eq!(track_status.gho, Some(GHO::TrueTargetTrack));
98 | assert_eq!(track_status.sup, Some(SUP::No));
99 | assert_eq!(track_status.tcc, Some(TCC::RadarPlanePlotTransformation));
100 | assert_eq!(track_status.fx2, Some(FX::EndOfDataItem));
101 |
102 | let communications_capability_flight_status =
103 | message.communications_capability_flight_status.as_ref().unwrap();
104 | assert_eq!(communications_capability_flight_status.com, COM::CommACommB);
105 | assert_eq!(
106 | communications_capability_flight_status.stat,
107 | STAT::NoAlertNoSPIAircraftAirborne
108 | );
109 | assert_eq!(communications_capability_flight_status.si, SI::SICodeCapable);
110 | assert_eq!(communications_capability_flight_status.mssc, MSSC::Yes);
111 | assert_eq!(communications_capability_flight_status.arc, ARC::Resolution25ft);
112 | assert_eq!(communications_capability_flight_status.aic, AIC::Yes);
113 | assert_eq!(communications_capability_flight_status.b1a, 1);
114 | assert_eq!(communications_capability_flight_status.b1b, 5);
115 | } else {
116 | unreachable!("Message is not CAT48");
117 | }
118 |
119 | packet.finalize().unwrap();
120 |
121 | // Confirm packet back to bytes
122 | assert_eq_hex!(packet.to_bytes(), Ok(bytes));
123 | }
124 |
125 | #[test]
126 | /// Remove communications_capability_flight_status and update fspec, making sure to check if the
127 | /// fspec works as well as the communications_capability_flight_status is none
128 | fn it_works_only_two_fspec() {
129 | let bytes = vec![
130 | 0x30, 0x00, 0x2d, 0xfd, 0xf6, 0x19, 0xc9, 0x35, 0x6d, 0x4d, 0xa0, 0xc5, 0xaf, 0xf1, 0xe0,
131 | 0x02, 0x00, 0x05, 0x28, 0x3c, 0x66, 0x0c, 0x10, 0xc2, 0x36, 0xd4, 0x18, 0x00, 0x01, 0xc0,
132 | 0x78, 0x00, 0x31, 0xbc, 0x00, 0x00, 0x40, 0x0d, 0xeb, 0x07, 0xb9, 0x58, 0x2e, 0x41, 0x00,
133 | ];
134 | let (_, mut packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
135 |
136 | assert_eq!(packet.category, 48);
137 | assert_eq!(packet.length, 45);
138 |
139 | if let AsterixMessage::Cat48(ref message) = packet.messages[0] {
140 | assert_eq!(message.fspec, &[0xfd, 0xf6]);
141 | // everything here is checked in the above test
142 | assert!(message.communications_capability_flight_status.is_none());
143 | } else {
144 | unreachable!("Message is not CAT48");
145 | }
146 |
147 | packet.finalize().unwrap();
148 |
149 | // Confirm packet back to bytes
150 | assert_eq_hex!(packet.to_bytes(), Ok(bytes));
151 | }
152 |
153 | #[test]
154 | fn third_packet() {
155 | let bytes = vec![
156 | 0x30, 0x00, 0x37, 0xff, 0xff, 0x02, 0x19, 0x0d, 0x35, 0x6d, 0xee, 0xa0, 0xc2, 0xd3, 0x5b,
157 | 0x90, 0x04, 0xc3, 0x05, 0xa0, 0xe0, 0x56, 0x0b, 0xb8, 0x4b, 0xaa, 0xcd, 0x50, 0x86, 0x79,
158 | 0x51, 0x88, 0x00, 0x01, 0xc6, 0x56, 0x32, 0xb0, 0xa8, 0x00, 0x00, 0x40, 0x01, 0xe2, 0x4b,
159 | 0xf6, 0xc3, 0x04, 0x08, 0x1e, 0xbb, 0x73, 0x40, 0x20, 0xf5,
160 | ];
161 | // TODO: Add CAT034
162 | let (_, mut packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
163 | assert_eq!(packet.category, 48);
164 | assert_eq!(packet.length, 55);
165 |
166 | // TODO check NONE values after more are implemented
167 | if let AsterixMessage::Cat48(ref message) = packet.messages[0] {
168 | assert_eq_hex!(message.fspec, &[0xff, 0x0ff, 0x02]);
169 |
170 | let data_source_identifier = message.data_source_identifier.as_ref().unwrap();
171 | assert_eq!(data_source_identifier.sac, 25);
172 | assert_eq!(data_source_identifier.sic, 13);
173 |
174 | let time_of_day = message.time_of_day.as_ref().unwrap();
175 | assert_eq!(time_of_day.time, 27_355.86);
176 |
177 | let target_report_descriptor = message.target_report_descriptor.as_ref().unwrap();
178 | assert_eq!(target_report_descriptor.typ, TYP::SingleModeSRollCall);
179 | assert_eq!(target_report_descriptor.sim, SIM::ActualTargetReport);
180 | assert_eq!(target_report_descriptor.rdp, RDP::ReportFromRDPChain1);
181 | assert_eq!(target_report_descriptor.spi, SPI::AbsenceOfSPI);
182 | assert_eq!(target_report_descriptor.rab, RAB::ReportFromAircraftTransponder);
183 | assert_eq!(target_report_descriptor.fx1, FX::EndOfDataItem);
184 |
185 | let measured_position_in_polar_coordinates =
186 | message.measured_position_in_polar_coordinates.as_ref().unwrap();
187 | assert_eq!(measured_position_in_polar_coordinates.rho, 194.824_22);
188 | assert_eq!(measured_position_in_polar_coordinates.theta, 128.759_77);
189 |
190 | let mode_3_a_code_in_octal_representation =
191 | message.mode_3_a_code_in_octal_representation.as_ref().unwrap();
192 | assert_eq!(mode_3_a_code_in_octal_representation.v, V::CodeValidated);
193 | assert_eq!(mode_3_a_code_in_octal_representation.g, G::Default);
194 | assert_eq!(
195 | mode_3_a_code_in_octal_representation.l,
196 | L::Mode3CodeDerivedFromTheReplyOfTheTransponder
197 | );
198 | //TODO add squawk?
199 |
200 | let flight_level_in_binary_repre = message.flight_level_in_binary_repre.as_ref().unwrap();
201 | assert_eq!(flight_level_in_binary_repre.v, V::CodeValidated);
202 | assert_eq!(flight_level_in_binary_repre.g, G::Default);
203 | assert_eq!(flight_level_in_binary_repre.flight_level, 360);
204 |
205 | let aircraft_address = message.aircraft_address.as_ref().unwrap();
206 | assert_eq!(aircraft_address.address, 0x004b_aacd);
207 |
208 | let aircraft_identification = message.aircraft_identification.as_ref().unwrap();
209 | assert_eq!(aircraft_identification.identification, "THY9TX ");
210 |
211 | let mode_smb_data = message.mode_smb_data.as_ref().unwrap();
212 | assert_eq!(mode_smb_data.count, 1);
213 | assert_eq!(
214 | mode_smb_data.mb_data,
215 | vec![MBData { data: [0xc6, 0x56, 0x32, 0xb0, 0xa8, 0x00, 0x00].to_vec() }]
216 | );
217 | assert_eq!(mode_smb_data.bds1, 4);
218 | assert_eq!(mode_smb_data.bds2, 0);
219 |
220 | let track_number = message.track_number.as_ref().unwrap();
221 | assert_eq!(track_number.number, 482);
222 |
223 | let cal_pos_cartesian_coor = message.calculated_position_cartesian_coor.as_ref().unwrap();
224 | assert_eq!(cal_pos_cartesian_coor.x, 151.921_88);
225 | assert_eq!(cal_pos_cartesian_coor.y, -121.96875);
226 |
227 | let calculated_track_velocity = message.calculated_track_velocity.as_ref().unwrap();
228 | assert_eq!(calculated_track_velocity.groundspeed, 0.126_831_05);
229 | assert_eq!(calculated_track_velocity.heading, 263.600_46);
230 |
231 | let track_status = message.track_status.as_ref().unwrap();
232 | assert_eq!(track_status.cnf, CNF::ConfirmedTrack);
233 | assert_eq!(track_status.rad, RAD::SSRModeSTrack);
234 | assert_eq!(track_status.dou, DOU::NormalConfidence);
235 | assert_eq!(track_status.mah, MAH::NoHorizontalManSensed);
236 | assert_eq!(track_status.cdm, CDM::Maintaining);
237 | assert_eq!(track_status.fx1, FX::EndOfDataItem);
238 | assert_eq!(track_status.tre, None);
239 | assert_eq!(track_status.gho, None);
240 | assert_eq!(track_status.sup, None);
241 | assert_eq!(track_status.tcc, None);
242 | assert_eq!(track_status.fx2, None);
243 |
244 | let communications_capability_flight_status =
245 | message.communications_capability_flight_status.as_ref().unwrap();
246 | assert_eq!(communications_capability_flight_status.com, COM::CommACommB);
247 | assert_eq!(
248 | communications_capability_flight_status.stat,
249 | STAT::NoAlertNoSPIAircraftAirborne
250 | );
251 | assert_eq!(communications_capability_flight_status.si, SI::SICodeCapable);
252 | assert_eq!(communications_capability_flight_status.mssc, MSSC::Yes);
253 | assert_eq!(communications_capability_flight_status.arc, ARC::Resolution25ft);
254 | assert_eq!(communications_capability_flight_status.aic, AIC::Yes);
255 | assert_eq!(communications_capability_flight_status.b1a, 1);
256 | assert_eq!(communications_capability_flight_status.b1b, 5);
257 | } else {
258 | unreachable!("Message is not CAT48");
259 | }
260 | packet.finalize().unwrap();
261 | assert_eq_hex!(packet.to_bytes(), Ok(bytes));
262 | }
263 |
264 | #[test]
265 | fn test_34() {
266 | let bytes = vec![0x22, 0x00, 0x0b, 0xf0, 0x19, 0x0d, 0x02, 0x35, 0x6d, 0xfa, 0x60];
267 | let (_, mut packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
268 |
269 | assert_eq!(packet.category, 34);
270 | assert_eq!(packet.length, 11);
271 |
272 | // TODO check NONE values after more are implemented
273 | if let AsterixMessage::Cat34(ref message) = packet.messages[0] {
274 | assert_eq_hex!(message.fspec, &[0xf0]);
275 |
276 | let data_source_identifier = message.data_source_identifier.as_ref().unwrap();
277 | assert_eq!(data_source_identifier.sac, 25);
278 | assert_eq!(data_source_identifier.sic, 13);
279 |
280 | let message_type = message.message_type.as_ref().unwrap();
281 | assert_eq!(message_type.t, MTYPE::SectorCrossing);
282 |
283 | let time_of_day = message.time_of_day.as_ref().unwrap();
284 | assert_eq!(time_of_day.time, 27355.953);
285 |
286 | let sector_number = message.sector_number.as_ref().unwrap();
287 | assert_eq!(sector_number.num, 135);
288 | } else {
289 | unreachable!("Not Cat 34");
290 | }
291 | packet.finalize().unwrap();
292 | assert_eq_hex!(packet.to_bytes(), Ok(bytes));
293 | }
294 |
295 | #[test]
296 | fn test_four_messages() {
297 | // Example of having multiple asterix messages being received in one packet, this requires one to
298 | // parse the first messages, and parsing until the rest.len() == 0
299 | let bytes = vec![
300 | // Cat 048
301 | 0x30, 0x00, 0xb9, 0xe1, 0x93, 0x02, 0x19, 0x0d, 0x35, 0x64, 0x21, 0x00, 0x44, 0xd0, 0x74,
302 | 0x02, 0xda, 0x41, 0x80, 0x20, 0xf5, 0xff, 0xff, 0x02, 0x19, 0x0d, 0x35, 0x6e, 0x06, 0xa0,
303 | 0x2b, 0x4d, 0x65, 0x1e, 0x04, 0x18, 0x05, 0xa0, 0xe0, 0x56, 0x0c, 0xcf, 0x46, 0x92, 0xd1,
304 | 0x04, 0x51, 0x72, 0x09, 0x28, 0x20, 0x02, 0xc6, 0x50, 0x00, 0x30, 0x7c, 0x00, 0x00, 0x40,
305 | 0xf0, 0x09, 0xf7, 0x2f, 0xa0, 0x64, 0x02, 0x60, 0x02, 0xf9, 0x0d, 0x46, 0xee, 0xe5, 0x07,
306 | 0xdc, 0xe1, 0xb5, 0x40, 0x20, 0xfd, 0xff, 0xff, 0x02, 0x19, 0x0d, 0x35, 0x6d, 0xfd, 0xa0,
307 | 0x63, 0xd3, 0x61, 0x68, 0x0e, 0xaa, 0x05, 0xa0, 0xe0, 0x56, 0x0c, 0xc2, 0x3c, 0x64, 0x8a,
308 | 0x10, 0xc2, 0x36, 0xe7, 0x58, 0x20, 0x01, 0xc6, 0x50, 0x00, 0x30, 0xb8, 0x00, 0x00, 0x40,
309 | 0x05, 0x9b, 0x22, 0x10, 0xdb, 0x84, 0x07, 0xfc, 0xe2, 0xff, 0x40, 0x20, 0xf5, 0xff, 0xff,
310 | 0x02, 0x19, 0x0d, 0x35, 0x6e, 0x03, 0xa0, 0x6d, 0xa2, 0x64, 0x22, 0x09, 0xe5, 0x06, 0x40,
311 | 0xe0, 0x64, 0x0c, 0xc0, 0xa0, 0x22, 0xa3, 0x3b, 0x1c, 0x38, 0x0c, 0x58, 0x20, 0x01, 0xf1,
312 | 0x19, 0xe9, 0x32, 0x60, 0x04, 0x00, 0x60, 0x05, 0x90, 0x22, 0xa3, 0xd5, 0x83, 0x07, 0xeb,
313 | 0xe2, 0x78, 0x40, 0x20, 0xf6, // Cat 034
314 | 0x22, 0x00, 0x0b, 0xf0, 0x19, 0x0d, 0x02, 0x35, 0x6e, 0x0e, 0x68,
315 | ];
316 | let (rest, mut packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
317 | packet.finalize().unwrap();
318 | assert_eq!(packet.category, 48);
319 | let (_, mut packet) = AsterixPacket::from_bytes(rest).unwrap();
320 | packet.finalize().unwrap();
321 | assert_eq!(packet.category, 34);
322 | }
323 |
324 | #[test]
325 | fn test_not_from_bytes() {
326 | let mut thirty_eight = Cat34::default();
327 | thirty_eight.data_source_identifier = Some(DataSourceIdentifier { sac: 25, sic: 13 });
328 | thirty_eight.message_type = Some(MessageType { t: MTYPE::SectorCrossing });
329 | thirty_eight.time_of_day = Some(TimeOfDay { time: 27355.953 });
330 | thirty_eight.sector_number = Some(SectorNumber { num: 135 });
331 |
332 | let mut packet = AsterixPacket {
333 | category: 34,
334 | messages: vec![asterix::AsterixMessage::Cat34(thirty_eight)],
335 | ..AsterixPacket::default()
336 | };
337 | packet.finalize().unwrap();
338 | let exp_bytes = vec![0x22, 0x00, 0x0b, 0xf0, 0x19, 0x0d, 0x02, 0x35, 0x6d, 0xfa, 0x60];
339 | assert_eq!(packet.to_bytes().unwrap(), exp_bytes)
340 | }
341 |
342 | // The following data items don't have pcap captures, and are my own testing
343 |
344 | #[test]
345 | fn test_48_track_quality() {
346 | let mut fourty_eight = Cat48::default();
347 | fourty_eight.track_quality = Some(TrackQuality {
348 | horizontal_stddev: 0.0,
349 | vertical_stddev: 0.0,
350 | groundspeed_stddev: 0.0,
351 | heading_stddev: 0.0,
352 | });
353 | let mut packet = AsterixPacket {
354 | category: 48,
355 | messages: vec![asterix::AsterixMessage::Cat48(fourty_eight)],
356 | ..AsterixPacket::default()
357 | };
358 | packet.finalize().unwrap();
359 | let exp_bytes = vec![0x30, 0x00, 0x0a, 0x01, 0x01, 0b1000_0000, 0x00, 0x00, 0x00, 0x00];
360 | assert_eq!(packet.to_bytes().unwrap(), exp_bytes);
361 | let (_, exp_packet) = AsterixPacket::from_bytes((&exp_bytes, 0)).unwrap();
362 | assert_eq!(packet, exp_packet);
363 |
364 | let mut fourty_eight = Cat48::default();
365 | fourty_eight.track_quality = Some(TrackQuality {
366 | horizontal_stddev: 32000.0,
367 | vertical_stddev: 32000.0,
368 | groundspeed_stddev: 0.015_563_965,
369 | heading_stddev: 22.412_11,
370 | });
371 | let mut packet = AsterixPacket {
372 | category: 48,
373 | messages: vec![asterix::AsterixMessage::Cat48(fourty_eight)],
374 | ..AsterixPacket::default()
375 | };
376 | packet.finalize().unwrap();
377 | let exp_bytes = vec![0x30, 0x00, 0x0a, 0x01, 0x01, 0b1000_0000, 0xfa, 0xfa, 0xff, 0xff];
378 | assert_eq_hex!(packet.to_bytes().unwrap(), exp_bytes);
379 | let (_, exp_packet) = AsterixPacket::from_bytes((&exp_bytes, 0)).unwrap();
380 | assert_eq_hex!(packet, exp_packet);
381 | }
382 |
383 | #[test]
384 | fn test_48_warning_error_con_target_class() {
385 | let mut fourty_eight = Cat48::default();
386 |
387 | let warning = WarningErrorConditionsTargetClass {
388 | codefxs: vec![
389 | CodeFx { code: CODE::Angel, fx: FX::ExtensionIntoFirstExtent },
390 | CodeFx { code: CODE::Angel, fx: FX::EndOfDataItem },
391 | ],
392 | };
393 |
394 | fourty_eight.warning_error_con_target_class = Some(warning);
395 | let mut packet = AsterixPacket {
396 | category: 48,
397 | messages: vec![asterix::AsterixMessage::Cat48(fourty_eight)],
398 | ..AsterixPacket::default()
399 | };
400 | packet.finalize().unwrap();
401 | let exp_bytes = vec![0x30, 0x00, 0x08, 0x01, 0x01, 0b100_0000, 0x5 << 1 | 0x01, 0x5 << 1];
402 | assert_eq_hex!(packet.to_bytes().unwrap(), exp_bytes);
403 | let (_, exp_packet) = AsterixPacket::from_bytes((&exp_bytes, 0)).unwrap();
404 | assert_eq_hex!(packet, exp_packet);
405 | }
406 |
407 | #[test]
408 | fn test_48_mode_3a_code_confidence_indicator() {
409 | let mut fourty_eight = Cat48::default();
410 |
411 | let confidence = Mode3ACodeConfidenceIndicator { reserved: 0, confidence: 0b0000_0001 };
412 | fourty_eight.mode3a_code_confidence_indicator = Some(confidence);
413 | let mut packet = AsterixPacket {
414 | category: 48,
415 | messages: vec![asterix::AsterixMessage::Cat48(fourty_eight)],
416 | ..AsterixPacket::default()
417 | };
418 | packet.finalize().unwrap();
419 | let exp_bytes = vec![0x30, 0x00, 0x08, 0x01, 0x01, 0b10_0000, 0x00, 0x01];
420 | assert_eq_hex!(packet.to_bytes().unwrap(), exp_bytes);
421 | let (_, exp_packet) = AsterixPacket::from_bytes((&exp_bytes, 0)).unwrap();
422 | assert_eq_hex!(packet, exp_packet);
423 | }
424 |
425 | #[test]
426 | fn test_48_mode_c_code_confidence() {
427 | let mut fourty_eight = Cat48::default();
428 | let confidence = ModeCCodeAndConfidenceIndicator {
429 | v: V::CodeValidated,
430 | g: G::Default,
431 | reserved0: 0,
432 | mode_c_gray_notation: 0x01,
433 | reserved1: 0,
434 | confidence: 0x01,
435 | };
436 | fourty_eight.modec_code_and_confidence_indicator = Some(confidence);
437 | let mut packet = AsterixPacket {
438 | category: 48,
439 | messages: vec![asterix::AsterixMessage::Cat48(fourty_eight)],
440 | ..AsterixPacket::default()
441 | };
442 | packet.finalize().unwrap();
443 | let exp_bytes = vec![0x30, 0x00, 0x0a, 0x01, 0x01, 0b1_0000, 0x00, 0x01, 0x00, 0x01];
444 | assert_eq_hex!(packet.to_bytes().unwrap(), exp_bytes);
445 | let (_, exp_packet) = AsterixPacket::from_bytes((&exp_bytes, 0)).unwrap();
446 | assert_eq_hex!(packet, exp_packet);
447 | }
448 |
449 | #[test]
450 | fn test_48_height_3d() {
451 | let mut fourty_eight = Cat48::default();
452 | let height = HeightMeasuredBy3dRadar { reserved: 0, height: 25 };
453 | fourty_eight.height_measured_by_3d_radar = Some(height);
454 | let mut packet = AsterixPacket {
455 | category: 48,
456 | messages: vec![asterix::AsterixMessage::Cat48(fourty_eight)],
457 | ..AsterixPacket::default()
458 | };
459 | packet.finalize().unwrap();
460 | let exp_bytes = vec![0x30, 0x00, 0x08, 0x01, 0x01, 0b1000, 0x00, 0x01];
461 | assert_eq_hex!(packet.to_bytes().unwrap(), exp_bytes);
462 | let (_, exp_packet) = AsterixPacket::from_bytes((&exp_bytes, 0)).unwrap();
463 | assert_eq_hex!(packet, exp_packet);
464 |
465 | let mut fourty_eight = Cat48::default();
466 | let height = HeightMeasuredBy3dRadar { reserved: 0, height: 37200 };
467 | fourty_eight.height_measured_by_3d_radar = Some(height);
468 | let mut packet = AsterixPacket {
469 | category: 48,
470 | messages: vec![asterix::AsterixMessage::Cat48(fourty_eight)],
471 | ..AsterixPacket::default()
472 | };
473 | packet.finalize().unwrap();
474 | let exp_bytes = vec![0x30, 0x00, 0x08, 0x01, 0x01, 0b1000, 0x05, 0xd0];
475 | assert_eq_hex!(packet.to_bytes().unwrap(), exp_bytes);
476 | let (_, exp_packet) = AsterixPacket::from_bytes((&exp_bytes, 0)).unwrap();
477 | assert_eq_hex!(packet, exp_packet);
478 | }
479 |
480 | #[test]
481 | fn test_48_radial_dopplerspeed() {
482 | // test the first subfield
483 | let bytes = vec![0x30, 0x00, 0x09, 0x01, 0x01, 0b100, 0b1000_0000, 0b1000_0000, 0b0000_0001];
484 | let (_, packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
485 | assert_eq_hex!(packet.to_bytes().unwrap(), bytes);
486 |
487 | // test the second subfield
488 | let bytes = vec![
489 | 0x30,
490 | 0x00,
491 | 0x0e,
492 | 0x01,
493 | 0x01,
494 | 0b100,
495 | 0b0100_0000,
496 | 0x01,
497 | 0x00,
498 | 0x01,
499 | 0x00,
500 | 0x01,
501 | 0x00,
502 | 0x01,
503 | ];
504 | let (_, packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
505 | assert_eq_hex!(packet.to_bytes().unwrap(), bytes);
506 | }
507 |
508 | #[test]
509 | fn test_acas_resolution() {
510 | let bytes = vec![
511 | 0x30,
512 | 0x00,
513 | 0x0e,
514 | 0x01,
515 | 0x01,
516 | 0x01,
517 | 0b1000_0000,
518 | 0x01,
519 | 0x02,
520 | 0x03,
521 | 0x04,
522 | 0x05,
523 | 0x06,
524 | 0x07,
525 | ];
526 | let (_, mut packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
527 | packet.finalize().unwrap();
528 | assert_eq_hex!(packet.to_bytes().unwrap(), bytes);
529 |
530 | if let AsterixMessage::Cat48(ref message) = packet.messages[0] {
531 | assert_eq_hex!(message.fspec, &[0x01, 0x01, 0x01, 0b1000_0000]);
532 |
533 | let field = message.acas_resolution_advisory_report.as_ref().unwrap();
534 | assert_eq_hex!(&field.mb_data, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]);
535 | }
536 | }
537 |
538 | #[test]
539 | fn test_mode1code_octal_representation() {
540 | let bytes = vec![0x30, 0x00, 0x08, 0x01, 0x01, 0x01, 0b0100_0000, 0b0000_0001];
541 | let (_, mut packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
542 | packet.finalize().unwrap();
543 | assert_eq_hex!(packet.to_bytes().unwrap(), bytes);
544 |
545 | if let AsterixMessage::Cat48(ref message) = packet.messages[0] {
546 | assert_eq_hex!(message.fspec, &[0x01, 0x01, 0x01, 0b0100_0000]);
547 |
548 | let field = message.mode_1_code_octal_representation.as_ref().unwrap();
549 | assert_eq!(field.v, V::CodeValidated);
550 | assert_eq!(field.g, G::Default);
551 | assert_eq!(field.l, L::Mode3CodeDerivedFromTheReplyOfTheTransponder);
552 | assert_eq!(field.data, 0x01);
553 | }
554 | }
555 |
556 | #[test]
557 | fn test_mode2code_octal_representation() {
558 | let bytes = vec![0x30, 0x00, 0x09, 0x01, 0x01, 0x01, 0b0010_0000, 0b0000_0000, 0b0000_0001];
559 | let (_, mut packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
560 | packet.finalize().unwrap();
561 | assert_eq_hex!(packet.to_bytes().unwrap(), bytes);
562 |
563 | if let AsterixMessage::Cat48(ref message) = packet.messages[0] {
564 | assert_eq_hex!(message.fspec, &[0x01, 0x01, 0x01, 0b0010_0000]);
565 |
566 | let field = message.mode_2_code_octal_representation.as_ref().unwrap();
567 | assert_eq!(field.v, V::CodeValidated);
568 | assert_eq!(field.g, G::Default);
569 | assert_eq!(field.l, L::Mode3CodeDerivedFromTheReplyOfTheTransponder);
570 | assert_eq!(field.data, 0x01);
571 | }
572 | }
573 |
574 | #[test]
575 | fn test_mode1codeconfidence() {
576 | let bytes = vec![0x30, 0x00, 0x08, 0x01, 0x01, 0x01, 0b0001_0000, 0x01];
577 | let (_, mut packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
578 | packet.finalize().unwrap();
579 | assert_eq_hex!(packet.to_bytes().unwrap(), bytes);
580 |
581 | if let AsterixMessage::Cat48(ref message) = packet.messages[0] {
582 | assert_eq_hex!(message.fspec, &[0x01, 0x01, 0x01, 0b0001_0000]);
583 |
584 | let field = message.mode_1_code_confidence.as_ref().unwrap();
585 | assert_eq!(field.data, 0x01);
586 | }
587 | }
588 |
589 | #[test]
590 | fn test_mode2codeconfidence() {
591 | let bytes = vec![0x30, 0x00, 0x09, 0x01, 0x01, 0x01, 0b0000_1000, 0x00, 0x01];
592 | let (_, mut packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
593 | packet.finalize().unwrap();
594 | assert_eq_hex!(packet.to_bytes().unwrap(), bytes);
595 |
596 | if let AsterixMessage::Cat48(ref message) = packet.messages[0] {
597 | assert_eq_hex!(message.fspec, &[0x01, 0x01, 0x01, 0b0000_1000]);
598 |
599 | let field = message.mode_2_code_confidence.as_ref().unwrap();
600 | assert_eq!(field.data, 0x01);
601 | }
602 | }
603 |
604 | #[test]
605 | fn test_34_antenna_rotation_speed() {
606 | let bytes = vec![0x22, 0x00, 0x06, 0b0000_1000, 0x00, 0x01];
607 | let (_, mut packet) = AsterixPacket::from_bytes((&bytes, 0)).unwrap();
608 | packet.finalize().unwrap();
609 | assert_eq_hex!(packet.to_bytes().unwrap(), bytes);
610 |
611 | if let AsterixMessage::Cat34(ref message) = packet.messages[0] {
612 | assert_eq_hex!(message.fspec, &[0b0000_1000]);
613 |
614 | let field = message.antenna_rotation_speed.as_ref().unwrap();
615 | assert_eq!(field.period, 1.0 / 128_f32);
616 | }
617 | }
618 |
--------------------------------------------------------------------------------