pymagnetos 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. pymagnetos/__init__.py +15 -0
  2. pymagnetos/cli.py +40 -0
  3. pymagnetos/core/__init__.py +19 -0
  4. pymagnetos/core/_config.py +340 -0
  5. pymagnetos/core/_data.py +132 -0
  6. pymagnetos/core/_processor.py +905 -0
  7. pymagnetos/core/config_models.py +57 -0
  8. pymagnetos/core/gui/__init__.py +6 -0
  9. pymagnetos/core/gui/_base_mainwindow.py +819 -0
  10. pymagnetos/core/gui/widgets/__init__.py +19 -0
  11. pymagnetos/core/gui/widgets/_batch_processing.py +319 -0
  12. pymagnetos/core/gui/widgets/_configuration.py +167 -0
  13. pymagnetos/core/gui/widgets/_files.py +129 -0
  14. pymagnetos/core/gui/widgets/_graphs.py +93 -0
  15. pymagnetos/core/gui/widgets/_param_content.py +20 -0
  16. pymagnetos/core/gui/widgets/_popup_progressbar.py +29 -0
  17. pymagnetos/core/gui/widgets/_text_logger.py +32 -0
  18. pymagnetos/core/signal_processing.py +1004 -0
  19. pymagnetos/core/utils.py +85 -0
  20. pymagnetos/log.py +126 -0
  21. pymagnetos/py.typed +0 -0
  22. pymagnetos/pytdo/__init__.py +6 -0
  23. pymagnetos/pytdo/_config.py +24 -0
  24. pymagnetos/pytdo/_config_models.py +59 -0
  25. pymagnetos/pytdo/_tdoprocessor.py +1052 -0
  26. pymagnetos/pytdo/assets/config_default.toml +84 -0
  27. pymagnetos/pytdo/gui/__init__.py +26 -0
  28. pymagnetos/pytdo/gui/_worker.py +106 -0
  29. pymagnetos/pytdo/gui/main.py +617 -0
  30. pymagnetos/pytdo/gui/widgets/__init__.py +8 -0
  31. pymagnetos/pytdo/gui/widgets/_buttons.py +66 -0
  32. pymagnetos/pytdo/gui/widgets/_configuration.py +78 -0
  33. pymagnetos/pytdo/gui/widgets/_graphs.py +280 -0
  34. pymagnetos/pytdo/gui/widgets/_param_content.py +137 -0
  35. pymagnetos/pyuson/__init__.py +7 -0
  36. pymagnetos/pyuson/_config.py +26 -0
  37. pymagnetos/pyuson/_config_models.py +71 -0
  38. pymagnetos/pyuson/_echoprocessor.py +1901 -0
  39. pymagnetos/pyuson/assets/config_default.toml +92 -0
  40. pymagnetos/pyuson/gui/__init__.py +26 -0
  41. pymagnetos/pyuson/gui/_worker.py +135 -0
  42. pymagnetos/pyuson/gui/main.py +767 -0
  43. pymagnetos/pyuson/gui/widgets/__init__.py +7 -0
  44. pymagnetos/pyuson/gui/widgets/_buttons.py +95 -0
  45. pymagnetos/pyuson/gui/widgets/_configuration.py +85 -0
  46. pymagnetos/pyuson/gui/widgets/_graphs.py +248 -0
  47. pymagnetos/pyuson/gui/widgets/_param_content.py +193 -0
  48. pymagnetos-0.1.0.dist-info/METADATA +23 -0
  49. pymagnetos-0.1.0.dist-info/RECORD +51 -0
  50. pymagnetos-0.1.0.dist-info/WHEEL +4 -0
  51. pymagnetos-0.1.0.dist-info/entry_points.txt +7 -0
