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}