summaryrefslogtreecommitdiff
path: root/src/qhyccd/mod.rs
diff options
context:
space:
mode:
authorAndy Wortman <ixineeringeverywhere@gmail.com>2019-07-27 20:07:05 -0700
committerAndy Wortman <ixineeringeverywhere@gmail.com>2019-07-27 20:07:05 -0700
commit144854e4b1a7ac38c5ddd2b0d45c495f491aa5dc (patch)
tree55306a36cfe8fef91a38496510acf34a771e582e /src/qhyccd/mod.rs
parent0d887779b8dee042999d04a29d4a095a3df93a6c (diff)
significant rewrite for threaded qhy operation
Diffstat (limited to 'src/qhyccd/mod.rs')
-rw-r--r--src/qhyccd/mod.rs388
1 files changed, 337 insertions, 51 deletions
diff --git a/src/qhyccd/mod.rs b/src/qhyccd/mod.rs
index b50ba3a..12630d6 100644
--- a/src/qhyccd/mod.rs
+++ b/src/qhyccd/mod.rs
@@ -12,17 +12,83 @@ 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;
-unsafe impl Send for Camera { }
+use crate::Dimensions;
+
+// unsafe impl Send for Camera { }
#[derive(Debug)]
pub struct Camera {
- imagew: u32,
- imageh: u32,
+ buffer: Vec<Vec<u8>>,
+ 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,
- bin: u8,
- handle: *mut os::raw::c_void
+ // 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; }
+ 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)]
@@ -45,7 +111,7 @@ fn check(result: os::raw::c_int) -> Result<()> {
static mut INITIALIZED: bool = false;
-fn fix_channels(dataslice: &mut [u8]) {
+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];
@@ -60,7 +126,139 @@ fn fix_channels(dataslice: &mut [u8]) {
}
}
-pub fn acquire(camera_idx: i32) -> Result<Camera> {
+pub fn fix_endianness(dataslice: &mut [u8]) {
+ for i in 0..(dataslice.len() / 2) {
+ let (low, high) = (dataslice[i * 2], dataslice[i * 2 + 1]);
+ dataslice[i * 2 + 0] = high;
+ dataslice[i * 2 + 1] = low;
+ }
+}
+
+
+pub fn connect(camera_idx: i32) -> Result<(Receiver<QHYResponse>, Sender<QHYMessage>)> {
+ 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;
+
+ 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) => {
+ counter = camera.settings.exposure;
+ println!("Beginning capture");
+ camera.begin_exposure().expect("can begin exposures");
+ exposing = true;
+ }
+ Ok(QHYMessage::StopCapture) => {
+ counter = 0;
+ 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;
+ }
+ }
+
+ 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))).unwrap();
+ } 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<u8>, Dimensions),
+ DroppedFrame,
+ Shutdown,
+ UpdatedSettings(Settings),
+ CurrentControlValue(Control, f64),
+}
+
+pub enum QHYMessage {
+ FrameAvailable(Vec<u8>),
+ BeginCapture,
+ StopCapture,
+ QueryControl(Control),
+ SetControl(Control, f64),
+ Shutdown,
+}
+
+fn acquire(camera_idx: i32) -> Result<Camera> {
unsafe {
if !INITIALIZED {
println!("Initializing QHYCCDResource");
@@ -87,29 +285,35 @@ pub fn acquire(camera_idx: i32) -> Result<Camera> {
check(QHYCCDCam::InitQHYCCD(handle))?;
check(QHYCCDCam::CancelQHYCCDExposingAndReadout(handle))?;
Ok(Camera {
- imagew: 0,
- imageh: 0,
- bpp: 16,
- bin: 1,
- channels: 3,
- handle: handle
+ buffer: vec![],
+ frame_size: 0,
+ handle: handle,
+ settings: Settings::default()
})
}
}
impl Camera {
+ pub fn get_frame(&mut self) -> Option<Vec<u8>> {
+ 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.imagew as usize) * (self.imageh as usize) * (self.bpp as usize / 8) * (self.channels as usize)
+ self.settings.frame_size()
}
- pub fn set_exposure_ms(&self, ms: u32) -> Result<()> {
- self.set_param(Control::Exposure, (ms as f64) * 1000.0)
+ 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_param(&self, control: Control) -> bool {
+ pub fn has_control(&self, control: Control) -> bool {
unsafe {
match QHYResult::from(QHYCCDCam::IsQHYCCDControlAvailable(self.handle, control as i32) as u32) {
QHYResult::QHYCCD_ERROR => {
@@ -124,19 +328,21 @@ impl Camera {
}
}
}
- pub fn set_param(&self, control: Control, value: f64) -> Result<()> {
+ pub fn set_control(&mut self, control: Control, value: f64) -> Result<()> {
unsafe {
- if self.has_param(control) {
- check(QHYCCDCam::SetQHYCCDParam(self.handle, control as i32, value))
+ if self.has_control(control) {
+ 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_param_limits(&self, control: Control) -> Result<(f64, f64, f64)> {
+ pub fn get_control_limits(&self, control: Control) -> Result<(f64, f64, f64)> {
unsafe {
- if self.has_param(control) {
+ if self.has_control(control) {
let mut min = 0f64;
let mut max = 0f64;
let mut step = 0f64;
@@ -154,7 +360,7 @@ impl Camera {
}
}
- pub fn get_param(&self, control: Control) -> f64 {
+ pub fn get_control(&self, control: Control) -> f64 {
unsafe {
QHYCCDCam::GetQHYCCDParam(self.handle, control as i32)
}
@@ -164,16 +370,34 @@ impl Camera {
check(QHYCCDCam::CloseQHYCCD(self.handle))
}
}
+ pub fn set_color_mode(&mut self, mono: bool) -> Result<()> {
+ // TODO: handle mono cameras correctly
+ if mono {
+ unsafe {
+ check(QHYCCDCam::SetQHYCCDDebayerOnOff(self.handle, 0))?;
+ }
+ self.set_control(Control::CONTROL_WBR, 1000.0)?;
+ self.set_control(Control::CONTROL_WBG, 1000.0)?;
+ self.set_control(Control::CONTROL_WBB, 1000.0)?;
+ self.settings.channels = 1;
+ } else {
+ unsafe {
+ check(QHYCCDCam::SetQHYCCDDebayerOnOff(self.handle, 1))?;
+ }
+ self.set_control(Control::CONTROL_WBR, 1000.0)?;
+ self.set_control(Control::CONTROL_WBG, 1000.0)?;
+ self.set_control(Control::CONTROL_WBB, 1000.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 => {
- check(QHYCCDCam::SetQHYCCDDebayerOnOff(self.handle, 1))?;
- self.set_param(Control::CONTROL_WBR, 1000.0)?;
- self.set_param(Control::CONTROL_WBG, 1000.0)?;
- self.set_param(Control::CONTROL_WBB, 1000.0)?;
+ self.set_color_mode(false)?;
},
a @ _ => {
println!("unexpected response when querying color setting: {}", a);
@@ -182,34 +406,35 @@ impl Camera {
}
self.set_roi(0, 0, imagew, imageh)?;
self.set_bin_mode(1)?;
- if self.has_param(Control::TransferBit) {
+ if self.has_control(Control::TransferBit) {
check(QHYCCDCam::SetQHYCCDBitsMode(self.handle, 16))?;
- self.bpp = 16;
+ self.settings.bpp = 16;
}
+ println!("roi set to {} x {} ???", imagew, imageh);
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.imageh = h;
- self.imagew = w;
+ 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_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); }
+ 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.bin = bin;
+ self.settings.bin = bin;
Ok(())
}
}
@@ -238,9 +463,65 @@ impl Camera {
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<u8>) -> Result<(Vec<u8>, 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_param(Control::Exposure);
+ let exposure_duration = self.get_control(Control::Exposure);
let exposure_ms = exposure_duration / 1000.0;
println!("Exposure duration: {}", exposure_ms);
@@ -260,19 +541,19 @@ impl Camera {
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)) {
+ 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?");
- let mut dataslice: &mut [u8] =
- unsafe {
- std::slice::from_raw_parts_mut(
- data,
- bufsize as usize
- )
- };
-
//fix_channels(dataslice);
let dest = Path::new(path);
@@ -303,12 +584,16 @@ impl Camera {
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_param(Control::Exposure);
+ 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);
@@ -362,13 +647,13 @@ impl Camera {
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;
+ 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_param(Control::CurTemp));
+ println!("Camera temp is currently: {}", self.get_control(Control::CurTemp));
counter -= 500;
}
@@ -390,7 +675,7 @@ impl Camera {
encoder.set(png::ColorType::RGB).set(png::BitDepth::Sixteen);
// crazy theory, endianness might be wrong...
// so flip the bytes first
- let mut dataslice: &mut [u8] =
+ let dataslice: &mut [u8] =
unsafe {
std::slice::from_raw_parts_mut(
data,
@@ -418,6 +703,7 @@ impl Camera {
Ok(())
}
}
+*/
pub fn get_overscan_area(&self) -> Result<(u32, u32, u32, u32)> {
unsafe {
let mut startX: i32 = 0;