neurosky 0.0.1

Rust library and TUI for NeuroSky MindWave EEG headsets via the ThinkGear serial protocol
Documentation
//! Protocol constant, decode, and parser tests (no hardware required).
use neurosky::types::*;
use neurosky::parser::Parser;
use neurosky::verify::{make_packet, verify_packet};

// ── Constants ─────────────────────────────────────────────────────────────────

#[test] fn test_sync_byte()           { assert_eq!(SYNC_BYTE, 0xAA); }
#[test] fn test_raw_sampling_rate()   { assert_eq!(RAW_SAMPLING_RATE, 512); }
#[test] fn test_asic_sampling_rate()  { assert_eq!(ASIC_SAMPLING_RATE, 1); }
#[test] fn test_max_payload_length()  { assert_eq!(MAX_PAYLOAD_LENGTH, 169); }
#[test] fn test_cmd_autoconnect()     { assert_eq!(CMD_AUTOCONNECT, 0xC2); }

// ── Packet codes ──────────────────────────────────────────────────────────────

#[test] fn test_single_byte_codes() {
    assert_eq!(CODE_POOR_SIGNAL, 0x02);
    assert_eq!(CODE_ATTENTION,   0x04);
    assert_eq!(CODE_MEDITATION,  0x05);
    assert_eq!(CODE_BLINK,       0x16);
}

#[test] fn test_multi_byte_codes() {
    assert_eq!(CODE_RAW_VALUE, 0x80);
    assert_eq!(CODE_ASIC_EEG,  0x83);
}

#[test] fn test_connection_status_codes() {
    assert_eq!(CODE_HEADSET_CONNECTED,    0xD0);
    assert_eq!(CODE_HEADSET_NOT_FOUND,    0xD1);
    assert_eq!(CODE_HEADSET_DISCONNECTED, 0xD2);
    assert_eq!(CODE_REQUEST_DENIED,       0xD3);
    assert_eq!(CODE_STANDBY,              0xD4);
}

// ── Band names ────────────────────────────────────────────────────────────────

#[test] fn test_band_names_length() { assert_eq!(BAND_NAMES.len(), 8); }
#[test] fn test_band_names_first()  { assert_eq!(BAND_NAMES[0], "Delta"); }
#[test] fn test_band_names_last()   { assert_eq!(BAND_NAMES[7], "Mid Gamma"); }
#[test] fn test_band_names_all() {
    assert_eq!(BAND_NAMES, [
        "Delta", "Theta", "Low Alpha", "High Alpha",
        "Low Beta", "High Beta", "Low Gamma", "Mid Gamma",
    ]);
}

// ── decode_raw_value ──────────────────────────────────────────────────────────

#[test] fn test_decode_raw_positive() { assert_eq!(decode_raw_value(&[0x01, 0x00]), Some(256)); }
#[test] fn test_decode_raw_negative() { assert_eq!(decode_raw_value(&[0xFF, 0xFE]), Some(-2)); }
#[test] fn test_decode_raw_zero()     { assert_eq!(decode_raw_value(&[0x00, 0x00]), Some(0)); }
#[test] fn test_decode_raw_min()      { assert_eq!(decode_raw_value(&[0x80, 0x00]), Some(i16::MIN)); }
#[test] fn test_decode_raw_max()      { assert_eq!(decode_raw_value(&[0x7F, 0xFF]), Some(i16::MAX)); }
#[test] fn test_decode_raw_short()    { assert_eq!(decode_raw_value(&[0x00]),       None); }
#[test] fn test_decode_raw_empty()    { assert_eq!(decode_raw_value(&[]),            None); }

// ── decode_asic_eeg ───────────────────────────────────────────────────────────

#[test] fn test_decode_asic_delta_and_mid_gamma() {
    let mut data = [0u8; 24];
    data[0] = 0; data[1] = 0x03; data[2] = 0xE8; // delta = 1000
    data[21] = 0; data[22] = 0x1F; data[23] = 0x40; // mid_gamma = 8000
    let eeg = decode_asic_eeg(&data).unwrap();
    assert_eq!(eeg.delta, 1000);
    assert_eq!(eeg.mid_gamma, 8000);
}

#[test] fn test_decode_asic_all_bands() {
    let mut data = [0u8; 24];
    for i in 0..8u32 {
        let v = (i + 1) * 100;
        data[i as usize * 3]     = (v >> 16) as u8;
        data[i as usize * 3 + 1] = (v >> 8)  as u8;
        data[i as usize * 3 + 2] =  v         as u8;
    }
    let eeg = decode_asic_eeg(&data).unwrap();
    assert_eq!(eeg.delta,      100);
    assert_eq!(eeg.theta,      200);
    assert_eq!(eeg.low_alpha,  300);
    assert_eq!(eeg.high_alpha, 400);
    assert_eq!(eeg.low_beta,   500);
    assert_eq!(eeg.high_beta,  600);
    assert_eq!(eeg.low_gamma,  700);
    assert_eq!(eeg.mid_gamma,  800);
}

#[test] fn test_decode_asic_short() { assert!(decode_asic_eeg(&[0u8; 23]).is_none()); }
#[test] fn test_decode_asic_empty() { assert!(decode_asic_eeg(&[]).is_none()); }

