RT_Table_Import.html 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  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 colspan="2">
  139. <button onclick="generateOutputTable()">Generate Output Table</button>
  140. </td>
  141. </tr>
  142. </table>
  143. </div>
  144. </section>
  145. <section>
  146. <h2 class="section-title">Results</h2>
  147. <!-- 动态生成的表格 -->
  148. <div style="max-height: 400px; overflow-y: auto; margin-bottom: 20px;">
  149. <table id="rtTable">
  150. <thead>
  151. <tr>
  152. <th>Temperature (°C)</th>
  153. <th id="value-header">Resistance (Ω)</th>
  154. </tr>
  155. </thead>
  156. <tbody>
  157. <!-- 数据将通过JavaScript动态插入 -->
  158. </tbody>
  159. </table>
  160. </div>
  161. <!-- JSON 数据文本框 -->
  162. <h3>JSON Data</h3>
  163. <textarea id="jsonData" readonly></textarea>
  164. <!-- C语言数组文本框 -->
  165. <h3>C Language Arrays</h3>
  166. <textarea id="cArrayData" readonly></textarea>
  167. </section>
  168. </div>
  169. <script>
  170. // 全局变量
  171. let importedData = [];
  172. let processedData = [];
  173. // 页面加载时初始化
  174. document.addEventListener('DOMContentLoaded', function() {
  175. // 设置默认数据类型为RT
  176. document.getElementById('data-type').value = 'RT';
  177. // 初始化输出温度范围
  178. document.getElementById('output_T_min').value = 0;
  179. document.getElementById('output_T_max').value = 0;
  180. });
  181. // 检测分隔符
  182. function detectDelimiter(text) {
  183. const lines = text.trim().split(/\r?\n/);
  184. if (lines.length === 0) return 'tab';
  185. const firstLine = lines[0];
  186. // 检查各种分隔符的出现次数
  187. const delimiters = {
  188. 'tab': (firstLine.match(/\t/g) || []).length,
  189. 'comma': (firstLine.match(/,/g) || []).length,
  190. 'semicolon': (firstLine.match(/;/g) || []).length,
  191. 'space': (firstLine.match(/ /g) || []).length
  192. };
  193. // 找出出现次数最多的分隔符
  194. let maxCount = 0;
  195. let detectedDelimiter = 'tab';
  196. for (const [delimiter, count] of Object.entries(delimiters)) {
  197. if (count > maxCount) {
  198. maxCount = count;
  199. detectedDelimiter = delimiter;
  200. }
  201. }
  202. return detectedDelimiter;
  203. }
  204. // 处理导入的数据
  205. function processImportedData() {
  206. const importText = document.getElementById("importData").value.trim();
  207. if (!importText) {
  208. alert("Please paste data first!");
  209. return;
  210. }
  211. // 获取用户选择的分隔符或自动检测
  212. let delimiterChoice = document.getElementById("delimiter").value;
  213. let delimiterChar;
  214. if (delimiterChoice === 'auto') {
  215. delimiterChoice = detectDelimiter(importText);
  216. }
  217. // 设置实际的分隔符字符
  218. switch (delimiterChoice) {
  219. case 'tab': delimiterChar = '\t'; break;
  220. case 'comma': delimiterChar = ','; break;
  221. case 'space': delimiterChar = ' '; break;
  222. case 'semicolon': delimiterChar = ';'; break;
  223. default: delimiterChar = '\t';
  224. }
  225. // 分割文本为行
  226. const lines = importText.split(/\r?\n/);
  227. importedData = [];
  228. // 处理每一行
  229. for (const line of lines) {
  230. if (!line.trim()) continue; // 跳过空行
  231. // 分割行为列
  232. let columns;
  233. if (delimiterChoice === 'space') {
  234. // 对于空格分隔,处理多个连续空格的情况
  235. columns = line.trim().split(/\s+/);
  236. } else {
  237. columns = line.split(delimiterChar);
  238. }
  239. // 确保至少有两列
  240. if (columns.length >= 2) {
  241. const temp = parseFloat(columns[0]);
  242. const value = parseFloat(columns[1]);
  243. // 确保两个值都是有效的数字
  244. if (!isNaN(temp) && !isNaN(value)) {
  245. importedData.push({ temperature: temp, value: value });
  246. }
  247. }
  248. }
  249. // 按温度排序
  250. importedData.sort((a, b) => a.temperature - b.temperature);
  251. // 更新数据类型标题
  252. const dataType = document.getElementById("data-type").value;
  253. const valueHeader = document.getElementById("value-header");
  254. if (dataType === "RT") {
  255. valueHeader.textContent = "Resistance (Ω)";
  256. } else {
  257. valueHeader.textContent = "Voltage (mV)";
  258. }
  259. // 显示导入的数据
  260. displayTable(importedData);
  261. // 更新参数
  262. if (importedData.length > 0) {
  263. // 设置温度范围
  264. const minTemp = importedData[0].temperature;
  265. const maxTemp = importedData[importedData.length - 1].temperature;
  266. document.getElementById("param_T_min").value = minTemp;
  267. document.getElementById("param_T_max").value = maxTemp;
  268. // 同时更新输出温度范围
  269. document.getElementById("output_T_min").value = minTemp;
  270. document.getElementById("output_T_max").value = maxTemp;
  271. // 计算平均步长
  272. if (importedData.length > 1) {
  273. let totalSteps = 0;
  274. let stepCount = 0;
  275. for (let i = 1; i < importedData.length; i++) {
  276. const step = importedData[i].temperature - importedData[i-1].temperature;
  277. if (step > 0) {
  278. totalSteps += step;
  279. stepCount++;
  280. }
  281. }
  282. const avgStep = stepCount > 0 ? (totalSteps / stepCount) : 1;
  283. document.getElementById("param_step").value = avgStep.toFixed(2);
  284. document.getElementById("output-step").value = avgStep.toFixed(2);
  285. }
  286. }
  287. // 显示JSON数据
  288. displayJsonData(importedData);
  289. }
  290. // 线性插值函数
  291. function linearInterpolate(x, x0, y0, x1, y1) {
  292. return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
  293. }
  294. // 多项式拟合函数
  295. function polynomialFit(data, degree) {
  296. // 简单实现,实际应用中可能需要更复杂的算法
  297. // 这里使用最小二乘法拟合多项式
  298. const n = data.length;
  299. const x = data.map(d => d.temperature);
  300. const y = data.map(d => d.value);
  301. // 创建矩阵
  302. const X = [];
  303. for (let i = 0; i < n; i++) {
  304. X[i] = [];
  305. for (let j = 0; j <= degree; j++) {
  306. X[i][j] = Math.pow(x[i], j);
  307. }
  308. }
  309. // 转置矩阵
  310. const Xt = [];
  311. for (let j = 0; j <= degree; j++) {
  312. Xt[j] = [];
  313. for (let i = 0; i < n; i++) {
  314. Xt[j][i] = X[i][j];
  315. }
  316. }
  317. // 计算 Xt * X
  318. const XtX = [];
  319. for (let i = 0; i <= degree; i++) {
  320. XtX[i] = [];
  321. for (let j = 0; j <= degree; j++) {
  322. XtX[i][j] = 0;
  323. for (let k = 0; k < n; k++) {
  324. XtX[i][j] += Xt[i][k] * X[k][j];
  325. }
  326. }
  327. }
  328. // 计算 Xt * y
  329. const Xty = [];
  330. for (let i = 0; i <= degree; i++) {
  331. Xty[i] = 0;
  332. for (let j = 0; j < n; j++) {
  333. Xty[i] += Xt[i][j] * y[j];
  334. }
  335. }
  336. // 解线性方程组 XtX * coeffs = Xty
  337. // 使用高斯消元法
  338. const coeffs = solveLinearSystem(XtX, Xty);
  339. // 返回多项式函数
  340. return function(x) {
  341. let result = 0;
  342. for (let i = 0; i <= degree; i++) {
  343. result += coeffs[i] * Math.pow(x, i);
  344. }
  345. return result;
  346. };
  347. }
  348. // 解线性方程组
  349. function solveLinearSystem(A, b) {
  350. const n = b.length;
  351. const x = new Array(n).fill(0);
  352. // 高斯消元法
  353. for (let i = 0; i < n; i++) {
  354. // 寻找主元
  355. let maxEl = Math.abs(A[i][i]);
  356. let maxRow = i;
  357. for (let k = i + 1; k < n; k++) {
  358. if (Math.abs(A[k][i]) > maxEl) {
  359. maxEl = Math.abs(A[k][i]);
  360. maxRow = k;
  361. }
  362. }
  363. // 交换行
  364. if (maxRow !== i) {
  365. [A[i], A[maxRow]] = [A[maxRow], A[i]];
  366. [b[i], b[maxRow]] = [b[maxRow], b[i]];
  367. }
  368. // 消元
  369. for (let k = i + 1; k < n; k++) {
  370. const c = -A[k][i] / A[i][i];
  371. for (let j = i; j < n; j++) {
  372. if (i === j) {
  373. A[k][j] = 0;
  374. } else {
  375. A[k][j] += c * A[i][j];
  376. }
  377. }
  378. b[k] += c * b[i];
  379. }
  380. }
  381. // 回代
  382. for (let i = n - 1; i >= 0; i--) {
  383. x[i] = b[i] / A[i][i];
  384. for (let k = i - 1; k >= 0; k--) {
  385. b[k] -= A[k][i] * x[i];
  386. }
  387. }
  388. return x;
  389. }
  390. // 生成输出表格
  391. function generateOutputTable() {
  392. if (importedData.length === 0) {
  393. alert("Please import and process data first!");
  394. return;
  395. }
  396. // 使用用户设置的输出温度范围,如果未设置则使用数据的温度范围
  397. let Tmin = parseFloat(document.getElementById("output_T_min").value);
  398. let Tmax = parseFloat(document.getElementById("output_T_max").value);
  399. const dataTmin = parseFloat(document.getElementById("param_T_min").value);
  400. const dataTmax = parseFloat(document.getElementById("param_T_max").value);
  401. // 验证输出温度范围
  402. if (isNaN(Tmin) || Tmin < dataTmin) {
  403. Tmin = dataTmin;
  404. document.getElementById("output_T_min").value = dataTmin;
  405. }
  406. if (isNaN(Tmax) || Tmax > dataTmax) {
  407. Tmax = dataTmax;
  408. document.getElementById("output_T_max").value = dataTmax;
  409. }
  410. if (Tmin > Tmax) {
  411. Tmin = dataTmin;
  412. Tmax = dataTmax;
  413. document.getElementById("output_T_min").value = dataTmin;
  414. document.getElementById("output_T_max").value = dataTmax;
  415. alert("Output temperature min cannot be greater than max. Using data range instead.");
  416. }
  417. const step = parseFloat(document.getElementById("output-step").value);
  418. const interpolationMethod = document.getElementById("interpolation").value;
  419. processedData = [];
  420. if (interpolationMethod === "linear") {
  421. // 线性插值
  422. for (let T = Tmin; T <= Tmax; T += step) {
  423. // 找到T所在的区间
  424. let value;
  425. // 如果T小于第一个点或大于最后一个点,使用外推
  426. if (T <= importedData[0].temperature) {
  427. value = importedData[0].value;
  428. } else if (T >= importedData[importedData.length - 1].temperature) {
  429. value = importedData[importedData.length - 1].value;
  430. } else {
  431. // 找到T所在的区间
  432. for (let i = 0; i < importedData.length - 1; i++) {
  433. if (T >= importedData[i].temperature && T <= importedData[i + 1].temperature) {
  434. value = linearInterpolate(
  435. T,
  436. importedData[i].temperature,
  437. importedData[i].value,
  438. importedData[i + 1].temperature,
  439. importedData[i + 1].value
  440. );
  441. break;
  442. }
  443. }
  444. }
  445. processedData.push({ temperature: T, value: value });
  446. }
  447. } else if (interpolationMethod === "polynomial") {
  448. // 多项式拟合
  449. const degree = parseInt(document.getElementById("polynomial-degree").value);
  450. const polyFunc = polynomialFit(importedData, degree);
  451. for (let T = Tmin; T <= Tmax; T += step) {
  452. const value = polyFunc(T);
  453. processedData.push({ temperature: T, value: value });
  454. }
  455. }
  456. // 显示处理后的数据
  457. displayTable(processedData);
  458. displayJsonData(processedData);
  459. displayCArrayData(processedData);
  460. }
  461. // 显示表格数据
  462. function displayTable(data) {
  463. const tbody = document.getElementById("rtTable").querySelector("tbody");
  464. tbody.innerHTML = ""; // 清空表格内容
  465. const dataType = document.getElementById("data-type").value;
  466. data.forEach(row => {
  467. const tr = document.createElement("tr");
  468. if (dataType === "RT") {
  469. tr.innerHTML = `<td>${row.temperature}</td><td>${row.value}</td>`;
  470. } else {
  471. tr.innerHTML = `<td>${row.temperature}</td><td>${row.value}</td>`;
  472. }
  473. tbody.appendChild(tr);
  474. });
  475. }
  476. // 显示 JSON 数据到文本框
  477. function displayJsonData(data) {
  478. const jsonData = JSON.stringify(data, null, 4); // 格式化JSON
  479. const textarea = document.getElementById("jsonData");
  480. textarea.value = jsonData;
  481. }
  482. // 显示 C 语言数组到文本框
  483. function displayCArrayData(data) {
  484. if (data.length === 0) return;
  485. const temperatures = data.map(row => row.temperature); // 提取温度值
  486. const values = data.map(row => row.value); // 提取值
  487. const dataType = document.getElementById("data-type").value;
  488. const namePrefix = document.getElementById("param_name").value;
  489. const Tmin = data[0].temperature;
  490. const Tmax = data[data.length - 1].temperature;
  491. const step = parseFloat(document.getElementById("output-step").value);
  492. const prefix = (Tmin < 0 ? 'N' : 'P') + Math.abs(Tmin).toString() + 'to' + (Tmax < 0 ? 'N' : 'P') + Math.abs(Tmax).toString() + '_' + step.toString();
  493. let arrayPrefix, valueArrayName;
  494. if (dataType === "RT") {
  495. arrayPrefix = `RT_table_${namePrefix}_${prefix}`;
  496. valueArrayName = "R";
  497. } else {
  498. arrayPrefix = `VT_table_${namePrefix}_${prefix}`;
  499. valueArrayName = "V";
  500. }
  501. const cArrayTempSize = `const uint16_t ${arrayPrefix}_size = ${temperatures.length};`;
  502. const cArrayTemp = `const double ${arrayPrefix}_T[${temperatures.length}] = { ${temperatures.join(", ")} };`;
  503. const cArrayValue = `const double ${arrayPrefix}_${valueArrayName}[${values.length}] = { ${values.join(", ")} };`;
  504. const textarea = document.getElementById("cArrayData");
  505. textarea.value = `${cArrayTempSize}\n\n${cArrayTemp}\n\n${cArrayValue}`;
  506. }
  507. // 监听插值方法选择变化
  508. document.getElementById("interpolation").addEventListener("change", function() {
  509. const polynomialRow = document.getElementById("polynomial-degree-row");
  510. if (this.value === "polynomial") {
  511. polynomialRow.style.display = "";
  512. } else {
  513. polynomialRow.style.display = "none";
  514. }
  515. });
  516. </script>
  517. </body>
  518. </html>