pub mod QHYCCDCam; pub use self::QHYCCDCam::Control; use self::QHYCCDCam::*; use std::alloc::{alloc, dealloc, Layout}; use std::collections::HashMap; use std::ffi::CStr; use std::os; use std::fs::File; use std::io::BufWriter; use std::path::Path; use std::time::Duration; use crossbeam_channel::unbounded; use crossbeam_channel::{Sender, Receiver, TryRecvError}; use crossbeam_channel::select; use png::HasParameters; use crate::Dimensions; use crate::Properties; // unsafe impl Send for Camera { } #[derive(Debug)] pub struct Camera { buffer: Vec>, frame_size: usize, handle: *mut os::raw::c_void, settings: Settings, } #[derive(Debug, Default, Copy, Clone)] pub struct Settings { exposure: u64, brightness: u32, contrast: u32, control_wbr: u32, control_wbb: u32, control_wbg: u32, gamma: u32, gain: u32, offset: u32, speed: u32, transfer_bit: u32, usb_traffic: u32, row_noise_re: bool, cur_temp: f64, cur_pwm: f64, manual_pwm: f64, cfw_port: f64, cooler: bool, st4_port: bool, color: bool, bin: u8, bpp: u8, channels: u8, // width, height image_roi: (u32, u32), image_xy: (u32, u32), } impl Settings { pub fn frame_size(&self) -> usize { let pixels = (self.image_roi.0 as usize) * (self.image_roi.1 as usize); let bytes_per_pixel = (self.bpp as usize / 8) * (self.channels as usize); pixels * bytes_per_pixel } pub fn update_param(&mut self, control: Control, value: f64) { match control { Control::Brightness => { self.brightness = value as u32; } Control::Contrast => { self.contrast = value as u32; } Control::CONTROL_WBR => { self.control_wbr = value as u32; } Control::CONTROL_WBB => { self.control_wbb = value as u32; } Control::CONTROL_WBG => { self.control_wbg = value as u32; } Control::Gamma => { self.gamma = value as u32; } Control::Gain => { self.gain = value as u32; } Control::Offset => { self.offset = value as u32; } Control::Exposure => { self.exposure = value as u64; } Control::TransferBit => { self.transfer_bit = value as u32; self.bpp = value as u8; } Control::Channels => { self.channels = value as u8; } Control::USBTraffic => { self.usb_traffic = value as u32; } Control::RowNoiseRe => { self.row_noise_re = value as u32 == 1; } Control::CurTemp => { self.cur_temp = value; } Control::CurPWM => { self.cur_pwm = value; } Control::ManulPwm => { self.manual_pwm = value; } Control::Cooler => { self.cooler = value as u32 == 1; } _ => { } } } } #[derive(Debug, Copy, Clone)] pub enum CameraError { QHYError, // unspecified error from the qhy sdk InvalidControl } type Result = std::result::Result; fn check(result: os::raw::c_int) -> Result<()> { match QHYResult::from(result as u32) { QHYResult::QHYCCD_SUCCESS => Ok(()), QHYResult::QHYCCD_ERROR => Err(CameraError::QHYError), a @ _ => { panic!("Unexpected result code from qhy sdk: {:?}", a); } } } static mut INITIALIZED: bool = false; pub fn fix_channels_and_endianness(dataslice: &mut [u8]) { for i in 0..(dataslice.len() / 6) { let (b_low, b_high) = (dataslice[i * 6], dataslice[i * 6 + 1]); let tmp2 = dataslice[i * 6 + 1]; dataslice[i * 6 + 1] = dataslice[i * 6 + 4]; dataslice[i * 6] = dataslice[i * 6 + 5]; dataslice[i * 6 + 4] = b_high; dataslice[i * 6 + 5] = b_low; let g_low = dataslice[i * 6 + 2]; dataslice[i * 6 + 2] = dataslice[i * 6 + 3]; dataslice[i * 6 + 3] = g_low; if false { for e in 0..3 { let el = ((dataslice[i * 6 + e * 2] as u16) << 8) | (dataslice[i * 6 + e * 2 + 1] as u16); let el = el.saturating_mul(16); dataslice[i * 6 + e * 2] = (el >> 8) as u8; dataslice[i * 6 + e * 2 + 1] = el as u8; } } } } pub fn fix_endianness(dataslice: &mut [u8]) { // yolo it if dataslice.len() % 2 != 0 { panic!("you want to fix endianness of a slice that has length {}, which is not divisible by two. are you certain the data has an endianness to reverse?", dataslice.len()); } let dataslice: &mut [u16] = unsafe { std::slice::from_raw_parts_mut(dataslice.as_mut_ptr() as *mut u16, dataslice.len() / 2) }; for i in 0..dataslice.len() { // TODO: simd-ize this? // this is a pshufb and a mul.. dataslice[i] = dataslice[i].swap_bytes(); if true { // dataslice[i] *= 16; } } } pub fn connect(camera_idx: i32) -> Result<(Receiver, Sender)> { let (response_sender, response_receiver) = unbounded(); let (message_sender, message_receiver) = unbounded(); std::thread::spawn(move || { let mut camera = match acquire(camera_idx) { Ok(camera) => camera, Err(e) => { response_sender.send(QHYResponse::InitializationError).unwrap(); return; }, }; camera.set_defaults().unwrap(); // sleep for 2ms between waiting for messages and reading frames. This introduces an // artificial cap of 500fps, but I don't own a camera that can hit that. let SLEEP_TIME = 2u64; let mut exposing = false; let mut counter = 0u64; let mut capture_count: Option = None; loop { match message_receiver.try_recv() { Ok(QHYMessage::Shutdown) => { println!("Got shutdown request, closing camera..."); return; } Ok(QHYMessage::FrameAvailable(data)) => { if data.len() == camera.settings.frame_size() { camera.buffer.push(data); } else { // otherwise the writer finished handling a frame from an old size, // and the array is incorrectly sized for current settings. // so drop the buffer and free that memory. drop(data); } } Ok(QHYMessage::BeginCapture(count)) => { counter = camera.settings.exposure; capture_count = count; println!("Beginning capture"); camera.begin_exposure().expect("can begin exposures"); exposing = true; } Ok(QHYMessage::StopCapture) => { counter = 0; capture_count = None; camera.cancel_exposure().expect("can cancel exposures"); exposing = false; } Ok(QHYMessage::SetControl(control, value)) => { if control == Control::Color { // overload Control::Color to signal if we want to switch to // debayer-disabled "mono" let mono_mode = value == 0.0; camera.set_color_mode(mono_mode); // yes, this requires reset. counter = 0; camera.cancel_exposure().expect("can cancel exposures"); exposing = false; } else { if control.requires_reset() { counter = 0; camera.cancel_exposure().expect("can cancel exposures"); exposing = false; } camera.set_control(control, value).expect("can set camera controls"); } response_sender.send(QHYResponse::UpdatedSettings(camera.settings.clone())).expect("can send responses to main thread"); } Ok(QHYMessage::QueryControl(control)) => { let value = camera.get_control(control); camera.settings.update_param(control, value); response_sender.send(QHYResponse::CurrentControlValue(control, value)).expect("can send control value"); } Err(TryRecvError::Empty) => { // this is fine. nothing new to do. }, Err(TryRecvError::Disconnected) => { // uh oh. main thread crashed? all we can do is exit. return; } } // println!("counter: {:?}, exposing: {:?}", counter, exposing); if counter == 0 && exposing { if let Some(data) = camera.get_frame() { let (data, width, height, bpp, channels) = camera.read_frame(data).expect("can read frames from camera"); counter = camera.settings.exposure; camera.begin_exposure().expect("can begin exposures"); response_sender.send(QHYResponse::Data( data, Dimensions::new(width, height, bpp, channels), Properties { device: "qhy376c", exposure_ms: camera.settings.exposure as u32, gain: camera.settings.gain as u16, offset: camera.settings.offset as u16, gamma: camera.settings.gamma as u16, temp: (camera.settings.cur_temp * 10.0) as u16, }, )).unwrap(); match &mut capture_count { Some(count) => { *count -= 1; if *count == 0 { println!("finished capture run"); exposing = false; camera.cancel_exposure(); continue; } }, None => { // do nothing } } } else { // no frame ready in the buffer! we can't actually read the image... counter = camera.settings.exposure; camera.begin_exposure().expect("can begin exposures"); response_sender.send(QHYResponse::DroppedFrame).unwrap(); } } counter = counter.saturating_sub(SLEEP_TIME * 1000); std::thread::sleep(Duration::from_millis(SLEEP_TIME)); } }); Ok((response_receiver, message_sender)) } pub enum QHYResponse { InitializationError, Data(Vec, Dimensions, Properties), DroppedFrame, Shutdown, UpdatedSettings(Settings), CurrentControlValue(Control, f64), } pub enum QHYMessage { FrameAvailable(Vec), BeginCapture(Option), StopCapture, QueryControl(Control), SetControl(Control, f64), Shutdown, } fn acquire(camera_idx: i32) -> Result { unsafe { if !INITIALIZED { println!("Initializing QHYCCDResource"); check(QHYCCDCam::InitQHYCCDResource())?; INITIALIZED = true; } let cameracount = QHYCCDCam::ScanQHYCCD(); println!("Detected {} cameras", cameracount); if camera_idx >= cameracount { panic!("Camera id is invalid (detected {} cameras)", cameracount); } let mut id_space: [os::raw::c_char; 32] = [0; 32]; check(QHYCCDCam::GetQHYCCDId(camera_idx, id_space.as_mut_ptr()))?; println!("Got camera id: {:?}", id_space); println!("One sec, trying again..."); println!("How's this: {}", CStr::from_ptr(id_space.as_ptr()).to_str().unwrap()); let handle: *mut os::raw::c_void = QHYCCDCam::OpenQHYCCD(id_space.as_mut_ptr()); if handle == std::ptr::null_mut() { println!("Failed to open the device"); return Err(CameraError::QHYError); } check(QHYCCDCam::SetQHYCCDStreamMode(handle, 0))?; // 0 means single frame mode... check(QHYCCDCam::InitQHYCCD(handle))?; check(QHYCCDCam::CancelQHYCCDExposingAndReadout(handle))?; Ok(Camera { buffer: vec![], frame_size: 0, handle: handle, settings: Settings::default() }) } } impl Camera { pub fn get_frame(&mut self) -> Option> { if self.frame_size != self.image_buf_size() { // something has happened that requires a new buffer self.resize_buffer(); } self.buffer.pop() } pub fn image_buf_size(&self) -> usize { self.settings.frame_size() } pub fn set_exposure_ms(&mut self, ms: u32) -> Result<()> { self.set_control(Control::Exposure, (ms as f64) * 1000.0) } pub fn set_target_temp(&self, temp: f64) -> Result<()> { unsafe { check(QHYCCDCam::ControlQHYCCDTemp(self.handle, temp)) } } pub fn has_control(&self, control: Control) -> bool { unsafe { match QHYResult::from(QHYCCDCam::IsQHYCCDControlAvailable(self.handle, control as i32) as u32) { QHYResult::QHYCCD_ERROR => { false }, QHYResult::QHYCCD_SUCCESS => { true } a @ _ => { panic!("Unexpected response when querying if control '{:?}' is available: {:?}", control, a); } } } } pub fn set_control(&mut self, control: Control, value: f64) -> Result<()> { unsafe { if self.has_control(control) { if control == Control::TransferBit { check(QHYCCDCam::SetQHYCCDBitsMode(self.handle, value as i32))?; } else { check(QHYCCDCam::SetQHYCCDParam(self.handle, control as i32, value))?; } self.settings.update_param(control, value); Ok(()) } else { println!("Cannot set control: {:?}", control); Ok(()) } } } pub fn get_control_limits(&self, control: Control) -> Result<(f64, f64, f64)> { unsafe { if self.has_control(control) { let mut min = 0f64; let mut max = 0f64; let mut step = 0f64; match check(QHYCCDCam::GetQHYCCDParamMinMaxStep(self.handle, control as i32, &mut min, &mut max, &mut step)) { Ok(_) => { Ok((min, max, step)) }, Err(_) => { Err(CameraError::QHYError) } } } else { Err(CameraError::InvalidControl) } } } pub fn get_control(&self, control: Control) -> f64 { unsafe { QHYCCDCam::GetQHYCCDParam(self.handle, control as i32) } } pub fn release(self) -> Result<()> { unsafe { check(QHYCCDCam::CloseQHYCCD(self.handle)) } } pub fn set_color_mode(&mut self, mono: bool) -> Result<()> { // TODO: handle mono cameras correctly if mono { unsafe { // well, this can fail if debayering just isn't supported. so let's... not QHYCCDCam::SetQHYCCDDebayerOnOff(self.handle, 0); } self.set_control(Control::CONTROL_WBR, 4000.0)?; self.set_control(Control::CONTROL_WBG, 4000.0)?; self.set_control(Control::CONTROL_WBB, 4000.0)?; self.settings.channels = 1; } else { unsafe { check(QHYCCDCam::SetQHYCCDDebayerOnOff(self.handle, 1))?; } self.set_control(Control::CONTROL_WBR, 4000.0)?; self.set_control(Control::CONTROL_WBG, 4000.0)?; self.set_control(Control::CONTROL_WBB, 4000.0)?; self.settings.channels = 3; } Ok(()) } pub fn set_defaults(&mut self) -> Result<()> { unsafe { println!("Hey wait gotta get dimensions first"); let ((chipw, chiph), (imagew, imageh), (pixelw, pixelh), bpp) = self.get_dimensions()?; match QHYCCDCam::IsQHYCCDControlAvailable(self.handle, Control::Color as i32) { 1 | 2 | 3 | 4 => { self.set_color_mode(false)?; }, 0 => { // no, the color control is not available. mono it is! self.set_color_mode(true)?; } a @ _ => { println!("unexpected response when querying color setting: {}", a); return Err(CameraError::QHYError) } } self.set_roi(0, 0, imagew, imageh)?; self.set_bin_mode(1)?; if self.has_control(Control::TransferBit) { check(QHYCCDCam::SetQHYCCDBitsMode(self.handle, 16))?; self.settings.bpp = 16; println!("set tp 16bpp"); } println!("roi set to {} x {} ???", imagew, imageh); println!("gain limits: {:?}", self.get_control_limits(Control::Gain)?); println!("exposure limits: {:?}", self.get_control_limits(Control::Exposure)?); println!("offset: {:?}", self.get_control_limits(Control::Offset)?); println!("brightness: {:?}", self.get_control_limits(Control::Brightness)?); println!("gamma: {:?}", self.get_control_limits(Control::Gamma)?); println!("contrast: {:?}", self.get_control_limits(Control::Contrast)?); // panic!("hi"); Ok(()) } } pub fn set_roi(&mut self, x: u32, y: u32, w: u32, h: u32) -> Result<()> { unsafe { check(QHYCCDCam::SetQHYCCDResolution(self.handle, x, y, w, h))?; self.settings.image_roi = (w, h); self.settings.image_xy = (x, y); Ok(()) } } pub fn set_bin_mode(&mut self, bin: u8) -> Result<()> { match bin { 1 => if !self.has_control(Control::Bin1x1Mode) { return Err(CameraError::InvalidControl); } 2 => if !self.has_control(Control::Bin2x2Mode) { return Err(CameraError::InvalidControl); } 3 => if !self.has_control(Control::Bin3x3Mode) { return Err(CameraError::InvalidControl); } 4 => if !self.has_control(Control::Bin4x4Mode) { return Err(CameraError::InvalidControl); } _ => { return Err(CameraError::InvalidControl); } } unsafe { check(QHYCCDCam::SetQHYCCDBinMode(self.handle, bin as i32, bin as i32))?; self.settings.bin = bin; Ok(()) } } pub fn get_exposure_remaining(&self) -> u32 { unsafe { QHYCCDCam::GetQHYCCDExposureRemaining(self.handle) } } pub fn display_camera_dimensions(&self) -> Result<()> { let (overscan_start_X, overscan_start_Y, overscan_size_X, overscan_size_Y) = self.get_overscan_area()?; println!("Overscan area:"); println!(" startX x startY : {:05} x {:05}", overscan_start_X, overscan_start_Y); println!(" sizeX x sizeY : {:05} x {:05}", overscan_size_X, overscan_size_Y); let (effective_start_X, effective_start_Y, effective_size_X, effective_size_Y) = self.get_effective_area()?; println!("Effective area:"); println!(" startX x startY : {:05} x {:05}", effective_start_X, effective_start_Y); println!(" sizeX x sizeY : {:05} x {:05}", effective_size_X, effective_size_Y); let ((chipw, chiph), (imagew, imageh), (pixelw, pixelh), bpp) = self.get_dimensions()?; println!("Chip dimensions:"); println!("Chip size (w/h): {:05} x {:05} [mm]", chipw, chiph); println!("Pixel size (w/h): {:05} x {:05} [um]", pixelw, pixelh); println!("Image size (w/h): {:05} x {:05} [pixels]", imagew, imageh); println!(" bpp: {}", bpp); Ok(()) } pub fn cancel_exposure(&self) -> Result<()> { unsafe { check(QHYCCDCam::CancelQHYCCDExposingAndReadout(self.handle)) } } pub fn begin_exposure(&self) -> Result<()> { let result = unsafe { QHYCCDCam::ExpQHYCCDSingleFrame(self.handle) }; match QHYCCDCam::QHYResult::from(result as u32) { QHYResult::QHYCCD_SUCCESS => { // println!("Didn't expect this result..."); Ok(()) }, QHYResult::QHYCCD_READ_DIRECTLY => { // println!("Exp complete, example sleeps so i'll sleep too"); Ok(()) }, QHYResult::QHYCCD_DELAY_200MS => { println!("Sleeping 200ms... but not actually, bout to have a bug :):)))"); Ok(()) }, a @ _ =>{ println!("exp err: {:?}", a); return Err(CameraError::QHYError); } } } fn resize_buffer(&mut self) { println!("Resizing buffer to 3x{}", self.settings.frame_size()); self.frame_size = self.settings.frame_size(); self.buffer = vec![ vec![0u8; self.frame_size], vec![0u8; self.frame_size], vec![0u8; self.frame_size], ]; } pub fn read_frame(&self, mut buf: Vec) -> Result<(Vec, u32, u32, u8, u8)> { let mut castediw = 0i32; let mut castedih = 0i32; let mut castedbpp = 0i32; let mut channels = 0i32; println!("Getting data..."); unsafe { use std::time::{SystemTime, UNIX_EPOCH}; let start = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let start = start.as_secs() * 1000 + (start.subsec_nanos() as u64) / 1000000; check(QHYCCDCam::GetQHYCCDSingleFrame(self.handle, &mut castediw, &mut castedih, &mut castedbpp, &mut channels, buf.as_mut_ptr()))?; let end = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let end = end.as_secs() * 1000 + (end.subsec_nanos() as u64) / 1000000; println!("camera record time: {}ms", end - start); } Ok((buf, castediw as u32, castedih as u32, castedbpp as u8, channels as u8)) } /* pub fn take_image_live(&self, path: &str) -> Result<()> { unsafe { let exposure_duration = self.get_control(Control::Exposure); let exposure_ms = exposure_duration / 1000.0; println!("Exposure duration: {}", exposure_ms); let mut bufsize: usize = self.image_buf_size(); println!("Ok, we'll need {} bytes...", bufsize); let data_layout = Layout::from_size_align(bufsize as usize, 8).unwrap(); let data = alloc(data_layout); check(QHYCCDCam::BeginQHYCCDLive(self.handle))?; loop { // println!("Getting data..."); let mut imagew: i32 = self.imagew as i32; let mut imageh: i32 = self.imageh as i32; let mut bpp: i32 = self.bpp as i32; let mut channels: i32 = self.channels as i32; let frame = match frame_rx.recv() { Ok(frame) => frame, Err(signal) => { return Err(CollectionError::BufferClosed); } }; // println!("w {} h {} bpp {} channels {}", imagew, imageh, bpp, channels); match check(QHYCCDCam::GetQHYCCDLiveFrame(self.handle, &mut imagew, &mut imageh, &mut bpp, &mut channels, data.as_mut_ptr())) { Ok(()) => { processor.send(Ok(frame)) } println!("Ok, guess we got it?"); //fix_channels(dataslice); let dest = Path::new(path); let file = File::create(dest).unwrap(); let ref mut w = BufWriter::new(file); let mut encoder = png::Encoder::new(w, imagew as u32, imageh as u32); encoder.set(png::ColorType::RGB).set(png::BitDepth::Eight); // crazy theory, endianness might be wrong... // so flip the bytes first let mut writer = encoder.write_header().unwrap(); writer.write_image_data(dataslice).unwrap(); println!("NEXT!"); QHYResult::QHYCCD_SUCCESS }, Err(CameraError::QHYError) => { // println!("Error in live capture: {:?}. sleeping..", CameraError::QHYError); println!("Still waiting..."); std::thread::sleep(std::time::Duration::from_millis(100)); QHYResult::QHYCCD_ERROR } Err(_) => { unreachable!(); } }; } dealloc(data as *mut u8, data_layout); Ok(()) pub fn begin_exposure(&self) -> Result<()> { } } } pub fn take_image(&self, path: &str) -> Result<()> { unsafe { let exposure_duration = self.get_control(Control::Exposure); let exposure_ms = exposure_duration / 1000.0; println!("Exposure duration: {}", exposure_ms); let result = QHYCCDCam::ExpQHYCCDSingleFrame(self.handle); match QHYCCDCam::QHYResult::from(result as u32) { QHYResult::QHYCCD_SUCCESS => { // println!("Didn't expect this result..."); // std::thread::sleep(std::time::Duration::from_millis(1000)); }, QHYResult::QHYCCD_READ_DIRECTLY => { println!("Exp complete, example sleeps so i'll sleep too"); // std::thread::sleep(std::time::Duration::from_millis(1000)); }, QHYResult::QHYCCD_DELAY_200MS => { println!("Sleeping 200ms..."); }, a @ _ =>{ println!("exp err: {:?}", a); return Err(CameraError::QHYError); } } let mut chipw: f64 = 0.0; let mut chiph: f64 = 0.0; let mut imagew: i32 = 0; let mut imageh: i32 = 0; let mut pixelw: f64 = 0.0; let mut pixelh: f64 = 0.0; let mut bpp: i32 = 0; check(QHYCCDCam::GetQHYCCDChipInfo( self.handle, &mut chipw as *mut os::raw::c_double, &mut chiph as *mut os::raw::c_double, &mut imagew as *mut os::raw::c_int, &mut imageh as *mut os::raw::c_int, &mut pixelw as *mut os::raw::c_double, &mut pixelh as *mut os::raw::c_double, &mut bpp as *mut os::raw::c_int))?; let mut channels: i32 = 3; let mut bufsize = self.image_buf_size(); println!("Ok, we'll need {} bytes...", bufsize); println!("but you claim to want {} bytes...", QHYCCDCam::GetQHYCCDMemLength(self.handle)); /* if self.bin != 1 { println!("Correcting for binning..."); bufsize /= self.bin as i32; bufsize /= self.bin as i32; println!("you're getting {} bytes!", bufsize); } */ let data_layout = Layout::from_size_align(bufsize as usize, 8).unwrap(); let data = alloc(data_layout); let mut counter: i64 = (self.get_control(Control::Exposure) as u64 / 1000) as i64; while counter > 0 { println!("I think there's about {}ms remaining", counter); println!("You think there's about {}ms remaining", self.get_exposure_remaining()); std::thread::sleep(std::time::Duration::from_millis(500)); println!("Camera temp is currently: {}", self.get_control(Control::CurTemp)); counter -= 500; } let mut castediw = self.imagew as i32; let mut castedih = self.imageh as i32; let mut castedbpp = self.bpp as i32; let mut channels = self.channels as i32; println!("Getting data..."); check(QHYCCDCam::GetQHYCCDSingleFrame(self.handle, &mut castediw, &mut castedih, &mut castedbpp, &mut channels, data))?; println!("Ok, guess we got it?"); println!("image: {} x {}", castediw, castedih); println!("bpp: {}", castedbpp); println!("channels: {}", channels); let dest = Path::new(path); let file = File::create(dest).unwrap(); let ref mut w = BufWriter::new(file); let mut encoder = png::Encoder::new(w, castediw as u32, castedih as u32); encoder.set(png::ColorType::RGB).set(png::BitDepth::Sixteen); // crazy theory, endianness might be wrong... // so flip the bytes first let dataslice: &mut [u8] = unsafe { std::slice::from_raw_parts_mut( data, bufsize as usize ) }; fix_channels(dataslice); let mut writer = encoder.write_header().unwrap(); if self.bin != 1 { let scale = self.bin as usize * self.bin as usize; let mut cherrypicked = vec![0; bufsize as usize / scale]; for i in 0..cherrypicked.len() / 6 { for j in 0..6 { cherrypicked[i * 6 + j] = dataslice[i * 6 * scale + j]; } } writer.write_image_data(&cherrypicked).unwrap(); } else { writer.write_image_data(dataslice).unwrap(); } dealloc(data as *mut u8, data_layout); Ok(()) } } */ pub fn get_overscan_area(&self) -> Result<(u32, u32, u32, u32)> { unsafe { let mut startX: i32 = 0; let mut startY: i32 = 0; let mut sizeX: i32 = 0; let mut sizeY: i32 = 0; check(QHYCCDCam::GetQHYCCDOverScanArea( self.handle, &mut startX as *mut os::raw::c_int, &mut startY as *mut os::raw::c_int, &mut sizeX as *mut os::raw::c_int, &mut sizeY as *mut os::raw::c_int ))?; Ok((startX as u32, startY as u32, sizeX as u32, sizeY as u32)) } } pub fn get_effective_area(&self) -> Result<(u32, u32, u32, u32)> { unsafe { let mut startX: i32 = 0; let mut startY: i32 = 0; let mut sizeX: i32 = 0; let mut sizeY: i32 = 0; check(QHYCCDCam::GetQHYCCDEffectiveArea( self.handle, &mut startX as *mut os::raw::c_int, &mut startY as *mut os::raw::c_int, &mut sizeX as *mut os::raw::c_int, &mut sizeY as *mut os::raw::c_int ))?; Ok((startX as u32, startY as u32, sizeX as u32, sizeY as u32)) } } pub fn get_dimensions(&self) -> Result<((f64, f64), (u32, u32), (f64, f64), u32)> { unsafe { let mut chipw: f64 = 0.0; let mut chiph: f64 = 0.0; let mut imagew: i32 = 0; let mut imageh: i32 = 0; let mut pixelw: f64 = 0.0; let mut pixelh: f64 = 0.0; let mut bpp: i32 = 0; check(QHYCCDCam::GetQHYCCDChipInfo( self.handle, &mut chipw as *mut os::raw::c_double, &mut chiph as *mut os::raw::c_double, &mut imagew as *mut os::raw::c_int, &mut imageh as *mut os::raw::c_int, &mut pixelw as *mut os::raw::c_double, &mut pixelh as *mut os::raw::c_double, &mut bpp as *mut os::raw::c_int))?; Ok(( (chipw, chiph), (imagew as u32, imageh as u32), (pixelw, pixelh), bpp as u32 )) } } }