Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 51 additions & 11 deletions dosview/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,23 +87,40 @@ def plot(self, data):
plot_evolution.plot(time_axis[window_size-1:], rolling_avg, pen=pen)

ev_data = self.data[2]
plot_spectrum.plot(range(len(ev_data)), ev_data,
plot_spectrum.plot(range(len(ev_data)), ev_data,
pen="r", symbol='x', symbolPen = 'g',
symbolBrush = 0.2, name = "Energy")
plot_spectrum.setLabel("left", "Total count per channel", units="#")
plot_spectrum.setLabel("bottom", "Channel", units="#")

# np_metadata = data[4]

# print("METADATA")
# print(np_metadata[:,0]/60)
# print(np_metadata[:,6])
# plot_evolution.plot(np_metadata[:,0]/60, np_metadata[:,6], pen="b", symbol='p', symbolPen='b', symbolBrush=0.1, name="Pressure")


plot_spectrum.setLogMode(x=True, y=True)
plot_spectrum.showGrid(x=True, y=True)

if len(self.data) > 4 and self.data[4]:
telemetry = self.data[4]
plot_telemetry = self.addPlot(row=2, col=0)
plot_telemetry.showGrid(x=True, y=True)
plot_telemetry.setLabel("bottom", "Time", units="min")
plot_telemetry.addLegend()

colors = {
"temperature_0": (220, 50, 50),
"humidity_0": (50, 100, 220),
"temperature_1": (220, 130, 50),
"humidity_1": (50, 180, 220),
"temperature_2": (180, 50, 180),
"pressure_3": (50, 200, 100),
"voltage": (240, 240, 50),
"current": (200, 50, 200),
"capacity_remaining": (100, 220, 150),
"capacity_full": (150, 150, 150),
"temperature": (220, 100, 100),
}
for key, (t, vals) in telemetry.items():
pen = pg.mkPen(color=colors.get(key, (200, 200, 200)), width=2)
line = plot_telemetry.plot(t / 60, vals, pen=pen, name=key)
self.telemetry_lines[key] = line

print("PLOT DURATION ... ", time.time()-start_time)

def telemetry_toggle(self, key, value):
Expand Down Expand Up @@ -804,6 +821,11 @@ def initUI(self):
self.upload_file_button.setMaximumHeight(20)
self.upload_file_button.clicked.connect(lambda: UploadFileDialog().exec_())

self.export_csv_button = QPushButton("Export spectrum")
self.export_csv_button.setMaximumHeight(20)
self.export_csv_button.clicked.connect(self.export_spectrum_csv)
self.export_csv_button.setEnabled(False)

log_view_widget = QWidget()

self.left_panel = QSplitter(Qt.Vertical)
Expand All @@ -814,6 +836,7 @@ def initUI(self):
vb = QHBoxLayout()
vb.addWidget(self.open_img_view_button)
vb.addWidget(self.upload_file_button)
vb.addWidget(self.export_csv_button)
self.left_panel.setLayout(vb)

self.logView_splitter = QSplitter(Qt.Horizontal)
Expand Down Expand Up @@ -843,8 +866,9 @@ def start_data_loading(self):
self.load_data_thread.start()

def on_data_loaded(self, data):
self.data = data # TODO>.. tohle do budoucna zrusit a nahradit tridou parseru..
self.data = data # TODO>.. tohle do budoucna zrusit a nahradit tridou parseru..
print("Data are fully loaded...")
self.export_csv_button.setEnabled(True)
self.plot_canvas.plot(data)
print("After plot data canvas")

Expand Down Expand Up @@ -888,12 +912,28 @@ def add_properties_to_tree(item, properties):


def open_spectrogram_view(self):
matrix = self.data[-1] #TODO .. tohle predelat na nejakou tridu pro parserovani
matrix = self.data[3] # metadata dict (spectrogram view)

w = DataSpectrumView(self)
w.show()
w.plot_data(matrix)

def export_spectrum_csv(self):
path, _ = QFileDialog.getSaveFileName(
self, "Export spectrum", "", "CSV files (*.csv)"
)
if not path:
return
if not path.endswith(".csv"):
path += ".csv"
import csv
hist = self.data[2]
with open(path, "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["channel", "counts"])
for ch, cnt in enumerate(hist):
writer.writerow([ch, int(cnt)])


class UploadFileDialog(QDialog):
def __init__(self, parent=None):
Expand Down
84 changes: 71 additions & 13 deletions dosview/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,38 +29,45 @@ def parse(self): # pragma: no cover - concrete classes implement
raise NotImplementedError


class Airdos04CLogParser(BaseLogParser):
"""Parser for AIRDOS04C log files."""
class AirdosV2LogParser(BaseLogParser):
"""Parser for AIRDOS log files using data format version 2.x."""

@staticmethod
def detect(file_path: str | Path) -> bool:
with open(file_path, "r") as f:
for line in f:
if line.startswith("$DOS") and "AIRDOS04C" in line:
return True
if line.startswith("$DOS"):
parts = line.strip().split(",")
# parts[2] = fw-version; MAJOR.MINOR encodes the data format version
if len(parts) > 2 and parts[2].startswith("2."):
return True
return False

def parse(self):
start_time = time.time()
print("AIRDOS04C parser start")
print("AIRDOS v2 parser start")
metadata = {
"log_runs_count": 0,
"log_device_info": {},
"log_info": {},
}
hist = np.zeros(1024, dtype=int)
hist = np.zeros(65536, dtype=int)
total_counts = 0
sums: List[int] = []
time_axis: List[float] = []
inside_run = False
current_hist = None
current_counts = 0
device_type = "unknown"
env_records: List[Tuple[float, ...]] = []
batt_records: List[Tuple[float, ...]] = []

with open(self.file_path, "r") as file:
for line in file:
parts = line.strip().split(",")
match parts[0]:
case "$DOS":
device_type = parts[1] if len(parts) > 1 else "unknown"
metadata["log_device_info"]["DOS"] = {
"type": parts[0],
"hw-model": parts[1],
Expand All @@ -83,8 +90,8 @@ def parse(self):
current_counts += 1
case "$STOP":
if inside_run:
if len(parts) > 4:
for idx, val in enumerate(parts[4:]):
if len(parts) > 5:
for idx, val in enumerate(parts[5:]):
try:
current_hist[idx] += int(val)
except ValueError:
Expand All @@ -95,17 +102,67 @@ def parse(self):
time_axis.append(float(parts[2]))
inside_run = False
current_hist = None
case "$ENV":
# $ENV,count,tm.tm_s100,T1,H1,T2,H2,T_MS5611,P_MS5611
if len(parts) >= 9:
try:
env_records.append((
float(parts[2]), # time
float(parts[3]), # T1 (SHT31 primary)
float(parts[4]), # H1
float(parts[5]), # T2 (SHT31 secondary)
float(parts[6]), # H2
float(parts[7]), # T_MS5611
float(parts[8]), # P_MS5611
))
except ValueError:
pass
case "$BATT":
# $BATT,count,tm.tm_s100,voltage_mV,current_mA,remaining_mAh,full_mAh,temp_C
if len(parts) >= 8:
try:
batt_records.append((
float(parts[2]), # time
float(parts[3]), # voltage_mV
float(parts[4]), # current_mA
float(parts[5]), # remaining_mAh
float(parts[6]), # full_charge_mAh
float(parts[7]), # temperature_C
))
except ValueError:
pass
case _:
continue

metadata["log_info"]["histogram_channels"] = hist.shape[0]
metadata["log_info"]["events_total"] = int(total_counts)
metadata["log_info"]["log_type_version"] = "2.0"
metadata["log_info"]["log_type"] = "xDOS_SPECTRAL"
metadata["log_info"]["detector_type"] = "AIRDOS04C"
print("Parsed AIRDOS04C format in", time.time() - start_time, "s")
metadata["log_info"]["detector_type"] = device_type

telemetry: dict = {}
if env_records:
ea = np.array(env_records)
telemetry["temperature_0"] = (ea[:, 0], ea[:, 1])
telemetry["humidity_0"] = (ea[:, 0], ea[:, 2])
telemetry["temperature_1"] = (ea[:, 0], ea[:, 3])
telemetry["humidity_1"] = (ea[:, 0], ea[:, 4])
telemetry["temperature_2"] = (ea[:, 0], ea[:, 5])
telemetry["pressure_3"] = (ea[:, 0], ea[:, 6])
if batt_records:
ba = np.array(batt_records)
telemetry["voltage"] = (ba[:, 0], ba[:, 1])
telemetry["current"] = (ba[:, 0], ba[:, 2])
telemetry["capacity_remaining"] = (ba[:, 0], ba[:, 3])
telemetry["capacity_full"] = (ba[:, 0], ba[:, 4])
telemetry["temperature"] = (ba[:, 0], ba[:, 5])

print("Parsed AIRDOS v2 format in", time.time() - start_time, "s")
return [np.array(time_axis), np.array(sums), hist, metadata, telemetry]


return [np.array(time_axis), np.array(sums), hist, metadata]
# Backwards-compatible alias
Airdos04CLogParser = AirdosV2LogParser


class OldLogParser(BaseLogParser):
Expand Down Expand Up @@ -203,7 +260,7 @@ def parse(self):
return [time_column, sums, hist, metadata]


LOG_PARSERS: Sequence[type[BaseLogParser]] = [Airdos04CLogParser, OldLogParser]
LOG_PARSERS: Sequence[type[BaseLogParser]] = [AirdosV2LogParser, OldLogParser]


def get_parser_for_file(file_path: str | Path) -> BaseLogParser:
Expand All @@ -220,7 +277,8 @@ def parse_file(file_path: str | Path):

__all__ = [
"BaseLogParser",
"Airdos04CLogParser",
"AirdosV2LogParser",
"Airdos04CLogParser", # backwards-compatible alias
"OldLogParser",
"get_parser_for_file",
"parse_file",
Expand Down
Loading