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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: readerbotda
3
- Version: 1.3.3
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.3"
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)