pica 0.1.7

Pica is a virtual UWB Controller implementing the FiRa UCI specification.
Documentation
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![allow(clippy::unused_io_amount)]

use std::path::Path;
use std::time::Instant;
use tokio::io::AsyncWriteExt;

pub struct File {
    file: tokio::fs::File,
    start_time: Instant,
}

pub enum Direction {
    Rx,
    Tx,
}

impl File {
    pub async fn create<P: AsRef<Path>>(path: P) -> std::io::Result<File> {
        let mut file = tokio::fs::File::create(path).await?;

        // PCAPng files must start with a Section Header Block.
        file.write(&u32::to_le_bytes(0x0A0D0D0A)).await?; // Block Type
        file.write(&u32::to_le_bytes(28)).await?; // Block Total Length
        file.write(&u32::to_le_bytes(0x1A2B3C4D)).await?; // Byte-Order Magic
        file.write(&u16::to_le_bytes(1)).await?; // Major Version
        file.write(&u16::to_le_bytes(0)).await?; // Minor Version
        file.write(&u64::to_le_bytes(0xFFFFFFFFFFFFFFFF)).await?; // Section Length (not specified)
        file.write(&u32::to_le_bytes(28)).await?; // Block Total Length

        // Write the Interface Description Block used for all
        // UCI records.
        file.write(&u32::to_le_bytes(0x00000001)).await?; // Block Type
        file.write(&u32::to_le_bytes(20)).await?; // Block Total Length
        file.write(&u16::to_le_bytes(293)).await?; // LinkType
        file.write(&u16::to_le_bytes(0)).await?; // Reserved
        file.write(&u32::to_le_bytes(0)).await?; // SnapLen (no limit)
        file.write(&u32::to_le_bytes(20)).await?; // Block Total Length

        Ok(File {
            file,
            start_time: Instant::now(),
        })
    }

    pub async fn write(&mut self, packet: &[u8], _dir: Direction) -> std::io::Result<()> {
        let packet_data_padding: usize = 4 - packet.len() % 4;
        let block_total_length: u32 = packet.len() as u32 + packet_data_padding as u32 + 32;
        let timestamp = self.start_time.elapsed().as_micros();
        let file = &mut self.file;

        // Wrap the packet inside an Enhanced Packet Block.
        file.write(&u32::to_le_bytes(0x00000006)).await?; // Block Type
        file.write(&u32::to_le_bytes(block_total_length)).await?;
        file.write(&u32::to_le_bytes(0)).await?; // Interface ID
        file.write(&u32::to_le_bytes((timestamp >> 32) as u32))
            .await?; // Timestamp (High)
        file.write(&u32::to_le_bytes(timestamp as u32)).await?; // Timestamp (Low)
        file.write(&u32::to_le_bytes(packet.len() as u32)).await?; // Captured Packet Length
        file.write(&u32::to_le_bytes(packet.len() as u32)).await?; // Original Packet Length
        file.write(packet).await?;
        file.write(&vec![0; packet_data_padding]).await?;
        file.write(&u32::to_le_bytes(block_total_length)).await?; // Block Total Length
        Ok(())
    }
}