Skip to content

Commit 73b66c6

Browse files
committed
Change import order
1 parent f1361bb commit 73b66c6

File tree

1 file changed

+124
-30
lines changed

1 file changed

+124
-30
lines changed

utils/plotgraph.py

Lines changed: 124 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
A file dialog will open; pick your lamp.dat (or any .dat file).
2727
2828
- Or run and pass a path to skip the dialog:
29-
python plot_lamp_filepicker.py "C:\path\to\lamp.dat"
29+
python plot_lamp_filepicker.py "C:\\path\\to\\lamp.dat"
3030
3131
Notes:
3232
- If you prefer not to have a GUI, pass the file path as the first argument.
@@ -38,12 +38,14 @@
3838
import sys
3939
import os
4040
import csv
41-
import pickle
4241
import numpy as np
4342
import matplotlib
44-
matplotlib.use("Agg") # safe backend for non-interactive use
43+
44+
matplotlib.use("Agg") # safe backend for non-interactive use
4545
import matplotlib.pyplot as plt
4646
from datetime import datetime
47+
from scipy.interpolate import UnivariateSpline, interp1d
48+
4749

4850
def choose_file_with_dialog(initialdir: str | None = None) -> Path | None:
4951
try:
@@ -56,13 +58,16 @@ def choose_file_with_dialog(initialdir: str | None = None) -> Path | None:
5658
root.withdraw()
5759
# On Windows the dialog will be a native explorer-file dialog
5860
filetypes = [("DAT files", "*.dat"), ("All files", "*.*")]
59-
filename = filedialog.askopenfilename(title="Select .dat file", initialdir=initialdir or ".", filetypes=filetypes)
61+
filename = filedialog.askopenfilename(
62+
title="Select .dat file", initialdir=initialdir or ".", filetypes=filetypes
63+
)
6064
root.update()
6165
root.destroy()
6266
if not filename:
6367
return None
6468
return Path(filename).expanduser().resolve()
6569

70+
6671
def read_file_lines(filename: str):
6772
encodings = ["utf-8", "cp1252", "latin-1"]
6873
for enc in encodings:
@@ -83,6 +88,7 @@ def read_file_lines(filename: str):
8388
print("Opened file via binary fallback, decoded with errors='replace'.")
8489
return lines
8590

91+
8692
def parse_lines_to_arrays(lines):
8793
pixels = []
8894
adcs = []
@@ -98,8 +104,10 @@ def parse_lines_to_arrays(lines):
98104
a = float(parts[1])
99105
except Exception:
100106
try:
101-
p = int(''.join(ch for ch in parts[0] if (ch.isdigit() or ch == '-')))
102-
a = float(''.join(ch for ch in parts[1] if (ch.isdigit() or ch in ".-eE")))
107+
p = int("".join(ch for ch in parts[0] if (ch.isdigit() or ch == "-")))
108+
a = float(
109+
"".join(ch for ch in parts[1] if (ch.isdigit() or ch in ".-eE"))
110+
)
103111
except Exception:
104112
continue
105113
pixels.append(p)
@@ -108,6 +116,7 @@ def parse_lines_to_arrays(lines):
108116
raise ValueError("No valid pixel/ADC pairs found in file.")
109117
return np.array(pixels, dtype=int), np.array(adcs, dtype=float)
110118

