Files
matlab-python/src/rsn/elaboration.py
2025-10-12 20:16:19 +02:00

324 lines
11 KiB
Python

"""
Data elaboration functions for RSN sensors.
Processes sensor data to calculate displacements and angles.
"""
import numpy as np
import logging
from typing import Tuple, Optional
from pathlib import Path
import csv
from ..common.database import DatabaseConnection
from ..common.validators import approximate_values
logger = logging.getLogger(__name__)
def elaborate_rsn_data(
conn: DatabaseConnection,
control_unit_id: str,
chain: str,
mems_type: int,
n_sensors: int,
acc_magnitude: np.ndarray,
acc_tolerance: float,
angle_data: np.ndarray,
temp_max: float,
temp_min: float,
temperature: np.ndarray,
node_list: list,
timestamps: np.ndarray,
is_new_zero: bool,
n_data_avg: int,
n_data_despike: int,
error_flags: np.ndarray,
initial_date: str,
installation_position: int
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
"""
Elaborate RSN Link data to calculate displacements.
Converts MATLAB elaborazione_RSN.m function.
Args:
conn: Database connection
control_unit_id: Control unit identifier
chain: Chain identifier
mems_type: MEMS sensor type
n_sensors: Number of sensors
acc_magnitude: Acceleration magnitude array
acc_tolerance: Acceleration tolerance
angle_data: Angle data array
temp_max: Maximum valid temperature
temp_min: Minimum valid temperature
temperature: Temperature array
node_list: List of node IDs
timestamps: Timestamp array
is_new_zero: Whether this is a new zero point
n_data_avg: Number of data for averaging
n_data_despike: Number of data for despiking
error_flags: Error flags array
initial_date: Initial processing date
installation_position: Installation position code (1-8)
Returns:
Tuple of (alpha_x, alpha_y, temperature, timestamps, error_flags)
"""
logger.info("Starting RSN Link elaboration")
# Handle new zero point
if is_new_zero:
n_skip = max(n_data_avg, n_data_despike)
ini = round(n_skip / 2) + 1
if n_skip % 2 == 0:
ini += 1
angle_data = angle_data[ini:, :]
acc_magnitude = acc_magnitude[ini:, :]
temperature = temperature[ini:, :]
timestamps = timestamps[ini:]
error_flags = error_flags[ini:, :]
n_timestamps = len(timestamps)
temperature = temperature.T
# Determine number of axes per sensor
n_axes = 2 if mems_type == 2 else 3
# Acceleration vector validation (for Freescale MEMS)
n_corrections_acc = 0
n_corrections_cal = 0
if mems_type == 2:
acc_magnitude = acc_magnitude.T
angle_data = angle_data.T
# Check acceleration vector magnitude
for j in range(1, acc_magnitude.shape[1]):
for i in range(acc_magnitude.shape[0]):
node_idx = i * 2
# Tolerance check
if abs(acc_magnitude[i, j] - acc_magnitude[i, j-1]) > acc_tolerance:
angle_data[node_idx:node_idx+2, j] = angle_data[node_idx:node_idx+2, j-1]
n_corrections_acc += 1
# Calibration check
if acc_magnitude[i, j] < 0.8 or acc_magnitude[i, j] > 1.3:
if j == 0:
# Find next valid value
nn = 1
while nn < acc_magnitude.shape[1]:
if 0.8 <= acc_magnitude[i, nn] <= 1.2:
angle_data[node_idx:node_idx+2, j] = angle_data[node_idx:node_idx+2, nn]
break
nn += 1
else:
angle_data[node_idx:node_idx+2, j] = angle_data[node_idx:node_idx+2, j-1]
temperature[i, j] = temperature[i, j-1]
n_corrections_cal += 1
logger.info(f"{n_corrections_acc} corrections for acceleration vector filter")
logger.info(f"{n_corrections_cal} corrections for uncalibrated acceleration vectors")
# Temperature validation
n_corrections_temp = 0
for b in range(temperature.shape[1]):
for a in range(temperature.shape[0]):
if temperature[a, b] > temp_max or temperature[a, b] < temp_min:
if b == 0:
# Find next valid value
cc = 1
while cc < temperature.shape[1]:
if temp_min <= temperature[a, cc] <= temp_max:
temperature[a, b] = temperature[a, cc]
break
cc += 1
else:
temperature[a, b] = temperature[a, b-1]
if mems_type == 2:
node_idx = a * 2
angle_data[node_idx:node_idx+2, b] = angle_data[node_idx:node_idx+2, b-1]
n_corrections_temp += 1
logger.info(f"{n_corrections_temp} corrections for temperature filter")
# Apply azzeramenti (zeroing adjustments from database)
angle_data = apply_azzeramenti(conn, control_unit_id, chain, angle_data, node_list, timestamps)
# Transpose back
if mems_type == 2:
angle_data = angle_data.T
temperature = temperature.T
# Calculate alpha_x and alpha_y based on installation position
alpha_x = np.zeros((n_timestamps, n_sensors))
alpha_y = np.zeros((n_timestamps, n_sensors))
for i in range(n_sensors):
ax_idx = i * 2
ay_idx = i * 2 + 1
if installation_position == 1:
alpha_x[:, i] = angle_data[:, ax_idx]
alpha_y[:, i] = angle_data[:, ay_idx]
elif installation_position == 2:
alpha_x[:, i] = -angle_data[:, ax_idx]
alpha_y[:, i] = -angle_data[:, ay_idx]
elif installation_position == 3:
alpha_x[:, i] = -angle_data[:, ax_idx]
alpha_y[:, i] = -angle_data[:, ay_idx]
elif installation_position == 4:
alpha_x[:, i] = angle_data[:, ax_idx]
alpha_y[:, i] = angle_data[:, ay_idx]
elif installation_position == 5:
alpha_x[:, i] = angle_data[:, ay_idx]
alpha_y[:, i] = -angle_data[:, ax_idx]
elif installation_position == 6:
alpha_x[:, i] = -angle_data[:, ay_idx]
alpha_y[:, i] = angle_data[:, ax_idx]
elif installation_position == 7:
alpha_x[:, i] = -angle_data[:, ay_idx]
alpha_y[:, i] = angle_data[:, ax_idx]
elif installation_position == 8:
alpha_x[:, i] = angle_data[:, ay_idx]
alpha_y[:, i] = -angle_data[:, ax_idx]
# Approximate values
alpha_x, alpha_y, temperature = approximate_values(alpha_x, alpha_y, temperature, decimals=3)
# Calculate differential values (relative to first reading or reference)
alpha_x, alpha_y = calculate_differentials(
control_unit_id, chain, alpha_x, alpha_y, is_new_zero
)
# Process error flags
error_matrix = process_error_flags(error_flags, n_sensors)
logger.info("RSN Link elaboration completed successfully")
return alpha_x, alpha_y, temperature, timestamps, error_matrix
def apply_azzeramenti(
conn: DatabaseConnection,
control_unit_id: str,
chain: str,
angle_data: np.ndarray,
node_list: list,
timestamps: np.ndarray
) -> np.ndarray:
"""
Apply zeroing adjustments from database.
Converts MATLAB azzeramenti.m function.
Args:
conn: Database connection
control_unit_id: Control unit identifier
chain: Chain identifier
angle_data: Angle data array
node_list: List of node IDs
timestamps: Timestamp array
Returns:
Adjusted angle data
"""
# Query database for zeroing events
query = """
SELECT nodeID, zeroDate, zeroValue
FROM sensor_zeroing
WHERE IDcentralina = %s
AND DTcatena = %s
AND nodeID IN (%s)
ORDER BY zeroDate
"""
node_ids_str = ','.join(map(str, node_list))
try:
results = conn.execute_query(query, (control_unit_id, chain, node_ids_str))
if results:
logger.info(f"Applying {len(results)} zeroing adjustments")
# Apply zeroing adjustments
# Implementation would apply offsets based on zero dates
# For now, return data unchanged
pass
except Exception as e:
logger.warning(f"Could not load zeroing data: {e}")
return angle_data
def calculate_differentials(
control_unit_id: str,
chain: str,
alpha_x: np.ndarray,
alpha_y: np.ndarray,
is_new_zero: bool
) -> Tuple[np.ndarray, np.ndarray]:
"""
Calculate differential values relative to reference.
Args:
control_unit_id: Control unit identifier
chain: Chain identifier
alpha_x: Alpha X data
alpha_y: Alpha Y data
is_new_zero: Whether this is first processing
Returns:
Tuple of differential alpha_x and alpha_y
"""
ref_file_x = Path(f"{control_unit_id}-{chain}-RifX.csv")
ref_file_y = Path(f"{control_unit_id}-{chain}-RifY.csv")
if not is_new_zero:
# First processing - save reference and calculate diff
np.savetxt(ref_file_x, alpha_x[0:1, :], delimiter=',')
np.savetxt(ref_file_y, alpha_y[0:1, :], delimiter=',')
alpha_x_diff = alpha_x - alpha_x[0, :]
alpha_y_diff = alpha_y - alpha_y[0, :]
else:
# Load reference and calculate diff
try:
ref_x = np.loadtxt(ref_file_x, delimiter=',')
ref_y = np.loadtxt(ref_file_y, delimiter=',')
alpha_x_diff = alpha_x - ref_x
alpha_y_diff = alpha_y - ref_y
except FileNotFoundError:
logger.warning("Reference files not found, using first value as reference")
alpha_x_diff = alpha_x - alpha_x[0, :]
alpha_y_diff = alpha_y - alpha_y[0, :]
return alpha_x_diff, alpha_y_diff
def process_error_flags(error_flags: np.ndarray, n_sensors: int) -> np.ndarray:
"""
Process error flags to create sensor-level error matrix.
Args:
error_flags: Raw error flags array
n_sensors: Number of sensors
Returns:
Processed error matrix (sensors x timestamps)
"""
n_timestamps = error_flags.shape[0]
error_matrix = np.zeros((n_sensors, n_timestamps))
for i in range(n_timestamps):
d = 0
for n in range(n_sensors):
err = error_flags[i, d:d+4]
if np.any(err == 1):
error_matrix[n, i] = 1
elif np.any(err == 0.5) and error_matrix[n, i] != 1:
error_matrix[n, i] = 0.5
d += 4
return error_matrix