emotibit_data/
types.rs

1//! Types for this crate
2use anyhow::{anyhow, Result};
3use csv::StringRecord;
4use itertools::Itertools;
5use std::str::FromStr;
6
7/// Returns CSV values
8pub trait Csv {
9    fn csv(&self) -> Vec<StringRecord>;
10}
11
12impl Csv for StringRecord {
13    fn csv(&self) -> Vec<StringRecord> {
14        vec![self.clone()]
15    }
16}
17
18/// Emotibit Data Packet
19#[derive(Debug, Clone)]
20pub struct DataPacket {
21    /// Local timestamp on a host PC
22    pub host_timestamp: f64,
23    /// Milliseconds since start of EmotiBit
24    pub emotibit_timestamp: f64,
25    /// Packet count since start of EmotiBit
26    pub packet_id: u32,
27    /// Number of data points in the payload
28    pub data_points: u8,
29    /// Version of packet protocol
30    pub version: u8,
31    /// Data reliability score out of 100, currently always 100
32    pub reliability: u8,
33    /// Type of data being sent and its payload
34    pub data_type: DataType,
35}
36
37impl Csv for DataPacket {
38    fn csv(&self) -> Vec<StringRecord> {
39        let mut vec = Vec::new();
40        let payload = Self::parse_data_type(&self.data_type, self.data_type.payload());
41        for p in payload {
42            vec.push(StringRecord::from(vec![
43                self.host_timestamp.to_string(),
44                self.emotibit_timestamp.to_string(),
45                self.packet_id.to_string(),
46                self.data_points.to_string(),
47                self.data_type.as_str().to_owned(),
48                self.version.to_string(),
49                self.reliability.to_string(),
50                p.to_string(),
51            ]));
52        }
53        vec
54    }
55}
56
57impl DataPacket {
58    fn parse_data_type(data_type: &DataType, payload: Vec<String>) -> Vec<String> {
59        use DataType::*;
60        match data_type {
61            TxLcLm(_) | TxTlLc(_) => vec![format!(
62                "{},{}",
63                payload.get(0).unwrap(),
64                payload.get(1).unwrap()
65            )],
66            _ => payload,
67        }
68    }
69    /// Performs linear interpolation based on `TimeSyncMap` and returns a new `DataPacket` with a host timestamp.
70    pub fn inject_host_timestamp(self, map: &TimeSyncMap) -> Self {
71        let timestamp = map.tl0
72            + (map.tl1 - map.tl0) * (self.emotibit_timestamp - map.te0) / (map.te1 - map.te0);
73        DataPacket {
74            host_timestamp: timestamp,
75            emotibit_timestamp: self.emotibit_timestamp,
76            packet_id: self.packet_id,
77            data_points: self.data_points,
78            version: self.version,
79            reliability: self.reliability,
80            data_type: self.data_type,
81        }
82    }
83}
84
85impl TryFrom<&StringRecord> for DataPacket {
86    type Error = anyhow::Error;
87    fn try_from(r: &StringRecord) -> Result<Self, Self::Error> {
88        if let (
89            Some(timestamp),
90            Some(packet_id),
91            Some(data_points),
92            Some(data_type),
93            Some(version),
94            Some(reliability),
95        ) = (r.get(0), r.get(1), r.get(2), r.get(3), r.get(4), r.get(5))
96        {
97            Ok(DataPacket {
98                host_timestamp: f64::NAN,
99                emotibit_timestamp: timestamp.parse()?,
100                packet_id: packet_id.parse()?,
101                data_points: data_points.parse()?,
102                version: version.parse()?,
103                reliability: reliability.parse()?,
104                data_type: get_data_type(r, data_type)?,
105            })
106        } else {
107            Err(anyhow!("Missing Column, record: {:?}", r))
108        }
109    }
110}
111
112impl TryFrom<&str> for DataPacket {
113    type Error = anyhow::Error;
114    fn try_from(str: &str) -> Result<Self, Self::Error> {
115        let r: Vec<&str> = str.split(',').collect();
116        let r = csv::ByteRecord::from(r);
117        let r = &StringRecord::from_byte_record(r)?;
118        r.try_into()
119    }
120}
121
122#[test]
123fn string_to_data() {
124    let input = "1126349,49106,10,PI,1,100,156593,156471,156372,156300,156205,156136,156130,156103,156051,156103";
125    let packet: DataPacket = input.try_into().unwrap();
126    assert_eq!(packet.packet_id, 49106);
127    assert_eq!(packet.data_points, 10);
128}
129
130/// Emotibit data type
131#[derive(Debug, Clone, PartialEq)]
132pub enum DataType {
133    /// EDA- Electrodermal Activity
134    EA(Vec<f32>),
135    /// EDL- Electrodermal Level
136    EL(Vec<f32>),
137    /// EDR- Electrodermal Response (EmotiBit V4+ combines ER into EA signal)
138    ER(Vec<f32>),
139    /// PPG Infrared
140    PI(Vec<u32>),
141    /// PPG Red
142    PR(Vec<u32>),
143    /// PPG Green
144    PG(Vec<u32>),
145    /// Temperature 0
146    T0(Vec<f32>),
147    /// Temperature 1
148    T1(Vec<f32>),
149    /// Temperature via Medical-grade Thermopile (only on EmotiBit MD)
150    TH(Vec<f32>),
151    /// Accelerometer X
152    AX(Vec<f32>),
153    /// Accelerometer Y
154    AY(Vec<f32>),
155    /// Accelerometer Z
156    AZ(Vec<f32>),
157    /// Gyroscope X
158    GX(Vec<f32>),
159    /// Gyroscope Y
160    GY(Vec<f32>),
161    /// Gyroscope Z
162    GZ(Vec<f32>),
163    /// Magnetometer X
164    MX(Vec<i32>),
165    /// Magnetometer Y
166    MY(Vec<i32>),
167    /// Magnetometer Z
168    MZ(Vec<i32>),
169    /// Battery Voltage
170    BV(Vec<f32>),
171    /// Battery Percentage Remaining (B%)
172    BATLV(Vec<u32>),
173    AK(Vec<String>),
174    /// Request Data, TypeTag in Payload
175    RD(Vec<String>),
176    TL(String),
177    TX(Vec<String>),
178    TxTlLc((String, f32)),
179    TxLcLm(Vec<f32>),
180    EM(Vec<String>),
181    /// Heart Rate
182    HR(Vec<i32>),
183    /// Heart Inter-beat Interval
184    BI(Vec<i32>),
185    /// Skin Conductance Response (SCR) Amplitude
186    SA(Vec<f32>),
187    /// Skin Conductance Response (SCR) Frequency
188    SF(Vec<f32>),
189    /// Skin Conductance Response (SCR) Rise Time
190    SR(Vec<f32>),
191    /// User Note
192    UN(Vec<String>),
193    /// LSL Marker/message
194    LM(String),
195    /// Record begin (Include timestamp in Data)
196    RB(String),
197}
198
199impl DataType {
200    pub fn as_str(&self) -> &'static str {
201        use DataType::*;
202        match self {
203            EA(_) => "EA",
204            EL(_) => "EL",
205            ER(_) => "ER",
206            PI(_) => "PI",
207            PR(_) => "PR",
208            PG(_) => "PG",
209            T0(_) => "T0",
210            T1(_) => "T1",
211            TH(_) => "TH",
212            AX(_) => "AX",
213            AY(_) => "AY",
214            AZ(_) => "AZ",
215            GX(_) => "GX",
216            GY(_) => "GY",
217            GZ(_) => "GZ",
218            MX(_) => "MX",
219            MY(_) => "MY",
220            MZ(_) => "MZ",
221            BV(_) => "BV",
222            BATLV(_) => "B%",
223            AK(_) => "AK",
224            RD(_) => "RD",
225            TL(_) => "TL",
226            TX(_) => "TX",
227            TxTlLc(_) => "TX_TL_LC",
228            TxLcLm(_) => "TX_LC_LM",
229            EM(_) => "EM",
230            HR(_) => "HR",
231            BI(_) => "BI",
232            SA(_) => "SA",
233            SF(_) => "SF",
234            SR(_) => "SR",
235            // Computer data TypeTags (sent over reliable channel e.g. Control)
236            UN(_) => "UN",
237            LM(_) => "LM",
238            // Control TypeTags
239            RB(_) => "RB",
240        }
241    }
242
243    pub fn payload(&self) -> Vec<String> {
244        use DataType::*;
245        match self {
246            EA(v) => v.iter().map(|p| p.to_string()).collect(),
247            EL(v) => v.iter().map(|p| p.to_string()).collect(),
248            ER(v) => v.iter().map(|p| p.to_string()).collect(),
249            PI(v) => v.iter().map(|p| p.to_string()).collect(),
250            PR(v) => v.iter().map(|p| p.to_string()).collect(),
251            PG(v) => v.iter().map(|p| p.to_string()).collect(),
252            T0(v) => v.iter().map(|p| p.to_string()).collect(),
253            T1(v) => v.iter().map(|p| p.to_string()).collect(),
254            TH(v) => v.iter().map(|p| p.to_string()).collect(),
255            AX(v) => v.iter().map(|p| p.to_string()).collect(),
256            AY(v) => v.iter().map(|p| p.to_string()).collect(),
257            AZ(v) => v.iter().map(|p| p.to_string()).collect(),
258            GX(v) => v.iter().map(|p| p.to_string()).collect(),
259            GY(v) => v.iter().map(|p| p.to_string()).collect(),
260            GZ(v) => v.iter().map(|p| p.to_string()).collect(),
261            MX(v) => v.iter().map(|p| p.to_string()).collect(),
262            MY(v) => v.iter().map(|p| p.to_string()).collect(),
263            MZ(v) => v.iter().map(|p| p.to_string()).collect(),
264            BV(v) => v.iter().map(|p| p.to_string()).collect(),
265            BATLV(v) => v.iter().map(|p| p.to_string()).collect(),
266            AK(sv) => sv.to_vec(),
267            RD(sv) => sv.to_vec(),
268            TL(s) => vec![s.to_owned()],
269            TX(sv) => sv.to_vec(),
270            TxTlLc((s, f)) => vec![s.to_owned(), f.to_string()],
271            TxLcLm(v) => v.iter().map(|p| p.to_string()).collect(),
272            EM(sv) => sv.to_vec(),
273            HR(v) => v.iter().map(|p| p.to_string()).collect(),
274            BI(v) => v.iter().map(|p| p.to_string()).collect(),
275            SA(v) => v.iter().map(|p| p.to_string()).collect(),
276            SF(v) => v.iter().map(|p| p.to_string()).collect(),
277            SR(v) => v.iter().map(|p| p.to_string()).collect(),
278            // // Computer data TypeTags (sent over reliable channel e.g. Control)
279            UN(sv) => sv.to_vec(),
280            LM(s) => vec![s.to_owned()],
281            // // Control TypeTags
282            RB(s) => vec![s.to_owned()],
283        }
284    }
285}
286
287fn get_data_type(record: &StringRecord, type_str: &str) -> Result<DataType> {
288    let skip_to_payload = 6_usize;
289    match type_str {
290        "RB" => Ok(DataType::RB(to_string(record, skip_to_payload))),
291        "AK" => Ok(DataType::AK(to_string_vec(record, skip_to_payload))),
292        "EA" => Ok(DataType::EA(to_vec::<f32>(record, skip_to_payload)?)),
293        "EL" => Ok(DataType::EL(to_vec::<f32>(record, skip_to_payload)?)),
294        "ER" => Ok(DataType::ER(to_vec::<f32>(record, skip_to_payload)?)),
295        "PI" => Ok(DataType::PI(to_vec::<u32>(record, skip_to_payload)?)),
296        "PR" => Ok(DataType::PR(to_vec::<u32>(record, skip_to_payload)?)),
297        "PG" => Ok(DataType::PG(to_vec::<u32>(record, skip_to_payload)?)),
298        "T0" => Ok(DataType::T0(to_vec::<f32>(record, skip_to_payload)?)),
299        "T1" => Ok(DataType::T1(to_vec::<f32>(record, skip_to_payload)?)),
300        "TH" => Ok(DataType::TH(to_vec::<f32>(record, skip_to_payload)?)),
301        "AX" => Ok(DataType::AX(to_vec::<f32>(record, skip_to_payload)?)),
302        "AY" => Ok(DataType::AY(to_vec::<f32>(record, skip_to_payload)?)),
303        "AZ" => Ok(DataType::AZ(to_vec::<f32>(record, skip_to_payload)?)),
304        "GX" => Ok(DataType::GX(to_vec::<f32>(record, skip_to_payload)?)),
305        "GY" => Ok(DataType::GY(to_vec::<f32>(record, skip_to_payload)?)),
306        "GZ" => Ok(DataType::GZ(to_vec::<f32>(record, skip_to_payload)?)),
307        "MX" => Ok(DataType::MX(to_vec::<i32>(record, skip_to_payload)?)),
308        "MY" => Ok(DataType::MY(to_vec::<i32>(record, skip_to_payload)?)),
309        "MZ" => Ok(DataType::MZ(to_vec::<i32>(record, skip_to_payload)?)),
310        "BI" => Ok(DataType::BI(to_vec::<i32>(record, skip_to_payload)?)),
311        "SA" => Ok(DataType::SA(to_vec::<f32>(record, skip_to_payload)?)),
312        "SF" => Ok(DataType::SF(to_vec::<f32>(record, skip_to_payload)?)),
313        "SR" => Ok(DataType::SR(to_vec::<f32>(record, skip_to_payload)?)),
314        "BV" => Ok(DataType::BV(to_vec::<f32>(record, skip_to_payload)?)),
315        "HR" => Ok(DataType::HR(to_vec::<i32>(record, skip_to_payload)?)),
316        "B%" => Ok(DataType::BATLV(to_vec::<u32>(record, skip_to_payload)?)),
317        "RD" => Ok(DataType::RD(to_string_vec(record, skip_to_payload))),
318        "UN" => Ok(DataType::UN(to_string_vec(record, skip_to_payload))),
319        "EM" => Ok(DataType::EM(to_string_vec(record, skip_to_payload))),
320        "TL" => Ok(DataType::TL(to_string(record, skip_to_payload))),
321        "TX" => Ok(DataType::TX(to_string_vec(record, skip_to_payload))),
322        "LM" => Ok(DataType::LM(to_string(record, skip_to_payload))),
323        // TODO: add data types
324        _ => Err(anyhow!("Unknown Type: {}, {:?}", type_str, record)),
325    }
326}
327
328// Helpers
329fn to_string(record: &StringRecord, index: usize) -> String {
330    record.iter().skip(index).join(",")
331}
332
333fn to_vec<T>(record: &StringRecord, index_from: usize) -> Result<Vec<T>>
334where
335    T: num::Num + FromStr,
336    <T as std::str::FromStr>::Err: std::fmt::Debug,
337{
338    let mut errors = vec![];
339    let vec = record
340        .iter()
341        .skip(index_from)
342        .map(|x| x.to_string().trim().parse::<T>())
343        .filter_map(|r| r.map_err(|e| errors.push(e)).ok())
344        .collect::<Vec<T>>();
345    if errors.is_empty() {
346        Ok(vec)
347    } else {
348        Err(anyhow!("Parse to Num Error: {:?}", record))
349    }
350}
351
352fn to_string_vec(record: &StringRecord, index_from: usize) -> Vec<String> {
353    record
354        .iter()
355        .skip(index_from)
356        .map(|str| str.to_owned())
357        .collect()
358}
359
360/// Time Syncs
361#[derive(Debug, Clone)]
362pub struct TimeSync {
363    /// Emotibit local time when RD was sent
364    pub rd: f64,
365    /// Emotibit local time when TS was received
366    pub ts_received: f64,
367    /// Timestamp at the moment TS was sent in `%Y-%m-%d_%H-%M-%S_f` format
368    pub ts_sent: String,
369    /// Emotibit local time when AK was sent
370    pub ak: f64,
371    /// Duration for the round trip Emotibit -> PC -> Emotibit
372    pub round_trip: f64,
373}
374
375impl Csv for TimeSync {
376    fn csv(&self) -> Vec<StringRecord> {
377        vec![StringRecord::from(vec![
378            self.rd.to_string(),
379            self.ts_received.to_string(),
380            self.ts_sent.to_owned(),
381            self.ak.to_string(),
382            self.round_trip.to_string(),
383        ])]
384    }
385}
386
387/// Time Sync Map
388#[derive(Debug)]
389pub struct TimeSyncMap {
390    pub te0: f64,
391    pub te1: f64,
392    pub tl0: f64,
393    pub tl1: f64,
394    pub syncs_received: usize,
395    pub emotibit_start_time: f64,
396    pub emotibit_end_time: f64,
397    pub parse_version: String,
398}
399
400impl Csv for TimeSyncMap {
401    fn csv(&self) -> Vec<StringRecord> {
402        vec![StringRecord::from(vec![
403            self.te0.to_string(),
404            self.te1.to_string(),
405            self.tl0.to_string(),
406            self.tl1.to_string(),
407            self.syncs_received.to_string(),
408            self.emotibit_start_time.to_string(),
409            self.emotibit_end_time.to_string(),
410            self.parse_version.to_owned(),
411        ])]
412    }
413}