1pub mod clock;
12pub mod config;
13pub mod inlet;
14pub mod outlet;
15pub mod postproc;
16pub mod resolver;
17pub mod sample;
18pub mod send_buffer;
19pub mod signal_quality;
20pub mod stream_info;
21pub mod tcp_server;
22pub mod time_receiver;
23pub mod types;
24pub mod udp_server;
25pub mod xml_dom;
26
27use once_cell::sync::Lazy;
28use tokio::runtime::Runtime;
29
30pub static RUNTIME: Lazy<Runtime> = Lazy::new(|| {
34 tokio::runtime::Builder::new_multi_thread()
35 .worker_threads(4)
36 .enable_all()
37 .build()
38 .expect("failed to create lsl-core tokio runtime")
39});
40
41pub mod prelude {
43 pub use crate::clock::local_clock;
44 pub use crate::inlet::StreamInlet;
45 pub use crate::outlet::StreamOutlet;
46 pub use crate::stream_info::StreamInfo;
47 pub use crate::types::*;
48}
49
50#[cfg(test)]
52mod tests {
53 use super::prelude::*;
54 use std::time::Duration;
55
56 #[test]
57 fn in_process_loopback() {
58 let info = StreamInfo::new(
59 "TestLoopback",
60 "EEG",
61 4,
62 250.0,
63 ChannelFormat::Float32,
64 "test_src_1",
65 );
66 let outlet = StreamOutlet::new(&info, 0, 360);
67 std::thread::sleep(Duration::from_millis(100));
68
69 let inlet = StreamInlet::new(&info, 360, 0, true);
70 inlet.open_stream(10.0).unwrap();
71
72 let data = [1.0f32, 2.0, 3.0, 4.0];
73 outlet.push_sample_f(&data, 0.0, true);
74
75 let mut buf = [0.0f32; 4];
76 let ts = inlet.pull_sample_f(&mut buf, 5.0).unwrap();
77 assert!(ts > 0.0);
78 assert_eq!(buf, data);
79 }
80
81 #[test]
82 fn xml_dom_operations() {
83 let info = StreamInfo::new("XMLTest", "EEG", 2, 250.0, ChannelFormat::Float32, "");
84 let desc = info.desc();
85
86 let channels = desc.append_child("channels");
87 let ch1 = channels.append_child("channel");
88 ch1.append_child_value("label", "C3");
89 ch1.append_child_value("unit", "microvolts");
90 let ch2 = channels.append_child("channel");
91 ch2.append_child_value("label", "C4");
92 ch2.append_child_value("unit", "microvolts");
93
94 let channels_read = desc.child("channels");
95 assert!(!channels_read.is_empty());
96 let ch1_read = channels_read.child("channel");
97 assert_eq!(ch1_read.child_value("label"), "C3");
98 assert_eq!(ch1_read.child_value("unit"), "microvolts");
99 let ch2_read = ch1_read.next_sibling_named("channel");
100 assert_eq!(ch2_read.child_value("label"), "C4");
101 }
102
103 #[test]
104 fn query_matching_xpath() {
105 let info = StreamInfo::new("MyEEG", "EEG", 8, 250.0, ChannelFormat::Float32, "src42");
106
107 assert!(info.matches_query("name='MyEEG'"));
109 assert!(info.matches_query("type='EEG'"));
110 assert!(!info.matches_query("name='Other'"));
111
112 assert!(info.matches_query("name='MyEEG' and type='EEG'"));
114 assert!(!info.matches_query("name='MyEEG' and type='Markers'"));
115
116 assert!(info.matches_query("name='MyEEG' or name='Other'"));
118 assert!(info.matches_query("name='Nope' or type='EEG'"));
119 assert!(!info.matches_query("name='A' or name='B'"));
120
121 assert!(info.matches_query("name!='Other'"));
123 assert!(!info.matches_query("name!='MyEEG'"));
124
125 assert!(info.matches_query("channel_count>4"));
127 assert!(!info.matches_query("channel_count>10"));
128 assert!(info.matches_query("channel_count>=8"));
129 assert!(info.matches_query("channel_count<100"));
130 assert!(info.matches_query("channel_count<=8"));
131 assert!(!info.matches_query("channel_count<8"));
132 assert!(info.matches_query("nominal_srate>100"));
133
134 assert!(info.matches_query("starts-with(name,'My')"));
136 assert!(!info.matches_query("starts-with(name,'Oth')"));
137
138 assert!(info.matches_query("contains(name,'EEG')"));
140 assert!(info.matches_query("contains(type,'EE')"));
141 assert!(!info.matches_query("contains(name,'XYZ')"));
142
143 assert!(info.matches_query("not(name='Other')"));
145 assert!(!info.matches_query("not(name='MyEEG')"));
146 assert!(info.matches_query("not(contains(name,'ZZZ'))"));
147
148 assert!(info
150 .matches_query("starts-with(name,'My') and channel_count>4 and not(type='Markers')"));
151
152 assert!(info.matches_query(""));
154 }
155
156 #[test]
157 fn protocol_100_serialization() {
158 use crate::sample::Sample;
159 use std::io::Cursor;
160
161 let fmt = ChannelFormat::Float32;
162 let nch = 4u32;
163
164 let mut sample = Sample::new(fmt, nch, 0.0);
166 sample.timestamp = 123.456;
167 sample.assign_f32(&[1.0, 2.0, 3.0, 4.0]);
168
169 let mut buf = Vec::new();
170 sample.serialize_100(&mut buf);
171
172 assert_eq!(buf.len(), 8 + 16);
174
175 let mut cursor = Cursor::new(&buf);
177 let decoded = Sample::deserialize_100(&mut cursor, fmt, nch).unwrap();
178 assert!((decoded.timestamp - 123.456).abs() < 1e-10);
179
180 let mut out = [0.0f32; 4];
181 decoded.retrieve_f32(&mut out);
182 assert_eq!(out, [1.0, 2.0, 3.0, 4.0]);
183
184 let sfmt = ChannelFormat::String;
186 let mut ssample = Sample::new(sfmt, 2, 0.0);
187 ssample.timestamp = 99.0;
188 ssample.assign_strings(&["hello".to_string(), "world".to_string()]);
189
190 let mut sbuf = Vec::new();
191 ssample.serialize_100(&mut sbuf);
192
193 let mut scursor = Cursor::new(&sbuf);
194 let sdecoded = Sample::deserialize_100(&mut scursor, sfmt, 2).unwrap();
195 assert!((sdecoded.timestamp - 99.0).abs() < 1e-10);
196 let strings = sdecoded.retrieve_strings();
197 assert_eq!(strings, vec!["hello", "world"]);
198
199 let mut tp = Sample::new(fmt, nch, 0.0);
201 tp.assign_test_pattern(4);
202 let mut tbuf = Vec::new();
203 tp.serialize_100(&mut tbuf);
204 let mut tcursor = Cursor::new(&tbuf);
205 let tdecoded = Sample::deserialize_100(&mut tcursor, fmt, nch).unwrap();
206 assert_eq!(tp, tdecoded);
207 }
208}