119+
111120
def estimate_dark(pixels: np.ndarray, adcs: np.ndarray, method: str = "median"):
112121
mask = ((pixels >= 1) & (pixels <= 32)) | ((pixels >= 3679) & (pixels <= 3694))
113122
dark_vals = adcs[mask]
@@ -116,42 +125,58 @@ def estimate_dark(pixels: np.ndarray, adcs: np.ndarray, method: str = "median"):
116125
if n >= 64:
117126
dark_vals = np.concatenate((adcs[:32], adcs[-32:]))
118127
else:
119-
dark_vals = adcs[:max(1, n//10)]
128+
dark_vals = adcs[: max(1, n // 10)]
120129
if method == "median":
121130
return float(np.median(dark_vals)), dark_vals
122131
else:
123132
return float(np.mean(dark_vals)), dark_vals
124133

125-
def make_interpolator(pixels: np.ndarray, intensities: np.ndarray, method: str = "spline", smooth: float = 0.2):
134+
135+
def make_interpolator(
136+
pixels: np.ndarray,
137+
intensities: np.ndarray,
138+
method: str = "spline",
139+
smooth: float = 0.2,
140+
):
126141
"""Create an interpolator. `smooth` increases smoothing when using spline (larger -> smoother).
127142
128143
Returns (callable, description)
129144
"""
130145
try:
131146
if method == "spline":
132-
from scipy.interpolate import UnivariateSpline
133147
# stronger smoothing by default: scale factor (user-controlled)
134148
s = max(0.0, len(pixels) * np.var(intensities) * float(smooth))
135149
spline = UnivariateSpline(pixels, intensities, s=s)
136150
return spline, f"UnivariateSpline(s={s:.3g})"
137151
else:
138-
from scipy.interpolate import interp1d
139152
kind = "cubic" if method == "cubic" else "linear"
140-
f = interp1d(pixels, intensities, kind=kind, bounds_error=False, fill_value="extrapolate")
153+
f = interp1d(
154+
pixels,
155+
intensities,
156+
kind=kind,
157+
bounds_error=False,
158+
fill_value="extrapolate",
159+
)
141160
return f, f"interp1d({kind})"
142161
except Exception:
162+
143163
def lininterp(x):
144164
return np.interp(x, pixels, intensities)
165+
145166
return lininterp, "numpy.interp(linear,fallback)"
146167

147-
def save_pixel_csv(pixels: np.ndarray, adcs: np.ndarray, intensities: np.ndarray, out_csv: Path):
168+
169+
def save_pixel_csv(
170+
pixels: np.ndarray, adcs: np.ndarray, intensities: np.ndarray, out_csv: Path
171+
):
148172
header = ["pixel", "ADC", "Ipixel"]
149173
with out_csv.open("w", newline="", encoding="utf-8") as f:
150174
w = csv.writer(f)
151175
w.writerow(header)
152176
for p, a, i in zip(pixels, adcs, intensities):
153177
w.writerow([int(p), float(a), float(i)])
154178

179+
155180
def format_ctime_for_name(path: Path) -> str:
156181
# Use filesystem creation time when available
157182
try:
@@ -161,16 +186,54 @@ def format_ctime_for_name(path: Path) -> str:
161186
dt = datetime.fromtimestamp(ctime)
162187
return dt.strftime("%Y%m%d_%H%M%S")
163188

189+
164190
def main():
165-
parser = argparse.ArgumentParser(description="Open a .dat file via dialog and produce intensity + regression outputs in a new timestamped folder.")
166-
parser.add_argument("infile", nargs="?", default=None, help="optional path to .dat file (if omitted, a file dialog will open)")
167-
parser.add_argument("--interp", choices=["spline","cubic","linear"], default="spline", help="regression method for overlay and sampled CSV")
168-
parser.add_argument("--dark-method", choices=["median","mean"], default="median", help="how to estimate ADC_dark")
169-
parser.add_argument("--samples", type=int, default=10000, help="number of points to sample the interpolated function")
170-
parser.add_argument("--smooth", type=float, default=0.2, help="smoothing multiplier for spline (larger -> smoother). Default 0.2")
191+
parser = argparse.ArgumentParser(
192+
description="Open a .dat file via dialog and produce intensity + regression outputs in a new timestamped folder."
193+
)
194+
parser.add_argument(
195+
"infile",
196+
nargs="?",
197+
default=None,
198+
help="optional path to .dat file (if omitted, a file dialog will open)",
199+
)
200+
parser.add_argument(
201+
"--interp",
202+
choices=["spline", "cubic", "linear"],
203+
default="spline",
204+
help="regression method for overlay and sampled CSV",
205+
)
206+
parser.add_argument(
207+
"--dark-method",
208+
choices=["median", "mean"],
209+
default="median",
210+
help="how to estimate ADC_dark",
211+
)
212+
parser.add_argument(
213+
"--samples",
214+
type=int,
215+
default=10000,
216+
help="number of points to sample the interpolated function",
217+
)
218+
parser.add_argument(
219+
"--smooth",
220+
type=float,
221+
default=0.2,
222+
help="smoothing multiplier for spline (larger -> smoother). Default 0.2",
223+
)
171224
# Default smooths: keep only the weaker values (remove 0.1 and 0.2 as requested)
172-
parser.add_argument("--smooths", type=str, default="0.01,0.02,0.05", help="comma-separated list of smoothing multipliers to plot multiple regressions (e.g. '0.01,0.02,0.05')")
173-
parser.add_argument("--linewidth", type=float, default=0.6, help="default line width for PNG output (thin lines)")
225+
parser.add_argument(
226+
"--smooths",
227+
type=str,
228+
default="0.01,0.02,0.05",
229+
help="comma-separated list of smoothing multipliers to plot multiple regressions (e.g. '0.01,0.02,0.05')",
230+
)
231+
parser.add_argument(
232+
"--linewidth",
233+
type=float,
234+
default=0.6,
235+
help="default line width for PNG output (thin lines)",
236+
)
174237
args = parser.parse_args()
175238

176239
infile_path: Path | None = None
@@ -186,7 +249,9 @@ def main():
186249
if args.infile:
187250
infile_path = Path(args.infile).expanduser().resolve()
188251
else:
189-
p = input("No file chosen. Enter the path to the .dat file (or press Enter to quit): ").strip()
252+
p = input(
253+
"No file chosen. Enter the path to the .dat file (or press Enter to quit): "
254+
).strip()
190255
if not p:
191256
print("No file provided. Exiting.")
192257
sys.exit(1)
@@ -213,7 +278,9 @@ def main():
213278
print(f"Read {pixels.size} samples (pixel range {pixels.min()}..{pixels.max()})")
214279

215280
adc_dark, dark_values = estimate_dark(pixels, adcs, method=args.dark_method)
216-
print(f"Estimated ADC_dark ({args.dark_method}) = {adc_dark:.3f} from {dark_values.size} dummy pixels")
281+
print(
282+
f"Estimated ADC_dark ({args.dark_method}) = {adc_dark:.3f} from {dark_values.size} dummy pixels"
283+
)
217284
intensities = adc_dark - adcs
218285

219286
# Save per-pixel CSV
@@ -227,13 +294,17 @@ def main():
227294

228295
# Parse multi-smoothing values for plotting several regression strengths
229296
try:
230-
smooth_values = [float(s.strip()) for s in str(args.smooths).split(",") if s.strip()]
297+
smooth_values = [
298+
float(s.strip()) for s in str(args.smooths).split(",") if s.strip()
299+
]
231300
except Exception:
232301
smooth_values = [args.smooth]
233302

234303
interp_results = []
235304
for s_val in smooth_values:
236-
interp_fn_i, interp_kind_i = make_interpolator(pixels, intensities, method=args.interp, smooth=s_val)
305+
interp_fn_i, interp_kind_i = make_interpolator(
306+
pixels, intensities, method=args.interp, smooth=s_val
307+
)
237308
try:
238309
ys_i = interp_fn_i(xs)
239310
ys_i = np.asarray(ys_i, dtype=float)
@@ -246,7 +317,9 @@ def main():
246317
fig, (ax0, ax1, ax2) = plt.subplots(3, 1, figsize=(12, 10), sharex=True)
247318
lw = float(args.linewidth)
248319
ax0.plot(pixels, adcs, color="tab:blue", lw=lw, label="ADC (raw)")
249-
ax0.axhline(adc_dark, color="tab:orange", ls="--", lw=lw, label=f"ADC_dark={adc_dark:.2f}")
320+
ax0.axhline(
321+
adc_dark, color="tab:orange", ls="--", lw=lw, label=f"ADC_dark={adc_dark:.2f}"
322+
)
250323
ax0.set_ylabel("ADC counts")
251324
ax0.legend(fontsize="small")
252325
ax0.grid(True, alpha=0.3)
@@ -255,7 +328,13 @@ def main():
255328
# Use max-based inversion to keep signal in the same positive range
256329
y_flipped = np.max(intensities) - intensities
257330
# Make the flipped trace more visually distinct: green, slightly thicker
258-
ax1.plot(pixels, y_flipped, color="green", lw=max(0.8, lw * 1.2), label="Ipixel (flipped)")
331+
ax1.plot(
332+
pixels,
333+
y_flipped,
334+
color="green",
335+
lw=max(0.8, lw * 1.2),
336+
label="Ipixel (flipped)",
337+
)
259338
ax1.set_ylabel("Flipped Ipixel")
260339
# Visually invert the y-axis so the panel appears upside-down (peaks downwards)
261340
ax1.invert_yaxis()
@@ -266,16 +345,30 @@ def main():
266345
# Plot a very faint original Ipixel trace in the regression panel for reference
267346
orig_on_xs = np.interp(xs, pixels, intensities)
268347
# Slightly more visible raw trace for reference
269-
ax2.plot(xs, orig_on_xs, color="gray", lw=0.8, alpha=0.28, label="raw Ipixel (faint)")
348+
ax2.plot(
349+
xs, orig_on_xs, color="gray", lw=0.8, alpha=0.28, label="raw Ipixel (faint)"
350+
)
270351

271352
# Use only the requested distinct colors (no green/yellow): blue, red, purple
272353
palette = ["blue", "red", "purple"]
273354
colors = [palette[i % len(palette)] for i in range(len(interp_results))]
274355
import math
356+
275357
for (s_val, kind, ys_i), col in zip(interp_results, colors):
276358
# Force s=0.05 to green for emphasis per request (use isclose for safety)
277-
plot_col = "green" if math.isclose(float(s_val), 0.05, rel_tol=1e-6, abs_tol=1e-9) else col
278-
ax2.plot(xs, ys_i, color=plot_col, lw=max(0.8, lw), alpha=0.95, label=f"s={s_val} ({kind})")
359+
plot_col = (
360+
"green"
361+
if math.isclose(float(s_val), 0.05, rel_tol=1e-6, abs_tol=1e-9)
362+
else col
363+
)
364+
ax2.plot(
365+
xs,
366+
ys_i,
367+
color=plot_col,
368+
lw=max(0.8, lw),
369+
alpha=0.95,
370+
label=f"s={s_val} ({kind})",
371+
)
279372
ax2.set_xlabel("pixel number")
280373
ax2.set_ylabel("Ipixel (interpolated)")
281374
ax2.legend(fontsize="small")
@@ -302,5 +395,6 @@ def main():
302395
print(f"Saved plot -> {pngfile}")
303396
print("Done. All generated files are in:", out_dir)
304397

398+
305399
if __name__ == "__main__":
306-
main()
400+
main()

0 commit comments

Comments
 (0)