90 def perform_calibration(self) -> CalibrationResult:
91 """Perform pressure transducer calibration using Bayesian regression"""
92 if len(self.calibration_points) < 5:
93 raise ValueError("Insufficient calibration points")
96 voltages = np.array([p.input_value for p in self.calibration_points])
97 pressures = np.array([p.reference_value for p in self.calibration_points])
98 uncertainties = np.array([p.uncertainty for p in self.calibration_points])
100 # Perform Bayesian regression (simplified implementation)
101 # In practice, this would implement the full Bayesian framework from the paper
103 # Linear fit: P = a + b*V + c*V^2 + environmental_terms
104 n_points = len(voltages)
106 # Design matrix for linear + quadratic + environmental terms
109 np.ones(n_points), # Constant term
110 voltages, # Linear term
111 voltages**2, # Quadratic term
112 voltages**3, # Cubic term
113 np.sqrt(voltages), # Square root term
114 np.log(1 + voltages), # Log term
118 # Add environmental terms
119 for point in self.calibration_points:
121 for key, value in point.environmental_conditions.items():
122 if key == "temperature":
123 env_terms.extend([value, value**2, value * point.input_value])
124 A = np.column_stack([A, np.array(env_terms)])
126 # Weight matrix (inverse of uncertainties)
127 W = np.diag(1.0 / (uncertainties**2 + 1e-6))
129 # Bayesian regression: θ = (A^T W A + Σ₀⁻¹)⁻¹ (A^T W b + Σ₀⁻¹ μ₀)
130 # Simplified version without priors
132 cov_matrix = np.linalg.inv(A.T @ W @ A)
133 parameters = cov_matrix @ (A.T @ W @ pressures)
134 except np.linalg.LinAlgError:
135 logger.warning("Singular matrix, using regularized solution")
136 regularization = 1e-6 * np.eye(A.shape[1])
137 cov_matrix = np.linalg.inv(A.T @ W @ A + regularization)
138 parameters = cov_matrix @ (A.T @ W @ pressures)
140 # Calculate quality metrics
141 predicted_pressures = A @ parameters
142 residuals = pressures - predicted_pressures
143 rmse = np.sqrt(np.mean(residuals**2))
144 nrmse = rmse / (np.max(pressures) - np.min(pressures))
146 # Calculate coverage (95% confidence interval)
147 confidence_intervals = 1.96 * np.sqrt(np.diag(A @ cov_matrix @ A.T))
148 coverage_count = np.sum(np.abs(residuals) <= confidence_intervals)
149 coverage_95 = coverage_count / n_points
151 # Extrapolation confidence
152 voltage_range = np.max(voltages) - np.min(voltages)
153 extrapolation_confidence = min(1.0, 0.9 * np.exp(-voltage_range / 2.0))
157 "nrmse": float(nrmse),
158 "coverage_95": float(coverage_95),
159 "extrapolation_confidence": float(extrapolation_confidence),
160 "condition_number": float(np.linalg.cond(A.T @ W @ A)),
161 "num_points": len(self.calibration_points),
164 return CalibrationResult(
165 sensor_id=self.sensor_id,
166 sensor_type=self.sensor_type,
167 calibration_points=self.calibration_points.copy(),
169 "a0": float(parameters[0]),
170 "a1": float(parameters[1]),
171 "a2": float(parameters[2]),
172 "a3": float(parameters[3]),
173 "a4": float(parameters[4]),
174 "a5": float(parameters[5]),
176 covariance_matrix=cov_matrix.tolist(),
177 quality_metrics=quality_metrics,
178 calibration_time=time.time(),