summaryrefslogtreecommitdiff
path: root/src/qhyccd
diff options
context:
space:
mode:
authorAndy Wortman <ixineeringeverywhere@gmail.com>2019-03-14 23:04:31 -0700
committerAndy Wortman <ixineeringeverywhere@gmail.com>2019-03-14 23:04:31 -0700
commit132d623a422e1b73f6e5d8911a82a75290637d10 (patch)
tree245c31266a0a3306e8611b4e33f164004f746811 /src/qhyccd
parente1fec0d69304aabc5d69226e787ed5939fcd8316 (diff)
add qhy control software
Diffstat (limited to 'src/qhyccd')
-rw-r--r--src/qhyccd/QHYCCDCam.rs128
-rw-r--r--src/qhyccd/mod.rs301
2 files changed, 429 insertions, 0 deletions
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<u32> 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<T> = std::result::Result<T, CameraError>;
+
+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<Camera> {
+ 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
+ ))
+ }
+ }
+}