readerbotda 1.3.3__tar.gz → 1.4.0__tar.gz
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.
- {readerbotda-1.3.3 → readerbotda-1.4.0}/PKG-INFO +25 -4
- {readerbotda-1.3.3 → readerbotda-1.4.0}/README.md +22 -3
- readerbotda-1.4.0/ReaderBOTDA/reader.py +389 -0
- {readerbotda-1.3.3 → readerbotda-1.4.0}/pyproject.toml +7 -2
- readerbotda-1.3.3/ReaderBOTDA/reader.py +0 -261
- {readerbotda-1.3.3 → readerbotda-1.4.0}/ReaderBOTDA/__init__.py +0 -0
- {readerbotda-1.3.3 → readerbotda-1.4.0}/ReaderBOTDA/converter.py +0 -0
- {readerbotda-1.3.3 → readerbotda-1.4.0}/ReaderBOTDA/plotter/__init__.py +0 -0
- {readerbotda-1.3.3 → readerbotda-1.4.0}/ReaderBOTDA/plotter/abc.py +0 -0
- {readerbotda-1.3.3 → readerbotda-1.4.0}/ReaderBOTDA/plotter/bokeh.py +0 -0
- {readerbotda-1.3.3 → readerbotda-1.4.0}/ReaderBOTDA/plotter/plotly.py +0 -0
- {readerbotda-1.3.3 → readerbotda-1.4.0}/ReaderBOTDA/script.py +0 -0
- {readerbotda-1.3.3 → readerbotda-1.4.0}/ReaderBOTDA/settings.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: readerbotda
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Pacchetto per lettura e visualizzazione file di log sensore BOTDA Cohaerentia
|
|
5
5
|
Author: Marco Brunero
|
|
6
6
|
Author-email: marco.brunero@cohaerentia.com
|
|
@@ -11,6 +11,8 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.13
|
|
12
12
|
Requires-Dist: bokeh (>=3.7.2,<4.0.0)
|
|
13
13
|
Requires-Dist: dacite (>=1.9.2,<2.0.0)
|
|
14
|
+
Requires-Dist: h5py (>=3.14.0,<4.0.0)
|
|
15
|
+
Requires-Dist: nbformat (>=5.10.4,<6.0.0)
|
|
14
16
|
Requires-Dist: numpy (>=2.2.4,<3.0.0)
|
|
15
17
|
Requires-Dist: plotly (>=6.0.1,<7.0.0)
|
|
16
18
|
Requires-Dist: progress (>=1.6,<2.0)
|
|
@@ -52,7 +54,7 @@ L'idea è che chiunque potrebbe creare una nuova classe parente della classe `Pl
|
|
|
52
54
|
|
|
53
55
|
### Misura Singola
|
|
54
56
|
|
|
55
|
-
Lettura di singola misura da un singolo file "profilo".
|
|
57
|
+
Lettura di singola misura da un singolo file "profilo" di tipo json.
|
|
56
58
|
|
|
57
59
|
```python
|
|
58
60
|
misura = Profile('data/profiles/2021-11-08_16-51-16.652_rawarray.json',plotter=plotter)
|
|
@@ -64,7 +66,7 @@ fig.write_html("prova.html", full_html=False, include_plotlyjs='cdn')
|
|
|
64
66
|
|
|
65
67
|
### Misure multiple in una cartella
|
|
66
68
|
|
|
67
|
-
Lettura di tutti i file di tipo profilo in una cartella:
|
|
69
|
+
Lettura di tutti i file di tipo profilo in una cartella, contenente files di tipo json:
|
|
68
70
|
|
|
69
71
|
```python
|
|
70
72
|
from datetime import datetime
|
|
@@ -119,7 +121,7 @@ Si utilizza la funzione [`np.corrcoef`](https://numpy.org/doc/stable/reference/g
|
|
|
119
121
|
|
|
120
122
|
### Misura Raw
|
|
121
123
|
|
|
122
|
-
Lettura di file di debug che contiene intera matrice BGS e test dei metodi disponibili.
|
|
124
|
+
Lettura di file json di debug che contiene intera matrice BGS e test dei metodi disponibili.
|
|
123
125
|
|
|
124
126
|
```python
|
|
125
127
|
raw = Raw(filename='data/raw/2021-11-08_16-51-16.652_rawmatrix.json', plotter=plotter)
|
|
@@ -135,6 +137,18 @@ fig = raw.plotBGS(index=125)
|
|
|
135
137
|
plotter.show(fig)
|
|
136
138
|
```
|
|
137
139
|
|
|
140
|
+
## Lettura di misure in singolo file H5
|
|
141
|
+
|
|
142
|
+
Per leggere misure salvate in un file H5, è possibile utilizzare la classe `h5Profile`:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from ReaderBOTDA.reader import h5Profile
|
|
146
|
+
|
|
147
|
+
h5 = h5Profile('data/2023_09/build1.3_profile.h5', plotter=plotter)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
A questo punto l'oggetto `h5Profile` si dovrebbe comportare come `multipleProfile`, quindi è possibile utilizzare i metodi `plot()`, `calcStatistics()`, `calcCorrelations()` e così via.
|
|
151
|
+
|
|
138
152
|
## Plotter Bokeh
|
|
139
153
|
|
|
140
154
|
Oltre al plotter Plotly è disponibile anche una versione che utilizza il pacchetto `bokeh`. Non sono al momento disponibili i metodi `Bokeh.plot2d()` e `Bokeh.plot3d()`.
|
|
@@ -157,3 +171,10 @@ fig = misura.plot()
|
|
|
157
171
|
plotter.show(fig)
|
|
158
172
|
```
|
|
159
173
|
|
|
174
|
+
## TODO
|
|
175
|
+
|
|
176
|
+
1. lettura misure raw da file H5.
|
|
177
|
+
2. sistemare settings nelle ultime versioni di sw.
|
|
178
|
+
3. sistemare la timezone nelle misure.
|
|
179
|
+
4. aggiungere ricalcolo stima BFS a partire da dati raw.
|
|
180
|
+
|
|
@@ -33,7 +33,7 @@ L'idea è che chiunque potrebbe creare una nuova classe parente della classe `Pl
|
|
|
33
33
|
|
|
34
34
|
### Misura Singola
|
|
35
35
|
|
|
36
|
-
Lettura di singola misura da un singolo file "profilo".
|
|
36
|
+
Lettura di singola misura da un singolo file "profilo" di tipo json.
|
|
37
37
|
|
|
38
38
|
```python
|
|
39
39
|
misura = Profile('data/profiles/2021-11-08_16-51-16.652_rawarray.json',plotter=plotter)
|
|
@@ -45,7 +45,7 @@ fig.write_html("prova.html", full_html=False, include_plotlyjs='cdn')
|
|
|
45
45
|
|
|
46
46
|
### Misure multiple in una cartella
|
|
47
47
|
|
|
48
|
-
Lettura di tutti i file di tipo profilo in una cartella:
|
|
48
|
+
Lettura di tutti i file di tipo profilo in una cartella, contenente files di tipo json:
|
|
49
49
|
|
|
50
50
|
```python
|
|
51
51
|
from datetime import datetime
|
|
@@ -100,7 +100,7 @@ Si utilizza la funzione [`np.corrcoef`](https://numpy.org/doc/stable/reference/g
|
|
|
100
100
|
|
|
101
101
|
### Misura Raw
|
|
102
102
|
|
|
103
|
-
Lettura di file di debug che contiene intera matrice BGS e test dei metodi disponibili.
|
|
103
|
+
Lettura di file json di debug che contiene intera matrice BGS e test dei metodi disponibili.
|
|
104
104
|
|
|
105
105
|
```python
|
|
106
106
|
raw = Raw(filename='data/raw/2021-11-08_16-51-16.652_rawmatrix.json', plotter=plotter)
|
|
@@ -116,6 +116,18 @@ fig = raw.plotBGS(index=125)
|
|
|
116
116
|
plotter.show(fig)
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
+
## Lettura di misure in singolo file H5
|
|
120
|
+
|
|
121
|
+
Per leggere misure salvate in un file H5, è possibile utilizzare la classe `h5Profile`:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from ReaderBOTDA.reader import h5Profile
|
|
125
|
+
|
|
126
|
+
h5 = h5Profile('data/2023_09/build1.3_profile.h5', plotter=plotter)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
A questo punto l'oggetto `h5Profile` si dovrebbe comportare come `multipleProfile`, quindi è possibile utilizzare i metodi `plot()`, `calcStatistics()`, `calcCorrelations()` e così via.
|
|
130
|
+
|
|
119
131
|
## Plotter Bokeh
|
|
120
132
|
|
|
121
133
|
Oltre al plotter Plotly è disponibile anche una versione che utilizza il pacchetto `bokeh`. Non sono al momento disponibili i metodi `Bokeh.plot2d()` e `Bokeh.plot3d()`.
|
|
@@ -137,3 +149,10 @@ misura = Profile('data/profiles/2021-11-08_16-51-16.652_rawarray.json',plotter=p
|
|
|
137
149
|
fig = misura.plot()
|
|
138
150
|
plotter.show(fig)
|
|
139
151
|
```
|
|
152
|
+
|
|
153
|
+
## TODO
|
|
154
|
+
|
|
155
|
+
1. lettura misure raw da file H5.
|
|
156
|
+
2. sistemare settings nelle ultime versioni di sw.
|
|
157
|
+
3. sistemare la timezone nelle misure.
|
|
158
|
+
4. aggiungere ricalcolo stima BFS a partire da dati raw.
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
from ReaderBOTDA.settings import parseSettingsDict, Settings
|
|
2
|
+
from ReaderBOTDA.plotter.abc import Plotter
|
|
3
|
+
from ReaderBOTDA.plotter.plotly import Plotly
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from json import load, loads
|
|
8
|
+
import numpy as np
|
|
9
|
+
import numpy.typing as npt
|
|
10
|
+
import h5py
|
|
11
|
+
from datetime import datetime, timezone # TODO usare numpy anche per questo?
|
|
12
|
+
from os import path, PathLike, strerror
|
|
13
|
+
from glob import glob
|
|
14
|
+
from typing import Tuple, Union, Literal
|
|
15
|
+
import errno
|
|
16
|
+
import warnings
|
|
17
|
+
from progress.bar import Bar
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NoFilesSelected(Exception):
|
|
21
|
+
def __init__(self, folder, message="No file found in folder"):
|
|
22
|
+
self.message = f"{message}: {folder}"
|
|
23
|
+
super().__init__(self.message)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# TODO da implementare nei metodi. E aggiungere anche i campi MaxMean e MaxStd
|
|
27
|
+
@dataclass
|
|
28
|
+
class Statistics:
|
|
29
|
+
BFSmean: npt.ArrayLike
|
|
30
|
+
BFSstd: npt.ArrayLike
|
|
31
|
+
BFSstd_mean: float
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Profile:
|
|
35
|
+
|
|
36
|
+
filename: str = ""
|
|
37
|
+
plotter: Plotter
|
|
38
|
+
settings: Settings
|
|
39
|
+
sw_version: str = "<1.2.0.0"
|
|
40
|
+
BFS: np.ndarray = np.array([])
|
|
41
|
+
MaxGain: np.ndarray = np.array([])
|
|
42
|
+
timestamp: datetime = datetime.now
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self, filename: PathLike, plotter: Plotter = Plotly(), name: str = None
|
|
46
|
+
) -> None:
|
|
47
|
+
"""Load a single measure from a json file."""
|
|
48
|
+
self.filename = filename
|
|
49
|
+
self.plotter = plotter
|
|
50
|
+
with open(self.filename) as file:
|
|
51
|
+
text = load(file)
|
|
52
|
+
self.settings = parseSettingsDict(text["Settings"])
|
|
53
|
+
if "sw_version" in text:
|
|
54
|
+
self.sw_version = text["sw_version"]
|
|
55
|
+
if "sw_version" in text and text["sw_version"] == "":
|
|
56
|
+
self.sw_version = "ambiente sviluppo"
|
|
57
|
+
self.timestamp = datetime.strptime(
|
|
58
|
+
text["Time Stamp"], "%Y-%m-%d" + "T" + "%H:%M:%S.%f" + "Z"
|
|
59
|
+
) # "2021-11-08T16:51:16.652Z"
|
|
60
|
+
if not name:
|
|
61
|
+
self.name = self.timestamp.strftime("%m/%d/%Y, %H:%M:%S")
|
|
62
|
+
else:
|
|
63
|
+
self.name = name
|
|
64
|
+
|
|
65
|
+
self.BFS = np.array(text["Profile"])
|
|
66
|
+
self._createPositionArray()
|
|
67
|
+
if "arrayMaxGain" in text:
|
|
68
|
+
self.MaxGain = np.array(text["arrayMaxGain"])
|
|
69
|
+
|
|
70
|
+
def _createPositionArray(self):
|
|
71
|
+
self.position = np.linspace(
|
|
72
|
+
0, self.settings.Cable.Length, num=len(self.BFS), endpoint=True
|
|
73
|
+
)
|
|
74
|
+
self.spatialResolution = self.position[1]
|
|
75
|
+
|
|
76
|
+
def plot(self, title: str = None):
|
|
77
|
+
if not title:
|
|
78
|
+
title = self.name
|
|
79
|
+
# TODO usare xrange espresso in metri per affettare array e poi passarli a single_plot. xrange:Tuple[float,float]=None
|
|
80
|
+
return self.plotter.single_plot(self.position, self.BFS, title=title)
|
|
81
|
+
|
|
82
|
+
def plotMax(self, title: str = None):
|
|
83
|
+
if self.MaxGain.size == 0:
|
|
84
|
+
warnings.warn(
|
|
85
|
+
"Max array in not available. If possibile, load the raw file of the same measure."
|
|
86
|
+
)
|
|
87
|
+
return None
|
|
88
|
+
if not title:
|
|
89
|
+
title = self.name
|
|
90
|
+
return self.plotter.max_plot(self.position, self.MaxGain, title)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class multipleProfile:
|
|
94
|
+
|
|
95
|
+
statistics: Statistics
|
|
96
|
+
plotter: Plotter
|
|
97
|
+
|
|
98
|
+
def __init__(
|
|
99
|
+
self,
|
|
100
|
+
folder: PathLike,
|
|
101
|
+
plotter: Plotter = Plotly(),
|
|
102
|
+
n_measure: int = None,
|
|
103
|
+
start_measure: Union[int, datetime] = 0,
|
|
104
|
+
stop_measure: datetime = None,
|
|
105
|
+
):
|
|
106
|
+
|
|
107
|
+
self.folder = folder
|
|
108
|
+
self.plotter = plotter
|
|
109
|
+
|
|
110
|
+
filelist = glob(path.join(folder, "*.json"))
|
|
111
|
+
if start_measure and isinstance(start_measure, datetime):
|
|
112
|
+
timestamps_files = [
|
|
113
|
+
datetime.strptime(
|
|
114
|
+
"_".join(path.basename(file).split("_")[:2]), "%Y-%m-%d_%H-%M-%S.%f"
|
|
115
|
+
)
|
|
116
|
+
for file in filelist
|
|
117
|
+
]
|
|
118
|
+
primo = next(
|
|
119
|
+
(
|
|
120
|
+
x
|
|
121
|
+
for x, value in enumerate(timestamps_files)
|
|
122
|
+
if value >= start_measure
|
|
123
|
+
),
|
|
124
|
+
0,
|
|
125
|
+
)
|
|
126
|
+
if stop_measure:
|
|
127
|
+
ultimo = next(
|
|
128
|
+
(
|
|
129
|
+
x
|
|
130
|
+
for x, value in enumerate(timestamps_files)
|
|
131
|
+
if value > stop_measure
|
|
132
|
+
),
|
|
133
|
+
len(filelist),
|
|
134
|
+
)
|
|
135
|
+
elif n_measure:
|
|
136
|
+
ultimo = primo + n_measure
|
|
137
|
+
else:
|
|
138
|
+
ultimo = len(filelist)
|
|
139
|
+
filelist = filelist[primo:ultimo]
|
|
140
|
+
|
|
141
|
+
if n_measure and (start_measure == 0 or isinstance(start_measure, int)):
|
|
142
|
+
filelist = filelist[start_measure : start_measure + n_measure]
|
|
143
|
+
|
|
144
|
+
if isinstance(start_measure, int) and not n_measure and stop_measure:
|
|
145
|
+
timestamps_files = [
|
|
146
|
+
datetime.strptime(
|
|
147
|
+
"_".join(path.basename(file).split("_")[:2]), "%Y-%m-%d_%H-%M-%S.%f"
|
|
148
|
+
)
|
|
149
|
+
for file in filelist
|
|
150
|
+
]
|
|
151
|
+
ultimo = next(
|
|
152
|
+
(x for x, value in enumerate(timestamps_files) if value > stop_measure),
|
|
153
|
+
len(filelist),
|
|
154
|
+
)
|
|
155
|
+
filelist = filelist[start_measure:ultimo]
|
|
156
|
+
|
|
157
|
+
if len(filelist) == 0:
|
|
158
|
+
raise NoFilesSelected(folder=folder)
|
|
159
|
+
|
|
160
|
+
timestamps = list()
|
|
161
|
+
with Bar("Redaing files", max=len(filelist)) as bar:
|
|
162
|
+
for file in filelist:
|
|
163
|
+
temp = Profile(filename=file)
|
|
164
|
+
timestamps.append(temp.timestamp)
|
|
165
|
+
try:
|
|
166
|
+
self.BFS = np.column_stack((self.BFS, temp.BFS))
|
|
167
|
+
self.MaxGain = np.column_stack((self.MaxGain, temp.MaxGain))
|
|
168
|
+
except AttributeError:
|
|
169
|
+
self.BFS = temp.BFS
|
|
170
|
+
self.MaxGain = temp.MaxGain
|
|
171
|
+
bar.next()
|
|
172
|
+
|
|
173
|
+
self.timestamps = np.array(timestamps)
|
|
174
|
+
self.settings = temp.settings
|
|
175
|
+
self.sw_version = temp.sw_version
|
|
176
|
+
self.position = temp.position
|
|
177
|
+
self.calcStatistics()
|
|
178
|
+
self.MaxGainMean = self.MaxGain.mean(axis=1)
|
|
179
|
+
self.MaxGainStd = self.MaxGain.std(axis=1)
|
|
180
|
+
|
|
181
|
+
def calcCorrelations(
|
|
182
|
+
self,
|
|
183
|
+
type: Literal["max", "bfs"],
|
|
184
|
+
reference: Literal["first", "previous"] = "previous",
|
|
185
|
+
range: Tuple[float, float] = None,
|
|
186
|
+
) -> np.array:
|
|
187
|
+
"""Ritorna correlazione tra prima misura e misura n-esima.
|
|
188
|
+
Si può scegliere se effettuarla su matrice dei BFS o matrice dei massimi"""
|
|
189
|
+
|
|
190
|
+
if type == "max":
|
|
191
|
+
correlations = np.corrcoef(
|
|
192
|
+
self.MaxGain[range[0] : range[1]] if range else self.MaxGain,
|
|
193
|
+
rowvar=False,
|
|
194
|
+
)
|
|
195
|
+
else:
|
|
196
|
+
correlations = np.corrcoef(
|
|
197
|
+
self.BFS[range[0] : range[1]] if range else self.BFS, rowvar=False
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if reference == "first":
|
|
201
|
+
return correlations[0, :]
|
|
202
|
+
|
|
203
|
+
indici = np.arange(1, np.shape(correlations)[0])
|
|
204
|
+
return np.insert(correlations[indici, indici - 1], 0, 1)
|
|
205
|
+
|
|
206
|
+
def calcStatistics(
|
|
207
|
+
self, plot: bool = False, range: Tuple[float, float] = None, title: str = None
|
|
208
|
+
) -> Statistics:
|
|
209
|
+
# TODO gestione input range
|
|
210
|
+
self.statistics = Statistics(
|
|
211
|
+
BFSmean=self.BFS.mean(axis=1),
|
|
212
|
+
BFSstd=self.BFS.std(axis=1),
|
|
213
|
+
BFSstd_mean=self.BFS.std(axis=1).mean(),
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
if plot:
|
|
217
|
+
if not title:
|
|
218
|
+
title = self.folder
|
|
219
|
+
return self.plotter.statistics(
|
|
220
|
+
self.position,
|
|
221
|
+
self.statistics.BFSmean,
|
|
222
|
+
self.statistics.BFSstd,
|
|
223
|
+
title=title,
|
|
224
|
+
)
|
|
225
|
+
return self.statistics
|
|
226
|
+
|
|
227
|
+
def plotStatistics(self, title: str = None):
|
|
228
|
+
if not title:
|
|
229
|
+
title = self.folder
|
|
230
|
+
# TODO se sono state calcolate con range allora position è sbagliato
|
|
231
|
+
return self.plotter.statistics(
|
|
232
|
+
self.position, self.statistics.BFSmean, self.statistics.BFSstd, title=title
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def plot(
|
|
236
|
+
self,
|
|
237
|
+
startTime: datetime = datetime.min,
|
|
238
|
+
stopTime: datetime = datetime.now(),
|
|
239
|
+
title: str = None,
|
|
240
|
+
):
|
|
241
|
+
if not title:
|
|
242
|
+
title = self.folder
|
|
243
|
+
match = [
|
|
244
|
+
i
|
|
245
|
+
for i, date in enumerate(self.timestamps)
|
|
246
|
+
if date >= startTime and date <= stopTime
|
|
247
|
+
]
|
|
248
|
+
return self.plotter.multiple_plot(
|
|
249
|
+
self.position,
|
|
250
|
+
self.BFS[:, match],
|
|
251
|
+
[self.timestamps[i] for i in match],
|
|
252
|
+
title=title,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
def plotMax(self, title: str = None):
|
|
256
|
+
if self.MaxGain.size == 0:
|
|
257
|
+
warnings.warn(
|
|
258
|
+
"Max array in not available. If possibile, load the raw file of the same measure."
|
|
259
|
+
)
|
|
260
|
+
return None
|
|
261
|
+
if not title:
|
|
262
|
+
title = self.folder
|
|
263
|
+
return self.plotter.max_stat_plot(self.MaxGainMean, self.MaxGainStd, title)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class h5Profile(multipleProfile):
|
|
267
|
+
|
|
268
|
+
def __init__(
|
|
269
|
+
self,
|
|
270
|
+
filename: PathLike,
|
|
271
|
+
plotter: Plotter = Plotly(),
|
|
272
|
+
# n_measure: int = None,
|
|
273
|
+
# start_measure: Union[int, datetime] = 0,
|
|
274
|
+
# stop_measure: datetime = None,
|
|
275
|
+
):
|
|
276
|
+
"""Load multiple measures from a h5 file."""
|
|
277
|
+
self.filename = filename
|
|
278
|
+
self.plotter = plotter
|
|
279
|
+
|
|
280
|
+
if not path.isfile(filename):
|
|
281
|
+
raise FileNotFoundError(errno.ENOENT, strerror(errno.ENOENT), filename)
|
|
282
|
+
|
|
283
|
+
with h5py.File(filename, "r") as f:
|
|
284
|
+
self.filename = filename
|
|
285
|
+
self.folder = f.attrs["folder"]
|
|
286
|
+
self.sw_version = f.attrs["sw_version"]
|
|
287
|
+
|
|
288
|
+
self.position = f["position"][:]
|
|
289
|
+
self.BFS = np.transpose(np.squeeze(f["data"]["bfs"][:]))
|
|
290
|
+
self.MaxGain = np.transpose(np.squeeze(f["data"]["max_gain"][:]))
|
|
291
|
+
self.timestamps = [
|
|
292
|
+
datetime.fromtimestamp(timestamp / 1000000) # FIXME, timezone.utc)
|
|
293
|
+
for timestamp in f["data"]["timestamps"][:]
|
|
294
|
+
]
|
|
295
|
+
self.settings = loads(
|
|
296
|
+
f.attrs["settings"]
|
|
297
|
+
) # FIXME parseSettingsDict(loads(f.attrs["settings"]))
|
|
298
|
+
|
|
299
|
+
self.calcStatistics()
|
|
300
|
+
self.MaxGainMean = self.MaxGain.mean(axis=1)
|
|
301
|
+
self.MaxGainStd = self.MaxGain.std(axis=1)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class Raw:
|
|
305
|
+
|
|
306
|
+
filename: PathLike = Path("")
|
|
307
|
+
plotter: Plotter
|
|
308
|
+
settings: Settings
|
|
309
|
+
sw_version: str = "<1.2.0.0"
|
|
310
|
+
BGS: np.ndarray = np.array([])
|
|
311
|
+
residuo: np.ndarray = np.array([])
|
|
312
|
+
timestamp: datetime = datetime.now
|
|
313
|
+
|
|
314
|
+
def __init__(self, filename: Union[str, Path], plotter: Plotter = Plotly()) -> None:
|
|
315
|
+
"""Load a single raw data from a json file."""
|
|
316
|
+
self.filename = filename
|
|
317
|
+
self.plotter = plotter
|
|
318
|
+
with open(self.filename) as file:
|
|
319
|
+
text = load(file)
|
|
320
|
+
self.settings = parseSettingsDict(text["Settings"])
|
|
321
|
+
if "sw_version" in text:
|
|
322
|
+
self.sw_version = text["sw_version"]
|
|
323
|
+
if "sw_version" in text and text["sw_version"] == "":
|
|
324
|
+
self.sw_version = "ambiente sviluppo"
|
|
325
|
+
self.BGS = np.transpose(np.array(text["Raw"]))
|
|
326
|
+
self.timestamp = datetime.strptime(
|
|
327
|
+
text["Time Stamp"], "%Y-%m-%d" + "T" + "%H:%M:%S.%f" + "Z"
|
|
328
|
+
) # "2021-11-08T16:51:16.652Z"
|
|
329
|
+
self._createPositionArray()
|
|
330
|
+
self._createFrequencyArray()
|
|
331
|
+
try:
|
|
332
|
+
self.residuo = np.array(text["residuo"])
|
|
333
|
+
except KeyError:
|
|
334
|
+
self.residuo = np.zeros(np.shape(self.frequency))
|
|
335
|
+
|
|
336
|
+
def _createPositionArray(self) -> None:
|
|
337
|
+
"""Crea array numpy delle posizioni e la risoluzione spaziale"""
|
|
338
|
+
self.position = np.linspace(
|
|
339
|
+
0, self.settings.Cable.Length, num=self.BGS.shape[1], endpoint=True
|
|
340
|
+
)
|
|
341
|
+
self.spatialResolution = self.position[1]
|
|
342
|
+
|
|
343
|
+
def _createFrequencyArray(self) -> None:
|
|
344
|
+
"""Crea array numpy delle frequenze, espresse in GHz"""
|
|
345
|
+
self.frequency = (
|
|
346
|
+
np.linspace(
|
|
347
|
+
self.settings.Clock.StartMHz,
|
|
348
|
+
self.settings.Clock.StartMHz
|
|
349
|
+
+ self.settings.Clock.StepMHz * self.BGS.shape[0],
|
|
350
|
+
num=self.BGS.shape[0],
|
|
351
|
+
endpoint=True,
|
|
352
|
+
)
|
|
353
|
+
/ 1000
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
def plot2d(self, title: str = None):
|
|
357
|
+
if not title:
|
|
358
|
+
title = self.filename
|
|
359
|
+
return self.plotter.raw2d_plot(
|
|
360
|
+
self.position, self.frequency, self.BGS, title=title
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
def plot3d(self, title: str = None):
|
|
364
|
+
if not title:
|
|
365
|
+
title = self.filename
|
|
366
|
+
return self.plotter.raw3d_plot(
|
|
367
|
+
self.position, self.frequency, self.BGS, title=title
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
def plotBGS(self, index: int = None, title: str = None):
|
|
371
|
+
"""Plot 2D di tutti gli spettri BGS"""
|
|
372
|
+
if not title:
|
|
373
|
+
title = self.filename
|
|
374
|
+
return self.plotter.rawBGS_plot(
|
|
375
|
+
self.frequency, self.BGS, index=index, title=title
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
def plotMax(self, title: str = None):
|
|
379
|
+
if not title:
|
|
380
|
+
title = self.filename
|
|
381
|
+
return self.plotter.max_plot(self.position, self.BGS.max(axis=0), title)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
if __name__ == "__main__":
|
|
385
|
+
"""Non va se si lancia direttamente questo script a meno di non cambiare i primi due import da modules.settings a settings; uguale per plotter."""
|
|
386
|
+
# a = Profile(filename='data/profiles/2021-11-08_16-51-16.652_rawarray.json')
|
|
387
|
+
# b = multipleProfile(folder='data/profiles/')
|
|
388
|
+
c = Raw(filename="data/raw/2021-11-08_16-51-16.652_rawmatrix.json")
|
|
389
|
+
# print(b.BFS.shape)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "readerbotda"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.4.0"
|
|
4
4
|
description = "Pacchetto per lettura e visualizzazione file di log sensore BOTDA Cohaerentia"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Marco Brunero",email = "marco.brunero@cohaerentia.com"}
|
|
@@ -13,12 +13,17 @@ dependencies = [
|
|
|
13
13
|
"numpy (>=2.2.4,<3.0.0)",
|
|
14
14
|
"bokeh (>=3.7.2,<4.0.0)",
|
|
15
15
|
"typer (>=0.15.2,<0.16.0)",
|
|
16
|
-
"progress (>=1.6,<2.0)"
|
|
16
|
+
"progress (>=1.6,<2.0)",
|
|
17
|
+
"h5py (>=3.14.0,<4.0.0)",
|
|
18
|
+
"nbformat (>=5.10.4,<6.0.0)"
|
|
17
19
|
]
|
|
18
20
|
|
|
19
21
|
[tool.poetry.scripts]
|
|
20
22
|
readBOTDA = "ReaderBOTDA.script:app"
|
|
21
23
|
|
|
24
|
+
[tool.poetry.group.dev.dependencies]
|
|
25
|
+
ipykernel = "^6.29.5"
|
|
26
|
+
|
|
22
27
|
[build-system]
|
|
23
28
|
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
24
29
|
build-backend = "poetry.core.masonry.api"
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
from ReaderBOTDA.settings import parseSettingsDict, Settings
|
|
2
|
-
from ReaderBOTDA.plotter.abc import Plotter
|
|
3
|
-
from ReaderBOTDA.plotter.plotly import Plotly
|
|
4
|
-
|
|
5
|
-
from dataclasses import dataclass
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from json import load
|
|
8
|
-
import numpy as np
|
|
9
|
-
from datetime import datetime # TODO usare numpy anche per questo?
|
|
10
|
-
from os import path
|
|
11
|
-
from glob import glob
|
|
12
|
-
from typing import Tuple, Union, Literal
|
|
13
|
-
import warnings
|
|
14
|
-
from progress.bar import Bar
|
|
15
|
-
|
|
16
|
-
class NoFilesSelected(Exception):
|
|
17
|
-
def __init__(self, folder, message='No file found in folder'):
|
|
18
|
-
self.message = f'{message}: {folder}'
|
|
19
|
-
super().__init__(self.message)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
#TODO da implementare nei metodi. E aggiungere anche i campi MaxMean e MaxStd
|
|
24
|
-
@dataclass
|
|
25
|
-
class Statistics:
|
|
26
|
-
BFSmean: np.array([])
|
|
27
|
-
BFSstd: np.array([])
|
|
28
|
-
BFSstd_mean: float
|
|
29
|
-
|
|
30
|
-
class Profile:
|
|
31
|
-
|
|
32
|
-
filename: str = ''
|
|
33
|
-
plotter: Plotter
|
|
34
|
-
settings: Settings
|
|
35
|
-
sw_version: str = '<1.2.0.0'
|
|
36
|
-
BFS: np.ndarray = np.array([])
|
|
37
|
-
MaxGain: np.ndarray = np.array([])
|
|
38
|
-
timestamp: datetime = datetime.now
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def __init__(self, filename: Union[str,Path], plotter: Plotter = Plotly(), name: str = None) -> None:
|
|
42
|
-
self.filename = filename
|
|
43
|
-
self.plotter = plotter
|
|
44
|
-
with open(self.filename) as file:
|
|
45
|
-
text = load(file)
|
|
46
|
-
self.settings = parseSettingsDict(text['Settings'])
|
|
47
|
-
if 'sw_version' in text:
|
|
48
|
-
self.sw_version = text['sw_version']
|
|
49
|
-
if 'sw_version' in text and text['sw_version'] == '':
|
|
50
|
-
self.sw_version = 'ambiente sviluppo'
|
|
51
|
-
self.timestamp = datetime.strptime(
|
|
52
|
-
text['Time Stamp'], '%Y-%m-%d'+'T'+'%H:%M:%S.%f'+'Z') # "2021-11-08T16:51:16.652Z"
|
|
53
|
-
if not name:
|
|
54
|
-
self.name = self.timestamp.strftime("%m/%d/%Y, %H:%M:%S")
|
|
55
|
-
else:
|
|
56
|
-
self.name = name
|
|
57
|
-
|
|
58
|
-
self.BFS = np.array(text['Profile'])
|
|
59
|
-
self._createPositionArray()
|
|
60
|
-
if 'arrayMaxGain' in text:
|
|
61
|
-
self.MaxGain = np.array(text['arrayMaxGain'])
|
|
62
|
-
|
|
63
|
-
def _createPositionArray(self):
|
|
64
|
-
self.position = np.linspace(
|
|
65
|
-
0, self.settings.Cable.Length, num=len(self.BFS), endpoint=True)
|
|
66
|
-
self.spatialResolution = self.position[1]
|
|
67
|
-
|
|
68
|
-
def plot(self,title:str = None):
|
|
69
|
-
if not title:
|
|
70
|
-
title = self.name
|
|
71
|
-
# TODO usare xrange espresso in metri per affettare array e poi passarli a single_plot. xrange:Tuple[float,float]=None
|
|
72
|
-
return self.plotter.single_plot(self.position,self.BFS,title=title)
|
|
73
|
-
|
|
74
|
-
def plotMax(self,title: str = None):
|
|
75
|
-
if self.MaxGain.size == 0:
|
|
76
|
-
warnings.warn("Max array in not available. If possibile, load the raw file of the same measure.")
|
|
77
|
-
return None
|
|
78
|
-
if not title:
|
|
79
|
-
title = self.name
|
|
80
|
-
return self.plotter.max_plot(self.position, self.MaxGain, title)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
class multipleProfile():
|
|
84
|
-
|
|
85
|
-
statistics: Statistics
|
|
86
|
-
plotter: Plotter
|
|
87
|
-
|
|
88
|
-
def __init__(self, folder: Union[str,Path], plotter: Plotter = Plotly(),
|
|
89
|
-
n_measure: int = None,
|
|
90
|
-
start_measure: Union[int,datetime] = 0,
|
|
91
|
-
stop_measure: datetime = None) -> None:
|
|
92
|
-
|
|
93
|
-
self.folder = folder
|
|
94
|
-
self.plotter = plotter
|
|
95
|
-
|
|
96
|
-
filelist = glob(path.join(folder,'*.json'))
|
|
97
|
-
if start_measure and isinstance(start_measure, datetime):
|
|
98
|
-
timestamps_files = [ datetime.strptime('_'.join(path.basename(file).split('_')[:2]), '%Y-%m-%d_%H-%M-%S.%f' ) for file in filelist]
|
|
99
|
-
primo = next((x for x,value in enumerate(timestamps_files) if value >= start_measure),0)
|
|
100
|
-
if stop_measure:
|
|
101
|
-
ultimo = next((x for x,value in enumerate(timestamps_files) if value > stop_measure),len(filelist))
|
|
102
|
-
elif n_measure:
|
|
103
|
-
ultimo = primo + n_measure
|
|
104
|
-
else:
|
|
105
|
-
ultimo = len(filelist)
|
|
106
|
-
filelist = filelist[primo:ultimo]
|
|
107
|
-
|
|
108
|
-
if n_measure and (start_measure==0 or isinstance(start_measure,int)):
|
|
109
|
-
filelist = filelist[start_measure:start_measure+n_measure]
|
|
110
|
-
|
|
111
|
-
if isinstance(start_measure,int) and not n_measure and stop_measure:
|
|
112
|
-
timestamps_files = [ datetime.strptime('_'.join(path.basename(file).split('_')[:2]), '%Y-%m-%d_%H-%M-%S.%f' ) for file in filelist]
|
|
113
|
-
ultimo = next((x for x,value in enumerate(timestamps_files) if value > stop_measure),len(filelist))
|
|
114
|
-
filelist = filelist[start_measure:ultimo]
|
|
115
|
-
|
|
116
|
-
if len(filelist) == 0:
|
|
117
|
-
raise NoFilesSelected(folder=folder)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
timestamps = list()
|
|
121
|
-
with Bar('Redaing files',max=len(filelist)) as bar:
|
|
122
|
-
for file in filelist:
|
|
123
|
-
temp = Profile(filename=file)
|
|
124
|
-
timestamps.append(temp.timestamp)
|
|
125
|
-
try:
|
|
126
|
-
self.BFS = np.column_stack((self.BFS, temp.BFS))
|
|
127
|
-
self.MaxGain = np.column_stack((self.MaxGain, temp.MaxGain))
|
|
128
|
-
except AttributeError:
|
|
129
|
-
self.BFS = temp.BFS
|
|
130
|
-
self.MaxGain = temp.MaxGain
|
|
131
|
-
bar.next()
|
|
132
|
-
|
|
133
|
-
self.timestamps = np.array(timestamps)
|
|
134
|
-
self.settings = temp.settings
|
|
135
|
-
self.sw_version = temp.sw_version
|
|
136
|
-
self.position = temp.position
|
|
137
|
-
self.calcStatistics()
|
|
138
|
-
self.MaxGainMean = self.MaxGain.mean(axis=1)
|
|
139
|
-
self.MaxGainStd = self.MaxGain.std(axis=1)
|
|
140
|
-
|
|
141
|
-
def calcCorrelations(self, type:Literal['max','bfs'],
|
|
142
|
-
reference:Literal['first','previous']='previous',
|
|
143
|
-
range:Tuple[float,float]=None) -> np.array:
|
|
144
|
-
'''Ritorna correlazione tra prima misura e misura n-esima.
|
|
145
|
-
Si può scegliere se effettuarla su matrice dei BFS o matrice dei massimi'''
|
|
146
|
-
|
|
147
|
-
if type == 'max':
|
|
148
|
-
correlations = np.corrcoef(self.MaxGain[range[0]:range[1]] if range else self.MaxGain,rowvar=False)
|
|
149
|
-
else:
|
|
150
|
-
correlations = np.corrcoef(self.BFS[range[0]:range[1]] if range else self.BFS,rowvar=False)
|
|
151
|
-
|
|
152
|
-
if reference == 'first':
|
|
153
|
-
return correlations[0,:]
|
|
154
|
-
|
|
155
|
-
indici = np.arange(1,np.shape(correlations)[0])
|
|
156
|
-
return np.insert(correlations[indici,indici-1],0,1)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def calcStatistics(self, plot:bool=False, range:Tuple[float,float]=None, title:str=None)->Statistics:
|
|
160
|
-
# TODO gestione input range
|
|
161
|
-
self.statistics = Statistics(BFSmean=self.BFS.mean(axis=1),
|
|
162
|
-
BFSstd=self.BFS.std(axis=1),
|
|
163
|
-
BFSstd_mean=self.BFS.std(axis=1).mean())
|
|
164
|
-
|
|
165
|
-
if plot:
|
|
166
|
-
if not title:
|
|
167
|
-
title=self.folder
|
|
168
|
-
return self.plotter.statistics(self.position,
|
|
169
|
-
self.statistics.BFSmean,
|
|
170
|
-
self.statistics.BFSstd, title=title)
|
|
171
|
-
return self.statistics
|
|
172
|
-
|
|
173
|
-
def plotStatistics(self, title:str=None):
|
|
174
|
-
if not title:
|
|
175
|
-
title=self.folder
|
|
176
|
-
# TODO se sono state calcolate con range allora position è sbagliato
|
|
177
|
-
return self.plotter.statistics(self.position, self.statistics, title=title)
|
|
178
|
-
|
|
179
|
-
def plot(self, startTime: datetime = datetime.min, stopTime: datetime = datetime.now(), title: str = None):
|
|
180
|
-
if not title:
|
|
181
|
-
title=self.folder
|
|
182
|
-
match = [i for i,date in enumerate(self.timestamps) if date >= startTime and date <= stopTime]
|
|
183
|
-
return self.plotter.multiple_plot(self.position,self.BFS[:,match],self.timestamps[match],title=title)
|
|
184
|
-
|
|
185
|
-
def plotMax(self,title: str = None):
|
|
186
|
-
if self.MaxGain.size == 0:
|
|
187
|
-
warnings.warn("Max array in not available. If possibile, load the raw file of the same measure.")
|
|
188
|
-
return None
|
|
189
|
-
if not title:
|
|
190
|
-
title = self.folder
|
|
191
|
-
return self.plotter.max_stat_plot(self.MaxGainMean, self.MaxGainStd, title)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
class Raw():
|
|
195
|
-
|
|
196
|
-
filename: Union[str,Path] = Path('')
|
|
197
|
-
plotter: Plotter
|
|
198
|
-
settings: Settings
|
|
199
|
-
sw_version: str = '<1.2.0.0'
|
|
200
|
-
BGS: np.ndarray = np.array([])
|
|
201
|
-
residuo: np.ndarray = np.array([])
|
|
202
|
-
timestamp: datetime = datetime.now
|
|
203
|
-
|
|
204
|
-
def __init__(self, filename: Union[str,Path], plotter: Plotter = Plotly()) -> None:
|
|
205
|
-
self.filename = filename
|
|
206
|
-
self.plotter = plotter
|
|
207
|
-
with open(self.filename) as file:
|
|
208
|
-
text = load(file)
|
|
209
|
-
self.settings = parseSettingsDict(text['Settings'])
|
|
210
|
-
if 'sw_version' in text:
|
|
211
|
-
self.sw_version = text['sw_version']
|
|
212
|
-
if 'sw_version' in text and text['sw_version'] == '':
|
|
213
|
-
self.sw_version = 'ambiente sviluppo'
|
|
214
|
-
self.BGS = np.transpose(np.array(text['Raw']))
|
|
215
|
-
self.timestamp = datetime.strptime(
|
|
216
|
-
text['Time Stamp'], '%Y-%m-%d'+'T'+'%H:%M:%S.%f'+'Z') # "2021-11-08T16:51:16.652Z"
|
|
217
|
-
self._createPositionArray()
|
|
218
|
-
self._createFrequencyArray()
|
|
219
|
-
try:
|
|
220
|
-
self.residuo = np.array(text['residuo'])
|
|
221
|
-
except KeyError:
|
|
222
|
-
self.residuo = np.zeros(np.shape(self.frequency))
|
|
223
|
-
|
|
224
|
-
def _createPositionArray(self) -> None:
|
|
225
|
-
'''Crea array numpy delle posizioni e la risoluzione spaziale'''
|
|
226
|
-
self.position = np.linspace(
|
|
227
|
-
0, self.settings.Cable.Length, num=self.BGS.shape[1], endpoint=True)
|
|
228
|
-
self.spatialResolution = self.position[1]
|
|
229
|
-
|
|
230
|
-
def _createFrequencyArray(self) -> None:
|
|
231
|
-
'''Crea array numpy delle frequenze, espresse in GHz'''
|
|
232
|
-
self.frequency = np.linspace( self.settings.Clock.StartMHz,
|
|
233
|
-
self.settings.Clock.StartMHz + self.settings.Clock.StepMHz * self.BGS.shape[0], num= self.BGS.shape[0], endpoint=True) / 1000
|
|
234
|
-
|
|
235
|
-
def plot2d(self, title: str = None):
|
|
236
|
-
if not title:
|
|
237
|
-
title=self.filename
|
|
238
|
-
return self.plotter.raw2d_plot(self.position, self.frequency, self.BGS, title=title)
|
|
239
|
-
|
|
240
|
-
def plot3d(self, title: str = None):
|
|
241
|
-
if not title:
|
|
242
|
-
title=self.filename
|
|
243
|
-
return self.plotter.raw3d_plot(self.position, self.frequency, self.BGS, title=title)
|
|
244
|
-
|
|
245
|
-
def plotBGS(self, index: int = None, title: str = None):
|
|
246
|
-
'''Plot 2D di tutti gli spettri BGS'''
|
|
247
|
-
if not title:
|
|
248
|
-
title=self.filename
|
|
249
|
-
return self.plotter.rawBGS_plot(self.frequency, self.BGS, index=index, title=title)
|
|
250
|
-
|
|
251
|
-
def plotMax(self, title: str = None):
|
|
252
|
-
if not title:
|
|
253
|
-
title=self.filename
|
|
254
|
-
return self.plotter.max_plot(self.position, self.BGS.max(axis=0), title)
|
|
255
|
-
|
|
256
|
-
if __name__ == '__main__':
|
|
257
|
-
'''Non va se si lancia direttamente questo script a meno di non cambiare i primi due import da modules.settings a settings; uguale per plotter.'''
|
|
258
|
-
#a = Profile(filename='data/profiles/2021-11-08_16-51-16.652_rawarray.json')
|
|
259
|
-
#b = multipleProfile(folder='data/profiles/')
|
|
260
|
-
c = Raw(filename='data/raw/2021-11-08_16-51-16.652_rawmatrix.json')
|
|
261
|
-
# print(b.BFS.shape)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|