nirfmxvna 26.3.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.
- nirfmxvna/__init__.py +106 -0
- nirfmxvna/attributes.py +2234 -0
- nirfmxvna/enums.py +1048 -0
- nirfmxvna/errors.py +96 -0
- nirfmxvna/examples/nirfmxvna_calibration_with_calset_save_two_port.py +186 -0
- nirfmxvna/examples/nirfmxvna_pulsed_s_params.py +174 -0
- nirfmxvna/examples/nirfmxvna_s_params_corrected_with_calset_load_two_port_grpc.py +234 -0
- nirfmxvna/examples/nirfmxvna_waves.py +174 -0
- nirfmxvna/grpc_session_options.py +64 -0
- nirfmxvna/internal/_custom_types.py +13 -0
- nirfmxvna/internal/_grpc_stub_interpreter.py +1757 -0
- nirfmxvna/internal/_helper.py +387 -0
- nirfmxvna/internal/_library.py +3790 -0
- nirfmxvna/internal/_library_interpreter.py +3011 -0
- nirfmxvna/internal/_library_singleton.py +53 -0
- nirfmxvna/internal/nidevice_pb2.py +37 -0
- nirfmxvna/internal/nidevice_pb2_grpc.py +3 -0
- nirfmxvna/internal/nirfmxvna_pb2.py +1094 -0
- nirfmxvna/internal/nirfmxvna_pb2_grpc.py +9990 -0
- nirfmxvna/internal/session_pb2.py +33 -0
- nirfmxvna/internal/session_pb2_grpc.py +3 -0
- nirfmxvna/iq.py +31 -0
- nirfmxvna/iq_configuration.py +290 -0
- nirfmxvna/iq_results.py +108 -0
- nirfmxvna/marker.py +31 -0
- nirfmxvna/marker_configuration.py +429 -0
- nirfmxvna/marker_results.py +168 -0
- nirfmxvna/s_params.py +31 -0
- nirfmxvna/s_params_configuration.py +1656 -0
- nirfmxvna/s_params_results.py +244 -0
- nirfmxvna/vna.py +12920 -0
- nirfmxvna/waves.py +31 -0
- nirfmxvna/waves_configuration.py +1255 -0
- nirfmxvna/waves_results.py +235 -0
- nirfmxvna-26.3.0.dist-info/METADATA +228 -0
- nirfmxvna-26.3.0.dist-info/RECORD +38 -0
- nirfmxvna-26.3.0.dist-info/WHEEL +4 -0
- nirfmxvna-26.3.0.dist-info/licenses/LICENSE +30 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"""Contains utility functions."""
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
from abc import ABC
|
|
5
|
+
from collections import defaultdict
|
|
6
|
+
from typing import Any, DefaultDict
|
|
7
|
+
|
|
8
|
+
import numpy
|
|
9
|
+
from fasteners import ReaderWriterLock
|
|
10
|
+
|
|
11
|
+
signal_name_prefix = "signal::"
|
|
12
|
+
result_name_prefix = "result::"
|
|
13
|
+
all_number_string = "::all"
|
|
14
|
+
segment_number_prefix = "segment"
|
|
15
|
+
sparam_number_prefix = "sparam"
|
|
16
|
+
wave_number_prefix = "wave"
|
|
17
|
+
calstep_number_prefix = "calstep"
|
|
18
|
+
pulsegen_number_prefix = "pulsegen"
|
|
19
|
+
marker_number_prefix = "marker"
|
|
20
|
+
port_name_prefix = "port::"
|
|
21
|
+
calkit_name_prefix = "ckit::"
|
|
22
|
+
connector_name_prefix = "conn::"
|
|
23
|
+
calibration_element_name_prefix = "calel::"
|
|
24
|
+
measurement_memory_name_prefix = "measmem::"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def prepend_signal_string(signal_name: str, selector_string: str) -> str:
|
|
28
|
+
"""Prepends the signal name to the selector string with 'signal::' prefix."""
|
|
29
|
+
updated_selector_string = []
|
|
30
|
+
|
|
31
|
+
if signal_name and selector_string:
|
|
32
|
+
updated_selector_string.append(f"{signal_name_prefix}{signal_name}/{selector_string}")
|
|
33
|
+
else:
|
|
34
|
+
if signal_name:
|
|
35
|
+
updated_selector_string.append(f"{signal_name_prefix}{signal_name}")
|
|
36
|
+
else:
|
|
37
|
+
updated_selector_string.append(selector_string)
|
|
38
|
+
|
|
39
|
+
return "".join(updated_selector_string)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def build_result_string(result_name: str) -> str:
|
|
43
|
+
"""Builds a result string with the result name prefixed by 'result::'."""
|
|
44
|
+
selector = []
|
|
45
|
+
if result_name:
|
|
46
|
+
if result_name.lower().startswith(result_name_prefix.lower()):
|
|
47
|
+
selector.append(result_name)
|
|
48
|
+
else:
|
|
49
|
+
selector.append(f"{result_name_prefix}{result_name}")
|
|
50
|
+
return "".join(selector)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def append_number(prefix: str, number: int, selector: str) -> str:
|
|
54
|
+
"""Appends a number to a prefix and selector string."""
|
|
55
|
+
selector_string = []
|
|
56
|
+
if number == -1:
|
|
57
|
+
number_string = all_number_string
|
|
58
|
+
else:
|
|
59
|
+
number_string = str(number)
|
|
60
|
+
|
|
61
|
+
if not selector:
|
|
62
|
+
selector_string.append(f"{prefix}{number_string}")
|
|
63
|
+
else:
|
|
64
|
+
selector_string.append(f"{selector}/{prefix}{number_string}")
|
|
65
|
+
|
|
66
|
+
return "".join(selector_string)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def split_string_by_comma(comma_separated_string: str) -> Any:
|
|
70
|
+
"""Splits a comma-separated string into a list."""
|
|
71
|
+
return comma_separated_string.split(",") if comma_separated_string else []
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def create_comma_separated_string(string_list: Any) -> str:
|
|
75
|
+
"""Helper function to join string list with commas."""
|
|
76
|
+
validate_not_none(string_list, "string_list")
|
|
77
|
+
|
|
78
|
+
if len(string_list) > 0:
|
|
79
|
+
return ",".join(string_list)
|
|
80
|
+
|
|
81
|
+
return "" # Return empty string if string_list is empty
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def validate_not_none(parameter: Any, parameter_name: str) -> None:
|
|
85
|
+
"""Validates that the parameter is not None."""
|
|
86
|
+
if parameter is None:
|
|
87
|
+
raise ValueError(f"{parameter_name} cannot be None.")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def validate_numpy_array(parameter: Any, parameter_name: Any, expected_data_type: Any) -> None:
|
|
91
|
+
"""Validates that the parameter is a numpy array with the expected data type."""
|
|
92
|
+
if parameter is None:
|
|
93
|
+
raise ValueError(f"{parameter_name} cannot be None.")
|
|
94
|
+
if type(parameter) is not numpy.ndarray:
|
|
95
|
+
raise TypeError(f"{parameter_name} must be numpy.ndarray, is {type(parameter)}")
|
|
96
|
+
if numpy.isfortran(parameter) is True:
|
|
97
|
+
raise TypeError(f"{parameter_name} must be in C-order")
|
|
98
|
+
if parameter.dtype is not numpy.dtype(f"{expected_data_type}"):
|
|
99
|
+
raise TypeError(
|
|
100
|
+
f"{parameter_name} must be numpy.ndarray of dtype={expected_data_type}, is "
|
|
101
|
+
+ str(parameter.dtype)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def validate_signal_not_empty(value: str, parameter_name: str) -> None:
|
|
106
|
+
"""Validates that the signal name is not empty."""
|
|
107
|
+
if len(value) == 0:
|
|
108
|
+
raise ValueError(f"{parameter_name} cannot be empty.")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def validate_and_update_selector_string(selector_string: str, obj: Any) -> str:
|
|
112
|
+
"""Validates and updates the selector string based on the signal configuration mode."""
|
|
113
|
+
param_name = "selector_string"
|
|
114
|
+
|
|
115
|
+
if obj._signal_configuration_mode == "Signal":
|
|
116
|
+
if selector_string.lower().startswith(signal_name_prefix.lower()):
|
|
117
|
+
raise ValueError(f"Invalid {param_name}.")
|
|
118
|
+
return prepend_signal_string(obj.signal_configuration_name, selector_string)
|
|
119
|
+
|
|
120
|
+
return ""
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def validate_and_remove_signal_qualifier(signal_name: str, parameter_name: Any) -> str:
|
|
124
|
+
r"""This function checks if the "signal_name" contains the qualified name.
|
|
125
|
+
If so, removes the qualifier "signal::" and returns just the name.
|
|
126
|
+
"""
|
|
127
|
+
validate_not_none(signal_name, parameter_name)
|
|
128
|
+
signal_prefix = signal_name_prefix
|
|
129
|
+
if signal_name.lower().startswith(signal_prefix.lower()):
|
|
130
|
+
return signal_name[len(signal_prefix) :]
|
|
131
|
+
return signal_name
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def validate_array_parameter_sizes_are_equal(
|
|
135
|
+
array_parameter_names: Any, *array_parameters: Any
|
|
136
|
+
) -> int:
|
|
137
|
+
"""Validates that all array parameters have the same size."""
|
|
138
|
+
array_size = 0
|
|
139
|
+
length = -1
|
|
140
|
+
|
|
141
|
+
for parameter in array_parameters:
|
|
142
|
+
if parameter is not None and len(parameter) != 0:
|
|
143
|
+
if length == -1:
|
|
144
|
+
# Get the first non-none item's length if not already obtained
|
|
145
|
+
length = len(parameter)
|
|
146
|
+
array_size = length
|
|
147
|
+
else:
|
|
148
|
+
# Compare consecutive array lengths
|
|
149
|
+
if len(parameter) != length:
|
|
150
|
+
raise ValueError(
|
|
151
|
+
f"Array size mismatch: {get_non_none_array_parameter_names(array_parameter_names, array_parameters)}"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
return array_size
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def get_non_none_array_parameter_names(array_parameter_names: Any, array_parameters: Any) -> Any:
|
|
158
|
+
"""Returns a comma-separated string of non-None array parameter names."""
|
|
159
|
+
parameter_name_list = []
|
|
160
|
+
comma_separator = ","
|
|
161
|
+
|
|
162
|
+
for i in range(len(array_parameters)):
|
|
163
|
+
if array_parameters[i] is not None:
|
|
164
|
+
parameter_name_list.append(array_parameter_names[i])
|
|
165
|
+
|
|
166
|
+
# Join the parameter names using the comma separator
|
|
167
|
+
return comma_separator.join(parameter_name_list)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def contains(source: str, to_check: str, comparison: str = "case_insensitive") -> bool:
|
|
171
|
+
"""Checks if 'to_check' is contained in 'source' based on the specified comparison type."""
|
|
172
|
+
if comparison == "case_insensitive":
|
|
173
|
+
return to_check.lower() in source.lower()
|
|
174
|
+
elif comparison == "case_sensitive":
|
|
175
|
+
return to_check in source
|
|
176
|
+
else:
|
|
177
|
+
raise ValueError("Invalid comparison type. Use 'case_insensitive' or 'case_sensitive'.")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def get_session_handle(value: Any) -> Any:
|
|
181
|
+
"""Get session handle(s)."""
|
|
182
|
+
if value is None:
|
|
183
|
+
raise ValueError(f"{value} cannot be None.")
|
|
184
|
+
elif isinstance(value, list):
|
|
185
|
+
session_handle_list = [x._interpreter.get_session_handle() for x in value]
|
|
186
|
+
return session_handle_list
|
|
187
|
+
else:
|
|
188
|
+
return value._interpreter.get_session_handle()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def build_segment_string(selector_string, segment_number):
|
|
192
|
+
"""Builds a segment string."""
|
|
193
|
+
segment_number_string = append_number(segment_number_prefix, segment_number, selector_string)
|
|
194
|
+
return segment_number_string
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def build_s_parameter_string(selector_string, s_parameter_number):
|
|
198
|
+
"""Builds a s_parameter string."""
|
|
199
|
+
sparam_number_string = append_number(sparam_number_prefix, s_parameter_number, selector_string)
|
|
200
|
+
return sparam_number_string
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def build_wave_string(selector_string, wave_number):
|
|
204
|
+
"""Builds a wave string."""
|
|
205
|
+
wave_number_string = append_number(wave_number_prefix, wave_number, selector_string)
|
|
206
|
+
return wave_number_string
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def build_calstep_string(selector_string, calstep_number):
|
|
210
|
+
"""Builds a calstep string."""
|
|
211
|
+
calstep_number_string = append_number(calstep_number_prefix, calstep_number, selector_string)
|
|
212
|
+
return calstep_number_string
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def build_pulse_generator_string(selector_string, pulse_generator_number):
|
|
216
|
+
"""Builds a pulse_generator string."""
|
|
217
|
+
pulsegen_number_string = append_number(
|
|
218
|
+
pulsegen_number_prefix, pulse_generator_number, selector_string
|
|
219
|
+
)
|
|
220
|
+
return pulsegen_number_string
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def build_marker_string(selector_string, marker_number):
|
|
224
|
+
"""Builds a marker string."""
|
|
225
|
+
marker_number_string = append_number(marker_number_prefix, marker_number, selector_string)
|
|
226
|
+
return marker_number_string
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def build_port_string(selector_string, port_string):
|
|
230
|
+
"""Builds a port string."""
|
|
231
|
+
selector = []
|
|
232
|
+
|
|
233
|
+
if selector_string:
|
|
234
|
+
selector.append(selector_string)
|
|
235
|
+
|
|
236
|
+
if port_string:
|
|
237
|
+
if selector_string:
|
|
238
|
+
selector.append("/")
|
|
239
|
+
if port_string.lower().startswith(port_name_prefix.lower()):
|
|
240
|
+
selector.append(port_string)
|
|
241
|
+
else:
|
|
242
|
+
selector.append(f"{port_name_prefix}{port_string}")
|
|
243
|
+
|
|
244
|
+
return "".join(selector)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def build_calkit_string(selector_string, calkit_id):
|
|
248
|
+
"""Builds a calkit string."""
|
|
249
|
+
selector = []
|
|
250
|
+
|
|
251
|
+
if selector_string:
|
|
252
|
+
selector.append(selector_string)
|
|
253
|
+
|
|
254
|
+
if calkit_id:
|
|
255
|
+
if selector_string:
|
|
256
|
+
selector.append("/")
|
|
257
|
+
if calkit_id.lower().startswith(calkit_name_prefix.lower()):
|
|
258
|
+
selector.append(calkit_id)
|
|
259
|
+
else:
|
|
260
|
+
selector.append(f"{calkit_name_prefix}{calkit_id}")
|
|
261
|
+
|
|
262
|
+
return "".join(selector)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def build_connector_string(selector_string, connector_id):
|
|
266
|
+
"""Builds a connector string."""
|
|
267
|
+
selector = []
|
|
268
|
+
|
|
269
|
+
if selector_string:
|
|
270
|
+
selector.append(selector_string)
|
|
271
|
+
|
|
272
|
+
if connector_id:
|
|
273
|
+
if selector_string:
|
|
274
|
+
selector.append("/")
|
|
275
|
+
if connector_id.lower().startswith(connector_name_prefix.lower()):
|
|
276
|
+
selector.append(connector_id)
|
|
277
|
+
else:
|
|
278
|
+
selector.append(f"{connector_name_prefix}{connector_id}")
|
|
279
|
+
|
|
280
|
+
return "".join(selector)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def build_calibration_element_string(selector_string, calibration_element_id):
|
|
284
|
+
"""Builds a calibration_element string."""
|
|
285
|
+
selector = []
|
|
286
|
+
|
|
287
|
+
if selector_string:
|
|
288
|
+
selector.append(selector_string)
|
|
289
|
+
|
|
290
|
+
if calibration_element_id:
|
|
291
|
+
if selector_string:
|
|
292
|
+
selector.append("/")
|
|
293
|
+
if calibration_element_id.lower().startswith(calibration_element_name_prefix.lower()):
|
|
294
|
+
selector.append(calibration_element_id)
|
|
295
|
+
else:
|
|
296
|
+
selector.append(f"{calibration_element_name_prefix}{calibration_element_id}")
|
|
297
|
+
|
|
298
|
+
return "".join(selector)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def build_measurement_memory_string(selector_string, measurement_memory_name):
|
|
302
|
+
"""Builds a measurement_memory string."""
|
|
303
|
+
selector = []
|
|
304
|
+
|
|
305
|
+
if selector_string:
|
|
306
|
+
selector.append(selector_string)
|
|
307
|
+
|
|
308
|
+
if measurement_memory_name:
|
|
309
|
+
if selector_string:
|
|
310
|
+
selector.append("/")
|
|
311
|
+
if measurement_memory_name.lower().startswith(measurement_memory_name_prefix.lower()):
|
|
312
|
+
selector.append(measurement_memory_name)
|
|
313
|
+
else:
|
|
314
|
+
selector.append(f"{measurement_memory_name_prefix}{measurement_memory_name}")
|
|
315
|
+
|
|
316
|
+
return "".join(selector)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class SessionFunctionLock:
|
|
320
|
+
"""A class to manage read/write locks for session functions."""
|
|
321
|
+
|
|
322
|
+
_lock = ReaderWriterLock()
|
|
323
|
+
|
|
324
|
+
@classmethod
|
|
325
|
+
def enter_read_lock(cls) -> Any:
|
|
326
|
+
"""Acquires a read lock for session functions."""
|
|
327
|
+
cls._lock.acquire_read_lock()
|
|
328
|
+
|
|
329
|
+
@classmethod
|
|
330
|
+
def exit_read_lock(cls) -> Any:
|
|
331
|
+
"""Releases the read lock for session functions."""
|
|
332
|
+
cls._lock.release_read_lock()
|
|
333
|
+
|
|
334
|
+
@classmethod
|
|
335
|
+
def enter_write_lock(cls) -> Any:
|
|
336
|
+
"""Acquires a write lock for session functions."""
|
|
337
|
+
cls._lock.acquire_write_lock()
|
|
338
|
+
|
|
339
|
+
@classmethod
|
|
340
|
+
def exit_write_lock(cls) -> Any:
|
|
341
|
+
"""Releases the write lock for session functions."""
|
|
342
|
+
cls._lock.release_write_lock()
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
class ConcurrentDictionary:
|
|
346
|
+
"""A thread-safe dictionary that allows concurrent access."""
|
|
347
|
+
|
|
348
|
+
def __init__(self):
|
|
349
|
+
"""Initializes the ConcurrentDictionary with a thread lock and a default dictionary."""
|
|
350
|
+
self._lock = threading.RLock()
|
|
351
|
+
self._data: DefaultDict[str, Any] = defaultdict(lambda: None)
|
|
352
|
+
|
|
353
|
+
def __getitem__(self, key: str) -> Any:
|
|
354
|
+
"""Retrieves an item from the dictionary in a thread-safe manner."""
|
|
355
|
+
with self._lock:
|
|
356
|
+
return self._data[key]
|
|
357
|
+
|
|
358
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
359
|
+
"""Sets an item in the dictionary in a thread-safe manner."""
|
|
360
|
+
with self._lock:
|
|
361
|
+
self._data[key] = value
|
|
362
|
+
|
|
363
|
+
def __delitem__(self, key: str) -> None:
|
|
364
|
+
"""Deletes an item from the dictionary in a thread-safe manner."""
|
|
365
|
+
with self._lock:
|
|
366
|
+
del self._data[key]
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def validate_mimo_resource_name(device_names: Any, parameter_name: Any) -> None:
|
|
370
|
+
"""Validates the MIMO resource names."""
|
|
371
|
+
if parameter_name is None:
|
|
372
|
+
raise ValueError(f"{parameter_name} cannot be None.")
|
|
373
|
+
|
|
374
|
+
if isinstance(device_names, list) and len(device_names) > 1:
|
|
375
|
+
for device in device_names:
|
|
376
|
+
if not device: # Checks for None or empty string
|
|
377
|
+
raise ValueError("Empty resource name is not valid for MIMO.")
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
class SignalConfiguration(ABC):
|
|
381
|
+
"""Represents a signal configuration. Implement this interface to expose measurement functionality."""
|
|
382
|
+
|
|
383
|
+
signal_configuration_type = None
|
|
384
|
+
"""Type of the current signal configuration object."""
|
|
385
|
+
|
|
386
|
+
signal_configuration_name = ""
|
|
387
|
+
"""Name assigned to the current signal configuration object."""
|