pluto-sdr 0.1.1

HAL for ADALM-Pluto SDR
Documentation
// Author: Roman Hayn
// MIT License 2023

use std::string::FromUtf8Error;

/// Providing an interface to transcode e.g. Strings (i.e. UTF-8) into Pulse-Amplitude-Modulation
trait Pam {
    fn to_pam(&self) -> Vec<u8>;
    fn from_pam(pam: Vec<u8>) -> Result<String, FromUtf8Error>;
}

impl Pam for String {
    /// convert String to UTF-8, and its bytes to PAM Symbols
    /// 4PAM gives 4 values per byte, since each 4PAM valua can have 4 values (0,1,2,3)
    fn to_pam(&self) -> Vec<u8> {
        let bytes = self.as_bytes();
        let mut result_vec: Vec<u8> = Vec::with_capacity(bytes.len() * 4);

        for byte in bytes {
            println!("0x{:08b}", byte);
            for i in 0..=3 {
                let downshifted = byte >> (3 - i) * 2;
                let masked = downshifted & 0b00000011;
                result_vec.push(masked);
            }
        }

        println!("vec: {:?}", result_vec);
        return result_vec;
    }

    fn from_pam(pam: Vec<u8>) -> Result<String, FromUtf8Error> {
        // len is the amount of utf- symbols (not string length!)
        let len = pam.len() / 4;
        // we over 1 byte per utf-8 symbol/code point
        let mut bytes: Vec<u8> = Vec::with_capacity(len);

        for i in 0..len {
            // reassemble the byte from 4x pam samples
            let mut byte: u8 = 0b00000000;
            for j in 0..=3 {
                byte |= pam[i * 4 + j] << 2 * (3 - j);
            }
            bytes.push(byte);
        }
        return String::from_utf8(bytes);
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_utf_8_size() {
        // € is 3 bytes long in utf-8
        let s: String = "hello €".to_string();
        assert_eq!(s.len(), 9);
    }

    #[test]
    fn string_to_pam() {
        let s: String = "not a planet".to_string();
        let pam = s.to_pam();

        let correct_pam = [
            1, 2, 3, 2, 1, 2, 3, 3, 1, 3, 1, 0, 0, 2, 0, 0, 1, 2, 0, 1, 0, 2, 0, 0, 1, 3, 0, 0, 1,
            2, 3, 0, 1, 2, 0, 1, 1, 2, 3, 2, 1, 2, 1, 1, 1, 3, 1, 0,
        ];
        assert_eq!(pam, correct_pam);
    }

    #[test]
    fn pam_to_string() {
        let pam = vec![
            1, 2, 3, 3, 1, 3, 0, 2, 0, 2, 0, 0, 1, 2, 2, 1, 1, 3, 0, 3, 0, 2, 0, 0, 1, 2, 2, 1, 1,
            3, 1, 0, 0, 3, 3, 3,
        ];
        let s = String::from_pam(pam);
        let correct_s = "or is it?".to_string();
        assert_eq!(s.unwrap(), correct_s);
    }

    #[test]
    fn str_to_pam_to_str() {
        // no matter what symbols we throw at it, if they are properly escaped (not {:?}), this
        // should never fail
        let s = "The quick brown fox jumps over the lazy dog 0123456789!@#$%^&*()€\n\r".to_string();
        let p = s.to_pam();

        let reconst = String::from_pam(p).unwrap();
        assert_eq!(s, reconst);
    }
}