// error.js - 示值误差测试相关功能 // 图表相关变量 let errorCtx = null; let errorChartInstance = null; // 初始化示值误差图表 function initErrorChart() { errorCtx = document.getElementById('errorChart').getContext('2d'); } // 在页面加载完成后初始化图表 document.addEventListener('DOMContentLoaded', function() { initErrorChart(); }); // 示值误差测试函数 function updateErrorPoint(input) { const pointContainer = input.closest('.error-point-container'); const originalValue = input.getAttribute('data-original'); const newValue = input.value; // 如果测试点值发生变化,更新所有相关元素的data-point属性 if (originalValue !== newValue) { input.setAttribute('data-original', newValue); pointContainer.querySelectorAll('.error-input').forEach(input => { input.setAttribute('data-point', newValue); }); pointContainer.querySelector('.error-avg').setAttribute('data-point', newValue); pointContainer.querySelector('.error-value').setAttribute('data-point', newValue); } calculateError(); } function createErrorPointContainer(pointValue = '') { const container = document.createElement('div'); container.className = 'error-point-container'; container.style.marginBottom = '1rem'; container.style.padding = '1rem'; container.style.border = '1px solid #eee'; container.style.borderRadius = '4px'; // 测试点输入 const pointRow = document.createElement('div'); pointRow.style.display = 'flex'; pointRow.style.alignItems = 'center'; pointRow.style.marginBottom = '0.5rem'; const pointLabel = document.createElement('label'); pointLabel.textContent = '测试点:'; pointLabel.style.marginRight = '0.5rem'; pointLabel.style.fontWeight = 'bold'; const pointInput = document.createElement('input'); pointInput.type = 'number'; pointInput.className = 'error-point-input'; pointInput.placeholder = '输入测试点值'; pointInput.value = pointValue; pointInput.setAttribute('data-original', pointValue); pointInput.style.flex = '1'; pointInput.style.marginRight = '0.5rem'; pointInput.oninput = function () { updateErrorPoint(this); // 保存示值误差数据到localStorage saveErrorData(); }; const deleteBtn = document.createElement('span'); deleteBtn.className = 'delete-btn'; deleteBtn.innerHTML = '×'; deleteBtn.onclick = function () { container.parentNode.removeChild(container); // 如果删除后没有测试点,添加一个新的空白测试点 if (document.querySelectorAll('.error-point-container').length === 0) { addErrorPoint(); } calculateError(); // 保存误差数据到localStorage saveErrorData(); }; pointRow.appendChild(pointLabel); pointRow.appendChild(pointInput); pointRow.appendChild(deleteBtn); // 输入值部分 const inputsContainer = document.createElement('div'); inputsContainer.className = 'error-inputs'; inputsContainer.style.display = 'flex'; inputsContainer.style.flexWrap = 'wrap'; inputsContainer.style.gap = '0.5rem'; inputsContainer.style.marginBottom = '0.5rem'; // 添加3个默认输入框 for (let i = 0; i < 3; i++) { const input = document.createElement('input'); input.type = 'number'; input.className = 'error-input'; input.placeholder = `输入值 ${i + 1}`; input.setAttribute('data-point', pointValue); input.style.flex = '1'; input.style.minWidth = '100px'; input.oninput = function () { calculateError(); // 保存误差数据到localStorage saveErrorData(); }; inputsContainer.appendChild(input); } // 结果显示部分 const resultsRow = document.createElement('div'); resultsRow.style.display = 'flex'; resultsRow.style.gap = '1rem'; const avgContainer = document.createElement('div'); avgContainer.style.flex = '1'; const avgLabel = document.createElement('span'); avgLabel.textContent = '平均值:'; avgLabel.style.fontWeight = 'bold'; const avgSpan = document.createElement('span'); avgSpan.className = 'error-avg'; avgSpan.setAttribute('data-point', pointValue); avgSpan.textContent = '-'; avgContainer.appendChild(avgLabel); avgContainer.appendChild(avgSpan); const errorContainer = document.createElement('div'); errorContainer.style.flex = '1'; const errorLabel = document.createElement('span'); errorLabel.textContent = '示值误差:'; errorLabel.style.fontWeight = 'bold'; const errorSpan = document.createElement('span'); errorSpan.className = 'error-value'; errorSpan.setAttribute('data-point', pointValue); errorSpan.textContent = '-'; errorContainer.appendChild(errorLabel); errorContainer.appendChild(errorSpan); resultsRow.appendChild(avgContainer); resultsRow.appendChild(errorContainer); container.appendChild(pointRow); container.appendChild(inputsContainer); container.appendChild(resultsRow); return container; } function addErrorPoint() { const container = document.getElementById('error-inputs'); const pointContainer = createErrorPointContainer(); container.appendChild(pointContainer); updateErrorPoints(); } function calculateError() { const pointContainers = document.querySelectorAll('.error-point-container'); const errorData = []; pointContainers.forEach(container => { const pointInput = container.querySelector('.error-point-input'); const inputs = container.querySelectorAll('.error-input'); const avgSpan = container.querySelector('.error-avg'); const errorSpan = container.querySelector('.error-value'); const pointValue = parseFloat(pointInput.value); const values = Array.from(inputs) .map(input => parseFloat(input.value)) .filter(value => !isNaN(value)); if (!isNaN(pointValue) && values.length > 0) { // 计算平均值 const avg = values.reduce((sum, val) => sum + val, 0) / values.length; // 计算示值误差 const error = avg - pointValue; // 更新显示 avgSpan.textContent = avg.toFixed(6); errorSpan.textContent = error.toFixed(6); // 收集数据用于图表和不确定度计算 errorData.push({ point: pointValue, avg: avg, error: error }); } else { avgSpan.textContent = '-'; errorSpan.textContent = '-'; } }); // 更新图表 updateErrorChart(errorData); // 计算不确定度 calculateUncertainty(errorData); return errorData; } function calculateUncertainty(errorData) { if (errorData.length === 0) { // 清空不确定度显示 document.getElementById('uncertainty-a').textContent = '-'; document.getElementById('uncertainty-b').textContent = '-'; document.getElementById('uncertainty-c').textContent = '-'; document.getElementById('uncertainty-u').textContent = '-'; return; } // 计算A类不确定度(重复测量的标准偏差) let sumSquaredDeviations = 0; let totalMeasurements = 0; document.querySelectorAll('.error-point-container').forEach(container => { const inputs = container.querySelectorAll('.error-input'); const values = Array.from(inputs) .map(input => parseFloat(input.value)) .filter(value => !isNaN(value)); if (values.length > 0) { const avg = values.reduce((sum, val) => sum + val, 0) / values.length; values.forEach(value => { sumSquaredDeviations += Math.pow(value - avg, 2); }); totalMeasurements += values.length; } }); // 计算A类不确定度 const uA = totalMeasurements > 1 ? Math.sqrt(sumSquaredDeviations / (totalMeasurements * (totalMeasurements - 1))) : 0; // 假设B类不确定度(仪器分辨率引起的不确定度) // 这里假设分辨率为0.001,则B类不确定度为0.001/√3 const resolution = 0.001; const uB = resolution / Math.sqrt(3); // 合成不确定度 const uC = Math.sqrt(Math.pow(uA, 2) + Math.pow(uB, 2)); // 扩展不确定度(k=2,约95%置信水平) const U = 2 * uC; // 更新显示 document.getElementById('uncertainty-a').textContent = uA.toExponential(3); document.getElementById('uncertainty-b').textContent = uB.toExponential(3); document.getElementById('uncertainty-c').textContent = uC.toExponential(3); document.getElementById('uncertainty-u').textContent = U.toExponential(3); } function updateErrorChart(data) { if (data.length === 0) { if (errorChartInstance) { errorChartInstance.data.labels = []; errorChartInstance.data.datasets[0].data = []; errorChartInstance.update(); } return; } // 按测试点值排序 data.sort((a, b) => a.point - b.point); // 提取标签和误差值 const labels = data.map(item => item.point); const errorValues = data.map(item => item.error); // 使用通用图表创建函数 errorChartInstance = createOrUpdateChart(errorChartInstance, errorCtx, labels, errorValues, '示值误差'); } function saveErrorData() { const errorData = Array.from(document.querySelectorAll('.error-point-container')).map(container => { const pointInput = container.querySelector('.error-point-input'); const inputs = container.querySelectorAll('.error-input'); const avgSpan = container.querySelector('.error-avg'); const errorSpan = container.querySelector('.error-value'); return { point: pointInput.value, inputs: Array.from(inputs).map(input => input.value), avg: avgSpan.innerText, error: errorSpan.innerText }; }); setLocalStorage('errorData', JSON.stringify(errorData), 365); }