From 0c01b2127b4e5c9d680dc0bcc6e34e652b54d2c0 Mon Sep 17 00:00:00 2001 From: renlixin Date: Mon, 27 Jul 2026 13:54:41 +0800 Subject: [PATCH] 6.95 --- src-tauri/src/algorithm/mod.rs | 5 ++ src-tauri/src/algorithm/spectraltools.rs | 72 ++++++++++++++++++++ src-tauri/src/main.rs | 2 + src/components/menubox/SetCalibrateHH3.vue | 2 + src/components/menubox/SetWavelenthcoeff.vue | 58 +++++++++++++++- 5 files changed, 136 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/algorithm/mod.rs b/src-tauri/src/algorithm/mod.rs index 964f2a4..8dd1f1f 100644 --- a/src-tauri/src/algorithm/mod.rs +++ b/src-tauri/src/algorithm/mod.rs @@ -8,6 +8,11 @@ pub fn interpolate_spline(x: Vec, y: Vec, step: f64) ->Vec<(f64, f64)> spectraltools::interpolate_spline(x, y, step).unwrap() } +#[tauri::command] +pub fn interpolate_spline_smooth(x: Vec, y: Vec, step: f64) ->Vec<(f64, f64)>{ + spectraltools::interpolate_spline_smooth(x, y, step).unwrap() +} + #[tauri::command] pub fn interpolate_spline_at_points(x: Vec, y: Vec, x_target: Vec) -> Vec{ spectraltools::interpolate_spline_at_points(x, y, x_target).unwrap() diff --git a/src-tauri/src/algorithm/spectraltools.rs b/src-tauri/src/algorithm/spectraltools.rs index 243f9df..83f21d4 100644 --- a/src-tauri/src/algorithm/spectraltools.rs +++ b/src-tauri/src/algorithm/spectraltools.rs @@ -39,6 +39,40 @@ pub fn interpolate_spline,>(x_t: Vec, y_t: Vec, step: Ok(result) } +pub fn interpolate_spline_smooth>(x_t: Vec, y_t: Vec, step: f64) -> Result, Box> { + let x: Vec = x_t.iter().map(|&x| x.into()).collect(); + let y: Vec = y_t.iter().map(|&y| y.into()).collect(); + + if x.len() != y.len() { + return Err("x and y must have the same length".into()); + } + + // 使用 Catmull-Rom 样条插值(平滑曲线) + let keys: Vec> = x.iter() + .zip(y.iter()) + .map(|(&x, &y)| Key::new(x, y, Interpolation::CatmullRom)) + .collect(); + + let spline = Spline::from_vec(keys); + + // 计算 x 的最大值和最小值 + let &start = x.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap(); + let &end = x.iter().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap(); + + // 插值到间隔为 step 的点 + let mut result = Vec::new(); + let mut t = start; + + while t <= end { + if let Some(value) = spline.clamped_sample(t) { + result.push((t, value)); + } + t += step; + } + + Ok(result) +} + pub fn interpolate_spline_at_points>(x_t: Vec, y_t: Vec, x_target: Vec) -> Result, Box> { let x: Vec = x_t.iter().map(|&x| x.into()).collect(); let y: Vec = y_t.iter().map(|&y| y.into()).collect(); @@ -181,4 +215,42 @@ fn test_find_peek(){ for p in peaks { println!("{} {}", p.0, p.1); } +} + +#[test] +fn test_compute_weave_coeff_with_data(){ + let pixels = vec![79.0, 128.0, 146.0, 157.0, 201.0, 240.0, 261.0, 334.0, 362.0, 436.0]; + let wavelengths = vec![965.779, 892.869, 866.794, 850.887, 785.482, 727.294, 696.543, 587.092, 546.074, 435.833]; + + let result = compute_weave_coeff(pixels.clone(), wavelengths.clone()); + + let a0 = result[3]; // 三次项 + let a1 = result[2]; // 二次项 + let a2 = result[1]; // 一次项 + let a3 = result[0]; // 常数项 + + println!("=== compute_weave_coeff 原始返回 ==="); + println!("[{:.15e}, {:.15e}, {:.15e}, {:.15e}]", result[0], result[1], result[2], result[3]); + + println!(); + println!("=== 前端 bochangxishu ==="); + println!("a0 (³项) = {:.15e}", a0); + println!("a1 (²项) = {:.15e}", a1); + println!("a2 (¹项) = {:.15e}", a2); + println!("a3 (常数) = {:.15e}", a3); + + println!(); + println!("=== 拟合验证 ==="); + for i in 0..pixels.len() { + let p = pixels[i]; + let pred = result[0] + result[1]*p + result[2]*p.powi(2) + result[3]*p.powi(3); + println!("像素 {:3.0}: 输入={:.3}nm, 拟合={:.3}nm, 误差={:+.3}nm", p, wavelengths[i], pred, pred-wavelengths[i]); + } + + println!(); + println!("=== 前端公式: weave = a0*p³ + a1*p² + a2*p + a3 ==="); + for &p in &[0.0_f64, 79.0, 128.0, 146.0, 157.0, 201.0, 240.0, 261.0, 334.0, 362.0, 436.0, 2047.0] { + let pred = a0*p.powi(3) + a1*p.powi(2) + a2*p + a3; + println!("像素 {:4.0}: {:.3}nm", p, pred); + } } \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index c4ff536..19f5109 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -11,6 +11,7 @@ mod comman1; mod myformatiris; use comman1::*; use algorithm::interpolate_spline; +use algorithm::interpolate_spline_smooth; use algorithm::sg_smooth; use mydefine::*; use iris_spectral::spectralbase::Senortype; @@ -245,6 +246,7 @@ fn main() { readformport, sendtoport, interpolate_spline, + interpolate_spline_smooth, sg_smooth, savecalibratefile, savecalibratefileIRIS, diff --git a/src/components/menubox/SetCalibrateHH3.vue b/src/components/menubox/SetCalibrateHH3.vue index de69da6..46af3b3 100644 --- a/src/components/menubox/SetCalibrateHH3.vue +++ b/src/components/menubox/SetCalibrateHH3.vue @@ -463,6 +463,7 @@ export default { let coeffweave3 = this.Devinfo.bochangxishu.a2; let coeffweave4 = this.Devinfo.bochangxishu.a3; await SensorMethod.Set_Gain(this.sensor_gain_up); + await SensorMethod.Get_Dark_Data(dire, this.shutter_time_up, Number(this.caijicishu[0])); var data = await SensorMethod.Get_Date_on_Derction(dire, this.shutter_time_up, true, Number(this.caijicishu[0])) let lastvalue = this.DataUP.value_lable; this.DataUP = data; @@ -487,6 +488,7 @@ export default { let coeffweave3 = this.Devinfo.bochangxishu2.a2; let coeffweave4 = this.Devinfo.bochangxishu2.a3; await SensorMethod.Set_Gain(this.sensor_gain_down); + await SensorMethod.Get_Dark_Data(dire, this.shutter_time_up, Number(this.caijicishu[0])); var data = await SensorMethod.Get_Date_on_Derction(dire, this.shutter_time_down, true, Number(this.caijicishu[1])) let lastvalue = this.DataDown.value_lable; this.DataDown = data; diff --git a/src/components/menubox/SetWavelenthcoeff.vue b/src/components/menubox/SetWavelenthcoeff.vue index ee66526..d5a32b2 100644 --- a/src/components/menubox/SetWavelenthcoeff.vue +++ b/src/components/menubox/SetWavelenthcoeff.vue @@ -274,7 +274,7 @@ export default { if (specindex == 0) { let originalData = this.Data.data; // Step 1: 亚像素插值 (0.05像素间隔,20倍数据量) - let resampled = await invoke("interpolate_spline", { + let resampled = await invoke("interpolate_spline_smooth", { x: Array.from({length: originalData.length}, (_, i) => i), y: originalData.map(v => Number(v)), step: 0.05 @@ -349,7 +349,7 @@ export default { else if (specindex == 1) { let originalDataDown = this.DataDown.data; // Step 1: 亚像素插值 (0.05像素间隔,20倍数据量) - let resampledDown = await invoke("interpolate_spline", { + let resampledDown = await invoke("interpolate_spline_smooth", { x: Array.from({length: originalDataDown.length}, (_, i) => i), y: originalDataDown.map(v => Number(v)), step: 0.05 @@ -605,6 +605,8 @@ export default { var chart_up = echarts.getInstanceByDom(this.$refs.chart_weavelenth); chart_up.setOption(this.option); + await this.savePeakResultToCSV(0, orgdata); + } else if (spectralnumber == 1) { this.Devinfo.bochangxishu2.a0 = result[3]; this.Devinfo.bochangxishu2.a1 = result[2]; @@ -626,6 +628,8 @@ export default { var chart_up = echarts.getInstanceByDom(this.$refs.chart_weavelenth_down); chart_up.setOption(this.optiondown); + await this.savePeakResultToCSV(1, orgdata); + } }, @@ -670,7 +674,55 @@ export default { await fs.writeTextFile(savePath, JSON.stringify(data, null, 2)); }, - + async savePeakResultToCSV(spectralnumber, orgdata) { + if (!orgdata || orgdata.length === 0) return; + + const now = new Date(); + const pad = n => String(n).padStart(2, '0'); + const dateStr = `${now.getFullYear()}${pad(now.getMonth()+1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`; + const serial = this.Devinfo?.serialnumber || this.Devinfo?.SerilNumber || "Unknown"; + const isDual = this.Devinfo?.fiber_type === "Dual"; + const sensorLabel = spectralnumber === 0 ? "UP" : "DOWN"; + + let fileSuffix = ""; + if (isDual) { + fileSuffix = `_${sensorLabel}`; + } + const defaultName = `${serial}${fileSuffix}_${dateStr}.csv`; + + const savePath = await dialog.save({ + defaultPath: defaultName, + filters: [{ name: "CSV文件", extensions: ["csv"] }] + }); + if (!savePath) return; + + const coeff = spectralnumber === 0 + ? [this.Devinfo.bochangxishu.a0, this.Devinfo.bochangxishu.a1, this.Devinfo.bochangxishu.a2, this.Devinfo.bochangxishu.a3] + : [this.Devinfo.bochangxishu2.a0, this.Devinfo.bochangxishu2.a1, this.Devinfo.bochangxishu2.a2, this.Devinfo.bochangxishu2.a3]; + + let lines = []; + lines.push(`# 设备序号: ${serial}`); + lines.push(`# 传感器: ${sensorLabel}`); + lines.push(`# 时间: ${now.toISOString()}`); + for (let i = 0; i < 4; i++) { + lines.push(`# a${i}: ${coeff[i]}`); + } + lines.push("像素,参考波长,拟合波长,误差"); + + orgdata.forEach(el => { + if (el[3] === true) { + let pix = el[2]; + let refWl = el[4]; + let fitWl = coeff[0]*pix*pix*pix + coeff[1]*pix*pix + coeff[2]*pix + coeff[3]; + let err = fitWl - refWl; + let errStr = err >= 0 ? "+" + err.toFixed(3) : err.toFixed(3); + lines.push(`${pix},${refWl},${fitWl.toFixed(3)},${errStr}`); + } + }); + + await fs.writeTextFile(savePath, lines.join("\n")); + }, + /** * 光谱峰自动匹配函数 * @param {number[]} detectedPeaks - 检测到的峰位置数组(单位:像素或通道号)