diff options
| author | iximeow <me@iximeow.net> | 2019-07-27 20:07:05 -0700 | 
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2019-07-27 20:07:05 -0700 | 
| commit | d00283946a41b3f0c4106b0f3939afac5dac59d0 (patch) | |
| tree | 55306a36cfe8fef91a38496510acf34a771e582e /src | |
| parent | 30c518c400fda401134a92eedbbd99004b387ab7 (diff) | |
significant rewrite for threaded qhy operation
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 172 | ||||
| -rw-r--r-- | src/qhyccd/QHYCCDCam.rs | 70 | ||||
| -rw-r--r-- | src/qhyccd/mod.rs | 388 | 
3 files changed, 573 insertions, 57 deletions
| diff --git a/src/main.rs b/src/main.rs index 7275c96..dc70da6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,20 +7,181 @@  mod asicam;  mod qhyccd; +#[macro_use] +extern crate crossbeam_channel; + +use crossbeam_channel::{Sender, Receiver}; +use crossbeam_channel::unbounded; + +use std::time::Duration; +  use crate::asicam::ASICamera2::{ControlType, ImageType};  use crate::asicam::Camera; +use crate::qhyccd::QHYResponse; +use crate::qhyccd::QHYMessage; + +use std::path::Path; +use std::fs::File; +use std::io::BufWriter; + +use png::HasParameters; + +fn record_image(data: &[u8], dimensions: Dimensions, target: &'static str, image_id: u32) { +    let path_string = format!("{}_{}.png", target, image_id); +    println!("writing {}..", path_string); +    let dest = Path::new(&path_string); +    let file = File::create(dest).unwrap(); +    let ref mut w = BufWriter::new(file); +    let mut encoder = png::Encoder::new(w, dimensions.width, dimensions.height); +    let color_type = if dimensions.channels == 3 { +        png::ColorType::RGB +    } else if dimensions.channels == 1 { +        png::ColorType::Grayscale +    } else { panic!("Unsupported channel count: {}", dimensions.channels); }; + +    let bitness = if dimensions.bpp == 8 { +        png::BitDepth::Eight +    } else if dimensions.bpp == 16 { +        png::BitDepth::Sixteen +    } else { +        panic!("Unknwon bitness: {}", dimensions.bpp); +    }; +    encoder.set(color_type).set(bitness); +    let mut writer = encoder.write_header().unwrap(); +    writer.write_image_data(data).unwrap(); +    println!("image written!"); +} +  fn main() {      let test = true; -//    println!("Doing qhy..."); -//    operate_qhy(test); -    println!("Doing asi..."); -    operate_asi(test); +    println!("Doing qhy..."); +    let (image_writer, image_reader) = unbounded(); +    let (frame_sender, free_frames) = unbounded(); + +    std::thread::spawn(move || { +        loop { +            select! { +                recv(image_reader) -> msg => { +                    match msg { +                        Ok(ImageInfo { mut data, dimensions, target, image_id }) => { +                            qhyccd::fix_channels_and_endianness(data.as_mut_slice()); +                            record_image(data.as_slice(), dimensions, target, image_id); +                            println!("pretend i wrote image {}_{}", target, image_id); +                            frame_sender.send(data).unwrap(); +                        } +                        Err(RecvError) => { +                            // something in the application has crashed. time 2 die +                            return; +                        } +                    } +                } +            } +        } +    }); + +    operate_qhy("dark_gain_4000", None, free_frames, image_writer); +//    println!("Doing asi..."); +//    operate_asi(test); +} + +#[derive(Debug, Copy, Clone)] +struct Dimensions { +    width: u32, +    height: u32, +    bpp: u8, +    channels: u8,  } -fn operate_qhy(test: bool) { +impl Dimensions { +    pub fn new(width: u32, height: u32, bpp: u8, channels: u8) -> Self { +        Dimensions { width, height, bpp, channels } +    } +} + +struct ImageInfo { +    data: Vec<u8>, +    dimensions: Dimensions, +    target: &'static str, +    image_id: u32 +} + +enum ImageWriter { +    FrameReady(Vec<u8>, Dimensions, &'static str, u32) +} + +fn operate_qhy(target: &'static str, count: Option<u32>, mut free_frames: Receiver<Vec<u8>>, mut image_writer: Sender<ImageInfo>) {      use crate::qhyccd::Control;      println!("Operating on qhy camera ... or i'll die trying"); +    let (mut camera_rx, mut camera_tx) = qhyccd::connect(0).unwrap(); + +    let mut image_id = 0u32; +    let mut settings_copy = qhyccd::Settings::default(); + +    camera_tx.send(QHYMessage::SetControl(Control::Exposure, 20000000.0)).unwrap(); +    camera_tx.send(QHYMessage::SetControl(Control::Gain, 4000.0)).unwrap(); +    camera_tx.send(QHYMessage::SetControl(Control::Offset, 00.0)).unwrap(); +    camera_tx.send(QHYMessage::SetControl(Control::Cooler, 27.0)).unwrap(); +    camera_tx.send(QHYMessage::SetControl(Control::USBTraffic, 60.0)).unwrap(); +    camera_tx.send(QHYMessage::SetControl(Control::Color, 0.0)).unwrap(); // disable color +//    camera.set_roi(0, 0, 1920 * 2, 1080 * 2).unwrap(); +//    println!("Gain: {:?}", camera.get_param_limits(Control::ManulPwm)); +//    println!("cur pwm ???: {}", camera.get_param(Control::CurPWM)); +    camera_tx.send(QHYMessage::BeginCapture).unwrap(); + +    loop { +        select! { +            recv(free_frames) -> msg => { +                match msg { +                    Ok(buffer) => { +                        camera_tx.send(QHYMessage::FrameAvailable(buffer)).unwrap(); +                    }, +                    Err(RecvError) => { +                        // disconnected. nothing we can do but to.. +                        return; +                    } +                } +            }, +            recv(camera_rx) -> msg => { +                match msg { +                    Ok(QHYResponse::CurrentControlValue(control, value)) => { +                        println!("Control {:?} value: {}", control, value); +                    } +                    Ok(QHYResponse::InitializationError) => { +                        println!("Failed to initialize camera, exiting..."); +                        return; +                    } +                    Ok(QHYResponse::Shutdown) => { +                        return; +                    } +                    Ok(QHYResponse::UpdatedSettings(settings)) => { +                        settings_copy = settings; +                    } +                    Ok(QHYResponse::Data(data, dimensions)) => { +                        image_writer.send(ImageInfo { data, dimensions, target, image_id: image_id }).unwrap(); +                        // images.log(target, image_id, settings_copy); +                        image_id += 1; +                        if Some(image_id) == count { +                            camera_tx.send(QHYMessage::Shutdown); +                        } +                    } +                    Ok(QHYResponse::DroppedFrame) => { +                        println!("Dropped frame..."); +                    } +                    Err(RecvError) => { +                        // camera is closed. hopefully it just shut down? but maybe crashed!! +                        return; +                    } +                } +            } +            default(Duration::from_millis(2000)) => { +                camera_tx.send( +                    QHYMessage::QueryControl(Control::CurTemp) +                ); +            } +        } +    } +    /*      let t = std::thread::spawn(move || {          let mut camera = qhyccd::acquire(0).unwrap();          camera.set_defaults().unwrap(); @@ -52,6 +213,7 @@ fn operate_qhy(test: bool) {          camera.release().unwrap();      });      t.join(); +    */  }  fn operate_asi(test: bool) { diff --git a/src/qhyccd/QHYCCDCam.rs b/src/qhyccd/QHYCCDCam.rs index c0a0775..2f6b4fb 100644 --- a/src/qhyccd/QHYCCDCam.rs +++ b/src/qhyccd/QHYCCDCam.rs @@ -24,7 +24,7 @@ impl From<u32> for QHYResult {  }  #[repr(u32)] -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)]  pub enum Control {      Brightness = 0, // !< image brightness      Contrast = 1,       //1 image contrast  @@ -84,6 +84,74 @@ pub enum Control {      OutputDataAlignment = 55                        //55  } +impl Control { +    pub fn requires_reset(&self) -> bool { +        match self { +            Control::Brightness +            | Control::Contrast +            | Control::CONTROL_WBR +            | Control::CONTROL_WBB +            | Control::CONTROL_WBG +            | Control::Gamma +            | Control::Gain +            | Control::Offset +            | Control::Exposure +            | Control::Speed +            | Control::TransferBit +            | Control::Channels +            | Control::Bin1x1Mode +            | Control::Bin2x2Mode +            | Control::Bin3x3Mode +            | Control::Bin4x4Mode +            | Control::USBTraffic => { +                true +            } +            | Control::CurTemp +            | Control::CurPWM +            | Control::ManulPwm +            | Control::CFWPort +            | Control::Cooler +            | Control::St4port +            | Control::Color +            | Control::CAM_MECHANICALSHUTTER +            | Control::CAM_TRIGER_INTERFACE +            | Control::CAM_TECOVERPROTECT_INTERFACE +            | Control::CAM_SINGNALCLAMP_INTERFACE +            | Control::CAM_FINETONE_INTERFACE +            | Control::CAM_SHUTTERMOTORHEATING_INTERFACE +            | Control::CAM_CALIBRATEFPN_INTERFACE +            | Control::CAM_CHIPTEMPERATURESENSOR_INTERFACE +            | Control::CAM_USBREADOUTSLOWEST_INTERFACE +            | Control::CAM_8BITS +            | Control::CAM_16BITS +            | Control::CAM_GPS +            | Control::CAM_IGNOREOVERSCAN_INTERFACE +            | Control::QHYCCD_3A_AUTOBALANCE +            | Control::QHYCCD_3A_AUTOEXPOSURE +            | Control::QHYCCD_3A_AUTOFOCUS +            | Control::CONTROL_AMPV +            | Control::CONTROL_VCAM +            | Control::CAM_VIEW_MODE +            | Control::CONTROL_CFWSLOTSNUM +            | Control::IS_EXPOSING_DONE +            | Control::ScreenStretchB +            | Control::ScreenStretchW +            | Control::CONTROL_DDR +            | Control::CAM_LIGHT_PERFORMANCE_MODE +            | Control::CAM_QHY5II_GUIDE_MODE +            | Control::DDR_BUFFER_CAPACITY +            | Control::DDR_BUFFER_READ_THRESHOLD +            | Control::DefaultOffset +            | Control::OutputDataActualBits +            | Control::OutputDataAlignment => { +                false +            } +            _ => { false } +        } +    } +} + +  #[repr(u32)]  #[derive(Copy, Clone, Debug)]  pub enum Bayer 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; | 
