Compare commits

..

8 Commits

Author SHA1 Message Date
18ed677ab6 Added plot derivatives 2026-06-04 10:29:20 +02:00
b1666cc498 Merge branch 'pierre' into GraphRPM 2026-06-04 10:25:58 +02:00
6f1fa2bbdd rpm plot script 2026-05-27 16:05:29 +02:00
2da49b50bf Vectorized data processing 2026-05-27 14:49:00 +02:00
Pierre Barbier
4e9277a023 fixed 1 error 2026-05-27 14:40:01 +02:00
Pierre Barbier
018517b662 d 2026-05-27 14:38:40 +02:00
b246f6b19c rpm_graph v1 2026-05-27 14:30:13 +02:00
Pierre Barbier
1452d231aa derivatives 2026-05-27 14:27:02 +02:00
4 changed files with 297 additions and 0 deletions

66
src/derivatives.py Normal file
View File

@@ -0,0 +1,66 @@
# Copyright (C) 2026 Hector van der Aa <hector@h3cx.dev>
# Copyright (C) 2026 Pierre Barbier <pierrebarbier741@gmail.com>
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
import argparse
from pathlib import Path
import pandas as pd
from tqdm import tqdm
def filter_data(file: Path) -> pd.DataFrame:
df = pd.read_csv(file, usecols=["time_us", "crank", "cam"])
crank_df = df.loc[df["crank"] == 1, ["time_us"]].copy()
crank_df["d1"] = crank_df["time_us"].diff()
crank_df["prev_d1"] = crank_df["d1"].shift(1)
crank_df["d2"] = crank_df["d1"] - crank_df["prev_d1"]
crank_df["ratio"] = crank_df["d2"] / crank_df["d1"]
crank_df = crank_df.dropna(subset=["d1", "d2", "ratio"])
return crank_df[["time_us", "d1", "d2", "ratio"]]
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("directory", type=Path, help="Source data directory")
args = parser.parse_args()
directory: Path = args.directory
if not directory.is_dir():
parser.error(f"{directory} is not a valid directory")
print(f"Processing data in: {directory}")
concat_files: list[Path] = []
for path in directory.glob("*.csv"):
stem = path.stem
try:
base_name, channel = stem.rsplit("_", 1)
except ValueError:
print(f"Skipping badly named file: {path}")
continue
if channel != "trimmed":
print(f"Skipping unknown file: {path}")
continue
concat_files.append(path)
for file in tqdm(concat_files, desc="Files"):
base_name, _ = file.stem.rsplit("_", 1)
output = file.parent / f"{base_name}_derivative.csv"
out_df = filter_data(file)
out_df.to_csv(output, index=False)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,80 @@
# Copyright (C) 2026 Hector van der Aa <hector@h3cx.dev>
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
from cProfile import label
import pandas as pd
from pathlib import Path
import argparse
from tqdm import tqdm
import matplotlib.pyplot as plt
parser = argparse.ArgumentParser()
parser.add_argument("directory", type=Path, help="Source data directory")
args = parser.parse_args()
directory: Path = args.directory
if not directory.is_dir():
parser.error(f"{directory} is not a valid directory")
print(f"Processing data in: {directory}")
files: list[Path] = []
for path in directory.glob("*.csv"):
stem = path.stem
try:
base_name, channel = stem.rsplit("_", 1)
except ValueError:
print(f"Skipping badly named file: {path}")
continue
if channel != "trimmed":
print(f"Skipping unknown file: {path}")
continue
files.append(path)
for file in files:
trimmed_df = pd.read_csv(file).set_index("time_us", drop=False)
base_name, _ = file.stem.rsplit("_", 1)
der_file = file.parent / f"{base_name}_derivative.csv"
der_df = pd.read_csv(der_file).set_index("time_us", drop=False)
fig, ax = plt.subplots(figsize=(12, 6))
# RPM
ax.plot(der_df["time_us"] / 1_000_000, der_df["d1"], label="d1")
ax.set_xlabel("Time (s)")
ax.set_ylabel("RPM")
ax.set_title(f"{base_name} RPM over Time")
ax.grid(True)
ax1 = ax.twinx()
ax1.plot(der_df["time_us"] / 1_000_000, der_df["d2"], label="d2", color="red")
# Crank on second y-axis
ax2 = ax.twinx()
ax2.plot(
trimmed_df["time_us"] / 1_000_000, trimmed_df["crank"], alpha=0.4, label="Crank"
)
ax2.set_ylabel("Crank")
ax2.set_ylim(-0.1, 1.1)
ax3 = ax.twinx()
ax3.plot(
trimmed_df["time_us"] / 1_000_000,
trimmed_df["cam"],
alpha=0.4,
label="Cam",
color="red",
)
ax3.set_ylabel("Cam")
ax3.set_ylim(-0.1, 1.1)
fig.tight_layout()
plt.show()
plt.close(fig)

