1use crate::error::EncodeError;
7use crate::header::DStarHeader;
8use crate::types::{Callsign, Module, StreamId};
9use crate::voice::{AMBE_SILENCE, DSTAR_SYNC_BYTES, VoiceFrame};
10
11use super::consts::{
12 CONNECT_ACK_TAG, CONNECT_LEN, CONNECT_NAK_TAG, CONNECT_REPLY_LEN, DSVT_MAGIC, POLL_LEN,
13 VOICE_DATA_LEN, VOICE_EOT_LEN, VOICE_HEADER_LEN,
14};
15
16pub fn encode_connect_link(
37 out: &mut [u8],
38 callsign: &Callsign,
39 reflector_module: Module,
40 client_module: Module,
41) -> Result<usize, EncodeError> {
42 write_connect_common(out, *callsign, client_module, reflector_module.as_byte())?;
43 Ok(CONNECT_LEN)
44}
45
46pub fn encode_unlink(
60 out: &mut [u8],
61 callsign: &Callsign,
62 client_module: Module,
63) -> Result<usize, EncodeError> {
64 write_connect_common(out, *callsign, client_module, b' ')?;
65 Ok(CONNECT_LEN)
66}
67
68pub fn encode_connect_ack(
89 out: &mut [u8],
90 callsign: &Callsign,
91 reflector_module: Module,
92) -> Result<usize, EncodeError> {
93 write_connect_reply(out, *callsign, reflector_module, CONNECT_ACK_TAG)?;
94 Ok(CONNECT_REPLY_LEN)
95}
96
97pub fn encode_connect_nak(
110 out: &mut [u8],
111 callsign: &Callsign,
112 reflector_module: Module,
113) -> Result<usize, EncodeError> {
114 write_connect_reply(out, *callsign, reflector_module, CONNECT_NAK_TAG)?;
115 Ok(CONNECT_REPLY_LEN)
116}
117
118pub fn encode_poll(out: &mut [u8], callsign: &Callsign) -> Result<usize, EncodeError> {
134 if out.len() < POLL_LEN {
135 return Err(EncodeError::BufferTooSmall {
136 need: POLL_LEN,
137 have: out.len(),
138 });
139 }
140 if let Some(dst) = out.get_mut(..8) {
141 dst.copy_from_slice(callsign.as_bytes());
142 }
143 if let Some(b) = out.get_mut(8) {
144 *b = 0x00;
145 }
146 Ok(POLL_LEN)
147}
148
149pub fn encode_poll_echo(out: &mut [u8], callsign: &Callsign) -> Result<usize, EncodeError> {
157 encode_poll(out, callsign)
158}
159
160pub fn encode_voice_header(
184 out: &mut [u8],
185 stream_id: StreamId,
186 header: &DStarHeader,
187) -> Result<usize, EncodeError> {
188 if out.len() < VOICE_HEADER_LEN {
189 return Err(EncodeError::BufferTooSmall {
190 need: VOICE_HEADER_LEN,
191 have: out.len(),
192 });
193 }
194 if let Some(dst) = out.get_mut(..4) {
195 dst.copy_from_slice(&DSVT_MAGIC);
196 }
197 if let Some(b) = out.get_mut(4) {
198 *b = 0x10;
199 }
200 if let Some(b) = out.get_mut(5) {
201 *b = 0x00;
202 }
203 if let Some(b) = out.get_mut(6) {
204 *b = 0x00;
205 }
206 if let Some(b) = out.get_mut(7) {
207 *b = 0x00;
208 }
209 if let Some(b) = out.get_mut(8) {
210 *b = 0x20;
211 }
212 if let Some(b) = out.get_mut(9) {
213 *b = 0x00;
214 }
215 if let Some(b) = out.get_mut(10) {
216 *b = 0x01;
217 }
218 if let Some(b) = out.get_mut(11) {
219 *b = 0x02;
220 }
221 let sid = stream_id.get().to_le_bytes();
222 if let Some(b) = out.get_mut(12) {
223 *b = sid[0];
224 }
225 if let Some(b) = out.get_mut(13) {
226 *b = sid[1];
227 }
228 if let Some(b) = out.get_mut(14) {
229 *b = 0x80;
230 }
231 let encoded = header.encode_for_dsvt();
232 if let Some(dst) = out.get_mut(15..56) {
233 dst.copy_from_slice(&encoded);
234 }
235 Ok(VOICE_HEADER_LEN)
236}
237
238pub fn encode_voice_data(
261 out: &mut [u8],
262 stream_id: StreamId,
263 seq: u8,
264 frame: &VoiceFrame,
265) -> Result<usize, EncodeError> {
266 if out.len() < VOICE_DATA_LEN {
267 return Err(EncodeError::BufferTooSmall {
268 need: VOICE_DATA_LEN,
269 have: out.len(),
270 });
271 }
272 write_voice_prefix(out, stream_id, seq);
273 if let Some(dst) = out.get_mut(15..24) {
274 dst.copy_from_slice(&frame.ambe);
275 }
276 if let Some(dst) = out.get_mut(24..27) {
277 dst.copy_from_slice(&frame.slow_data);
278 }
279 Ok(VOICE_DATA_LEN)
280}
281
282pub fn encode_voice_eot(
300 out: &mut [u8],
301 stream_id: StreamId,
302 seq: u8,
303) -> Result<usize, EncodeError> {
304 if out.len() < VOICE_EOT_LEN {
305 return Err(EncodeError::BufferTooSmall {
306 need: VOICE_EOT_LEN,
307 have: out.len(),
308 });
309 }
310 write_voice_prefix(out, stream_id, seq | 0x40);
311 if let Some(dst) = out.get_mut(15..24) {
312 dst.copy_from_slice(&AMBE_SILENCE);
313 }
314 if let Some(dst) = out.get_mut(24..27) {
315 dst.copy_from_slice(&DSTAR_SYNC_BYTES);
316 }
317 Ok(VOICE_EOT_LEN)
318}
319
320fn write_voice_prefix(out: &mut [u8], stream_id: StreamId, seq: u8) {
323 if let Some(dst) = out.get_mut(..4) {
324 dst.copy_from_slice(&DSVT_MAGIC);
325 }
326 if let Some(b) = out.get_mut(4) {
327 *b = 0x20;
328 }
329 if let Some(b) = out.get_mut(5) {
330 *b = 0x00;
331 }
332 if let Some(b) = out.get_mut(6) {
333 *b = 0x00;
334 }
335 if let Some(b) = out.get_mut(7) {
336 *b = 0x00;
337 }
338 if let Some(b) = out.get_mut(8) {
339 *b = 0x20;
340 }
341 if let Some(b) = out.get_mut(9) {
342 *b = 0x00;
343 }
344 if let Some(b) = out.get_mut(10) {
345 *b = 0x01;
346 }
347 if let Some(b) = out.get_mut(11) {
348 *b = 0x02;
349 }
350 let sid = stream_id.get().to_le_bytes();
351 if let Some(b) = out.get_mut(12) {
352 *b = sid[0];
353 }
354 if let Some(b) = out.get_mut(13) {
355 *b = sid[1];
356 }
357 if let Some(b) = out.get_mut(14) {
358 *b = seq;
359 }
360}
361
362fn write_connect_common(
377 out: &mut [u8],
378 callsign: Callsign,
379 client_module: Module,
380 byte9: u8,
381) -> Result<(), EncodeError> {
382 if out.len() < CONNECT_LEN {
383 return Err(EncodeError::BufferTooSmall {
384 need: CONNECT_LEN,
385 have: out.len(),
386 });
387 }
388 if let Some(region) = out.get_mut(..CONNECT_LEN) {
394 region.fill(b' ');
395 }
396 let cs = callsign.as_bytes();
400 if let Some(dst) = out.get_mut(..7)
401 && let Some(src) = cs.get(..7)
402 {
403 dst.copy_from_slice(src);
404 }
405 if let Some(b) = out.get_mut(8) {
408 *b = client_module.as_byte();
409 }
410 if let Some(b) = out.get_mut(9) {
413 *b = byte9;
414 }
415 if let Some(b) = out.get_mut(10) {
417 *b = 0x00;
418 }
419 Ok(())
420}
421
422fn write_connect_reply(
434 out: &mut [u8],
435 callsign: Callsign,
436 reflector_module: Module,
437 tag: [u8; 3],
438) -> Result<(), EncodeError> {
439 if out.len() < CONNECT_REPLY_LEN {
440 return Err(EncodeError::BufferTooSmall {
441 need: CONNECT_REPLY_LEN,
442 have: out.len(),
443 });
444 }
445 if let Some(region) = out.get_mut(..CONNECT_REPLY_LEN) {
450 region.fill(b' ');
451 }
452 let cs = callsign.as_bytes();
454 if let Some(dst) = out.get_mut(..7)
455 && let Some(src) = cs.get(..7)
456 {
457 dst.copy_from_slice(src);
458 }
459 if let Some(b) = out.get_mut(8) {
462 *b = cs.get(7).copied().unwrap_or(b' ');
463 }
464 if let Some(b) = out.get_mut(9) {
466 *b = reflector_module.as_byte();
467 }
468 if let Some(dst) = out.get_mut(10..13) {
470 dst.copy_from_slice(&tag);
471 }
472 if let Some(b) = out.get_mut(13) {
474 *b = 0x00;
475 }
476 Ok(())
477}
478
479#[cfg(test)]
480mod tests {
481 use super::*;
482 use crate::types::Suffix;
483
484 type TestResult = Result<(), Box<dyn std::error::Error>>;
485
486 const fn cs(bytes: [u8; 8]) -> Callsign {
487 Callsign::from_wire_bytes(bytes)
488 }
489
490 #[expect(clippy::unwrap_used, reason = "compile-time validated: n != 0")]
491 const fn sid(n: u16) -> StreamId {
492 StreamId::new(n).unwrap()
493 }
494
495 const fn test_header() -> DStarHeader {
496 DStarHeader {
497 flag1: 0,
498 flag2: 0,
499 flag3: 0,
500 rpt2: Callsign::from_wire_bytes(*b"XRF030 G"),
501 rpt1: Callsign::from_wire_bytes(*b"XRF030 C"),
502 ur_call: Callsign::from_wire_bytes(*b"CQCQCQ "),
503 my_call: Callsign::from_wire_bytes(*b"W1AW "),
504 my_suffix: Suffix::EMPTY,
505 }
506 }
507
508 #[test]
510 fn encode_connect_link_writes_11_bytes() -> TestResult {
511 let mut buf = [0u8; 16];
512 let n = encode_connect_link(&mut buf, &cs(*b"W1AW "), Module::C, Module::B)?;
513 assert_eq!(n, 11);
514 assert_eq!(&buf[..7], b"W1AW ", "callsign chars 0..7");
517 assert_eq!(buf[7], b' ', "pad slot before module letter");
519 assert_eq!(buf[8], b'B', "client module");
521 assert_eq!(buf[9], b'C', "reflector module");
523 assert_eq!(buf[10], 0x00, "trailing null");
524 Ok(())
525 }
526
527 #[test]
528 fn encode_connect_link_rejects_small_buffer() -> TestResult {
529 let mut buf = [0u8; 10];
530 let Err(err) = encode_connect_link(&mut buf, &cs(*b"W1AW "), Module::C, Module::B)
531 else {
532 return Err("expected too small".into());
533 };
534 assert_eq!(err, EncodeError::BufferTooSmall { need: 11, have: 10 });
535 Ok(())
536 }
537
538 #[test]
540 fn encode_unlink_writes_11_bytes_with_space_at_9() -> TestResult {
541 let mut buf = [0u8; 16];
542 let n = encode_unlink(&mut buf, &cs(*b"W1AW "), Module::B)?;
543 assert_eq!(n, 11);
544 assert_eq!(&buf[..7], b"W1AW ", "callsign chars 0..7");
545 assert_eq!(buf[7], b' ', "pad slot before module letter");
546 assert_eq!(buf[8], b'B', "client module");
547 assert_eq!(buf[9], b' ', "space where reflector module would be");
548 assert_eq!(buf[10], 0x00);
549 Ok(())
550 }
551
552 #[test]
553 fn encode_unlink_rejects_small_buffer() -> TestResult {
554 let mut buf = [0u8; 5];
555 let Err(err) = encode_unlink(&mut buf, &cs(*b"W1AW "), Module::B) else {
556 return Err("expected too small".into());
557 };
558 assert_eq!(err, EncodeError::BufferTooSmall { need: 11, have: 5 });
559 Ok(())
560 }
561
562 #[test]
564 fn encode_connect_ack_writes_14_bytes() -> TestResult {
565 let mut buf = [0u8; 16];
566 let n = encode_connect_ack(&mut buf, &cs(*b"XRF030 C"), Module::B)?;
570 assert_eq!(n, 14);
571 assert_eq!(&buf[..7], b"XRF030 ", "callsign chars 0..7");
573 assert_eq!(buf[7], b' ', "pad slot");
575 assert_eq!(buf[8], b'C', "echoed repeater module");
577 assert_eq!(buf[9], b'B', "reflector module");
579 assert_eq!(&buf[10..13], b"ACK");
581 assert_eq!(buf[13], 0x00, "NUL terminator");
582 Ok(())
583 }
584
585 #[test]
586 fn encode_connect_nak_writes_14_bytes() -> TestResult {
587 let mut buf = [0u8; 16];
588 let n = encode_connect_nak(&mut buf, &cs(*b"XRF030 C"), Module::B)?;
589 assert_eq!(n, 14);
590 assert_eq!(&buf[..7], b"XRF030 ");
591 assert_eq!(buf[7], b' ');
592 assert_eq!(buf[8], b'C');
593 assert_eq!(buf[9], b'B');
594 assert_eq!(&buf[10..13], b"NAK");
595 assert_eq!(buf[13], 0x00);
596 Ok(())
597 }
598
599 #[test]
600 fn encode_connect_ack_rejects_small_buffer() -> TestResult {
601 let mut buf = [0u8; 13];
602 let Err(err) = encode_connect_ack(&mut buf, &cs(*b"XRF030 "), Module::C) else {
603 return Err("expected too small".into());
604 };
605 assert_eq!(err, EncodeError::BufferTooSmall { need: 14, have: 13 });
606 Ok(())
607 }
608
609 #[test]
611 fn encode_poll_writes_9_bytes() -> TestResult {
612 let mut buf = [0u8; 16];
613 let n = encode_poll(&mut buf, &cs(*b"W1AW "))?;
614 assert_eq!(n, 9);
615 assert_eq!(&buf[..8], b"W1AW ");
616 assert_eq!(buf[8], 0x00);
617 Ok(())
618 }
619
620 #[test]
621 fn encode_poll_echo_matches_poll() -> TestResult {
622 let mut poll_buf = [0u8; 16];
623 let mut echo_buf = [0u8; 16];
624 let n1 = encode_poll(&mut poll_buf, &cs(*b"XRF030 "))?;
625 let n2 = encode_poll_echo(&mut echo_buf, &cs(*b"XRF030 "))?;
626 assert_eq!(n1, n2);
627 assert_eq!(
628 poll_buf.get(..n1).ok_or("n1 within poll_buf")?,
629 echo_buf.get(..n2).ok_or("n2 within echo_buf")?,
630 );
631 Ok(())
632 }
633
634 #[test]
635 fn encode_poll_rejects_small_buffer() -> TestResult {
636 let mut buf = [0u8; 8];
637 let Err(err) = encode_poll(&mut buf, &cs(*b"W1AW ")) else {
638 return Err("expected too small".into());
639 };
640 assert_eq!(err, EncodeError::BufferTooSmall { need: 9, have: 8 });
641 Ok(())
642 }
643
644 #[test]
646 fn encode_voice_header_writes_56_bytes() -> TestResult {
647 let mut buf = [0u8; 64];
648 let n = encode_voice_header(&mut buf, sid(0xCAFE), &test_header())?;
649 assert_eq!(n, 56);
650 assert_eq!(&buf[..4], b"DSVT", "magic at offset 0 (no DPlus prefix)");
651 assert_eq!(buf[4], 0x10, "header type");
652 assert_eq!(buf[5], 0x00);
653 assert_eq!(buf[6], 0x00);
654 assert_eq!(buf[7], 0x00);
655 assert_eq!(buf[8], 0x20, "config");
656 assert_eq!(buf[9], 0x00, "band1");
657 assert_eq!(buf[10], 0x01, "band2");
658 assert_eq!(buf[11], 0x02, "band3");
659 assert_eq!(buf[12], 0xFE, "stream id LE low byte");
660 assert_eq!(buf[13], 0xCA, "stream id LE high byte");
661 assert_eq!(buf[14], 0x80, "header indicator");
662 assert_eq!(buf[15], 0, "flag1 zeroed by encode_for_dsvt");
663 assert_eq!(buf[16], 0, "flag2 zeroed");
664 assert_eq!(buf[17], 0, "flag3 zeroed");
665 Ok(())
666 }
667
668 #[test]
669 fn encode_voice_header_rejects_small_buffer() -> TestResult {
670 let mut buf = [0u8; 32];
671 let Err(err) = encode_voice_header(&mut buf, sid(0x1234), &test_header()) else {
672 return Err("expected too small".into());
673 };
674 assert_eq!(err, EncodeError::BufferTooSmall { need: 56, have: 32 });
675 Ok(())
676 }
677
678 #[test]
680 fn encode_voice_data_writes_27_bytes() -> TestResult {
681 let mut buf = [0u8; 64];
682 let frame = VoiceFrame {
683 ambe: [0x11; 9],
684 slow_data: [0x22; 3],
685 };
686 let n = encode_voice_data(&mut buf, sid(0x1234), 5, &frame)?;
687 assert_eq!(n, 27);
688 assert_eq!(&buf[..4], b"DSVT");
689 assert_eq!(buf[4], 0x20, "voice type");
690 assert_eq!(buf[5], 0x00);
691 assert_eq!(buf[6], 0x00);
692 assert_eq!(buf[7], 0x00);
693 assert_eq!(buf[8], 0x20, "config");
694 assert_eq!(buf[9], 0x00);
695 assert_eq!(buf[10], 0x01);
696 assert_eq!(buf[11], 0x02);
697 assert_eq!(buf[12], 0x34, "stream id LE low byte");
698 assert_eq!(buf[13], 0x12, "stream id LE high byte");
699 assert_eq!(buf[14], 5, "seq");
700 assert_eq!(&buf[15..24], &[0x11; 9]);
701 assert_eq!(&buf[24..27], &[0x22; 3]);
702 Ok(())
703 }
704
705 #[test]
706 fn encode_voice_data_rejects_small_buffer() -> TestResult {
707 let mut buf = [0u8; 10];
708 let frame = VoiceFrame {
709 ambe: [0; 9],
710 slow_data: [0; 3],
711 };
712 let Err(err) = encode_voice_data(&mut buf, sid(0x1234), 0, &frame) else {
713 return Err("expected too small".into());
714 };
715 assert_eq!(err, EncodeError::BufferTooSmall { need: 27, have: 10 });
716 Ok(())
717 }
718
719 #[test]
721 fn encode_voice_eot_writes_27_bytes() -> TestResult {
722 let mut buf = [0u8; 64];
723 let n = encode_voice_eot(&mut buf, sid(0x1234), 7)?;
724 assert_eq!(n, 27);
725 assert_eq!(&buf[..4], b"DSVT");
726 assert_eq!(buf[4], 0x20);
727 assert_eq!(buf[14] & 0x40, 0x40, "EOT bit set");
728 assert_eq!(buf[14] & 0x3F, 7, "low bits preserve seq");
729 assert_eq!(&buf[15..24], &AMBE_SILENCE);
730 assert_eq!(&buf[24..27], &DSTAR_SYNC_BYTES);
731 Ok(())
732 }
733
734 #[test]
735 fn encode_voice_eot_rejects_small_buffer() -> TestResult {
736 let mut buf = [0u8; 20];
737 let Err(err) = encode_voice_eot(&mut buf, sid(0x1234), 0) else {
738 return Err("expected too small".into());
739 };
740 assert_eq!(err, EncodeError::BufferTooSmall { need: 27, have: 20 });
741 Ok(())
742 }
743}