mdf4_rs/
channel.rs

1use crate::{
2    Result,
3    blocks::{ChannelBlock, read_string_block},
4    parsing::{
5        RawChannel, RawChannelGroup, RawDataGroup, SourceInfo,
6        decoder::{DecodedValue, decode_channel_value_with_validity},
7    },
8};
9
10/// High level handle for a single channel within a group.
11///
12/// It holds references to the raw blocks and allows convenient access to
13/// metadata and decoded values.
14pub struct Channel<'a> {
15    block: &'a ChannelBlock,
16    raw_data_group: &'a RawDataGroup,
17    raw_channel_group: &'a RawChannelGroup,
18    raw_channel: &'a RawChannel,
19    mmap: &'a [u8],
20}
21
22impl<'a> Channel<'a> {
23    /// Construct a new [`Channel`] from raw block references.
24    ///
25    /// # Arguments
26    /// * `block` - Channel block containing metadata
27    /// * `raw_data_group` - Parent data group
28    /// * `raw_channel_group` - Parent channel group
29    /// * `raw_channel` - Raw channel helper used to iterate samples
30    /// * `mmap` - Memory mapped file backing all data
31    ///
32    /// # Returns
33    /// A [`Channel`] handle with no samples decoded yet.
34    pub fn new(
35        block: &'a ChannelBlock,
36        raw_data_group: &'a RawDataGroup,
37        raw_channel_group: &'a RawChannelGroup,
38        raw_channel: &'a RawChannel,
39        mmap: &'a [u8],
40    ) -> Self {
41        Channel {
42            block,
43            raw_data_group,
44            raw_channel_group,
45            raw_channel,
46            mmap,
47        }
48    }
49    /// Retrieve the channel name if present.
50    pub fn name(&self) -> Result<Option<String>> {
51        read_string_block(self.mmap, self.block.name_addr)
52    }
53
54    /// Retrieve the physical unit description.
55    pub fn unit(&self) -> Result<Option<String>> {
56        read_string_block(self.mmap, self.block.unit_addr)
57    }
58
59    /// Retrieve the channel comment if present.
60    pub fn comment(&self) -> Result<Option<String>> {
61        read_string_block(self.mmap, self.block.comment_addr)
62    }
63
64    /// Get the acquisition source for this channel if available.
65    pub fn source(&self) -> Result<Option<SourceInfo>> {
66        let addr = self.block.source_addr;
67        SourceInfo::from_mmap(self.mmap, addr)
68    }
69
70    /// Decode and convert all samples of this channel.
71    ///
72    /// This method decodes all channel values and applies conversions.
73    /// Invalid samples (as indicated by invalidation bits) are returned as `None`.
74    ///
75    /// # Returns
76    /// A vector with one `Option<DecodedValue>` per record:
77    /// - `Some(value)` for valid samples
78    /// - `None` for invalid samples (invalidation bit set or decoding failed)
79    pub fn values(&self) -> Result<Vec<Option<DecodedValue>>> {
80        let record_id_size = self.raw_data_group.block.record_id_size as usize;
81        let cg_data_bytes = self.raw_channel_group.block.record_size;
82        let mut out = Vec::new();
83
84        let records_iter =
85            self.raw_channel
86                .records(self.raw_data_group, self.raw_channel_group, self.mmap)?;
87
88        for rec_res in records_iter {
89            let rec = rec_res?;
90
91            // Decode with validity checking
92            if let Some(decoded) =
93                decode_channel_value_with_validity(rec, record_id_size, cg_data_bytes, self.block)
94            {
95                if decoded.is_valid {
96                    // Value is valid, apply conversion
97                    let phys = self
98                        .block
99                        .apply_conversion_value(decoded.value, self.mmap)?;
100                    out.push(Some(phys));
101                } else {
102                    // Value is invalid according to invalidation bit
103                    out.push(None);
104                }
105            } else {
106                // Decoding failed
107                out.push(None);
108            }
109        }
110        Ok(out)
111    }
112
113    /// Get the channel block (for internal use)
114    pub fn block(&self) -> &ChannelBlock {
115        self.block
116    }
117}