76
src/plot_rpm_graphs.py Normal file
View File

@@ -0,0 +1,76 @@
# Copyright (C) 2026 Hector van der Aa <hector@h3cx.dev>
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
import pandas as pd
from pathlib import Path
import argparse
from tqdm import tqdm
import matplotlib.pyplot as plt
parser = argparse.ArgumentParser()
parser.add_argument("directory", type=Path, help="Source data directory")
args = parser.parse_args()
directory: Path = args.directory
if not directory.is_dir():
parser.error(f"{directory} is not a valid directory")
print(f"Processing data in: {directory}")
files: list[Path] = []
for path in directory.glob("*.csv"):
stem = path.stem
try:
base_name, channel = stem.rsplit("_", 1)
except ValueError:
print(f"Skipping badly named file: {path}")
continue
if channel != "trimmed":
print(f"Skipping unknown file: {path}")
continue
files.append(path)
for file in files:
trimmed_df = pd.read_csv(file).set_index("time_us", drop=False)
base_name, _ = file.stem.rsplit("_", 1)
rpm_file = file.parent / f"{base_name}_rpm.csv"
rpm_df = pd.read_csv(rpm_file).set_index("time_us", drop=False)
fig, ax = plt.subplots(figsize=(12, 6))
# RPM
ax.plot(rpm_df["time_us"] / 1_000_000, rpm_df["rpm"], label="RPM")
ax.set_xlabel("Time (s)")
ax.set_ylabel("RPM")
ax.set_title(f"{base_name} RPM over Time")
ax.grid(True)
# Crank on second y-axis
ax2 = ax.twinx()
ax2.plot(
trimmed_df["time_us"] / 1_000_000, trimmed_df["crank"], alpha=0.4, label="Crank"
)
ax2.set_ylabel("Crank")
ax2.set_ylim(-0.1, 1.1)
ax3 = ax.twinx()
ax3.plot(
trimmed_df["time_us"] / 1_000_000,
trimmed_df["cam"],
alpha=0.4,
label="Cam",
color="red",
)
ax3.set_ylabel("Cam")
ax3.set_ylim(-0.1, 1.1)
fig.tight_layout()
plt.show()
plt.close(fig)

75
src/rpm_graph.py Normal file
View File

@@ -0,0 +1,75 @@
# Copyright (C) 2026 Hector van der Aa <hector@h3cx.dev>
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
import pandas as pd
from pathlib import Path
import argparse
from tqdm import tqdm
import matplotlib.pyplot as plt
parser = argparse.ArgumentParser()
parser.add_argument("directory", type=Path, help="Source data directory")
args = parser.parse_args()
directory: Path = args.directory
if not directory.is_dir():
parser.error(f"{directory} is not a valid directory")
print(f"Processing data in: {directory}")
files: list[Path] = []
for path in directory.glob("*.csv"):
stem = path.stem
try:
base_name, channel = stem.rsplit("_", 1)
except ValueError:
print(f"Skipping badly named file: {path}")
continue
if channel != "trimmed":
print(f"Skipping unknown file: {path}")
continue
files.append(path)
for file in files:
print(f"Processing {file.name}")
df = pd.read_csv(file).set_index("time_us", drop=False)
rows = []
current_time_us: int = 0
current_crank: int = 0
last_crank: int = -1
for _, row in tqdm(df.iterrows(), total=len(df)):
current_time_us = row["time_us"]
current_crank = row["crank"]
if current_crank == 1:
if last_crank != -1:
rpm = 30_000_000 / (current_time_us - last_crank)
rows.append({"time_us": current_time_us, "rpm": rpm})
last_crank = current_time_us
out_df = pd.DataFrame(rows)
base_name, _ = file.stem.rsplit("_", 1)
output = file.parent / f"{base_name}_rpm.csv"
image_output = file.parent / f"{base_name}_rpm.png"
out_df.to_csv(output)
fig, ax = plt.subplots(figsize=(12, 6))
ax.plot(out_df["time_us"] / 1_000_000, out_df["rpm"])
ax.set_xlabel("Time (s)")
ax.set_ylabel("RPM")
ax.set_title(f"{base_name} RPM over Time")
ax.grid(True)
fig.tight_layout()
fig.savefig(image_output, dpi=300)
plt.close(fig)