Skip to main content

brainbit/
types.rs

1//! FFI types matching `cmn_type.h` from the NeuroSDK2 C API.
2//!
3//! These are `#[repr(C)]` structs and enums that are ABI-compatible with
4//! the `neurosdk2` shared library on Windows, Linux, and macOS.
5
6#![allow(non_camel_case_types)]
7
8use std::ffi::c_void;
9use std::fmt;
10
11// ── Constants ────────────────────────────────────────────────────────────────
12
13pub const ERR_MSG_LEN: usize = 512;
14pub const SENSOR_NAME_LEN: usize = 256;
15pub const SENSOR_ADR_LEN: usize = 128;
16pub const SENSOR_SN_LEN: usize = 128;
17pub const SENSOR_CHANNEL_NAME_LEN: usize = 8;
18pub const NEURO_EEG_MAX_CH_COUNT: usize = 24;
19pub const BRAINBIT2_MAX_CH_COUNT: usize = 8;
20
21// ── Opaque handles ───────────────────────────────────────────────────────────
22
23/// Opaque scanner handle (pointer to C struct).
24pub type SensorScanner = c_void;
25/// Opaque sensor/device handle (pointer to C struct).
26pub type Sensor = c_void;
27
28// Callback listener handles (all opaque pointers).
29pub type SensorsListenerHandle = *mut c_void;
30pub type BattPowerListenerHandle = *mut c_void;
31pub type BattVoltageListenerHandle = *mut c_void;
32pub type SensorStateListenerHandle = *mut c_void;
33pub type BrainBitSignalDataListenerHandle = *mut c_void;
34pub type BrainBitResistDataListenerHandle = *mut c_void;
35pub type BrainBit2SignalDataListenerHandle = *mut c_void;
36pub type BrainBit2ResistDataListenerHandle = *mut c_void;
37pub type MEMSDataListenerHandle = *mut c_void;
38pub type CallibriSignalDataListenerHandle = *mut c_void;
39pub type CallibriRespirationDataListenerHandle = *mut c_void;
40pub type CallibriElectrodeStateListenerHandle = *mut c_void;
41pub type CallibriEnvelopeDataListenerHandle = *mut c_void;
42pub type QuaternionDataListenerHandle = *mut c_void;
43pub type FPGDataListenerHandle = *mut c_void;
44pub type HeadphonesSignalDataListenerHandle = *mut c_void;
45pub type HeadphonesResistDataListenerHandle = *mut c_void;
46pub type Headphones2SignalDataListenerHandle = *mut c_void;
47pub type Headphones2ResistDataListenerHandle = *mut c_void;
48pub type AmpModeListenerHandle = *mut c_void;
49pub type HeadbandSignalDataListenerHandle = *mut c_void;
50pub type HeadbandResistDataListenerHandle = *mut c_void;
51pub type NeuroEEGSignalDataListenerHandle = *mut c_void;
52pub type NeuroEEGResistDataListenerHandle = *mut c_void;
53pub type NeuroEEGSignalResistDataListenerHandle = *mut c_void;
54pub type NeuroEEGSignalRawDataListenerHandle = *mut c_void;
55pub type NeuroEEGFileStreamDataListenerHandle = *mut c_void;
56pub type NeuroEEGSignalProcessParam = *mut c_void;
57pub type StimulModeListenerHandle = *mut c_void;
58pub type PhotoStimulSyncStateListenerHandle = *mut c_void;
59
60// ── OpStatus ─────────────────────────────────────────────────────────────────
61
62/// Status returned by most SDK functions.
63#[repr(C)]
64#[derive(Clone)]
65pub struct OpStatus {
66    pub success: u8,
67    pub error: u32,
68    pub error_msg: [u8; ERR_MSG_LEN],
69}
70
71impl Default for OpStatus {
72    fn default() -> Self {
73        Self {
74            success: 0,
75            error: 0,
76            error_msg: [0u8; ERR_MSG_LEN],
77        }
78    }
79}
80
81impl OpStatus {
82    /// Returns `true` if the operation succeeded.
83    pub fn is_ok(&self) -> bool {
84        self.success != 0
85    }
86
87    /// Extract the error message as a Rust string.
88    pub fn message(&self) -> String {
89        let nul = self.error_msg.iter().position(|&b| b == 0).unwrap_or(ERR_MSG_LEN);
90        String::from_utf8_lossy(&self.error_msg[..nul]).into_owned()
91    }
92}
93
94impl fmt::Debug for OpStatus {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        f.debug_struct("OpStatus")
97            .field("success", &self.success)
98            .field("error", &self.error)
99            .field("error_msg", &self.message())
100            .finish()
101    }
102}
103
104// ── Enums ────────────────────────────────────────────────────────────────────
105
106/// Device family used for scanner filtering and identification.
107#[repr(u8)]
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
109pub enum SensorFamily {
110    Unknown = 0,
111    LECallibri = 1,
112    LEKolibri = 2,
113    LEBrainBit = 3,
114    LEBrainBitBlack = 4,
115    LEHeadPhones = 5,
116    LEHeadPhones2 = 6,
117    LEHeadband = 11,
118    LENeuroEEG = 14,
119    LEBrainBit2 = 18,
120    LEBrainBitPro = 19,
121    LEBrainBitFlex = 20,
122    LEPhotoStim = 21,
123}
124
125/// Sensor feature capabilities.
126#[repr(i8)]
127#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128pub enum SensorFeature {
129    Signal = 0,
130    MEMS = 1,
131    CurrentStimulator = 2,
132    Respiration = 3,
133    Resist = 4,
134    FPG = 5,
135    Envelope = 6,
136    PhotoStimulator = 7,
137    AcousticStimulator = 8,
138    FlashCard = 9,
139    LedChannels = 10,
140    SignalWithResist = 11,
141}
142
143/// Firmware mode.
144#[repr(i8)]
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub enum SensorFirmwareMode {
147    Bootloader = 0,
148    Application = 1,
149}
150
151/// Commands that can be sent to a sensor.
152#[repr(i8)]
153#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154pub enum SensorCommand {
155    StartSignal = 0,
156    StopSignal = 1,
157    StartResist = 2,
158    StopResist = 3,
159    StartMEMS = 4,
160    StopMEMS = 5,
161    StartRespiration = 6,
162    StopRespiration = 7,
163    StartCurrentStimulation = 8,
164    StopCurrentStimulation = 9,
165    EnableMotionAssistant = 10,
166    DisableMotionAssistant = 11,
167    FindMe = 12,
168    StartAngle = 13,
169    StopAngle = 14,
170    CalibrateMEMS = 15,
171    ResetQuaternion = 16,
172    StartEnvelope = 17,
173    StopEnvelope = 18,
174    ResetMotionCounter = 19,
175    CalibrateStimulation = 20,
176    Idle = 21,
177    PowerDown = 22,
178    StartFPG = 23,
179    StopFPG = 24,
180    StartSignalAndResist = 25,
181    StopSignalAndResist = 26,
182    StartPhotoStimulation = 27,
183    StopPhotoStimulation = 28,
184    StartAcousticStimulation = 29,
185    StopAcousticStimulation = 30,
186    FileSystemEnable = 31,
187    FileSystemDisable = 32,
188    FileSystemStreamClose = 33,
189    StartCalibrateSignal = 34,
190    StopCalibrateSignal = 35,
191    PhotoStimEnable = 36,
192    PhotoStimDisable = 37,
193}
194
195/// Sensor parameter identifiers.
196#[repr(i8)]
197#[derive(Debug, Clone, Copy, PartialEq, Eq)]
198pub enum SensorParameter {
199    Name = 0,
200    State = 1,
201    Address = 2,
202    SerialNumber = 3,
203    HardwareFilterState = 4,
204    FirmwareMode = 5,
205    SamplingFrequency = 6,
206    Gain = 7,
207    Offset = 8,
208    ExternalSwitchState = 9,
209    ADCInputState = 10,
210    AccelerometerSens = 11,
211    GyroscopeSens = 12,
212    StimulatorAndMAState = 13,
213    StimulatorParamPack = 14,
214    MotionAssistantParamPack = 15,
215    FirmwareVersion = 16,
216    MEMSCalibrationStatus = 17,
217    MotionCounterParamPack = 18,
218    MotionCounter = 19,
219    BattPower = 20,
220    SensorFamilyParam = 21,
221    SensorMode = 22,
222    IrAmplitude = 23,
223    RedAmplitude = 24,
224    EnvelopeAvgWndSz = 25,
225    EnvelopeDecimation = 26,
226    SamplingFrequencyResist = 27,
227    SamplingFrequencyMEMS = 28,
228    SamplingFrequencyFPG = 29,
229    Amplifier = 30,
230    SensorChannels = 31,
231    SamplingFrequencyResp = 32,
232    SurveyId = 33,
233    FileSystemStatus = 34,
234    FileSystemDiskInfo = 35,
235    ReferentsShort = 36,
236    ReferentsGround = 37,
237    SamplingFrequencyEnvelope = 38,
238    ChannelConfiguration = 39,
239    ElectrodeState = 40,
240    ChannelResistConfiguration = 41,
241    BattVoltage = 42,
242    PhotoStimTimeDefer = 43,
243    PhotoStimSyncState = 44,
244    SensorPhotoStim = 45,
245    StimMode = 46,
246    LedChannels = 47,
247    LedState = 48,
248}
249
250/// Parameter access mode.
251#[repr(i8)]
252#[derive(Debug, Clone, Copy, PartialEq, Eq)]
253pub enum SensorParamAccess {
254    Read = 0,
255    ReadWrite = 1,
256    ReadNotify = 2,
257    Write = 3,
258}
259
260/// Connection state.
261#[repr(i8)]
262#[derive(Debug, Clone, Copy, PartialEq, Eq)]
263pub enum SensorState {
264    InRange = 0,
265    OutOfRange = 1,
266}
267
268/// Sampling frequency presets.
269#[repr(u8)]
270#[derive(Debug, Clone, Copy, PartialEq, Eq)]
271pub enum SensorSamplingFrequency {
272    Hz10 = 0,
273    Hz20 = 1,
274    Hz100 = 2,
275    Hz125 = 3,
276    Hz250 = 4,
277    Hz500 = 5,
278    Hz1000 = 6,
279    Hz2000 = 7,
280    Hz4000 = 8,
281    Hz8000 = 9,
282    Hz10000 = 10,
283    Hz12000 = 11,
284    Hz16000 = 12,
285    Hz24000 = 13,
286    Hz32000 = 14,
287    Hz48000 = 15,
288    Hz64000 = 16,
289    Unsupported = 0xFF,
290}
291
292impl SensorSamplingFrequency {
293    /// Convert to the actual Hz value.
294    pub fn to_hz(self) -> Option<u32> {
295        match self {
296            Self::Hz10 => Some(10),
297            Self::Hz20 => Some(20),
298            Self::Hz100 => Some(100),
299            Self::Hz125 => Some(125),
300            Self::Hz250 => Some(250),
301            Self::Hz500 => Some(500),
302            Self::Hz1000 => Some(1000),
303            Self::Hz2000 => Some(2000),
304            Self::Hz4000 => Some(4000),
305            Self::Hz8000 => Some(8000),
306            Self::Hz10000 => Some(10000),
307            Self::Hz12000 => Some(12000),
308            Self::Hz16000 => Some(16000),
309            Self::Hz24000 => Some(24000),
310            Self::Hz32000 => Some(32000),
311            Self::Hz48000 => Some(48000),
312            Self::Hz64000 => Some(64000),
313            Self::Unsupported => None,
314        }
315    }
316}
317
318/// Signal gain settings.
319#[repr(i8)]
320#[derive(Debug, Clone, Copy, PartialEq, Eq)]
321pub enum SensorGain {
322    Gain1 = 0,
323    Gain2 = 1,
324    Gain3 = 2,
325    Gain4 = 3,
326    Gain6 = 4,
327    Gain8 = 5,
328    Gain12 = 6,
329    Gain24 = 7,
330    Gain5 = 8,
331    Gain2x = 9,
332    Gain4x = 10,
333    Unsupported = 11,
334}
335
336/// Data offset settings.
337#[repr(u8)]
338#[derive(Debug, Clone, Copy, PartialEq, Eq)]
339pub enum SensorDataOffset {
340    Offset0 = 0x00,
341    Offset1 = 0x01,
342    Offset2 = 0x02,
343    Offset3 = 0x03,
344    Offset4 = 0x04,
345    Offset5 = 0x05,
346    Offset6 = 0x06,
347    Offset7 = 0x07,
348    Offset8 = 0x08,
349    Unsupported = 0xFF,
350}
351
352/// Hardware filter presets.
353#[repr(u16)]
354#[derive(Debug, Clone, Copy, PartialEq, Eq)]
355pub enum SensorFilter {
356    HPFBwhLvl1CutoffFreq1Hz = 0,
357    HPFBwhLvl1CutoffFreq5Hz = 1,
358    BSFBwhLvl2CutoffFreq45_55Hz = 2,
359    BSFBwhLvl2CutoffFreq55_65Hz = 3,
360    HPFBwhLvl2CutoffFreq10Hz = 4,
361    LPFBwhLvl2CutoffFreq400Hz = 5,
362    HPFBwhLvl2CutoffFreq80Hz = 6,
363    Unknown = 0xFF,
364}
365
366/// EEG channel identifiers (10-20 system).
367#[repr(u8)]
368#[derive(Debug, Clone, Copy, PartialEq, Eq)]
369pub enum EEGChannelId {
370    Unknown = 0,
371    O1 = 1,
372    P3 = 2,
373    C3 = 3,
374    F3 = 4,
375    Fp1 = 5,
376    T5 = 6,
377    T3 = 7,
378    F7 = 8,
379    F8 = 9,
380    T4 = 10,
381    T6 = 11,
382    Fp2 = 12,
383    F4 = 13,
384    C4 = 14,
385    P4 = 15,
386    O2 = 16,
387    D1 = 17,
388    D2 = 18,
389    OZ = 19,
390    PZ = 20,
391    CZ = 21,
392    FZ = 22,
393    FpZ = 23,
394    D3 = 24,
395    Ref = 25,
396    A1 = 26,
397    A2 = 27,
398    Gnd1 = 28,
399    Gnd2 = 29,
400}
401
402/// EEG channel type.
403#[repr(u8)]
404#[derive(Debug, Clone, Copy, PartialEq, Eq)]
405pub enum EEGChannelType {
406    SingleA1 = 0,
407    SingleA2 = 1,
408    Differential = 2,
409    Ref = 3,
410}
411
412/// BrainBit2 channel mode.
413#[repr(i8)]
414#[derive(Debug, Clone, Copy, PartialEq, Eq)]
415pub enum BrainBit2ChannelMode {
416    Short = 0,
417    Normal = 1,
418}
419
420/// Generator current.
421#[repr(u8)]
422#[derive(Debug, Clone, Copy, PartialEq, Eq)]
423pub enum GenCurrent {
424    GenCurr0nA = 0,
425    GenCurr6nA = 1,
426    GenCurr12nA = 2,
427    GenCurr18nA = 3,
428    GenCurr24nA = 4,
429    GenCurr6uA = 5,
430    GenCurr24uA = 6,
431    Unsupported = 0xFF,
432}
433
434/// Accelerometer sensitivity.
435#[repr(i8)]
436#[derive(Debug, Clone, Copy, PartialEq, Eq)]
437pub enum SensorAccelerometerSensitivity {
438    Sens2g = 0,
439    Sens4g = 1,
440    Sens8g = 2,
441    Sens16g = 3,
442    Unsupported = 4,
443}
444
445/// Gyroscope sensitivity.
446#[repr(i8)]
447#[derive(Debug, Clone, Copy, PartialEq, Eq)]
448pub enum SensorGyroscopeSensitivity {
449    Sens250Grad = 0,
450    Sens500Grad = 1,
451    Sens1000Grad = 2,
452    Sens2000Grad = 3,
453    Unsupported = 4,
454}
455
456// ── Structs ──────────────────────────────────────────────────────────────────
457
458/// Firmware/hardware version information.
459#[repr(C)]
460#[derive(Debug, Clone, Copy)]
461pub struct SensorVersion {
462    pub fw_major: u32,
463    pub fw_minor: u32,
464    pub fw_patch: u32,
465    pub hw_major: u32,
466    pub hw_minor: u32,
467    pub hw_patch: u32,
468    pub ext_major: u32,
469}
470
471/// Discovered device information (from scanner).
472#[repr(C)]
473#[derive(Clone)]
474pub struct SensorInfo {
475    pub sens_family: SensorFamily,
476    pub sens_model: u8,
477    pub name: [u8; SENSOR_NAME_LEN],
478    pub address: [u8; SENSOR_ADR_LEN],
479    pub serial_number: [u8; SENSOR_SN_LEN],
480    pub pairing_required: u8,
481    pub rssi: i16,
482}
483
484impl Default for SensorInfo {
485    fn default() -> Self {
486        Self {
487            sens_family: SensorFamily::Unknown,
488            sens_model: 0,
489            name: [0u8; SENSOR_NAME_LEN],
490            address: [0u8; SENSOR_ADR_LEN],
491            serial_number: [0u8; SENSOR_SN_LEN],
492            pairing_required: 0,
493            rssi: 0,
494        }
495    }
496}
497
498impl SensorInfo {
499    fn buf_to_string(buf: &[u8]) -> String {
500        let nul = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
501        String::from_utf8_lossy(&buf[..nul]).into_owned()
502    }
503
504    /// Device name as a Rust string.
505    pub fn name_str(&self) -> String {
506        Self::buf_to_string(&self.name)
507    }
508
509    /// Device BLE address as a Rust string.
510    pub fn address_str(&self) -> String {
511        Self::buf_to_string(&self.address)
512    }
513
514    /// Device serial number as a Rust string.
515    pub fn serial_number_str(&self) -> String {
516        Self::buf_to_string(&self.serial_number)
517    }
518}
519
520impl fmt::Debug for SensorInfo {
521    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522        f.debug_struct("SensorInfo")
523            .field("family", &self.sens_family)
524            .field("model", &self.sens_model)
525            .field("name", &self.name_str())
526            .field("address", &self.address_str())
527            .field("serial_number", &self.serial_number_str())
528            .field("pairing_required", &self.pairing_required)
529            .field("rssi", &self.rssi)
530            .finish()
531    }
532}
533
534/// Parameter information.
535#[repr(C)]
536#[derive(Debug, Clone, Copy)]
537pub struct ParameterInfo {
538    pub param: SensorParameter,
539    pub param_access: SensorParamAccess,
540}
541
542/// BrainBit (original) 4-channel EEG signal sample.
543#[repr(C)]
544#[derive(Debug, Clone, Copy)]
545pub struct BrainBitSignalData {
546    pub pack_num: u32,
547    pub marker: u8,
548    pub o1: f64,
549    pub o2: f64,
550    pub t3: f64,
551    pub t4: f64,
552}
553
554/// BrainBit (original) 4-channel resistance data.
555#[repr(C)]
556#[derive(Debug, Clone, Copy)]
557pub struct BrainBitResistData {
558    pub o1: f64,
559    pub o2: f64,
560    pub t3: f64,
561    pub t4: f64,
562}
563
564/// Multi-channel signal data (BrainBit2, NeuroEEG, etc.).
565#[repr(C)]
566#[derive(Debug, Clone, Copy)]
567pub struct SignalChannelsData {
568    pub pack_num: u32,
569    pub marker: u8,
570    pub sz_samples: u32,
571    pub samples: *mut f64,
572}
573
574/// Multi-channel resistance data with referents.
575#[repr(C)]
576#[derive(Debug, Clone, Copy)]
577pub struct ResistRefChannelsData {
578    pub pack_num: u32,
579    pub sz_samples: u32,
580    pub sz_referents: u32,
581    pub samples: *mut f64,
582    pub referents: *mut f64,
583}
584
585/// EEG channel information.
586#[repr(C)]
587#[derive(Clone, Copy)]
588pub struct EEGChannelInfo {
589    pub id: EEGChannelId,
590    pub ch_type: EEGChannelType,
591    pub name: [u8; SENSOR_CHANNEL_NAME_LEN],
592    pub num: u8,
593}
594
595impl EEGChannelInfo {
596    /// Channel name as a Rust string.
597    pub fn name_str(&self) -> String {
598        let nul = self.name.iter().position(|&b| b == 0).unwrap_or(SENSOR_CHANNEL_NAME_LEN);
599        String::from_utf8_lossy(&self.name[..nul]).into_owned()
600    }
601}
602
603impl fmt::Debug for EEGChannelInfo {
604    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
605        f.debug_struct("EEGChannelInfo")
606            .field("id", &self.id)
607            .field("ch_type", &self.ch_type)
608            .field("name", &self.name_str())
609            .field("num", &self.num)
610            .finish()
611    }
612}
613
614/// BrainBit2 amplifier parameters.
615#[repr(C)]
616#[derive(Debug, Clone, Copy)]
617pub struct BrainBit2AmplifierParam {
618    pub ch_signal_mode: [BrainBit2ChannelMode; BRAINBIT2_MAX_CH_COUNT],
619    pub ch_resist_use: [u8; BRAINBIT2_MAX_CH_COUNT],
620    pub ch_gain: [SensorGain; BRAINBIT2_MAX_CH_COUNT],
621    pub current: GenCurrent,
622}
623
624/// MEMS (accelerometer + gyroscope) data sample.
625#[repr(C)]
626#[derive(Debug, Clone, Copy)]
627pub struct MEMSData {
628    pub pack_num: u32,
629    pub accelerometer_x: f64,
630    pub accelerometer_y: f64,
631    pub accelerometer_z: f64,
632    pub gyroscope_x: f64,
633    pub gyroscope_y: f64,
634    pub gyroscope_z: f64,
635}
636
637// ── Callibri types ───────────────────────────────────────────────────────────
638
639/// Callibri device colour.
640#[repr(u8)]
641#[derive(Debug, Clone, Copy, PartialEq, Eq)]
642pub enum CallibriColorType {
643    Red = 0,
644    Yellow = 1,
645    Blue = 2,
646    White = 3,
647    Unknown = 4,
648}
649
650/// Callibri electrode state.
651#[repr(u8)]
652#[derive(Debug, Clone, Copy, PartialEq, Eq)]
653pub enum CallibriElectrodeState {
654    Normal = 0,
655    HighResistance = 1,
656    Detached = 2,
657}
658
659/// External switch input.
660#[repr(u8)]
661#[derive(Debug, Clone, Copy, PartialEq, Eq)]
662pub enum SensorExternalSwitchInput {
663    ElectrodesRespUSB = 0,
664    Electrodes = 1,
665    USB = 2,
666    RespUSB = 3,
667    Short = 4,
668    Unknown = 0xFF,
669}
670
671/// ADC input mode.
672#[repr(i8)]
673#[derive(Debug, Clone, Copy, PartialEq, Eq)]
674pub enum SensorADCInput {
675    Electrodes = 0,
676    Short = 1,
677    Test = 2,
678    Resistance = 3,
679}
680
681/// Callibri stimulator state.
682#[repr(u8)]
683#[derive(Debug, Clone, Copy, PartialEq, Eq)]
684pub enum CallibriStimulatorState {
685    NoParams = 0,
686    Disabled = 1,
687    Enabled = 2,
688    Unsupported = 0xFF,
689}
690
691/// Callibri stimulator + motion assistant state.
692#[repr(C)]
693#[derive(Debug, Clone, Copy)]
694pub struct CallibriStimulatorMAState {
695    pub stimulator_state: CallibriStimulatorState,
696    pub ma_state: CallibriStimulatorState,
697}
698
699/// Callibri stimulation parameters.
700#[repr(C)]
701#[derive(Debug, Clone, Copy)]
702pub struct CallibriStimulationParams {
703    /// Stimulus amplitude in mA (1..100).
704    pub current: u8,
705    /// Duration of the stimulating pulse in µs (20..460).
706    pub pulse_width: u16,
707    /// Frequency of stimulation impulses in Hz (1..200).
708    pub frequency: u8,
709    /// Maximum stimulation time in ms (0..65535, 0 = infinite).
710    pub stimulus_duration: u16,
711}
712
713/// Callibri motion assistant limb.
714#[repr(u8)]
715#[derive(Debug, Clone, Copy, PartialEq, Eq)]
716pub enum CallibriMotionAssistantLimb {
717    RightLeg = 0,
718    LeftLeg = 1,
719    RightArm = 2,
720    LeftArm = 3,
721    Unsupported = 0xFF,
722}
723
724/// Callibri motion assistant parameters.
725#[repr(C)]
726#[derive(Debug, Clone, Copy)]
727pub struct CallibriMotionAssistantParams {
728    pub gyro_start: u8,
729    pub gyro_stop: u8,
730    pub limb: CallibriMotionAssistantLimb,
731    /// Multiple of 10.
732    pub min_pause_ms: u8,
733}
734
735/// Callibri motion counter parameters.
736#[repr(C)]
737#[derive(Debug, Clone, Copy)]
738pub struct CallibriMotionCounterParam {
739    /// Insensitivity threshold in mg (0..500).
740    pub insense_threshold_mg: u16,
741    /// Algorithm insensitivity threshold in samples (0..500).
742    pub insense_threshold_sample: u16,
743}
744
745/// Callibri signal data.
746#[repr(C)]
747#[derive(Debug, Clone, Copy)]
748pub struct CallibriSignalData {
749    pub pack_num: u32,
750    pub samples: *mut f64,
751    pub sz_samples: u32,
752}
753
754/// Callibri respiration data.
755#[repr(C)]
756#[derive(Debug, Clone, Copy)]
757pub struct CallibriRespirationData {
758    pub pack_num: u32,
759    pub samples: *mut f64,
760    pub sz_samples: u32,
761}
762
763/// Callibri envelope data.
764#[repr(C)]
765#[derive(Debug, Clone, Copy)]
766pub struct CallibriEnvelopeData {
767    pub pack_num: u32,
768    pub sample: f64,
769}
770
771/// Callibri signal type.
772#[repr(u8)]
773#[derive(Debug, Clone, Copy, PartialEq, Eq)]
774pub enum SignalTypeCallibri {
775    EEG = 0,
776    EMG = 1,
777    ECG = 2,
778    EDA = 3,
779    StrainGaugeBreathing = 4,
780    ImpedanceBreathing = 5,
781    TenzoBreathing = 6,
782    Unknown = 7,
783}
784
785/// Quaternion data (MEMS orientation).
786#[repr(C)]
787#[derive(Debug, Clone, Copy)]
788pub struct QuaternionData {
789    pub pack_num: u32,
790    pub w: f32,
791    pub x: f32,
792    pub y: f32,
793    pub z: f32,
794}
795
796// ── FPG types ────────────────────────────────────────────────────────────────
797
798/// IR amplitude for FPG.
799#[repr(u8)]
800#[derive(Debug, Clone, Copy, PartialEq, Eq)]
801pub enum IrAmplitude {
802    Amp0 = 0,
803    Amp14 = 1,
804    Amp28 = 2,
805    Amp42 = 3,
806    Amp56 = 4,
807    Amp70 = 5,
808    Amp84 = 6,
809    Amp100 = 7,
810    Unsupported = 0xFF,
811}
812
813/// Red amplitude for FPG.
814#[repr(u8)]
815#[derive(Debug, Clone, Copy, PartialEq, Eq)]
816pub enum RedAmplitude {
817    Amp0 = 0,
818    Amp14 = 1,
819    Amp28 = 2,
820    Amp42 = 3,
821    Amp56 = 4,
822    Amp70 = 5,
823    Amp84 = 6,
824    Amp100 = 7,
825    Unsupported = 0xFF,
826}
827
828/// FPG (photoplethysmography) data sample.
829#[repr(C)]
830#[derive(Debug, Clone, Copy)]
831pub struct FPGData {
832    pub pack_num: u32,
833    pub ir_amplitude: f64,
834    pub red_amplitude: f64,
835}
836
837// ── Headphones types ─────────────────────────────────────────────────────────
838
839/// Headphones (v1) 7-channel signal data.
840#[repr(C)]
841#[derive(Debug, Clone, Copy)]
842pub struct HeadphonesSignalData {
843    pub pack_num: u32,
844    pub marker: u8,
845    pub ch1: f64,
846    pub ch2: f64,
847    pub ch3: f64,
848    pub ch4: f64,
849    pub ch5: f64,
850    pub ch6: f64,
851    pub ch7: f64,
852}
853
854/// Headphones (v1) 7-channel resistance data.
855#[repr(C)]
856#[derive(Debug, Clone, Copy)]
857pub struct HeadphonesResistData {
858    pub pack_num: u32,
859    pub ch1: f64,
860    pub ch2: f64,
861    pub ch3: f64,
862    pub ch4: f64,
863    pub ch5: f64,
864    pub ch6: f64,
865    pub ch7: f64,
866}
867
868/// Headphones (v1) amplifier parameters.
869#[repr(C)]
870#[derive(Debug, Clone, Copy)]
871pub struct HeadphonesAmplifierParam {
872    pub ch_signal_use: [u8; 7],
873    pub ch_resist_use: [u8; 7],
874    pub ch_gain: [SensorGain; 7],
875    pub current: GenCurrent,
876}
877
878/// Headphones2 4-channel signal data.
879#[repr(C)]
880#[derive(Debug, Clone, Copy)]
881pub struct Headphones2SignalData {
882    pub pack_num: u32,
883    pub marker: u8,
884    pub ch1: f64,
885    pub ch2: f64,
886    pub ch3: f64,
887    pub ch4: f64,
888}
889
890/// Headphones2 4-channel resistance data.
891#[repr(C)]
892#[derive(Debug, Clone, Copy)]
893pub struct Headphones2ResistData {
894    pub pack_num: u32,
895    pub ch1: f64,
896    pub ch2: f64,
897    pub ch3: f64,
898    pub ch4: f64,
899}
900
901/// Headphones2 amplifier parameters.
902#[repr(C)]
903#[derive(Debug, Clone, Copy)]
904pub struct Headphones2AmplifierParam {
905    pub ch_signal_use: [u8; 4],
906    pub ch_resist_use: [u8; 4],
907    pub ch_gain: [SensorGain; 4],
908    pub current: GenCurrent,
909}
910
911// ── Headband types ───────────────────────────────────────────────────────────
912
913/// Headband 4-channel signal data.
914#[repr(C)]
915#[derive(Debug, Clone, Copy)]
916pub struct HeadbandSignalData {
917    pub pack_num: u32,
918    pub marker: u8,
919    pub o1: f64,
920    pub o2: f64,
921    pub t3: f64,
922    pub t4: f64,
923}
924
925/// Headband 4-channel resistance data.
926#[repr(C)]
927#[derive(Debug, Clone, Copy)]
928pub struct HeadbandResistData {
929    pub pack_num: u32,
930    pub o1: f64,
931    pub o2: f64,
932    pub t3: f64,
933    pub t4: f64,
934}
935
936// ── AmpMode ──────────────────────────────────────────────────────────────────
937
938/// Amplifier mode.
939#[repr(u8)]
940#[derive(Debug, Clone, Copy, PartialEq, Eq)]
941pub enum SensorAmpMode {
942    Invalid = 0,
943    PowerDown = 1,
944    Idle = 2,
945    Signal = 3,
946    Resist = 4,
947    SignalResist = 5,
948    Envelope = 6,
949}
950
951// ── SmartBand types ──────────────────────────────────────────────────────────
952
953pub const SMART_BAND_MAX_CH_COUNT: usize = 4;
954
955/// SmartBand amplifier parameters.
956#[repr(C)]
957#[derive(Debug, Clone, Copy)]
958pub struct SmartBandAmplifierParam {
959    pub ch_signal_use: [u8; SMART_BAND_MAX_CH_COUNT],
960    pub ch_resist_use: [u8; SMART_BAND_MAX_CH_COUNT],
961    pub ch_gain: [SensorGain; SMART_BAND_MAX_CH_COUNT],
962    pub current: GenCurrent,
963}
964
965// ── NeuroEEG types ───────────────────────────────────────────────────────────
966
967/// EEG channel mode (NeuroEEG).
968#[repr(u8)]
969#[derive(Debug, Clone, Copy, PartialEq, Eq)]
970pub enum EEGChannelMode {
971    Off = 0,
972    Shorted = 1,
973    SignalResist = 2,
974    Signal = 3,
975    Test = 4,
976}
977
978/// EEG reference mode (NeuroEEG).
979#[repr(u8)]
980#[derive(Debug, Clone, Copy, PartialEq, Eq)]
981pub enum EEGRefMode {
982    HeadTop = 1,
983    A1A2 = 2,
984}
985
986/// NeuroEEG amplifier parameters.
987#[repr(C)]
988#[derive(Debug, Clone, Copy)]
989pub struct NeuroEEGAmplifierParam {
990    pub referent_resist_measure_allow: u8,
991    pub frequency: SensorSamplingFrequency,
992    pub referent_mode: EEGRefMode,
993    pub channel_mode: [EEGChannelMode; NEURO_EEG_MAX_CH_COUNT],
994    pub channel_gain: [SensorGain; NEURO_EEG_MAX_CH_COUNT],
995    pub respiration_on: u8,
996}
997
998/// NeuroEEG resistance data (with A1, A2, Bias).
999#[repr(C)]
1000#[derive(Debug, Clone, Copy)]
1001pub struct ResistChannelsData {
1002    pub pack_num: u32,
1003    pub a1: f64,
1004    pub a2: f64,
1005    pub bias: f64,
1006    pub sz_values: u32,
1007    pub values: *mut f64,
1008}
1009
1010// ── NeuroEEG filesystem types ────────────────────────────────────────────────
1011
1012/// NeuroEEG filesystem status.
1013#[repr(u8)]
1014#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1015pub enum SensorFSStatus {
1016    OK = 0,
1017    NoInit = 1,
1018    NoDisk = 2,
1019    Protect = 3,
1020}
1021
1022/// NeuroEEG filesystem I/O status.
1023#[repr(u8)]
1024#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1025pub enum SensorFSIOStatus {
1026    NoError = 0,
1027    IOError = 1,
1028    Timeout = 2,
1029}
1030
1031/// NeuroEEG filesystem stream status.
1032#[repr(u8)]
1033#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1034pub enum SensorFSStreamStatus {
1035    Closed = 0,
1036    Write = 1,
1037    Read = 2,
1038}
1039
1040/// NeuroEEG filesystem composite status.
1041#[repr(C)]
1042#[derive(Debug, Clone, Copy)]
1043pub struct NeuroEEGFSStatus {
1044    pub status: SensorFSStatus,
1045    pub io_status: SensorFSIOStatus,
1046    pub stream_status: SensorFSStreamStatus,
1047    pub autosave_signal: u8,
1048}
1049
1050pub const FILE_NAME_MAX_LEN: usize = 64;
1051
1052/// File information on the NeuroEEG filesystem.
1053#[repr(C)]
1054#[derive(Clone, Copy)]
1055pub struct SensorFileInfo {
1056    pub file_name: [u8; FILE_NAME_MAX_LEN],
1057    pub file_size: u32,
1058    pub modified_year: u16,
1059    pub modified_month: u8,
1060    pub modified_day_of_month: u8,
1061    pub modified_hour: u8,
1062    pub modified_min: u8,
1063    pub modified_sec: u8,
1064    pub attribute: u8,
1065}
1066
1067impl fmt::Debug for SensorFileInfo {
1068    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1069        let nul = self.file_name.iter().position(|&b| b == 0).unwrap_or(FILE_NAME_MAX_LEN);
1070        f.debug_struct("SensorFileInfo")
1071            .field("file_name", &String::from_utf8_lossy(&self.file_name[..nul]))
1072            .field("file_size", &self.file_size)
1073            .finish()
1074    }
1075}
1076
1077/// File data (for read/write operations).
1078#[repr(C)]
1079#[derive(Debug, Clone, Copy)]
1080pub struct SensorFileData {
1081    pub offset_start: u32,
1082    pub data_amount: u32,
1083    pub sz_data: u32,
1084    pub data: *mut u8,
1085}
1086
1087/// Disk information.
1088#[repr(C)]
1089#[derive(Debug, Clone, Copy)]
1090pub struct SensorDiskInfo {
1091    pub total_size: u64,
1092    pub free_size: u64,
1093}
1094
1095// ── PhotoStim types ──────────────────────────────────────────────────────────
1096
1097/// Stimulation phase parameters.
1098#[repr(C)]
1099#[derive(Debug, Clone, Copy)]
1100pub struct StimulPhase {
1101    /// Stimulation frequency.
1102    pub frequency: f64,
1103    /// Stimulus power 0..100 %.
1104    pub power: f64,
1105    /// Duration of a single stimulation pulse.
1106    pub pulse: f64,
1107    /// Stimulation phase duration.
1108    pub stimul_duration: f64,
1109    /// Duration of pause after the stimulation phase.
1110    pub pause: f64,
1111    /// Filling frequency of the signal for acoustic stimulation.
1112    pub filling_frequency: f64,
1113}
1114
1115/// Stimulation mode.
1116#[repr(u8)]
1117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1118pub enum SensorStimulMode {
1119    Invalid = 0,
1120    Stopped = 1,
1121    PendingSync = 2,
1122    Synchronized = 3,
1123    StimProgramRunning = 4,
1124    Error = 5,
1125}
1126
1127/// Stimulation sync state.
1128#[repr(u8)]
1129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1130pub enum SensorStimulSyncState {
1131    Normal = 0,
1132    TimeOut = 1,
1133}