From 132d623a422e1b73f6e5d8911a82a75290637d10 Mon Sep 17 00:00:00 2001 From: Andy Wortman Date: Thu, 14 Mar 2019 23:04:31 -0700 Subject: add qhy control software --- src/main.rs | 21 +++- src/qhyccd/QHYCCDCam.rs | 128 ++++++++++++++++++++ src/qhyccd/mod.rs | 301 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 449 insertions(+), 1 deletion(-) create mode 100644 src/qhyccd/QHYCCDCam.rs create mode 100644 src/qhyccd/mod.rs (limited to 'src') diff --git a/src/main.rs b/src/main.rs index a9a664b..3640f34 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ #![allow(dead_code)] #![feature(alloc_layout_extra)] mod asicam; - +mod qhyccd; use crate::asicam::ASICamera2::{ControlType, ImageType}; use crate::asicam::Camera; @@ -15,7 +15,26 @@ fn main() { } fn operate_qhy() { + use crate::qhyccd::Control; println!("Operating on qhy camera ... or i'll die trying"); + let mut camera = qhyccd::acquire(0).unwrap(); + camera.set_exposure_ms(40000).unwrap(); + camera.set_param(Control::Gain, 64.0).unwrap(); + camera.set_param(Control::Offset, 00.0).unwrap(); + camera.set_param(Control::USBTraffic, 250.0).unwrap(); + camera.set_target_temp(0.0).unwrap(); + camera.set_param(Control::Cooler, 0.0).unwrap(); + println!("Binning modes:"); + println!("1x1: {}", camera.has_param(Control::Bin1x1Mode)); + println!("2x2: {}", camera.has_param(Control::Bin2x2Mode)); + println!("3x3: {}", camera.has_param(Control::Bin3x3Mode)); + println!("4x4: {}", camera.has_param(Control::Bin4x4Mode)); +// camera.set_param(Control::Speed, 1.0).unwrap(); + println!("current temp: {}", camera.get_param(Control::CurTemp)); + camera.set_defaults().unwrap(); +// camera.set_bin_mode(2).unwrap(); + camera.take_image("../../asdf.png"); + camera.release().unwrap(); } fn operate_asi() { diff --git a/src/qhyccd/QHYCCDCam.rs b/src/qhyccd/QHYCCDCam.rs new file mode 100644 index 0000000..6194835 --- /dev/null +++ b/src/qhyccd/QHYCCDCam.rs @@ -0,0 +1,128 @@ +use std::os; + +#[repr(u32)] +#[derive(Copy, Clone, Debug)] +pub enum QHYResult { + QHYCCD_SUCCESS = 0, + QHYCCD_READ_DIRECTLY = 0x2001, + QHYCCD_DELAY_200MS = 0x2000, + QHYCCD_ERROR = 0xffffffff +} + +impl From for QHYResult { + fn from(u: u32) -> QHYResult { + match u { + 0 => QHYResult::QHYCCD_SUCCESS, + 0x2000 => QHYResult::QHYCCD_READ_DIRECTLY, + 0x2001 => QHYResult::QHYCCD_DELAY_200MS, + 0xffffffff => QHYResult::QHYCCD_ERROR, + _ => { + panic!("Unexpected result code from qhy sdk: {:08x}", u); + } + } + } +} + +#[repr(u32)] +#[derive(Copy, Clone, Debug)] +pub enum Control { + Brightness = 0, // !< image brightness + Contrast = 1, //1 image contrast + CONTROL_WBR = 2, //2 red of white balance + CONTROL_WBB = 3, //3 blue of white balance + CONTROL_WBG = 4, //4 the green of white balance + Gamma = 5, //5 screen gamma + Gain = 6, //6 camera gain + Offset = 7, //7 camera offset + Exposure = 8, //8 expose time (us) + Speed = 9, //9 transfer speed + TransferBit = 10, //10 image depth bits + Channels = 11, //11 image channels + USBTraffic = 12, //12 hblank + RowNoiseRe = 13, //13 row denoise + CurTemp = 14, //14 current cmos or ccd temprature + CurPWM = 15, //15 current cool pwm + ManulPwm = 16, //16 set the cool pwm + CFWPort = 17, //17 control camera color filter wheel port + Cooler = 18, //18 check if camera has cooler + St4port = 19, //19 check if camera has st4port + Color = 20, //20 + Bin1x1Mode = 21, //21 check if camera has bin1x1 mode + Bin2x2Mode = 22, //22 check if camera has bin2x2 mode + Bin3x3Mode = 23, //23 check if camera has bin3x3 mode + Bin4x4Mode = 24, //24 check if camera has bin4x4 mode + CAM_MECHANICALSHUTTER = 25, //25 mechanical shutter + CAM_TRIGER_INTERFACE = 26, //26 triger + CAM_TECOVERPROTECT_INTERFACE = 27, //27 tec overprotect + CAM_SINGNALCLAMP_INTERFACE = 28, //28 singnal clamp + CAM_FINETONE_INTERFACE = 29, //29 fine tone + CAM_SHUTTERMOTORHEATING_INTERFACE = 30, //30 shutter motor heating + CAM_CALIBRATEFPN_INTERFACE = 31, //31 calibrated frame + CAM_CHIPTEMPERATURESENSOR_INTERFACE = 32, //32 chip temperaure sensor + CAM_USBREADOUTSLOWEST_INTERFACE = 33, //33 usb readout slowest + CAM_8BITS = 34, //34 8bit depth + CAM_16BITS = 35, //35 16bit depth + CAM_GPS = 36, //36 check if camera has gps + CAM_IGNOREOVERSCAN_INTERFACE = 37, //37 ignore overscan area + QHYCCD_3A_AUTOBALANCE = 38, //38 + QHYCCD_3A_AUTOEXPOSURE = 39, //39 + QHYCCD_3A_AUTOFOCUS = 40, //40 + CONTROL_AMPV = 41, //41 ccd or cmos ampv + CONTROL_VCAM = 42, //42 Virtual Camera on off + CAM_VIEW_MODE = 43, //43 + CONTROL_CFWSLOTSNUM = 44, //44 check CFW slots number + IS_EXPOSING_DONE = 45, //45 + ScreenStretchB = 46, //46 + ScreenStretchW = 47, //47 + CONTROL_DDR = 48, //47 + CAM_LIGHT_PERFORMANCE_MODE = 49, //49 + CAM_QHY5II_GUIDE_MODE = 50, //50 + DDR_BUFFER_CAPACITY = 51, //51 + DDR_BUFFER_READ_THRESHOLD = 52, //52 + DefaultOffset = 53, //53 + OutputDataActualBits = 54, //54 + OutputDataAlignment = 55 //55 +} + +#[repr(u32)] +#[derive(Copy, Clone, Debug)] +pub enum Bayer +{ + GB = 1, + GR = 2, + BG = 3, + RG = 4 +} + + +extern "C" { + pub fn ScanQHYCCD() -> os::raw::c_int; + pub fn InitQHYCCDResource() -> os::raw::c_int; + pub fn GetQHYCCDId(id: os::raw::c_int, id: *mut os::raw::c_char) -> os::raw::c_int; + pub fn GetQHYCCDModel(id: *mut os::raw::c_char, model: *mut os::raw::c_char) -> os::raw::c_int; + pub fn OpenQHYCCD(id: *mut os::raw::c_char) -> *mut os::raw::c_void; + pub fn SetQHYCCDStreamMode(handle: *mut os::raw::c_void, mode: os::raw::c_char) -> os::raw::c_int; + pub fn SetQHYCCDResolution(handle: *mut os::raw::c_void, x: os::raw::c_uint, y: os::raw::c_uint, xsize: os::raw::c_uint, ysize: os::raw::c_uint) -> os::raw::c_int; + pub fn InitQHYCCD(id: *mut os::raw::c_void) -> os::raw::c_int; + pub fn IsQHYCCDControlAvailable(handle: *mut os::raw::c_void, control: os::raw::c_int) -> os::raw::c_int; + pub fn SetQHYCCDParam(handle: *mut os::raw::c_void, control: os::raw::c_int, value: os::raw::c_double) -> os::raw::c_int; + pub fn GetQHYCCDParam(handle: *mut os::raw::c_void, control: os::raw::c_int) -> os::raw::c_double; + pub fn GetQHYCCDEffectiveArea(handle: *mut os::raw::c_void, startx: *mut os::raw::c_int, starty: *mut os::raw::c_int, sizex: *mut os::raw::c_int, sizey: *mut os::raw::c_int) -> os::raw::c_int; + pub fn GetQHYCCDOverScanArea(handle: *mut os::raw::c_void, startx: *mut os::raw::c_int, starty: *mut os::raw::c_int, sizex: *mut os::raw::c_int, sizey: *mut os::raw::c_int) -> os::raw::c_int; + pub fn GetQHYCCDChipInfo( + handle: *mut os::raw::c_void, + chipw: *mut os::raw::c_double, chiph: *mut os::raw::c_double, + imagew: *mut os::raw::c_int, imageh: *mut os::raw::c_int, + pixelw: *mut os::raw::c_double, pixelh: *mut os::raw::c_double, + bpp: *mut os::raw::c_int) -> os::raw::c_int; + pub fn ControlQHYCCDTemp(handle: *mut os::raw::c_void, target: os::raw::c_double) -> os::raw::c_int; + 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; + pub fn ExpQHYCCDSingleFrame(handle: *mut os::raw::c_void) -> os::raw::c_int; + pub fn GetQHYCCDExposureRemaining(handle: *mut os::raw::c_void) -> os::raw::c_uint; + pub fn GetQHYCCDMemLength(handle: *mut os::raw::c_void) -> os::raw::c_int; + pub fn GetQHYCCDSingleFrame(handle: *mut os::raw::c_void, w: *mut os::raw::c_int, h: *mut os::raw::c_int, bpp: *mut os::raw::c_int, channels: *mut os::raw::c_int, data: *mut os::raw::c_uchar) -> os::raw::c_int; + pub fn CloseQHYCCD(handle: *mut os::raw::c_void) -> os::raw::c_int; + pub fn ReleaseQHYCCDResource() -> os::raw::c_int; +} diff --git a/src/qhyccd/mod.rs b/src/qhyccd/mod.rs new file mode 100644 index 0000000..192b943 --- /dev/null +++ b/src/qhyccd/mod.rs @@ -0,0 +1,301 @@ +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 png::HasParameters; + +#[derive(Debug)] +pub struct Camera { + handle: *mut os::raw::c_void +} + +#[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 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))?; + Ok(Camera { + handle: handle + }) + } +} + +impl Camera { + pub fn set_exposure_ms(&self, ms: u32) -> Result<()> { + self.set_param(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_param(&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_param(&self, control: Control, value: f64) -> Result<()> { + unsafe { + if self.has_param(control) { + check(QHYCCDCam::SetQHYCCDParam(self.handle, control as i32, value)) + } else { + println!("Cannot set control: {:?}", control); + Ok(()) + } + } + } + pub fn get_param(&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_defaults(&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)?; + }, + 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))?; + if self.has_param(Control::TransferBit) { + check(QHYCCDCam::SetQHYCCDBitsMode(self.handle, 16))?; + } + Ok(()) + } + } + + pub fn set_bin_mode(&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); } + 3 => if !self.has_param(Control::Bin3x3Mode) { return Err(CameraError::InvalidControl); } + 4 => if !self.has_param(Control::Bin4x4Mode) { return Err(CameraError::InvalidControl); } + _ => { return Err(CameraError::InvalidControl); } + } + unsafe { + check(QHYCCDCam::SetQHYCCDBinMode(self.handle, bin as i32, bin as i32)) + } + } + + 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 take_image(&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 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)); + }, + a @ _ =>{ + println!("exp err: {:?}", a); + return Err(CameraError::QHYError); + } + } + + let bufsize = QHYCCDCam::GetQHYCCDMemLength(self.handle); + println!("Ok, we'll need {} 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_param(Control::Exposure) as u64 / 1000) as i64; + + while counter > 0 { + println!("I think there's about {}ms remaining", counter); + 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; + 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); + let mut writer = encoder.write_header().unwrap(); + writer.write_image_data( + unsafe { + std::slice::from_raw_parts( + data, + bufsize as usize + ) + } + ).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 + )) + } + } +} -- cgit v1.1