PyDPEET 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pydpeet/__init__.py +56 -0
- pydpeet/citations/__init__.py +7 -0
- pydpeet/citations/citeme.py +397 -0
- pydpeet/citations/html_writer.py +137 -0
- pydpeet/dev_utils/__init__.py +7 -0
- pydpeet/dev_utils/generate_inits/__init__.py +7 -0
- pydpeet/dev_utils/generate_inits/generate_inits.py +327 -0
- pydpeet/io/__init__.py +7 -0
- pydpeet/io/configs/__init__.py +7 -0
- pydpeet/io/configs/config.py +227 -0
- pydpeet/io/configs/const.py +1 -0
- pydpeet/io/convert.py +594 -0
- pydpeet/io/device/__init__.py +7 -0
- pydpeet/io/device/arbin_4_23_PV090331/__init__.py +7 -0
- pydpeet/io/device/arbin_4_23_PV090331/formatter.py +22 -0
- pydpeet/io/device/arbin_4_23_PV090331/mapper.py +18 -0
- pydpeet/io/device/arbin_4_23_PV090331/reader.py +68 -0
- pydpeet/io/device/arbin_8_00_PV221201/__init__.py +7 -0
- pydpeet/io/device/arbin_8_00_PV221201/formatter.py +26 -0
- pydpeet/io/device/arbin_8_00_PV221201/mapper.py +18 -0
- pydpeet/io/device/arbin_8_00_PV221201/reader.py +66 -0
- pydpeet/io/device/basytec_6_3_1_0/__init__.py +7 -0
- pydpeet/io/device/basytec_6_3_1_0/formatter.py +18 -0
- pydpeet/io/device/basytec_6_3_1_0/mapper.py +18 -0
- pydpeet/io/device/basytec_6_3_1_0/reader.py +54 -0
- pydpeet/io/device/digatron_4_20_6_236/__init__.py +7 -0
- pydpeet/io/device/digatron_4_20_6_236/formatter.py +27 -0
- pydpeet/io/device/digatron_4_20_6_236/mapper.py +18 -0
- pydpeet/io/device/digatron_4_20_6_236/reader.py +45 -0
- pydpeet/io/device/digatron_eis_4_20_6_236/__init__.py +7 -0
- pydpeet/io/device/digatron_eis_4_20_6_236/formatter.py +39 -0
- pydpeet/io/device/digatron_eis_4_20_6_236/mapper.py +18 -0
- pydpeet/io/device/digatron_eis_4_20_6_236/reader.py +42 -0
- pydpeet/io/device/neware_8_0_0_516/__init__.py +7 -0
- pydpeet/io/device/neware_8_0_0_516/formatter.py +31 -0
- pydpeet/io/device/neware_8_0_0_516/mapper.py +18 -0
- pydpeet/io/device/neware_8_0_0_516/reader.py +397 -0
- pydpeet/io/device/parstat_2_63_3/__init__.py +7 -0
- pydpeet/io/device/parstat_2_63_3/formatter.py +34 -0
- pydpeet/io/device/parstat_2_63_3/mapper.py +18 -0
- pydpeet/io/device/parstat_2_63_3/reader.py +34 -0
- pydpeet/io/device/safion_1_9/__init__.py +7 -0
- pydpeet/io/device/safion_1_9/formatter.py +25 -0
- pydpeet/io/device/safion_1_9/mapper.py +18 -0
- pydpeet/io/device/safion_1_9/reader.py +89 -0
- pydpeet/io/device/zahner/__init__.py +7 -0
- pydpeet/io/device/zahner/formatter.py +73 -0
- pydpeet/io/device/zahner/mapper.py +34 -0
- pydpeet/io/device/zahner/reader.py +46 -0
- pydpeet/io/device/zahner_new/__init__.py +7 -0
- pydpeet/io/device/zahner_new/formatter.py +72 -0
- pydpeet/io/device/zahner_new/mapper.py +50 -0
- pydpeet/io/device/zahner_new/reader.py +46 -0
- pydpeet/io/map.py +69 -0
- pydpeet/io/read.py +50 -0
- pydpeet/io/utils/__init__.py +7 -0
- pydpeet/io/utils/ext_path.py +34 -0
- pydpeet/io/utils/formatter_utils.py +429 -0
- pydpeet/io/utils/load_custom_module.py +33 -0
- pydpeet/io/utils/timing.py +36 -0
- pydpeet/io/write.py +99 -0
- pydpeet/process/__init__.py +7 -0
- pydpeet/process/analyze/__init__.py +7 -0
- pydpeet/process/analyze/average.py +297 -0
- pydpeet/process/analyze/capacity.py +208 -0
- pydpeet/process/analyze/configs/__init__.py +7 -0
- pydpeet/process/analyze/configs/battery_config.py +38 -0
- pydpeet/process/analyze/configs/ocv_config.py +177 -0
- pydpeet/process/analyze/configs/step_analyzer_config.py +178 -0
- pydpeet/process/analyze/cycle.py +63 -0
- pydpeet/process/analyze/efficiency.py +182 -0
- pydpeet/process/analyze/energy.py +51 -0
- pydpeet/process/analyze/extract/__init__.py +7 -0
- pydpeet/process/analyze/extract/dva_ica.py +224 -0
- pydpeet/process/analyze/extract/ocv.py +196 -0
- pydpeet/process/analyze/power.py +32 -0
- pydpeet/process/analyze/resistance.py +87 -0
- pydpeet/process/analyze/soc.py +418 -0
- pydpeet/process/analyze/soh.py +72 -0
- pydpeet/process/analyze/utils.py +122 -0
- pydpeet/process/merge/__init__.py +7 -0
- pydpeet/process/merge/series.py +274 -0
- pydpeet/process/sequence/__init__.py +7 -0
- pydpeet/process/sequence/configs/__init__.py +7 -0
- pydpeet/process/sequence/configs/config.py +17 -0
- pydpeet/process/sequence/step_analyzer.py +440 -0
- pydpeet/process/sequence/utils/__init__.py +7 -0
- pydpeet/process/sequence/utils/annotate/__init__.py +7 -0
- pydpeet/process/sequence/utils/annotate/annotate_primitives.py +419 -0
- pydpeet/process/sequence/utils/configs/CONFIG_Fallback.py +231 -0
- pydpeet/process/sequence/utils/configs/CONFIG_preprocessing.py +243 -0
- pydpeet/process/sequence/utils/configs/__init__.py +7 -0
- pydpeet/process/sequence/utils/console_prints/__init__.py +7 -0
- pydpeet/process/sequence/utils/console_prints/log_time.py +30 -0
- pydpeet/process/sequence/utils/postprocessing/__init__.py +7 -0
- pydpeet/process/sequence/utils/postprocessing/df_primitives_correction.py +166 -0
- pydpeet/process/sequence/utils/postprocessing/filter_df.py +168 -0
- pydpeet/process/sequence/utils/postprocessing/generate_instructions.py +297 -0
- pydpeet/process/sequence/utils/preprocessing/__init__.py +7 -0
- pydpeet/process/sequence/utils/preprocessing/calculate_thresholds.py +27 -0
- pydpeet/process/sequence/utils/processing/__init__.py +7 -0
- pydpeet/process/sequence/utils/processing/analyze_segments.py +368 -0
- pydpeet/process/sequence/utils/processing/attempt_to_merge_neighboring_segments.py +122 -0
- pydpeet/process/sequence/utils/processing/check_CV_results.py +226 -0
- pydpeet/process/sequence/utils/processing/check_power_zero_watt_segments.py +85 -0
- pydpeet/process/sequence/utils/processing/check_zero_length.py +117 -0
- pydpeet/process/sequence/utils/processing/split_in_segments.py +97 -0
- pydpeet/process/sequence/utils/processing/supress_smaller_segments.py +99 -0
- pydpeet/process/sequence/utils/processing/widen_constant_segments.py +185 -0
- pydpeet/process/sequence/utils/visualize/__init__.py +7 -0
- pydpeet/process/sequence/utils/visualize/visualize_data.py +288 -0
- pydpeet/res/__init__.py +7 -0
- pydpeet/settings.py +0 -0
- pydpeet/utils/__init__.py +7 -0
- pydpeet/utils/logging_style.py +28 -0
- pydpeet/version.py +0 -0
- pydpeet-0.2.0.dist-info/METADATA +85 -0
- pydpeet-0.2.0.dist-info/RECORD +122 -0
- pydpeet-0.2.0.dist-info/WHEEL +5 -0
- pydpeet-0.2.0.dist-info/licenses/AUTHORS.md +0 -0
- pydpeet-0.2.0.dist-info/licenses/LICENCE.md +27 -0
- pydpeet-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
from pydpeet.process.analyze.utils import (
|
|
7
|
+
StepTimer,
|
|
8
|
+
drop_duplicate_testtime,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def merge_into_series(
|
|
13
|
+
dfs: list[pd.DataFrame],
|
|
14
|
+
time_between_tests_seconds: float = 60.0,
|
|
15
|
+
verbose: bool = True,
|
|
16
|
+
sort_dfs: bool = True,
|
|
17
|
+
) -> pd.DataFrame:
|
|
18
|
+
"""
|
|
19
|
+
Run a list of DataFrames as a single aging test series.
|
|
20
|
+
|
|
21
|
+
Each DataFrame is treated as a single test in the series. The 'Test_Time[s]' column of each DataFrame is adjusted to create a continuous time axis. A pause row is added after each DataFrame (except the last one) to indicate a pause in the test series.
|
|
22
|
+
|
|
23
|
+
The function returns a single DataFrame with the merged test series.
|
|
24
|
+
|
|
25
|
+
Parameters:
|
|
26
|
+
df_list (List[List[pandas.DataFrame]]): List of DataFrames (or list of (df, filename) pairs).
|
|
27
|
+
time_between_tests_seconds (float, optional): Time between tests in seconds. Defaults to 60.0.
|
|
28
|
+
verbose (bool, optional): If True, log debug messages. Defaults to True.
|
|
29
|
+
sort_dfs (bool, optional): If True, sort the DataFrames by their 'Date_Time' column. Defaults to True.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
pandas.DataFrame: Merged DataFrame with a single test series.
|
|
33
|
+
"""
|
|
34
|
+
if not dfs:
|
|
35
|
+
logging.info("No DataFrames provided.")
|
|
36
|
+
return pd.DataFrame()
|
|
37
|
+
|
|
38
|
+
if time_between_tests_seconds <= 0:
|
|
39
|
+
raise ValueError("time_between_tests_seconds must be > 0")
|
|
40
|
+
|
|
41
|
+
first_item = dfs[0]
|
|
42
|
+
if isinstance(first_item, tuple | list) and len(first_item) == 2:
|
|
43
|
+
# Already (df, filename) pairs
|
|
44
|
+
input_pairs = dfs
|
|
45
|
+
else:
|
|
46
|
+
# Wrap plain DataFrames with None as filename
|
|
47
|
+
input_pairs = [(df, None) for df in dfs]
|
|
48
|
+
|
|
49
|
+
if verbose:
|
|
50
|
+
logging.info("Sorting DataFrames...")
|
|
51
|
+
|
|
52
|
+
if sort_dfs:
|
|
53
|
+
sorted_dfs = _sort_dfs(input_pairs, verbose=verbose)
|
|
54
|
+
else:
|
|
55
|
+
# Keep only the DataFrame from the pairs (df, filename)
|
|
56
|
+
sorted_dfs = [df for df, _ in input_pairs]
|
|
57
|
+
|
|
58
|
+
time_offset = 0.0
|
|
59
|
+
numeric_cols, datetime_cols, object_cols = [], [], []
|
|
60
|
+
col_order = []
|
|
61
|
+
numeric_storage = []
|
|
62
|
+
other_storage = {}
|
|
63
|
+
valid_index = 0 # counts only valid DataFrames
|
|
64
|
+
|
|
65
|
+
if verbose:
|
|
66
|
+
logging.info(f"Adjusting Testtime and adding {time_between_tests_seconds}s pauses...")
|
|
67
|
+
|
|
68
|
+
with StepTimer(verbose) as st:
|
|
69
|
+
for i, raw_df in enumerate(sorted_dfs):
|
|
70
|
+
if raw_df is None or raw_df.empty or "Test_Time[s]" not in raw_df.columns:
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
df = raw_df.copy()
|
|
74
|
+
|
|
75
|
+
# Skip battery description files (existing behavior)
|
|
76
|
+
if "Step_Count" in df.columns:
|
|
77
|
+
first_val = df["Step_Count"].iloc[0]
|
|
78
|
+
if isinstance(first_val, str) and first_val.lower() == "test":
|
|
79
|
+
logging.warning(f"Skipping DataFrame {i} because Step_Count starts with 'Test'")
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
last_valid_idx = df["Test_Time[s]"].last_valid_index()
|
|
83
|
+
if last_valid_idx is None:
|
|
84
|
+
logging.info(f"DataFrame {i} skipped: 'Test_Time[s]' is all NaN")
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
# Initialize column types only once
|
|
88
|
+
if not numeric_cols:
|
|
89
|
+
col_order = df.columns.tolist()
|
|
90
|
+
for column in df.columns:
|
|
91
|
+
dt = df[column].dtype
|
|
92
|
+
if pd.api.types.is_numeric_dtype(dt):
|
|
93
|
+
numeric_cols.append(column)
|
|
94
|
+
elif pd.api.types.is_datetime64_any_dtype(dt):
|
|
95
|
+
datetime_cols.append(column)
|
|
96
|
+
else:
|
|
97
|
+
object_cols.append(column)
|
|
98
|
+
other_storage = {c: [] for c in datetime_cols + object_cols}
|
|
99
|
+
|
|
100
|
+
# Convert Step_Count to int64
|
|
101
|
+
if "Step_Count" in df.columns:
|
|
102
|
+
df["Step_Count"] = pd.to_numeric(df["Step_Count"], errors="coerce").fillna(0).astype(np.int64)
|
|
103
|
+
|
|
104
|
+
# Ensure Test_Time[s] is numeric and safe for addition
|
|
105
|
+
df["Test_Time[s]"] = pd.to_numeric(df["Test_Time[s]"], errors="coerce").fillna(0.0)
|
|
106
|
+
|
|
107
|
+
# --- Apply the current cumulative offset to this DataFrame ---
|
|
108
|
+
df["Test_Time[s]"] = df["Test_Time[s]"] + time_offset
|
|
109
|
+
|
|
110
|
+
# Assign sequential TestIndex
|
|
111
|
+
df["TestIndex"] = valid_index
|
|
112
|
+
valid_index += 1
|
|
113
|
+
|
|
114
|
+
# Ensure TestIndex is tracked in numeric columns / col_order
|
|
115
|
+
if "TestIndex" not in numeric_cols:
|
|
116
|
+
numeric_cols.append("TestIndex")
|
|
117
|
+
if "TestIndex" not in col_order:
|
|
118
|
+
col_order.append("TestIndex")
|
|
119
|
+
|
|
120
|
+
# Store numeric and other columns
|
|
121
|
+
numeric_storage.append(df[numeric_cols].to_numpy())
|
|
122
|
+
for column in datetime_cols + object_cols:
|
|
123
|
+
other_storage[column].extend(df[column].tolist())
|
|
124
|
+
|
|
125
|
+
# Compute the maximum Testtime after offset (this is the basis for next offset)
|
|
126
|
+
df_max = df["Test_Time[s]"].dropna().max()
|
|
127
|
+
|
|
128
|
+
# If not the last DataFrame, add a pause row placed at df_max + time_between_tests_seconds
|
|
129
|
+
if i < len(sorted_dfs) - 1:
|
|
130
|
+
pause_value = float(df_max + time_between_tests_seconds)
|
|
131
|
+
pause_numeric = []
|
|
132
|
+
for column in numeric_cols:
|
|
133
|
+
if column == "Test_Time[s]":
|
|
134
|
+
pause_numeric.append(pause_value)
|
|
135
|
+
elif column == "TestIndex":
|
|
136
|
+
pause_numeric.append(-1)
|
|
137
|
+
else:
|
|
138
|
+
pause_numeric.append(np.nan)
|
|
139
|
+
# ensure shape matches (1, n_numeric_cols)
|
|
140
|
+
numeric_storage.append(np.array([pause_numeric]))
|
|
141
|
+
|
|
142
|
+
for column in datetime_cols:
|
|
143
|
+
other_storage[column].append(pd.NaT)
|
|
144
|
+
for column in object_cols:
|
|
145
|
+
other_storage[column].append(pd.NA)
|
|
146
|
+
|
|
147
|
+
if verbose:
|
|
148
|
+
st.log(f"Added pause row after DataFrame {i} at {pause_value}s")
|
|
149
|
+
|
|
150
|
+
# --- Set time_offset for the next DataFrame to df_max + pause ---
|
|
151
|
+
time_offset = df_max + time_between_tests_seconds
|
|
152
|
+
else:
|
|
153
|
+
# Last DataFrame: update time_offset to the end of this df (no extra pause needed)
|
|
154
|
+
time_offset = df_max
|
|
155
|
+
|
|
156
|
+
# If no valid numeric data, return empty DataFrame
|
|
157
|
+
if not numeric_storage:
|
|
158
|
+
return pd.DataFrame()
|
|
159
|
+
|
|
160
|
+
with StepTimer(verbose) as st:
|
|
161
|
+
logging.info("Merging DataFrames...")
|
|
162
|
+
numeric_array = np.vstack(numeric_storage)
|
|
163
|
+
|
|
164
|
+
data_dict = {}
|
|
165
|
+
for idx, column in enumerate(numeric_cols):
|
|
166
|
+
data_dict[column] = numeric_array[:, idx]
|
|
167
|
+
logging.info(f"Merged numeric column: {column}")
|
|
168
|
+
for column in datetime_cols + object_cols:
|
|
169
|
+
data_dict[column] = other_storage[column]
|
|
170
|
+
logging.info(f"Merged non-numeric column: {column}")
|
|
171
|
+
|
|
172
|
+
dfs_merged = pd.DataFrame(data_dict, columns=col_order)
|
|
173
|
+
dfs_merged["Test_Time[s]"] = dfs_merged["Test_Time[s]"].astype(float)
|
|
174
|
+
st.log(f"Final merged DataFrame shape: {dfs_merged.shape}")
|
|
175
|
+
|
|
176
|
+
dfs_merged = drop_duplicate_testtime(dfs_merged)
|
|
177
|
+
|
|
178
|
+
return dfs_merged
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _sort_dfs(
|
|
182
|
+
dfs: list[pd.DataFrame],
|
|
183
|
+
verbose: bool = True,
|
|
184
|
+
) -> list[pd.DataFrame]:
|
|
185
|
+
"""
|
|
186
|
+
Sorts a list of DataFrames by their 'Date_Time' column.
|
|
187
|
+
Falls back to filename order if no valid absolute time is found.
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
df_list : list of pandas.DataFrame or list of (DataFrame, str)
|
|
192
|
+
List of DataFrames to be sorted. Optionally, pass (df, filename) tuples
|
|
193
|
+
so filenames can be used as a fallback sorting key.
|
|
194
|
+
|
|
195
|
+
Returns
|
|
196
|
+
-------
|
|
197
|
+
list of pandas.DataFrame
|
|
198
|
+
Sorted list of DataFrames.
|
|
199
|
+
"""
|
|
200
|
+
if not dfs:
|
|
201
|
+
return []
|
|
202
|
+
|
|
203
|
+
time_col = "Date_Time"
|
|
204
|
+
|
|
205
|
+
# ensure df_list always work with (df, filename) pairs
|
|
206
|
+
if isinstance(dfs[0], tuple | list) and len(dfs[0]) == 2:
|
|
207
|
+
pairs = [(df, fn) for df, fn in dfs if df is not None and not df.empty]
|
|
208
|
+
else:
|
|
209
|
+
pairs = [(df, None) for df in dfs if df is not None and not df.empty]
|
|
210
|
+
|
|
211
|
+
if not pairs:
|
|
212
|
+
logging.warning("No valid DataFrames found.")
|
|
213
|
+
return []
|
|
214
|
+
|
|
215
|
+
# Check if at least one DF has a valid absolute time column
|
|
216
|
+
has_abs_time = all(time_col in df.columns and df[time_col].notna().any() for df, _ in pairs)
|
|
217
|
+
|
|
218
|
+
if has_abs_time:
|
|
219
|
+
if verbose:
|
|
220
|
+
logging.info("Sorting DataFrames by absolute time...")
|
|
221
|
+
sorted_pairs = sorted(
|
|
222
|
+
pairs,
|
|
223
|
+
key=lambda x: (
|
|
224
|
+
pd.to_datetime(x[0][time_col].dropna().iloc[0])
|
|
225
|
+
if time_col in x[0].columns and x[0][time_col].notna().any()
|
|
226
|
+
else pd.Timestamp.min
|
|
227
|
+
),
|
|
228
|
+
)
|
|
229
|
+
else:
|
|
230
|
+
if any(fn is not None for _, fn in pairs):
|
|
231
|
+
if verbose:
|
|
232
|
+
logging.info("No valid absolute time found, sorting by filename...")
|
|
233
|
+
sorted_pairs = sorted(pairs, key=lambda x: x[1] or "")
|
|
234
|
+
else:
|
|
235
|
+
if verbose:
|
|
236
|
+
logging.info("No valid absolute time found, keeping original order...")
|
|
237
|
+
sorted_pairs = pairs
|
|
238
|
+
|
|
239
|
+
if verbose:
|
|
240
|
+
logging.info("Sorting complete.")
|
|
241
|
+
|
|
242
|
+
return [df for df, _ in sorted_pairs]
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def merge_into_campaign(
|
|
246
|
+
dfs_list: list[list[pd.DataFrame]],
|
|
247
|
+
verbose: bool = True,
|
|
248
|
+
) -> list[pd.DataFrame]:
|
|
249
|
+
"""
|
|
250
|
+
Execute a list of test series and return a list of merged DataFrames.
|
|
251
|
+
|
|
252
|
+
Parameters:
|
|
253
|
+
-----------
|
|
254
|
+
test_series_list : List[List[pandas.DataFrame]]
|
|
255
|
+
List of lists of DataFrames, where each DataFrame represents a single test.
|
|
256
|
+
verbose : bool, optional
|
|
257
|
+
If True, log debug messages.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
--------
|
|
261
|
+
List of pandas.DataFrame
|
|
262
|
+
List of DataFrames, where each DataFrame represents a merged test series.
|
|
263
|
+
"""
|
|
264
|
+
test_campaigns = []
|
|
265
|
+
for test in dfs_list:
|
|
266
|
+
with StepTimer(verbose) as st:
|
|
267
|
+
test_campaign = merge_into_series(test, verbose=verbose)
|
|
268
|
+
st.log("Executed one test series")
|
|
269
|
+
test_campaigns.append(test_campaign)
|
|
270
|
+
|
|
271
|
+
if verbose:
|
|
272
|
+
logging.info(f"Completed campaign with {len(test_campaigns)} test series.")
|
|
273
|
+
|
|
274
|
+
return test_campaigns
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
BASYTEC_CTS = [ # BaSyTec
|
|
2
|
+
0.002, # ACCURACY_VOLTAGE_SIGNAL
|
|
3
|
+
0.003, # ACCURACY_CURRENT_SIGNAL
|
|
4
|
+
0.002, # ACCURACY_VOLTAGE_MEASUREMENT
|
|
5
|
+
0.003, # ACCURACY_CURRENT_MEASUREMENT
|
|
6
|
+
6, # FS_VOLTAGE
|
|
7
|
+
5, # FS_CURRENT
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
NEWARE_CT_4008Q_5V12A_S1 = [
|
|
11
|
+
0.0005, # ACCURACY_VOLTAGE_SIGNAL
|
|
12
|
+
0.0005, # ACCURACY_CURRENT_SIGNAL
|
|
13
|
+
0.0005, # ACCURACY_VOLTAGE_MEASUREMENT
|
|
14
|
+
0.0005, # ACCURACY_CURRENT_MEASUREMENT
|
|
15
|
+
5, # FS_VOLTAGE
|
|
16
|
+
3, # FS_CURRENT
|
|
17
|
+
]
|