This commit is contained in:
2026-07-27 13:54:41 +08:00
parent 0cbfb764a7
commit 0c01b2127b
5 changed files with 136 additions and 3 deletions

View File

@ -8,6 +8,11 @@ pub fn interpolate_spline(x: Vec<f64>, y: Vec<f64>, step: f64) ->Vec<(f64, f64)>
spectraltools::interpolate_spline(x, y, step).unwrap()
}
#[tauri::command]
pub fn interpolate_spline_smooth(x: Vec<f64>, y: Vec<f64>, step: f64) ->Vec<(f64, f64)>{
spectraltools::interpolate_spline_smooth(x, y, step).unwrap()
}
#[tauri::command]
pub fn interpolate_spline_at_points(x: Vec<f64>, y: Vec<f64>, x_target: Vec<f64>) -> Vec<f64>{
spectraltools::interpolate_spline_at_points(x, y, x_target).unwrap()

View File

@ -39,6 +39,40 @@ pub fn interpolate_spline<T: Copy + Into<f64>,>(x_t: Vec<T>, y_t: Vec<T>, step:
Ok(result)
}
pub fn interpolate_spline_smooth<T: Copy + Into<f64>>(x_t: Vec<T>, y_t: Vec<T>, step: f64) -> Result<Vec<(f64, f64)>, Box<dyn Error>> {
let x: Vec<f64> = x_t.iter().map(|&x| x.into()).collect();
let y: Vec<f64> = 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<Key<f64, f64>> = 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<T: Copy + Into<f64>>(x_t: Vec<T>, y_t: Vec<T>, x_target: Vec<f64>) -> Result<Vec<f64>, Box<dyn Error>> {
let x: Vec<f64> = x_t.iter().map(|&x| x.into()).collect();
let y: Vec<f64> = 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);
}
}

View File

@ -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,

View File

@ -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;

View File

@ -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 - 检测到的峰位置数组(单位:像素或通道号)