@@ -0,0 +1,92 @@
1
+ # This configuration file specifies the default values if they are not filled in the
2
+ # per-experiment config.toml file.
3
+
4
+ expid = ""
5
+ data_directory = "." # path or "."
6
+
7
+ [files]
8
+ [files.pickup]
9
+ # Pickup coil binary file
10
+ ext = "-pickup.bin"
11
+ header = 0
12
+ precision = 8 # number of bytes/element
13
+ endian = "<" # endian, ">" for big-endian, "<" for little-endian
14
+ order = "F" # order style in the binary file, "F" for Fortran, "C" for C
15
+ [files.oscillo]
16
+ # Oscilloscope binary file
17
+ ext = ".bin"
18
+ header = 0
19
+ precision = 4 # number of bytes/element
20
+ endian = ">" # endian, ">" for big-endian, "<" for little-endian
21
+ order = "F" # order style in the binary file, "F" for Fortran, "C" for C
22
+ [files.reference_time]
23
+ # Reference time text file
24
+ ext = ".txt"
25
+ header = 8 # number of lines composing the header (data should start at header + 1)
26
+ delimiter = "\t"
27
+
28
+ [measurements]
29
+ # Measurements settings
30
+ # Link a physical measurement to a channel of the oscilloscope
31
+ # For oscillo data saved with LabVIEW, they correspond to the columns in the file (0-based)
32
+ # For WFM files they correspond to the file number (..._chX.wfm)
33
+ amplitude = 0
34
+ in_phase = 1
35
+ out_phase = 2
36
+
37
+ # Parameters are values that should be fixed once and for all for a given experiment
38
+ [parameters]
39
+ pickup_surface = 1 # surface of the pickup coil (m²)
40
+ pickup_samplerate = 1 # pickup coil acquisition rate (Hz)
41
+ sample_length = 1 # length of the sample (m)
42
+ sample_speed = 1 # (estimated) speed in the measured mode (m/s)
43
+ detection_mode = "reflection" # "reflection" or "transmission"
44
+
45
+ pickup_number = 1 # number of pickup(s) used and save in the pickup bin file
46
+ pickup_index = 0 # index of the pickup to use for analysis, 0-based
47
+ logamp_slope = 24.75e-3 # logarithmic amplificator slope
48
+
49
+ # Settings are analysis-specific and can be changed afterwards
50
+ [settings]
51
+ echo_index = 1 # number of the analyzed echo (1-based)
52
+ frame_indices = [0, 1] # frame index to plot
53
+ rolling_mean_wlen = 3 # rolling-average time window size (in indices)
54
+ rolling_mean_subsample = false # sub-sample resulting time trace
55
+ range_baseline = [0.01, 0.02] # range in which the baseline of amplitude and phase are estimated in seconds
56
+ analysis_window = [] # frame time-range in which to perform attenuation and phase-shift computation
57
+ max_phase_jump = 0.5 # maximum allowed phase-jump (multiplied by pi)
58
+
59
+ [metadata]
60
+ # Parameters on how to handle time reference text file (only for bin files)
61
+ [metadata.index_map]
62
+ # map a metadata to its line index in the header (0-based)
63
+ timestamp = 0
64
+ sample_name = 1
65
+ sample_angle = 2
66
+ polarisation = 3
67
+ rf_frequency = 4
68
+ dt_acq = 5
69
+ temperature = 6
70
+ [metadata.conversion_map]
71
+ # determine whether conversion to numeric is required
72
+ timestamp = false
73
+ sample_name = false
74
+ sample_angle = false
75
+ polarisation = false
76
+ rf_frequency = true
77
+ dt_acq = true
78
+ temperature = true
79
+
80
+ [nx]
81
+ # NeXus file consolidation
82
+
83
+ [nexus]
84
+ [nexus.groups]
85
+ # NeXus groups attributes
86
+ root = {name = ""}
87
+ # processed/analysis
88
+ analysis = {}
89
+ # data
90
+ data = {}
91
+ [nexus.datasets]
92
+ # NeXus datasets attributes
@@ -0,0 +1,26 @@
1
+ """Application for ultra-sound echoes experiments during high-magnetic field."""
2
+
3
+ from PyQt6 import QtWidgets
4
+
5
+ from .main import MainWindow
6
+
7
+
8
+ def run(exec: bool = True) -> None | MainWindow:
9
+ """
10
+ Build and run the app.
11
+
12
+ Set `exec=False` in an interactive prompt to interact with the GUI from the shell.
13
+
14
+ Parameters
15
+ ----------
16
+ exec : bool, optional
17
+ Execute the process thread. Set to False in an interactive prompt. Default is
18
+ True.
19
+ """
20
+ app = QtWidgets.QApplication([])
21
+ win = MainWindow()
22
+ win.show()
23
+ if exec:
24
+ app.exec()
25
+ else:
26
+ return win
@@ -0,0 +1,135 @@
1
+ """Worker that connects to an EchoProcessor object."""
2
+
3
+ from pathlib import Path
4
+
5
+ from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot
6
+
7
+ from pymagnetos.pyuson import EchoProcessor
8
+
9
+
10
+ class ProcessorWorker(QObject):
11
+ """
12
+ Wrapper around the EchoProcessor class.
13
+
14
+ It exposes the EchoProcessor methods as slots and emits a signal when the task is
15
+ finished.
16
+
17
+ pyqtSignals
18
+ -------
19
+ sig_load_finished : emits when loading data is finished.
20
+ sig_align_finished : emits when aligning magnetic field is finished.
21
+ sig_rolling_finished : emits when rolling average is finished.
22
+ sig_find_f0_finished : emits f0 when finding center frequency is finished.
23
+ sig_demodulate_finished : emits when demodulation is finished.
24
+ sig_average_finished : emits when averaging frames and computing is finished.
25
+ sig_batch_finished : emits when batch-processing is finished.
26
+
27
+ sig_export_csv_finished : emits when exporting as CSV is finished.
28
+ sig_save_nexus_finished : emits when saving as NeXus file is finished.
29
+
30
+ sig_demodulation_progress : emits step index during demodulation.
31
+ sig_batch_process_progress : emits step index during batch-processing.
32
+ """
33
+
34
+ sig_load_finished = pyqtSignal()
35
+ sig_align_finished = pyqtSignal()
36
+ sig_rolling_finished = pyqtSignal()
37
+ sig_find_f0_finished = pyqtSignal(float)
38
+ sig_demodulate_finished = pyqtSignal()
39
+ sig_average_finished = pyqtSignal(bool)
40
+ sig_batch_finished = pyqtSignal()
41
+
42
+ sig_export_csv_finished = pyqtSignal()
43
+ sig_save_nexus_finished = pyqtSignal()
44
+
45
+ sig_demodulation_progress = pyqtSignal(int)
46
+ sig_batch_progress = pyqtSignal(int)
47
+
48
+ def __init__(self, *args, **kwargs) -> None:
49
+ super().__init__()
50
+ self.proc = EchoProcessor(*args, **kwargs)
51
+ if self.proc.is_nexus_file:
52
+ # From file : data should already be loaded and ready to use
53
+ self.is_dataloaded = True
54
+ else:
55
+ self.is_dataloaded = False
56
+
57
+ @pyqtSlot()
58
+ def load_data(self) -> None:
59
+ """Load data."""
60
+ self.proc.load_oscillo(scale=True)
61
+ self.is_dataloaded = True
62
+ self.proc.align_field() # use the Processor method directly to not emit signal
63
+ self.sig_load_finished.emit()
64
+
65
+ @pyqtSlot()
66
+ def rolling_average(self) -> None:
67
+ """Apply moving average."""
68
+ self.proc.rolling_average()
69
+ self.sig_rolling_finished.emit()
70
+
71
+ def align_field(self) -> None:
72
+ """Align magnetic field on data."""
73
+ self.proc.align_field()
74
+ self.sig_align_finished.emit()
75
+
76
+ @pyqtSlot()
77
+ def find_f0(self) -> None:
78
+ """Find F0."""
79
+ self.proc.find_f0()
80
+ self.sig_find_f0_finished.emit(self.proc.metadata["rf_frequency"])
81
+
82
+ @pyqtSlot()
83
+ def demodulate(self) -> None:
84
+ """Apply digital demodulation."""
85
+ self.proc.demodulate(progress_emitter=self.sig_demodulation_progress)
86
+ self.sig_demodulate_finished.emit()
87
+
88
+ @pyqtSlot()
89
+ def average_frame(self) -> None:
90
+ """Average in the time window, comptue attenuation and phase-shift."""
91
+ self.proc.average_frame_range().compute_attenuation().compute_phase_shift()
92
+ # Check it went fine
93
+ if self.proc.is_averaged:
94
+ self.sig_average_finished.emit(True)
95
+ else:
96
+ self.sig_average_finished.emit(False)
97
+
98
+ @pyqtSlot(list, bool, bool, bool, bool)
99
+ def batch_process(
100
+ self,
101
+ expids: list,
102
+ rolling_average: bool,
103
+ save_csv: bool,
104
+ to_cm: bool,
105
+ find_f0: bool,
106
+ ) -> None:
107
+ """Batch-process several datasets."""
108
+ self.proc.batch_process(
109
+ expids,
110
+ rolling_average=rolling_average,
111
+ save_csv=save_csv,
112
+ save_csv_kwargs=dict(to_cm=to_cm),
113
+ find_f0=find_f0,
114
+ batch_progress_emitter=self.sig_batch_progress,
115
+ demodulation_progress_emitter=self.sig_demodulation_progress,
116
+ )
117
+
118
+ # Emit signals
119
+ self.sig_load_finished.emit()
120
+ if self.proc.is_digital:
121
+ self.sig_demodulate_finished.emit()
122
+ self.sig_average_finished.emit(True)
123
+ self.sig_batch_finished.emit()
124
+
125
+ @pyqtSlot(str, bool)
126
+ def export_as_csv(self, fname: str, to_cm: bool) -> None:
127
+ """Export results as CSV."""
128
+ self.proc.to_csv(fname=fname, to_cm=to_cm)
129
+ self.sig_export_csv_finished.emit()
130
+
131
+ @pyqtSlot(str)
132
+ def save_as_nexus(self, fname: str | Path) -> None:
133
+ """Save data as NeXus."""
134
+ self.proc.save(filename=fname)
135
+ self.sig_save_nexus_finished.emit()