Compare commits

...

2 Commits

5 changed files with 142 additions and 8 deletions

7
Cargo.lock generated
View File

@ -1,5 +1,10 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "argparse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "autocfg"
version = "0.1.6"
@ -14,6 +19,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "noptica-rs"
version = "0.1.0"
dependencies = [
"argparse 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
@ -90,6 +96,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum argparse 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3f8ebf5827e4ac4fd5946560e6a99776ea73b596d80898f357007317a7141e47"
"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875"
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"

View File

@ -17,3 +17,4 @@ num-traits = "0.2"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
argparse = "0.2.2"

View File

@ -15,9 +15,12 @@ pub struct Dpll {
}
impl Dpll {
pub const TURN: i64 = 0x100000000; // One turn in DPLL phase units.
pub fn new(ftw_min: i64, ftw_max: i64, ki: i64, kp: i64) -> Dpll {
assert!(ftw_min < 0x80000000);
assert!(ftw_max < 0x80000000);
assert!(ftw_min < Dpll::TURN/2);
assert!(ftw_max < Dpll::TURN/2);
assert!(Dpll::TURN & (Dpll::TURN - 1) == 0); // must be a power of 2
let init_ftw = (ftw_min + ftw_max)/2;
Dpll {
ftw_min: ftw_min,
@ -32,14 +35,14 @@ impl Dpll {
}
pub fn frequency_to_ftw(frequency: f64, sample_rate: f64) -> i64 {
(frequency*(0x100000000i64 as f64)/sample_rate) as i64
(frequency*(Dpll::TURN as f64)/sample_rate) as i64
}
pub fn tick(&mut self, edge: bool) {
self.phase = (self.phase + self.ftw) & 0xffffffff;
self.phase = (self.phase + self.ftw) & (Dpll::TURN - 1);
self.phase_unwrapped = self.phase_unwrapped.wrapping_add(self.ftw);
if edge {
let pe = 0x80000000 - self.phase;
let pe = Dpll::TURN/2 - self.phase;
self.integrator = clamp(self.integrator + (pe*self.ki >> 32),
self.ftw_min, self.ftw_max);
self.ftw = clamp(self.integrator + (pe*self.kp >> 32),
@ -68,7 +71,7 @@ impl PositionTracker {
pub fn edge(&mut self, phase: i64) -> i64 {
let phase_diff = phase.wrapping_sub(self.last_phase);
self.last_phase = phase;
self.current_position += 0x100000000 - phase_diff;
self.current_position += Dpll::TURN - phase_diff;
self.current_position
}
}

View File

@ -1,3 +1,109 @@
fn main() {
println!("hello world");
extern crate argparse;
extern crate num_traits;
extern crate serde_derive;
extern crate serde_json;
use argparse::{ArgumentParser, StoreTrue, Store};
use serde_derive::Deserialize;
use std::error::Error;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
mod noptica;
#[derive(Deserialize, Debug)]
struct Config {
sample_command: String, // Shell command to start the logic analyzer.
sample_rate: f64, // Sample rate of the logic analyzer in Hz.
// The logic analyzer command must produce a stream of 4-bit nibbles on its
// standard output, which are continuously sampled at the nominal sample rate.
// Each of the signals below are mapped to one bit within each nibble.
bit_ref: u8, // Bit# for REF signal of the reference laser head (HP 5501B).
bit_meas: u8, // Bit# for displacement measurement detector (HP 10780).
bit_input: u8, // Bit# for input laser interference detector.
// The REF DPLL locks to the REF output of the reference laser and provides REF phase
// information at each sample of the logic analyzer.
// ref_min and ref_max are used to initialize the DPLL and clamp its NCO frequency.
ref_min: f64, // Minimum REF frequency in Hz.
ref_max: f64, // Maximum REF frequency in Hz.
refpll_ki: i64, // Integration constant of the DPLL loop filter.
refpll_kp: i64, // Proportionality constant of the DPLL loop filter.
ref_wavelength: f64, // Wavelength of the reference laser in m.
scan_displacement: f64, // Optical path difference across one scan cycle in m (use -c).
scan_frequency: f64, // Frequency of the scan in Hz.
scan_blanking: f64, // Fraction of the scan period that is blanked at the beginning
// and end of each slope (4 times per scan period).
}
fn read_config_from_file<P: AsRef<Path>>(path: P) -> Result<Config, Box<dyn Error>> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let u = serde_json::from_reader(reader)?;
Ok(u)
}
fn do_calibrate(config: &Config) {
let mut sample_count = 0;
let max_sample_count = (config.sample_rate/4.0) as u32;
let mut position_min = i64::max_value();
let mut position_max = i64::min_value();
let mut refpll = noptica::Dpll::new(
noptica::Dpll::frequency_to_ftw(config.ref_min, config.sample_rate),
noptica::Dpll::frequency_to_ftw(config.ref_max, config.sample_rate),
config.refpll_ki,
config.refpll_kp);
let mut position_tracker = noptica::PositionTracker::new();
noptica::sample(&config.sample_command, |rising, _falling| {
refpll.tick(rising & (1 << config.bit_ref) != 0);
if rising & (1 << config.bit_meas) != 0 {
let position = position_tracker.edge(refpll.get_phase_unwrapped());
if position > position_max {
position_max = position;
}
if position < position_min {
position_min = position;
}
}
sample_count += 1;
if sample_count == max_sample_count {
println!("{}", ((position_max-position_min) as f64)/(noptica::Dpll::TURN as f64)*config.ref_wavelength);
sample_count = 0;
position_min = i64::max_value();
position_max = i64::min_value();
}
})
}
fn do_wavemeter(config: &Config) {
println!("TODO {}", config.sample_command);
}
fn main() {
let mut calibrate = false;
let mut config_file = "wavemeter.json".to_string();
{
let mut ap = ArgumentParser::new();
ap.refer(&mut calibrate)
.add_option(&["-c", "--calibrate"], StoreTrue,
"Calibrate scan displacement");
ap.refer(&mut config_file)
.add_option(&["--config"], Store,
"Configuration file");
ap.parse_args_or_exit();
}
let config = read_config_from_file(config_file).unwrap();
if calibrate {
do_calibrate(&config);
} else {
do_wavemeter(&config);
}
}

17
wavemeter.json Normal file
View File

@ -0,0 +1,17 @@
{
"sample_command": "glasgow run logic -V 3.3 --pins-d 0,1,2",
"sample_rate": 48e6,
"bit_ref": 0,
"bit_meas": 1,
"bit_input": 2,
"ref_min": 1.9e6,
"ref_max": 2.1e6,
"refpll_ki": 4294967,
"refpll_kp": 85899345,
"ref_wavelength": 632.991372e-9,
"scan_displacement": 40e-6,
"scan_frequency": 50.0,
"scan_blanking": 0.05
}