From 4a98c3bae376df1fbcb31c6df8da28acacdd2c1f Mon Sep 17 00:00:00 2001 From: Ronan Barzic Date: Sat, 18 Apr 2026 14:50:52 +0200 Subject: [PATCH] Add Xyce .prn file support to cicsim plot - WaveFile now parses Xyce .prn format (header, node names, #C time/data rows) - rawplot routes non-.raw extensions through WaveFile instead of ngRawRead --- cicsim/plot.py | 21 +++++++++------ cicsim/wavefiles.py | 62 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/cicsim/plot.py b/cicsim/plot.py index 93a3892..a762349 100644 --- a/cicsim/plot.py +++ b/cicsim/plot.py @@ -5,6 +5,7 @@ import numpy as np import pandas as pd import sys +import os from .command import * import math from .ngraw import * @@ -47,14 +48,18 @@ def plot(df,xname,yname,ptype=None,ax=None,label=""): def rawplot(fraw,xname,yname,ptype=None,axes=None,fname=None): - - dfs = toDataFrames(ngRawRead(fraw)) - - - if(len(dfs) == 0): - raise ValueError("No plots found in .raw file") - - df = dfs[0] + ext = os.path.splitext(fraw)[1].lower() + if ext == '.raw': + dfs = toDataFrames(ngRawRead(fraw)) + if len(dfs) == 0: + raise ValueError("No plots found in .raw file") + df = dfs[0] + else: + from .wavefiles import WaveFile + wf = WaveFile(fraw, xaxis=xname) + df = wf.df + if df is None or df.empty: + raise ValueError("No data found in %s" % fraw) if("," in yname): names = yname.split(",") diff --git a/cicsim/wavefiles.py b/cicsim/wavefiles.py index b5a444f..5ae768c 100644 --- a/cicsim/wavefiles.py +++ b/cicsim/wavefiles.py @@ -2,6 +2,7 @@ import cicsim as cs import os +import re import numpy as np import pandas as pd from matplotlib.ticker import EngFormatter @@ -133,6 +134,7 @@ def reload(self): self.modified = newmodified PANDAS_READERS = { + '.prn': lambda self: self._read_prn(), '.csv': lambda self: self._read_csv(','), '.tsv': lambda self: self._read_csv('\t'), '.txt': lambda self: self._read_csv('\t'), @@ -175,6 +177,66 @@ def _read_excel(self): df.columns = [c.strip() for c in df.columns] return df + def _read_prn(self): + """Parse Xyce .prn (print) waveform file into a DataFrame.""" + sweep_var = "time" + var_names = [] + times = [] + data_rows = [] + + with open(self.fname, 'r') as f: + in_header = False + in_nodes = False + pending_time = None + + for line in f: + line_s = line.strip() + + if line_s.startswith('#H'): + in_header = True + in_nodes = False + continue + + if line_s.startswith('#N'): + in_header = False + in_nodes = True + continue + + if line_s.startswith('#C'): + in_nodes = False + in_header = False + parts = line_s.split() + pending_time = float(parts[1]) + continue + + if line_s.startswith('#'): + in_header = False + in_nodes = False + pending_time = None + continue + + if in_header: + m = re.search(r"SWEEPVAR='([^']+)'", line_s) + if m: + sweep_var = m.group(1).lower() + continue + + if in_nodes: + var_names.extend(re.findall(r"'([^']+)'", line_s)) + continue + + if pending_time is not None and line_s: + vals = [float(v.split(':')[0]) for v in line_s.split()] + if vals: + times.append(pending_time) + data_rows.append(vals) + pending_time = None + + n_vars = len(var_names) + cols = [sweep_var] + [v.lower() for v in var_names] + rows = [[t] + d[:n_vars] for t, d in zip(times, data_rows)] + return pd.DataFrame(rows, columns=cols) + @staticmethod def excel_sheet_names(fname): xl = pd.ExcelFile(fname)