use rusb::{Context, UsbContext};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum SpectroError {
#[error("USB Communication Error: {0}")]
Usb(#[from] rusb::Error),
#[error("Calibration Error: {0}")]
Calibration(String),
#[error("Device Error: {0}")]
Device(String),
#[error("Mode Mismatch: {0}")]
Mode(String),
}
pub type Result<T> = std::result::Result<T, SpectroError>;
pub const WAVELENGTHS: [f32; 41] = [
380.0, 390.0, 400.0, 410.0, 420.0, 430.0, 440.0, 450.0, 460.0, 470.0, 480.0, 490.0, 500.0,
510.0, 520.0, 530.0, 540.0, 550.0, 560.0, 570.0, 580.0, 590.0, 600.0, 610.0, 620.0, 630.0,
640.0, 650.0, 660.0, 670.0, 680.0, 690.0, 700.0, 710.0, 720.0, 730.0, 740.0, 750.0, 760.0,
770.0, 780.0,
];
pub mod cam02;
pub mod colorimetry;
pub mod device;
pub mod i18n;
pub mod icc;
pub mod munki;
pub mod persistence;
pub mod spectrum;
pub mod sprague;
pub mod tm30;
pub mod tm30_data;
pub mod tm30_data_cmf;
pub mod transport;
pub use device::{BoxedSpectrometer, DeviceInfo, DevicePosition, DeviceStatus, Spectrometer};
pub use spectrum::{MeasurementMode as SpectrumMeasurementMode, SpectralData};
pub use transport::{Transport, UsbTransport};
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum MeasurementMode {
Reflective,
Emissive,
Ambient,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum Illuminant {
D50,
D55,
D65,
D75,
A,
F2,
F7,
F11,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum Observer {
CIE1931_2,
CIE1964_10,
}
const MUNKI_VIDS: [u16; 2] = [0x0765, 0x0971];
const MUNKI_PID: u16 = 0x2007;
pub fn discover() -> Result<BoxedSpectrometer> {
let context = Context::new()?;
discover_with_context(&context)
}
pub fn discover_with_context<T: UsbContext + 'static>(
context: &T,
) -> Result<Box<dyn Spectrometer + Send>> {
let devices = context.devices()?;
for device in devices.iter() {
let desc = device.device_descriptor()?;
let vid = desc.vendor_id();
let pid = desc.product_id();
if MUNKI_VIDS.contains(&vid) && pid == MUNKI_PID {
let handle = device.open()?;
handle.claim_interface(0)?;
let transport = transport::UsbTransport::new(handle);
let munki = munki::Munki::new(transport)?;
return Ok(Box::new(munki));
}
}
Err(SpectroError::Device(
"No supported spectrometer found. Ensure device is connected and drivers are installed."
.into(),
))
}
pub fn list_devices() -> Result<Vec<(u16, u16, &'static str)>> {
let context = Context::new()?;
let devices = context.devices()?;
let mut found = Vec::new();
for device in devices.iter() {
if let Ok(desc) = device.device_descriptor() {
let vid = desc.vendor_id();
let pid = desc.product_id();
if MUNKI_VIDS.contains(&vid) && pid == MUNKI_PID {
found.push((vid, pid, "ColorMunki"));
}
}
}
Ok(found)
}