// ── AsicEeg helpers ───────────────────────────────────────────────────────────

#[test] fn test_asic_as_array() {
    let eeg = AsicEeg {
        delta: 1, theta: 2, low_alpha: 3, high_alpha: 4,
        low_beta: 5, high_beta: 6, low_gamma: 7, mid_gamma: 8,
    };
    assert_eq!(eeg.as_array(), [1, 2, 3, 4, 5, 6, 7, 8]);
}

#[test] fn test_asic_default() {
    let eeg = AsicEeg::default();
    assert_eq!(eeg.as_array(), [0u32; 8]);
}

// ── Parser ────────────────────────────────────────────────────────────────────

#[test] fn test_parse_attention() {
    let data = make_packet(&[CODE_ATTENTION, 75]);
    let packets = Parser::new().parse(&data);
    assert!(matches!(packets[0], Packet::Attention(75)));
}

#[test] fn test_parse_meditation() {
    let data = make_packet(&[CODE_MEDITATION, 50]);
    let packets = Parser::new().parse(&data);
    assert!(matches!(packets[0], Packet::Meditation(50)));
}

#[test] fn test_parse_poor_signal() {
    let data = make_packet(&[CODE_POOR_SIGNAL, 200]);
    let packets = Parser::new().parse(&data);
    assert!(matches!(packets[0], Packet::PoorSignal(200)));
}

#[test] fn test_parse_blink() {
    let data = make_packet(&[CODE_BLINK, 128]);
    let packets = Parser::new().parse(&data);
    assert!(matches!(packets[0], Packet::Blink(128)));
}

#[test] fn test_parse_raw_value_positive() {
    let data = make_packet(&[CODE_RAW_VALUE, 0x02, 0x01, 0x00]);
    let packets = Parser::new().parse(&data);
    assert!(matches!(packets[0], Packet::RawValue(256)));
}

#[test] fn test_parse_raw_value_negative() {
    let data = make_packet(&[CODE_RAW_VALUE, 0x02, 0xFF, 0xFE]);
    let packets = Parser::new().parse(&data);
    assert!(matches!(packets[0], Packet::RawValue(-2)));
}

#[test] fn test_parse_headset_connected() {
    let data = make_packet(&[CODE_HEADSET_CONNECTED, 0x02, 0xAB, 0xCD]);
    let packets = Parser::new().parse(&data);
    assert!(matches!(packets[0], Packet::HeadsetConnected(0xABCD)));
}

#[test] fn test_parse_headset_not_found() {
    let data = make_packet(&[CODE_HEADSET_NOT_FOUND, 0x00]);
    let packets = Parser::new().parse(&data);
    assert!(matches!(packets[0], Packet::HeadsetNotFound));
}

#[test] fn test_parse_headset_disconnected() {
    let data = make_packet(&[CODE_HEADSET_DISCONNECTED, 0x00]);
    let packets = Parser::new().parse(&data);
    assert!(matches!(packets[0], Packet::HeadsetDisconnected));
}

#[test] fn test_parse_standby() {
    let data = make_packet(&[CODE_STANDBY, 0x00]);
    let packets = Parser::new().parse(&data);
    assert!(matches!(packets[0], Packet::Standby));
}

#[test] fn test_parse_request_denied() {
    let data = make_packet(&[CODE_REQUEST_DENIED, 0x00]);
    let packets = Parser::new().parse(&data);
    assert!(matches!(packets[0], Packet::RequestDenied));
}

#[test] fn test_bad_checksum_rejected() {
    let mut data = make_packet(&[CODE_ATTENTION, 75]);
    *data.last_mut().unwrap() ^= 0xFF;
    assert!(Parser::new().parse(&data).is_empty());
}

#[test] fn test_noise_before_sync() {
    let mut data = vec![0x00, 0xFF, 0x42];
    data.extend_from_slice(&make_packet(&[CODE_ATTENTION, 90]));
    let packets = Parser::new().parse(&data);
    assert_eq!(packets.len(), 1);
    assert!(matches!(packets[0], Packet::Attention(90)));
}

#[test] fn test_multiple_packets_in_stream() {
    let mut data = make_packet(&[CODE_ATTENTION, 80]);
    data.extend_from_slice(&make_packet(&[CODE_MEDITATION, 60]));
    let packets = Parser::new().parse(&data);
    assert_eq!(packets.len(), 2);
    assert!(matches!(packets[0], Packet::Attention(80)));
    assert!(matches!(packets[1], Packet::Meditation(60)));
}

// ── verify_packet ─────────────────────────────────────────────────────────────

#[test] fn test_verify_packet_valid() {
    let payload = [CODE_ATTENTION, 75];
    let pkt = make_packet(&payload);
    assert_eq!(verify_packet(&pkt).unwrap(), &payload);
}

#[test] fn test_verify_packet_bad_checksum() {
    let mut pkt = make_packet(&[CODE_ATTENTION, 75]);
    *pkt.last_mut().unwrap() ^= 0xFF;
    assert!(verify_packet(&pkt).is_err());
}