tsp 1.8.1__py3-none-any.whl → 1.10.2__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.
- tsp/__init__.py +11 -11
- tsp/__meta__.py +1 -1
- tsp/concatenation.py +159 -153
- tsp/core.py +1306 -1162
- tsp/data/2023-01-06_755-test-Dataset_2031-Constant_Over_Interval-Hourly-Ground_Temperature-Thermistor_Automated.timeserie.csv +4 -4
- tsp/data/2023-01-06_755-test.metadata.txt +208 -208
- tsp/data/NTGS_example_csv.csv +6 -6
- tsp/data/NTGS_example_slash_dates.csv +6 -6
- tsp/data/NTGS_gtr_example_excel.xlsx +0 -0
- tsp/data/example_geotop.csv +5240 -5240
- tsp/data/example_gtnp.csv +1298 -1298
- tsp/data/example_permos.csv +7 -7
- tsp/data/ntgs-db-multi.txt +3872 -0
- tsp/data/ntgs-db-single.txt +2251 -0
- tsp/data/test_geotop_has_space.txt +5 -5
- tsp/data/tsp_format_long.csv +10 -0
- tsp/data/tsp_format_wide_1.csv +7 -0
- tsp/data/tsp_format_wide_2.csv +7 -0
- tsp/dataloggers/AbstractReader.py +43 -43
- tsp/dataloggers/FG2.py +110 -110
- tsp/dataloggers/GP5W.py +114 -114
- tsp/dataloggers/Geoprecision.py +34 -34
- tsp/dataloggers/HOBO.py +930 -914
- tsp/dataloggers/RBRXL800.py +190 -190
- tsp/dataloggers/RBRXR420.py +371 -308
- tsp/dataloggers/Vemco.py +84 -0
- tsp/dataloggers/__init__.py +15 -15
- tsp/dataloggers/logr.py +196 -115
- tsp/dataloggers/test_files/004448.DAT +2543 -2543
- tsp/dataloggers/test_files/004531.DAT +17106 -17106
- tsp/dataloggers/test_files/004531.HEX +3587 -3587
- tsp/dataloggers/test_files/004534.HEX +3587 -3587
- tsp/dataloggers/test_files/010252.dat +1731 -1731
- tsp/dataloggers/test_files/010252.hex +1739 -1739
- tsp/dataloggers/test_files/010274.hex +1291 -1291
- tsp/dataloggers/test_files/010278.hex +3544 -3544
- tsp/dataloggers/test_files/012064.dat +1286 -1286
- tsp/dataloggers/test_files/012064.hex +1294 -1294
- tsp/dataloggers/test_files/012064_modified_start.hex +1294 -0
- tsp/dataloggers/test_files/012081.hex +3532 -3532
- tsp/dataloggers/test_files/013138_recovery_stamp.hex +1123 -0
- tsp/dataloggers/test_files/014037-2007.hex +95 -0
- tsp/dataloggers/test_files/019360_20160918_1146_SlumpIslandTopofHill.hex +11253 -0
- tsp/dataloggers/test_files/019360_20160918_1146_SlumpIslandTopofHill.xls +0 -0
- tsp/dataloggers/test_files/07B1592.DAT +1483 -1483
- tsp/dataloggers/test_files/07B1592.HEX +1806 -1806
- tsp/dataloggers/test_files/07B4450.DAT +2234 -2234
- tsp/dataloggers/test_files/07B4450.HEX +2559 -2559
- tsp/dataloggers/test_files/2022018_2025-09-18T22-16-16.txt +36 -0
- tsp/dataloggers/test_files/2022018_2025-09-18T22-16-16_raw.csv +2074 -0
- tsp/dataloggers/test_files/2022018_2025-09-18T22-16-16_temp.csv +2074 -0
- tsp/dataloggers/test_files/2025004_2025-12-02T17-07-28_cfg.txt +30 -0
- tsp/dataloggers/test_files/2025004_2025-12-02T17-07-28_raw.csv +35 -0
- tsp/dataloggers/test_files/2025004_2025-12-02T17-07-28_temp.csv +35 -0
- tsp/dataloggers/test_files/204087.xlsx +0 -0
- tsp/dataloggers/test_files/Asc-1455As02.000 +2982 -0
- tsp/dataloggers/test_files/Asc-1456As02.000 +2992 -0
- tsp/dataloggers/test_files/Asc-1457As02.000 +2917 -0
- tsp/dataloggers/test_files/BGC_BH15_019362_20140610_1253.hex +1729 -0
- tsp/dataloggers/test_files/Bin2944.csv +759 -0
- tsp/dataloggers/test_files/Bin5494.csv +2972 -0
- tsp/dataloggers/test_files/Bin6786.csv +272 -0
- tsp/dataloggers/test_files/FG2_399.csv +9881 -9881
- tsp/dataloggers/test_files/GP5W.csv +1121 -1121
- tsp/dataloggers/test_files/GP5W_260.csv +1884 -1884
- tsp/dataloggers/test_files/GP5W_270.csv +2210 -2210
- tsp/dataloggers/test_files/H08-030-08_HOBOware.csv +998 -998
- tsp/dataloggers/test_files/Minilog-II-T_350763_20190711_1.csv +2075 -0
- tsp/dataloggers/test_files/Minilog-II-T_350769_20190921_1.csv +6384 -0
- tsp/dataloggers/test_files/Minilog-II-T_354284_20190921_1.csv +4712 -0
- tsp/dataloggers/test_files/Minilog-T_7943_20140920_1.csv +5826 -0
- tsp/dataloggers/test_files/Minilog-T_8979_20140806_1.csv +2954 -0
- tsp/dataloggers/test_files/Minilog-T_975_20110824_1.csv +4343 -0
- tsp/dataloggers/test_files/RBR_01.dat +1046 -1046
- tsp/dataloggers/test_files/RBR_02.dat +2426 -2426
- tsp/dataloggers/test_files/RI03b_062831_20240905_1801.rsk +0 -0
- tsp/dataloggers/test_files/RI03b_062831_20240905_1801.xlsx +0 -0
- tsp/dataloggers/test_files/RSTDT2055.csv +2152 -2152
- tsp/dataloggers/test_files/U23-001_HOBOware.csv +1001 -1001
- tsp/dataloggers/test_files/hobo-negative-2.txt +6396 -6396
- tsp/dataloggers/test_files/hobo-negative-3.txt +5593 -5593
- tsp/dataloggers/test_files/hobo-positive-number-1.txt +1000 -1000
- tsp/dataloggers/test_files/hobo-positive-number-2.csv +1003 -1003
- tsp/dataloggers/test_files/hobo-positive-number-3.csv +1133 -1133
- tsp/dataloggers/test_files/hobo-positive-number-4.csv +1209 -1209
- tsp/dataloggers/test_files/hobo2.csv +8702 -8702
- tsp/dataloggers/test_files/hobo_1_AB.csv +21732 -21732
- tsp/dataloggers/test_files/hobo_1_AB_Details.txt +133 -133
- tsp/dataloggers/test_files/hobo_1_AB_classic.csv +4373 -4373
- tsp/dataloggers/test_files/hobo_1_AB_defaults.csv +21732 -21732
- tsp/dataloggers/test_files/hobo_1_AB_minimal.txt +1358 -1358
- tsp/dataloggers/test_files/hobo_1_AB_var2.csv +3189 -3189
- tsp/dataloggers/test_files/hobo_1_AB_var3.csv +2458 -2458
- tsp/dataloggers/test_files/logR_ULogC16-32_1.csv +106 -106
- tsp/dataloggers/test_files/logR_ULogC16-32_2.csv +100 -100
- tsp/dataloggers/test_files/mon_3_Ta_2010-08-18_2013-02-08.txt +21724 -21724
- tsp/dataloggers/test_files/rbr_001.dat +1133 -1133
- tsp/dataloggers/test_files/rbr_001.hex +1139 -1139
- tsp/dataloggers/test_files/rbr_001_no_comment.dat +1132 -1132
- tsp/dataloggers/test_files/rbr_001_no_comment.hex +1138 -1138
- tsp/dataloggers/test_files/rbr_002.dat +1179 -1179
- tsp/dataloggers/test_files/rbr_002.hex +1185 -1185
- tsp/dataloggers/test_files/rbr_003.hex +1292 -1292
- tsp/dataloggers/test_files/rbr_xl_001.DAT +1105 -1105
- tsp/dataloggers/test_files/rbr_xl_002.DAT +1126 -1126
- tsp/dataloggers/test_files/rbr_xl_003.DAT +4622 -4622
- tsp/dataloggers/test_files/rbr_xl_003.HEX +3587 -3587
- tsp/gtnp.py +148 -148
- tsp/labels.py +3 -3
- tsp/misc.py +90 -90
- tsp/physics.py +101 -101
- tsp/plots/static.py +388 -374
- tsp/readers.py +829 -548
- tsp/standardization/__init__.py +0 -0
- tsp/standardization/metadata.py +95 -0
- tsp/standardization/metadata_ref.py +0 -0
- tsp/standardization/validator.py +535 -0
- tsp/time.py +45 -45
- tsp/tspwarnings.py +27 -15
- tsp/utils.py +131 -101
- tsp/version.py +1 -1
- {tsp-1.8.1.dist-info → tsp-1.10.2.dist-info}/METADATA +95 -86
- tsp-1.10.2.dist-info/RECORD +132 -0
- {tsp-1.8.1.dist-info → tsp-1.10.2.dist-info}/licenses/LICENSE +674 -674
- {tsp-1.8.1.dist-info → tsp-1.10.2.dist-info}/top_level.txt +1 -0
- tsp-1.8.1.dist-info/RECORD +0 -94
- {tsp-1.8.1.dist-info → tsp-1.10.2.dist-info}/WHEEL +0 -0
tsp/dataloggers/RBRXR420.py
CHANGED
|
@@ -1,308 +1,371 @@
|
|
|
1
|
-
import sqlite3
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
from
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
elif file_extention == ".
|
|
79
|
-
self.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
self.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
self.META["
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
self.META["
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
""
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
self.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
self.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
1
|
+
import sqlite3
|
|
2
|
+
import re
|
|
3
|
+
import pathlib
|
|
4
|
+
import warnings
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import datetime as dt
|
|
8
|
+
from copy import deepcopy
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from pyrsktools import RSK
|
|
12
|
+
except ModuleNotFoundError:
|
|
13
|
+
warnings.warn("Missing pyRSKtools library. .rsk files can not be imported.")
|
|
14
|
+
from .AbstractReader import AbstractReader
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RBRXR420(AbstractReader):
|
|
18
|
+
|
|
19
|
+
def read(self, file_path: str) -> "pd.DataFrame":
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
file_path
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
file_extention = pathlib.Path(file_path).suffix.lower()
|
|
31
|
+
if file_extention in [".dat", ".hex"]:
|
|
32
|
+
with open(file_path, "r") as f:
|
|
33
|
+
num_lines = len(f.readlines())
|
|
34
|
+
with open(file_path, "r") as f:
|
|
35
|
+
first_75 = [next(f) for i in range(min([75, num_lines]))]
|
|
36
|
+
for line_num in range(len(first_75)):
|
|
37
|
+
if first_75[line_num].lower().startswith("logger start:"):
|
|
38
|
+
header_length = line_num + 1
|
|
39
|
+
break
|
|
40
|
+
with open(file_path, "r") as f:
|
|
41
|
+
header_lines = [next(f) for i in range(header_length)]
|
|
42
|
+
self._parse_meta(header_lines)
|
|
43
|
+
|
|
44
|
+
data_lines = f.readlines()
|
|
45
|
+
if file_extention == ".dat":
|
|
46
|
+
line_num = 0
|
|
47
|
+
for line_num in range(len(data_lines)):
|
|
48
|
+
if data_lines[line_num] != "\n":
|
|
49
|
+
split_line = data_lines[line_num].split()
|
|
50
|
+
else:
|
|
51
|
+
split_line = ["no data"]
|
|
52
|
+
if split_line[0].lower() == "temp":
|
|
53
|
+
break
|
|
54
|
+
if line_num == len(data_lines) - 1:
|
|
55
|
+
raise RuntimeError("No column names found")
|
|
56
|
+
data_lines = data_lines[line_num:]
|
|
57
|
+
first_line = data_lines[0].split()
|
|
58
|
+
second_line = data_lines[1].split()
|
|
59
|
+
|
|
60
|
+
if len(first_line) == len(second_line):
|
|
61
|
+
self._read_standard_dat_format(data_lines[1:], False)
|
|
62
|
+
elif len(first_line) + 2 == len(second_line):
|
|
63
|
+
try:
|
|
64
|
+
is_datetime = bool(dt.datetime.strptime(" ".join(second_line[:2]), "%Y/%m/%d %H:%M:%S"))
|
|
65
|
+
except ValueError:
|
|
66
|
+
is_datetime = False
|
|
67
|
+
if is_datetime:
|
|
68
|
+
self._read_standard_dat_format(data_lines[1:], True)
|
|
69
|
+
else:
|
|
70
|
+
raise RuntimeError("Error, expected date time with format %Y/%m/%d %H:%M:%S at start of"
|
|
71
|
+
"row.")
|
|
72
|
+
else:
|
|
73
|
+
raise RuntimeError("Error: Number of column names and number of columns do not match any"
|
|
74
|
+
"expected pattern.")
|
|
75
|
+
|
|
76
|
+
else:
|
|
77
|
+
self._read_standard_hex_format(data_lines)
|
|
78
|
+
elif file_extention == ".xls":
|
|
79
|
+
self._read_standard_xls_format(file_path)
|
|
80
|
+
elif file_extention == ".xlsx":
|
|
81
|
+
self._read_standard_xlsx_format(file_path)
|
|
82
|
+
elif file_extention == ".rsk":
|
|
83
|
+
self._read_standard_rsk_format(file_path)
|
|
84
|
+
else:
|
|
85
|
+
raise IOError("Unrecognised file. File is not a .dat, .hex, .xls, .xlsx, or .rsk.")
|
|
86
|
+
return self.DATA
|
|
87
|
+
|
|
88
|
+
def _parse_meta(self, header_lines: list):
|
|
89
|
+
self.META["logger_model"] = header_lines[0].split()[1]
|
|
90
|
+
self.META["logger_sn"] = header_lines[0].split()[3]
|
|
91
|
+
sample_interval = dt.datetime.strptime(header_lines[5].split()[-1], "%H:%M:%S")
|
|
92
|
+
self.META["download_date"] = dt.datetime.strptime(header_lines[1][14:31], "%y/%m/%d %H:%M:%S")
|
|
93
|
+
self.META["sample_interval"] = dt.timedelta(hours=sample_interval.hour, minutes=sample_interval.minute,
|
|
94
|
+
seconds=sample_interval.second)
|
|
95
|
+
self.META["logging_start"] = dt.datetime.strptime(" ".join(header_lines[3].split()[-2:]),
|
|
96
|
+
"%y/%m/%d %H:%M:%S")
|
|
97
|
+
line_7_info = header_lines[6].split(",")
|
|
98
|
+
self.META["num_channels"] = int(line_7_info[0].split()[-1])
|
|
99
|
+
self.META["num_samples"] = int(line_7_info[1].split()[-1])
|
|
100
|
+
formatting = header_lines[7].split("%")[1]
|
|
101
|
+
if formatting.endswith("\n"):
|
|
102
|
+
self.META["precision"] = int(formatting[-3])
|
|
103
|
+
else:
|
|
104
|
+
self.META["precision"] = int(formatting[-2])
|
|
105
|
+
|
|
106
|
+
self.META["calibration_parameters"] = {}
|
|
107
|
+
calibration_start_line = 8
|
|
108
|
+
for i in range(self.META["num_channels"]):
|
|
109
|
+
self.META["calibration_parameters"][f"channel_{i + 1}"] = {}
|
|
110
|
+
for j in range(4):
|
|
111
|
+
line_num = calibration_start_line + 4 * i + j
|
|
112
|
+
if header_lines[line_num].lower().startswith("calibration"):
|
|
113
|
+
self.META["calibration_parameters"][f"channel_{i + 1}"][chr(ord("a") + j)] \
|
|
114
|
+
= float(header_lines[line_num].split()[-1])
|
|
115
|
+
else:
|
|
116
|
+
self.META["calibration_parameters"][f"channel_{i + 1}"][chr(ord("a") + j)] \
|
|
117
|
+
= float(header_lines[line_num].split()[0])
|
|
118
|
+
|
|
119
|
+
self.META['raw'] = "".join(header_lines)
|
|
120
|
+
self.META["internal_log"] = []
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
def _read_standard_dat_format(self, raw_data: list, time_stamps: bool = False):
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
raw_data
|
|
129
|
+
line_numbers
|
|
130
|
+
|
|
131
|
+
Returns
|
|
132
|
+
-------
|
|
133
|
+
|
|
134
|
+
"""
|
|
135
|
+
self.DATA = pd.DataFrame(columns=[f"channel_{i + 1}" for i in range(self.META["num_channels"])])
|
|
136
|
+
line_num = 0
|
|
137
|
+
for line in raw_data:
|
|
138
|
+
line_data = line.split()
|
|
139
|
+
if time_stamps:
|
|
140
|
+
self.DATA.loc[dt.datetime.strptime(" ".join(line_data[:2]), "%Y/%m/%d %H:%M:%S")] = line_data[2:]
|
|
141
|
+
else:
|
|
142
|
+
self.DATA.loc[self.META["logging_start"] + self.META["sample_interval"] * line_num] = line_data
|
|
143
|
+
line_num += 1
|
|
144
|
+
for col in self.DATA:
|
|
145
|
+
self.DATA[col] = pd.to_numeric(self.DATA[col], errors='coerce')
|
|
146
|
+
self.DATA.reset_index(inplace=True)
|
|
147
|
+
self.DATA.rename(columns={"index": "TIME"}, inplace=True)
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
def _read_standard_hex_format(self, raw_data: list):
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
raw_data
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
|
|
160
|
+
"""
|
|
161
|
+
log_line_numbers = []
|
|
162
|
+
for line_num in range(len(raw_data)):
|
|
163
|
+
if raw_data[line_num].lower().startswith("number of bytes of data"):
|
|
164
|
+
hex_header_length = line_num + 2
|
|
165
|
+
break
|
|
166
|
+
elif raw_data[line_num].lower().startswith("number of bytes in header"):
|
|
167
|
+
header_bytes = int(raw_data[line_num].split()[-1])
|
|
168
|
+
elif raw_data[line_num].lower().startswith(" ") or raw_data[line_num].lower().startswith("\n"):
|
|
169
|
+
pass
|
|
170
|
+
elif raw_data[line_num].lower().startswith("extended data range"):
|
|
171
|
+
self.META["extended data range"] = raw_data[line_num].split()[-1]
|
|
172
|
+
else:
|
|
173
|
+
raw_log = raw_data[line_num][:-1]
|
|
174
|
+
time_stamp_regex = re.search(r"\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}", raw_log)
|
|
175
|
+
time_stamp = time_stamp_regex.group(0)
|
|
176
|
+
# action = raw_log[:time_stamp_regex.start() - 1]
|
|
177
|
+
sample_num = int(re.search(r"\d+", raw_log[time_stamp_regex.end():]).group(0))
|
|
178
|
+
action_type = re.search(": .+", raw_log).group(0)[2:]
|
|
179
|
+
self.META["internal_log"].append({"timestamp": time_stamp, "sample_num": sample_num,
|
|
180
|
+
"action": action_type})
|
|
181
|
+
log_line_numbers.append(sample_num)
|
|
182
|
+
num_hex_header_values = int(header_bytes / 3)
|
|
183
|
+
hex_vals = []
|
|
184
|
+
raw_data = raw_data[hex_header_length:]
|
|
185
|
+
for line_num in range(len(raw_data)):
|
|
186
|
+
line = raw_data[line_num].replace(" ", "")
|
|
187
|
+
line_hex_vals = [line[i: i + 6] for i in range(0, len(line), 6)][:-1]
|
|
188
|
+
for hex_val in line_hex_vals:
|
|
189
|
+
hex_vals.append(hex_val)
|
|
190
|
+
hex_vals = hex_vals[num_hex_header_values:]
|
|
191
|
+
|
|
192
|
+
self.DATA = pd.DataFrame(columns=[f"channel_{i + 1}" for i in range(self.META["num_channels"])])
|
|
193
|
+
line_num = 0
|
|
194
|
+
hex_num = 0
|
|
195
|
+
reference_time = deepcopy(self.META["logging_start"])
|
|
196
|
+
interval_num = 0
|
|
197
|
+
for line in range(self.META["num_samples"]):
|
|
198
|
+
if line_num + 1 not in log_line_numbers:
|
|
199
|
+
line_time = reference_time + self.META["sample_interval"] * interval_num
|
|
200
|
+
else:
|
|
201
|
+
log_records = [ele for ele in self.META["internal_log"] if ele["sample_num"] == line_num + 1]
|
|
202
|
+
for ele in log_records:
|
|
203
|
+
if ele["action"] in ["TIME STAMP", "RTC STAMP"]:
|
|
204
|
+
line_time = dt.datetime.strptime(ele["timestamp"], "%Y/%m/%d %H:%M:%S")
|
|
205
|
+
interval_num = 0
|
|
206
|
+
reference_time = line_time
|
|
207
|
+
hex_num += 4
|
|
208
|
+
elif ele["action"] == "RECOVERY STAMP":
|
|
209
|
+
if ele["sample_num"] != self.META["num_samples"]:
|
|
210
|
+
raise RuntimeError("Recovery stamp logged part way through data. Logger may have been "
|
|
211
|
+
"reset then continued logging. Open file in RBR software and "
|
|
212
|
+
"investigate.")
|
|
213
|
+
else:
|
|
214
|
+
warnings.warn(f"{ele}")
|
|
215
|
+
time_hex_vals = hex_vals[hex_num: hex_num + 8]
|
|
216
|
+
line_vals = [int(h, 16) / int("FFFFFF", 16) for h in time_hex_vals]
|
|
217
|
+
line_temps = []
|
|
218
|
+
for channel in range(len(line_vals)):
|
|
219
|
+
val = line_vals[channel]
|
|
220
|
+
if val not in [0, 1]:
|
|
221
|
+
a = self.META["calibration_parameters"][f"channel_{channel + 1}"]["a"]
|
|
222
|
+
b = self.META["calibration_parameters"][f"channel_{channel + 1}"]["b"]
|
|
223
|
+
c = self.META["calibration_parameters"][f"channel_{channel + 1}"]["c"]
|
|
224
|
+
d = self.META["calibration_parameters"][f"channel_{channel + 1}"]["d"]
|
|
225
|
+
x = np.log((1 / val) - 1)
|
|
226
|
+
temp = 1 / (a + b * x + c * x ** 2 + d * x ** 3) - 273.15
|
|
227
|
+
line_temps.append(round(temp, self.META["precision"]))
|
|
228
|
+
else:
|
|
229
|
+
line_temps.append(np.nan)
|
|
230
|
+
self.DATA.loc[line_time] = line_temps
|
|
231
|
+
line_num += 1
|
|
232
|
+
interval_num += 1
|
|
233
|
+
hex_num += 8
|
|
234
|
+
for col in self.DATA:
|
|
235
|
+
self.DATA[col] = pd.to_numeric(self.DATA[col], errors='coerce')
|
|
236
|
+
self.DATA.reset_index(inplace=True)
|
|
237
|
+
self.DATA.rename(columns={"index": "TIME"}, inplace=True)
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
def _read_standard_xls_format(self, file_path: str):
|
|
241
|
+
xls = pd.ExcelFile(file_path)
|
|
242
|
+
sheet = xls.sheet_names[0]
|
|
243
|
+
xls.close()
|
|
244
|
+
raw_data = pd.read_excel(file_path, sheet, header=None)
|
|
245
|
+
raw_meta = raw_data.iloc[:5].copy()
|
|
246
|
+
if raw_meta.iloc[0, 0] != "RBR data file":
|
|
247
|
+
raise IOError("Not a valid .xls file")
|
|
248
|
+
meta = {}
|
|
249
|
+
for i, r in raw_meta.iterrows():
|
|
250
|
+
for j in range(0, len(r) - 1, 2):
|
|
251
|
+
if not pd.isna(raw_meta.iloc[i, j]):
|
|
252
|
+
meta[raw_meta.iloc[i, j]] = raw_meta.iloc[i, j + 1]
|
|
253
|
+
self.META["logger_model"] = meta["Model:"]
|
|
254
|
+
self.META["logger_sn"] = meta["Serial Number:"]
|
|
255
|
+
self.META["sample_interval"] = dt.timedelta(seconds=int(meta["Logging sampling period (s):"]))
|
|
256
|
+
self.META["logging_start"] = dt.datetime.strptime(meta["Logging start time:"], "%Y/%m/%d")
|
|
257
|
+
|
|
258
|
+
column_names = {}
|
|
259
|
+
for col in raw_data:
|
|
260
|
+
if col == 0:
|
|
261
|
+
col_name = "TIME"
|
|
262
|
+
else:
|
|
263
|
+
col_name = f"channel_{col}"
|
|
264
|
+
column_names[col] = col_name
|
|
265
|
+
self.DATA = raw_data.iloc[6:].copy()
|
|
266
|
+
self.DATA.reset_index(drop=True, inplace=True)
|
|
267
|
+
self.DATA.rename(columns=column_names, inplace=True)
|
|
268
|
+
for col in self.DATA:
|
|
269
|
+
if col == "TIME":
|
|
270
|
+
self.DATA["TIME"] = pd.to_datetime(self.DATA["TIME"], format="%d/%m/%Y %H:%M:%S.%f")
|
|
271
|
+
else:
|
|
272
|
+
self.DATA[col] = pd.to_numeric(self.DATA[col], errors='coerce')
|
|
273
|
+
return
|
|
274
|
+
|
|
275
|
+
def _read_standard_xlsx_format(self, file_path: str):
|
|
276
|
+
with warnings.catch_warnings():
|
|
277
|
+
warnings.simplefilter("ignore")
|
|
278
|
+
version = pd.read_excel(file_path, sheet_name="Metadata", header=1, nrows=1)
|
|
279
|
+
known_formats = {"2.7.3": [9, 24, 28, 5], "2.19.1": [12, 62, 66, 5]}
|
|
280
|
+
meta_table = None
|
|
281
|
+
for k, v in known_formats.items():
|
|
282
|
+
try:
|
|
283
|
+
with warnings.catch_warnings():
|
|
284
|
+
warnings.simplefilter("ignore")
|
|
285
|
+
mt = {"Instrument": pd.read_excel(file_path, sheet_name="Metadata", header=v[0], nrows=1),
|
|
286
|
+
"Schedule": pd.read_excel(file_path, sheet_name="Metadata", header=v[1], nrows=1),
|
|
287
|
+
"Sampling": pd.read_excel(file_path, sheet_name="Metadata", header=v[2], nrows=1),
|
|
288
|
+
"Export": pd.read_excel(file_path, sheet_name="Metadata", header=v[3], nrows=1)}
|
|
289
|
+
except:
|
|
290
|
+
pass
|
|
291
|
+
else:
|
|
292
|
+
if "Model" in mt["Instrument"]:
|
|
293
|
+
meta_table = mt
|
|
294
|
+
print(f"RBR xlsx version {version.loc[0, 'Ruskin']} read as version {k}")
|
|
295
|
+
break
|
|
296
|
+
|
|
297
|
+
if meta_table is None:
|
|
298
|
+
raise ValueError(f"Unrecognized formatting (version {version.loc[0, 'Ruskin']}). this code has been tested "
|
|
299
|
+
f"on v1.12.1, v2.7.3, and v2.19.1")
|
|
300
|
+
self.META["logger_model"] = meta_table["Instrument"]["Model"].loc[0]
|
|
301
|
+
self.META["logger_sn"] = meta_table["Instrument"]["Serial"].loc[0]
|
|
302
|
+
self.META["sample_interval"] = dt.timedelta(seconds=int(meta_table["Sampling"]["Period"].loc[0]))
|
|
303
|
+
self.META["logging_start"] = meta_table["Schedule"]["Start time"].loc[0]
|
|
304
|
+
self.META["download_date"] = meta_table["Export"].loc[0, "Export Time"]
|
|
305
|
+
|
|
306
|
+
with warnings.catch_warnings():
|
|
307
|
+
warnings.simplefilter("ignore")
|
|
308
|
+
self.DATA = pd.read_excel(file_path, sheet_name="Data", header=1)
|
|
309
|
+
column_names = {}
|
|
310
|
+
for col in self.DATA:
|
|
311
|
+
if col == "Time":
|
|
312
|
+
col_name = "TIME"
|
|
313
|
+
elif col == "Temperature":
|
|
314
|
+
col_name = "channel_1"
|
|
315
|
+
else:
|
|
316
|
+
col_name = f"channel_{int(col.split('.')[-1]) + 1}"
|
|
317
|
+
column_names[col] = col_name
|
|
318
|
+
self.DATA.rename(columns=column_names, inplace=True)
|
|
319
|
+
|
|
320
|
+
for col in self.DATA:
|
|
321
|
+
if col == "TIME":
|
|
322
|
+
self.DATA["TIME"] = pd.to_datetime(self.DATA["TIME"], format="%Y-%m-%d %H:%M:%S.%f")
|
|
323
|
+
else:
|
|
324
|
+
self.DATA[col] = pd.to_numeric(self.DATA[col], errors='coerce')
|
|
325
|
+
return
|
|
326
|
+
|
|
327
|
+
def _read_standard_rsk_format(self, file_path: str):
|
|
328
|
+
raw_meta = {}
|
|
329
|
+
try:
|
|
330
|
+
with RSK(file_path) as rsk:
|
|
331
|
+
rsk.open()
|
|
332
|
+
rsk.readdata()
|
|
333
|
+
rsk_data = rsk.data
|
|
334
|
+
raw_meta["calibration"] = rsk.calibrations
|
|
335
|
+
raw_meta["deployment"] = rsk.deployment
|
|
336
|
+
raw_meta["instrument"] = rsk.instrument
|
|
337
|
+
raw_meta["schedule"] = rsk.scheduleInfo
|
|
338
|
+
raw_meta["parameter key"] = rsk.parameterKeys
|
|
339
|
+
raw_meta["epoch"] = rsk.epoch
|
|
340
|
+
except NameError:
|
|
341
|
+
raise ModuleNotFoundError("You must install pyRSKtools")
|
|
342
|
+
except sqlite3.OperationalError:
|
|
343
|
+
raise RuntimeError("An error occurred when opening the .rsk file. Try opening the .rsk file in the ruskin\n"
|
|
344
|
+
" software then rerunning the code.")
|
|
345
|
+
self.DATA = pd.DataFrame(rsk_data)
|
|
346
|
+
self.META["logger_model"] = raw_meta["instrument"].model
|
|
347
|
+
self.META["logger_sn"] = str(raw_meta["instrument"].serialID)
|
|
348
|
+
self.META["download_date"] = raw_meta["deployment"].timeOfDownload.astype(dt.datetime)
|
|
349
|
+
self.META["sample_interval"] = dt.timedelta(seconds=raw_meta["schedule"].samplingPeriod / 1000)
|
|
350
|
+
self.META["logging_start"] = raw_meta["epoch"].startTime
|
|
351
|
+
utc_offset = [element.value for element in raw_meta["parameter key"] if element.key == "OFFSET_FROM_UTC"][0]
|
|
352
|
+
if pd.notna(utc_offset) and str(utc_offset).lower() != "nan":
|
|
353
|
+
self.META["utc_offset"] = int(round(float(utc_offset) * 3600, 0))
|
|
354
|
+
self.META["calibration_parameters"] = {}
|
|
355
|
+
for cal in raw_meta["calibration"]:
|
|
356
|
+
self.META["calibration_parameters"][f"channel_{cal.channelOrder}"] = {}
|
|
357
|
+
self.META["calibration_parameters"][f"channel_{cal.channelOrder}"]["a"] = cal.c[0]
|
|
358
|
+
self.META["calibration_parameters"][f"channel_{cal.channelOrder}"]["b"] = cal.c[1]
|
|
359
|
+
self.META["calibration_parameters"][f"channel_{cal.channelOrder}"]["c"] = cal.c[2]
|
|
360
|
+
self.META["calibration_parameters"][f"channel_{cal.channelOrder}"]["d"] = cal.c[3]
|
|
361
|
+
column_names = {}
|
|
362
|
+
for col in self.DATA:
|
|
363
|
+
if col == "timestamp":
|
|
364
|
+
col_name = "TIME"
|
|
365
|
+
elif col == "temperature":
|
|
366
|
+
col_name = "channel_1"
|
|
367
|
+
else:
|
|
368
|
+
col_name = f"channel_{int(col[-1]) + 1}"
|
|
369
|
+
column_names[col] = col_name
|
|
370
|
+
self.DATA.rename(columns=column_names, inplace=True)
|
|
371
|
+
return
|