dstar_gateway_core/voice.rs
1//! D-STAR voice frame types and constants.
2//!
3//! Each voice frame carries 9 bytes of AMBE-encoded audio and 3
4//! bytes of slow data. Frames are transmitted at 50 Hz (one every
5//! 20 ms) per the JARL D-STAR specification. A superframe is 21
6//! frames (frame 0 is sync, frames 1-20 carry slow data) for a
7//! total of 420 ms per superframe.
8//!
9//! See `g4klx/MMDVMHost/DStarDefines.h:44` for `NULL_AMBE_DATA_BYTES`
10//! (the AMBE silence pattern).
11
12/// AMBE silence frame (9 bytes) — used in EOT packets.
13///
14/// Reference: `g4klx/MMDVMHost/DStarDefines.h:44` (`NULL_AMBE_DATA_BYTES`).
15pub const AMBE_SILENCE: [u8; 9] = [0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8];
16
17/// D-STAR sync bytes (3 bytes) — slow data filler for sync frames.
18pub const DSTAR_SYNC_BYTES: [u8; 3] = [0x55, 0x55, 0x55];
19
20/// A D-STAR voice data frame (9 bytes AMBE + 3 bytes slow data).
21///
22/// 21 frames form one superframe. Frame 0 carries the sync pattern,
23/// frames 1-20 carry slow data. At 20 ms per frame, one superframe
24/// is 420 ms of audio.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct VoiceFrame {
27 /// AMBE 3600x2450 codec voice data (9 bytes).
28 pub ambe: [u8; 9],
29 /// Slow data payload (3 bytes).
30 pub slow_data: [u8; 3],
31}
32
33impl VoiceFrame {
34 /// Create a silence frame (used for EOT and padding).
35 #[must_use]
36 pub const fn silence() -> Self {
37 Self {
38 ambe: AMBE_SILENCE,
39 slow_data: DSTAR_SYNC_BYTES,
40 }
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn ambe_silence_is_nine_bytes() {
50 assert_eq!(AMBE_SILENCE.len(), 9);
51 }
52
53 #[test]
54 fn ambe_silence_matches_mmdvmhost() {
55 // Reference: g4klx/MMDVMHost/DStarDefines.h:44
56 assert_eq!(
57 AMBE_SILENCE,
58 [0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8]
59 );
60 }
61
62 #[test]
63 fn dstar_sync_bytes_are_0x555555() {
64 assert_eq!(DSTAR_SYNC_BYTES, [0x55, 0x55, 0x55]);
65 }
66
67 #[test]
68 fn voice_frame_silence_is_ambe_silence_plus_sync() {
69 let frame = VoiceFrame::silence();
70 assert_eq!(frame.ambe, AMBE_SILENCE);
71 assert_eq!(frame.slow_data, DSTAR_SYNC_BYTES);
72 }
73}