From af97f13b64baf43680573ff5f683eb899bc433d3 Mon Sep 17 00:00:00 2001 From: xiyehu2 <xiyehu2@illinois.edu> Date: Fri, 30 May 2025 18:55:31 -0500 Subject: [PATCH] added awg control, stable connection Commenting out unused features for Basler and waveform generation Commenting out unused features and making changes to how config is interpreted Refinement of channel and sequence config structure Commenting out basler Commenting out unused features and minor edit to match config.rs added awg control commands added awg init/drop check on any command Commenting out unused features and modified various config check logics according to changes in awg.rs and config.rs under awg-cxx/lib added awg control added back temperature_monitor that got lost in an older commit probably due to some merge conflict sync added awg control --- awg-cxx/build.rs | 16 +- awg-cxx/include/awg.h | 146 +-- awg-cxx/lib/awg.cc | 377 +++--- awg-cxx/lib/awg.rs | 2567 ++++++++++++++++++++-------------------- awg-cxx/lib/awg_ffi.rs | 114 +- awg-cxx/lib/config.rs | 133 ++- awg-cxx/lib/lib.rs | 2 +- awg-cxx/lib/prelude.rs | 28 +- config.toml | 97 +- librs/actions.rs | 121 +- librs/cli.rs | 25 + librs/colors.rs | 18 - librs/config.rs | 1167 +++++++----------- src/cli.rs | 11 +- 14 files changed, 2285 insertions(+), 2537 deletions(-) diff --git a/awg-cxx/build.rs b/awg-cxx/build.rs index 29aff69..bb443af 100644 --- a/awg-cxx/build.rs +++ b/awg-cxx/build.rs @@ -6,21 +6,21 @@ fn main() { println!("cargo:rustc-link-search=/usr/lib64"); println!("cargo:rustc-link-lib=spcm_linux"); - println!("cargo:rustc-link-search=/opt/pylon/lib"); - println!("cargo:rustc-link-lib=pylonbase"); - println!("cargo:rustc-link-lib=pylonutility"); - println!("cargo:rustc-link-lib=GenApi_gcc_v3_1_Basler_pylon"); - println!("cargo:rustc-link-lib=GCBase_gcc_v3_1_Basler_pylon"); + // println!("cargo:rustc-link-search=/opt/pylon/lib"); + // println!("cargo:rustc-link-lib=pylonbase"); + // println!("cargo:rustc-link-lib=pylonutility"); + // println!("cargo:rustc-link-lib=GenApi_gcc_v3_1_Basler_pylon"); + // println!("cargo:rustc-link-lib=GCBase_gcc_v3_1_Basler_pylon"); cxx_build::bridge("lib/awg_ffi.rs") .cpp(true) .warnings(false) .flag("-std=c++14") .include("include") - .include("awg-control/Cpp/lib") + .include("awg-control/Cpp/lib/devices") .include("awg-control/Cpp/lib/devices/driver_header") - .include("/opt/pylon/include") - .include("/usr/include/opencv4") + // .include("/opt/pylon/include") + // .include("/usr/include/opencv4") .file("awg-control/Cpp/lib/devices/AWG.cpp") .file("lib/awg.cc") .compile("awg-cxx"); diff --git a/awg-cxx/include/awg.h b/awg-cxx/include/awg.h index 7df74a2..8b033b1 100644 --- a/awg-cxx/include/awg.h +++ b/awg-cxx/include/awg.h @@ -3,9 +3,9 @@ #include <vector> #include "rust/cxx.h" #include "awg-cxx/awg-control/Cpp/lib/devices/AWG.h" -#include "awg-cxx/awg-control/Cpp/lib/devices/basler.h" -#include "awg-cxx/awg-control/Cpp/lib/waveform.h" -#include "awg-cxx/awg-control/Cpp/lib/uniformization.h" +// #include "awg-cxx/awg-control/Cpp/lib/devices/basler.h" +// #include "awg-cxx/awg-control/Cpp/lib/waveform.h" +// #include "awg-cxx/awg-control/Cpp/lib/uniformization.h" // class PageAlignedMem { // public: @@ -39,41 +39,41 @@ private: AWG awg; }; -class BaslerBox { -public: - BaslerBox(int); - ~BaslerBox(); +// class BaslerBox { +// public: +// BaslerBox(int); +// ~BaslerBox(); - BaslerCam &get_cam(); -private: - BaslerCam cam; -}; +// BaslerCam &get_cam(); +// private: +// BaslerCam cam; +// }; -class UniformizationParams { -public: - UniformizationParams( - std::string source, - double polarizability, - double mean_depth, - double step_size, - double error_threshold, - int max_iters, - int num_imaging_avg, - int num_tweezer - ); - ~UniformizationParams(); - - Uniformization::Params to_raw(); -private: - std::string source; - double polarizability; - double mean_depth; - double step_size; - double error_threshold; - int max_iters; - int num_imaging_avg; - int num_tweezers; -}; +// class UniformizationParams { +// public: +// UniformizationParams( +// std::string source, +// double polarizability, +// double mean_depth, +// double step_size, +// double error_threshold, +// int max_iters, +// int num_imaging_avg, +// int num_tweezer +// ); +// ~UniformizationParams(); + +// Uniformization::Params to_raw(); +// private: +// std::string source; +// double polarizability; +// double mean_depth; +// double step_size; +// double error_threshold; +// int max_iters; +// int num_imaging_avg; +// int num_tweezers; +// }; std::unique_ptr<AWGBox> new_awg(int idx); @@ -150,17 +150,17 @@ int64_t awg_get_clock_out_freq(AWGBox &awg); // void arraywaveform_set_freq_resolution(ArrayWaveform &waveform, ulong resolution); // ulong arraywaveform_get_freq_resolution(ArrayWaveform &waveform); -void arraywaveform_set_freq_tones_spaced(ArrayWaveform &waveform, int center, int spacing, int num_tones); -void arraywaveform_set_freq_tones(ArrayWaveform &waveform, const std::vector<int> &tones); -rust::Vec<int> arraywaveform_get_freq_tones(ArrayWaveform &waveform); -void arraywaveform_set_phases(ArrayWaveform &waveform, const std::vector<double> &phases); -rust::Vec<double> arraywaveform_get_phases(ArrayWaveform &waveform); -void arraywaveform_set_amplitudes(ArrayWaveform &waveform, const std::vector<double>& amplitudes); -rust::Vec<double> arraywaveform_get_amplitudes(ArrayWaveform &waveform); -void arraywaveform_set_default_params(ArrayWaveform &waveform); -void arraywaveform_save_params(ArrayWaveform &waveform, const std::string &filename); -void arraywaveform_load_params(ArrayWaveform &waveform, const std::string &filename); -void arraywaveform_print_params(ArrayWaveform &waveform); +// void arraywaveform_set_freq_tones_spaced(ArrayWaveform &waveform, int center, int spacing, int num_tones); +// void arraywaveform_set_freq_tones(ArrayWaveform &waveform, const std::vector<int> &tones); +// rust::Vec<int> arraywaveform_get_freq_tones(ArrayWaveform &waveform); +// void arraywaveform_set_phases(ArrayWaveform &waveform, const std::vector<double> &phases); +// rust::Vec<double> arraywaveform_get_phases(ArrayWaveform &waveform); +// void arraywaveform_set_amplitudes(ArrayWaveform &waveform, const std::vector<double>& amplitudes); +// rust::Vec<double> arraywaveform_get_amplitudes(ArrayWaveform &waveform); +// void arraywaveform_set_default_params(ArrayWaveform &waveform); +// void arraywaveform_save_params(ArrayWaveform &waveform, const std::string &filename); +// void arraywaveform_load_params(ArrayWaveform &waveform, const std::string &filename); +// void arraywaveform_print_params(ArrayWaveform &waveform); // ulong arraywaveform_get_min_sample_len(ArrayWaveform &waveform, ulong sampling_rate, ulong frequency_resolution); // ulong arraywaveform_get_sample_len(ArrayWaveform &waveform, double tau, ulong sampling_rate, ulong frequency_resolution); @@ -168,38 +168,38 @@ void arraywaveform_print_params(ArrayWaveform &waveform); // std::unique_ptr<WaveformData> arraywaveform_get_static_waveform(ArrayWaveform &waveform); // std::unique_ptr<WaveformData> arraywaveform_get_trick_waveform(ArrayWaveform &waveform, const std::vector<int> &site_index, double df, double tau_move, double tau_stay); -void arraywaveform_save_waveform(ArrayWaveform &waveform, const std::string &filename); +// void arraywaveform_save_waveform(ArrayWaveform &waveform, const std::string &filename); -std::unique_ptr<PageAlignedMem> waveformdata_get_mem(WaveformData &data); -int64_t waveformdata_get_datalen(WaveformData &data); +// std::unique_ptr<PageAlignedMem> waveformdata_get_mem(WaveformData &data); +// int64_t waveformdata_get_datalen(WaveformData &data); /******************************************************************************/ -std::unique_ptr<BaslerBox> new_basler(int index); - -void basler_print_device_info(BaslerBox &basler); -void basler_set_exposure(BaslerBox &basler, double exposure_time); -double basler_get_exposure(BaslerBox &basler); -void basler_set_frame_rate(BaslerBox &basler, uint frame_rate); -void basler_set_frame_rate_max(BaslerBox &basler); -void basler_set_gain(BaslerBox &basler, double gain); -double basler_get_gain(BaslerBox &basler); -void basler_set_roi(BaslerBox &basler, uint offset_x, uint offset_y, uint width, uint height); -uint basler_get_offset_x(BaslerBox &basler); -uint basler_get_offset_y(BaslerBox &basler); -uint basler_get_width(BaslerBox &basler); -uint basler_get_height(BaslerBox &basler); -void basler_set_acquisition_mode(BaslerBox &basler, const std::string &mode); -void basler_print_available_acquisition_modes(BaslerBox &basler); - -void basler_start_grabbing(BaslerBox &basler); -void basler_stop_grabbing(BaslerBox &basler); -bool basler_is_grabbing(BaslerBox &basler); -rust::Vec<uint8_t> basler_get_image(BaslerBox &basler); +// std::unique_ptr<BaslerBox> new_basler(int index); + +// void basler_print_device_info(BaslerBox &basler); +// void basler_set_exposure(BaslerBox &basler, double exposure_time); +// double basler_get_exposure(BaslerBox &basler); +// void basler_set_frame_rate(BaslerBox &basler, uint frame_rate); +// void basler_set_frame_rate_max(BaslerBox &basler); +// void basler_set_gain(BaslerBox &basler, double gain); +// double basler_get_gain(BaslerBox &basler); +// void basler_set_roi(BaslerBox &basler, uint offset_x, uint offset_y, uint width, uint height); +// uint basler_get_offset_x(BaslerBox &basler); +// uint basler_get_offset_y(BaslerBox &basler); +// uint basler_get_width(BaslerBox &basler); +// uint basler_get_height(BaslerBox &basler); +// void basler_set_acquisition_mode(BaslerBox &basler, const std::string &mode); +// void basler_print_available_acquisition_modes(BaslerBox &basler); + +// void basler_start_grabbing(BaslerBox &basler); +// void basler_stop_grabbing(BaslerBox &basler); +// bool basler_is_grabbing(BaslerBox &basler); +// rust::Vec<uint8_t> basler_get_image(BaslerBox &basler); /******************************************************************************/ -std::unique_ptr<UniformizationParams> new_uniformizationparams(const std::string &source, double polarizability, double mean_depth, double step_size, double error_threshold, int max_iters, int num_imaging_avg, int num_tweezers); +// std::unique_ptr<UniformizationParams> new_uniformizationparams(const std::string &source, double polarizability, double mean_depth, double step_size, double error_threshold, int max_iters, int num_imaging_avg, int num_tweezers); -void run_uniformization(AWGBox &awg, BaslerBox &basler, ArrayWaveform &waveform, const UniformizationParams ¶ms); +// void run_uniformization(AWGBox &awg, BaslerBox &basler, ArrayWaveform &waveform, const UniformizationParams ¶ms); diff --git a/awg-cxx/lib/awg.cc b/awg-cxx/lib/awg.cc index d806aa3..9088e17 100644 --- a/awg-cxx/lib/awg.cc +++ b/awg-cxx/lib/awg.cc @@ -1,5 +1,5 @@ #include "awg-cxx/awg-control/Cpp/lib/devices/AWG.h" -#include "awg-cxx/awg-control/Cpp/lib/devices/basler.h" +// #include "awg-cxx/awg-control/Cpp/lib/devices/basler.h" #include "awg-cxx/include/awg.h" #include "rust/cxx.h" @@ -215,6 +215,7 @@ AWGBox::AWGBox(int idx) { } AWGBox::~AWGBox() { + this->awg.cardStop(); this->awg.close(); } @@ -562,10 +563,10 @@ int64_t awg_get_clock_out_freq(AWGBox &awg) { // waveform.setFreqTone(tones); // } -rust::Vec<int> arraywaveform_get_freq_tones(ArrayWaveform &waveform) { - std::vector<int> tones = waveform.getFreqTone(); - return conv_to_int_vec(tones); -} +// rust::Vec<int> arraywaveform_get_freq_tones(ArrayWaveform &waveform) { +// std::vector<int> tones = waveform.getFreqTone(); +// return conv_to_int_vec(tones); +// } // void arraywaveform_set_phases( // ArrayWaveform &waveform, @@ -574,10 +575,10 @@ rust::Vec<int> arraywaveform_get_freq_tones(ArrayWaveform &waveform) { // waveform.setPhase(phases); // } -rust::Vec<double> arraywaveform_get_phases(ArrayWaveform &waveform) { - std::vector<double> phases = waveform.getPhase(); - return conv_to_f64_vec(phases); -} +// rust::Vec<double> arraywaveform_get_phases(ArrayWaveform &waveform) { +// std::vector<double> phases = waveform.getPhase(); +// return conv_to_f64_vec(phases); +// } // void arraywaveform_set_amplitudes( // ArrayWaveform &waveform, @@ -586,10 +587,10 @@ rust::Vec<double> arraywaveform_get_phases(ArrayWaveform &waveform) { // waveform.setAmplitude(amplitudes); // } -rust::Vec<double> arraywaveform_get_amplitudes(ArrayWaveform &waveform) { - std::vector<double> amplitudes = waveform.getAmplitude(); - return conv_to_f64_vec(amplitudes); -} +// rust::Vec<double> arraywaveform_get_amplitudes(ArrayWaveform &waveform) { +// std::vector<double> amplitudes = waveform.getAmplitude(); +// return conv_to_f64_vec(amplitudes); +// } // void arraywaveform_set_default_params(ArrayWaveform &waveform) { // waveform.setDefaultParam(); @@ -630,36 +631,36 @@ rust::Vec<double> arraywaveform_get_amplitudes(ArrayWaveform &waveform) { // return waveform.getSampleLen(tau, sampling_rate, frequency_resolution); // } -std::unique_ptr<WaveformData> arraywaveform_get_static_waveform( - ArrayWaveform &waveform -) { - std::pair<void *, int64_t> memsize = waveform.getStaticWaveform(); - std::unique_ptr<WaveformData> - data_ptr_unique(new WaveformData(memsize.first, memsize.second)); - // auto [mem, datalen] = waveform.getStaticWaveform(); - // std::unique_ptr<WaveformData> - // data_ptr_unique(new WaveformData(mem, datalen)); - return data_ptr_unique; -} - -std::unique_ptr<WaveformData> arraywaveform_get_trick_waveform( - ArrayWaveform &waveform, - const std::vector<int> &site_index, - double df, - double tau_move, - double tau_stay -) { - std::set<int> sites = int_vector_to_int_set(site_index); - std::pair<void *, int64_t> memsize - = waveform.getTrickWaveform(sites, df, tau_move, tau_stay); - std::unique_ptr<WaveformData> - data_ptr_unique(new WaveformData(memsize.first, memsize.second)); - // auto [mem, datalen] - // = waveform.getTrickWaveform(sites, df, tau_move, tau_stay); - // std::unique_ptr<WaveformData> - // data_ptr_unique(new WaveformData(mem, datalen)); - return data_ptr_unique; -} +// std::unique_ptr<WaveformData> arraywaveform_get_static_waveform( +// ArrayWaveform &waveform +// ) { +// std::pair<void *, int64_t> memsize = waveform.getStaticWaveform(); +// std::unique_ptr<WaveformData> +// data_ptr_unique(new WaveformData(memsize.first, memsize.second)); +// // auto [mem, datalen] = waveform.getStaticWaveform(); +// // std::unique_ptr<WaveformData> +// // data_ptr_unique(new WaveformData(mem, datalen)); +// return data_ptr_unique; +// } + +// std::unique_ptr<WaveformData> arraywaveform_get_trick_waveform( +// ArrayWaveform &waveform, +// const std::vector<int> &site_index, +// double df, +// double tau_move, +// double tau_stay +// ) { +// std::set<int> sites = int_vector_to_int_set(site_index); +// std::pair<void *, int64_t> memsize +// = waveform.getTrickWaveform(sites, df, tau_move, tau_stay); +// std::unique_ptr<WaveformData> +// data_ptr_unique(new WaveformData(memsize.first, memsize.second)); +// // auto [mem, datalen] +// // = waveform.getTrickWaveform(sites, df, tau_move, tau_stay); +// // std::unique_ptr<WaveformData> +// // data_ptr_unique(new WaveformData(mem, datalen)); +// return data_ptr_unique; +// } // void arraywaveform_save_waveform( // ArrayWaveform &waveform, @@ -676,176 +677,176 @@ std::unique_ptr<WaveformData> arraywaveform_get_trick_waveform( // return mem_ptr_unique; // } -int64_t waveformdata_get_datalen(WaveformData &data) { - return data.get_datalen(); -} +// int64_t waveformdata_get_datalen(WaveformData &data) { +// return data.get_datalen(); +// } /******************************************************************************/ -BaslerBox::BaslerBox(int index) { - this->cam.open(index); -} +// BaslerBox::BaslerBox(int index) { +// this->cam.open(index); +// } -BaslerBox::~BaslerBox() { } +// BaslerBox::~BaslerBox() { } -BaslerCam &BaslerBox::get_cam() { - return this->cam; -} +// BaslerCam &BaslerBox::get_cam() { +// return this->cam; +// } -std::unique_ptr<BaslerBox> new_basler(int index) { - std::unique_ptr<BaslerBox> - basler_ptr_unique(new BaslerBox(index)); - return basler_ptr_unique; -} +// std::unique_ptr<BaslerBox> new_basler(int index) { +// std::unique_ptr<BaslerBox> +// basler_ptr_unique(new BaslerBox(index)); +// return basler_ptr_unique; +// } -void basler_print_device_info(BaslerBox &basler) { - basler.get_cam().printDeviceInfo(); -} +// void basler_print_device_info(BaslerBox &basler) { +// basler.get_cam().printDeviceInfo(); +// } -void basler_set_exposure(BaslerBox &basler, double exposure_time) { - basler.get_cam().setExposure(exposure_time); -} +// void basler_set_exposure(BaslerBox &basler, double exposure_time) { +// basler.get_cam().setExposure(exposure_time); +// } -double basler_get_exposure(BaslerBox &basler) { - return basler.get_cam().getExposure(); -} +// double basler_get_exposure(BaslerBox &basler) { +// return basler.get_cam().getExposure(); +// } -void basler_set_frame_rate(BaslerBox &basler, uint frame_rate) { - basler.get_cam().setFrameRate(frame_rate); -} +// void basler_set_frame_rate(BaslerBox &basler, uint frame_rate) { +// basler.get_cam().setFrameRate(frame_rate); +// } -void basler_set_frame_rate_max(BaslerBox &basler) { - basler.get_cam().setFrameRateMax(); -} +// void basler_set_frame_rate_max(BaslerBox &basler) { +// basler.get_cam().setFrameRateMax(); +// } -void basler_set_gain(BaslerBox &basler, double gain) { - basler.get_cam().setGain(gain); -} +// void basler_set_gain(BaslerBox &basler, double gain) { +// basler.get_cam().setGain(gain); +// } -double basler_get_gain(BaslerBox &basler) { - return basler.get_cam().getGain(); -} +// double basler_get_gain(BaslerBox &basler) { +// return basler.get_cam().getGain(); +// } -void basler_set_roi(BaslerBox &basler, uint offset_x, uint offset_y, uint width, uint height) { - basler.get_cam().setROI(offset_x, offset_y, width, height); -} +// void basler_set_roi(BaslerBox &basler, uint offset_x, uint offset_y, uint width, uint height) { +// basler.get_cam().setROI(offset_x, offset_y, width, height); +// } -uint basler_get_offset_x(BaslerBox &basler) { - return basler.get_cam().getOffsetX(); -} +// uint basler_get_offset_x(BaslerBox &basler) { +// return basler.get_cam().getOffsetX(); +// } -uint basler_get_offset_y(BaslerBox &basler) { - return basler.get_cam().getOffsetY(); -} +// uint basler_get_offset_y(BaslerBox &basler) { +// return basler.get_cam().getOffsetY(); +// } -uint basler_get_width(BaslerBox &basler) { - return basler.get_cam().getWidth(); -} +// uint basler_get_width(BaslerBox &basler) { +// return basler.get_cam().getWidth(); +// } -uint basler_get_height(BaslerBox &basler) { - return basler.get_cam().getHeight(); -} +// uint basler_get_height(BaslerBox &basler) { +// return basler.get_cam().getHeight(); +// } -void basler_set_acquisition_mode(BaslerBox &basler, const std::string &mode) { - basler.get_cam().setAcquisitionMode(mode); -} +// void basler_set_acquisition_mode(BaslerBox &basler, const std::string &mode) { +// basler.get_cam().setAcquisitionMode(mode); +// } -void basler_print_available_acquisition_modes(BaslerBox &basler) { - basler.get_cam().printAvailAcqModes(); -} +// void basler_print_available_acquisition_modes(BaslerBox &basler) { +// basler.get_cam().printAvailAcqModes(); +// } -void basler_start_grabbing(BaslerBox &basler) { - basler.get_cam().startGrabbing(); -} +// void basler_start_grabbing(BaslerBox &basler) { +// basler.get_cam().startGrabbing(); +// } -void basler_stop_grabbing(BaslerBox &basler) { - basler.get_cam().stopGrabbing(); -} +// void basler_stop_grabbing(BaslerBox &basler) { +// basler.get_cam().stopGrabbing(); +// } -bool basler_is_grabbing(BaslerBox &basler) { - return basler.get_cam().isGrabbing(); -} +// bool basler_is_grabbing(BaslerBox &basler) { +// return basler.get_cam().isGrabbing(); +// } -rust::Vec<uint8_t> basler_get_image(BaslerBox &basler) { - std::vector<uint8_t> image = basler.get_cam().getImage(); - return to_u8_vec(image); -} +// rust::Vec<uint8_t> basler_get_image(BaslerBox &basler) { +// std::vector<uint8_t> image = basler.get_cam().getImage(); +// return to_u8_vec(image); +// } -/******************************************************************************/ +// /******************************************************************************/ -UniformizationParams::UniformizationParams( - std::string source, - double polarizability, - double mean_depth, - double step_size, - double error_threshold, - int max_iters, - int num_imaging_avg, - int num_tweezers -) { - this->source = source; - this->polarizability = polarizability; - this->mean_depth = mean_depth; - this->step_size = step_size; - this->error_threshold = error_threshold; - this->max_iters = max_iters; - this->num_imaging_avg = num_imaging_avg; - this->num_tweezers = num_tweezers; -} - -UniformizationParams::~UniformizationParams() { } - -Uniformization::Params UniformizationParams::to_raw() { - Uniformization::Params params; - params.probeScanPath = this->source; - params.polarizability = this->polarizability; - params.meanDepth = this->mean_depth; - params.stepSize = this->step_size; - params.errorThreshold = this->error_threshold; - params.maxLoop = this->max_iters; - params.numImgingAvg = this->num_imaging_avg; - params.numTweezer = this->num_tweezers; - return params; -} - -std::unique_ptr<UniformizationParams> new_uniformizationparams( - const std::string &source, - double polarizability, - double mean_depth, - double step_size, - double error_threshold, - int max_iters, - int num_imaging_avg, - int num_tweezers -) { - std::unique_ptr<UniformizationParams> - params_ptr_unique( - new UniformizationParams( - source, - polarizability, - mean_depth, - step_size, - error_threshold, - max_iters, - num_imaging_avg, - num_tweezers - ) - ); - return params_ptr_unique; -} +// UniformizationParams::UniformizationParams( +// std::string source, +// double polarizability, +// double mean_depth, +// double step_size, +// double error_threshold, +// int max_iters, +// int num_imaging_avg, +// int num_tweezers +// ) { +// this->source = source; +// this->polarizability = polarizability; +// this->mean_depth = mean_depth; +// this->step_size = step_size; +// this->error_threshold = error_threshold; +// this->max_iters = max_iters; +// this->num_imaging_avg = num_imaging_avg; +// this->num_tweezers = num_tweezers; +// } -void run_uniformization( - AWGBox &awg, - BaslerBox &basler, - ArrayWaveform &waveform, - UniformizationParams ¶ms -) { - const Uniformization::Params raw_params = params.to_raw(); - Uniformization::run( - awg.get_awg(), - basler.get_cam(), - waveform, - raw_params - ); -} +// UniformizationParams::~UniformizationParams() { } + +// Uniformization::Params UniformizationParams::to_raw() { +// Uniformization::Params params; +// params.probeScanPath = this->source; +// params.polarizability = this->polarizability; +// params.meanDepth = this->mean_depth; +// params.stepSize = this->step_size; +// params.errorThreshold = this->error_threshold; +// params.maxLoop = this->max_iters; +// params.numImgingAvg = this->num_imaging_avg; +// params.numTweezer = this->num_tweezers; +// return params; +// } + +// std::unique_ptr<UniformizationParams> new_uniformizationparams( +// const std::string &source, +// double polarizability, +// double mean_depth, +// double step_size, +// double error_threshold, +// int max_iters, +// int num_imaging_avg, +// int num_tweezers +// ) { +// std::unique_ptr<UniformizationParams> +// params_ptr_unique( +// new UniformizationParams( +// source, +// polarizability, +// mean_depth, +// step_size, +// error_threshold, +// max_iters, +// num_imaging_avg, +// num_tweezers +// ) +// ); +// return params_ptr_unique; +// } + +// void run_uniformization( +// AWGBox &awg, +// BaslerBox &basler, +// ArrayWaveform &waveform, +// UniformizationParams ¶ms +// ) { +// const Uniformization::Params raw_params = params.to_raw(); +// Uniformization::run( +// awg.get_awg(), +// basler.get_cam(), +// waveform, +// raw_params +// ); +// } diff --git a/awg-cxx/lib/awg.rs b/awg-cxx/lib/awg.rs index a5d1a72..09c698f 100644 --- a/awg-cxx/lib/awg.rs +++ b/awg-cxx/lib/awg.rs @@ -7,23 +7,24 @@ use std::{ collections::HashSet, ffi::{ OsStr, OsString }, - f64::consts::TAU, - fmt, - fs, - io::{ Read, Write }, - path::{ Path, PathBuf }, + // f64::consts::TAU, + // fmt, + // fs, + // io::{ Read, Write }, + // path::{ Path, PathBuf }, + path::{ Path }, pin::Pin, }; use cxx::{ let_cxx_string, CxxVector, UniquePtr, kind::Trivial }; use thiserror::Error; use crate::{ awg_ffi::ffi, - basler::Basler, + // basler::Basler, config::{ TriggerConfig, ChannelConfig, - SequenceStepConfig, - SequenceKind, + SequenceSegmentsConfig, + // SequenceKind, Config, ConfigError, }, @@ -43,9 +44,6 @@ pub enum AWGError { // #[error("bad waveform filename '{0:?}': must end in '.csv'")] // WaveformCSV(OsString), - #[error("bad waveform data filename '{0:?}': must end in '.txt'")] - WaveformTXT(OsString), - // #[error("uninitialized waveform parameters")] // WaveformUninit, @@ -82,38 +80,38 @@ pub enum AWGError { // #[error("invalid amplitude setting: number of amplitudes must match frequency tone and phase settings")] // NumAmplitudes, - #[error("invalid amplitude {0}: must be [0, 2^15 - 1]")] - InvalidAmplitude(f64), + // #[error("invalid waveform amplitude {0}: must be 0..2^15-1")] + // InvalidAmplitude(f64), - #[error("uniformization error: {0}")] - UniformizationError(String), + // #[error("uniformization error: {0}")] + // UniformizationError(String), - #[error("uninitialized uniformization parameters")] - UniformizationUninit, + // #[error("uninitialized uniformization parameters")] + // UniformizationUninit, - #[error("invalid uniformization waveform source '{0}': file does not exist")] - InvalidUniformizationSource(String), + // #[error("invalid uniformization waveform source '{0}': file does not exist")] + // InvalidUniformizationSource(String), - #[error("invalid polarizability {0}: setting only makes sense for negative values")] - InvalidPolarizability(f64), + // #[error("invalid polarizability {0}: setting only makes sense for negative values")] + // InvalidPolarizability(f64), - #[error("invalid mean depth {0}: setting only makes sense for positive values")] - InvalidMeanDepth(f64), + // #[error("invalid mean depth {0}: setting only makes sense for positive values")] + // InvalidMeanDepth(f64), - #[error("invalid step size {0}: setting only makes sense for positive values")] - InvalidStepSize(f64), + // #[error("invalid step size {0}: setting only makes sense for positive values")] + // InvalidStepSize(f64), - #[error("invalid error threshold {0}: setting only makes sense for positive values")] - InvalidErrorThreshold(f64), + // #[error("invalid error threshold {0}: setting only makes sense for positive values")] + // InvalidErrorThreshold(f64), - #[error("invalid max iters {0}: must be at least 1")] - InvalidMaxIters(usize), + // #[error("invalid max iters {0}: must be at least 1")] + // InvalidMaxIters(usize), - #[error("invalid imaging average {0}: must be at least 1")] - InvalidNumImagingAvg(usize), + // #[error("invalid imaging average {0}: must be at least 1")] + // InvalidNumImagingAvg(usize), - #[error("invalid tweezer array size {0}: must be at least 1")] - InvalidNumTweezers(usize), + // #[error("invalid tweezer array size {0}: must be at least 1")] + // InvalidNumTweezers(usize), #[error("config error: {0}")] ConfigError(#[from] ConfigError), @@ -125,13 +123,13 @@ impl AWGError { Self::DeviceError(except.what().to_string()) } - fn as_waveform_err(except: cxx::Exception) -> Self { - Self::WaveformError(except.what().to_string()) - } + // fn as_waveform_err(except: cxx::Exception) -> Self { + // Self::WaveformError(except.what().to_string()) + // } - fn as_uniformization_err(except: cxx::Exception) -> Self { - Self::UniformizationError(except.what().to_string()) - } + // fn as_uniformization_err(except: cxx::Exception) -> Self { + // Self::UniformizationError(except.what().to_string()) + // } } fn collect_cxx_vector<'a, I, T>(vals: I) -> UniquePtr<CxxVector<T>> @@ -157,58 +155,58 @@ macro_rules! impl_debug_boring { /// Represents a thin wrapper around a (thin wrapper around a) raw C++ `void*` /// pointing to a page-aligned slice of memory. -pub struct PageAlignedMem<'a> { - ptr: UniquePtr<ffi::PageAlignedMem>, - wf_ref: &'a ArrayWaveform, -} +// pub struct PageAlignedMem<'a> { +// ptr: UniquePtr<ffi::PageAlignedMem>, +// wf_ref: &'a ArrayWaveform, +// } -impl<'a> std::fmt::Debug for PageAlignedMem<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "PageAlignedMem") - } -} +// impl<'a> std::fmt::Debug for PageAlignedMem<'a> { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// write!(f, "PageAlignedMem") +// } +// } -impl<'a> PageAlignedMem<'a> { - fn pinned(&mut self) -> Pin<&mut ffi::PageAlignedMem> { self.ptr.pin_mut() } -} +// impl<'a> PageAlignedMem<'a> { +// fn pinned(&mut self) -> Pin<&mut ffi::PageAlignedMem> { self.ptr.pin_mut() } +// } /// Thin wrapper around a raw C++ `void*` pointing to a waveform array stored at /// a page-aligned slice of memory, along with the array's length. -pub struct WaveformDataPtr<'a> { - ptr: UniquePtr<ffi::WaveformData>, - wf_ref: &'a ArrayWaveform, -} - -impl<'a> std::fmt::Debug for WaveformDataPtr<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "WaveformDataPtr") - } -} - -impl<'a> TryFrom<WaveformDataPtr<'a>> for (PageAlignedMem<'a>, u64) { - type Error = AWGError; - - fn try_from(wf_data_ptr: WaveformDataPtr<'a>) -> AWGResult<Self> { - wf_data_ptr.into_memsize() - } -} - -impl<'a> WaveformDataPtr<'a> { - fn pinned(&mut self) -> Pin<&mut ffi::WaveformData> { self.ptr.pin_mut() } - - fn into_memsize(self) -> AWGResult<(PageAlignedMem<'a>, u64)> { - unsafe { - let Self { mut ptr, wf_ref } = self; - ffi::waveformdata_get_mem(ptr.pin_mut()) - .map(|ptr| PageAlignedMem { ptr, wf_ref }) - .map_err(AWGError::as_waveform_err) - .and_then(|buf| { - ffi::waveformdata_get_datalen(ptr.pin_mut()) - .map(|len| (buf, len as u64)) - .map_err(AWGError::as_waveform_err) - }) - } - } +// pub struct WaveformDataPtr<'a> { +// ptr: UniquePtr<ffi::WaveformData>, +// wf_ref: &'a ArrayWaveform, +// } + +// impl<'a> std::fmt::Debug for WaveformDataPtr<'a> { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// write!(f, "WaveformDataPtr") +// } +// } + +// impl<'a> TryFrom<WaveformDataPtr<'a>> for (PageAlignedMem<'a>, u64) { +// type Error = AWGError; + +// fn try_from(wf_data_ptr: WaveformDataPtr<'a>) -> AWGResult<Self> { +// wf_data_ptr.into_memsize() +// } +// } + +// impl<'a> WaveformDataPtr<'a> { +// fn pinned(&mut self) -> Pin<&mut ffi::WaveformData> { self.ptr.pin_mut() } + +// fn into_memsize(self) -> AWGResult<(PageAlignedMem<'a>, u64)> { +// unsafe { +// let Self { mut ptr, wf_ref } = self; +// ffi::waveformdata_get_mem(ptr.pin_mut()) +// .map(|ptr| PageAlignedMem { ptr, wf_ref }) +// .map_err(AWGError::as_waveform_err) +// .and_then(|buf| { +// ffi::waveformdata_get_datalen(ptr.pin_mut()) +// .map(|len| (buf, len as u64)) +// .map_err(AWGError::as_waveform_err) +// }) +// } +// } // pub fn get_mem(&mut self) -> AWGResult<PageAlignedMem> { // unsafe { @@ -225,7 +223,7 @@ impl<'a> WaveformDataPtr<'a> { // .map_err(|err| AWGError::WaveformError(err.to_string())) // } // } -} +// } macro_rules! cpp_enum { ( @@ -490,24 +488,6 @@ cpp_enum!( } ); -/// AWG card index. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum AWGIndex { - Zero = 0, - One = 1, -} - -impl TryFrom<i32> for AWGIndex { - type Error = AWGError; - - fn try_from(i: i32) -> AWGResult<Self> { - match i { - 0 => Ok(Self::Zero), - 1 => Ok(Self::One), - x => Err(AWGError::InvalidCardIndex(x)), - } - } -} /// AWG channel pair for use in [`AWG::set_channel_diff_mode`]. #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -573,7 +553,7 @@ impl_debug_boring!(AWG); impl AWG { /// Open a new connection to the AWG by index. - pub fn connect(idx: AWGIndex) -> AWGResult<Self> { + pub fn connect(idx: usize) -> AWGResult<Self> { unsafe { ffi::new_awg(idx as i32) .map(|awg| Self { awg }) @@ -596,11 +576,10 @@ impl AWG { } /// Get the AWG card index. - pub fn get_card_idx(&mut self) -> AWGResult<AWGIndex> { + pub fn get_card_idx(&mut self) -> AWGResult<i32> { unsafe { ffi::awg_get_card_idx(self.pinned()) .map_err(AWGError::as_device_err) - .and_then(|idx| idx.try_into()) } } @@ -997,7 +976,7 @@ impl AWG { } /// Initialize sequence replay mode. - pub fn init_replay_mode_seq(&mut self, n_segments: usize) + pub fn init_replay_mode_seq(&mut self, n_segments: u32) -> AWGResult<&mut Self> { unsafe { @@ -1032,70 +1011,90 @@ impl AWG { } /// Write buffered data into a memory segment in sequence replay mode. - pub fn write_seq_mode_segment<'a, P>( + // pub fn write_seq_mode_segment<'a, P>( + // &mut self, + // segment: i32, + // data_buffer: P, + // ) -> AWGResult<&mut Self> + // where P: TryInto<(PageAlignedMem<'a>, u64), Error = AWGError> + // { + // let (mut p_data_buffer, size) = data_buffer.try_into()?; + // unsafe { + // ffi::awg_write_seq_mode_segment( + // self.pinned(), + // segment, + // p_data_buffer.pinned(), + // size as i32, + // ) + // .map_err(AWGError::as_device_err)?; + // Ok(self) + // } + // } + + pub fn write_seq_mode_segment<P>( &mut self, segment: i32, - data_buffer: P, + filename: P, ) -> AWGResult<&mut Self> - where P: TryInto<(PageAlignedMem<'a>, u64), Error = AWGError> + where P: AsRef<Path> { - let (mut p_data_buffer, size) = data_buffer.try_into()?; unsafe { - ffi::awg_write_seq_mode_segment( - self.pinned(), - segment, - p_data_buffer.pinned(), - size as i32, - ) - .map_err(AWGError::as_device_err)?; + let path_osstr: &OsStr = filename.as_ref().as_os_str(); + let path_str: &str + = path_osstr.to_str() + .ok_or(AWGError::PathError(path_osstr.to_os_string()))?; + let_cxx_string!(fname = path_str); + ffi::awg_write_seq_mode_segment(self.pinned(), segment, &fname) + .map_err(AWGError::as_device_err)?; Ok(self) } + } /// Prepare the board for data transfer. - pub fn prep_data_transfer<'a, P>( - &mut self, - data_buffer: P, - buf_type: BufferType, - dir: TransferDir, - notify_size: u32, - board_mem_offs: u64, - ) -> AWGResult<&mut Self> - where P: TryInto<(PageAlignedMem<'a>, u64), Error = AWGError> - { - let (mut p_data_buffer, buffer_len) = data_buffer.try_into()?; - unsafe { - ffi::awg_prep_data_transfer( - self.pinned(), - p_data_buffer.pinned(), - buffer_len, - buf_type as i32, - dir as i32, - notify_size, - board_mem_offs, - ) - .map_err(AWGError::as_device_err)?; - Ok(self) - } - } + // pub fn prep_data_transfer<'a, P>( + // &mut self, + // data_buffer: P, + // buf_type: BufferType, + // dir: TransferDir, + // notify_size: u32, + // board_mem_offs: u64, + // ) -> AWGResult<&mut Self> + // where P: TryInto<(PageAlignedMem<'a>, u64), Error = AWGError> + // { + // let (mut p_data_buffer, buffer_len) = data_buffer.try_into()?; + // unsafe { + // ffi::awg_prep_data_transfer( + // self.pinned(), + // p_data_buffer.pinned(), + // buffer_len, + // buf_type as i32, + // dir as i32, + // notify_size, + // board_mem_offs, + // ) + // .map_err(AWGError::as_device_err)?; + // Ok(self) + // } + // } - /// Start a data transfer. - pub fn start_data_transfer(&mut self) -> AWGResult<&mut Self> { - unsafe { - ffi::awg_start_data_transfer(self.pinned()) - .map_err(AWGError::as_device_err)?; - Ok(self) - } - } + // /// Start a data transfer. + // pub fn start_data_transfer(&mut self) -> AWGResult<&mut Self> { + // unsafe { + // ffi::awg_start_data_transfer(self.pinned()) + // .map_err(AWGError::as_device_err)?; + // Ok(self) + // } + // } - /// Stop the current data transfer. - pub fn stop_data_transfer(&mut self) -> AWGResult<&mut Self> { - unsafe { - ffi::awg_stop_data_transfer(self.pinned()) - .map_err(AWGError::as_device_err)?; - Ok(self) - } - } + // /// Stop the current data transfer. + // pub fn stop_data_transfer(&mut self) -> AWGResult<&mut Self> { + // unsafe { + // ffi::awg_stop_data_transfer(self.pinned()) + // .map_err(AWGError::as_device_err)?; + // Ok(self) + // } + // } /// Set the clock mode. pub fn set_clock_mode(&mut self, mode: ClockMode) -> AWGResult<&mut Self> { @@ -1146,129 +1145,129 @@ impl AWG { pub fn set_trigger_config(&mut self, config: &TriggerConfig) -> AWGResult<&mut Self> { - // validate before actually setting anything - config.check()?; + // config validation should have already been done at this point // channel termination impedance for both channels self.set_trig_term(config.termination)?; // ext0 settings self.set_trig_mode(TriggerChannel::Ext0, config.ext0.mode)?; self.set_trig_level(TriggerChannel::Ext0, config.ext0.level)?; self.set_trig_rearm_level(TriggerChannel::Ext0, config.ext0.rearm_level)?; + self.set_trig_coupling(TriggerChannel::Ext0, config.ext0.coupling)?; // ext1 settings self.set_trig_mode(TriggerChannel::Ext1, config.ext1.mode)?; self.set_trig_level(TriggerChannel::Ext1, config.ext1.level)?; self.set_trig_rearm_level(TriggerChannel::Ext1, config.ext1.rearm_level)?; + self.set_trig_coupling(TriggerChannel::Ext1, config.ext1.coupling)?; // masks + // self.set_trig_mask_and(&config.masks_and)?; self.set_trig_mask_or(&config.masks_or)?; - self.set_trig_mask_and(&config.masks_and)?; Ok(self) } - pub fn set_channels_config(&mut self, channels: &[ChannelConfig]) + pub fn set_channels_config(&mut self, channels: &ChannelConfig) -> AWGResult<&mut Self> { - // validate before actually setting anything - Config::check_channels(channels)?; - // reset all channels - self.set_active_channels([])?; + // config validation should have already been done at this point + let enabled_channels: Vec<usize> + = channels.enabled_channels + .iter() + .enumerate() + .filter_map(|(index, &b)| (b).then_some(index)) + .collect::<Vec<_>>(); // set channel states and params - channels.iter() - .map(|ch| { - self.set_channel( - ch.channel as i32, - ch.amplitude as i32, - ch.stop_level, - ch.enabled, - ) - .map(|_| ()) - }) - .collect::<AWGResult<Vec<()>>>()?; + let enabled_channels_i32: Vec<i32> + = enabled_channels.iter().map(|&ch| ch as i32).collect(); + self.set_active_channels(&enabled_channels_i32)?; + for ch in enabled_channels { + self.set_channel_amp(ch as i32, channels.amplitudes[ch])?; + self.set_channel_stop_level(ch as i32, channels.stop_levels[ch])?; + self.set_channel_output(ch as i32, true)?; + } Ok(self) } pub fn load_sequence_loop_config( &mut self, - sequence_loop: &[SequenceStepConfig], - sampling_rate: Option<u64>, - frequency_resolution: Option<u64>, + sequence_loop: &[SequenceSegmentsConfig], ) -> AWGResult<&mut Self> { - // validate before actually setting anything - if sequence_loop.is_empty() { - eprintln!( - "AWG::load_sequence_loop_config: \ - called on an empty sequence loop" - ); - return Ok(self); - } - Config::check_sequence_steps(sequence_loop)?; + // validition should have already been done at this point // load waveform param data - let waveform_steps: Vec<(WaveformParams, &SequenceStepConfig)> - = sequence_loop.iter() - .map(|step| WaveformParams::load(&step.source).map(|wf| (wf, step))) - .collect::<AWGResult<Vec<_>>>()?; - let sampling_rate - = sampling_rate.unwrap_or_else(|| { - waveform_steps.iter() - .map(|(wf, _)| wf.get_sampling_rate().unwrap()) - .max() - .unwrap() - }); - let frequency_resolution - = frequency_resolution.unwrap_or_else(|| { - waveform_steps.iter() - .map(|(wf, _)| wf.get_frequency_resolution().unwrap()) - .max() - .unwrap() - }); - let n_segments: usize - = 2.0_f64.powi( - (waveform_steps.len() as f64).log2().ceil() as i32 - ) as usize; - - // generate and write waveforms - let waveform_steps: Vec<(ArrayWaveform, &SequenceStepConfig)> - = waveform_steps.into_iter() - .map(|(mut wf_params, step)| { - wf_params.set_sampling_rate(sampling_rate); - wf_params.set_frequency_resolution(frequency_resolution); - wf_params.to_waveform() - .map(|wf| (wf, step)) - }) - .collect::<AWGResult<Vec<_>>>()?; - self.init_replay_mode_seq(n_segments)?; - self.set_sample_rate(sampling_rate as i64)?; - let mut wf_data_ptr: WaveformDataPtr; - for (k, (mut wf, step_conf)) in waveform_steps.into_iter().enumerate() { - wf_data_ptr - = match step_conf.kind { - SequenceKind::Static => wf.get_static_waveform(), - SequenceKind::Trick => todo!(), - }?; - // let (mut mem, size) = wf_data_ptr.into_memsize()?; - self.write_seq_mode_segment(k as i32, wf_data_ptr)?; - // self.write_seq_mode_segment(k as i32, &mut mem, size as i32)?; + // let waveform_steps: Vec<(WaveformParams, &SequenceStepConfig)> + // = sequence_loop.iter() + // .map(|step| WaveformParams::load(&step.source).map(|wf| (wf, step))) + // .collect::<AWGResult<Vec<_>>>()?; + // let sampling_rate + // = sampling_rate.unwrap_or_else(|| { + // waveform_steps.iter() + // .map(|(wf, _)| wf.get_sampling_rate().unwrap()) + // .max() + // .unwrap() + // }); + // let frequency_resolution + // = frequency_resolution.unwrap_or_else(|| { + // waveform_steps.iter() + // .map(|(wf, _)| wf.get_frequency_resolution().unwrap()) + // .max() + // .unwrap() + // }); + // let n_segments: usize + // = 2.0_f64.powi( + // (waveform_steps.len() as f64).log2().ceil() as i32 + // ) as usize; + + // // generate and write waveforms + // let waveform_steps: Vec<(ArrayWaveform, &SequenceStepConfig)> + // = waveform_steps.into_iter() + // .map(|(mut wf_params, step)| { + // wf_params.set_sampling_rate(sampling_rate); + // wf_params.set_frequency_resolution(frequency_resolution); + // wf_params.to_waveform() + // .map(|wf| (wf, step)) + // }) + // .collect::<AWGResult<Vec<_>>>()?; + // self.init_replay_mode_seq(n_segments)?; + // self.set_sample_rate(sampling_rate as i64)?; + // let mut wf_data_ptr: WaveformDataPtr; + // for (k, (mut wf, step_conf)) in waveform_steps.into_iter().enumerate() { + // wf_data_ptr + // = match step_conf.kind { + // SequenceKind::Static => wf.get_static_waveform(), + // SequenceKind::Trick => todo!(), + // }?; + // // let (mut mem, size) = wf_data_ptr.into_memsize()?; + // self.write_seq_mode_segment(k as i32, wf_data_ptr)?; + // // self.write_seq_mode_segment(k as i32, &mut mem, size as i32)?; + // self.set_seq_mode_step( + // k as u64, + // k as u64, + // step_conf.next_step as u64, + // step_conf.n_loop as u64, + // step_conf.condition, + // )?; + // } + for (seg_index, seg_config) in sequence_loop.iter().enumerate() { + self.write_seq_mode_segment(seg_index as i32, &seg_config.source)?; self.set_seq_mode_step( - k as u64, - k as u64, - step_conf.next_step as u64, - step_conf.n_loop as u64, - step_conf.condition, + seg_config.step as u64, + seg_index as u64, + seg_config.next_step as u64, + seg_config.n_loop as u64, + seg_config.loop_condition, )?; } Ok(self) } pub fn set_config(&mut self, config: &Config) -> AWGResult<&mut Self> { + config.check()?; + self.reset()?; self.set_sample_rate(config.sampling_rate as i64)?; self.set_trigger_config(&config.trigger)?; + self.init_replay_mode_seq(config.num_segments)?; + self.load_sequence_loop_config(&config.sequence_segments)?; self.set_channels_config(&config.active_channels)?; - self.load_sequence_loop_config( - &config.sequence_steps, - Some(config.sampling_rate), - Some(config.frequency_resolution), - )?; Ok(self) } @@ -1291,1041 +1290,1039 @@ impl AWG { // } } -/// Safe interface to waveform parameter configuration. -/// -/// All parameters must be initialized before an [`ArrayWaveform`] can be -/// generated: -/// - [`tones`][Self::set_tones]: Frequency tones in hertz -/// - [`phases`][Self::set_phases]: Initial phases for each tone in radians -/// - [`amplitudes`][Self::set_amplitudes]: Amplitudes for each tone -/// - [`sampling_rate`][Self::set_sampling_rate]: Sampling rate in hertz -/// - [`frequency_resolution`][Self::set_frequency_resolution]: Frequency -/// -/// resolution in hertz -#[derive(Clone, Debug)] -pub struct WaveformParams { - tones: Option<Vec<i32>>, - phases: Option<Vec<f64>>, - amplitudes: Option<Vec<f64>>, - sampling_rate: Option<u64>, - frequency_resolution: Option<u64>, -} - -impl Default for WaveformParams { - fn default() -> Self { Self::new() } -} - -impl WaveformParams { - /// Create a new, uninitialized set of waveform parameters. - pub fn new() -> Self { - Self { - tones: None, - phases: None, - amplitudes: None, - sampling_rate: None, - frequency_resolution: None, - } - } - - /// Return `true` if all parameters have been initialized. - pub fn is_init(&self) -> bool { - self.has_tones() - && self.has_phases() - && self.has_amplitudes() - && self.has_sampling_rate() - && self.has_frequency_resolution() - } - - /// Write current parameter settings to a file. - /// - /// The file is formatted as a CSV file: - /// ```text - /// sampling_rate - /// frequency_resolution - /// tones[0],tones[1],... - /// phases[0],phases[1],... - /// amplitudes[0],amplitudes[1],... - /// ``` - /// - /// The given file name must have a `.csv` extension. - pub fn save<P>(&self, outfile: P) -> AWGResult<&Self> - where P: AsRef<Path> - { - self.is_init().then_some(()) - .ok_or(AWGError::WaveformUninit)?; - let outfile = outfile.as_ref(); - outfile.extension().and_then(|ext| (ext == "csv").then_some(())) - .ok_or(AWGError::WaveformCSV(outfile.as_os_str().to_os_string()))?; - let mut out - = fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(outfile) - .map_err(|err| AWGError::WaveformWriteError(err.to_string()))?; - writeln!(&mut out, "{}", self.sampling_rate.unwrap()) - .map_err(|err| AWGError::WaveformWriteError(err.to_string()))?; - writeln!(&mut out, "{}", self.frequency_resolution.unwrap()) - .map_err(|err| AWGError::WaveformWriteError(err.to_string()))?; - writeln!(&mut out, "{}", - self.tones.as_ref().unwrap().iter() - .map(|f| f.to_string()) - .collect::<Vec<String>>() - .join(",") - ) - .map_err(|err| AWGError::WaveformWriteError(err.to_string()))?; - writeln!(&mut out, "{}", - self.phases.as_ref().unwrap().iter() - .map(|ph| ph.to_string()) - .collect::<Vec<String>>() - .join(",") - ) - .map_err(|err| AWGError::WaveformWriteError(err.to_string()))?; - writeln!(&mut out, "{}", - self.amplitudes.as_ref().unwrap().iter() - .map(|a| a.to_string()) - .collect::<Vec<String>>() - .join(",") - ) - .map_err(|err| AWGError::WaveformWriteError(err.to_string()))?; - Ok(self) - } - - /// Load parameter settings from a file. - /// - /// The file is expected as a CSV file: - /// ```text - /// sampling_rate - /// frequency_resolution - /// tones[0],tones[1],... - /// phases[0],phases[1],... - /// amplitudes[0],amplitudes[1],... - /// ``` - /// - /// The given file name must have a `.csv` extension. - pub fn load<P>(infile: P) -> AWGResult<Self> - where P: AsRef<Path> - { - let infile = infile.as_ref(); - infile.extension().and_then(|ext| (ext == "csv").then_some(())) - .ok_or(AWGError::WaveformCSV(infile.as_os_str().to_os_string()))?; - let mut buf = String::new(); - fs::OpenOptions::new() - .read(true) - .open(infile) - .map_err(|err| AWGError::WaveformParseError(err.to_string()))? - .read_to_string(&mut buf) - .map_err(|err| AWGError::WaveformParseError(err.to_string()))?; - let mut lines = buf.split('\n'); - let sampling_rate: u64 - = lines.next() - .ok_or(AWGError::WaveformParseError( - "missing sampling rate".to_string() - ))? - .parse::<u64>() - .map_err(|err| AWGError::WaveformParseError(err.to_string()))?; - let frequency_resolution: u64 - = lines.next() - .ok_or(AWGError::WaveformParseError( - "missing frequency resolution".to_string() - ))? - .parse::<u64>() - .map_err(|err| AWGError::WaveformParseError(err.to_string()))?; - let tones: Vec<i32> - = lines.next() - .ok_or(AWGError::WaveformParseError( - "missing frequency tones".to_string() - ))? - .split(',') - .map(|f| { - f.parse::<i32>() - .map_err(|err| AWGError::WaveformParseError(err.to_string())) - }) - .collect::<AWGResult<Vec<i32>>>()?; - let phases: Vec<f64> - = lines.next() - .ok_or(AWGError::WaveformParseError( - "missing phases".to_string() - ))? - .split(',') - .map(|ph| { - ph.parse::<f64>() - .map_err(|err| AWGError::WaveformParseError(err.to_string())) - }) - .collect::<AWGResult<Vec<f64>>>()?; - let amplitudes: Vec<f64> - = lines.next() - .ok_or(AWGError::WaveformParseError( - "missing amplitudes".to_string() - ))? - .split(',') - .map(|a| { - a.parse::<f64>() - .map_err(|err| AWGError::WaveformParseError(err.to_string())) - }) - .collect::<AWGResult<Vec<f64>>>()?; - let data = Self { - tones: Some(tones), - phases: Some(phases), - amplitudes: Some(amplitudes), - sampling_rate: Some(sampling_rate), - frequency_resolution: Some(frequency_resolution), - }; - Ok(data) - } - - /// Return `true` if frequency tones have been initialized. - pub fn has_tones(&self) -> bool { self.tones.is_some() } - - /// Get the frequency tones in hertz, if initialized. - pub fn get_tones(&self) -> Option<&Vec<i32>> { - self.tones.as_ref() - } - - /// Set the frequency tones in hertz. - pub fn set_tones<'a, I>(&mut self, tones: I) -> AWGResult<&mut Self> - where I: IntoIterator<Item = &'a i32> - { - let tones: Vec<i32> = tones.into_iter().copied().collect(); - if let Some(phases) = &self.phases { - (tones.len() == phases.len()).then_some(()) - .ok_or(AWGError::NumTones)?; - } - if let Some(amplitudes) = &self.amplitudes { - (tones.len() == amplitudes.len()).then_some(()) - .ok_or(AWGError::NumTones)?; - } - self.tones = Some(tones); - Ok(self) - } - - /// Set the frequency tones using a center frequency and a uniform spacing, - /// both in hertz. - pub fn set_tones_spaced(&mut self, center: i32, spacing: i32, num: usize) - -> AWGResult<&mut Self> - { - if let Some(phases) = &self.phases { - (num == phases.len()).then_some(()) - .ok_or(AWGError::NumTones)?; - } - if let Some(amplitudes) = &self.amplitudes { - (num == amplitudes.len()).then_some(()) - .ok_or(AWGError::NumTones)?; - } - let f0: i32 = center - spacing * num as i32 / 2; - let tones: Vec<i32> - = (0..num as i32).map(|k| f0 + spacing * k).collect(); - self.tones = Some(tones); - Ok(self) - } - - /// Return `true` if initial phases have been initialized. - pub fn has_phases(&self) -> bool { self.phases.is_some() } - - /// Get the initial phases of each tone in radians. - pub fn get_phases(&self) -> Option<&Vec<f64>> { - self.phases.as_ref() - } - - /// Set the initial phases of each tone in radians. - pub fn set_phases<'a, I>(&mut self, phases: I) -> AWGResult<&mut Self> - where I: IntoIterator<Item = &'a f64> - { - let phases: Vec<f64> - = phases.into_iter().copied().map(|ph| ph % TAU).collect(); - if let Some(tones) = &self.tones { - (phases.len() == tones.len()).then_some(()) - .ok_or(AWGError::NumPhases)?; - } - if let Some(amplitudes) = &self.amplitudes { - (phases.len() == amplitudes.len()).then_some(()) - .ok_or(AWGError::NumPhases)?; - } - self.phases = Some(phases); - Ok(self) - } - - /// Return `true` if amplitudes have been initialized. - pub fn has_amplitudes(&self) -> bool { self.amplitudes.is_some() } - - /// Get the amplitudes of each tone. - pub fn get_amplitudes(&self) -> Option<&Vec<f64>> { - self.amplitudes.as_ref() - } - - /// Set the amplitudes of each tone. - /// - /// Each amplitude must lie in the range `[0, 2^15 - 1)`. - pub fn set_amplitudes<'a, I>(&mut self, amplitudes: I) - -> AWGResult<&mut Self> - where I: IntoIterator<Item = &'a f64> - { - let amplitudes: Vec<f64> - = amplitudes.into_iter().copied() - .map(|a| { - (0.0..32767.0).contains(&a) - .then_some(a) - .ok_or(AWGError::InvalidAmplitude(a)) - }) - .collect::<AWGResult<Vec<f64>>>()?; - if let Some(tones) = &self.tones { - (amplitudes.len() == tones.len()).then_some(()) - .ok_or(AWGError::NumAmplitudes)?; - } - if let Some(phases) = &self.phases { - (amplitudes.len() == phases.len()).then_some(()) - .ok_or(AWGError::NumAmplitudes)?; - } - self.amplitudes = Some(amplitudes); - Ok(self) - } - - /// Return `true` if the sampling rate has been initialized. - pub fn has_sampling_rate(&self) -> bool { - self.sampling_rate.is_some() - } - - /// Get the sampling rate in hertz. - pub fn get_sampling_rate(&self) -> Option<u64> { - self.sampling_rate - } - - /// Set the sampling rate in hertz. - pub fn set_sampling_rate(&mut self, sampling_rate: u64) -> &mut Self { - self.sampling_rate = Some(sampling_rate); - self - } - - /// Return `true` if the frequency resolution has been initialized. - pub fn has_frequency_resolution(&self) -> bool { - self.frequency_resolution.is_some() - } - - /// Get the frequency resolution in hertz. - pub fn get_frequency_resolution(&self) -> Option<u64> { - self.frequency_resolution - } - - /// Set the frequency resolution in hertz. - pub fn set_frequency_resolution(&mut self, resolution: u64) -> &mut Self { - self.frequency_resolution = Some(resolution); - self - } - - /// Generate an [`ArrayWaveform`] from the current set of parameters if all - /// have been initialized. - pub fn to_waveform(&self) -> AWGResult<ArrayWaveform> { - self.is_init().then_some(()) - .ok_or(AWGError::WaveformUninit)?; - let mut waveform = ArrayWaveform::new_uninit(); - waveform - .set_freq_tones(self.tones.as_ref().unwrap())? - .set_phases(self.phases.as_ref().unwrap())? - .set_amplitudes(self.amplitudes.as_ref().unwrap())? - .set_sampling_rate(self.sampling_rate.unwrap())? - .set_freq_resolution(self.frequency_resolution.unwrap())?; - Ok(waveform) - } -} - -impl TryFrom<&WaveformParams> for ArrayWaveform { - type Error = AWGError; - - fn try_from(params: &WaveformParams) -> AWGResult<Self> { - params.to_waveform() - } -} - -impl TryFrom<WaveformParams> for ArrayWaveform { - type Error = AWGError; - - fn try_from(params: WaveformParams) -> AWGResult<Self> { - params.to_waveform() - } -} - -impl fmt::Display for WaveformParams { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "WaveformParams {{")?; - - write!(f, " tones: [")?; - if let Some(tones) = &self.tones { - let n = tones.len(); - for (k, tone) in tones.iter().enumerate() { - tone.fmt(f)?; - if k < n - 1 { write!(f, ", ")?; } - } - } else { - write!(f, "<uninit>")?; - } - writeln!(f, "],")?; - - write!(f, " phases: [")?; - if let Some(phases) = &self.phases { - let n = phases.len(); - for (k, phase) in phases.iter().enumerate() { - phase.fmt(f)?; - if k < n - 1 { write!(f, ", ")?; } - } - } else { - write!(f, "<uninit>")?; - } - writeln!(f, "],")?; - - write!(f, " amplitudes: [")?; - if let Some(amplitudes) = &self.amplitudes { - let n = amplitudes.len(); - for (k, amplitude) in amplitudes.iter().enumerate() { - amplitude.fmt(f)?; - if k < n - 1 { write!(f, ", ")?; } - } - } else { - write!(f, "<uninit>")?; - } - writeln!(f, "],")?; - - write!(f, " sampling_rate: ")?; - if let Some(sampling_rate) = self.sampling_rate { - sampling_rate.fmt(f)?; - } else { - write!(f, "<uninit>")?; - } - writeln!(f, ",")?; - - write!(f, " frequency_resolution: ")?; - if let Some(frequency_resolution) = self.frequency_resolution { - frequency_resolution.fmt(f)?; - } else { - write!(f, "<uninit>")?; - } - writeln!(f, ",")?; - - write!(f, "}}")?; - Ok(()) - } -} - -/// Data type for array waveform generation. -pub struct ArrayWaveform { - data: UniquePtr<ffi::ArrayWaveform>, -} - -impl_debug_boring!(ArrayWaveform); - -impl Default for ArrayWaveform { - fn default() -> Self { Self::new_uninit() } -} - -impl ArrayWaveform { - fn pinned(&mut self) -> Pin<&mut ffi::ArrayWaveform> { self.data.pin_mut() } - - /// Create a new, uninitialized waveform. - /// - /// # Safety - /// After this constructor is called, all private member variables are set - /// to 0 or `nullptr` values. It is the programmer's responsibility to - /// ensure that all properties and memory addresses are properly - /// initialized. - fn new_uninit() -> Self { - unsafe { - Self { data: ffi::new_arraywaveform().unwrap() } - } - } - - /// Get a pointer to the associated data buffer. - pub fn get_data_buffer(&mut self) -> AWGResult<PageAlignedMem> { - unsafe { - ffi::arraywaveform_get_data_buffer(self.pinned()) - .map(|ptr| PageAlignedMem { ptr, wf_ref: self }) - .map_err(AWGError::as_waveform_err) - } - } - - /// Get the number of samples in the current waveform. - pub fn get_data_len(&mut self) -> AWGResult<u64> { - unsafe { - ffi::arraywaveform_get_data_len(self.pinned()) - .map(|len| len as u64) - .map_err(AWGError::as_waveform_err) - } - } - - /// Set the sampling rate in hertz. - fn set_sampling_rate(&mut self, rate: u64) -> AWGResult<&mut Self> { - unsafe { - ffi::arraywaveform_set_sampling_rate(self.pinned(), rate) - .map_err(AWGError::as_waveform_err)?; - Ok(self) - } - } - - /// Get the sampling rate in hertz. - pub fn get_sampling_rate(&mut self) -> AWGResult<u64> { - unsafe { - ffi::arraywaveform_get_sampling_rate(self.pinned()) - .map_err(AWGError::as_waveform_err) - } - } - - /// Set the frequency resolution in hertz. - fn set_freq_resolution(&mut self, resolution: u64) - -> AWGResult<&mut Self> - { - unsafe { - ffi::arraywaveform_set_freq_resolution(self.pinned(), resolution) - .map_err(AWGError::as_waveform_err)?; - Ok(self) - } - } - - /// Get the frequency resolution in hertz. - pub fn get_freq_resolution(&mut self) -> AWGResult<u64> { - unsafe { - ffi::arraywaveform_get_freq_resolution(self.pinned()) - .map_err(AWGError::as_waveform_err) - } - } - - /// Set the constituent tones using a fixed center frequency and uniform - /// spacing in hertz. - fn set_freq_tones_spaced( - &mut self, - center: i32, - spacing: i32, - num_tones: u16, - ) -> AWGResult<&mut Self> - { - unsafe { - ffi::arraywaveform_set_freq_tones_spaced( - self.pinned(), center, spacing, num_tones.into()) - .map_err(AWGError::as_waveform_err)?; - Ok(self) - } - } - - /// Set the constituent tones to arbitrary values in hertz. - fn set_freq_tones<'a, I>(&mut self, tones: I) -> AWGResult<&mut Self> - where I: IntoIterator<Item = &'a i32> - { - unsafe { - let tones_cxx: UniquePtr<CxxVector<i32>> - = collect_cxx_vector(tones); - ffi::arraywaveform_set_freq_tones( - self.pinned(), tones_cxx.as_ref().unwrap()) - .map_err(AWGError::as_waveform_err)?; - Ok(self) - } - } - - /// Get a list of contituent tones. - pub fn get_freq_tones(&mut self) -> AWGResult<Vec<i32>> { - unsafe { - ffi::arraywaveform_get_freq_tones(self.pinned()) - .map_err(AWGError::as_waveform_err) - } - } - - /// Set the initial phases in radians of all tones. - fn set_phases<'a, I>(&mut self, phases: I) -> AWGResult<&mut Self> - where I: IntoIterator<Item = &'a f64> - { - unsafe { - let phases_cxx: UniquePtr<CxxVector<f64>> - = collect_cxx_vector(phases); - ffi::arraywaveform_set_phases( - self.pinned(), phases_cxx.as_ref().unwrap()) - .map_err(AWGError::as_waveform_err)?; - Ok(self) - } - } - - /// Get a list of initial phases in radians for each tone. - pub fn get_phases(&mut self) -> AWGResult<Vec<f64>> { - unsafe { - ffi::arraywaveform_get_phases(self.pinned()) - .map_err(AWGError::as_waveform_err) - } - } - - /// Set the amplitudes of all tones. - /// - /// Each amplitude must be in the range `[0, 2^15 - 1)` - fn set_amplitudes<'a, I>(&mut self, amplitudes: I) - -> AWGResult<&mut Self> - where I: IntoIterator<Item = &'a f64> - { - unsafe { - let amplitudes: Vec<f64> - = amplitudes.into_iter().copied() - .map(|a| { - (0.0..32767.0).contains(&a) - .then_some(a) - .ok_or(AWGError::InvalidAmplitude(a)) - }) - .collect::<AWGResult<Vec<f64>>>()?; - let amplitudes_cxx: UniquePtr<CxxVector<f64>> - = collect_cxx_vector(&litudes); - ffi::arraywaveform_set_amplitudes( - self.pinned(), amplitudes_cxx.as_ref().unwrap()) - .map_err(AWGError::as_waveform_err)?; - Ok(self) - } - } - - /// Get a list of amplitudes for each tone. - pub fn get_amplitudes(&mut self) -> AWGResult<Vec<f64>> { - unsafe { - ffi::arraywaveform_get_amplitudes(self.pinned()) - .map_err(AWGError::as_waveform_err) - } - } - - /// Set phases to 0 and amplitudes to 2000. - fn set_default_params(&mut self) -> AWGResult<&mut Self> { - unsafe { - ffi::arraywaveform_set_default_params(self.pinned()) - .map_err(AWGError::as_waveform_err)?; - Ok(self) - } - } - - /// Save the current waveform parameters to a file in CSV format. - fn save_params<P>(&mut self, filename: P) -> AWGResult<&mut Self> - where P: AsRef<Path> - { - unsafe { - let path_osstr: &OsStr = filename.as_ref().as_os_str(); - let path_str: &str - = path_osstr.to_str() - .ok_or(AWGError::PathError(path_osstr.to_os_string()))?; - path_str.ends_with(".csv").then_some(()) - .ok_or(AWGError::WaveformCSV(path_osstr.to_os_string()))?; - let_cxx_string!(fname = path_str); - ffi::arraywaveform_save_params(self.pinned(), &fname) - .map_err(AWGError::as_waveform_err)?; - Ok(self) - } - } - - /// Load waveform parameters from a file. - /// - /// The file must be in CSV format. - fn load_params<P>(&mut self, filename: P) -> AWGResult<&mut Self> - where P: AsRef<Path> - { - unsafe { - let path_osstr: &OsStr = filename.as_ref().as_os_str(); - let path_str: &str - = path_osstr.to_str() - .ok_or(AWGError::PathError(path_osstr.to_os_string()))?; - path_str.ends_with(".csv").then_some(()) - .ok_or(AWGError::WaveformCSV(path_osstr.to_os_string()))?; - let_cxx_string!(fname = path_str); - ffi::arraywaveform_load_params(self.pinned(), &fname) - .map_err(AWGError::as_waveform_err)?; - Ok(self) - } - } - - /// Print the current waveform parameters. - pub fn print_params(&mut self) -> AWGResult<&mut Self> { - unsafe { - ffi::arraywaveform_print_params(self.pinned()) - .map_err(AWGError::as_waveform_err)?; - Ok(self) - } - } - - /// Get the minimum data length that fulfills rounding constraints for the - /// given sampling rate and frequency resolution (in hertz). - pub fn get_min_sample_len( - &mut self, - sampling_rate: u64, - frequency_resolution: u64, - ) -> AWGResult<u64> - { - unsafe { - ffi::arraywaveform_get_min_sample_len( - self.pinned(), sampling_rate, frequency_resolution) - .map_err(AWGError::as_waveform_err) - } - } - - /// Get a data array length close to the specified runtime tau (in seconds) - /// that also fulfills rounding constraints for the given sampling rate and - /// frequency resolution (in hertz). - pub fn get_sample_len( - &mut self, - tau: f64, - sampling_rate: u64, - frequency_resolution: u64, - ) -> AWGResult<u64> - { - unsafe { - ffi::arraywaveform_get_sample_len( - self.pinned(), tau, sampling_rate, frequency_resolution) - .map_err(AWGError::as_waveform_err) - } - } - - /// Generate a static frequency waveform from the current set of parameters. - /// - /// The returned [`WaveformDataPtr`] is passed a reference to `self` upon - /// creation for safety. - pub fn get_static_waveform(&mut self) -> AWGResult<WaveformDataPtr> { - unsafe { - ffi::arraywaveform_get_static_waveform(self.pinned()) - .map(|ptr| WaveformDataPtr { ptr, wf_ref: &*self }) - .map_err(AWGError::as_waveform_err) - } - } - - /// Generate a tricky-trick waveform from the current set of parameters. - /// - /// - `sites`: Iterable of array site indices for which the tricky-trick - /// will be performed - /// - `df`: Frequency to move by in hertz - /// - `tau_move`: Moving time in seconds - /// - `tau_stay`: Wait time in seconds - /// - /// The returned [`WaveformDataPtr`] is passed a reference to `self` upon - /// creation for safety. - pub fn get_trick_waveform<'a, I>( - &mut self, - sites: I, - df: f64, - tau_move: f64, - tau_stay: f64, - ) -> AWGResult<WaveformDataPtr> - where I: IntoIterator<Item = &'a i32> - { - unsafe { - let sites_cxx: UniquePtr<CxxVector<i32>> - = collect_cxx_vector(sites); - ffi::arraywaveform_get_trick_waveform( - self.pinned(), - sites_cxx.as_ref().unwrap(), - df, - tau_move, - tau_stay, - ) - .map(|ptr| WaveformDataPtr { ptr, wf_ref: &*self }) - .map_err(AWGError::as_waveform_err) - } - } - - /// Save the generated waveform to a CSV-formatted file. - pub fn save_waveform<P>(&mut self, filename: P) -> AWGResult<&mut Self> - where P: AsRef<Path> - { - unsafe { - let path_osstr: &OsStr = filename.as_ref().as_os_str(); - let path_str: &str - = path_osstr.to_str() - .ok_or(AWGError::PathError(path_osstr.to_os_string()))?; - path_str.ends_with(".csv").then_some(()) - .ok_or(AWGError::WaveformCSV(path_osstr.to_os_string()))?; - let_cxx_string!(fname = path_str); - ffi::arraywaveform_save_waveform(self.pinned(), &fname) - .map_err(AWGError::as_waveform_err)?; - Ok(self) - } - } -} - -/// Parameters to the tweezer uniformization routine. -/// -/// All parameters must be initialized before they can be passed to -/// [`run_uniformization`]: -/// - [`source`][Self::set_source] source waveform file -/// - [`polarizability`][Self::set_polarizability] expected atomic -/// polarizability of the 3P1 ∣F = 3/2, mF = -3/2⟩ state in kHz/μK -/// - [`mean_depth`][Self::set_mean_depth] target mean tweezer depth in -/// μK -/// - [`step_size`][Self::set_step_size] step size to use for the optimization -/// algorithm -/// - [`error_threshold`][Self::set_error_threshold] threshold for termination -/// to use in the optimization algorithm -/// - [`max_iters`][Self::set_max_iters] maximum number of iterations to use in -/// the optimization algorithm -/// - [`num_imaging_avg`][Self::set_num_imaging_avg] number of Basler images to -/// average for tweezer power measurements -/// - [`num_tweezers`][Self::set_num_tweezers] tweezer array size -#[derive(Clone, Debug)] -pub struct UniformizationParams { - source: Option<PathBuf>, - polarizability: Option<f64>, - mean_depth: Option<f64>, - step_size: Option<f64>, - error_threshold: Option<f64>, - max_iters: Option<usize>, - num_imaging_avg: Option<usize>, - num_tweezers: Option<usize>, -} - -impl Default for UniformizationParams { - fn default() -> Self { Self::new() } -} - -impl UniformizationParams { - /// Create a new, uninitialized uniformization config. - pub fn new() -> Self { - Self { - source: None, - polarizability: None, - mean_depth: None, - step_size: None, - error_threshold: None, - max_iters: None, - num_imaging_avg: None, - num_tweezers: None, - } - } - - #[allow(clippy::too_many_arguments)] - pub fn new_all<P>( - source: P, - polarizability: f64, - mean_depth: f64, - step_size: f64, - error_threshold: f64, - max_iters: usize, - num_imaging_avg: usize, - num_tweezers: usize, - ) -> Self - where P: AsRef<Path> - { - Self { - source: Some(source.as_ref().to_path_buf()), - polarizability: Some(polarizability), - mean_depth: Some(mean_depth), - step_size: Some(step_size), - error_threshold: Some(error_threshold), - max_iters: Some(max_iters), - num_imaging_avg: Some(num_imaging_avg), - num_tweezers: Some(num_tweezers), - } - } - - /// Return `true` if all parameters have been initialized. - pub fn is_init(&self) -> bool { - self.has_source() - && self.has_polarizability() - && self.has_mean_depth() - && self.has_step_size() - && self.has_error_threshold() - && self.has_max_iters() - && self.has_num_imaging_avg() - && self.has_num_tweezers() - } - - /// Return `true` if the source waveform file has been set. - pub fn has_source(&self) -> bool { self.source.is_some() } - - /// Get the source waveform file. - pub fn get_source(&self) -> Option<&PathBuf> { - self.source.as_ref() - } - - /// Set the source waveform file. - pub fn set_source<P>(&mut self, source: P) -> AWGResult<&mut Self> - where P: AsRef<Path> - { - let source = source.as_ref().to_path_buf(); - let source_string = source.display().to_string(); - source.exists().then_some(()) - .ok_or(AWGError::InvalidUniformizationSource(source_string))?; - self.source = Some(source); - Ok(self) - } - - /// Return `true` if the polarizability has been set. - pub fn has_polarizability(&self) -> bool { self.polarizability.is_some() } - - /// Get the polarizability in kHz/μK. - pub fn get_polarizability(&self) -> Option<f64> { self.polarizability } - - /// Set the polarizability in kHz/μK. - pub fn set_polarizability(&mut self, polarizability: f64) - -> AWGResult<&mut Self> - { - (polarizability < 0.0).then_some(()) - .ok_or(AWGError::InvalidPolarizability(polarizability))?; - self.polarizability = Some(polarizability); - Ok(self) - } - - /// Return `true` if the mean tweezer depth has been set. - pub fn has_mean_depth(&self) -> bool { self.mean_depth.is_some() } - - /// Get the mean tweezer depth in μK. - pub fn get_mean_depth(&self) -> Option<f64> { self.mean_depth } - - pub fn set_mean_depth(&mut self, mean_depth: f64) -> AWGResult<&mut Self> { - (mean_depth > 0.0).then_some(()) - .ok_or(AWGError::InvalidMeanDepth(mean_depth))?; - self.mean_depth = Some(mean_depth); - Ok(self) - } - - /// Return `true` if the step size has been set. - pub fn has_step_size(&self) -> bool { self.step_size.is_some() } - - /// Get the step size. - pub fn get_step_size(&self) -> Option<f64> { self.step_size } - - /// Set the step size. - pub fn set_step_size(&mut self, step_size: f64) -> AWGResult<&mut Self> { - (step_size > 0.0).then_some(()) - .ok_or(AWGError::InvalidStepSize(step_size))?; - self.step_size = Some(step_size); - Ok(self) - } - - /// Return `true` if the error threshold has been set. - pub fn has_error_threshold(&self) -> bool { self.error_threshold.is_some() } - - /// Get the error threshold. - pub fn get_error_threshold(&self) -> Option<f64> { self.error_threshold } - - pub fn set_error_threshold(&mut self, error_threshold: f64) - -> AWGResult<&mut Self> - { - (error_threshold > 0.0).then_some(()) - .ok_or(AWGError::InvalidErrorThreshold(error_threshold))?; - self.error_threshold = Some(error_threshold); - Ok(self) - } - - /// Return `true` if the maximum iterations has been set. - pub fn has_max_iters(&self) -> bool { self.max_iters.is_some() } - - /// Get the max iters. - pub fn get_max_iters(&self) -> Option<usize> { self.max_iters } - - /// Set the max iters. - pub fn set_max_iters(&mut self, max_iters: usize) -> AWGResult<&mut Self> { - (max_iters > 0).then_some(()) - .ok_or(AWGError::InvalidMaxIters(max_iters))?; - self.max_iters = Some(max_iters); - Ok(self) - } - - /// Return `true` if the number of tweezer images to average has been set. - pub fn has_num_imaging_avg(&self) -> bool { self.num_imaging_avg.is_some() } - - /// Get the number of images to average over. - pub fn get_num_imaging_avg(&self) -> Option<usize> { self.num_imaging_avg } - - /// Set the number of images to average over. - pub fn set_num_imaging_avg(&mut self, num_imaging_avg: usize) - -> AWGResult<&mut Self> - { - (num_imaging_avg > 0).then_some(()) - .ok_or(AWGError::InvalidNumImagingAvg(num_imaging_avg))?; - self.num_imaging_avg = Some(num_imaging_avg); - Ok(self) - } - - pub fn has_num_tweezers(&self) -> bool { self.num_tweezers.is_some() } - - /// Get the number of tweezers. - pub fn get_num_tweezers(&self) -> Option<usize> { self.num_tweezers } - - /// Set the number of tweezers. - pub fn set_num_tweezers(&mut self, num_tweezers: usize) - -> AWGResult<&mut Self> - { - (num_tweezers > 0).then_some(()) - .ok_or(AWGError::InvalidNumTweezers(num_tweezers))?; - self.num_tweezers = Some(num_tweezers); - Ok(self) - } - - /// Convert to the corresponding C++ data type. - fn to_ffi_type(&self) -> AWGResult<UniquePtr<ffi::UniformizationParams>> { - self.check()?; - let source_osstr: &OsStr = self.source.as_ref().unwrap().as_os_str(); - let source_str: &str - = source_osstr.to_str() - .ok_or(AWGError::PathError(source_osstr.to_os_string()))?; - let_cxx_string!(source = source_str); - unsafe { - let ffi_params: UniquePtr<ffi::UniformizationParams> - = ffi::new_uniformizationparams( - &source, - self.polarizability.unwrap(), - self.mean_depth.unwrap(), - self.step_size.unwrap(), - self.error_threshold.unwrap(), - self.max_iters.unwrap() as i32, - self.num_imaging_avg.unwrap() as i32, - self.num_tweezers.unwrap() as i32, - ) - .map_err(AWGError::as_uniformization_err)?; - Ok(ffi_params) - } - } - - /// Check that all necessary conditions are satisfied. - pub fn check(&self) -> AWGResult<()> { - self.is_init().then_some(()) - .ok_or(AWGError::UniformizationUninit)?; - if let Some(source) = self.source.as_ref() { - let source_string = source.display().to_string(); - source.exists().then_some(()) - .ok_or(AWGError::InvalidUniformizationSource(source_string))?; - } - if let Some(polarizability) = self.polarizability { - (polarizability < 0.0).then_some(()) - .ok_or(AWGError::InvalidPolarizability(polarizability))?; - } - if let Some(mean_depth) = self.mean_depth { - (mean_depth > 0.0).then_some(()) - .ok_or(AWGError::InvalidMeanDepth(mean_depth))?; - } - if let Some(step_size) = self.step_size { - (step_size > 0.0).then_some(()) - .ok_or(AWGError::InvalidStepSize(step_size))?; - } - if let Some(error_threshold) = self.error_threshold { - (error_threshold > 0.0).then_some(()) - .ok_or(AWGError::InvalidErrorThreshold(error_threshold))?; - } - if let Some(max_iters) = self.max_iters { - (max_iters > 0).then_some(()) - .ok_or(AWGError::InvalidMaxIters(max_iters))?; - } - if let Some(num_imaging_avg) = self.num_imaging_avg { - (num_imaging_avg > 0).then_some(()) - .ok_or(AWGError::InvalidNumImagingAvg(num_imaging_avg))?; - } - if let Some(num_tweezers) = self.num_tweezers { - (num_tweezers > 0).then_some(()) - .ok_or(AWGError::InvalidNumTweezers(num_tweezers))?; - } - Ok(()) - } -} - -pub fn run_uniformization<A, B>( - mut awg: A, - mut basler: B, - waveform: &mut ArrayWaveform, - params: &UniformizationParams, -) -> AWGResult<()> -where - A: std::ops::DerefMut<Target = AWG>, - B: std::ops::DerefMut<Target = Basler>, -{ - unsafe { - ffi::run_uniformization( - awg.deref_mut().pinned(), - basler.deref_mut().pinned(), - waveform.pinned(), - params.to_ffi_type()?.as_ref().unwrap(), - ) - .map_err(AWGError::as_uniformization_err) - } -} - +// Safe interface to waveform parameter configuration. +// +// All parameters must be initialized before an [`ArrayWaveform`] can be +// generated: +// - [`tones`][Self::set_tones]: Frequency tones in hertz +// - [`phases`][Self::set_phases]: Initial phases for each tone in radians +// - [`amplitudes`][Self::set_amplitudes]: Amplitudes for each tone +// - [`sampling_rate`][Self::set_sampling_rate]: Sampling rate in hertz +// - [`frequency_resolution`][Self::set_frequency_resolution]: Frequency resolution in hertz + +// #[derive(Clone, Debug)] +// pub struct WaveformParams { +// tones: Option<Vec<i32>>, +// phases: Option<Vec<f64>>, +// amplitudes: Option<Vec<f64>>, +// sampling_rate: Option<u64>, +// frequency_resolution: Option<u64>, +// } + +// impl Default for WaveformParams { +// fn default() -> Self { Self::new() } +// } + +// impl WaveformParams { +// /// Create a new, uninitialized set of waveform parameters. +// pub fn new() -> Self { +// Self { +// tones: None, +// phases: None, +// amplitudes: None, +// sampling_rate: None, +// frequency_resolution: None, +// } +// } + +// /// Return `true` if all parameters have been initialized. +// pub fn is_init(&self) -> bool { +// self.has_tones() +// && self.has_phases() +// && self.has_amplitudes() +// && self.has_sampling_rate() +// && self.has_frequency_resolution() +// } + +// /// Write current parameter settings to a file. +// /// +// /// The file is formatted as a CSV file: +// /// ```text +// /// sampling_rate +// /// frequency_resolution +// /// tones[0],tones[1],... +// /// phases[0],phases[1],... +// /// amplitudes[0],amplitudes[1],... +// /// ``` +// /// +// /// The given file name must have a `.csv` extension. +// pub fn save<P>(&self, outfile: P) -> AWGResult<&Self> +// where P: AsRef<Path> +// { +// self.is_init().then_some(()) +// .ok_or(AWGError::WaveformUninit)?; +// let outfile = outfile.as_ref(); +// outfile.extension().and_then(|ext| (ext == "csv").then_some(())) +// .ok_or(AWGError::WaveformCSV(outfile.as_os_str().to_os_string()))?; +// let mut out +// = fs::OpenOptions::new() +// .write(true) +// .create(true) +// .truncate(true) +// .open(outfile) +// .map_err(|err| AWGError::WaveformWriteError(err.to_string()))?; +// writeln!(&mut out, "{}", self.sampling_rate.unwrap()) +// .map_err(|err| AWGError::WaveformWriteError(err.to_string()))?; +// writeln!(&mut out, "{}", self.frequency_resolution.unwrap()) +// .map_err(|err| AWGError::WaveformWriteError(err.to_string()))?; +// writeln!(&mut out, "{}", +// self.tones.as_ref().unwrap().iter() +// .map(|f| f.to_string()) +// .collect::<Vec<String>>() +// .join(",") +// ) +// .map_err(|err| AWGError::WaveformWriteError(err.to_string()))?; +// writeln!(&mut out, "{}", +// self.phases.as_ref().unwrap().iter() +// .map(|ph| ph.to_string()) +// .collect::<Vec<String>>() +// .join(",") +// ) +// .map_err(|err| AWGError::WaveformWriteError(err.to_string()))?; +// writeln!(&mut out, "{}", +// self.amplitudes.as_ref().unwrap().iter() +// .map(|a| a.to_string()) +// .collect::<Vec<String>>() +// .join(",") +// ) +// .map_err(|err| AWGError::WaveformWriteError(err.to_string()))?; +// Ok(self) +// } + +// /// Load parameter settings from a file. +// /// +// /// The file is expected as a CSV file: +// /// ```text +// /// sampling_rate +// /// frequency_resolution +// /// tones[0],tones[1],... +// /// phases[0],phases[1],... +// /// amplitudes[0],amplitudes[1],... +// /// ``` +// /// +// /// The given file name must have a `.csv` extension. +// pub fn load<P>(infile: P) -> AWGResult<Self> +// where P: AsRef<Path> +// { +// let infile = infile.as_ref(); +// infile.extension().and_then(|ext| (ext == "csv").then_some(())) +// .ok_or(AWGError::WaveformCSV(infile.as_os_str().to_os_string()))?; +// let mut buf = String::new(); +// fs::OpenOptions::new() +// .read(true) +// .open(infile) +// .map_err(|err| AWGError::WaveformParseError(err.to_string()))? +// .read_to_string(&mut buf) +// .map_err(|err| AWGError::WaveformParseError(err.to_string()))?; +// let mut lines = buf.split('\n'); +// let sampling_rate: u64 +// = lines.next() +// .ok_or(AWGError::WaveformParseError( +// "missing sampling rate".to_string() +// ))? +// .parse::<u64>() +// .map_err(|err| AWGError::WaveformParseError(err.to_string()))?; +// let frequency_resolution: u64 +// = lines.next() +// .ok_or(AWGError::WaveformParseError( +// "missing frequency resolution".to_string() +// ))? +// .parse::<u64>() +// .map_err(|err| AWGError::WaveformParseError(err.to_string()))?; +// let tones: Vec<i32> +// = lines.next() +// .ok_or(AWGError::WaveformParseError( +// "missing frequency tones".to_string() +// ))? +// .split(',') +// .map(|f| { +// f.parse::<i32>() +// .map_err(|err| AWGError::WaveformParseError(err.to_string())) +// }) +// .collect::<AWGResult<Vec<i32>>>()?; +// let phases: Vec<f64> +// = lines.next() +// .ok_or(AWGError::WaveformParseError( +// "missing phases".to_string() +// ))? +// .split(',') +// .map(|ph| { +// ph.parse::<f64>() +// .map_err(|err| AWGError::WaveformParseError(err.to_string())) +// }) +// .collect::<AWGResult<Vec<f64>>>()?; +// let amplitudes: Vec<f64> +// = lines.next() +// .ok_or(AWGError::WaveformParseError( +// "missing amplitudes".to_string() +// ))? +// .split(',') +// .map(|a| { +// a.parse::<f64>() +// .map_err(|err| AWGError::WaveformParseError(err.to_string())) +// }) +// .collect::<AWGResult<Vec<f64>>>()?; +// let data = Self { +// tones: Some(tones), +// phases: Some(phases), +// amplitudes: Some(amplitudes), +// sampling_rate: Some(sampling_rate), +// frequency_resolution: Some(frequency_resolution), +// }; +// Ok(data) +// } + +// /// Return `true` if frequency tones have been initialized. +// pub fn has_tones(&self) -> bool { self.tones.is_some() } + +// /// Get the frequency tones in hertz, if initialized. +// pub fn get_tones(&self) -> Option<&Vec<i32>> { +// self.tones.as_ref() +// } + +// /// Set the frequency tones in hertz. +// pub fn set_tones<'a, I>(&mut self, tones: I) -> AWGResult<&mut Self> +// where I: IntoIterator<Item = &'a i32> +// { +// let tones: Vec<i32> = tones.into_iter().copied().collect(); +// if let Some(phases) = &self.phases { +// (tones.len() == phases.len()).then_some(()) +// .ok_or(AWGError::NumTones)?; +// } +// if let Some(amplitudes) = &self.amplitudes { +// (tones.len() == amplitudes.len()).then_some(()) +// .ok_or(AWGError::NumTones)?; +// } +// self.tones = Some(tones); +// Ok(self) +// } + +// /// Set the frequency tones using a center frequency and a uniform spacing, +// /// both in hertz. +// pub fn set_tones_spaced(&mut self, center: i32, spacing: i32, num: usize) +// -> AWGResult<&mut Self> +// { +// if let Some(phases) = &self.phases { +// (num == phases.len()).then_some(()) +// .ok_or(AWGError::NumTones)?; +// } +// if let Some(amplitudes) = &self.amplitudes { +// (num == amplitudes.len()).then_some(()) +// .ok_or(AWGError::NumTones)?; +// } +// let f0: i32 = center - spacing * num as i32 / 2; +// let tones: Vec<i32> +// = (0..num as i32).map(|k| f0 + spacing * k).collect(); +// self.tones = Some(tones); +// Ok(self) +// } + +// /// Return `true` if initial phases have been initialized. +// pub fn has_phases(&self) -> bool { self.phases.is_some() } + +// /// Get the initial phases of each tone in radians. +// pub fn get_phases(&self) -> Option<&Vec<f64>> { +// self.phases.as_ref() +// } + +// /// Set the initial phases of each tone in radians. +// pub fn set_phases<'a, I>(&mut self, phases: I) -> AWGResult<&mut Self> +// where I: IntoIterator<Item = &'a f64> +// { +// let phases: Vec<f64> +// = phases.into_iter().copied().map(|ph| ph % TAU).collect(); +// if let Some(tones) = &self.tones { +// (phases.len() == tones.len()).then_some(()) +// .ok_or(AWGError::NumPhases)?; +// } +// if let Some(amplitudes) = &self.amplitudes { +// (phases.len() == amplitudes.len()).then_some(()) +// .ok_or(AWGError::NumPhases)?; +// } +// self.phases = Some(phases); +// Ok(self) +// } + +// /// Return `true` if amplitudes have been initialized. +// pub fn has_amplitudes(&self) -> bool { self.amplitudes.is_some() } + +// /// Get the amplitudes of each tone. +// pub fn get_amplitudes(&self) -> Option<&Vec<f64>> { +// self.amplitudes.as_ref() +// } + +// /// Set the amplitudes of each tone. +// /// +// /// Each amplitude must lie in the range `[0, 2^15 - 1)`. +// pub fn set_amplitudes<'a, I>(&mut self, amplitudes: I) +// -> AWGResult<&mut Self> +// where I: IntoIterator<Item = &'a f64> +// { +// let amplitudes: Vec<f64> +// = amplitudes.into_iter().copied() +// .map(|a| { +// (0.0..32767.0).contains(&a) +// .then_some(a) +// .ok_or(AWGError::InvalidAmplitude(a)) +// }) +// .collect::<AWGResult<Vec<f64>>>()?; +// if let Some(tones) = &self.tones { +// (amplitudes.len() == tones.len()).then_some(()) +// .ok_or(AWGError::NumAmplitudes)?; +// } +// if let Some(phases) = &self.phases { +// (amplitudes.len() == phases.len()).then_some(()) +// .ok_or(AWGError::NumAmplitudes)?; +// } +// self.amplitudes = Some(amplitudes); +// Ok(self) +// } + +// /// Return `true` if the sampling rate has been initialized. +// pub fn has_sampling_rate(&self) -> bool { +// self.sampling_rate.is_some() +// } + +// /// Get the sampling rate in hertz. +// pub fn get_sampling_rate(&self) -> Option<u64> { +// self.sampling_rate +// } + +// /// Set the sampling rate in hertz. +// pub fn set_sampling_rate(&mut self, sampling_rate: u64) -> &mut Self { +// self.sampling_rate = Some(sampling_rate); +// self +// } + +// /// Return `true` if the frequency resolution has been initialized. +// pub fn has_frequency_resolution(&self) -> bool { +// self.frequency_resolution.is_some() +// } + +// /// Get the frequency resolution in hertz. +// pub fn get_frequency_resolution(&self) -> Option<u64> { +// self.frequency_resolution +// } + +// /// Set the frequency resolution in hertz. +// pub fn set_frequency_resolution(&mut self, resolution: u64) -> &mut Self { +// self.frequency_resolution = Some(resolution); +// self +// } + +// /// Generate an [`ArrayWaveform`] from the current set of parameters if all +// /// have been initialized. +// pub fn to_waveform(&self) -> AWGResult<ArrayWaveform> { +// self.is_init().then_some(()) +// .ok_or(AWGError::WaveformUninit)?; +// let mut waveform = ArrayWaveform::new_uninit(); +// waveform +// .set_freq_tones(self.tones.as_ref().unwrap())? +// .set_phases(self.phases.as_ref().unwrap())? +// .set_amplitudes(self.amplitudes.as_ref().unwrap())? +// .set_sampling_rate(self.sampling_rate.unwrap())? +// .set_freq_resolution(self.frequency_resolution.unwrap())?; +// Ok(waveform) +// } +// } + +// impl TryFrom<&WaveformParams> for ArrayWaveform { +// type Error = AWGError; + +// fn try_from(params: &WaveformParams) -> AWGResult<Self> { +// params.to_waveform() +// } +// } + +// impl TryFrom<WaveformParams> for ArrayWaveform { +// type Error = AWGError; + +// fn try_from(params: WaveformParams) -> AWGResult<Self> { +// params.to_waveform() +// } +// } + +// impl fmt::Display for WaveformParams { +// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +// writeln!(f, "WaveformParams {{")?; + +// write!(f, " tones: [")?; +// if let Some(tones) = &self.tones { +// let n = tones.len(); +// for (k, tone) in tones.iter().enumerate() { +// tone.fmt(f)?; +// if k < n - 1 { write!(f, ", ")?; } +// } +// } else { +// write!(f, "<uninit>")?; +// } +// writeln!(f, "],")?; + +// write!(f, " phases: [")?; +// if let Some(phases) = &self.phases { +// let n = phases.len(); +// for (k, phase) in phases.iter().enumerate() { +// phase.fmt(f)?; +// if k < n - 1 { write!(f, ", ")?; } +// } +// } else { +// write!(f, "<uninit>")?; +// } +// writeln!(f, "],")?; + +// write!(f, " amplitudes: [")?; +// if let Some(amplitudes) = &self.amplitudes { +// let n = amplitudes.len(); +// for (k, amplitude) in amplitudes.iter().enumerate() { +// amplitude.fmt(f)?; +// if k < n - 1 { write!(f, ", ")?; } +// } +// } else { +// write!(f, "<uninit>")?; +// } +// writeln!(f, "],")?; + +// write!(f, " sampling_rate: ")?; +// if let Some(sampling_rate) = self.sampling_rate { +// sampling_rate.fmt(f)?; +// } else { +// write!(f, "<uninit>")?; +// } +// writeln!(f, ",")?; + +// write!(f, " frequency_resolution: ")?; +// if let Some(frequency_resolution) = self.frequency_resolution { +// frequency_resolution.fmt(f)?; +// } else { +// write!(f, "<uninit>")?; +// } +// writeln!(f, ",")?; + +// write!(f, "}}")?; +// Ok(()) +// } +// } + +// /// Data type for array waveform generation. +// pub struct ArrayWaveform { +// data: UniquePtr<ffi::ArrayWaveform>, +// } + +// impl_debug_boring!(ArrayWaveform); + +// impl Default for ArrayWaveform { +// fn default() -> Self { Self::new_uninit() } +// } + +// impl ArrayWaveform { +// fn pinned(&mut self) -> Pin<&mut ffi::ArrayWaveform> { self.data.pin_mut() } + +// /// Create a new, uninitialized waveform. +// /// +// /// # Safety +// /// After this constructor is called, all private member variables are set +// /// to 0 or `nullptr` values. It is the programmer's responsibility to +// /// ensure that all properties and memory addresses are properly +// /// initialized. +// fn new_uninit() -> Self { +// unsafe { +// Self { data: ffi::new_arraywaveform().unwrap() } +// } +// } + +// /// Get a pointer to the associated data buffer. +// pub fn get_data_buffer(&mut self) -> AWGResult<PageAlignedMem> { +// unsafe { +// ffi::arraywaveform_get_data_buffer(self.pinned()) +// .map(|ptr| PageAlignedMem { ptr, wf_ref: self }) +// .map_err(AWGError::as_waveform_err) +// } +// } + +// /// Get the number of samples in the current waveform. +// pub fn get_data_len(&mut self) -> AWGResult<u64> { +// unsafe { +// ffi::arraywaveform_get_data_len(self.pinned()) +// .map(|len| len as u64) +// .map_err(AWGError::as_waveform_err) +// } +// } + +// /// Set the sampling rate in hertz. +// fn set_sampling_rate(&mut self, rate: u64) -> AWGResult<&mut Self> { +// unsafe { +// ffi::arraywaveform_set_sampling_rate(self.pinned(), rate) +// .map_err(AWGError::as_waveform_err)?; +// Ok(self) +// } +// } + +// /// Get the sampling rate in hertz. +// pub fn get_sampling_rate(&mut self) -> AWGResult<u64> { +// unsafe { +// ffi::arraywaveform_get_sampling_rate(self.pinned()) +// .map_err(AWGError::as_waveform_err) +// } +// } + +// /// Set the frequency resolution in hertz. +// fn set_freq_resolution(&mut self, resolution: u64) +// -> AWGResult<&mut Self> +// { +// unsafe { +// ffi::arraywaveform_set_freq_resolution(self.pinned(), resolution) +// .map_err(AWGError::as_waveform_err)?; +// Ok(self) +// } +// } + +// /// Get the frequency resolution in hertz. +// pub fn get_freq_resolution(&mut self) -> AWGResult<u64> { +// unsafe { +// ffi::arraywaveform_get_freq_resolution(self.pinned()) +// .map_err(AWGError::as_waveform_err) +// } +// } + +// /// Set the constituent tones using a fixed center frequency and uniform +// /// spacing in hertz. +// fn set_freq_tones_spaced( +// &mut self, +// center: i32, +// spacing: i32, +// num_tones: u16, +// ) -> AWGResult<&mut Self> +// { +// unsafe { +// ffi::arraywaveform_set_freq_tones_spaced( +// self.pinned(), center, spacing, num_tones.into()) +// .map_err(AWGError::as_waveform_err)?; +// Ok(self) +// } +// } + +// /// Set the constituent tones to arbitrary values in hertz. +// fn set_freq_tones<'a, I>(&mut self, tones: I) -> AWGResult<&mut Self> +// where I: IntoIterator<Item = &'a i32> +// { +// unsafe { +// let tones_cxx: UniquePtr<CxxVector<i32>> +// = collect_cxx_vector(tones); +// ffi::arraywaveform_set_freq_tones( +// self.pinned(), tones_cxx.as_ref().unwrap()) +// .map_err(AWGError::as_waveform_err)?; +// Ok(self) +// } +// } + +// /// Get a list of contituent tones. +// pub fn get_freq_tones(&mut self) -> AWGResult<Vec<i32>> { +// unsafe { +// ffi::arraywaveform_get_freq_tones(self.pinned()) +// .map_err(AWGError::as_waveform_err) +// } +// } + +// /// Set the initial phases in radians of all tones. +// fn set_phases<'a, I>(&mut self, phases: I) -> AWGResult<&mut Self> +// where I: IntoIterator<Item = &'a f64> +// { +// unsafe { +// let phases_cxx: UniquePtr<CxxVector<f64>> +// = collect_cxx_vector(phases); +// ffi::arraywaveform_set_phases( +// self.pinned(), phases_cxx.as_ref().unwrap()) +// .map_err(AWGError::as_waveform_err)?; +// Ok(self) +// } +// } + +// /// Get a list of initial phases in radians for each tone. +// pub fn get_phases(&mut self) -> AWGResult<Vec<f64>> { +// unsafe { +// ffi::arraywaveform_get_phases(self.pinned()) +// .map_err(AWGError::as_waveform_err) +// } +// } + +// /// Set the amplitudes of all tones. +// /// +// /// Each amplitude must be in the range `[0, 2^15 - 1)` +// fn set_amplitudes<'a, I>(&mut self, amplitudes: I) +// -> AWGResult<&mut Self> +// where I: IntoIterator<Item = &'a f64> +// { +// unsafe { +// let amplitudes: Vec<f64> +// = amplitudes.into_iter().copied() +// .map(|a| { +// (0.0..32767.0).contains(&a) +// .then_some(a) +// .ok_or(AWGError::InvalidAmplitude(a)) +// }) +// .collect::<AWGResult<Vec<f64>>>()?; +// let amplitudes_cxx: UniquePtr<CxxVector<f64>> +// = collect_cxx_vector(&litudes); +// ffi::arraywaveform_set_amplitudes( +// self.pinned(), amplitudes_cxx.as_ref().unwrap()) +// .map_err(AWGError::as_waveform_err)?; +// Ok(self) +// } +// } + +// /// Get a list of amplitudes for each tone. +// pub fn get_amplitudes(&mut self) -> AWGResult<Vec<f64>> { +// unsafe { +// ffi::arraywaveform_get_amplitudes(self.pinned()) +// .map_err(AWGError::as_waveform_err) +// } +// } + +// /// Set phases to 0 and amplitudes to 2000. +// fn set_default_params(&mut self) -> AWGResult<&mut Self> { +// unsafe { +// ffi::arraywaveform_set_default_params(self.pinned()) +// .map_err(AWGError::as_waveform_err)?; +// Ok(self) +// } +// } + +// /// Save the current waveform parameters to a file in CSV format. +// fn save_params<P>(&mut self, filename: P) -> AWGResult<&mut Self> +// where P: AsRef<Path> +// { +// unsafe { +// let path_osstr: &OsStr = filename.as_ref().as_os_str(); +// let path_str: &str +// = path_osstr.to_str() +// .ok_or(AWGError::PathError(path_osstr.to_os_string()))?; +// path_str.ends_with(".csv").then_some(()) +// .ok_or(AWGError::WaveformCSV(path_osstr.to_os_string()))?; +// let_cxx_string!(fname = path_str); +// ffi::arraywaveform_save_params(self.pinned(), &fname) +// .map_err(AWGError::as_waveform_err)?; +// Ok(self) +// } +// } + +// /// Load waveform parameters from a file. +// /// +// /// The file must be in CSV format. +// fn load_params<P>(&mut self, filename: P) -> AWGResult<&mut Self> +// where P: AsRef<Path> +// { +// unsafe { +// let path_osstr: &OsStr = filename.as_ref().as_os_str(); +// let path_str: &str +// = path_osstr.to_str() +// .ok_or(AWGError::PathError(path_osstr.to_os_string()))?; +// path_str.ends_with(".csv").then_some(()) +// .ok_or(AWGError::WaveformCSV(path_osstr.to_os_string()))?; +// let_cxx_string!(fname = path_str); +// ffi::arraywaveform_load_params(self.pinned(), &fname) +// .map_err(AWGError::as_waveform_err)?; +// Ok(self) +// } +// } + +// /// Print the current waveform parameters. +// pub fn print_params(&mut self) -> AWGResult<&mut Self> { +// unsafe { +// ffi::arraywaveform_print_params(self.pinned()) +// .map_err(AWGError::as_waveform_err)?; +// Ok(self) +// } +// } + +// /// Get the minimum data length that fulfills rounding constraints for the +// /// given sampling rate and frequency resolution (in hertz). +// pub fn get_min_sample_len( +// &mut self, +// sampling_rate: u64, +// frequency_resolution: u64, +// ) -> AWGResult<u64> +// { +// unsafe { +// ffi::arraywaveform_get_min_sample_len( +// self.pinned(), sampling_rate, frequency_resolution) +// .map_err(AWGError::as_waveform_err) +// } +// } + +// /// Get a data array length close to the specified runtime tau (in seconds) +// /// that also fulfills rounding constraints for the given sampling rate and +// /// frequency resolution (in hertz). +// pub fn get_sample_len( +// &mut self, +// tau: f64, +// sampling_rate: u64, +// frequency_resolution: u64, +// ) -> AWGResult<u64> +// { +// unsafe { +// ffi::arraywaveform_get_sample_len( +// self.pinned(), tau, sampling_rate, frequency_resolution) +// .map_err(AWGError::as_waveform_err) +// } +// } + +// /// Generate a static frequency waveform from the current set of parameters. +// /// +// /// The returned [`WaveformDataPtr`] is passed a reference to `self` upon +// /// creation for safety. +// pub fn get_static_waveform(&mut self) -> AWGResult<WaveformDataPtr> { +// unsafe { +// ffi::arraywaveform_get_static_waveform(self.pinned()) +// .map(|ptr| WaveformDataPtr { ptr, wf_ref: &*self }) +// .map_err(AWGError::as_waveform_err) +// } +// } + +// /// Generate a tricky-trick waveform from the current set of parameters. +// /// +// /// - `sites`: Iterable of array site indices for which the tricky-trick +// /// will be performed +// /// - `df`: Frequency to move by in hertz +// /// - `tau_move`: Moving time in seconds +// /// - `tau_stay`: Wait time in seconds +// /// +// /// The returned [`WaveformDataPtr`] is passed a reference to `self` upon +// /// creation for safety. +// pub fn get_trick_waveform<'a, I>( +// &mut self, +// sites: I, +// df: f64, +// tau_move: f64, +// tau_stay: f64, +// ) -> AWGResult<WaveformDataPtr> +// where I: IntoIterator<Item = &'a i32> +// { +// unsafe { +// let sites_cxx: UniquePtr<CxxVector<i32>> +// = collect_cxx_vector(sites); +// ffi::arraywaveform_get_trick_waveform( +// self.pinned(), +// sites_cxx.as_ref().unwrap(), +// df, +// tau_move, +// tau_stay, +// ) +// .map(|ptr| WaveformDataPtr { ptr, wf_ref: &*self }) +// .map_err(AWGError::as_waveform_err) +// } +// } + +// /// Save the generated waveform to a CSV-formatted file. +// pub fn save_waveform<P>(&mut self, filename: P) -> AWGResult<&mut Self> +// where P: AsRef<Path> +// { +// unsafe { +// let path_osstr: &OsStr = filename.as_ref().as_os_str(); +// let path_str: &str +// = path_osstr.to_str() +// .ok_or(AWGError::PathError(path_osstr.to_os_string()))?; +// path_str.ends_with(".csv").then_some(()) +// .ok_or(AWGError::WaveformCSV(path_osstr.to_os_string()))?; +// let_cxx_string!(fname = path_str); +// ffi::arraywaveform_save_waveform(self.pinned(), &fname) +// .map_err(AWGError::as_waveform_err)?; +// Ok(self) +// } +// } +// } + +// /// Parameters to the tweezer uniformization routine. +// /// +// /// All parameters must be initialized before they can be passed to +// /// [`run_uniformization`]: +// /// - [`source`][Self::set_source] source waveform file +// /// - [`polarizability`][Self::set_polarizability] expected atomic +// /// polarizability of the 3P1 ∣F = 3/2, mF = -3/2⟩ state in kHz/μK +// /// - [`mean_depth`][Self::set_mean_depth] target mean tweezer depth in +// /// μK +// /// - [`step_size`][Self::set_step_size] step size to use for the optimization +// /// algorithm +// /// - [`error_threshold`][Self::set_error_threshold] threshold for termination +// /// to use in the optimization algorithm +// /// - [`max_iters`][Self::set_max_iters] maximum number of iterations to use in +// /// the optimization algorithm +// /// - [`num_imaging_avg`][Self::set_num_imaging_avg] number of Basler images to +// /// average for tweezer power measurements +// /// - [`num_tweezers`][Self::set_num_tweezers] tweezer array size +// #[derive(Clone, Debug)] +// pub struct UniformizationParams { +// source: Option<PathBuf>, +// polarizability: Option<f64>, +// mean_depth: Option<f64>, +// step_size: Option<f64>, +// error_threshold: Option<f64>, +// max_iters: Option<usize>, +// num_imaging_avg: Option<usize>, +// num_tweezers: Option<usize>, +// } + +// impl Default for UniformizationParams { +// fn default() -> Self { Self::new() } +// } + +// impl UniformizationParams { +// /// Create a new, uninitialized uniformization config. +// pub fn new() -> Self { +// Self { +// source: None, +// polarizability: None, +// mean_depth: None, +// step_size: None, +// error_threshold: None, +// max_iters: None, +// num_imaging_avg: None, +// num_tweezers: None, +// } +// } + +// #[allow(clippy::too_many_arguments)] +// pub fn new_all<P>( +// source: P, +// polarizability: f64, +// mean_depth: f64, +// step_size: f64, +// error_threshold: f64, +// max_iters: usize, +// num_imaging_avg: usize, +// num_tweezers: usize, +// ) -> Self +// where P: AsRef<Path> +// { +// Self { +// source: Some(source.as_ref().to_path_buf()), +// polarizability: Some(polarizability), +// mean_depth: Some(mean_depth), +// step_size: Some(step_size), +// error_threshold: Some(error_threshold), +// max_iters: Some(max_iters), +// num_imaging_avg: Some(num_imaging_avg), +// num_tweezers: Some(num_tweezers), +// } +// } + +// /// Return `true` if all parameters have been initialized. +// pub fn is_init(&self) -> bool { +// self.has_source() +// && self.has_polarizability() +// && self.has_mean_depth() +// && self.has_step_size() +// && self.has_error_threshold() +// && self.has_max_iters() +// && self.has_num_imaging_avg() +// && self.has_num_tweezers() +// } + +// /// Return `true` if the source waveform file has been set. +// pub fn has_source(&self) -> bool { self.source.is_some() } + +// /// Get the source waveform file. +// pub fn get_source(&self) -> Option<&PathBuf> { +// self.source.as_ref() +// } + +// /// Set the source waveform file. +// pub fn set_source<P>(&mut self, source: P) -> AWGResult<&mut Self> +// where P: AsRef<Path> +// { +// let source = source.as_ref().to_path_buf(); +// let source_string = source.display().to_string(); +// source.exists().then_some(()) +// .ok_or(AWGError::InvalidUniformizationSource(source_string))?; +// self.source = Some(source); +// Ok(self) +// } + +// /// Return `true` if the polarizability has been set. +// pub fn has_polarizability(&self) -> bool { self.polarizability.is_some() } + +// /// Get the polarizability in kHz/μK. +// pub fn get_polarizability(&self) -> Option<f64> { self.polarizability } + +// /// Set the polarizability in kHz/μK. +// pub fn set_polarizability(&mut self, polarizability: f64) +// -> AWGResult<&mut Self> +// { +// (polarizability < 0.0).then_some(()) +// .ok_or(AWGError::InvalidPolarizability(polarizability))?; +// self.polarizability = Some(polarizability); +// Ok(self) +// } + +// /// Return `true` if the mean tweezer depth has been set. +// pub fn has_mean_depth(&self) -> bool { self.mean_depth.is_some() } + +// /// Get the mean tweezer depth in μK. +// pub fn get_mean_depth(&self) -> Option<f64> { self.mean_depth } + +// pub fn set_mean_depth(&mut self, mean_depth: f64) -> AWGResult<&mut Self> { +// (mean_depth > 0.0).then_some(()) +// .ok_or(AWGError::InvalidMeanDepth(mean_depth))?; +// self.mean_depth = Some(mean_depth); +// Ok(self) +// } + +// /// Return `true` if the step size has been set. +// pub fn has_step_size(&self) -> bool { self.step_size.is_some() } + +// /// Get the step size. +// pub fn get_step_size(&self) -> Option<f64> { self.step_size } + +// /// Set the step size. +// pub fn set_step_size(&mut self, step_size: f64) -> AWGResult<&mut Self> { +// (step_size > 0.0).then_some(()) +// .ok_or(AWGError::InvalidStepSize(step_size))?; +// self.step_size = Some(step_size); +// Ok(self) +// } + +// /// Return `true` if the error threshold has been set. +// pub fn has_error_threshold(&self) -> bool { self.error_threshold.is_some() } + +// /// Get the error threshold. +// pub fn get_error_threshold(&self) -> Option<f64> { self.error_threshold } + +// pub fn set_error_threshold(&mut self, error_threshold: f64) +// -> AWGResult<&mut Self> +// { +// (error_threshold > 0.0).then_some(()) +// .ok_or(AWGError::InvalidErrorThreshold(error_threshold))?; +// self.error_threshold = Some(error_threshold); +// Ok(self) +// } + +// /// Return `true` if the maximum iterations has been set. +// pub fn has_max_iters(&self) -> bool { self.max_iters.is_some() } + +// /// Get the max iters. +// pub fn get_max_iters(&self) -> Option<usize> { self.max_iters } + +// /// Set the max iters. +// pub fn set_max_iters(&mut self, max_iters: usize) -> AWGResult<&mut Self> { +// (max_iters > 0).then_some(()) +// .ok_or(AWGError::InvalidMaxIters(max_iters))?; +// self.max_iters = Some(max_iters); +// Ok(self) +// } + +// /// Return `true` if the number of tweezer images to average has been set. +// pub fn has_num_imaging_avg(&self) -> bool { self.num_imaging_avg.is_some() } + +// /// Get the number of images to average over. +// pub fn get_num_imaging_avg(&self) -> Option<usize> { self.num_imaging_avg } + +// /// Set the number of images to average over. +// pub fn set_num_imaging_avg(&mut self, num_imaging_avg: usize) +// -> AWGResult<&mut Self> +// { +// (num_imaging_avg > 0).then_some(()) +// .ok_or(AWGError::InvalidNumImagingAvg(num_imaging_avg))?; +// self.num_imaging_avg = Some(num_imaging_avg); +// Ok(self) +// } + +// pub fn has_num_tweezers(&self) -> bool { self.num_tweezers.is_some() } + +// /// Get the number of tweezers. +// pub fn get_num_tweezers(&self) -> Option<usize> { self.num_tweezers } + +// /// Set the number of tweezers. +// pub fn set_num_tweezers(&mut self, num_tweezers: usize) +// -> AWGResult<&mut Self> +// { +// (num_tweezers > 0).then_some(()) +// .ok_or(AWGError::InvalidNumTweezers(num_tweezers))?; +// self.num_tweezers = Some(num_tweezers); +// Ok(self) +// } + +// /// Convert to the corresponding C++ data type. +// fn to_ffi_type(&self) -> AWGResult<UniquePtr<ffi::UniformizationParams>> { +// self.check()?; +// let source_osstr: &OsStr = self.source.as_ref().unwrap().as_os_str(); +// let source_str: &str +// = source_osstr.to_str() +// .ok_or(AWGError::PathError(source_osstr.to_os_string()))?; +// let_cxx_string!(source = source_str); +// unsafe { +// let ffi_params: UniquePtr<ffi::UniformizationParams> +// = ffi::new_uniformizationparams( +// &source, +// self.polarizability.unwrap(), +// self.mean_depth.unwrap(), +// self.step_size.unwrap(), +// self.error_threshold.unwrap(), +// self.max_iters.unwrap() as i32, +// self.num_imaging_avg.unwrap() as i32, +// self.num_tweezers.unwrap() as i32, +// ) +// .map_err(AWGError::as_uniformization_err)?; +// Ok(ffi_params) +// } +// } + +// /// Check that all necessary conditions are satisfied. +// pub fn check(&self) -> AWGResult<()> { +// self.is_init().then_some(()) +// .ok_or(AWGError::UniformizationUninit)?; +// if let Some(source) = self.source.as_ref() { +// let source_string = source.display().to_string(); +// source.exists().then_some(()) +// .ok_or(AWGError::InvalidUniformizationSource(source_string))?; +// } +// if let Some(polarizability) = self.polarizability { +// (polarizability < 0.0).then_some(()) +// .ok_or(AWGError::InvalidPolarizability(polarizability))?; +// } +// if let Some(mean_depth) = self.mean_depth { +// (mean_depth > 0.0).then_some(()) +// .ok_or(AWGError::InvalidMeanDepth(mean_depth))?; +// } +// if let Some(step_size) = self.step_size { +// (step_size > 0.0).then_some(()) +// .ok_or(AWGError::InvalidStepSize(step_size))?; +// } +// if let Some(error_threshold) = self.error_threshold { +// (error_threshold > 0.0).then_some(()) +// .ok_or(AWGError::InvalidErrorThreshold(error_threshold))?; +// } +// if let Some(max_iters) = self.max_iters { +// (max_iters > 0).then_some(()) +// .ok_or(AWGError::InvalidMaxIters(max_iters))?; +// } +// if let Some(num_imaging_avg) = self.num_imaging_avg { +// (num_imaging_avg > 0).then_some(()) +// .ok_or(AWGError::InvalidNumImagingAvg(num_imaging_avg))?; +// } +// if let Some(num_tweezers) = self.num_tweezers { +// (num_tweezers > 0).then_some(()) +// .ok_or(AWGError::InvalidNumTweezers(num_tweezers))?; +// } +// Ok(()) +// } +// } + +// pub fn run_uniformization<A, B>( +// mut awg: A, +// mut basler: B, +// waveform: &mut ArrayWaveform, +// params: &UniformizationParams, +// ) -> AWGResult<()> +// where +// A: std::ops::DerefMut<Target = AWG>, +// B: std::ops::DerefMut<Target = Basler>, +// { +// unsafe { +// ffi::run_uniformization( +// awg.deref_mut().pinned(), +// basler.deref_mut().pinned(), +// waveform.pinned(), +// params.to_ffi_type()?.as_ref().unwrap(), +// ) +// .map_err(AWGError::as_uniformization_err) +// } +// } diff --git a/awg-cxx/lib/awg_ffi.rs b/awg-cxx/lib/awg_ffi.rs index 53bb031..90d0de4 100644 --- a/awg-cxx/lib/awg_ffi.rs +++ b/awg-cxx/lib/awg_ffi.rs @@ -3,8 +3,8 @@ pub(crate) mod ffi { unsafe extern "C++" { include!("awg-cxx/awg-control/Cpp/lib/devices/AWG.h"); - include!("awg-cxx/awg-control/Cpp/lib/devices/basler.h"); - include!("awg-cxx/awg-control/Cpp/lib/waveform.h"); + // include!("awg-cxx/awg-control/Cpp/lib/devices/basler.h"); + // include!("awg-cxx/awg-control/Cpp/lib/waveform.h"); include!("awg-cxx/include/awg.h"); } @@ -77,75 +77,75 @@ pub(crate) mod ffi { /**********************************************************************/ - type ArrayWaveform; - type WaveformData; + // type ArrayWaveform; + // type WaveformData; - unsafe fn new_arraywaveform() -> Result<UniquePtr<ArrayWaveform>>; + // unsafe fn new_arraywaveform() -> Result<UniquePtr<ArrayWaveform>>; - unsafe fn arraywaveform_get_data_buffer(waveform: Pin<&mut ArrayWaveform>) -> Result<UniquePtr<PageAlignedMem>>; - unsafe fn arraywaveform_get_data_len(waveform: Pin<&mut ArrayWaveform>) -> Result<i64>; + // unsafe fn arraywaveform_get_data_buffer(waveform: Pin<&mut ArrayWaveform>) -> Result<UniquePtr<PageAlignedMem>>; + // unsafe fn arraywaveform_get_data_len(waveform: Pin<&mut ArrayWaveform>) -> Result<i64>; - unsafe fn arraywaveform_set_sampling_rate(waveform: Pin<&mut ArrayWaveform>, rate: u64) -> Result<()>; - unsafe fn arraywaveform_get_sampling_rate(waveform: Pin<&mut ArrayWaveform>) -> Result<u64>; - unsafe fn arraywaveform_set_freq_resolution(waveform: Pin<&mut ArrayWaveform>, resolution: u64) -> Result<()>; - unsafe fn arraywaveform_get_freq_resolution(waveform: Pin<&mut ArrayWaveform>) -> Result<u64>; + // unsafe fn arraywaveform_set_sampling_rate(waveform: Pin<&mut ArrayWaveform>, rate: u64) -> Result<()>; + // unsafe fn arraywaveform_get_sampling_rate(waveform: Pin<&mut ArrayWaveform>) -> Result<u64>; + // unsafe fn arraywaveform_set_freq_resolution(waveform: Pin<&mut ArrayWaveform>, resolution: u64) -> Result<()>; + // unsafe fn arraywaveform_get_freq_resolution(waveform: Pin<&mut ArrayWaveform>) -> Result<u64>; - unsafe fn arraywaveform_set_freq_tones_spaced(waveform: Pin<&mut ArrayWaveform>, center: i32, spacing: i32, num_tones: i32) -> Result<()>; - unsafe fn arraywaveform_set_freq_tones(waveform: Pin<&mut ArrayWaveform>, tones: &CxxVector<i32>) -> Result<()>; - unsafe fn arraywaveform_get_freq_tones(waveform: Pin<&mut ArrayWaveform>) -> Result<Vec<i32>>; - unsafe fn arraywaveform_set_phases(waveform: Pin<&mut ArrayWaveform>, phases: &CxxVector<f64>) -> Result<()>; - unsafe fn arraywaveform_get_phases(waveform: Pin<&mut ArrayWaveform>) -> Result<Vec<f64>>; - unsafe fn arraywaveform_set_amplitudes(waveform: Pin<&mut ArrayWaveform>, amplitudes: &CxxVector<f64>) -> Result<()>; - unsafe fn arraywaveform_get_amplitudes(waveform: Pin<&mut ArrayWaveform>) -> Result<Vec<f64>>; - unsafe fn arraywaveform_set_default_params(waveform: Pin<&mut ArrayWaveform>) -> Result<()>; - unsafe fn arraywaveform_save_params(waveform: Pin<&mut ArrayWaveform>, filename: &CxxString) -> Result<()>; - unsafe fn arraywaveform_load_params(waveform: Pin<&mut ArrayWaveform>, filename: &CxxString) -> Result<()>; - unsafe fn arraywaveform_print_params(waveform: Pin<&mut ArrayWaveform>) -> Result<()>; + // unsafe fn arraywaveform_set_freq_tones_spaced(waveform: Pin<&mut ArrayWaveform>, center: i32, spacing: i32, num_tones: i32) -> Result<()>; + // unsafe fn arraywaveform_set_freq_tones(waveform: Pin<&mut ArrayWaveform>, tones: &CxxVector<i32>) -> Result<()>; + // unsafe fn arraywaveform_get_freq_tones(waveform: Pin<&mut ArrayWaveform>) -> Result<Vec<i32>>; + // unsafe fn arraywaveform_set_phases(waveform: Pin<&mut ArrayWaveform>, phases: &CxxVector<f64>) -> Result<()>; + // unsafe fn arraywaveform_get_phases(waveform: Pin<&mut ArrayWaveform>) -> Result<Vec<f64>>; + // unsafe fn arraywaveform_set_amplitudes(waveform: Pin<&mut ArrayWaveform>, amplitudes: &CxxVector<f64>) -> Result<()>; + // unsafe fn arraywaveform_get_amplitudes(waveform: Pin<&mut ArrayWaveform>) -> Result<Vec<f64>>; + // unsafe fn arraywaveform_set_default_params(waveform: Pin<&mut ArrayWaveform>) -> Result<()>; + // unsafe fn arraywaveform_save_params(waveform: Pin<&mut ArrayWaveform>, filename: &CxxString) -> Result<()>; + // unsafe fn arraywaveform_load_params(waveform: Pin<&mut ArrayWaveform>, filename: &CxxString) -> Result<()>; + // unsafe fn arraywaveform_print_params(waveform: Pin<&mut ArrayWaveform>) -> Result<()>; - unsafe fn arraywaveform_get_min_sample_len(waveform: Pin<&mut ArrayWaveform>, sampling_rate: u64, frequency_resolution: u64) -> Result<u64>; - unsafe fn arraywaveform_get_sample_len(waveform: Pin<&mut ArrayWaveform>, tau: f64, sampling_rate: u64, frequency_resolution: u64) -> Result<u64>; + // unsafe fn arraywaveform_get_min_sample_len(waveform: Pin<&mut ArrayWaveform>, sampling_rate: u64, frequency_resolution: u64) -> Result<u64>; + // unsafe fn arraywaveform_get_sample_len(waveform: Pin<&mut ArrayWaveform>, tau: f64, sampling_rate: u64, frequency_resolution: u64) -> Result<u64>; - unsafe fn arraywaveform_get_static_waveform(waveform: Pin<&mut ArrayWaveform>) -> Result<UniquePtr<WaveformData>>; - unsafe fn arraywaveform_get_trick_waveform(waveform: Pin<&mut ArrayWaveform>, site_index: &CxxVector<i32>, df: f64, tau_move: f64, tau_stay: f64) -> Result<UniquePtr<WaveformData>>; + // unsafe fn arraywaveform_get_static_waveform(waveform: Pin<&mut ArrayWaveform>) -> Result<UniquePtr<WaveformData>>; + // unsafe fn arraywaveform_get_trick_waveform(waveform: Pin<&mut ArrayWaveform>, site_index: &CxxVector<i32>, df: f64, tau_move: f64, tau_stay: f64) -> Result<UniquePtr<WaveformData>>; - unsafe fn arraywaveform_save_waveform(waveform: Pin<&mut ArrayWaveform>, filename: &CxxString) -> Result<()>; + // unsafe fn arraywaveform_save_waveform(waveform: Pin<&mut ArrayWaveform>, filename: &CxxString) -> Result<()>; - unsafe fn waveformdata_get_mem(data: Pin<&mut WaveformData>) -> Result<UniquePtr<PageAlignedMem>>; - unsafe fn waveformdata_get_datalen(data: Pin<&mut WaveformData>) -> Result<i64>; + // unsafe fn waveformdata_get_mem(data: Pin<&mut WaveformData>) -> Result<UniquePtr<PageAlignedMem>>; + // unsafe fn waveformdata_get_datalen(data: Pin<&mut WaveformData>) -> Result<i64>; - /**********************************************************************/ + // /**********************************************************************/ - type BaslerBox; - - unsafe fn new_basler(index: i32) -> Result<UniquePtr<BaslerBox>>; - - unsafe fn basler_print_device_info(basler: Pin<&mut BaslerBox>) -> Result<()>; - unsafe fn basler_set_exposure(basler: Pin<&mut BaslerBox>, exposure_time: f64) -> Result<()>; - unsafe fn basler_get_exposure(basler: Pin<&mut BaslerBox>) -> Result<f64>; - unsafe fn basler_set_frame_rate(basler: Pin<&mut BaslerBox>, frame_rate: u32) -> Result<()>; - unsafe fn basler_set_frame_rate_max(basler: Pin<&mut BaslerBox>) -> Result<()>; - unsafe fn basler_set_gain(basler: Pin<&mut BaslerBox>, gain: f64) -> Result<()>; - unsafe fn basler_get_gain(basler: Pin<&mut BaslerBox>) -> Result<f64>; - unsafe fn basler_set_roi(basler: Pin<&mut BaslerBox>, offset_x: u32, offset_y: u32, width: u32, height: u32) -> Result<()>; - unsafe fn basler_get_offset_x(basler: Pin<&mut BaslerBox>) -> Result<u32>; - unsafe fn basler_get_offset_y(basler: Pin<&mut BaslerBox>) -> Result<u32>; - unsafe fn basler_get_width(basler: Pin<&mut BaslerBox>) -> Result<u32>; - unsafe fn basler_get_height(basler: Pin<&mut BaslerBox>) -> Result<u32>; - unsafe fn basler_set_acquisition_mode(basler: Pin<&mut BaslerBox>, mode: &CxxString) -> Result<()>; - unsafe fn basler_print_available_acquisition_modes(basler: Pin<&mut BaslerBox>) -> Result<()>; - - unsafe fn basler_start_grabbing(basler: Pin<&mut BaslerBox>) -> Result<()>; - unsafe fn basler_stop_grabbing(basler: Pin<&mut BaslerBox>) -> Result<()>; - unsafe fn basler_is_grabbing(basler: Pin<&mut BaslerBox>) -> Result<bool>; - unsafe fn basler_get_image(basler: Pin<&mut BaslerBox>) -> Result<Vec<u8>>; + // type BaslerBox; - /**********************************************************************/ + // unsafe fn new_basler(index: i32) -> Result<UniquePtr<BaslerBox>>; + + // unsafe fn basler_print_device_info(basler: Pin<&mut BaslerBox>) -> Result<()>; + // unsafe fn basler_set_exposure(basler: Pin<&mut BaslerBox>, exposure_time: f64) -> Result<()>; + // unsafe fn basler_get_exposure(basler: Pin<&mut BaslerBox>) -> Result<f64>; + // unsafe fn basler_set_frame_rate(basler: Pin<&mut BaslerBox>, frame_rate: u32) -> Result<()>; + // unsafe fn basler_set_frame_rate_max(basler: Pin<&mut BaslerBox>) -> Result<()>; + // unsafe fn basler_set_gain(basler: Pin<&mut BaslerBox>, gain: f64) -> Result<()>; + // unsafe fn basler_get_gain(basler: Pin<&mut BaslerBox>) -> Result<f64>; + // unsafe fn basler_set_roi(basler: Pin<&mut BaslerBox>, offset_x: u32, offset_y: u32, width: u32, height: u32) -> Result<()>; + // unsafe fn basler_get_offset_x(basler: Pin<&mut BaslerBox>) -> Result<u32>; + // unsafe fn basler_get_offset_y(basler: Pin<&mut BaslerBox>) -> Result<u32>; + // unsafe fn basler_get_width(basler: Pin<&mut BaslerBox>) -> Result<u32>; + // unsafe fn basler_get_height(basler: Pin<&mut BaslerBox>) -> Result<u32>; + // unsafe fn basler_set_acquisition_mode(basler: Pin<&mut BaslerBox>, mode: &CxxString) -> Result<()>; + // unsafe fn basler_print_available_acquisition_modes(basler: Pin<&mut BaslerBox>) -> Result<()>; + + // unsafe fn basler_start_grabbing(basler: Pin<&mut BaslerBox>) -> Result<()>; + // unsafe fn basler_stop_grabbing(basler: Pin<&mut BaslerBox>) -> Result<()>; + // unsafe fn basler_is_grabbing(basler: Pin<&mut BaslerBox>) -> Result<bool>; + // unsafe fn basler_get_image(basler: Pin<&mut BaslerBox>) -> Result<Vec<u8>>; + + // /**********************************************************************/ - type UniformizationParams; + // type UniformizationParams; - unsafe fn new_uniformizationparams(source: &CxxString, polarizability: f64, mean_depth: f64, step_size: f64, error_threshold: f64, max_iters: i32, num_imaging_avg: i32, num_tweezers: i32) -> Result<UniquePtr<UniformizationParams>>; + // unsafe fn new_uniformizationparams(source: &CxxString, polarizability: f64, mean_depth: f64, step_size: f64, error_threshold: f64, max_iters: i32, num_imaging_avg: i32, num_tweezers: i32) -> Result<UniquePtr<UniformizationParams>>; - unsafe fn run_uniformization(awg: Pin<&mut AWGBox>, basler: Pin<&mut BaslerBox>, waveform: Pin<&mut ArrayWaveform>, params: &UniformizationParams) -> Result<()>; + // unsafe fn run_uniformization(awg: Pin<&mut AWGBox>, basler: Pin<&mut BaslerBox>, waveform: Pin<&mut ArrayWaveform>, params: &UniformizationParams) -> Result<()>; } } diff --git a/awg-cxx/lib/config.rs b/awg-cxx/lib/config.rs index 405725d..eacf53e 100644 --- a/awg-cxx/lib/config.rs +++ b/awg-cxx/lib/config.rs @@ -9,6 +9,7 @@ use crate::awg::{ TriggerMask, TriggerMode, TriggerTerm, + TriggerCoupling }; #[derive(Error, Debug)] @@ -16,15 +17,24 @@ pub enum ConfigError { #[error("invalid trigger level {0} for {1}: must be in the range -10000..+10000 mV")] InvalidTriggerLevel(i32, &'static str), - #[error("number of active channels must not be 3 and ≤ 4")] + #[error("length of enabled_channels, amplitudes, and stop_levels must be 4")] + ChannelLength, + + #[error("number of enabled channels must not be 3 and <= 4")] NumChannels, - #[error("channel {0}: amplitude must be in range 0..2^15 - 1")] + #[error("channel {0}: amplitude must be in range 80..2500 mV")] InvalidAmplitude(usize), #[error("invalid next_step index {0} at step {1}")] InvalidStepIndex(usize, usize), + #[error("number of memory segments must be power of 2, got {0}")] + InvalidNumSegments(u32), + + #[error("number of sequence segments must be less than or equal to number of memory segments, got {0}")] + InvalidNumSequenceSegments(usize), + // #[error("non-unique segment index {0} at step {1}")] // NonUniqueSegmentIndex(usize, usize), @@ -39,6 +49,7 @@ pub struct TriggerParams { pub mode: TriggerMode, pub level: i32, pub rearm_level: i32, + pub coupling: TriggerCoupling, } /// Configuration for the two `ext*` trigger channels. @@ -72,19 +83,32 @@ impl TriggerConfig { /// Output channel configuration. /// -/// Channel amplitudes must be in the range 0..2^15 - 1. -#[derive(Copy, Clone, Debug)] +/// Channel amplitudes must be in the range 80..2500 mV. +#[derive(Clone, Debug)] pub struct ChannelConfig { - pub channel: usize, - pub enabled: bool, - pub amplitude: u32, // ∊ [0, 2^15 - 1] - pub stop_level: ChannelStopLevel, + pub enabled_channels: [bool; 4], + pub amplitudes: [i32; 4], // ∊ [80, 2500] mV + pub stop_levels: Vec<ChannelStopLevel>, // must be 4 elements, use of vec here is not ideal, but okay for now } impl ChannelConfig { pub fn check(&self) -> ConfigResult<()> { - (0..32767_u32).contains(&self.amplitude).then_some(()) - .ok_or(ConfigError::InvalidAmplitude(self.channel))?; + let channels = &self.enabled_channels; + (self.stop_levels.len() == 4) + .then_some(()) + .ok_or(ConfigError::ChannelLength)?; + (channels.iter().filter(|&&c| c).count() != 3) + .then_some(()) + .ok_or(ConfigError::NumChannels)?; + self.amplitudes + .iter() + .enumerate() + .map(|(i, &)| { + (80..=2500).contains(&).then_some(()) + .ok_or(ConfigError::InvalidAmplitude(i)) + }) + .collect::<ConfigResult<Vec<_>>>()?; + Ok(()) } } @@ -98,23 +122,24 @@ pub enum SequenceKind { /// Sequence step configuration. #[derive(Clone, Debug)] -pub struct SequenceStepConfig { +pub struct SequenceSegmentsConfig { pub source: PathBuf, - pub kind: SequenceKind, - pub next_step: usize, // < number of steps + pub step: usize, // step index associated with this segment + pub next_step: usize, // next step index to jump to pub n_loop: u32, - pub condition: SeqLoopCondition, + pub loop_condition: SeqLoopCondition, } /// Overall AWG configuration. #[derive(Clone, Debug)] pub struct Config { + pub r#use: bool, // whether to use the AWG pub index: usize, pub sampling_rate: u64, // ≥ 1 - pub frequency_resolution: u64, // ≥ 1 + pub num_segments: u32, // must be a power of 2 pub trigger: TriggerConfig, - pub active_channels: Vec<ChannelConfig>, - pub sequence_steps: Vec<SequenceStepConfig>, + pub active_channels: ChannelConfig, + pub sequence_segments: Vec<SequenceSegmentsConfig>, } impl Config { @@ -124,70 +149,44 @@ impl Config { trigger.check() } - pub(crate) fn check_channels(channels: &[ChannelConfig]) + pub(crate) fn check_channels(channels: &ChannelConfig) -> ConfigResult<()> { - let n_channels = channels.len(); - (n_channels <= 4 && n_channels != 3).then_some(()) - .ok_or(ConfigError::NumChannels)?; - channels.iter() - .map(|ch| ch.check()) - .collect::<ConfigResult<Vec<()>>>()?; - Ok(()) + channels.check() } - pub(crate) fn check_sequence_steps(steps: &[SequenceStepConfig]) + pub(crate) fn check_sequence_segments(segments: &[SequenceSegmentsConfig]) -> ConfigResult<()> { - let n_steps = steps.len(); - let step_range = 0..n_steps; - // let mut segments: Vec<usize> = (0..n_steps).collect(); - steps.iter().enumerate() - .map(|(k, step)| { - Ok((k, step)) - // .and_then(|(k, step)| { - // step_range.contains(&step.segment) - // .then_some((k, step)) - // .ok_or(ConfigError::InvalidStepIndex( - // step.segment, k - // )) - // }) - // .and_then(|(k, step)| { - // segments.iter().enumerate() - // .find_map(|(i, seg)| { - // (*seg == step.segment).then_some(i) - // }) - // .map(|i| { - // segments.swap_remove(i); - // (k, step) - // }) - // .ok_or(ConfigError::NonUniqueSegmentIndex( - // step.segment, k - // )) - // }) - .and_then(|(k, step)| { - step_range.contains(&step.next_step) - .then_some((k, step)) - .ok_or(ConfigError::InvalidStepIndex( - step.next_step, k - )) - }) - .and_then(|(k, step)| { - step.source.exists().then_some((k, step)) + segments.iter().enumerate() + .map(|(k, seg)| { + Ok((k, seg)) + .and_then(|(k, seg)| { + (seg.source.exists() && seg.source.ends_with(".txt")) + .then_some((k, seg)) .ok_or(ConfigError::MissingWaveformSource( - step.source.display().to_string(), k + seg.source.display().to_string(), k )) }) }) .collect::<ConfigResult<Vec<_>>>()?; Ok(()) - } + } // - /// Verify that all data satisfies necessary constraints. + /// Verify that all data satisfies necessary constraints, commenting out since + /// this is already checked at the camera control config level. pub fn check(&self) -> ConfigResult<()> { - Self::check_trigger_params(&self.trigger)?; - Self::check_channels(&self.active_channels)?; - Self::check_sequence_steps(&self.sequence_steps)?; + // let n_segments = self.num_segments; + // (n_segments & (n_segments - 1) == 0) // this checks if n_segments is a power of 2 + // .then_some(()) + // .ok_or(ConfigError::InvalidNumSegments(n_segments))?; + // let n_seq_segments = &self.sequence_segments.len(); + // (*n_seq_segments as u32 > n_segments) + // .then_some(()) + // .ok_or(ConfigError::InvalidNumSequenceSegments(*n_seq_segments))?; + // Self::check_trigger_params(&self.trigger)?; + // Self::check_channels(&self.active_channels)?; + // Self::check_sequence_segments(&self.sequence_segments)?; Ok(()) } } diff --git a/awg-cxx/lib/lib.rs b/awg-cxx/lib/lib.rs index ad29185..4a53e55 100644 --- a/awg-cxx/lib/lib.rs +++ b/awg-cxx/lib/lib.rs @@ -2,7 +2,7 @@ pub(crate) mod awg_ffi; pub mod awg; -pub mod basler; +// pub mod basler; pub mod config; pub mod prelude; diff --git a/awg-cxx/lib/prelude.rs b/awg-cxx/lib/prelude.rs index 4a71ddb..291850e 100644 --- a/awg-cxx/lib/prelude.rs +++ b/awg-cxx/lib/prelude.rs @@ -1,11 +1,11 @@ pub use crate::{ awg::{ AWG, - ArrayWaveform, - PageAlignedMem, - WaveformDataPtr, - WaveformParams, - BufferType, + // ArrayWaveform, + // PageAlignedMem, + // WaveformDataPtr, + // WaveformParams, + // BufferType, ChannelPair, ChannelStopLevel, ClockMode, @@ -17,23 +17,23 @@ pub use crate::{ TriggerMask, TriggerMode, TriggerTerm, - UniformizationParams, + // UniformizationParams, AWGError, AWGResult, }, - basler::{ - Basler, - Config as BaslerConfig, - AcquisitionMode, - BaslerError, - BaslerResult, - }, + // basler::{ + // Basler, + // Config as BaslerConfig, + // AcquisitionMode, + // BaslerError, + // BaslerResult, + // }, config::{ TriggerParams, TriggerConfig, ChannelConfig, SequenceKind, - SequenceStepConfig, + SequenceSegmentsConfig, Config, ConfigError, ConfigResult, diff --git a/config.toml b/config.toml index 63f5aae..7bebff8 100644 --- a/config.toml +++ b/config.toml @@ -78,7 +78,7 @@ enabled = false image_dim = [3, 3] # [int, int] : [w, h] n_loop = 3 # int : images per loop mode = "bright" # str : bright, dark -box = [9, 3, 3, 3] # [int, int, int, int] : sub-ROI for photon counting +counting_box = [9, 3, 3, 3] # [int, int, int, int] : sub-ROI for photon counting threshold = 12.0 # float [processing.ff_series] @@ -86,61 +86,62 @@ enabled = false image_dim = [3, 3] # [int, int] : [w, h] n_loop = 14 # int : images per loop mode = "xor" # str : xor, xnor, alternate -box = [9, 3, 3, 3] # [int, int, int, int] : sub-ROI for photon counting +counting_box = [9, 3, 3, 3] # [int, int, int, int] : sub-ROI for photon counting threshold = 12.0 # float [awg] -index = 0 # int : 0, 1 +use = false # bool +index = 1 # int : 0, 1 sampling_rate = 614400000 # int, Hz -frequency_resolution = 1000 # int, Hz +num_segments = 2 # int : must be pow(2) +# frequency_resolution = 1000 # int, Hz [awg.trigger] masks_or = ["ext0"] # [str] : none, software, ext0, ext1 -masks_and = [] # [str] : none, software, ext0, ext1 +masks_and = ["none"] # [str] : none, ext0, ext1 termination = "fiftyohm" # str : high, fiftyohm [awg.trigger.ext0] mode = "pos" # str : none, pos, neg, both, high, low, winenter, winleave, inwin, outsidewin, pos_rearm, neg_rearm -level = 2500 # int : [-10000, +10000] mV -rearm_level = 2500 # int : [-10000, +10000] mV +level = 1000 # int : [-10000, +10000] mV +rearm_level = 1000 # int : [-10000, +10000] mV, used only in "win*, *win, *_rearm" modes +coupling = "dc" # str : dc, ac [awg.trigger.ext1] mode = "none" # str : none, pos, neg, both, high, low, winenter, winleave, inwin, outsidewin, pos_rearm, neg_rearm -level = 2500 # int : [-10000, +10000] mV -rearm_level = 2500 # int : [-10000, +10000] mV - -[[awg.active_channels]] -channel = 0 # int -enabled = true # bool -amplitude = 2500 # int : 0..2^15-1 -stop_level = "zero" # str : low, high, hold-last, zero - -[[awg.sequence_steps]] -source = "<file>" # str -kind = "static" # str : static, trick -segment = 0 # int -next_segment = 1 # int +level = 1000 # int : [-10000, +10000] mV +rearm_level = 1000 # int : [-10000, +10000] mV +coupling = "dc" # str : dc, ac + +[awg.active_channels] +enabled_channels = [true, false, false, false] # [bool, bool, bool, bool] : [ch0, ch1, ch2, ch3] +amplitudes = [2500, 2500, 2500, 2500] # [80, 2500] mV +stop_levels = ["zero", "zero", "zero", "zero"] # str : low, high, hold-last, zero + +[[awg.sequence_segments]] +source = "/home/coveylab/Documents/Control/awg-control/Python/data/wfmPython.txt" # str +step = 0 # int +next_step = 0 # int n_loop = 1 # int : ≥ 1 -condition = "ontrigger" # str : always, ontrigger, end - -[[awg.sequence_steps]] -source = "<file>" # str -kind = "static" # str : static, trick -segment = 1 # int -next_segment = 0 # int -n_loop = 1 # int -condition = "ontrigger" # str : always, ontrigger, end - -[awg.uniformization] -source = "/home/coveylab/Documents/Data/atomic_data/20231013/probe-scan_000" # str -# polarizability = -1.24e-3 # float : kHz/μK (H-polarized tweezers, measured 2023.08.08) -polarizability_kHz_uK = -5.6e-3 # float : kHz/μK (V-polarized tweezers, measured 2023.08.08) -mean_depth_uK = 575.0 # float : μK -step_size = 50.0 # float : 0..2^15 - 1 -error_threshold = 0.003 # float -max_iters = 50 # int -num_imaging_avg = 10 # int -num_tweezers = 20 # int +loop_condition = "ontrigger" # str : always, ontrigger, end + +# [[awg.sequence_segments]] +# source = "<file>" # str +# step = 1 # int +# next_step = 0 # int +# n_loop = 1 # int : ≥ 1 +# loop_condition = "ontrigger" # str : always, ontrigger, end + +# [awg.uniformization] +# source = "/home/coveylab/Documents/Data/atomic_data/20231013/probe-scan_000" # str +# # polarizability = -1.24e-3 # float : kHz/μK (H-polarized tweezers, measured 2023.08.08) +# polarizability_kHz_uK = -5.6e-3 # float : kHz/μK (V-polarized tweezers, measured 2023.08.08) +# mean_depth_uK = 575.0 # float : μK +# step_size = 50.0 # float : 0..2^15 - 1 +# error_threshold = 0.003 # float +# max_iters = 50 # int +# num_imaging_avg = 10 # int +# num_tweezers = 20 # int [timetagger] use = false # bool @@ -151,11 +152,11 @@ stop_channel = 3 # int binwidth_sec = 0.015 # float n_bins = 1 # int -[basler] -index = 0 # int -exposure_time_us = 1200.0 # float -gain = 0.0 # float -acquisition_mode = "continuous" # str : continuous, <?...> -frame_rate = 10 # int -roi = [0, 500, 0, 500] # [xmin, xmax, ymin, ymax] +# [basler] +# index = 0 # int +# exposure_time_us = 1200.0 # float +# gain = 0.0 # float +# acquisition_mode = "continuous" # str : continuous, <?...> +# frame_rate = 10 # int +# roi = [0, 500, 0, 500] # [xmin, xmax, ymin, ymax] diff --git a/librs/actions.rs b/librs/actions.rs index 3f412bb..9efa646 100644 --- a/librs/actions.rs +++ b/librs/actions.rs @@ -65,6 +65,9 @@ pub enum ActionError { #[error("AWG error: {0}")] AWGError(#[from] awg::AWGError), + #[error("AWG mutability lock has been violated")] + AWGMutabilityLock, + #[error("timetagger mutability lock has been violated")] TaggerMutabilityLock, @@ -109,6 +112,11 @@ pub static ACTION_REGISTRY: phf::Map<&'static str, ActionFn> "acquire" => acquire, "camera-info" => camera_info, "temperature" => temperature, + "temperature-monitor" => temperature_monitor, + "awg-run" =>awg_run, + "awg-stop" => awg_stop, + "awg-trigger" => awg_trigger, + "awg-reload-config" => awg_reload_config, // "get-capabilities" => get_capabilities, }; @@ -269,6 +277,39 @@ pub fn temperature( } } +pub fn temperature_monitor( + devices: Arc<ArcDevices>, + _config: &config::Config, + _args: &[String], +) -> ActionResult<()> +{ + struct Stop; + let cam = devices.camera(); + let (tx, rx) = unbounded::<Stop>(); + println!("Press ENTER to stop:"); + let slave = std::thread::spawn(move || -> ActionResult<()> { + let mut line: String = "".to_string(); + loop { + if rx.try_recv().is_ok() { + println!(); + break; + } + let (temperature, status): (f32, andor::TemperatureStatus) + = cam.get_temperature()?; + print_flush!("\r{}", " ".repeat(line.len())); + line = format!(" Temperature: {:+.1}C; {:?} ", temperature, status); + print_flush!("\r{}", line); + std::thread::sleep(std::time::Duration::from_millis(1000)); + } + Ok(()) + }); + + let mut line = String::new(); + std::io::stdin().read_line(&mut line)?; + tx.send(Stop).expect("termination channel closed unexpectedly"); + return jointhrough!(slave); +} + macro_rules! convert_cmap_bound { ($val:expr) => { if $val < 0.0 { @@ -392,13 +433,9 @@ pub fn acquire( = outpath.join(format!("{}_{:03}.npy", out_name, counter)); } outfile = Some(test_out.clone()); - let mut file_name = test_out.file_stem().unwrap().to_owned(); - file_name.push(format!("_tt.npy")); outfile_tt = Some( - // test_out.with_file_name( - // format!("{:?}_tt.npy", test_out.file_stem().unwrap().to_str().unwrap()) - // ) - test_out.with_file_name(file_name) + test_out.with_file_name( + format!("{:?}_tt.npy", test_out.file_stem().unwrap())) ); println!("Save data to file '{}'", outfile.as_ref().unwrap().display()); } @@ -513,6 +550,78 @@ pub fn acquire( Ok(()) } +pub fn awg_run( + devices: Arc<ArcDevices>, + _config: &config::Config, + _args: &[String], +) -> ActionResult<()> +{ + if let Some(awg_arc) = devices.awg().as_mut() { + awg_arc.lock() + .map_err(|_| ActionError::AWGMutabilityLock)? + .card_run()? + .force_trigger()? + ; + println!("AWG is running"); + } else { + println!("AWG is not connected"); + } + Ok(()) +} + +pub fn awg_stop( + devices: Arc<ArcDevices>, + _config: &config::Config, + _args: &[String], +) -> ActionResult<()> +{ + if let Some(awg_arc) = devices.awg().as_mut() { + awg_arc.lock() + .map_err(|_| ActionError::AWGMutabilityLock)? + .card_stop()?; + println!("AWG is stopped"); + } else { + println!("AWG is not connected"); + } + Ok(()) +} + +pub fn awg_trigger( + devices: Arc<ArcDevices>, + _config: &config::Config, + _args: &[String], +) -> ActionResult<()> +{ + if let Some(awg_arc) = devices.awg().as_mut() { + awg_arc.lock() + .map_err(|_| ActionError::AWGMutabilityLock)? + .force_trigger()?; + println!("AWG is triggered"); + } else { + println!("AWG is not connected"); + } + Ok(()) +} + +pub fn awg_reload_config( + devices: Arc<ArcDevices>, + config: &config::Config, + _args: &[String], +) -> ActionResult<()> +{ + if let Some(awg_arc) = devices.awg().as_mut() { + awg_arc.lock() + .map_err(|_| ActionError::AWGMutabilityLock)? + .card_stop()? + .set_config(&config.awg.clone().into())?; + println!("AWG config reloaded"); + } else { + println!("AWG is not connected"); + } + Ok(()) +} + + /// Acquisition abort signal. pub struct AbortAcquisition; diff --git a/librs/cli.rs b/librs/cli.rs index caff2b8..8648e65 100644 --- a/librs/cli.rs +++ b/librs/cli.rs @@ -647,6 +647,31 @@ Available commands:", continue; } } + if config.awg.r#use && !devices.has_awg() { + if let Some(dev) = Arc::get_mut(&mut devices) { + let awg_index: usize = config.awg.index; + let mut awg + = continue_err!(AWG::connect(awg_index)); + awg.set_config(&config.awg.clone().into())?; + dev.use_awg(awg); + } else { + println!( + "Error: device mutability check failed. \ + This shouldn't happen!" + ); + continue; + } + } else if !config.awg.r#use && devices.has_awg() { + if let Some(dev) = Arc::get_mut(&mut devices) { + dev.remove_awg(); + } else { + println!( + "Error: device mutability check failed. \ + This shouldn't happen!" + ); + continue; + } + } let action_res = action_f( devices.clone(), diff --git a/librs/colors.rs b/librs/colors.rs index 0c940f5..207de25 100644 --- a/librs/colors.rs +++ b/librs/colors.rs @@ -55,9 +55,6 @@ pub enum ColorMap { /// Linear scale from blue through gold to white. Pix(Pix), - /// Linear scale from black to 556nm (modeled as `#b6ff00`). - AtomWL(AtomWL), - /// Specify a custom colormap as a list of colors at different points with /// linear interpolation in between. LinearSegmentedColorMap(LinearSegmentedColorMap), @@ -116,11 +113,6 @@ impl ColorMap { Self::Pix(Pix::new()) } - /// Create a new [AtomWL] colormap. - pub fn new_atom_wl() -> Self { - return Self::AtomWL(AtomWL::new()); - } - /// Create a new linear segmented colormap. pub fn new_linear_segmented_colormap(colormap: LinearSegmentedColorMap) -> Self @@ -145,7 +137,6 @@ impl ColorMap { Self::Vibrant(v) => v.c(x), Self::Artsy(a) => a.c(x), Self::Pix(p) => p.c(x), - Self::AtomWL(a) => a.c(x), Self::FuncColorMap(f) => f.c(x), Self::LinearSegmentedColorMap(l) => l.c(x), } @@ -353,15 +344,6 @@ make_linsegcolormap!( } ); -make_linsegcolormap!( - "Linear scale black -> 556nm (`#b6ff00`)", - AtomWL, - colors: { - 0.000 => ( 0, 0, 0), - 1.000 => (182, 255, 0), - } -); - /// Color scale based on a provided function. #[derive(Copy, Clone, Debug)] pub struct FuncColorMap { diff --git a/librs/config.rs b/librs/config.rs index 24eb505..bc50514 100644 --- a/librs/config.rs +++ b/librs/config.rs @@ -322,12 +322,29 @@ impl From<AWGTriggerTerm> for awg::TriggerTerm { } } +/// awg.trigger.coupling +#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)] +pub enum AWGTriggerCoupling { + dc, + ac, +} + +impl From<AWGTriggerCoupling> for awg::TriggerCoupling { + fn from(coupling: AWGTriggerCoupling) -> Self { + match coupling { + AWGTriggerCoupling::dc => Self::DC, + AWGTriggerCoupling::ac => Self::AC, + } + } +} + /// `awg.trigger.ext0`, `awg.trigger.ext1` #[derive(Copy, Clone, Debug, Deserialize)] pub struct AWGTriggerParams { pub mode: AWGTriggerMode, pub level: i32, pub rearm_level: i32, + pub coupling: AWGTriggerCoupling, // } impl From<AWGTriggerParams> for awg::TriggerParams { @@ -336,6 +353,7 @@ impl From<AWGTriggerParams> for awg::TriggerParams { mode: params.mode.into(), level: params.level, rearm_level: params.rearm_level, + coupling: params.coupling.into(), } } } @@ -407,21 +425,20 @@ impl From<AWGChannelStopLevel> for awg::ChannelStopLevel { } /// `awg.active_channels[...]` -#[derive(Copy, Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize)] pub struct AWGChannel { - pub channel: usize, - pub enabled: bool, - pub amplitude: u32, // ∊ [0, 2^15 - 1] - pub stop_level: AWGChannelStopLevel, + pub enabled_channels: [bool; 4], // ∈ {true, false} + pub amplitudes: [i32; 4], // ∊ [80, 2500] mV + pub stop_levels: Vec<AWGChannelStopLevel>, // must be 4 elements, use of vec here is not ideal, but okay for now } impl From<AWGChannel> for awg::ChannelConfig { fn from(channel_conf: AWGChannel) -> Self { Self { - channel: channel_conf.channel, - enabled: channel_conf.enabled, - amplitude: channel_conf.amplitude, - stop_level: channel_conf.stop_level.into(), + enabled_channels: channel_conf.enabled_channels, + amplitudes: channel_conf.amplitudes, + stop_levels: channel_conf.stop_levels.into_iter() + .map(|sl| sl.into()).collect(), } } } @@ -462,79 +479,79 @@ impl From<AWGSequenceCondition> for awg::SeqLoopCondition { /// `awg.sequence_steps[...]` #[derive(Clone, Debug, Deserialize)] -pub struct AWGSequenceStep { +pub struct AWGSequenceSegment { pub source: PathBuf, - pub kind: AWGSequenceKind, + pub step: usize, pub next_step: usize, // < number of steps pub n_loop: u32, - pub condition: AWGSequenceCondition, + pub loop_condition: AWGSequenceCondition, } -impl From<AWGSequenceStep> for awg::SequenceStepConfig { - fn from(seq_step: AWGSequenceStep) -> Self { +impl From<AWGSequenceSegment> for awg::SequenceSegmentsConfig { + fn from(seq_seg: AWGSequenceSegment) -> Self { Self { - source: seq_step.source, - kind: seq_step.kind.into(), - next_step: seq_step.next_step, - n_loop: seq_step.n_loop, - condition: seq_step.condition.into(), + source: seq_seg.source, + step: seq_seg.step, + next_step: seq_seg.next_step, + n_loop: seq_seg.n_loop, + loop_condition: seq_seg.loop_condition.into(), } } } /// `awg.uniformization` -#[derive(Clone, Debug, Deserialize)] -pub struct AWGUniformizationParams { - pub source: PathBuf, - pub polarizability_kHz_uK: f64, - pub mean_depth_uK: f64, - pub step_size: f64, - pub error_threshold: f64, - pub max_iters: usize, - pub num_imaging_avg: usize, - pub num_tweezers: usize, -} - -impl From<AWGUniformizationParams> for awg::UniformizationParams { - fn from(params: AWGUniformizationParams) -> Self { - Self::new_all( - params.source, - params.polarizability_kHz_uK, - params.mean_depth_uK, - params.step_size, - params.error_threshold, - params.max_iters, - params.num_imaging_avg, - params.num_tweezers, - ) - } -} +// #[derive(Clone, Debug, Deserialize)] +// pub struct AWGUniformizationParams { +// pub source: PathBuf, +// pub polarizability_kHz_uK: f64, +// pub mean_depth_uK: f64, +// pub step_size: f64, +// pub error_threshold: f64, +// pub max_iters: usize, +// pub num_imaging_avg: usize, +// pub num_tweezers: usize, +// } + +// impl From<AWGUniformizationParams> for awg::UniformizationParams { +// fn from(params: AWGUniformizationParams) -> Self { +// Self::new_all( +// params.source, +// params.polarizability_kHz_uK, +// params.mean_depth_uK, +// params.step_size, +// params.error_threshold, +// params.max_iters, +// params.num_imaging_avg, +// params.num_tweezers, +// ) +// } +// } /// `awg` #[derive(Clone, Debug, Deserialize)] pub struct AWG { + pub r#use: bool, pub index: usize, pub sampling_rate: u64, // ≥ 1 - pub frequency_resolution: u64, // ≥ 1 + pub num_segments: u32, pub trigger: AWGTrigger, - pub active_channels: Vec<AWGChannel>, - pub sequence_steps: Vec<AWGSequenceStep>, - pub uniformization: AWGUniformizationParams, + pub active_channels: AWGChannel, + pub sequence_segments: Vec<AWGSequenceSegment>, + // pub uniformization: AWGUniformizationParams, } impl From<AWG> for awg::Config { fn from(awg_conf: AWG) -> Self { Self { + r#use: awg_conf.r#use, index: awg_conf.index, sampling_rate: awg_conf.sampling_rate, - frequency_resolution: awg_conf.frequency_resolution, + num_segments: awg_conf.num_segments, trigger: awg_conf.trigger.into(), - active_channels: - awg_conf.active_channels.into_iter() - .map(|ch| ch.into()).collect(), - sequence_steps: - awg_conf.sequence_steps.into_iter() - .map(|step| step.into()).collect(), + active_channels: awg_conf.active_channels.into(), + sequence_segments: + awg_conf.sequence_segments.into_iter() + .map(|seg| seg.into()).collect(), } } } @@ -552,729 +569,337 @@ pub struct TimeTagger { } /// `basler.acquisition_mode` -#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)] -pub enum BaslerAcquisitionMode { - continuous, - // ...? -} - -impl From<BaslerAcquisitionMode> for awg::AcquisitionMode { - fn from(mode: BaslerAcquisitionMode) -> Self { - match mode { - BaslerAcquisitionMode::continuous => Self::Continuous, - } - } -} +// #[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)] +// pub enum BaslerAcquisitionMode { +// continuous, +// // ...? +// } + +// impl From<BaslerAcquisitionMode> for awg::AcquisitionMode { +// fn from(mode: BaslerAcquisitionMode) -> Self { +// match mode { +// BaslerAcquisitionMode::continuous => Self::Continuous, +// } +// } +// } /// `basler` -#[derive(Copy, Clone, Debug, Deserialize)] -pub struct Basler { - pub index: usize, - pub exposure_time_us: f64, - pub gain: f64, - pub acquisition_mode: BaslerAcquisitionMode, - pub frame_rate: u32, - pub roi: [usize; 4], -} - -/// Shorthand for creating [`ConfigSpecItem`]s. -#[macro_export] -macro_rules! spec { - ( Bool ) => { ConfigSpecItem::Value(Verifier::TypeVer(TypeVer::Bool)) }; - ( Int ) => { ConfigSpecItem::Value(Verifier::TypeVer(TypeVer::Int)) }; - ( Float ) => { ConfigSpecItem::Value(Verifier::TypeVer(TypeVer::Float)) }; - ( Datetime ) => { ConfigSpecItem::Value(Verifier::TypeVer(TypeVer::Datetime)) }; - ( Array ) => { ConfigSpecItem::Value(Verifier::TypeVer(TypeVer::Array)) }; - ( TypedArray { [ $( $t:expr ),* $(,)? ], finite: $f:expr } ) => { - ConfigSpecItem::Value( - Verifier::TypeVer( - TypeVer::TypedArray { types: vec![$( $t ),*], finite: $f } - ) - ) - }; - ( Str ) => { ConfigSpecItem::Value(Verifier::TypeVer(TypeVer::Str)) }; - ( IntRange { ($min:expr, $max:expr) } ) => { - ConfigSpecItem::Value( - Verifier::ValueVer( - ValueVer::IntRange { - min: $min, - max: $max, - incl_start: false, - incl_end: false, - } - ) - ) - }; - ( IntRange { ($min:expr, $max:expr)= } ) => { - ConfigSpecItem::Value( - Verifier::ValueVer( - ValueVer::IntRange { - min: $min, - max: $max, - incl_start: false, - incl_end: true, - } - ) - ) - }; - ( IntRange { =($min:expr, $max:expr) } ) => { - ConfigSpecItem::Value( - Verifier::ValueVer( - ValueVer::IntRange { - min: $min, - max: $max, - incl_start: true, - incl_end: false, - } - ) - ) - }; - ( IntRange { =($min:expr, $max:expr)= } ) => { - ConfigSpecItem::Value( - Verifier::ValueVer( - ValueVer::IntRange { - min: $min, - max: $max, - incl_start: true, - incl_end: true, - } - ) - ) - }; - ( FloatRange { ($min:expr, $max:expr) } ) => { - ConfigSpecItem::Value( - Verifier::ValueVer( - ValueVer::FloatRange { - min: $min, - max: $max, - incl_start: false, - incl_end: false, - } - ) - ) - }; - ( FloatRange { ($min:expr, $max:expr)= } ) => { - ConfigSpecItem::Value( - Verifier::ValueVer( - ValueVer::FloatRange { - min: $min, - max: $max, - incl_start: false, - incl_end: true, - } - ) - ) - }; - ( FloatRange { =($min:expr, $max:expr) } ) => { - ConfigSpecItem::Value( - Verifier::ValueVer( - ValueVer::FloatRange { - min: $min, - max: $max, - incl_start: true, - incl_end: false, - } - ) - ) - }; - ( FloatRange { =($min:expr, $max:expr)= } ) => { - ConfigSpecItem::Value( - Verifier::ValueVer( - ValueVer::FloatRange { - min: $min, - max: $max, - incl_start: true, - incl_end: true, - } - ) - ) - }; - ( Table { $( $key:literal => $val:expr ),* $(,)? } ) => { - ConfigSpecItem::Table( - ConfigSpec::from_iter([ - $( - ($key.to_string(), $val) - ),* - ]) - ) - }; - ( StrCollection { $( $s:expr ),* $(,)? } ) => { - ConfigSpecItem::Value( - Verifier::ValueVer( - ValueVer::StrCollection( - HashSet::from_iter([ - $( $s.to_string() ),* - ]) - ) - ) - ) - }; -} - -/// Sugared [`std::collections::HashMap`] representing a specification for the -/// values and structure of a TOML-formatted config file. -#[derive(Clone, Debug)] -pub struct ConfigSpec { - spec: HashMap<String, ConfigSpecItem> -} - -impl AsRef<HashMap<String, ConfigSpecItem>> for ConfigSpec { - fn as_ref(&self) -> &HashMap<String, ConfigSpecItem> { &self.spec } -} - -impl From<HashMap<String, ConfigSpecItem>> for ConfigSpec { - fn from(spec: HashMap<String, ConfigSpecItem>) -> Self { Self { spec } } -} - -impl FromIterator<(String, ConfigSpecItem)> for ConfigSpec { - fn from_iter<I>(iter: I) -> Self - where I: IntoIterator<Item = (String, ConfigSpecItem)> - { - return Self { spec: iter.into_iter().collect() }; - } -} - -/// Create a [`ConfigSpec`]. -/// -/// Expects `str` literals for keys and [`ConfigSpecItem`]s for values. See also -/// [`spec`]. -#[macro_export] -macro_rules! config_spec { - ( $( $key:literal => $val:expr ),* $(,)? ) => { - ConfigSpec::from_iter([ - $( - ($key.to_string(), $val) - ),* - ]) - } -} - -impl Default for ConfigSpec { - fn default() -> Self { - return config_spec!( - "output" => spec!(Table { - "save" => spec!(Bool), - "datadir" => spec!(Str), - "dir" => spec!(Str), - "name" => spec!(Str), - "counter" => spec!(Int), - "show_frames" => spec!(Table { - "mode" => spec!(StrCollection { - "off", - "first", - "last", - "all", - }), - "color_scale" => spec!(StrCollection { - "gray", - "grayclip", - "paperscale", - "plasma", - "vibrant", - "artsy", - "pix", - "atomwl", - }), - "colorbar_limits" => spec!(TypedArray { - [TypeVer::Float, TypeVer::Float], - finite: true - }), - }), - }), - "camera" => spec!(Table { - "acquisition_mode" => spec!(StrCollection { - "single", - "accum", - "kinetic", - "fast_kinetic", - "cont", - }), - "roi" => spec!(TypedArray { - [TypeVer::Int, TypeVer::Int, TypeVer::Int, TypeVer::Int], - finite: true - }), - "bin" => spec!(TypedArray { - [TypeVer::Int, TypeVer::Int], - finite: true - }), - "exposure_time_ms" => spec!(FloatRange { =(1e-3, 1e3)= }), - "cooler" => spec!(Table { - "on" => spec!(Bool), - "fan_mode" => spec!(StrCollection { - "full", - "low", - "off", - }), - "temperature_c" => spec!(FloatRange { =(-100.0, 30.0)= }), - }), - "amp" => spec!(Table { - "em_gain" => spec!(IntRange { =(2, 300)= }), - "preamp_gain_idx" => spec!(IntRange { =(0, 1)= }), - "oamp" => spec!(IntRange { =(0, 1)= }), - }), - "shutter" => spec!(Table { - "mode" => spec!(StrCollection { - "auto", - "open", - "closed", - "fvb", - "any", - }), - "ttl_mode" => spec!(IntRange { =(0, 1)= }), - "open_time_ms" - => spec!(FloatRange { =(27.0, f64::INFINITY) }), - "close_time_ms" - => spec!(FloatRange { =(27.0, f64::INFINITY) }), - }), - "trigger" => spec!(Table { - "mode" => spec!(StrCollection { - "int", - "ext", - "ext_start", - "ext_exp", - "software", - "ext_chargeshift", - }), - }), - "read" => spec!(Table { - "mode" => spec!(StrCollection { - "fvb", - "single_track", - "multi_track", - "random_track", - "image", - }), - "hsspeed_idx" => spec!(IntRange { =(0, 3)= }), - "vsspeed_idx" => spec!(IntRange { =(0, 3)= }), - }), - "kinetic" => spec!(Table { - "buffer_size" => spec!(IntRange { =(1, i64::MAX)= }), - "cycle_time" - => spec!(FloatRange { =(0.0, f64::INFINITY) }), - "num_acc" => spec!(IntRange { =(1, i64::MAX)= }), - "cycle_time_acc" - => spec!(FloatRange { =(0.0, f64::INFINITY) }), - "num_prescan" => spec!(IntRange { =(0, i64::MAX)= }), - }), - }), - "processing" => spec!(Table { - "show" => spec!(StrCollection { - "off", - "first", - "last", - "all", - }), - "color_scale" => spec!(StrCollection { - "gray", - "grayclip", - "paperscale", - "plasma", - "vibrant", - "artsy", - "pix", - "atomwl", - }), - "colorbar_limits" => spec!(TypedArray { - [TypeVer::Float, TypeVer::Float], - finite: true - }), - "count_bias" => spec!(Float), - "qe" => spec!(FloatRange { =(0.0, 1.0)= }), - "window_avg" => spec!(Table { - "enabled" => spec!(Bool), - "image_dim" => spec!(TypedArray { - [TypeVer::Int, TypeVer::Int], - finite: true - }), - "window_size" => spec!(IntRange { =(1, i64::MAX)= }), - "rolling_avg" => spec!(Bool), - }), - "ff_prep" => spec!(Table { - "enabled" => spec!(Bool), - "image_dim" => spec!(TypedArray { - [TypeVer::Int, TypeVer::Int], - finite: true - }), - "n_loop" => spec!(IntRange { =(1, i64::MAX)= }), - "mode" => spec!(StrCollection { "bright", "dark" }), - "box" => spec!(TypedArray { - [ - TypeVer::Int, - TypeVer::Int, - TypeVer::Int, - TypeVer::Int, - ], - finite: true - }), - "threshold" => spec!(FloatRange { =(0.0, f64::INFINITY) }), - }), - "ff_series" => spec!(Table { - "enabled" => spec!(Bool), - "image_dim" => spec!(TypedArray { - [TypeVer::Int, TypeVer::Int], - finite: true - }), - "n_loop" => spec!(IntRange { =(1, i64::MAX)= }), - "mode" => spec!(StrCollection { - "xor", - "xnor", - "alternate", - }), - "box" => spec!(TypedArray { - [ - TypeVer::Int, - TypeVer::Int, - TypeVer::Int, - TypeVer::Int, - ], - finite: true - }), - "threshold" => spec!(FloatRange { =(0.0, f64::INFINITY) }), - }), - }), - "timetagger" => spec!(Table { - "use" => spec!(Bool), - "serial" => spec!(Str), - "counter_channel" => spec!(Int), - "start_channel" => spec!(Int), - "stop_channel" => spec!(Int), - "binwidth_sec" => spec!(Float), - "n_bins" => spec!(Int), - }), - ); - } -} - -impl ConfigSpec { - pub fn from_spec(spec: HashMap<String, ConfigSpecItem>) -> Self { - return Self { spec }; - } - - fn verify_ok(&self, value: Value) -> ConfigResult<Value> { - if let Value::Table(mut tab) = value { - let mut data = Table::new(); - let mut val: Value; - let mut valstr: String; - for (key, spec_item) in self.spec.iter() { - val = match (spec_item, tab.remove(key)) { - (ConfigSpecItem::Table(spec_table), Some(v)) => { - spec_table.verify_ok(v) - }, - (ConfigSpecItem::Value(verifier), Some(v)) => { - match verifier { - Verifier::TypeVer(type_ver) => { - valstr = v.to_string(); - type_ver.verify_ok_or( - v, - ConfigError::InvalidType( - key.clone(), - type_ver.to_string(), - valstr, - ) - ) - }, - Verifier::ValueVer(value_ver) => { - valstr = v.to_string(); - value_ver.verify_ok_or( - v, - ConfigError::InvalidValue( - key.clone(), - value_ver.to_string(), - valstr, - ) - ) - }, - } - }, - _ => Err(ConfigError::MissingKey(key.clone())), - }?; - data.insert(key.clone(), val); - } - return Ok(Value::Table(data)); - } else { - return Err(ConfigError::IncompatibleStructure); - } - } - - /// Return `Ok` if `table` matches the specification, `Err` otherwise. - /// - /// The keys of `table` are filtered to contain only those in the - /// specification. - pub fn verify(&self, table: Table) -> ConfigResult<Config> { - let data: Table - = self.verify_ok(Value::Table(table))? - .try_into() - .unwrap(); - return Ok(Config { data }); - } -} - -/// Sugared [`toml::Table`] holding configuration values. -#[derive(Clone, Debug)] +// #[derive(Copy, Clone, Debug, Deserialize)] +// pub struct Basler { +// pub index: usize, +// pub exposure_time_us: f64, +// pub gain: f64, +// pub acquisition_mode: BaslerAcquisitionMode, +// pub frame_rate: u32, +// pub roi: [usize; 4], +// } + +// impl From<Basler> for awg::BaslerConfig { +// fn from(config: Basler) -> Self { +// Self { +// exposure_time_us: config.exposure_time_us, +// gain: config.gain, +// acquisition_mode: config.acquisition_mode.into(), +// frame_rate: config.frame_rate, +// roi: config.roi, +// } +// } +// } + +/// Everything. +#[derive(Clone, Debug, Deserialize)] pub struct Config { - data: Table -} - -impl AsRef<Table> for Config { - fn as_ref(&self) -> &Table { &self.data } + pub output: Output, + pub camera: Camera, + pub processing: Processing, + pub awg: AWG, + pub timetagger: TimeTagger, + // pub basler: Basler, } impl Config { - /// Create a new [`Config`] with verification against [`spec`]. - pub fn new(data: Table, spec: &ConfigSpec) -> ConfigResult<Self> { - return spec.verify(data); - } - - fn table_get_path<'a, K>(table: &Table, mut keys: Peekable<K>) - -> Option<&Value> - where K: Iterator<Item = &'a str> - { - return if let Some(key) = keys.next() { - match (table.get(key), keys.peek()) { - (Some(Value::Table(tab)), Some(_)) => { - Self::table_get_path(tab, keys) - }, - (x, None) => x, - (Some(_), Some(_)) => None, - (None, _) => None, - } - } else { - unreachable!() - }; - } - - /// Access a key path in `self`, returning `Some` if the complete path - /// exists, `None` otherwise. - pub fn get_path<'a, K>(&self, keys: K) -> Option<&Value> - where K: IntoIterator<Item = &'a str> - { - return Self::table_get_path(&self.data, keys.into_iter().peekable()); - } - - /// Access a key path in `self` where the individual keys in the path are - /// separated by `'.'`. Returns `Some` if the complete path exists, `None` - /// otherwise. - pub fn get_path_s(&self, keys: &str) -> Option<&Value> { - return self.get_path(keys.split('.')); - } - - /// Access a key path in `self` and attempt to convert its type to `T`, - /// returning `Some(T)` if the complete path exists and the type is - /// convertible, `None` otherwise. - pub fn get_path_into<'a, 'de, K, T>(&self, keys: K) -> Option<T> - where - T: Deserialize<'de>, - K: IntoIterator<Item = &'a str>, - { - return match self.get_path(keys) { - Some(x) => x.clone().try_into().ok(), - None => None, - }; - } - - /// Access a key path in `self`, where individual keys in the path are - /// separated by `'.'`, and attempt to convert its type to `T`, returning - /// `Some(T)` if the complete path exists and the type is convertible, - /// `None` otherwise. - pub fn get_path_s_into<'de, T>(&self, keys: &str) -> Option<T> - where T: Deserialize<'de> - { - return match self.get_path_s(keys) { - Some(x) => x.clone().try_into().ok(), - None => None, - }; - } - - fn table_get_path_ok<'a, K>(table: &Table, mut keys: Peekable<K>) - -> ConfigResult<&Value> - where K: Iterator<Item = &'a str> - { - return if let Some(key) = keys.next() { - match (table.get(key), keys.peek()) { - (Some(Value::Table(tab)), Some(_)) => { - Self::table_get_path_ok(tab, keys) - }, - (x, None) => { - x.ok_or_else(|| ConfigError::MissingKey(key.to_string())) - }, - (Some(_), Some(k)) - => Err(ConfigError::KeyPathTooLong(k.to_string())), - (None, _) => Err(ConfigError::MissingKey(key.to_string())), - } - } else { - unreachable!() - }; - } - - /// Access a key path in `self`, returning `Ok` if the complete path - /// exists, `Err` otherwise. - pub fn get_path_ok<'a, K>(&self, keys: K) -> ConfigResult<&Value> - where K: IntoIterator<Item = &'a str> - { - return Self::table_get_path_ok(&self.data, keys.into_iter().peekable()); - } - - /// Access a key path in `self` where the individual keys in the path are - /// separated by `'.'`. Returns `Ok` if the complete path exists, `Err` - /// otherwise. - pub fn get_path_s_ok(&self, keys: &str) -> ConfigResult<&Value> { - return self.get_path_ok(keys.split('.')); - } - - /// Access a key path in `self` and attempt to convert its type to `T`, - /// returning `Ok(T)` if the complete path exists and the type is - /// convertible, `Err` otherwise. - pub fn get_path_ok_into<'a, 'de, K, T>(&self, keys: K) -> ConfigResult<T> - where - T: Deserialize<'de>, - K: IntoIterator<Item = &'a str>, - { - return self.get_path_ok(keys) - .and_then(|x| { - x.clone() - .try_into() - .map_err(|_| { - ConfigError::FailedTypeConversion(x.to_string()) - }) - }); - } - - /// Access a key path in `self`, where individual keys in the path are - /// separated by `'.'`, and attempt to convert its type to `T`, returning - /// `Ok(T)` if the complete path exists and the type is convertible, `Err` - /// otherwise. - pub fn get_path_s_ok_into<'de, T>(&self, keys: &str) -> ConfigResult<T> - where T: Deserialize<'de> - { - return self.get_path_s_ok(keys) - .and_then(|x| { - x.clone() - .try_into() - .map_err(|_| { - ConfigError::FailedTypeConversion(x.to_string()) + fn check_shutter_times(&self) -> ConfigResult<()> { + (self.camera.shutter.open_time_ms >= 27.0).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.camera.shutter.open_time_ms.to_string(), + "camera.shutter.open_time_ms".to_string(), + "≥ 27.0", + ))?; + (self.camera.shutter.close_time_ms >= 27.0).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.camera.shutter.close_time_ms.to_string(), + "camera.shutter.close_time_ms".to_string(), + "≥ 27.0", + ))?; + Ok(()) + } + + fn check_shift_speed(&self) -> ConfigResult<()> { + const IDX: &[usize] = &[0, 1, 2, 3]; + IDX.contains(&self.camera.read.hsspeed_idx).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.camera.read.hsspeed_idx.to_string(), + "camera.read.hsspeed_idx".to_string(), + "one of {0, 1, 2, 3}", + ))?; + IDX.contains(&self.camera.read.vsspeed_idx).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.camera.read.vsspeed_idx.to_string(), + "camera.read.vsspeed_idx".to_string(), + "one of {0, 1, 2, 3}", + ))?; + Ok(()) + } + + fn check_kinetic_params(&self) -> ConfigResult<()> { + (self.camera.kinetic.buffer_size >= 1).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.camera.kinetic.buffer_size.to_string(), + "camera.kinetic.buffer_size".to_string(), + "≥ 1", + ))?; + (self.camera.kinetic.cycle_time >= 0.0).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.camera.kinetic.cycle_time.to_string(), + "camera.kinetic.cycle_time".to_string(), + "≥ 0.0", + ))?; + (self.camera.kinetic.num_acc >= 1).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.camera.kinetic.num_acc.to_string(), + "camera.kinetic.num_acc".to_string(), + "≥ 1", + ))?; + (self.camera.kinetic.cycle_time_acc >= 0.0).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.camera.kinetic.cycle_time_acc.to_string(), + "camera.kinetic.cycle_time_acc".to_string(), + "≥ 0.0", + ))?; + Ok(()) + } + + fn check_roi(&self) -> ConfigResult<()> { + let xmin = self.camera.roi[0]; + let xmax = self.camera.roi[1]; + (xmin < xmax).then_some(()) + .ok_or(ConfigError::InvalidValue( + format!("{:?}", self.camera.roi), + "camera.roi".to_string(), + "xmin < xmax", + ))?; + let ymin = self.camera.roi[2]; + let ymax = self.camera.roi[3]; + (ymin < ymax).then_some(()) + .ok_or(ConfigError::InvalidValue( + format!("{:?}", self.camera.roi), + "camera.roi".to_string(), + "ymin < ymax", + ))?; + Ok(()) + } + + fn check_camera_temperature(&self) -> ConfigResult<()> { + (-100.0..30.0).contains(&self.camera.cooler.temperature_c).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.camera.cooler.temperature_c.to_string(), + "camera.cooler.temperature_c".to_string(), + "in range -100..+30", + ))?; + Ok(()) + } + + fn check_camera_params(&self) -> ConfigResult<()> { + (self.camera.exposure_time_ms > 0.0).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.camera.exposure_time_ms.to_string(), + "camera.exposure_time_ms".to_string(), + "> 0.0", + ))?; + Ok(()) + } + + fn check_windowavg(&self) -> ConfigResult<()> { + (self.processing.window_avg.window_size >= 1).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.processing.window_avg.window_size.to_string(), + "processing.window_avg.window_size".to_string(), + "≥ 1", + ))?; + Ok(()) + } + + fn check_awg_trigger_params(&self) -> ConfigResult<()> { + let volt_range = -10000..=10000; + volt_range.contains(&self.awg.trigger.ext0.level).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.awg.trigger.ext0.level.to_string(), + "awg.trigger.ext0.level".to_string(), + "in range -10000..+10000 mV", + ))?; + volt_range.contains(&self.awg.trigger.ext0.rearm_level).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.awg.trigger.ext0.rearm_level.to_string(), + "awg.trigger.ext0.rearm_level".to_string(), + "in range -10000..+10000 mV", + ))?; + volt_range.contains(&self.awg.trigger.ext1.level).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.awg.trigger.ext1.level.to_string(), + "awg.trigger.ext1.level".to_string(), + "in range -10000..+10000 mV", + ))?; + volt_range.contains(&self.awg.trigger.ext1.rearm_level).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.awg.trigger.ext1.rearm_level.to_string(), + "awg.trigger.ext1.rearm_level".to_string(), + "in range -10000..+10000 mV", + ))?; + Ok(()) + } + + + fn check_awg_channels(&self) -> ConfigResult<()> { + (self.awg.active_channels.stop_levels.len() == 4).then_some(()) + .ok_or(ConfigError::InvalidValue( + "[{...}]".to_string(), + "awg.active_channels.stop_levels".to_string(), + "length must be 4", + ))?; + let channels = &self.awg.active_channels.enabled_channels; + (channels.iter().filter(|&&c| c).count() != 3).then_some(()) + .ok_or(ConfigError::InvalidValue( + "[{...}]".to_string(), + "awg.active_channels.enabled_channels".to_string(), + "number of enabled channels must not be 3", + ))?; + self.awg.active_channels.amplitudes.iter().enumerate() + .map(|(k, &)| { + (80..=2500).contains(&).then_some(()) + .ok_or(ConfigError::InvalidValue( + amp.to_string(), + format!("awg.active_channels.amplitudes[{}]", k), + "in range 80..2500 mV", + )) + }) + .collect::<ConfigResult<Vec<()>>>()?; + + Ok(()) + } + + fn check_awg_sequence_steps(&self) -> ConfigResult<()> { + self.awg.sequence_segments.iter().enumerate() + .map(|(k, seg)| { + Ok((k, seg)) + .and_then(|(k, seg)| { + (seg.source.exists() + && seg.source.to_str().unwrap_or("").ends_with(".txt") + ) + .then_some((k, seg)) + .ok_or(ConfigError::InvalidValue( + seg.source.to_string_lossy().to_string(), + format!("awg.sequence_segments[{}].source", k), + "file must exist and end with .txt", + )) }) - }); - } - - /// Alias for [`Self::get_path_s_ok_into`]. - pub fn a<'de, T>(&self, keys: &str) -> ConfigResult<T> - where T: Deserialize<'de> + }) + .collect::<ConfigResult<Vec<_>>>()?; + Ok(()) + } + + fn check_awg_params(&self) -> ConfigResult<()> { + let n_segs = self.awg.num_segments; + (n_segs & (n_segs - 1) == 0) // n_segs must be a power of two + .then_some(()) + .ok_or(ConfigError::InvalidValue( + n_segs.to_string(), + "awg.num_segments".to_string(), + "must be a power of two", + ))?; + + (self.awg.sampling_rate >= 1).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.awg.sampling_rate.to_string(), + "awg.sampling_rate".to_string(), + "≥ 1", + ))?; + self.check_awg_trigger_params()?; + self.check_awg_channels()?; + self.check_awg_sequence_steps()?; + Ok(()) + } + + fn check_timetagger_params(&self) -> ConfigResult<()> { + (self.timetagger.binwidth_sec > 0.0).then_some(()) + .ok_or(ConfigError::InvalidValue( + self.timetagger.binwidth_sec.to_string(), + "timetagger.binwidth_sec".to_string(), + "> 0.0", + ))?; + Ok(()) + } + + // fn check_basler_params(&self) -> ConfigResult<()> { + // (self.basler.exposure_time_us > 0.0).then_some(()) + // .ok_or(ConfigError::InvalidValue( + // self.basler.exposure_time_us.to_string(), + // "basler.exposure_time_us".to_string(), + // "> 0.0", + // ))?; + // (self.basler.frame_rate > 0).then_some(()) + // .ok_or(ConfigError::InvalidValue( + // self.basler.frame_rate.to_string(), + // "basler.frame_rate".to_string(), + // "> 0", + // ))?; + // ( + // self.basler.roi[0] < self.basler.roi[1] + // && self.basler.roi[1] <= awg::Basler::SENSOR_WIDTH + // && self.basler.roi[2] < self.basler.roi[3] + // && self.basler.roi[3] <= awg::Basler::SENSOR_HEIGHT + // ).then_some(()) + // .ok_or(ConfigError::InvalidValue( + // format!("{:?}", self.basler.roi), + // "basler.roi".to_string(), + // "xmin < xmax < 3840, ymin < ymax < 2740", + // ))?; + // Ok(()) + // } + + /// Verify that all necessary conditions are satisfied. + pub fn check(&self) -> ConfigResult<()> { + self.check_shutter_times()?; + self.check_shift_speed()?; + self.check_kinetic_params()?; + self.check_roi()?; + self.check_camera_temperature()?; + self.check_camera_params()?; + self.check_windowavg()?; + self.check_awg_params()?; + self.check_timetagger_params()?; + // self.check_basler_params()?; + Ok(()) + } + + + /// Load a config from a file and verify that all necessary conditions are + /// satisfied. + pub fn load<P>(infile: P) -> ConfigResult<Self> + where P: AsRef<Path> { - return self.get_path_s_ok_into(keys); - } -} - -/// Load a [`Config`] from the given path, optionally verifying against a -/// specification. -fn load_config<P>(infile: P, verify: Option<&ConfigSpec>) - -> ConfigResult<Config> -where P: AsRef<Path> -{ - let infile_str: String = infile.as_ref().display().to_string(); - let table: Table - = fs::read_to_string(infile) - .map_err(|_| ConfigError::FileRead(infile_str.clone()))? - .parse() - .map_err(|_| ConfigError::FileParse(infile_str.clone()))?; - return if let Some(config_spec) = verify { - config_spec.verify(table) - } else { - Ok(Config { data: table }) - }; -} - -/// Load a [`Config`] from the given path, verifying aginst -/// [`ConfigSpec::default`] -pub fn load_def_config<P>(infile: P) -> ConfigResult<Config> -where P: AsRef<Path> -{ - return load_config(infile, Some(DEF_CONFIG_SPEC.deref())); -} - -#[cfg(test)] -mod test { - use super::*; - - fn create_table_good() -> Table { - return "[suptab]\nf1 = true\nf2 = \"foo\"\nf3 = [1.0, 1]" - .parse::<Table>().unwrap(); - } - - fn create_table_bad() -> Table { - return "[suptab]\nf1 = true\nf2 = \"foo\"\nf3 = [1, 1.0]" - .parse::<Table>().unwrap(); - } - - fn create_spec() -> ConfigSpec { - return config_spec!( - "suptab" => spec!(Table { - "f1" => spec!(Bool), - "f2" => spec!(StrCollection { "foo", "bar" }), - "f3" => spec!(TypedArray { - [TypeVer::Float, TypeVer::Int], - finite: true - }), - }), - ); - } - - #[test] - fn verify_config_good() -> ConfigResult<()> { - let table = create_table_good(); - let spec = create_spec(); - spec.verify(table)?; - return Ok(()); - } - - #[test] - #[should_panic] - fn verify_config_bad() -> () { - let table = create_table_bad(); - let spec = create_spec(); - spec.verify(table).expect("should fail!"); - return (); - } - - #[test] - fn key_path_access() -> ConfigResult<()> { - let table = create_table_good(); - let spec = create_spec(); - let config = spec.verify(table)?; - config.get_path_ok(["suptab", "f1"])?; - config.get_path_s_ok("suptab.f1")?; - return Ok(()); - } - - #[test] - #[should_panic] - fn key_path_access_bad() -> () { - let table = create_table_good(); - let spec = create_spec(); - let config = spec.verify(table).expect("won't fail"); - config.get_path_ok(["suptab", "f4"]).expect("should fail!"); - return (); - } - - #[test] - fn key_path_access_convert() -> ConfigResult<()> { - let table = create_table_good(); - let spec = create_spec(); - let config = spec.verify(table)?; - let f1: bool = config.get_path_ok_into(["suptab", "f1"])?; - assert_eq!(f1, true); - return Ok(()); - } - - #[test] - #[should_panic] - fn key_path_access_convert_bad() -> () { - let table = create_table_good(); - let spec = create_spec(); - let config = spec.verify(table).expect("won't fail"); - let f1: f64 = config.get_path_ok_into(["suptab", "f1"]) - .expect("should fail!"); - assert_eq!(f1, 1.0); - return () + let infile_str: String = infile.as_ref().display().to_string(); + let config_str: String + = fs::read_to_string(infile) + .map_err(|err| { + ConfigError::FileRead(infile_str.clone(), err.to_string()) + })?; + let config: Self + = toml::from_str(&config_str) + .map_err(|err| { + ConfigError::FileParse(infile_str.clone(), err.to_string()) + })?; + config.check()?; + Ok(config) } } diff --git a/src/cli.rs b/src/cli.rs index 44e682a..50f8f8f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -76,7 +76,16 @@ fn main() -> Result<()> { } // TODO - let awg: Option<AWG> = None; + let mut awg: Option<AWG> + = config.awg.r#use + .then(|| { + println!("Connect to AWG"); + AWG::connect(config.awg.index) + }) + .transpose()?; + if awg.is_some() { + AWG::set_config(awg.as_mut().unwrap(), &config.awg.into())?; + } let tagger: Option<TimeTaggerUltra> = config.timetagger.r#use -- GitLab