1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-21 14:08:13 +00:00
trezor-firmware/tools/automatic_battery_tester/analysis/temperature_analysis.py
2025-07-02 14:19:11 +02:00

258 lines
6.8 KiB
Python

import sys
from pathlib import Path
import matplotlib.pyplot as plt
from InquirerPy import inquirer
from InquirerPy.base import Choice
from utils import load_measured_data
default_dataset_dir = Path("../single_capture_test_results")
battery_thermal_limit = 45.0 # Celsius
case_thermal_limit = 41.0 # Celsius
def select_waveforms(dataset_directory=default_dataset_dir):
"""
Select waveforms from a given dataset directory.
Args:
dataset_directory (Path): The directory containing the dataset.
Returns:
list: A list of selected waveforms.
"""
if not dataset_directory.exists():
print(f"Dataset directory {dataset_directory} does not exist.")
return []
# glob all .csv files in the directory
all_csv_files = list(dataset_directory.glob("*.csv"))
if not all_csv_files:
print(f"No CSV files found in {dataset_directory}.")
return []
external_temp_files = list(dataset_directory.glob("external_temp.*.csv"))
# Filter out external temperature files
waveform_files = [f for f in all_csv_files if f not in external_temp_files]
choices = []
for waveform_file in waveform_files:
time_id = waveform_file.stem.split(".")[1]
ch = Choice(
name=f"{waveform_file.name}",
value={"waveform": waveform_file, "external_temp": None},
)
for temp_file in external_temp_files:
if time_id in temp_file.stem:
ch.name += " (ext. temp available)"
ch.value["external_temp"] = temp_file
break
choices.append(ch)
try:
selected = inquirer.fuzzy(
message="Select one or more waveforms:",
choices=choices,
multiselect=True,
instruction="(Use <tab> to select, <enter> to confirm)",
).execute()
except KeyboardInterrupt:
print("Selection cancelled by user.")
sys.exit(0)
except Exception as e:
print(f"An error occurred during selection: {e}")
sys.exit(1)
return selected
def colored_region_plot(axis, time_vector, data_vector, mask, color="red", alpha=0.5):
start = None
in_region = False
for i, val in enumerate(mask):
if val and not in_region:
start = i
in_region = True
elif not val and in_region:
axis.plot(
time_vector[start : i - 1],
data_vector[start : i - 1],
color=color,
alpha=alpha,
)
in_region = False
if in_region:
axis.plot(
time_vector[start : (i - 1)],
data_vector[start : i - 1],
color=color,
alpha=alpha,
)
def colored_region_box(axis, time_vector, mask, color="orange", alpha=0.5):
start = None
in_region = False
for i, val in enumerate(mask):
if val and not in_region:
start = i
in_region = True
elif not val and in_region:
axis.axvspan(
time_vector[start], time_vector[i - 1], color=color, alpha=alpha
)
in_region = False
if in_region:
axis.axvspan(time_vector[start], time_vector[-1], color=color, alpha=alpha)
def sec_to_min(time_vector):
return (time_vector - time_vector[0]) / 60.0
def plot_temperature_profile(waveform_name, profile_data):
fig, ax = plt.subplots(2)
fig.canvas.manager.set_window_title(waveform_name)
ax[0].plot(
sec_to_min(profile_data.time),
profile_data.battery_temp,
color="green",
label="battery temeperature",
)
ax[0].axhline(y=battery_thermal_limit, color="green", linestyle="--")
ax[0].plot(
sec_to_min(profile_data.time),
profile_data.pmic_die_temp,
color="orange",
label="pmic die temperature",
)
colored_region_plot(
ax[0],
sec_to_min(profile_data.time),
profile_data.battery_temp,
profile_data.battery_temp > battery_thermal_limit,
color="red",
alpha=1,
)
if profile_data.ext_temp is not None:
ax[0].plot(
sec_to_min(profile_data.ext_temp_time),
profile_data.ext_temp,
color="blue",
label="case temperature",
linestyle="--",
)
ax[0].axhline(y=case_thermal_limit, color="blue", linestyle="--")
colored_region_plot(
ax[0],
sec_to_min(profile_data.ext_temp_time),
profile_data.ext_temp,
profile_data.ext_temp > case_thermal_limit,
color="red",
alpha=1,
)
ax[0].set_xlabel("Time (min)")
ax[0].set_ylabel("Temperature (C)")
ax[0].set_title("Temperature Profile: " + waveform_name)
ax[0].set_xlim(
left=sec_to_min(profile_data.time)[0], right=sec_to_min(profile_data.time)[-1]
)
ax[0].legend()
ax[0].grid(True)
def min_to_hr(x):
return x / 60.0
def hr_to_min(x):
return x * 60.0
secax = ax[0].secondary_xaxis("top", functions=(min_to_hr, hr_to_min))
secax.set_xlabel("Time (hours)")
# Change background color according to charging state
usb_charging_mask = (profile_data.usb == "USB_connected") & (
abs(profile_data.battery_current) > 0
)
wlc_charging_mask = (
(profile_data.wlc == "WLC_connected")
& ~usb_charging_mask
& (abs(profile_data.battery_current) > 0)
)
colored_region_box(
ax[0], sec_to_min(profile_data.time), usb_charging_mask, color="blue", alpha=0.2
)
colored_region_box(
ax[0],
sec_to_min(profile_data.time),
wlc_charging_mask,
color="green",
alpha=0.2,
)
ax[1].plot(
sec_to_min(profile_data.time),
profile_data.battery_current,
color="purple",
label="battery current",
)
ax[1].set_xlabel("Time (min)")
ax[1].set_ylabel("Current (mA)")
ax[1].set_xlim(
left=sec_to_min(profile_data.time)[0], right=sec_to_min(profile_data.time)[-1]
)
ax[1].grid(True)
ax[1].legend()
colored_region_box(
ax[1], sec_to_min(profile_data.time), usb_charging_mask, color="blue", alpha=0.2
)
colored_region_box(
ax[1],
sec_to_min(profile_data.time),
wlc_charging_mask,
color="green",
alpha=0.2,
)
def main():
selected_waveforms = select_waveforms()
for waveform in selected_waveforms:
# Load data from files
profile_data = load_measured_data(
data_file_path=waveform["waveform"],
extern_temp_file_path=waveform["external_temp"],
)
plot_temperature_profile(waveform["waveform"].name, profile_data)
# Plot graphs
plt.show()
if __name__ == "__main__":
main()