use crate::channel_config::GainTracker;
use crate::sample::{now, Sample};
pub const START_BYTE: u8 = 0xA0;
pub const END_BYTE_STANDARD: u8 = 0xC0; pub const END_BYTE_ANALOG: u8 = 0xC1; pub const END_BYTE_MAX: u8 = 0xC6;
const EEG_REF: f64 = 4.5 / 8_388_607.0 * 1_000_000.0;
pub const GANGLION_EEG_SCALE: f64 = 1.2 * 1_000_000.0 / (8_388_607.0 * 1.5 * 51.0);
const ACCEL_SCALE: f64 = 0.002 / 16.0;
pub fn cast_24bit_to_i32(b: &[u8]) -> i32 {
let v = ((b[0] as i32) << 16) | ((b[1] as i32) << 8) | (b[2] as i32);
if v & 0x0080_0000 != 0 { v | !0x00FF_FFFF } else { v }
}
pub fn cast_16bit_to_i32(b: &[u8]) -> i32 {
let v = ((b[0] as i32) << 8) | (b[1] as i32);
if v & 0x8000 != 0 { v | !0xFFFF } else { v }
}
fn byte_to_bits(b: u8, out: &mut [u8]) {
for i in 0..8 {
out[i] = (b >> (7 - i)) & 1;
}
}
fn bytes_to_bits_160(data: &[u8]) -> [u8; 160] {
let mut bits = [0u8; 160];
for (i, &b) in data.iter().enumerate().take(20) {
byte_to_bits(b, &mut bits[i * 8..i * 8 + 8]);
}
bits
}
fn bits_to_i32(bits: &[u8], offset: usize, n: usize) -> i32 {
let mut result: i32 = 0;
for i in 0..n {
result = (result << 1) | (bits[offset + i] as i32);
}
if n < 32 && bits[offset] == 1 {
result |= !((1i32 << n) - 1);
}
result
}
pub fn decode_cyton(body: &[u8; 32], gains: &GainTracker, num_ch: usize) -> Option<Sample> {
let end_byte = body[31];
if !(END_BYTE_STANDARD..=END_BYTE_MAX).contains(&end_byte) {
return None;
}
let mut sample = Sample::zeroed(num_ch);
sample.sample_num = body[0];
sample.end_byte = end_byte;
sample.timestamp = now();
sample.aux_bytes = body[25..31].try_into().ok()?;
for i in 0..num_ch.min(8) {
let raw = cast_24bit_to_i32(&body[1 + i * 3..]);
let scale = EEG_REF / gains.gain_for(i);
sample.eeg[i] = scale * raw as f64;
}
let aux = &body[25..31];
if end_byte == END_BYTE_STANDARD {
let ax = cast_16bit_to_i32(&aux[0..2]);
let ay = cast_16bit_to_i32(&aux[2..4]);
let az = cast_16bit_to_i32(&aux[4..6]);
if ax != 0 {
sample.accel = Some([
ACCEL_SCALE * ax as f64,
ACCEL_SCALE * ay as f64,
ACCEL_SCALE * az as f64,
]);
}
} else if end_byte == END_BYTE_ANALOG {
sample.analog = Some([
cast_16bit_to_i32(&aux[0..2]) as f64,
cast_16bit_to_i32(&aux[2..4]) as f64,
cast_16bit_to_i32(&aux[4..6]) as f64,
]);
}
Some(sample)
}
pub fn decode_cyton_daisy(
body: &[u8; 32],
gains: &GainTracker,
buffer: &mut Option<Sample>,
) -> Option<Sample> {
let end_byte = body[31];
if !(END_BYTE_STANDARD..=END_BYTE_MAX).contains(&end_byte) {
return None;
}
let sample_num = body[0];
let is_daisy_packet = sample_num % 2 == 0;
let aux = &body[25..31];
let accel_from_aux = if end_byte == END_BYTE_STANDARD {
let ax = cast_16bit_to_i32(&aux[0..2]);
let ay = cast_16bit_to_i32(&aux[2..4]);
let az = cast_16bit_to_i32(&aux[4..6]);
if ax != 0 {
Some([ACCEL_SCALE * ax as f64, ACCEL_SCALE * ay as f64, ACCEL_SCALE * az as f64])
} else {
None
}
} else {
None
};
let analog_from_aux = if end_byte == END_BYTE_ANALOG {
Some([
cast_16bit_to_i32(&aux[0..2]) as f64,
cast_16bit_to_i32(&aux[2..4]) as f64,
cast_16bit_to_i32(&aux[4..6]) as f64,
])
} else {
None
};
if is_daisy_packet {
let mut s = Sample::zeroed(16);
s.sample_num = sample_num;
s.end_byte = end_byte;
s.aux_bytes = aux.try_into().unwrap_or([0u8; 6]);
for i in 0..8 {
let raw = cast_24bit_to_i32(&body[1 + i * 3..]);
let scale = EEG_REF / gains.gain_for(i + 8); s.eeg[i + 8] = scale * raw as f64;
}
s.accel = accel_from_aux;
s.analog = analog_from_aux;
*buffer = Some(s);
None
} else {
if let Some(ref mut s) = buffer {
for i in 0..8 {
let raw = cast_24bit_to_i32(&body[1 + i * 3..]);
let scale = EEG_REF / gains.gain_for(i);
s.eeg[i] = scale * raw as f64;
}
if let Some(new_acc) = accel_from_aux {
if let Some(old_acc) = s.accel {
s.accel = Some([
(old_acc[0] + new_acc[0]) * 0.5,
(old_acc[1] + new_acc[1]) * 0.5,
(old_acc[2] + new_acc[2]) * 0.5,
]);
} else {
s.accel = Some(new_acc);
}
}
if let Some(new_an) = analog_from_aux {
if let Some(old_an) = s.analog {
s.analog = Some([
(old_an[0] + new_an[0]) * 0.5,
(old_an[1] + new_an[1]) * 0.5,
(old_an[2] + new_an[2]) * 0.5,
]);
} else {
s.analog = Some(new_an);
}
}
s.timestamp = now();
buffer.take()
} else {
None
}
}
}
#[derive(Debug, Default)]
pub struct GanglionState {
pub last_data: [f64; 8],
pub accel: [f64; 3],
}
const GANGLION_ACCEL_SCALE: f64 = 0.016;
pub fn decode_ganglion(
data: &[u8],
state: &mut GanglionState,
firmware: u8, ) -> Vec<Sample> {
if data.len() < 2 {
return vec![];
}
let id = data[0];
if id > 200 && id < 206 {
let mut s = Sample::zeroed(4);
s.sample_num = id;
s.timestamp = now();
let ascii_val: String = data[1..].iter()
.take_while(|&&b| b != b'Z')
.map(|&b| b as char)
.collect();
if let Ok(val) = ascii_val.trim().parse::<f64>() {
let mut resist = vec![0.0f64; 5]; let slot = (id % 10) as usize;
if slot > 0 && slot <= 5 {
resist[slot - 1] = val;
}
s.resistance = Some(resist);
}
return vec![s];
}
if id > 200 {
return vec![]; }
let bits = bytes_to_bits_160(data);
if firmware == 2 {
decode_ganglion_fw2(data, &bits, id, state)
} else {
decode_ganglion_fw3(data, &bits, id, state)
}
}
fn decode_ganglion_fw3(
data: &[u8],
bits: &[u8; 160],
id: u8,
state: &mut GanglionState,
) -> Vec<Sample> {
let bits_per_num: usize;
if id < 100 {
match id % 10 {
0 => state.accel[2] = -GANGLION_ACCEL_SCALE * (data[19] as i8) as f64,
1 => state.accel[1] = GANGLION_ACCEL_SCALE * (data[19] as i8) as f64,
2 => state.accel[0] = GANGLION_ACCEL_SCALE * (data[19] as i8) as f64,
_ => {}
}
bits_per_num = 18;
} else {
bits_per_num = 19;
}
let mut values = [0f64; 8];
let shift = 24 - bits_per_num; for (counter, bit_offset) in (8..bits_per_num * 8).step_by(bits_per_num).enumerate().take(8) {
let raw = bits_to_i32(bits, bit_offset, bits_per_num);
values[counter] = (raw << shift) as f64;
}
let mut s1 = Sample::zeroed(4);
s1.sample_num = id;
s1.timestamp = now();
s1.accel = Some(state.accel);
for i in 0..4 {
s1.eeg[i] = GANGLION_EEG_SCALE * values[i];
state.last_data[i] = values[i];
}
let mut s2 = Sample::zeroed(4);
s2.sample_num = id;
s2.timestamp = now();
s2.accel = Some(state.accel);
for i in 0..4 {
s2.eeg[i] = GANGLION_EEG_SCALE * values[i + 4];
state.last_data[i + 4] = values[i + 4];
}
vec![s1, s2]
}
fn decode_ganglion_fw2(
data: &[u8],
bits: &[u8; 160],
id: u8,
state: &mut GanglionState,
) -> Vec<Sample> {
if id == 0 {
state.last_data[0] = state.last_data[4];
state.last_data[1] = state.last_data[5];
state.last_data[2] = state.last_data[6];
state.last_data[3] = state.last_data[7];
for i in 0..4 {
state.last_data[4 + i] = cast_24bit_to_i32(&data[1 + i * 3..]) as f64;
}
let mut s = Sample::zeroed(4);
s.sample_num = 0;
s.timestamp = now();
s.accel = Some(state.accel);
for i in 0..4 {
s.eeg[i] = GANGLION_EEG_SCALE * state.last_data[4 + i];
}
return vec![s];
}
let bits_per_num: usize;
if id >= 1 && id <= 100 {
match id % 10 {
0 => state.accel[2] = -GANGLION_ACCEL_SCALE * (data[19] as i8) as f64,
1 => state.accel[1] = GANGLION_ACCEL_SCALE * (data[19] as i8) as f64,
2 => state.accel[0] = GANGLION_ACCEL_SCALE * (data[19] as i8) as f64,
_ => {}
}
bits_per_num = 18;
} else {
bits_per_num = 19;
}
let mut delta = [0f64; 8];
for (counter, bit_offset) in (8..bits_per_num * 8).step_by(bits_per_num).enumerate().take(8) {
delta[counter] = bits_to_i32(bits, bit_offset, bits_per_num) as f64;
}
for i in 0..4 {
state.last_data[i] = state.last_data[i + 4] - delta[i];
}
for i in 0..4 {
state.last_data[i + 4] = state.last_data[i] - delta[i + 4];
}
let mut s1 = Sample::zeroed(4);
s1.sample_num = id;
s1.timestamp = now();
s1.accel = Some(state.accel);
for i in 0..4 {
s1.eeg[i] = GANGLION_EEG_SCALE * state.last_data[i];
}
let mut s2 = Sample::zeroed(4);
s2.sample_num = id;
s2.timestamp = now();
s2.accel = Some(state.accel);
for i in 0..4 {
s2.eeg[i] = GANGLION_EEG_SCALE * state.last_data[i + 4];
}
vec![s1, s2]
}