RT_Table_Import.html 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>RT Table Import</title>
  7. <link rel="stylesheet" href="./styles.css">
  8. </head>
  9. <body>
  10. <header>
  11. <h1>RT Table Generator</h1>
  12. <ul class="nav-tabs">
  13. <li><a href="./RT_Table_NTC.html">NTC</a></li>
  14. <li><a href="./RT_Table_PT100.html">PT100</a></li>
  15. <li><a href="./RT_Table_PT1000.html">PT1000</a></li>
  16. <li><a href="./VT_Table_TJ.html">Thermocouple</a></li>
  17. <li><a href="./RT_Table_Import.html" class="active">Import Data</a></li>
  18. </ul>
  19. </header>
  20. <div class="container">
  21. <section>
  22. <h2 class="section-title">RT/VT Data Import Tool</h2>
  23. <div class="formula">
  24. <p>This tool allows you to import and analyze temperature-resistance (RT) or temperature-voltage (VT) data from various sources.</p>
  25. <p>Paste your data in the text area below. The tool supports the following formats:</p>
  26. <ul style="text-align: left; margin: 10px auto; display: inline-block;">
  27. <li>CSV format (comma, tab, or space separated)</li>
  28. <li>Excel data (copied directly from spreadsheet)</li>
  29. <li>Plain text with temperature and resistance/voltage values</li>
  30. </ul>
  31. <p>The first column should be temperature values, and the second column should be resistance or voltage values.</p>
  32. </div>
  33. </section>
  34. <section>
  35. <h2 class="section-title">Import Data</h2>
  36. <!-- 数据导入区域 -->
  37. <div>
  38. <textarea id="importData" placeholder="Paste your temperature and resistance/voltage data here..."></textarea>
  39. <div style="margin: 10px 0;">
  40. <label for="data-type">Data Type:</label>
  41. <select id="data-type" class="tableInput" style="width: auto; margin-right: 20px;">
  42. <option value="RT">RT Data (Temperature-Resistance)</option>
  43. <option value="VT">VT Data (Temperature-Voltage)</option>
  44. </select>
  45. <label for="delimiter">Delimiter:</label>
  46. <select id="delimiter" class="tableInput" style="width: auto;">
  47. <option value="auto">Auto Detect</option>
  48. <option value="tab">Tab</option>
  49. <option value="comma">Comma</option>
  50. <option value="space">Space</option>
  51. <option value="semicolon">Semicolon</option>
  52. </select>
  53. </div>
  54. <button onclick="processImportedData()">Process Data</button>
  55. </div>
  56. </section>
  57. <section>
  58. <h2 class="section-title">Parameters</h2>
  59. <!-- 参数设置 -->
  60. <div>
  61. <table class="params-table">
  62. <tr>
  63. <td>
  64. <label for="param_name">Name Prefix:</label>
  65. </td>
  66. <td>
  67. <input type="text" id="param_name" name="param_name" value="Custom" class="tableInput">
  68. </td>
  69. </tr>
  70. <tr>
  71. <td>
  72. <label for="param_T_min">Data Temperature Min (°C):</label>
  73. </td>
  74. <td>
  75. <input type="number" id="param_T_min" name="param_T_min" value="0" class="tableInput" readonly>
  76. </td>
  77. </tr>
  78. <tr>
  79. <td>
  80. <label for="param_T_max">Data Temperature Max (°C):</label>
  81. </td>
  82. <td>
  83. <input type="number" id="param_T_max" name="param_T_max" value="0" class="tableInput" readonly>
  84. </td>
  85. </tr>
  86. <tr>
  87. <td>
  88. <label for="param_step">Data Temperature Step (°C):</label>
  89. </td>
  90. <td>
  91. <input type="number" id="param_step" name="param_step" value="0" class="tableInput" readonly>
  92. </td>
  93. </tr>
  94. <tr>
  95. <td>
  96. <label for="output_T_min">Output Temperature Min (°C):</label>
  97. </td>
  98. <td>
  99. <input type="number" id="output_T_min" name="output_T_min" value="0" class="tableInput">
  100. </td>
  101. </tr>
  102. <tr>
  103. <td>
  104. <label for="output_T_max">Output Temperature Max (°C):</label>
  105. </td>
  106. <td>
  107. <input type="number" id="output_T_max" name="output_T_max" value="0" class="tableInput">
  108. </td>
  109. </tr>
  110. <tr>
  111. <td>
  112. <label for="interpolation">Interpolation Method:</label>
  113. </td>
  114. <td>
  115. <select id="interpolation" class="tableInput">
  116. <option value="linear">Linear</option>
  117. <option value="polynomial">Polynomial</option>
  118. </select>
  119. </td>
  120. </tr>
  121. <tr id="polynomial-degree-row" style="display: none;">
  122. <td>
  123. <label for="polynomial-degree">Polynomial Degree:</label>
  124. </td>
  125. <td>
  126. <input type="number" id="polynomial-degree" name="polynomial-degree" value="3" min="2" max="10" class="tableInput">
  127. </td>
  128. </tr>
  129. <tr>
  130. <td>
  131. <label for="output-step">Output Step (°C):</label>
  132. </td>
  133. <td>
  134. <input type="number" id="output-step" name="output-step" value="1" min="0.1" step="0.1" class="tableInput">
  135. </td>
  136. </tr>
  137. <tr>
  138. <td>
  139. <label for="result-coefficient">Result Coefficient:</label>
  140. </td>
  141. <td>
  142. <input type="number" id="result-coefficient" name="result-coefficient" value="1" min="0.001" step="0.001" class="tableInput">
  143. </td>
  144. </tr>
  145. <tr>
  146. <td colspan="2">
  147. <button onclick="generateOutputTable()">Generate Output Table</button>
  148. </td>
  149. </tr>
  150. </table>
  151. </div>
  152. </section>
  153. <section>
  154. <h2 class="section-title">Results</h2>
  155. <!-- 动态生成的表格 -->
  156. <div style="max-height: 400px; overflow-y: auto; margin-bottom: 20px;">
  157. <table id="rtTable">
  158. <thead>
  159. <tr>
  160. <th>Temperature (°C)</th>
  161. <th id="value-header">Resistance (Ω)</th>
  162. </tr>
  163. </thead>
  164. <tbody>
  165. <!-- 数据将通过JavaScript动态插入 -->
  166. </tbody>
  167. </table>
  168. </div>
  169. <!-- JSON 数据文本框 -->
  170. <h3>JSON Data</h3>
  171. <textarea id="jsonData" readonly></textarea>
  172. <!-- C语言数组文本框 -->
  173. <h3>C Language Arrays</h3>
  174. <textarea id="cArrayData" readonly></textarea>
  175. </section>
  176. </div>
  177. <script>
  178. // 全局变量
  179. let importedData = [];
  180. let processedData = [];
  181. // 页面加载时初始化
  182. document.addEventListener('DOMContentLoaded', function() {
  183. // 设置默认数据类型为RT
  184. document.getElementById('data-type').value = 'RT';
  185. // 初始化输出温度范围
  186. document.getElementById('output_T_min').value = 0;
  187. document.getElementById('output_T_max').value = 0;
  188. });
  189. // 检测分隔符
  190. function detectDelimiter(text) {
  191. const lines = text.trim().split(/\r?\n/);
  192. if (lines.length === 0) return 'tab';
  193. const firstLine = lines[0];
  194. // 检查各种分隔符的出现次数
  195. const delimiters = {
  196. 'tab': (firstLine.match(/\t/g) || []).length,
  197. 'comma': (firstLine.match(/,/g) || []).length,
  198. 'semicolon': (firstLine.match(/;/g) || []).length,
  199. 'space': (firstLine.match(/ /g) || []).length
  200. };
  201. // 找出出现次数最多的分隔符
  202. let maxCount = 0;
  203. let detectedDelimiter = 'tab';
  204. for (const [delimiter, count] of Object.entries(delimiters)) {
  205. if (count > maxCount) {
  206. maxCount = count;
  207. detectedDelimiter = delimiter;
  208. }
  209. }
  210. return detectedDelimiter;
  211. }
  212. // 处理导入的数据
  213. function processImportedData() {
  214. const importText = document.getElementById("importData").value.trim();
  215. if (!importText) {
  216. alert("Please paste data first!");
  217. return;
  218. }
  219. // 获取用户选择的分隔符或自动检测
  220. let delimiterChoice = document.getElementById("delimiter").value;
  221. let delimiterChar;
  222. if (delimiterChoice === 'auto') {
  223. delimiterChoice = detectDelimiter(importText);
  224. }
  225. // 设置实际的分隔符字符
  226. switch (delimiterChoice) {
  227. case 'tab': delimiterChar = '\t'; break;
  228. case 'comma': delimiterChar = ','; break;
  229. case 'space': delimiterChar = ' '; break;
  230. case 'semicolon': delimiterChar = ';'; break;
  231. default: delimiterChar = '\t';
  232. }
  233. // 分割文本为行
  234. const lines = importText.split(/\r?\n/);
  235. importedData = [];
  236. // 处理每一行
  237. for (const line of lines) {
  238. if (!line.trim()) continue; // 跳过空行
  239. // 分割行为列
  240. let columns;
  241. if (delimiterChoice === 'space') {
  242. // 对于空格分隔,处理多个连续空格的情况
  243. columns = line.trim().split(/\s+/);
  244. } else {
  245. columns = line.split(delimiterChar);
  246. }
  247. // 确保至少有两列
  248. if (columns.length >= 2) {
  249. const temp = parseFloat(columns[0]);
  250. const value = parseFloat(columns[1]);
  251. // 确保两个值都是有效的数字
  252. if (!isNaN(temp) && !isNaN(value)) {
  253. importedData.push({ temperature: temp, value: value });
  254. }
  255. }
  256. }
  257. // 按温度排序
  258. importedData.sort((a, b) => a.temperature - b.temperature);
  259. // 更新数据类型标题
  260. const dataType = document.getElementById("data-type").value;
  261. const valueHeader = document.getElementById("value-header");
  262. if (dataType === "RT") {
  263. valueHeader.textContent = "Resistance (Ω)";
  264. } else {
  265. valueHeader.textContent = "Voltage (mV)";
  266. }
  267. // 显示导入的数据
  268. displayTable(importedData);
  269. // 更新参数
  270. if (importedData.length > 0) {
  271. // 设置温度范围
  272. const minTemp = importedData[0].temperature;
  273. const maxTemp = importedData[importedData.length - 1].temperature;
  274. document.getElementById("param_T_min").value = minTemp;
  275. document.getElementById("param_T_max").value = maxTemp;
  276. // 同时更新输出温度范围
  277. document.getElementById("output_T_min").value = minTemp;
  278. document.getElementById("output_T_max").value = maxTemp;
  279. // 计算平均步长
  280. if (importedData.length > 1) {
  281. let totalSteps = 0;
  282. let stepCount = 0;
  283. for (let i = 1; i < importedData.length; i++) {
  284. const step = importedData[i].temperature - importedData[i-1].temperature;
  285. if (step > 0) {
  286. totalSteps += step;
  287. stepCount++;
  288. }
  289. }
  290. const avgStep = stepCount > 0 ? (totalSteps / stepCount) : 1;
  291. document.getElementById("param_step").value = avgStep.toFixed(2);
  292. document.getElementById("output-step").value = avgStep.toFixed(2);
  293. }
  294. }
  295. // 显示JSON数据
  296. displayJsonData(importedData);
  297. }
  298. // 线性插值函数
  299. function linearInterpolate(x, x0, y0, x1, y1) {
  300. return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
  301. }
  302. // 多项式拟合函数
  303. function polynomialFit(data, degree) {
  304. // 简单实现,实际应用中可能需要更复杂的算法
  305. // 这里使用最小二乘法拟合多项式
  306. const n = data.length;
  307. const x = data.map(d => d.temperature);
  308. const y = data.map(d => d.value);
  309. // 创建矩阵
  310. const X = [];
  311. for (let i = 0; i < n; i++) {
  312. X[i] = [];
  313. for (let j = 0; j <= degree; j++) {
  314. X[i][j] = Math.pow(x[i], j);
  315. }
  316. }
  317. // 转置矩阵
  318. const Xt = [];
  319. for (let j = 0; j <= degree; j++) {
  320. Xt[j] = [];
  321. for (let i = 0; i < n; i++) {
  322. Xt[j][i] = X[i][j];
  323. }
  324. }
  325. // 计算 Xt * X
  326. const XtX = [];
  327. for (let i = 0; i <= degree; i++) {
  328. XtX[i] = [];
  329. for (let j = 0; j <= degree; j++) {
  330. XtX[i][j] = 0;
  331. for (let k = 0; k < n; k++) {
  332. XtX[i][j] += Xt[i][k] * X[k][j];
  333. }
  334. }
  335. }
  336. // 计算 Xt * y
  337. const Xty = [];
  338. for (let i = 0; i <= degree; i++) {
  339. Xty[i] = 0;
  340. for (let j = 0; j < n; j++) {
  341. Xty[i] += Xt[i][j] * y[j];
  342. }
  343. }
  344. // 解线性方程组 XtX * coeffs = Xty
  345. // 使用高斯消元法
  346. const coeffs = solveLinearSystem(XtX, Xty);
  347. // 返回多项式函数
  348. return function(x) {
  349. let result = 0;
  350. for (let i = 0; i <= degree; i++) {
  351. result += coeffs[i] * Math.pow(x, i);
  352. }
  353. return result;
  354. };
  355. }
  356. // 解线性方程组
  357. function solveLinearSystem(A, b) {
  358. const n = b.length;
  359. const x = new Array(n).fill(0);
  360. // 高斯消元法
  361. for (let i = 0; i < n; i++) {
  362. // 寻找主元
  363. let maxEl = Math.abs(A[i][i]);
  364. let maxRow = i;
  365. for (let k = i + 1; k < n; k++) {
  366. if (Math.abs(A[k][i]) > maxEl) {
  367. maxEl = Math.abs(A[k][i]);
  368. maxRow = k;
  369. }
  370. }
  371. // 交换行
  372. if (maxRow !== i) {
  373. [A[i], A[maxRow]] = [A[maxRow], A[i]];
  374. [b[i], b[maxRow]] = [b[maxRow], b[i]];
  375. }
  376. // 消元
  377. for (let k = i + 1; k < n; k++) {
  378. const c = -A[k][i] / A[i][i];
  379. for (let j = i; j < n; j++) {
  380. if (i === j) {
  381. A[k][j] = 0;
  382. } else {
  383. A[k][j] += c * A[i][j];
  384. }
  385. }
  386. b[k] += c * b[i];
  387. }
  388. }
  389. // 回代
  390. for (let i = n - 1; i >= 0; i--) {
  391. x[i] = b[i] / A[i][i];
  392. for (let k = i - 1; k >= 0; k--) {
  393. b[k] -= A[k][i] * x[i];
  394. }
  395. }
  396. return x;
  397. }
  398. // 生成输出表格
  399. function generateOutputTable() {
  400. if (importedData.length === 0) {
  401. alert("Please import and process data first!");
  402. return;
  403. }
  404. // 使用用户设置的输出温度范围,如果未设置则使用数据的温度范围
  405. let Tmin = parseFloat(document.getElementById("output_T_min").value);
  406. let Tmax = parseFloat(document.getElementById("output_T_max").value);
  407. const dataTmin = parseFloat(document.getElementById("param_T_min").value);
  408. const dataTmax = parseFloat(document.getElementById("param_T_max").value);
  409. // 验证输出温度范围
  410. if (isNaN(Tmin) || Tmin < dataTmin) {
  411. Tmin = dataTmin;
  412. document.getElementById("output_T_min").value = dataTmin;
  413. }
  414. if (isNaN(Tmax) || Tmax > dataTmax) {
  415. Tmax = dataTmax;
  416. document.getElementById("output_T_max").value = dataTmax;
  417. }
  418. if (Tmin > Tmax) {
  419. Tmin = dataTmin;
  420. Tmax = dataTmax;
  421. document.getElementById("output_T_min").value = dataTmin;
  422. document.getElementById("output_T_max").value = dataTmax;
  423. alert("Output temperature min cannot be greater than max. Using data range instead.");
  424. }
  425. const step = parseFloat(document.getElementById("output-step").value);
  426. const resultCoefficient = parseFloat(document.getElementById("result-coefficient").value);
  427. const interpolationMethod = document.getElementById("interpolation").value;
  428. processedData = [];
  429. if (interpolationMethod === "linear") {
  430. // 线性插值
  431. for (let T = Tmin; T <= Tmax; T += step) {
  432. // 找到T所在的区间
  433. let value;
  434. // 如果T小于第一个点或大于最后一个点,使用外推
  435. if (T <= importedData[0].temperature) {
  436. value = importedData[0].value;
  437. } else if (T >= importedData[importedData.length - 1].temperature) {
  438. value = importedData[importedData.length - 1].value;
  439. } else {
  440. // 找到T所在的区间
  441. for (let i = 0; i < importedData.length - 1; i++) {
  442. if (T >= importedData[i].temperature && T <= importedData[i + 1].temperature) {
  443. value = linearInterpolate(
  444. T,
  445. importedData[i].temperature,
  446. importedData[i].value,
  447. importedData[i + 1].temperature,
  448. importedData[i + 1].value
  449. );
  450. break;
  451. }
  452. }
  453. }
  454. // 应用系数并限制小数位数为6位,并去除末尾的0
  455. const roundedT = Number(T.toFixed(6));
  456. const roundedValue = Number((value * resultCoefficient).toFixed(6));
  457. processedData.push({ temperature: roundedT, value: roundedValue });
  458. }
  459. } else if (interpolationMethod === "polynomial") {
  460. // 多项式拟合
  461. const degree = parseInt(document.getElementById("polynomial-degree").value);
  462. const polyFunc = polynomialFit(importedData, degree);
  463. for (let T = Tmin; T <= Tmax; T += step) {
  464. // 应用系数并限制小数位数为6位,并去除末尾的0
  465. const roundedT = Number(T.toFixed(6));
  466. const value = Number((polyFunc(T) * resultCoefficient).toFixed(6));
  467. processedData.push({ temperature: roundedT, value: value });
  468. }
  469. }
  470. // 显示处理后的数据
  471. displayTable(processedData);
  472. displayJsonData(processedData);
  473. displayCArrayData(processedData);
  474. }
  475. // 显示表格数据
  476. function displayTable(data) {
  477. const tbody = document.getElementById("rtTable").querySelector("tbody");
  478. tbody.innerHTML = ""; // 清空表格内容
  479. const dataType = document.getElementById("data-type").value;
  480. data.forEach(row => {
  481. const tr = document.createElement("tr");
  482. // 限制小数位数为6位,并去除末尾的0
  483. const displayValue = Number(row.value.toFixed(6));
  484. const tempValue = Number(row.temperature.toFixed(6));
  485. if (dataType === "RT") {
  486. tr.innerHTML = `<td>${tempValue}</td><td>${displayValue}</td>`;
  487. } else {
  488. tr.innerHTML = `<td>${tempValue}</td><td>${displayValue}</td>`;
  489. }
  490. tbody.appendChild(tr);
  491. });
  492. }
  493. // 显示 JSON 数据到文本框
  494. function displayJsonData(data) {
  495. // 应用小数位数限制,并去除末尾的0
  496. const dataWithLimitedDecimals = data.map(item => ({
  497. temperature: Number(item.temperature.toFixed(6)), // 限制温度小数位数为6位并去除末尾的0
  498. value: Number(item.value.toFixed(6)) // 限制值小数位数为6位并去除末尾的0
  499. }));
  500. const jsonData = JSON.stringify(dataWithLimitedDecimals, null, 4); // 格式化JSON
  501. const textarea = document.getElementById("jsonData");
  502. textarea.value = jsonData;
  503. }
  504. // 显示 C 语言数组到文本框
  505. function displayCArrayData(data) {
  506. if (data.length === 0) return;
  507. // 提取温度值和值,并限制小数位数为6位,去除末尾的0
  508. const temperatures = data.map(row => Number(row.temperature.toFixed(6))); // 限制温度小数位数为6位并去除末尾的0
  509. const values = data.map(row => Number(row.value.toFixed(6))); // 限制值小数位数为6位并去除末尾的0
  510. const dataType = document.getElementById("data-type").value;
  511. const namePrefix = document.getElementById("param_name").value;
  512. const Tmin = Number(data[0].temperature.toFixed(6));
  513. const Tmax = Number(data[data.length - 1].temperature.toFixed(6));
  514. const step = Number(parseFloat(document.getElementById("output-step").value).toFixed(6));
  515. const prefix = (Tmin < 0 ? 'N' : 'P') + Math.abs(Tmin).toString() + 'to' + (Tmax < 0 ? 'N' : 'P') + Math.abs(Tmax).toString() + '_' + step.toString();
  516. let arrayPrefix, valueArrayName;
  517. if (dataType === "RT") {
  518. arrayPrefix = `RT_table_${namePrefix}_${prefix}`;
  519. valueArrayName = "R";
  520. } else {
  521. arrayPrefix = `VT_table_${namePrefix}_${prefix}`;
  522. valueArrayName = "V";
  523. }
  524. // 格式化数组元素,确保使用6位小数,并去除末尾的0
  525. const formattedTemperatures = temperatures.map(t => Number(t.toFixed(6)).toString());
  526. const formattedValues = values.map(v => Number(v.toFixed(6)).toString());
  527. const cArrayTempSize = `const uint16_t ${arrayPrefix}_size = ${temperatures.length};`;
  528. const cArrayTemp = `const double ${arrayPrefix}_T[${temperatures.length}] = { ${formattedTemperatures.join(", ")} };`;
  529. const cArrayValue = `const double ${arrayPrefix}_${valueArrayName}[${values.length}] = { ${formattedValues.join(", ")} };`;
  530. const textarea = document.getElementById("cArrayData");
  531. textarea.value = `${cArrayTempSize}\n\n${cArrayTemp}\n\n${cArrayValue}`;
  532. }
  533. // 监听插值方法选择变化
  534. document.getElementById("interpolation").addEventListener("change", function() {
  535. const polynomialRow = document.getElementById("polynomial-degree-row");
  536. if (this.value === "polynomial") {
  537. polynomialRow.style.display = "";
  538. } else {
  539. polynomialRow.style.display = "none";
  540. }
  541. });
  542. </script>
  543. </body>
  544. </html>