// use crate::ASICamera2::BayerPattern; // #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(dead_code)] #![feature(alloc_layout_extra)] mod asicam; mod qhyccd; #[macro_use] extern crate crossbeam_channel; use crossbeam_channel::{Sender, Receiver}; use crossbeam_channel::unbounded; use std::time::Duration; // use std::time::Instant; 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 std::io::Write; use png::HasParameters; fn record_image(data: &[u8], dimensions: Dimensions, target: &'static str, image_id: u32, properties: &Properties) { let props_path = format!("{}_{}.json", target, image_id); let props_dest = Path::new(&props_path); let mut props_file = File::create(props_dest).unwrap(); // TODO: serde_json::serialize props_file.write(properties.stringy().as_bytes()).unwrap(); 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!"); println!(".. writing raw"); let raw_path = format!("{}_{}.raw", target, image_id); // let raw_dest = Path::new(&raw_path); // let mut file = File::create(raw_dest).unwrap(); // file.write(data); } fn main() { println!("Doing qhy..."); let (image_writer, image_reader) = unbounded(); let (frame_sender, free_frames) = unbounded(); std::thread::spawn(move || { use show_image::make_window; let window = make_window("image").expect("can make the window"); loop { select! { recv(image_reader) -> msg => { match msg { Ok(ImageInfo { mut data, dimensions, target, image_id, properties }) => { if dimensions.bpp == 16 { match dimensions.channels { 1 => { qhyccd::fix_endianness(data.as_mut_slice()); }, 3 => { qhyccd::fix_channels_and_endianness(data.as_mut_slice()); } c => { panic!("unsupported channel count: {}", c); } } } // downscales a mono image by `factor` fn downscale(data: &[u8], width: u32, height: u32, factor: u8) -> Vec { let factor = factor as u32; assert!(width % factor == 0); assert!(height % factor == 0); let mut result = vec![0; ((width / factor) * (height / factor)) as usize]; for h in 0..(height / factor) { for w in 0..(width / factor) { let mut acc = 0u32; let srcrow = h * width * factor; let srcpx = srcrow + w * factor; let dstrow = h * (width / factor); for i in 0..factor { for j in 0..factor { acc = acc.saturating_add(data[(srcpx + i as u32 * width + j as u32) as usize] as u32); } } result[(dstrow + w) as usize] = (acc / 4) as u8; } } return result; } // make a 16bpp image into an 8bpp image fn monoize(data: &[u8], width: u32, height: u32) -> Vec { let mut result = vec![0; (width * height) as usize]; for h in 0..height { for w in 0..width { result[(h * width + w) as usize] = data[(h * width + w) as usize * 2]; } } return result; } // for 16bpp aka qhy600m // let monoed = monoize(data.as_slice(), dimensions.width, dimensions.height); let scale_factor = 2; if scale_factor != 1 { let downscaled = downscale(data.as_slice(), dimensions.width, dimensions.height, scale_factor); let image_info = show_image::ImageInfo::mono8(dimensions.width as usize / scale_factor as usize, dimensions.height as usize / scale_factor as usize); let _ = window.set_image(&(downscaled.as_slice(), image_info), "image-001"); } else { let image_info = show_image::ImageInfo::mono8(dimensions.width as usize, dimensions.height as usize); let _ = window.set_image(&(data.as_slice(), image_info), "image-001"); } record_image(data.as_slice(), dimensions, target, image_id, &properties); println!("pretend i wrote image {}_{}", target, image_id); frame_sender.send(data).unwrap(); } Err(recv_error) => { eprintln!("recv error: {:?}", recv_error); // something in the application has crashed. time 2 die return; } } } } } }); operate_qhy("settings", None, free_frames, image_writer); // println!("Doing asi..."); // operate_asi(test); } #[derive(Debug, Copy, Clone)] pub struct Dimensions { width: u32, height: u32, bpp: u8, channels: u8, } impl Dimensions { pub fn new(width: u32, height: u32, bpp: u8, channels: u8) -> Self { Dimensions { width, height, bpp, channels } } } #[derive(Debug)] pub struct Properties { pub device: &'static str, pub exposure_ms: u32, pub gain: u16, pub offset: u16, pub gamma: u16, pub temp: u16, // in hundredths of degrees, C. eg, 100 => 1deg C, } impl Properties { pub fn stringy(&self) -> String { format!("{{\n \"device\": \"{}\",\n \"exposure_ms\": {},\n \"gain\": {},\n \"offset\": {},\n \"gamma\": {},\n \"temp\": {}\n}}", self.device, self.exposure_ms, self.gain, self.offset, self.gamma, self.temp ) } } struct ImageInfo { data: Vec, dimensions: Dimensions, properties: Properties, target: &'static str, image_id: u32 } enum ImageWriter { FrameReady(Vec, Dimensions, &'static str, u32) } fn operate_qhy(target: &'static str, count: Option, free_frames: Receiver>, image_writer: Sender) { use crate::qhyccd::Control; println!("Operating on qhy camera ... or i'll die trying"); let (camera_rx, camera_tx) = qhyccd::connect(0).unwrap(); let mut image_id = 0u32; let mut settings_copy = qhyccd::Settings::default(); eprintln!("settings initialized: {:?}", settings_copy); // qhy376c settings camera_tx.send(QHYMessage::SetControl(Control::Exposure, 20000.0 * 1000.0)).unwrap(); // camera_tx.send(QHYMessage::SetControl(Control::Exposure, 200.0 * 1000.0)).unwrap(); camera_tx.send(QHYMessage::SetControl(Control::Cooler, 0.0)).unwrap(); camera_tx.send(QHYMessage::SetControl(Control::Color, 1.0)).unwrap(); camera_tx.send(QHYMessage::SetControl(Control::Gain, 4000.0)).unwrap(); /* camera_tx.send(QHYMessage::SetControl(Control::Exposure, 60000.0 * 1000.0)).unwrap(); camera_tx.send(QHYMessage::SetControl(Control::Cooler, 0.0)).unwrap(); camera_tx.send(QHYMessage::SetControl(Control::Color, 0.0)).unwrap(); camera_tx.send(QHYMessage::SetControl(Control::Gain, 110.0)).unwrap(); */ /* camera_tx.send(QHYMessage::SetControl(Control::Exposure, 20000.0 * 1000.0)).unwrap(); // camera_tx.send(QHYMessage::SetControl(Control::Offset, 00.0)).unwrap(); camera_tx.send(QHYMessage::SetControl(Control::Gamma, 1.0)).unwrap(); camera_tx.send(QHYMessage::SetControl(Control::Brightness, 00.0)).unwrap(); camera_tx.send(QHYMessage::SetControl(Control::Contrast, 00.0)).unwrap(); // camera_tx.send(QHYMessage::SetControl(Control::Gamma, 2.0)).unwrap(); camera_tx.send(QHYMessage::SetControl(Control::Cooler, 0.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_tx.send(QHYMessage::SetControl(Control::Gain, 4000.0)).unwrap(); // camera_tx.send(QHYMessage::SetControl(Control::Gain, 140.0)).unwrap(); // camera_tx.send(QHYMessage::SetControl(Control::Gain, 4000.0)).unwrap(); // 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(None)).unwrap(); let _LAPSE_PERIOD = Duration::from_millis(0); // let capture_start = Instant::now().checked_sub(Duration::from_millis(10)).unwrap().checked_sub(LAPSE_PERIOD).unwrap(); loop { /* if capture_start.elapsed() > LAPSE_PERIOD { println!("Lapsing!!"); camera_tx.send(QHYMessage::BeginCapture(Some(1))).unwrap(); capture_start = Instant::now(); } */ select! { recv(free_frames) -> msg => { match msg { Ok(buffer) => { camera_tx.send(QHYMessage::FrameAvailable(buffer)).unwrap(); }, Err(recv_error) => { eprintln!("recv error: {:?}", recv_error); // 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; eprintln!("settings updated: {:?}", settings_copy); } Ok(QHYResponse::Data(data, dimensions, properties)) => { image_writer.send(ImageInfo { data, dimensions, target, image_id: image_id, properties}).unwrap(); // images.log(target, image_id, settings_copy); image_id += 1; if Some(image_id) == count { camera_tx.send(QHYMessage::Shutdown).unwrap(); } } Ok(QHYResponse::DroppedFrame) => { println!("Dropped frame..."); } Err(e) => { eprintln!("recv error: {:?}", e); // camera is closed. hopefully it just shut down? but maybe crashed!! return; } } } default(Duration::from_millis(2000)) => { camera_tx.send( QHYMessage::QueryControl(Control::CurTemp) ).unwrap(); } } } } fn operate_asi(_test: bool) { println!("Operating on asi camera ... or i'll die trying"); let mut camera = asicam::acquire(0).unwrap(); println!("{:?}", camera); camera.set_control_value(ControlType::CoolerOn, 1).unwrap(); camera.set_control_value(ControlType::TargetTemp, -200).unwrap(); std::thread::sleep(std::time::Duration::from_millis(500)); println!("Camera temperature is currently {:?}", camera.get_control_value(ControlType::Temperature).unwrap()); camera.set_exposure_ms(30000).unwrap(); // camera.set_control_value(ControlType::Exposure, 70000000).unwrap(); camera.set_control_value(ControlType::Gain, 250).unwrap(); camera.set_control_value(ControlType::Offset, 0).unwrap(); camera.set_control_value(ControlType::HardwareBin, 1).unwrap(); camera.set_roi_format(camera.width, camera.height, 1, ImageType::RAW16).unwrap(); for i in 0..240 { println!("doing image {}", i); println!("Camera temperature is currently {:?}", camera.get_control_value(ControlType::Temperature).unwrap()); camera.take_image(&format!("ngc7380_{}.png", i)).unwrap(); } println!("Done!"); } fn take_calibration_images(camera: &Camera, count: u32, path_fragment: &str) { for i in 0..count { println!("{} image {:06}", path_fragment, i); let temp = camera.get_control_value(ControlType::Temperature).unwrap(); println!("Camera temperature is currently {:?}", temp); camera.take_image(&format!("{}_{:06}_temp_{:03}.png", path_fragment, i, temp)).unwrap(); } }