Skip to main content

playerone_sdk/
types.rs

1use std::ffi::CStr;
2use std::fmt::{Display, Formatter};
3
4use playerone_sdk_sys::{
5    _POABayerPattern, _POACameraProperties, _POAConfig, _POAImgFormat, _POAValueType, POABool,
6    POAConfig, POAConfigAttributes, POAErrors, POAImgFormat,
7};
8
9#[derive(Debug, Clone)]
10pub struct CameraProperties {
11    /// the camera name
12    pub camera_model_name: String,
13    /// user custom name, it will be added after the camera name, max len 16 bytes,like:Mars-C [Juno], default is empty
14    pub user_custom_id: String,
15    pub camera_id: u32,
16    /// max width of the camera
17    pub max_width: u32,
18    /// max height of the camera
19    pub max_height: u32,
20    /// ADC depth of CMOS sensor
21    pub bit_depth: u32,
22    /// is a color camera or not
23    pub is_color_camera: bool,
24    /// does the camera have ST4 port, if not, camera don't support ST4 guide
25    pub is_has_st_4_port: bool,
26    /// does the camera have cooler assembly, generally, the cooled camera with cooler, window heater and fan
27    pub is_has_cooler: bool,
28    /// is usb3.0 speed connection
29    pub is_usb_3_speed: bool,
30    /// the bayer filter pattern of camera
31    pub bayer_pattern: BayerPattern,
32    /// camera pixel size(unit: um)
33    pub pixel_size: f64,
34    /// the serial number of camera, unique
35    pub serial_number: String,
36    /// the sensor model name, eg: IMX462
37    pub sensor_model_name: String,
38    /// the path of the camera in the computer host
39    pub local_path: String,
40    /// bins supported by the camera, 1 == bin1, 2 == bin2,...
41    pub bins: Vec<u32>,
42    /// image data format supported by the camera
43    pub img_formats: Vec<POAImgFormat>,
44    /// does the camera sensor support hardware bin
45    pub is_support_hard_bin: bool,
46    /// camera's Product ID, note: the vID of PlayerOne is 0xA0A0
47    pub product_id: i32,
48}
49
50impl From<_POACameraProperties> for CameraProperties {
51    fn from(value: _POACameraProperties) -> Self {
52        let camera_model_name = unsafe {
53            CStr::from_ptr(value.cameraModelName.as_ptr())
54                .to_string_lossy()
55                .to_string()
56        };
57        let user_custom_id = unsafe {
58            CStr::from_ptr(value.userCustomID.as_ptr())
59                .to_string_lossy()
60                .to_string()
61        };
62        let sn = unsafe {
63            CStr::from_ptr(value.SN.as_ptr())
64                .to_string_lossy()
65                .to_string()
66        };
67        let sensor_model_name = unsafe {
68            CStr::from_ptr(value.sensorModelName.as_ptr())
69                .to_string_lossy()
70                .to_string()
71        };
72        let local_path = unsafe {
73            CStr::from_ptr(value.localPath.as_ptr())
74                .to_string_lossy()
75                .to_string()
76        };
77
78        let mut bins = Vec::with_capacity(value.bins.len());
79        for bin in value.bins {
80            if bin <= 0 {
81                break;
82            }
83            bins.push(bin as u32);
84        }
85
86        let mut img_formats = Vec::with_capacity(value.imgFormats.len());
87        for img_format in value.imgFormats {
88            if img_format == _POAImgFormat::POA_END {
89                break;
90            }
91            img_formats.push(img_format.into());
92        }
93
94        Self {
95            camera_model_name,
96            user_custom_id,
97            camera_id: value.cameraID as u32,
98            max_width: value.maxWidth as u32,
99            max_height: value.maxHeight as u32,
100            bit_depth: value.bitDepth as u32,
101            is_color_camera: value.isColorCamera == POABool::POA_TRUE,
102            is_has_st_4_port: value.isHasST4Port == POABool::POA_TRUE,
103            is_has_cooler: value.isHasCooler == POABool::POA_TRUE,
104            is_usb_3_speed: value.isUSB3Speed == POABool::POA_TRUE,
105            bayer_pattern: value.bayerPattern.into(),
106            pixel_size: value.pixelSize,
107            serial_number: sn,
108            sensor_model_name,
109            local_path,
110            bins,
111            img_formats,
112            is_support_hard_bin: value.isSupportHardBin == POABool::POA_TRUE,
113            product_id: value.pID.try_into().expect("c_int is not i32"),
114        }
115    }
116}
117
118/// A sensor-mode slot reported by the camera. Player One cameras that
119/// advertise "Dual Sampling" typically expose at least `"Normal"` (higher FPS)
120/// and `"LRN"` (lower read noise). The set of modes is camera-specific and
121/// identified by the integer `index` used with [`crate::Camera::set_sensor_mode`].
122#[derive(Debug, Clone, PartialEq, Eq)]
123pub struct SensorMode {
124    /// Zero-based index to pass to [`crate::Camera::set_sensor_mode`].
125    pub index: u32,
126    /// Short display name (e.g. "Normal", "LRN"). Suitable for a UI combobox.
127    pub name: String,
128    /// Longer description, suitable for a tooltip.
129    pub description: String,
130}
131
132impl std::fmt::Display for SensorMode {
133    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134        write!(f, "{}", self.name)
135    }
136}
137
138#[repr(i32)]
139#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
140pub enum ImageFormat {
141    /// 8bit raw data, 1 pixel 1 byte, value range[0, 255]
142    RAW8,
143    /// 16bit raw data, 1 pixel 2 bytes, value range[0, 65535]
144    RAW16,
145    /// RGB888 color data, 1 pixel 3 bytes, value range[0, 255] (only color camera)
146    RGB24,
147    /// 8bit monochrome data, convert the Bayer Filter Array to monochrome data. 1 pixel 1 byte, value range[0, 255] (only color camera)
148    MONO8,
149}
150
151impl ImageFormat {
152    pub fn bytes_per_pixel(&self) -> usize {
153        use ImageFormat::*;
154        match self {
155            RAW8 => 1,
156            RAW16 => 2,
157            RGB24 => 3,
158            MONO8 => 1,
159        }
160    }
161}
162
163impl From<_POAImgFormat> for ImageFormat {
164    fn from(value: _POAImgFormat) -> Self {
165        use ImageFormat::*;
166        use _POAImgFormat::*;
167        match value {
168            POA_RAW8 => RAW8,
169            POA_RAW16 => RAW16,
170            POA_RGB24 => RGB24,
171            POA_MONO8 => MONO8,
172            POA_END => unreachable!("POA_END should have been parsed before"),
173        }
174    }
175}
176
177impl Into<_POAImgFormat> for ImageFormat {
178    fn into(self) -> _POAImgFormat {
179        use ImageFormat::*;
180        use _POAImgFormat::*;
181        match self {
182            RAW8 => POA_RAW8,
183            RAW16 => POA_RAW16,
184            RGB24 => POA_RGB24,
185            MONO8 => POA_MONO8,
186        }
187    }
188}
189
190#[repr(i32)]
191#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
192pub enum BayerPattern {
193    /// Monochrome, the mono camera with this
194    MONO,
195    /// RGGB
196    RG,
197    /// BGGR
198    BG,
199    /// GRBG
200    GR,
201    /// GBRG
202    GB,
203}
204
205impl From<_POABayerPattern> for BayerPattern {
206    fn from(value: _POABayerPattern) -> Self {
207        use BayerPattern::*;
208        use _POABayerPattern::*;
209        match value {
210            POA_BAYER_RG => RG,
211            POA_BAYER_BG => BG,
212            POA_BAYER_GR => GR,
213            POA_BAYER_GB => GB,
214            POA_BAYER_MONO => MONO,
215        }
216    }
217}
218
219#[derive(Debug)]
220pub struct ConfigBounds<T> {
221    pub min: T,
222    pub max: T,
223    pub default: T,
224    pub conf_name: String,
225    pub description: String,
226}
227
228trait FromAttribute: Sized {
229    fn from_attribute(value: POAConfigAttributes) -> (Self, Self, Self);
230}
231
232impl FromAttribute for i64 {
233    fn from_attribute(value: POAConfigAttributes) -> (Self, Self, Self) {
234        if value.valueType != _POAValueType::VAL_INT {
235            let name = unsafe {
236                CStr::from_ptr(value.szConfName.as_ptr())
237                    .to_string_lossy()
238                    .to_string()
239            };
240            panic!("valueType is not VAL_INT for {}", name);
241        }
242        unsafe {
243            (
244                value.minValue.intValue as i64,
245                value.maxValue.intValue as i64,
246                value.defaultValue.intValue as i64,
247            )
248        }
249    }
250}
251
252impl FromAttribute for f64 {
253    fn from_attribute(value: POAConfigAttributes) -> (Self, Self, Self) {
254        if value.valueType != _POAValueType::VAL_FLOAT {
255            let name = unsafe {
256                CStr::from_ptr(value.szConfName.as_ptr())
257                    .to_string_lossy()
258                    .to_string()
259            };
260            panic!("valueType is not VAL_FLOAT for {}", name);
261        }
262        unsafe {
263            (
264                value.minValue.floatValue,
265                value.maxValue.floatValue,
266                value.defaultValue.floatValue,
267            )
268        }
269    }
270}
271
272impl FromAttribute for bool {
273    fn from_attribute(value: POAConfigAttributes) -> (Self, Self, Self) {
274        if value.valueType != _POAValueType::VAL_BOOL {
275            let name = unsafe {
276                CStr::from_ptr(value.szConfName.as_ptr())
277                    .to_string_lossy()
278                    .to_string()
279            };
280            panic!("valueType is not VAL_BOOL for {}", name);
281        }
282        unsafe {
283            (
284                value.minValue.boolValue == POABool::POA_TRUE,
285                value.maxValue.boolValue == POABool::POA_TRUE,
286                value.defaultValue.boolValue == POABool::POA_TRUE,
287            )
288        }
289    }
290}
291
292impl<T: FromAttribute> From<POAConfigAttributes> for ConfigBounds<T> {
293    fn from(value: POAConfigAttributes) -> Self {
294        let (min, max, default) = T::from_attribute(value);
295
296        Self {
297            min,
298            max,
299            default,
300            conf_name: unsafe {
301                CStr::from_ptr(value.szConfName.as_ptr())
302                    .to_string_lossy()
303                    .to_string()
304            },
305            description: unsafe {
306                CStr::from_ptr(value.szDescription.as_ptr())
307                    .to_string_lossy()
308                    .to_string()
309            },
310        }
311    }
312}
313
314#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
315pub enum ConfigKind {
316    /// exposure time(unit: us), read-write
317    Exposure,
318    /// gain, read-write
319    Gain,
320    /// hardware bin, read-write
321    HardwareBin,
322    /// camera temperature(uint: C), read-only
323    Temperature,
324    /// red pixels coefficient of white balance, read-write
325    WbR,
326    /// green pixels coefficient of white balance, read-write
327    WbG,
328    /// blue pixels coefficient of white balance, read-write
329    WbB,
330    /// camera offset, read-write
331    Offset,
332    /// maximum gain when auto-adjust, read-write
333    AutoexpoMaxGain,
334    /// maximum exposure when auto-adjust(uint: ms), read-write
335    AutoexpoMaxExposure,
336    /// target brightness when auto-adjust, read-write
337    AutoexpoBrightness,
338    /// ST4 guide north, generally,it's DEC+ on the mount, read-write
339    GuideNorth,
340    /// ST4 guide south, generally,it's DEC- on the mount, read-write
341    GuideSouth,
342    /// ST4 guide east, generally,it's RA+ on the mount, read-write
343    GuideEast,
344    /// ST4 guide west, generally,it's RA- on the mount, read-write
345    GuideWest,
346    /// e/ADU, This value will change with gain, read-only
347    Egain,
348    /// cooler power percentage[0-100%](only cool camera), read-only
349    CoolerPower,
350    /// camera target temperature(uint: C), read-write
351    TargetTemp,
352    /// turn cooler(and fan) on or off, read-write
353    Cooler,
354    /// (deprecated)get state of lens heater(on or off), read-only
355    Heater,
356    /// lens heater power percentage[0-100%], read-write
357    HeaterPower,
358    /// radiator fan power percentage[0-100%], read-write
359    FanPower,
360    /// no flip, Note: set this config(POASetConfig), the 'confValue' will be ignored, read-write
361    FlipNone,
362    /// flip the image horizontally, Note: set this config(POASetConfig), the 'confValue' will be ignored, read-write
363    FlipHori,
364    /// flip the image vertically, Note: set this config(POASetConfig), the 'confValue' will be ignored, read-write
365    FlipVert,
366    /// flip the image horizontally and vertically, Note: set this config(POASetConfig), the 'confValue' will be ignored, read-write
367    FlipBoth,
368    /// Frame rate limit, the range:[0, 2000], 0 means no limit, read-write
369    FrameLimit,
370    /// High Quality Image, for those without DDR camera(guide camera), if set POA_TRUE, this will reduce the waviness and stripe of the image,\n< but frame rate may go down, note: this config has no effect on those cameras that with DDR. read-write
371    Hqi,
372    /// USB bandwidth limit, read-write
373    UsbBandwidthLimit,
374    /// take the sum of pixels after binning, POA_TRUE is sum and POA_FLASE is average, default is POA_FLASE, read-write
375    PixelBinSum,
376    /// only for color camera, when set to POA_TRUE, pixel binning will use neighbour pixels and image after binning will lose the bayer pattern, read-write
377    MonoBin,
378}
379
380#[derive(Debug)]
381pub struct AllConfigBounds {
382    /// exposure time(unit: us)
383    pub exposure: ConfigBounds<i64>,
384    pub gain: ConfigBounds<i64>,
385    /// red pixels coefficient of white balance
386    pub wb_r: Option<ConfigBounds<i64>>,
387    /// green pixels coefficient of white balance
388    pub wb_g: Option<ConfigBounds<i64>>,
389    /// blue pixels coefficient of white balance
390    pub wb_b: Option<ConfigBounds<i64>>,
391    /// gain offset (meaning: what 0 represents)
392    pub offset: ConfigBounds<i64>,
393    /// maximum gain when auto-adjust
394    pub auto_max_gain: ConfigBounds<i64>,
395    /// maximum exposure when auto-adjust(unit: ms)
396    pub auto_max_exposure: ConfigBounds<i64>,
397    /// target brightness when auto-adjust
398    pub auto_target_brightness: ConfigBounds<i64>,
399    /// frame rate limit, the range:[0, 2000], 0 means no limit
400    pub frame_limit: ConfigBounds<i64>,
401    /// USB bandwidth limit [0, 100]%, default is 90
402    pub usb_bandwidth_limit: ConfigBounds<i64>,
403    /// cooler power percentage[0-100%]
404    pub cooler_power: Option<ConfigBounds<i64>>,
405    /// camera target temperature (Celsius)
406    pub target_temperature: Option<ConfigBounds<i64>>,
407    /// heater power percentage[0-100%]
408    pub heater_power: Option<ConfigBounds<i64>>,
409    /// radiator fan power percentage[0-100%]
410    pub fan_power: Option<ConfigBounds<i64>>,
411}
412
413impl From<Vec<POAConfigAttributes>> for AllConfigBounds {
414    fn from(values: Vec<POAConfigAttributes>) -> Self {
415        let mut exposure: Option<ConfigBounds<i64>> = None;
416        let mut gain: Option<ConfigBounds<i64>> = None;
417        let mut wb_r: Option<ConfigBounds<i64>> = None;
418        let mut wb_g: Option<ConfigBounds<i64>> = None;
419        let mut wb_b: Option<ConfigBounds<i64>> = None;
420        let mut offset: Option<ConfigBounds<i64>> = None;
421        let mut autoexpo_max_gain: Option<ConfigBounds<i64>> = None;
422        let mut autoexpo_max_exposure: Option<ConfigBounds<i64>> = None;
423        let mut autoexpo_brightness: Option<ConfigBounds<i64>> = None;
424        let mut cooler_power: Option<ConfigBounds<i64>> = None;
425        let mut target_temp: Option<ConfigBounds<i64>> = None;
426        let mut heater_power: Option<ConfigBounds<i64>> = None;
427        let mut fan_power: Option<ConfigBounds<i64>> = None;
428        let mut frame_limit: Option<ConfigBounds<i64>> = None;
429        let mut usb_bandwidth_limit: Option<ConfigBounds<i64>> = None;
430
431        for value in values {
432            let kind = value.configID.into();
433            match kind {
434                ConfigKind::Exposure => {
435                    exposure = Some(ConfigBounds::from(value));
436                }
437                ConfigKind::Gain => {
438                    gain = Some(ConfigBounds::from(value));
439                }
440                ConfigKind::WbR => {
441                    wb_r = Some(ConfigBounds::from(value));
442                }
443                ConfigKind::WbG => {
444                    wb_g = Some(ConfigBounds::from(value));
445                }
446                ConfigKind::WbB => {
447                    wb_b = Some(ConfigBounds::from(value));
448                }
449                ConfigKind::Offset => {
450                    offset = Some(ConfigBounds::from(value));
451                }
452                ConfigKind::AutoexpoMaxGain => {
453                    autoexpo_max_gain = Some(ConfigBounds::from(value));
454                }
455                ConfigKind::AutoexpoMaxExposure => {
456                    autoexpo_max_exposure = Some(ConfigBounds::from(value));
457                }
458                ConfigKind::AutoexpoBrightness => {
459                    autoexpo_brightness = Some(ConfigBounds::from(value));
460                }
461                ConfigKind::CoolerPower => {
462                    cooler_power = Some(ConfigBounds::from(value));
463                }
464                ConfigKind::TargetTemp => {
465                    target_temp = Some(ConfigBounds::from(value));
466                }
467                ConfigKind::HeaterPower => {
468                    heater_power = Some(ConfigBounds::from(value));
469                }
470                ConfigKind::FanPower => {
471                    fan_power = Some(ConfigBounds::from(value));
472                }
473                ConfigKind::FrameLimit => {
474                    frame_limit = Some(ConfigBounds::from(value));
475                }
476                ConfigKind::UsbBandwidthLimit => {
477                    usb_bandwidth_limit = Some(ConfigBounds::from(value));
478                }
479                _ => {}
480            }
481        }
482
483        Self {
484            exposure: exposure.expect("exposure is not found"),
485            gain: gain.expect("gain is not found"),
486            wb_r,
487            wb_g,
488            wb_b,
489            offset: offset.expect("offset is not found"),
490            auto_max_gain: autoexpo_max_gain.expect("autoexpo_max_gain is not found"),
491            auto_max_exposure: autoexpo_max_exposure.expect("autoexpo_max_exposure is not found"),
492            auto_target_brightness: autoexpo_brightness.expect("autoexpo_brightness is not found"),
493            cooler_power,
494            target_temperature: target_temp,
495            heater_power,
496            fan_power,
497            frame_limit: frame_limit.expect("frame_limit is not found"),
498            usb_bandwidth_limit: usb_bandwidth_limit.expect("usb_bandwidth_limit is not found"),
499        }
500    }
501}
502
503impl From<POAConfig> for ConfigKind {
504    fn from(value: POAConfig) -> Self {
505        use ConfigKind::*;
506        use _POAConfig::*;
507        match value {
508            POA_EXPOSURE => Exposure,
509            POA_GAIN => Gain,
510            POA_HARDWARE_BIN => HardwareBin,
511            POA_TEMPERATURE => Temperature,
512            POA_WB_R => WbR,
513            POA_WB_G => WbG,
514            POA_WB_B => WbB,
515            POA_OFFSET => Offset,
516            POA_AUTOEXPO_MAX_GAIN => AutoexpoMaxGain,
517            POA_AUTOEXPO_MAX_EXPOSURE => AutoexpoMaxExposure,
518            POA_AUTOEXPO_BRIGHTNESS => AutoexpoBrightness,
519            POA_GUIDE_NORTH => GuideNorth,
520            POA_GUIDE_SOUTH => GuideSouth,
521            POA_GUIDE_EAST => GuideEast,
522            POA_GUIDE_WEST => GuideWest,
523            POA_EGAIN => Egain,
524            POA_COOLER_POWER => CoolerPower,
525            POA_TARGET_TEMP => TargetTemp,
526            POA_COOLER => Cooler,
527            POA_HEATER => Heater,
528            POA_HEATER_POWER => HeaterPower,
529            POA_FAN_POWER => FanPower,
530            POA_FLIP_NONE => FlipNone,
531            POA_FLIP_HORI => FlipHori,
532            POA_FLIP_VERT => FlipVert,
533            POA_FLIP_BOTH => FlipBoth,
534            POA_FRAME_LIMIT => FrameLimit,
535            POA_HQI => Hqi,
536            POA_USB_BANDWIDTH_LIMIT => UsbBandwidthLimit,
537            POA_PIXEL_BIN_SUM => PixelBinSum,
538            POA_MONO_BIN => MonoBin,
539        }
540    }
541}
542
543#[repr(u32)]
544#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
545pub enum Error {
546    /// invalid index, means the index is < 0 or >= the count( camera or config)
547    InvalidIndex,
548    InvalidCameraId,
549    InvalidConfig,
550    InvalidArgument,
551    NotOpened,
552    DeviceNotFound,
553    OutOfBounds,
554    ExposureFailed,
555    Timeout,
556    BufferSizeTooSmall,
557    /// camera is exposing. must stop exposure first
558    Exposing,
559    NullPointer,
560    ConfigNotWritable,
561    ConfigNotReadable,
562    AccessDenied,
563    /// maybe the camera disconnected suddenly
564    OperationFailed,
565    MemoryAllocationFailed,
566}
567
568impl Display for Error {
569    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
570        use Error::*;
571        write!(
572            f,
573            "{}",
574            match self {
575                InvalidIndex => "invalid index",
576                InvalidCameraId => "invalid camera id",
577                InvalidConfig => "invalid config",
578                InvalidArgument => "invalid argument",
579                NotOpened => "camera is not opened",
580                DeviceNotFound => "device not found",
581                OutOfBounds => "out of bounds",
582                ExposureFailed => "exposure failed",
583                Timeout => "timeout",
584                BufferSizeTooSmall => "buffer size too small",
585                Exposing => "camera is exposing",
586                NullPointer => "null pointer",
587                ConfigNotWritable => "config is not writable",
588                ConfigNotReadable => "config is not readable",
589                AccessDenied => "access denied",
590                OperationFailed => "operation failed",
591                MemoryAllocationFailed => "memory allocation failed",
592            }
593        )
594    }
595}
596
597impl std::error::Error for Error {}
598
599impl From<POAErrors> for Error {
600    fn from(value: POAErrors) -> Self {
601        use Error::*;
602        use POAErrors::*;
603        match value {
604            POA_OK => unreachable!("POA_OK should have been checked before"),
605            POA_ERROR_INVALID_INDEX => InvalidIndex,
606            POA_ERROR_INVALID_ID => InvalidCameraId,
607            POA_ERROR_INVALID_CONFIG => InvalidConfig,
608            POA_ERROR_INVALID_ARGU => InvalidArgument,
609            POA_ERROR_NOT_OPENED => NotOpened,
610            POA_ERROR_DEVICE_NOT_FOUND => DeviceNotFound,
611            POA_ERROR_OUT_OF_LIMIT => OutOfBounds,
612            POA_ERROR_EXPOSURE_FAILED => ExposureFailed,
613            POA_ERROR_TIMEOUT => Timeout,
614            POA_ERROR_SIZE_LESS => BufferSizeTooSmall,
615            POA_ERROR_EXPOSING => Exposing,
616            POA_ERROR_POINTER => NullPointer,
617            POA_ERROR_CONF_CANNOT_WRITE => ConfigNotWritable,
618            POA_ERROR_CONF_CANNOT_READ => ConfigNotReadable,
619            POA_ERROR_ACCESS_DENIED => AccessDenied,
620            POA_ERROR_OPERATION_FAILED => OperationFailed,
621            POA_ERROR_MEMORY_FAILED => MemoryAllocationFailed,
622        }
623    }
624}