From 1af8e5a324fe55aaaac978c78c7b3cf4408496e9 Mon Sep 17 00:00:00 2001 From: iximeow Date: Tue, 23 Jul 2019 21:43:31 -0700 Subject: adjust qhy support to work properly in single-frame mode, fix channel ordering issue --- src/qhyccd/QHYCCDCam.rs | 3 +- src/qhyccd/mod.rs | 200 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 173 insertions(+), 30 deletions(-) diff --git a/src/qhyccd/QHYCCDCam.rs b/src/qhyccd/QHYCCDCam.rs index 960993e..b5911c1 100644 --- a/src/qhyccd/QHYCCDCam.rs +++ b/src/qhyccd/QHYCCDCam.rs @@ -1,7 +1,7 @@ use std::os; #[repr(u32)] -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum QHYResult { QHYCCD_SUCCESS = 0, QHYCCD_READ_DIRECTLY = 0x2001, @@ -118,6 +118,7 @@ extern "C" { bpp: *mut os::raw::c_int) -> os::raw::c_int; pub fn CancelQHYCCDExposingAndReadout(handle: *mut os::raw::c_void) -> os::raw::c_int; pub fn ControlQHYCCDTemp(handle: *mut os::raw::c_void, target: os::raw::c_double) -> os::raw::c_int; + pub fn SetQHYCCDLogLevel(log_level: os::raw::c_char); pub fn SetQHYCCDDebayerOnOff(handle: *mut os::raw::c_void, onoff: os::raw::c_int) -> os::raw::c_int; pub fn SetQHYCCDBinMode(handle: *mut os::raw::c_void, wbin: os::raw::c_int, hbin: os::raw::c_int) -> os::raw::c_int; pub fn SetQHYCCDBitsMode(handle: *mut os::raw::c_void, bits: os::raw::c_int) -> os::raw::c_int; diff --git a/src/qhyccd/mod.rs b/src/qhyccd/mod.rs index 2de1b52..b50ba3a 100644 --- a/src/qhyccd/mod.rs +++ b/src/qhyccd/mod.rs @@ -14,8 +14,14 @@ use std::path::Path; use png::HasParameters; +unsafe impl Send for Camera { } #[derive(Debug)] pub struct Camera { + imagew: u32, + imageh: u32, + bpp: u8, + channels: u8, + bin: u8, handle: *mut os::raw::c_void } @@ -39,6 +45,21 @@ fn check(result: os::raw::c_int) -> Result<()> { static mut INITIALIZED: bool = false; +fn fix_channels(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; + } +} + pub fn acquire(camera_idx: i32) -> Result { unsafe { if !INITIALIZED { @@ -62,16 +83,24 @@ pub fn acquire(camera_idx: i32) -> Result { println!("Failed to open the device"); return Err(CameraError::QHYError); } - check(QHYCCDCam::SetQHYCCDStreamMode(handle, 1))?; // 0 means single frame mode... + check(QHYCCDCam::SetQHYCCDStreamMode(handle, 0))?; // 0 means single frame mode... check(QHYCCDCam::InitQHYCCD(handle))?; check(QHYCCDCam::CancelQHYCCDExposingAndReadout(handle))?; Ok(Camera { + imagew: 0, + imageh: 0, + bpp: 16, + bin: 1, + channels: 3, handle: handle }) } } impl Camera { + pub fn image_buf_size(&self) -> usize { + (self.imagew as usize) * (self.imageh as usize) * (self.bpp as usize / 8) * (self.channels as usize) + } pub fn set_exposure_ms(&self, ms: u32) -> Result<()> { self.set_param(Control::Exposure, (ms as f64) * 1000.0) } @@ -135,32 +164,42 @@ impl Camera { check(QHYCCDCam::CloseQHYCCD(self.handle)) } } - pub fn set_defaults(&self) -> Result<()> { + 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 => { check(QHYCCDCam::SetQHYCCDDebayerOnOff(self.handle, 1))?; - self.set_param(Control::CONTROL_WBR, 20.0)?; - self.set_param(Control::CONTROL_WBG, 20.0)?; - self.set_param(Control::CONTROL_WBB, 20.0)?; + self.set_param(Control::CONTROL_WBR, 1000.0)?; + self.set_param(Control::CONTROL_WBG, 1000.0)?; + self.set_param(Control::CONTROL_WBB, 1000.0)?; }, a @ _ => { println!("unexpected response when querying color setting: {}", a); return Err(CameraError::QHYError) } } - check(QHYCCDCam::SetQHYCCDResolution(self.handle, 0, 0, imagew, imageh))?; - check(QHYCCDCam::SetQHYCCDBinMode(self.handle, 1, 1))?; + self.set_roi(0, 0, imagew, imageh)?; + self.set_bin_mode(1)?; if self.has_param(Control::TransferBit) { check(QHYCCDCam::SetQHYCCDBitsMode(self.handle, 16))?; + self.bpp = 16; } Ok(()) } } - pub fn set_bin_mode(&self, bin: u8) -> Result<()> { + 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.imageh = h; + self.imagew = w; + Ok(()) + } + } + + pub fn set_bin_mode(&mut self, bin: u8) -> Result<()> { match bin { 1 => if !self.has_param(Control::Bin1x1Mode) { return Err(CameraError::InvalidControl); } 2 => if !self.has_param(Control::Bin2x2Mode) { return Err(CameraError::InvalidControl); } @@ -169,7 +208,9 @@ impl Camera { _ => { return Err(CameraError::InvalidControl); } } unsafe { - check(QHYCCDCam::SetQHYCCDBinMode(self.handle, bin as i32, bin as i32)) + check(QHYCCDCam::SetQHYCCDBinMode(self.handle, bin as i32, bin as i32))?; + self.bin = bin; + Ok(()) } } @@ -197,6 +238,74 @@ impl Camera { Ok(()) } + pub fn take_image_live(&self, path: &str) -> Result<()> { + unsafe { + let exposure_duration = self.get_param(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; + +// println!("w {} h {} bpp {} channels {}", imagew, imageh, bpp, channels); + match check(QHYCCDCam::GetQHYCCDLiveFrame(self.handle, &mut imagew, &mut imageh, &mut bpp, &mut channels, data)) { + Ok(()) => { + println!("Ok, guess we got it?"); + + let mut dataslice: &mut [u8] = + unsafe { + std::slice::from_raw_parts_mut( + data, + bufsize as usize + ) + }; + + //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 take_image(&self, path: &str) -> Result<()> { unsafe { let exposure_duration = self.get_param(Control::Exposure); @@ -210,7 +319,10 @@ impl Camera { }, QHYResult::QHYCCD_READ_DIRECTLY => { println!("Exp complete, example sleeps so i'll sleep too"); - std::thread::sleep(std::time::Duration::from_millis(1000)); +// std::thread::sleep(std::time::Duration::from_millis(1000)); + }, + QHYResult::QHYCCD_DELAY_200MS => { + println!("Sleeping 200ms..."); }, a @ _ =>{ println!("exp err: {:?}", a); @@ -218,8 +330,35 @@ impl Camera { } } - let bufsize = QHYCCDCam::GetQHYCCDMemLength(self.handle); + 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); @@ -227,15 +366,16 @@ impl Camera { 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_param(Control::CurTemp)); counter -= 500; } - let mut castediw = 0i32; - let mut castedih = 0i32; - let mut castedbpp = 0i32; - let mut channels = 0; + 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?"); @@ -257,21 +397,23 @@ impl Camera { bufsize as usize ) }; - 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; - } - println!("the amount of data mod 6: {}", dataslice.len() % 6); + + fix_channels(dataslice); + let mut writer = encoder.write_header().unwrap(); - writer.write_image_data(dataslice).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(()) } -- cgit v1.1