1#![deny(unsafe_code)]
2pub mod ctf;
16#[cfg(feature = "fiff")]
17pub mod data;
18pub mod headshape;
19pub mod metadata;
20
21use bids_core::error::Result;
22use bids_core::file::BidsFile;
23use bids_layout::BidsLayout;
24pub use ctf::{CtfHeader, read_ctf_header};
25#[cfg(feature = "fiff")]
26pub use data::{MegData, read_fiff};
27pub use headshape::{DigPoint, PointKind, read_headshape_pos};
28pub use metadata::MegMetadata;
29
30pub struct MegLayout<'a> {
31 layout: &'a BidsLayout,
32}
33impl<'a> MegLayout<'a> {
34 pub fn new(layout: &'a BidsLayout) -> Self {
35 Self { layout }
36 }
37 pub fn get_meg_files(&self) -> Result<Vec<BidsFile>> {
38 self.layout.get().suffix("meg").collect()
39 }
40 pub fn get_meg_files_for_subject(&self, s: &str) -> Result<Vec<BidsFile>> {
41 self.layout.get().suffix("meg").subject(s).collect()
42 }
43 pub fn get_meg_files_for_task(&self, t: &str) -> Result<Vec<BidsFile>> {
44 self.layout.get().suffix("meg").task(t).collect()
45 }
46 pub fn get_channels(&self, f: &BidsFile) -> Result<Option<Vec<bids_eeg::Channel>>> {
47 bids_core::try_read_companion(&f.companion("channels", "tsv"), bids_eeg::read_channels_tsv)
48 }
49 pub fn get_events(&self, f: &BidsFile) -> Result<Option<Vec<bids_eeg::EegEvent>>> {
50 bids_core::try_read_companion(&f.companion("events", "tsv"), bids_eeg::read_events_tsv)
51 }
52 pub fn get_headshape_path(&self, f: &BidsFile) -> Option<std::path::PathBuf> {
54 let p = f.companion("headshape", "pos");
55 if p.exists() { Some(p) } else { None }
56 }
57
58 pub fn get_headshape(&self, f: &BidsFile) -> Result<Option<Vec<DigPoint>>> {
60 let p = f.companion("headshape", "pos");
61 if p.exists() {
62 Ok(Some(read_headshape_pos(&p)?))
63 } else {
64 Ok(None)
65 }
66 }
67 pub fn get_coordsystem(&self, f: &BidsFile) -> Result<Option<bids_eeg::CoordinateSystem>> {
68 let p = f.companion("coordsystem", "json");
69 if p.exists() {
70 Ok(Some(bids_eeg::CoordinateSystem::from_file(&p)?))
71 } else {
72 Ok(None)
73 }
74 }
75 pub fn get_metadata(&self, f: &BidsFile) -> Result<Option<MegMetadata>> {
76 Ok(self.layout.get_metadata(&f.path)?.deserialize_as())
77 }
78
79 #[cfg(feature = "fiff")]
83 pub fn read_data(&self, f: &BidsFile) -> Result<MegData> {
84 read_fiff(&f.path)
85 }
86 pub fn get_all_channels_files(&self) -> Result<Vec<BidsFile>> {
87 self.layout
88 .get()
89 .suffix("channels")
90 .datatype("meg")
91 .extension("tsv")
92 .collect()
93 }
94 pub fn get_all_events_files(&self) -> Result<Vec<BidsFile>> {
95 self.layout
96 .get()
97 .suffix("events")
98 .datatype("meg")
99 .extension("tsv")
100 .collect()
101 }
102 pub fn get_meg_subjects(&self) -> Result<Vec<String>> {
103 self.layout.get().suffix("meg").return_unique("subject")
104 }
105 pub fn get_meg_tasks(&self) -> Result<Vec<String>> {
106 self.layout.get().suffix("meg").return_unique("task")
107 }
108 pub fn summary(&self) -> Result<MegSummary> {
109 let files = self.get_meg_files()?;
110 let subjects = self.get_meg_subjects()?;
111 let tasks = self.get_meg_tasks()?;
112 let sf = files
113 .first()
114 .and_then(|f| self.get_metadata(f).ok().flatten())
115 .map(|m| m.sampling_frequency);
116 let ch = files
117 .first()
118 .and_then(|f| self.get_channels(f).ok().flatten())
119 .map(|c| c.len());
120 Ok(MegSummary {
121 n_subjects: subjects.len(),
122 n_recordings: files.len(),
123 subjects,
124 tasks,
125 sampling_frequency: sf,
126 channel_count: ch,
127 })
128 }
129}
130#[derive(Debug, Clone)]
132pub struct MegSummary {
133 pub n_subjects: usize,
134 pub n_recordings: usize,
135 pub subjects: Vec<String>,
136 pub tasks: Vec<String>,
137 pub sampling_frequency: Option<f64>,
138 pub channel_count: Option<usize>,
139}
140impl std::fmt::Display for MegSummary {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 writeln!(
143 f,
144 "MEG Summary: {} subjects, {} recordings, tasks: {:?}",
145 self.n_subjects, self.n_recordings, self.tasks
146 )?;
147 if let Some(sf) = self.sampling_frequency {
148 writeln!(f, " Sampling: {sf} Hz")?;
149 }
150 if let Some(ch) = self.channel_count {
151 writeln!(f, " Channels: {ch}")?;
152 }
153 Ok(())
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 #[test]
161 fn test_meg_metadata() {
162 let json = r#"{"SamplingFrequency":2400,"MEGChannelCount":274,"MEGREFChannelCount":26,"DewarPosition":"Upright"}"#;
163 let md: MegMetadata = serde_json::from_str(json).unwrap();
164 assert_eq!(md.sampling_frequency, 2400.0);
165 assert_eq!(md.meg_channel_count, Some(274));
166 assert_eq!(md.dewar_position.as_deref(), Some("Upright"));
167 }
168}