|
|
@@ -5,18 +5,18 @@
|
|
|
<meta charset="UTF-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
<title>RT Table Import</title>
|
|
|
- <link rel="stylesheet" href="styles.css">
|
|
|
+ <link rel="stylesheet" href="./styles.css">
|
|
|
</head>
|
|
|
|
|
|
<body>
|
|
|
<header>
|
|
|
<h1>RT Table Generator</h1>
|
|
|
<ul class="nav-tabs">
|
|
|
- <li><a href="RT_Table_NTC.html">NTC</a></li>
|
|
|
- <li><a href="RT_Table_PT100.html">PT100</a></li>
|
|
|
- <li><a href="RT_Table_PT1000.html">PT1000</a></li>
|
|
|
- <li><a href="VT_Table_TJ.html">Thermocouple</a></li>
|
|
|
- <li><a href="RT_Table_Import.html" class="active">Import Data</a></li>
|
|
|
+ <li><a href="./RT_Table_NTC.html">NTC</a></li>
|
|
|
+ <li><a href="./RT_Table_PT100.html">PT100</a></li>
|
|
|
+ <li><a href="./RT_Table_PT1000.html">PT1000</a></li>
|
|
|
+ <li><a href="./VT_Table_TJ.html">Thermocouple</a></li>
|
|
|
+ <li><a href="./RT_Table_Import.html" class="active">Import Data</a></li>
|
|
|
</ul>
|
|
|
</header>
|
|
|
|
|
|
@@ -141,6 +141,14 @@
|
|
|
<input type="number" id="output-step" name="output-step" value="1" min="0.1" step="0.1" class="tableInput">
|
|
|
</td>
|
|
|
</tr>
|
|
|
+ <tr>
|
|
|
+ <td>
|
|
|
+ <label for="result-coefficient">Result Coefficient:</label>
|
|
|
+ </td>
|
|
|
+ <td>
|
|
|
+ <input type="number" id="result-coefficient" name="result-coefficient" value="1" min="0.001" step="0.001" class="tableInput">
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
<tr>
|
|
|
<td colspan="2">
|
|
|
<button onclick="generateOutputTable()">Generate Output Table</button>
|
|
|
@@ -182,24 +190,24 @@
|
|
|
// 全局变量
|
|
|
let importedData = [];
|
|
|
let processedData = [];
|
|
|
-
|
|
|
+
|
|
|
// 页面加载时初始化
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
// 设置默认数据类型为RT
|
|
|
document.getElementById('data-type').value = 'RT';
|
|
|
-
|
|
|
+
|
|
|
// 初始化输出温度范围
|
|
|
document.getElementById('output_T_min').value = 0;
|
|
|
document.getElementById('output_T_max').value = 0;
|
|
|
});
|
|
|
-
|
|
|
+
|
|
|
// 检测分隔符
|
|
|
function detectDelimiter(text) {
|
|
|
const lines = text.trim().split(/\r?\n/);
|
|
|
if (lines.length === 0) return 'tab';
|
|
|
-
|
|
|
+
|
|
|
const firstLine = lines[0];
|
|
|
-
|
|
|
+
|
|
|
// 检查各种分隔符的出现次数
|
|
|
const delimiters = {
|
|
|
'tab': (firstLine.match(/\t/g) || []).length,
|
|
|
@@ -207,21 +215,21 @@
|
|
|
'semicolon': (firstLine.match(/;/g) || []).length,
|
|
|
'space': (firstLine.match(/ /g) || []).length
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
// 找出出现次数最多的分隔符
|
|
|
let maxCount = 0;
|
|
|
let detectedDelimiter = 'tab';
|
|
|
-
|
|
|
+
|
|
|
for (const [delimiter, count] of Object.entries(delimiters)) {
|
|
|
if (count > maxCount) {
|
|
|
maxCount = count;
|
|
|
detectedDelimiter = delimiter;
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
+ }
|
|
|
+
|
|
|
return detectedDelimiter;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 处理导入的数据
|
|
|
function processImportedData() {
|
|
|
const importText = document.getElementById("importData").value.trim();
|
|
|
@@ -229,56 +237,56 @@
|
|
|
alert("Please paste data first!");
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 获取用户选择的分隔符或自动检测
|
|
|
let delimiterChoice = document.getElementById("delimiter").value;
|
|
|
let delimiterChar;
|
|
|
-
|
|
|
+
|
|
|
if (delimiterChoice === 'auto') {
|
|
|
delimiterChoice = detectDelimiter(importText);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 设置实际的分隔符字符
|
|
|
switch (delimiterChoice) {
|
|
|
- case 'tab': delimiterChar = '\t'; break;
|
|
|
- case 'comma': delimiterChar = ','; break;
|
|
|
+ case 'tab': delimiterChar = '\t'; break;
|
|
|
+ case 'comma': delimiterChar = ','; break;
|
|
|
case 'space': delimiterChar = ' '; break;
|
|
|
case 'semicolon': delimiterChar = ';'; break;
|
|
|
default: delimiterChar = '\t';
|
|
|
}
|
|
|
-
|
|
|
- // 分割文本为行
|
|
|
+
|
|
|
+ // 分割文本为行
|
|
|
const lines = importText.split(/\r?\n/);
|
|
|
importedData = [];
|
|
|
-
|
|
|
+
|
|
|
// 处理每一行
|
|
|
for (const line of lines) {
|
|
|
if (!line.trim()) continue; // 跳过空行
|
|
|
-
|
|
|
+
|
|
|
// 分割行为列
|
|
|
- let columns;
|
|
|
+ let columns;
|
|
|
if (delimiterChoice === 'space') {
|
|
|
// 对于空格分隔,处理多个连续空格的情况
|
|
|
columns = line.trim().split(/\s+/);
|
|
|
} else {
|
|
|
columns = line.split(delimiterChar);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 确保至少有两列
|
|
|
if (columns.length >= 2) {
|
|
|
- const temp = parseFloat(columns[0]);
|
|
|
+ const temp = parseFloat(columns[0]);
|
|
|
const value = parseFloat(columns[1]);
|
|
|
-
|
|
|
+
|
|
|
// 确保两个值都是有效的数字
|
|
|
if (!isNaN(temp) && !isNaN(value)) {
|
|
|
importedData.push({ temperature: temp, value: value });
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 按温度排序
|
|
|
importedData.sort((a, b) => a.temperature - b.temperature);
|
|
|
-
|
|
|
+
|
|
|
// 更新数据类型标题
|
|
|
const dataType = document.getElementById("data-type").value;
|
|
|
const valueHeader = document.getElementById("value-header");
|
|
|
@@ -287,28 +295,28 @@
|
|
|
} else {
|
|
|
valueHeader.textContent = "Voltage (mV)";
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 显示导入的数据
|
|
|
displayTable(importedData);
|
|
|
-
|
|
|
- // 更新参数
|
|
|
+
|
|
|
+ // 更新参数
|
|
|
if (importedData.length > 0) {
|
|
|
// 设置温度范围
|
|
|
- const minTemp = importedData[0].temperature;
|
|
|
+ const minTemp = importedData[0].temperature;
|
|
|
const maxTemp = importedData[importedData.length - 1].temperature;
|
|
|
-
|
|
|
+
|
|
|
document.getElementById("param_T_min").value = minTemp;
|
|
|
document.getElementById("param_T_max").value = maxTemp;
|
|
|
-
|
|
|
+
|
|
|
// 同时更新输出温度范围
|
|
|
- document.getElementById("output_T_min").value = minTemp;
|
|
|
+ document.getElementById("output_T_min").value = minTemp;
|
|
|
document.getElementById("output_T_max").value = maxTemp;
|
|
|
-
|
|
|
+
|
|
|
// 计算平均步长
|
|
|
if (importedData.length > 1) {
|
|
|
let totalSteps = 0;
|
|
|
let stepCount = 0;
|
|
|
-
|
|
|
+
|
|
|
for (let i = 1; i < importedData.length; i++) {
|
|
|
const step = importedData[i].temperature - importedData[i-1].temperature;
|
|
|
if (step > 0) {
|
|
|
@@ -316,39 +324,39 @@
|
|
|
stepCount++;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const avgStep = stepCount > 0 ? (totalSteps / stepCount) : 1;
|
|
|
document.getElementById("param_step").value = avgStep.toFixed(2);
|
|
|
document.getElementById("output-step").value = avgStep.toFixed(2);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 显示JSON数据
|
|
|
displayJsonData(importedData);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 线性插值函数
|
|
|
function linearInterpolate(x, x0, y0, x1, y1) {
|
|
|
return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 多项式拟合函数
|
|
|
function polynomialFit(data, degree) {
|
|
|
// 简单实现,实际应用中可能需要更复杂的算法
|
|
|
- // 这里使用最小二乘法拟合多项式
|
|
|
+ // 这里使用最小二乘法拟合多项式
|
|
|
const n = data.length;
|
|
|
const x = data.map(d => d.temperature);
|
|
|
const y = data.map(d => d.value);
|
|
|
-
|
|
|
+
|
|
|
// 创建矩阵
|
|
|
const X = [];
|
|
|
for (let i = 0; i < n; i++) {
|
|
|
- X[i] = [];
|
|
|
+ X[i] = [];
|
|
|
for (let j = 0; j <= degree; j++) {
|
|
|
X[i][j] = Math.pow(x[i], j);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 转置矩阵
|
|
|
const Xt = [];
|
|
|
for (let j = 0; j <= degree; j++) {
|
|
|
@@ -357,7 +365,7 @@
|
|
|
Xt[j][i] = X[i][j];
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 计算 Xt * X
|
|
|
const XtX = [];
|
|
|
for (let i = 0; i <= degree; i++) {
|
|
|
@@ -369,22 +377,22 @@
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 计算 Xt * y
|
|
|
const Xty = [];
|
|
|
- for (let i = 0; i <= degree; i++) {
|
|
|
+ for (let i = 0; i <= degree; i++) {
|
|
|
Xty[i] = 0;
|
|
|
for (let j = 0; j < n; j++) {
|
|
|
Xty[i] += Xt[i][j] * y[j];
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 解线性方程组 XtX * coeffs = Xty
|
|
|
- // 使用高斯消元法
|
|
|
+ // 使用高斯消元法
|
|
|
const coeffs = solveLinearSystem(XtX, Xty);
|
|
|
-
|
|
|
+
|
|
|
// 返回多项式函数
|
|
|
- return function(x) {
|
|
|
+ return function(x) {
|
|
|
let result = 0;
|
|
|
for (let i = 0; i <= degree; i++) {
|
|
|
result += coeffs[i] * Math.pow(x, i);
|
|
|
@@ -392,13 +400,13 @@
|
|
|
return result;
|
|
|
};
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 解线性方程组
|
|
|
- function solveLinearSystem(A, b) {
|
|
|
+ function solveLinearSystem(A, b) {
|
|
|
const n = b.length;
|
|
|
const x = new Array(n).fill(0);
|
|
|
-
|
|
|
- // 高斯消元法
|
|
|
+
|
|
|
+ // 高斯消元法
|
|
|
for (let i = 0; i < n; i++) {
|
|
|
// 寻找主元
|
|
|
let maxEl = Math.abs(A[i][i]);
|
|
|
@@ -409,13 +417,13 @@
|
|
|
maxRow = k;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 交换行
|
|
|
if (maxRow !== i) {
|
|
|
[A[i], A[maxRow]] = [A[maxRow], A[i]];
|
|
|
[b[i], b[maxRow]] = [b[maxRow], b[i]];
|
|
|
- }
|
|
|
-
|
|
|
+ }
|
|
|
+
|
|
|
// 消元
|
|
|
for (let k = i + 1; k < n; k++) {
|
|
|
const c = -A[k][i] / A[i][i];
|
|
|
@@ -427,33 +435,33 @@
|
|
|
}
|
|
|
}
|
|
|
b[k] += c * b[i];
|
|
|
- }
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 回代
|
|
|
for (let i = n - 1; i >= 0; i--) {
|
|
|
x[i] = b[i] / A[i][i];
|
|
|
for (let k = i - 1; k >= 0; k--) {
|
|
|
b[k] -= A[k][i] * x[i];
|
|
|
- }
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return x;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 生成输出表格
|
|
|
function generateOutputTable() {
|
|
|
if (importedData.length === 0) {
|
|
|
alert("Please import and process data first!");
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 使用用户设置的输出温度范围,如果未设置则使用数据的温度范围
|
|
|
let Tmin = parseFloat(document.getElementById("output_T_min").value);
|
|
|
let Tmax = parseFloat(document.getElementById("output_T_max").value);
|
|
|
const dataTmin = parseFloat(document.getElementById("param_T_min").value);
|
|
|
const dataTmax = parseFloat(document.getElementById("param_T_max").value);
|
|
|
-
|
|
|
+
|
|
|
// 验证输出温度范围
|
|
|
if (isNaN(Tmin) || Tmin < dataTmin) {
|
|
|
Tmin = dataTmin;
|
|
|
@@ -470,29 +478,30 @@
|
|
|
document.getElementById("output_T_max").value = dataTmax;
|
|
|
alert("Output temperature min cannot be greater than max. Using data range instead.");
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const step = parseFloat(document.getElementById("output-step").value);
|
|
|
+ const resultCoefficient = parseFloat(document.getElementById("result-coefficient").value);
|
|
|
const interpolationMethod = document.getElementById("interpolation").value;
|
|
|
-
|
|
|
+
|
|
|
processedData = [];
|
|
|
-
|
|
|
+
|
|
|
if (interpolationMethod === "linear") {
|
|
|
// 线性插值
|
|
|
for (let T = Tmin; T <= Tmax; T += step) {
|
|
|
// 找到T所在的区间
|
|
|
let value;
|
|
|
-
|
|
|
+
|
|
|
// 如果T小于第一个点或大于最后一个点,使用外推
|
|
|
if (T <= importedData[0].temperature) {
|
|
|
value = importedData[0].value;
|
|
|
} else if (T >= importedData[importedData.length - 1].temperature) {
|
|
|
value = importedData[importedData.length - 1].value;
|
|
|
- } else {
|
|
|
+ } else {
|
|
|
// 找到T所在的区间
|
|
|
for (let i = 0; i < importedData.length - 1; i++) {
|
|
|
if (T >= importedData[i].temperature && T <= importedData[i + 1].temperature) {
|
|
|
value = linearInterpolate(
|
|
|
- T,
|
|
|
+ T,
|
|
|
importedData[i].temperature,
|
|
|
importedData[i].value,
|
|
|
importedData[i + 1].temperature,
|
|
|
@@ -502,67 +511,82 @@
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- processedData.push({ temperature: T, value: value });
|
|
|
+
|
|
|
+ // 应用系数并限制小数位数为6位,并去除末尾的0
|
|
|
+ const roundedT = Number(T.toFixed(6));
|
|
|
+ const roundedValue = Number((value * resultCoefficient).toFixed(6));
|
|
|
+ processedData.push({ temperature: roundedT, value: roundedValue });
|
|
|
}
|
|
|
} else if (interpolationMethod === "polynomial") {
|
|
|
// 多项式拟合
|
|
|
const degree = parseInt(document.getElementById("polynomial-degree").value);
|
|
|
const polyFunc = polynomialFit(importedData, degree);
|
|
|
-
|
|
|
+
|
|
|
for (let T = Tmin; T <= Tmax; T += step) {
|
|
|
- const value = polyFunc(T);
|
|
|
- processedData.push({ temperature: T, value: value });
|
|
|
+ // 应用系数并限制小数位数为6位,并去除末尾的0
|
|
|
+ const roundedT = Number(T.toFixed(6));
|
|
|
+ const value = Number((polyFunc(T) * resultCoefficient).toFixed(6));
|
|
|
+ processedData.push({ temperature: roundedT, value: value });
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 显示处理后的数据
|
|
|
displayTable(processedData);
|
|
|
displayJsonData(processedData);
|
|
|
displayCArrayData(processedData);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 显示表格数据
|
|
|
function displayTable(data) {
|
|
|
const tbody = document.getElementById("rtTable").querySelector("tbody");
|
|
|
tbody.innerHTML = ""; // 清空表格内容
|
|
|
-
|
|
|
+
|
|
|
const dataType = document.getElementById("data-type").value;
|
|
|
-
|
|
|
+
|
|
|
data.forEach(row => {
|
|
|
const tr = document.createElement("tr");
|
|
|
+ // 限制小数位数为6位,并去除末尾的0
|
|
|
+ const displayValue = Number(row.value.toFixed(6));
|
|
|
+ const tempValue = Number(row.temperature.toFixed(6));
|
|
|
if (dataType === "RT") {
|
|
|
- tr.innerHTML = `<td>${row.temperature}</td><td>${row.value}</td>`;
|
|
|
+ tr.innerHTML = `<td>${tempValue}</td><td>${displayValue}</td>`;
|
|
|
} else {
|
|
|
- tr.innerHTML = `<td>${row.temperature}</td><td>${row.value}</td>`;
|
|
|
+ tr.innerHTML = `<td>${tempValue}</td><td>${displayValue}</td>`;
|
|
|
}
|
|
|
tbody.appendChild(tr);
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 显示 JSON 数据到文本框
|
|
|
function displayJsonData(data) {
|
|
|
- const jsonData = JSON.stringify(data, null, 4); // 格式化JSON
|
|
|
+ // 应用小数位数限制,并去除末尾的0
|
|
|
+ const dataWithLimitedDecimals = data.map(item => ({
|
|
|
+ temperature: Number(item.temperature.toFixed(6)), // 限制温度小数位数为6位并去除末尾的0
|
|
|
+ value: Number(item.value.toFixed(6)) // 限制值小数位数为6位并去除末尾的0
|
|
|
+ }));
|
|
|
+
|
|
|
+ const jsonData = JSON.stringify(dataWithLimitedDecimals, null, 4); // 格式化JSON
|
|
|
const textarea = document.getElementById("jsonData");
|
|
|
textarea.value = jsonData;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 显示 C 语言数组到文本框
|
|
|
function displayCArrayData(data) {
|
|
|
if (data.length === 0) return;
|
|
|
-
|
|
|
- const temperatures = data.map(row => row.temperature); // 提取温度值
|
|
|
- const values = data.map(row => row.value); // 提取值
|
|
|
-
|
|
|
+
|
|
|
+ // 提取温度值和值,并限制小数位数为6位,去除末尾的0
|
|
|
+ const temperatures = data.map(row => Number(row.temperature.toFixed(6))); // 限制温度小数位数为6位并去除末尾的0
|
|
|
+ const values = data.map(row => Number(row.value.toFixed(6))); // 限制值小数位数为6位并去除末尾的0
|
|
|
+
|
|
|
const dataType = document.getElementById("data-type").value;
|
|
|
const namePrefix = document.getElementById("param_name").value;
|
|
|
- const Tmin = data[0].temperature;
|
|
|
- const Tmax = data[data.length - 1].temperature;
|
|
|
- const step = parseFloat(document.getElementById("output-step").value);
|
|
|
-
|
|
|
+ const Tmin = Number(data[0].temperature.toFixed(6));
|
|
|
+ const Tmax = Number(data[data.length - 1].temperature.toFixed(6));
|
|
|
+ const step = Number(parseFloat(document.getElementById("output-step").value).toFixed(6));
|
|
|
+
|
|
|
const prefix = (Tmin < 0 ? 'N' : 'P') + Math.abs(Tmin).toString() + 'to' + (Tmax < 0 ? 'N' : 'P') + Math.abs(Tmax).toString() + '_' + step.toString();
|
|
|
-
|
|
|
- let arrayPrefix, valueArrayName;
|
|
|
+
|
|
|
+ let arrayPrefix, valueArrayName;
|
|
|
if (dataType === "RT") {
|
|
|
arrayPrefix = `RT_table_${namePrefix}_${prefix}`;
|
|
|
valueArrayName = "R";
|
|
|
@@ -570,15 +594,19 @@
|
|
|
arrayPrefix = `VT_table_${namePrefix}_${prefix}`;
|
|
|
valueArrayName = "V";
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ // 格式化数组元素,确保使用6位小数,并去除末尾的0
|
|
|
+ const formattedTemperatures = temperatures.map(t => Number(t.toFixed(6)).toString());
|
|
|
+ const formattedValues = values.map(v => Number(v.toFixed(6)).toString());
|
|
|
+
|
|
|
const cArrayTempSize = `const uint16_t ${arrayPrefix}_size = ${temperatures.length};`;
|
|
|
- const cArrayTemp = `const double ${arrayPrefix}_T[${temperatures.length}] = { ${temperatures.join(", ")} };`;
|
|
|
- const cArrayValue = `const double ${arrayPrefix}_${valueArrayName}[${values.length}] = { ${values.join(", ")} };`;
|
|
|
-
|
|
|
+ const cArrayTemp = `const double ${arrayPrefix}_T[${temperatures.length}] = { ${formattedTemperatures.join(", ")} };`;
|
|
|
+ const cArrayValue = `const double ${arrayPrefix}_${valueArrayName}[${values.length}] = { ${formattedValues.join(", ")} };`;
|
|
|
+
|
|
|
const textarea = document.getElementById("cArrayData");
|
|
|
textarea.value = `${cArrayTempSize}\n\n${cArrayTemp}\n\n${cArrayValue}`;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 监听插值方法选择变化
|
|
|
document.getElementById("interpolation").addEventListener("change", function() {
|
|
|
const polynomialRow = document.getElementById("polynomial-degree-row");
|