mirror of
https://github.com/MidnightBlueLabs/tetra-bluestation.git
synced 2026-03-29 05:09:51 +00:00
Added parsing for U-STATUS and D-STATUS pre-coded status field
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use tetra_config::bluestation::SharedConfig;
|
||||
use tetra_core::{BitBuffer, Sap, SsiType, TetraAddress, tetra_entities::TetraEntity, unimplemented_log};
|
||||
use tetra_pdus::cmce::enums::pre_coded_status::PreCodedStatus;
|
||||
use tetra_saps::control::enums::sds_user_data::SdsUserData;
|
||||
use tetra_saps::control::sds::CmceSdsData;
|
||||
use tetra_saps::lcmc::LcmcMleUnitdataReq;
|
||||
@@ -174,7 +175,7 @@ impl SdsBsSubentity {
|
||||
dltime: tetra_core::TdmaTime,
|
||||
source_issi: u32,
|
||||
dest_issi: u32,
|
||||
pre_coded_status: u16,
|
||||
pre_coded_status: PreCodedStatus,
|
||||
) {
|
||||
let pdu = DStatus {
|
||||
calling_party_type_identifier: PartyTypeIdentifier::Ssi,
|
||||
|
||||
@@ -6,6 +6,7 @@ use tetra_config::bluestation::{CfgBrew, StackMode};
|
||||
use tetra_core::tetra_entities::TetraEntity;
|
||||
use tetra_core::{BitBuffer, Sap, SsiType, TdmaTime, TetraAddress, debug};
|
||||
use tetra_pdus::cmce::enums::party_type_identifier::PartyTypeIdentifier;
|
||||
use tetra_pdus::cmce::enums::pre_coded_status::PreCodedStatus;
|
||||
use tetra_pdus::cmce::pdus::u_sds_data::USdsData;
|
||||
use tetra_pdus::cmce::pdus::u_status::UStatus;
|
||||
use tetra_saps::control::enums::sds_user_data::SdsUserData;
|
||||
@@ -268,7 +269,7 @@ fn test_u_status_forwarded_as_d_status() {
|
||||
called_party_short_number_address: None,
|
||||
called_party_ssi: Some(2000001),
|
||||
called_party_extension: None,
|
||||
pre_coded_status: 0x8210,
|
||||
pre_coded_status: PreCodedStatus::try_from(0x8210).unwrap(),
|
||||
external_subscriber_number: None,
|
||||
dm_ms_address: None,
|
||||
};
|
||||
@@ -331,7 +332,7 @@ fn test_u_status_unregistered_dest_dropped() {
|
||||
called_party_short_number_address: None,
|
||||
called_party_ssi: Some(9999999),
|
||||
called_party_extension: None,
|
||||
pre_coded_status: 0x8210,
|
||||
pre_coded_status: PreCodedStatus::from(0x8210),
|
||||
external_subscriber_number: None,
|
||||
dm_ms_address: None,
|
||||
};
|
||||
|
||||
@@ -5,6 +5,8 @@ pub mod cmce_pdu_type_dl;
|
||||
pub mod cmce_pdu_type_ul;
|
||||
pub mod disconnect_cause;
|
||||
pub mod party_type_identifier;
|
||||
pub mod pre_coded_status;
|
||||
pub mod sds_protocol_id;
|
||||
pub mod short_report_type;
|
||||
pub mod transmission_grant;
|
||||
pub mod type3_elem_id;
|
||||
|
||||
54
crates/tetra-pdus/src/cmce/enums/pre_coded_status.rs
Normal file
54
crates/tetra-pdus/src/cmce/enums/pre_coded_status.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
use crate::cmce::fields::sds_short_report::SdsShortReport;
|
||||
|
||||
/// Clause 14.8.34 Pre-coded status
|
||||
/// The pre-coded status information element shall define general purpose status messages known to all TETRA systems as
|
||||
/// defined in table 14.72 and shall provide support for the SDS-TL "short reporting" protocol.
|
||||
/// Bits: 2
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum PreCodedStatus {
|
||||
Emergency,
|
||||
Reserved(u16),
|
||||
SdsTl(SdsShortReport),
|
||||
NetworkUserSpecific(u16),
|
||||
}
|
||||
|
||||
impl From<u16> for PreCodedStatus {
|
||||
fn from(x: u16) -> Self {
|
||||
match x {
|
||||
0 => PreCodedStatus::Emergency,
|
||||
1..=31742 => PreCodedStatus::Reserved(x),
|
||||
31743..=32767 => PreCodedStatus::SdsTl(SdsShortReport::from_u16(x).unwrap()),
|
||||
32768..=65535 => PreCodedStatus::NetworkUserSpecific(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PreCodedStatus {
|
||||
/// Convert this enum back into the raw integer value
|
||||
pub fn into_raw(self) -> u16 {
|
||||
match self {
|
||||
PreCodedStatus::Emergency => 0,
|
||||
PreCodedStatus::Reserved(x) => x,
|
||||
PreCodedStatus::SdsTl(x) => x.to_u16(),
|
||||
PreCodedStatus::NetworkUserSpecific(x) => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PreCodedStatus> for u16 {
|
||||
fn from(e: PreCodedStatus) -> Self {
|
||||
e.into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for PreCodedStatus {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
PreCodedStatus::Emergency => write!(f, "Emergency"),
|
||||
PreCodedStatus::Reserved(x) => write!(f, "Reserved({})", x),
|
||||
PreCodedStatus::SdsTl(x) => write!(f, "SdsTl({})", x),
|
||||
PreCodedStatus::NetworkUserSpecific(x) => write!(f, "NetworkUserSpecific({})", x),
|
||||
}
|
||||
}
|
||||
}
|
||||
53
crates/tetra-pdus/src/cmce/enums/short_report_type.rs
Normal file
53
crates/tetra-pdus/src/cmce/enums/short_report_type.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
/// Clause 29.4.3.11 Short report type
|
||||
/// The Short report type information element shall indicate the reason for report as defined in table 29.23.
|
||||
/// Bits: 2
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum ShortReportType {
|
||||
ProtOrEncodingNotSupported = 0,
|
||||
DestMemFull = 1,
|
||||
MessageReceived = 2,
|
||||
MessageConsumed = 3,
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u64> for ShortReportType {
|
||||
type Error = ();
|
||||
fn try_from(x: u64) -> Result<Self, Self::Error> {
|
||||
match x {
|
||||
0 => Ok(ShortReportType::ProtOrEncodingNotSupported),
|
||||
1 => Ok(ShortReportType::DestMemFull),
|
||||
2 => Ok(ShortReportType::MessageReceived),
|
||||
3 => Ok(ShortReportType::MessageConsumed),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ShortReportType {
|
||||
/// Convert this enum back into the raw integer value
|
||||
pub fn into_raw(self) -> u64 {
|
||||
match self {
|
||||
ShortReportType::ProtOrEncodingNotSupported => 0,
|
||||
ShortReportType::DestMemFull => 1,
|
||||
ShortReportType::MessageReceived => 2,
|
||||
ShortReportType::MessageConsumed => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ShortReportType> for u64 {
|
||||
fn from(e: ShortReportType) -> Self {
|
||||
e.into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for ShortReportType {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
ShortReportType::ProtOrEncodingNotSupported => write!(f, "ProtOrEncodingNotSupported"),
|
||||
ShortReportType::DestMemFull => write!(f, "DestMemFull"),
|
||||
ShortReportType::MessageReceived => write!(f, "MessageReceived"),
|
||||
ShortReportType::MessageConsumed => write!(f, "MessageConsumed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
pub mod basic_service_information;
|
||||
pub mod sds_short_report;
|
||||
|
||||
69
crates/tetra-pdus/src/cmce/fields/sds_short_report.rs
Normal file
69
crates/tetra-pdus/src/cmce/fields/sds_short_report.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use core::fmt;
|
||||
|
||||
use tetra_core::{PduParseErr, expect_value};
|
||||
|
||||
use crate::cmce::enums::short_report_type::ShortReportType;
|
||||
|
||||
/// Clause 29.4.2.3 SDS-SHORT REPORT
|
||||
/// This PDU shall be used to report on the progress of previously received SDS data
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct SdsShortReport {
|
||||
/// 2 bits
|
||||
short_report_type: ShortReportType,
|
||||
/// 8 bits. The same value as in the corresponding request PDU
|
||||
message_reference: u8,
|
||||
}
|
||||
|
||||
impl SdsShortReport {
|
||||
// No from_bitbuf, to_bitbuf functions, as we'll parse this in a bit of a different way originating from an enum field in the U-STATUS PDU pre-coded status field
|
||||
pub fn from_u16(val: u16) -> Result<Self, PduParseErr> {
|
||||
// TODO FIXME implement parsing of the pre-coded status field into this struct, as defined in table 14.72
|
||||
let pdu_type = val >> 10;
|
||||
expect_value!(pdu_type, 0b011111)?;
|
||||
let raw = ((val >> 8) & 0x3) as u64;
|
||||
let short_report_type = ShortReportType::try_from(raw).unwrap(); // never fails
|
||||
let message_reference = (val & 0xFF) as u8;
|
||||
|
||||
Ok(SdsShortReport {
|
||||
short_report_type,
|
||||
message_reference,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_u16(&self) -> u16 {
|
||||
// TODO FIXME implement conversion of this struct into the pre-coded status field, as defined in table 14.72
|
||||
assert!(self.short_report_type.into_raw() <= 0b11, "short_report_type must be 2 bits");
|
||||
(0b011111 << 10) | ((self.short_report_type.into_raw() as u16) << 8) | (self.message_reference as u16 & 0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SdsShortReport {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"SdsShortReport {{ short_report_type: {:?}, message_reference: {:?} }}",
|
||||
self.short_report_type, self.message_reference,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_to_u16_roundtrip() {
|
||||
// Synthetic test, not real world data
|
||||
let sds = SdsShortReport {
|
||||
short_report_type: ShortReportType::MessageReceived,
|
||||
message_reference: 0b00000001,
|
||||
};
|
||||
|
||||
let converted = sds.to_u16();
|
||||
assert_eq!(converted, 0b0111111000000001);
|
||||
|
||||
let parsed = SdsShortReport::from_u16(converted).unwrap();
|
||||
assert_eq!(parsed.short_report_type, sds.short_report_type);
|
||||
assert_eq!(parsed.message_reference, sds.message_reference);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
use core::fmt;
|
||||
|
||||
use crate::cmce::enums::pre_coded_status::PreCodedStatus;
|
||||
use crate::cmce::enums::{cmce_pdu_type_dl::CmcePduTypeDl, party_type_identifier::PartyTypeIdentifier, type3_elem_id::CmceType3ElemId};
|
||||
use tetra_core::typed_pdu_fields::*;
|
||||
use tetra_core::{BitBuffer, expect_pdu_type, pdu_parse_error::PduParseErr};
|
||||
@@ -19,7 +20,7 @@ pub struct DStatus {
|
||||
/// Conditional 24 bits, Calling party extension condition: calling_party_type_identifier == 2
|
||||
pub calling_party_extension: Option<u64>,
|
||||
/// Type1, 16 bits, Pre-coded status
|
||||
pub pre_coded_status: u16,
|
||||
pub pre_coded_status: PreCodedStatus,
|
||||
/// Type3, External subscriber number
|
||||
pub external_subscriber_number: Option<Type3FieldGeneric>,
|
||||
/// Type3, DM-MS address
|
||||
@@ -53,7 +54,8 @@ impl DStatus {
|
||||
None
|
||||
};
|
||||
// Type1
|
||||
let pre_coded_status = buffer.read_field(16, "pre_coded_status")? as u16;
|
||||
let val = buffer.read_field(16, "pre_coded_status")? as u16;
|
||||
let pre_coded_status = PreCodedStatus::from(val);
|
||||
|
||||
// obit designates presence of any further type2, type3 or type4 fields
|
||||
let mut obit = delimiters::read_obit(buffer)?;
|
||||
@@ -95,7 +97,7 @@ impl DStatus {
|
||||
buffer.write_bits(*value, 24);
|
||||
}
|
||||
// Type1
|
||||
buffer.write_bits(self.pre_coded_status as u64, 16);
|
||||
buffer.write_bits(self.pre_coded_status.into_raw().into(), 16);
|
||||
|
||||
// Check if any optional field present and place o-bit
|
||||
let obit = self.external_subscriber_number.is_some() || self.dm_ms_address.is_some();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use core::fmt;
|
||||
|
||||
use crate::cmce::enums::pre_coded_status::PreCodedStatus;
|
||||
use crate::cmce::enums::{cmce_pdu_type_ul::CmcePduTypeUl, party_type_identifier::PartyTypeIdentifier, type3_elem_id::CmceType3ElemId};
|
||||
use tetra_core::typed_pdu_fields::*;
|
||||
use tetra_core::{BitBuffer, expect_pdu_type, pdu_parse_error::PduParseErr};
|
||||
@@ -24,7 +25,7 @@ pub struct UStatus {
|
||||
/// Conditional 24 bits, See note 2, condition: called_party_type_identifier == 2
|
||||
pub called_party_extension: Option<u64>,
|
||||
/// Type1, 16 bits, Pre-coded status
|
||||
pub pre_coded_status: u16,
|
||||
pub pre_coded_status: PreCodedStatus,
|
||||
/// Type3, External subscriber number
|
||||
pub external_subscriber_number: Option<Type3FieldGeneric>,
|
||||
/// Type3, DM-MS address
|
||||
@@ -66,7 +67,8 @@ impl UStatus {
|
||||
None
|
||||
};
|
||||
// Type1
|
||||
let pre_coded_status = buffer.read_field(16, "pre_coded_status")? as u16;
|
||||
let val = buffer.read_field(16, "pre_coded_status")? as u16;
|
||||
let pre_coded_status = PreCodedStatus::from(val);
|
||||
|
||||
// obit designates presence of any further type2, type3 or type4 fields
|
||||
let mut obit = delimiters::read_obit(buffer)?;
|
||||
@@ -116,7 +118,7 @@ impl UStatus {
|
||||
buffer.write_bits(*value, 24);
|
||||
}
|
||||
// Type1
|
||||
buffer.write_bits(self.pre_coded_status as u64, 16);
|
||||
buffer.write_bits(self.pre_coded_status.into_raw().into(), 16);
|
||||
|
||||
// Check if any optional field present and place o-bit
|
||||
let obit = self.external_subscriber_number.is_some() || self.dm_ms_address.is_some();
|
||||
|
||||
Reference in New Issue
Block a user