BDPotentiometer 0.0.2__tar.gz → 0.0.3__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.
- {BDPotentiometer-0.0.2/src/BDPotentiometer.egg-info → BDPotentiometer-0.0.3}/PKG-INFO +9 -2
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer/__helpers.py +31 -14
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer/__init__.py +1 -1
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer/digital_potentiometer.py +54 -7
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer/digital_wiper.py +32 -12
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer/mcp4xxx/mcp4xx1.py +1 -1
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer/mcp4xxx/mcp4xxx.py +1 -1
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer/potentiometer.py +23 -17
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3/src/BDPotentiometer.egg-info}/PKG-INFO +9 -2
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer.egg-info/SOURCES.txt +0 -4
- BDPotentiometer-0.0.3/tests/test_version.py +51 -0
- BDPotentiometer-0.0.2/tests/test_digital_potentiometer.py +0 -289
- BDPotentiometer-0.0.2/tests/test_digital_wiper.py +0 -271
- BDPotentiometer-0.0.2/tests/test_helpers.py +0 -186
- BDPotentiometer-0.0.2/tests/test_potentiometer.py +0 -210
- BDPotentiometer-0.0.2/tests/test_version.py +0 -29
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/LICENSE +0 -0
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/README.md +0 -0
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/pyproject.toml +0 -0
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/setup.cfg +0 -0
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer/__gpiozero_helpers.py +0 -0
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer/mcp4xxx/__init__.py +0 -0
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer/mcp4xxx/mcp4xx2.py +0 -0
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer.egg-info/dependency_links.txt +0 -0
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer.egg-info/requires.txt +0 -0
- {BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: BDPotentiometer
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: BD Digital Potentiometer
|
|
5
5
|
Author-email: Anton Bondarenko <bond.anton@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -32,10 +32,17 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
32
32
|
Classifier: Operating System :: OS Independent
|
|
33
33
|
Requires-Python: >=3.9
|
|
34
34
|
Description-Content-Type: text/markdown
|
|
35
|
+
License-File: LICENSE
|
|
36
|
+
Requires-Dist: gpiozero>=1.6.2
|
|
35
37
|
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: gpiozero>=1.6.2; extra == "dev"
|
|
39
|
+
Requires-Dist: numpy; extra == "dev"
|
|
36
40
|
Provides-Extra: test
|
|
41
|
+
Requires-Dist: flake8; extra == "test"
|
|
42
|
+
Requires-Dist: pytest; extra == "test"
|
|
43
|
+
Requires-Dist: numpy; extra == "test"
|
|
37
44
|
Provides-Extra: lint
|
|
38
|
-
|
|
45
|
+
Requires-Dist: pylint; extra == "lint"
|
|
39
46
|
|
|
40
47
|
# BDPotentiometer
|
|
41
48
|
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
"""This module contains internal helper functions and not supposed to be used outside."""
|
|
2
2
|
|
|
3
3
|
from numbers import Real, Integral
|
|
4
|
+
from math import isnan
|
|
4
5
|
from typing import Union, Callable
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def check_number(num: Union[float, int, Real]) -> Union[float, int]:
|
|
8
9
|
"""
|
|
9
10
|
Checks if argument is a real number.
|
|
11
|
+
check_number does not allow argument to be `+inf` or `-inf`, or `NaN`.
|
|
12
|
+
Special float numbers `+0.0` and `-0.0` will be converted to just 0.0
|
|
13
|
+
|
|
10
14
|
:param num: variable to check.
|
|
11
15
|
:return: Unchanged `num` as float or int or raises ValueError if num is not a real number.
|
|
12
16
|
"""
|
|
13
17
|
if not isinstance(num, Real):
|
|
14
|
-
raise
|
|
18
|
+
raise TypeError(f"Expected integer or float number, got {type(num)}")
|
|
15
19
|
if isinstance(num, Integral):
|
|
16
20
|
return int(num)
|
|
21
|
+
if num in [float("inf"), float("-inf")]:
|
|
22
|
+
raise ValueError(f"Expected integer or float number, got {num}")
|
|
23
|
+
if isnan(num):
|
|
24
|
+
raise ValueError("Expected integer or float number, not NaN")
|
|
25
|
+
if num == 0:
|
|
26
|
+
return 0.0
|
|
17
27
|
return float(num)
|
|
18
28
|
|
|
19
29
|
|
|
@@ -57,11 +67,11 @@ def check_positive(num: Union[float, int]) -> Union[float, int]:
|
|
|
57
67
|
return num
|
|
58
68
|
|
|
59
69
|
|
|
60
|
-
def
|
|
70
|
+
def clamp(
|
|
61
71
|
value: Union[float, int], min_value: Union[float, int], max_value: Union[float, int]
|
|
62
72
|
) -> Union[float, int]:
|
|
63
73
|
"""
|
|
64
|
-
|
|
74
|
+
Clamp `value` to given range between `min_value` and `max_value`.
|
|
65
75
|
|
|
66
76
|
:param value: Either float or int number to be coerced.
|
|
67
77
|
:param min_value: Lower coerce range boundary.
|
|
@@ -74,14 +84,18 @@ def coerce(
|
|
|
74
84
|
if min_value > max_value:
|
|
75
85
|
min_value, max_value = max_value, min_value
|
|
76
86
|
if value < min_value:
|
|
87
|
+
if isinstance(value, float):
|
|
88
|
+
return float(min_value)
|
|
77
89
|
return min_value
|
|
78
90
|
if value > max_value:
|
|
91
|
+
if isinstance(value, float):
|
|
92
|
+
return float(max_value)
|
|
79
93
|
return max_value
|
|
80
94
|
return value
|
|
81
95
|
|
|
82
96
|
|
|
83
97
|
def build_tuple(
|
|
84
|
-
value: Union[float, int, tuple, list],
|
|
98
|
+
value: Union[float, int, tuple[float, ...], list[float, ...]],
|
|
85
99
|
num: Union[int, float],
|
|
86
100
|
func: Union[Callable, None] = None,
|
|
87
101
|
) -> tuple[float, ...]:
|
|
@@ -103,24 +117,24 @@ def build_tuple(
|
|
|
103
117
|
return val
|
|
104
118
|
|
|
105
119
|
if not isinstance(value, (float, int, tuple, list)):
|
|
106
|
-
raise
|
|
120
|
+
raise TypeError("Argument should be a number or list or tuple of numbers")
|
|
107
121
|
num = check_integer(check_positive(num))
|
|
108
122
|
if func is not None:
|
|
109
123
|
if not callable(func):
|
|
110
|
-
raise
|
|
124
|
+
raise TypeError("function must be callable or None")
|
|
111
125
|
else:
|
|
112
126
|
func = identity
|
|
113
127
|
|
|
114
|
-
if isinstance(value, (float, int)):
|
|
115
|
-
return tuple(float(func(value)) for _ in range(num))
|
|
116
128
|
if isinstance(value, (list, tuple)):
|
|
117
129
|
if len(value) != num:
|
|
118
130
|
raise ValueError(f"A tuple or list of length {num} expected")
|
|
119
|
-
return tuple(float(func(value_i)) for value_i in value)
|
|
131
|
+
return tuple(float(func(check_number(value_i))) for value_i in value)
|
|
132
|
+
value = check_number(value)
|
|
133
|
+
return tuple(float(func(value)) for _ in range(num))
|
|
120
134
|
|
|
121
135
|
|
|
122
136
|
def adjust_tuple(
|
|
123
|
-
value: Union[tuple[float], list[float]], num: int, default_value: float
|
|
137
|
+
value: Union[tuple[float, ...], list[float, ...]], num: int, default_value: float
|
|
124
138
|
) -> tuple[float, ...]:
|
|
125
139
|
"""
|
|
126
140
|
Adjusts tuple or list of floats to a given length `num` and returns a tuple.
|
|
@@ -133,11 +147,14 @@ def adjust_tuple(
|
|
|
133
147
|
:return: A tuple of floats of length `num`.
|
|
134
148
|
"""
|
|
135
149
|
if not isinstance(value, (tuple, list)):
|
|
136
|
-
raise
|
|
150
|
+
raise TypeError("Argument should be a list or a tuple of numbers")
|
|
137
151
|
num = check_integer(check_positive(num))
|
|
138
152
|
default_value = float(check_number(default_value))
|
|
139
153
|
if len(value) < num:
|
|
140
|
-
return tuple(
|
|
154
|
+
return tuple(
|
|
155
|
+
[float(check_number(value_i)) for value_i in value]
|
|
156
|
+
+ [default_value] * (num - len(value))
|
|
157
|
+
)
|
|
141
158
|
if len(value) > num:
|
|
142
|
-
return tuple(value)
|
|
143
|
-
return tuple(value)
|
|
159
|
+
return tuple(float(check_number(value[i])) for i in range(num))
|
|
160
|
+
return tuple(float(check_number(value_i)) for value_i in value)
|
{BDPotentiometer-0.0.2 → BDPotentiometer-0.0.3}/src/BDPotentiometer/digital_potentiometer.py
RENAMED
|
@@ -200,6 +200,51 @@ class DigitalPotentiometerDevice:
|
|
|
200
200
|
for i in range(self.channels_num):
|
|
201
201
|
self.channels[i].value = value[i]
|
|
202
202
|
|
|
203
|
+
def set_invert(self, channel: Union[int, str] = 0, invert: bool = False) -> int:
|
|
204
|
+
"""
|
|
205
|
+
Set the invert property for a given channel.
|
|
206
|
+
|
|
207
|
+
:param channel: Channel number or label (int | str)
|
|
208
|
+
:param invert: bool.
|
|
209
|
+
:return: Wiper position value as int.
|
|
210
|
+
"""
|
|
211
|
+
channel_number = self._get_channel_number_by_label_or_id(channel)
|
|
212
|
+
if channel_number is None:
|
|
213
|
+
raise ValueError(f"Channel {channel} not found.")
|
|
214
|
+
self.channels[channel_number].invert = invert
|
|
215
|
+
return self.channels[channel_number].value
|
|
216
|
+
|
|
217
|
+
def get_invert(self, channel: Union[int, str] = 0) -> bool:
|
|
218
|
+
"""
|
|
219
|
+
Get the invert property value for a given channel.
|
|
220
|
+
|
|
221
|
+
:param channel: Channel number or label (int | str)
|
|
222
|
+
:return: invert property value as bool.
|
|
223
|
+
"""
|
|
224
|
+
channel_number = self._get_channel_number_by_label_or_id(channel)
|
|
225
|
+
if channel_number is None:
|
|
226
|
+
raise ValueError(f"Channel {channel} not found.")
|
|
227
|
+
return self.channels[channel_number].invert
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def invert(self) -> tuple[bool, ...]:
|
|
231
|
+
"""
|
|
232
|
+
Tuple of the invert property of channels.
|
|
233
|
+
|
|
234
|
+
:return: Invert property values for all channels as a tuple of booleans.
|
|
235
|
+
"""
|
|
236
|
+
return tuple(wiper.invert for _, wiper in self.channels.items())
|
|
237
|
+
|
|
238
|
+
@invert.setter
|
|
239
|
+
def invert(self, invert: Union[list[bool], tuple[bool, ...]]) -> None:
|
|
240
|
+
if len(invert) != self.channels_num:
|
|
241
|
+
raise ValueError(
|
|
242
|
+
f"A tuple or list of length {self.channels_num} is expected."
|
|
243
|
+
)
|
|
244
|
+
for channel, wiper in self.channels.items():
|
|
245
|
+
wiper.invert = invert[channel]
|
|
246
|
+
|
|
247
|
+
|
|
203
248
|
def set_r_wb(self, channel: Union[int, str] = 0, resistance: float = 0) -> int:
|
|
204
249
|
"""
|
|
205
250
|
Set the resistance for given channel between B and W terminals
|
|
@@ -384,19 +429,21 @@ class DigitalPotentiometerDevice:
|
|
|
384
429
|
for channel, wiper in self.channels.items():
|
|
385
430
|
wiper.r_load = resistance[channel]
|
|
386
431
|
|
|
387
|
-
def set_voltage_in(
|
|
432
|
+
def set_voltage_in(
|
|
433
|
+
self, channel: Union[int, str] = 0, voltage: float = 0.0
|
|
434
|
+
) -> float:
|
|
388
435
|
"""
|
|
389
436
|
Set input voltage for a given channel number.
|
|
390
437
|
|
|
391
438
|
:param channel: Channel number (int).
|
|
392
|
-
:param voltage:
|
|
393
|
-
:return:
|
|
439
|
+
:param voltage: Requested input voltage (float).
|
|
440
|
+
:return: Set input voltage (float)
|
|
394
441
|
"""
|
|
395
442
|
channel_number = self._get_channel_number_by_label_or_id(channel)
|
|
396
443
|
if channel_number is None:
|
|
397
444
|
raise ValueError(f"Channel {channel} not found.")
|
|
398
445
|
self.channels[channel_number].voltage_in = voltage
|
|
399
|
-
return self.channels[channel_number].
|
|
446
|
+
return self.channels[channel_number].voltage_in
|
|
400
447
|
|
|
401
448
|
def get_voltage_in(self, channel: Union[int, str] = 0) -> float:
|
|
402
449
|
"""
|
|
@@ -430,19 +477,19 @@ class DigitalPotentiometerDevice:
|
|
|
430
477
|
|
|
431
478
|
def set_voltage_out(
|
|
432
479
|
self, channel: Union[int, str] = 0, voltage: float = 0.0
|
|
433
|
-
) ->
|
|
480
|
+
) -> float:
|
|
434
481
|
"""
|
|
435
482
|
Set voltage at wiper for a given channel number.
|
|
436
483
|
|
|
437
484
|
:param channel: Channel number (int).
|
|
438
485
|
:param voltage: Voltage requested (float).
|
|
439
|
-
:return:
|
|
486
|
+
:return: Actual output voltage closest to requested (float).
|
|
440
487
|
"""
|
|
441
488
|
channel_number = self._get_channel_number_by_label_or_id(channel)
|
|
442
489
|
if channel_number is None:
|
|
443
490
|
raise ValueError(f"Channel {channel} not found.")
|
|
444
491
|
self.channels[channel_number].voltage_out = voltage
|
|
445
|
-
return self.channels[channel_number].
|
|
492
|
+
return self.channels[channel_number].voltage_out
|
|
446
493
|
|
|
447
494
|
def get_voltage_out(self, channel: Union[int, str] = 0) -> float:
|
|
448
495
|
"""
|
|
@@ -5,7 +5,7 @@ from typing import Union
|
|
|
5
5
|
from gpiozero import SPI
|
|
6
6
|
|
|
7
7
|
from .potentiometer import Potentiometer
|
|
8
|
-
from .__helpers import check_integer, check_positive, check_not_negative,
|
|
8
|
+
from .__helpers import check_integer, check_positive, check_not_negative, clamp
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class DigitalWiper:
|
|
@@ -22,23 +22,30 @@ class DigitalWiper:
|
|
|
22
22
|
self,
|
|
23
23
|
potentiometer: Potentiometer,
|
|
24
24
|
max_value: int = 128,
|
|
25
|
+
invert: bool = False,
|
|
25
26
|
parameters_locked: bool = False,
|
|
26
27
|
) -> None:
|
|
27
|
-
self.
|
|
28
|
-
|
|
28
|
+
self.__parameters_locked: bool = bool(parameters_locked)
|
|
29
|
+
if isinstance(potentiometer, Potentiometer):
|
|
30
|
+
self.__potentiometer: Potentiometer = potentiometer
|
|
31
|
+
else:
|
|
32
|
+
raise TypeError(
|
|
33
|
+
f"Expected an instance of Potentiometer class, got {type(potentiometer)}"
|
|
34
|
+
)
|
|
29
35
|
self.__channel: int = 0
|
|
36
|
+
self.__invert: bool = bool(invert)
|
|
30
37
|
self.__max_value: int = check_integer(check_positive(max_value))
|
|
31
38
|
self.__value: int = 0
|
|
32
39
|
self.read()
|
|
33
40
|
|
|
34
41
|
@property
|
|
35
|
-
def
|
|
42
|
+
def parameters_locked(self) -> bool:
|
|
36
43
|
"""
|
|
37
44
|
Check if parameters of the wiper are locked.
|
|
38
45
|
|
|
39
46
|
:return: True if locked and False otherwise
|
|
40
47
|
"""
|
|
41
|
-
return self.
|
|
48
|
+
return self.__parameters_locked
|
|
42
49
|
|
|
43
50
|
@property
|
|
44
51
|
def potentiometer(self) -> Potentiometer:
|
|
@@ -57,6 +64,14 @@ class DigitalWiper:
|
|
|
57
64
|
def channel(self, channel: int) -> None:
|
|
58
65
|
self.__channel = check_integer(check_not_negative(channel))
|
|
59
66
|
|
|
67
|
+
@property
|
|
68
|
+
def invert(self) -> bool:
|
|
69
|
+
return self.__invert
|
|
70
|
+
|
|
71
|
+
@invert.setter
|
|
72
|
+
def invert(self, invert: bool) -> None:
|
|
73
|
+
self.__invert = bool(invert)
|
|
74
|
+
|
|
60
75
|
@property
|
|
61
76
|
def min_value(self) -> int:
|
|
62
77
|
"""
|
|
@@ -76,8 +91,9 @@ class DigitalWiper:
|
|
|
76
91
|
|
|
77
92
|
@max_value.setter
|
|
78
93
|
def max_value(self, max_value: int) -> None:
|
|
79
|
-
|
|
80
|
-
|
|
94
|
+
new_value = check_integer(check_positive(max_value))
|
|
95
|
+
if not self.parameters_locked:
|
|
96
|
+
self.__max_value = new_value
|
|
81
97
|
self.value = self.__value
|
|
82
98
|
|
|
83
99
|
def _set_value(self, value: int) -> int:
|
|
@@ -87,7 +103,9 @@ class DigitalWiper:
|
|
|
87
103
|
:param value: Requested value as int.
|
|
88
104
|
:return: Value actually set as int.
|
|
89
105
|
"""
|
|
90
|
-
value = int(round(
|
|
106
|
+
value = int(round(clamp(value, 0, self.max_value)))
|
|
107
|
+
if self.invert:
|
|
108
|
+
return self.max_value - value
|
|
91
109
|
return value
|
|
92
110
|
|
|
93
111
|
def _read_value(self) -> int:
|
|
@@ -112,11 +130,13 @@ class DigitalWiper:
|
|
|
112
130
|
:return: Wiper position value (int).
|
|
113
131
|
"""
|
|
114
132
|
self.read()
|
|
133
|
+
if self.invert:
|
|
134
|
+
return self.max_value - self.__value
|
|
115
135
|
return self.__value
|
|
116
136
|
|
|
117
137
|
@value.setter
|
|
118
138
|
def value(self, value: int) -> None:
|
|
119
|
-
value = check_integer(
|
|
139
|
+
value = clamp(check_integer(value), 0, self.max_value)
|
|
120
140
|
data = self._set_value(value)
|
|
121
141
|
self.__value = data
|
|
122
142
|
|
|
@@ -130,7 +150,7 @@ class DigitalWiper:
|
|
|
130
150
|
|
|
131
151
|
@value_relative.setter
|
|
132
152
|
def value_relative(self, value: float) -> None:
|
|
133
|
-
value =
|
|
153
|
+
value = clamp(value, 0, 1)
|
|
134
154
|
value_int = int(round(value * self.max_value))
|
|
135
155
|
self.value = value_int
|
|
136
156
|
|
|
@@ -141,7 +161,7 @@ class DigitalWiper:
|
|
|
141
161
|
|
|
142
162
|
:return: Resistance between terminals B and W (float).
|
|
143
163
|
"""
|
|
144
|
-
return self.potentiometer.r_wb(self.
|
|
164
|
+
return self.potentiometer.r_wb(self.value_relative)
|
|
145
165
|
|
|
146
166
|
@r_wb.setter
|
|
147
167
|
def r_wb(self, resistance: float) -> None:
|
|
@@ -156,7 +176,7 @@ class DigitalWiper:
|
|
|
156
176
|
|
|
157
177
|
:return: Resistance between terminals A and W (float).
|
|
158
178
|
"""
|
|
159
|
-
return self.potentiometer.r_wa(self.
|
|
179
|
+
return self.potentiometer.r_wa(self.value_relative)
|
|
160
180
|
|
|
161
181
|
@r_wa.setter
|
|
162
182
|
def r_wa(self, resistance: float) -> None:
|
|
@@ -68,7 +68,7 @@ class MCP4251(MCP4xx1):
|
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
class MCP4261(MCP4xx1):
|
|
71
|
-
"""8-bit, dual channel pot with volatile wiper"""
|
|
71
|
+
"""8-bit, dual channel pot with non-volatile wiper"""
|
|
72
72
|
|
|
73
73
|
def __init__(self, r_ab: float = 10e3, **spi_args) -> None:
|
|
74
74
|
super().__init__(r_ab=r_ab, max_value=256, channels=2, **spi_args)
|
|
@@ -107,7 +107,7 @@ class MCP4xxxPotentiometer(Potentiometer):
|
|
|
107
107
|
|
|
108
108
|
def __init__(self, r_ab: float, rheostat: bool = False) -> None:
|
|
109
109
|
r_ab = _coerce_r_ab(r_ab)
|
|
110
|
-
super().__init__(r_ab=r_ab, r_w=75, rheostat=rheostat,
|
|
110
|
+
super().__init__(r_ab=r_ab, r_w=75, rheostat=rheostat, parameters_locked=True)
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
class MCP4xxxWiper(SpiDigitalWiper):
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import math as m
|
|
4
4
|
|
|
5
|
-
from .__helpers import
|
|
5
|
+
from .__helpers import clamp, check_number, check_positive, check_not_negative
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Potentiometer:
|
|
@@ -78,11 +78,15 @@ class Potentiometer:
|
|
|
78
78
|
# pylint: disable=too-many-instance-attributes
|
|
79
79
|
|
|
80
80
|
def __init__(
|
|
81
|
-
self,
|
|
81
|
+
self,
|
|
82
|
+
r_ab: float,
|
|
83
|
+
r_w: float = 0,
|
|
84
|
+
rheostat: bool = False,
|
|
85
|
+
parameters_locked: bool = False,
|
|
82
86
|
) -> None:
|
|
83
|
-
self.
|
|
84
|
-
self.__r_ab: float = check_positive(r_ab)
|
|
85
|
-
self.__r_w: float = check_not_negative(r_w)
|
|
87
|
+
self.__parameters_locked: bool = bool(parameters_locked)
|
|
88
|
+
self.__r_ab: float = float(check_positive(r_ab))
|
|
89
|
+
self.__r_w: float = float(check_not_negative(r_w))
|
|
86
90
|
self.__rheostat: bool = bool(rheostat)
|
|
87
91
|
|
|
88
92
|
self.__r_lim: float = 0
|
|
@@ -100,8 +104,8 @@ class Potentiometer:
|
|
|
100
104
|
|
|
101
105
|
@r_ab.setter
|
|
102
106
|
def r_ab(self, resistance: float) -> None:
|
|
103
|
-
if not self.
|
|
104
|
-
self.__r_ab = check_positive(resistance)
|
|
107
|
+
if not self.parameters_locked:
|
|
108
|
+
self.__r_ab = float(check_positive(resistance))
|
|
105
109
|
|
|
106
110
|
@property
|
|
107
111
|
def r_w(self) -> float:
|
|
@@ -114,17 +118,17 @@ class Potentiometer:
|
|
|
114
118
|
|
|
115
119
|
@r_w.setter
|
|
116
120
|
def r_w(self, r_w: float) -> None:
|
|
117
|
-
if not self.
|
|
121
|
+
if not self.parameters_locked:
|
|
118
122
|
self.__r_w = float(check_not_negative(r_w))
|
|
119
123
|
|
|
120
124
|
@property
|
|
121
|
-
def
|
|
125
|
+
def parameters_locked(self) -> bool:
|
|
122
126
|
"""
|
|
123
127
|
Check if parameters of the potentiometer are locked.
|
|
124
128
|
|
|
125
129
|
:return: True if locked and False otherwise
|
|
126
130
|
"""
|
|
127
|
-
return self.
|
|
131
|
+
return self.__parameters_locked
|
|
128
132
|
|
|
129
133
|
@property
|
|
130
134
|
def rheostat(self) -> bool:
|
|
@@ -143,7 +147,7 @@ class Potentiometer:
|
|
|
143
147
|
|
|
144
148
|
@r_lim.setter
|
|
145
149
|
def r_lim(self, r_lim: float) -> None:
|
|
146
|
-
self.__r_lim = check_not_negative(r_lim)
|
|
150
|
+
self.__r_lim = float(check_not_negative(r_lim))
|
|
147
151
|
|
|
148
152
|
@property
|
|
149
153
|
def r_load(self) -> float:
|
|
@@ -152,7 +156,7 @@ class Potentiometer:
|
|
|
152
156
|
|
|
153
157
|
@r_load.setter
|
|
154
158
|
def r_load(self, r_load: float) -> None:
|
|
155
|
-
self.__r_load = check_not_negative(r_load)
|
|
159
|
+
self.__r_load = float(check_not_negative(r_load))
|
|
156
160
|
|
|
157
161
|
@property
|
|
158
162
|
def voltage_in(self) -> float:
|
|
@@ -165,7 +169,7 @@ class Potentiometer:
|
|
|
165
169
|
|
|
166
170
|
@voltage_in.setter
|
|
167
171
|
def voltage_in(self, voltage: float) -> None:
|
|
168
|
-
self.__voltage_in = check_number(voltage)
|
|
172
|
+
self.__voltage_in = float(check_number(voltage))
|
|
169
173
|
|
|
170
174
|
def r_wa(self, wiper_position: float) -> float:
|
|
171
175
|
"""
|
|
@@ -175,7 +179,7 @@ class Potentiometer:
|
|
|
175
179
|
:param wiper_position: Wiper position in the range [0: 1]
|
|
176
180
|
:return: Resistance between terminals A and W (float).
|
|
177
181
|
"""
|
|
178
|
-
wiper_position =
|
|
182
|
+
wiper_position = clamp(wiper_position, 0, 1)
|
|
179
183
|
return self.r_w + (1 - wiper_position) * self.r_ab
|
|
180
184
|
|
|
181
185
|
def r_wb(self, wiper_position: float) -> float:
|
|
@@ -186,7 +190,7 @@ class Potentiometer:
|
|
|
186
190
|
:param wiper_position: Wiper position in the range [0: 1]
|
|
187
191
|
:return: Resistance between terminals B and W (float).
|
|
188
192
|
"""
|
|
189
|
-
wiper_position =
|
|
193
|
+
wiper_position = clamp(wiper_position, 0, 1)
|
|
190
194
|
return self.r_w + wiper_position * self.r_ab
|
|
191
195
|
|
|
192
196
|
def r_wa_to_position(self, r_wa: float) -> float:
|
|
@@ -197,7 +201,7 @@ class Potentiometer:
|
|
|
197
201
|
:param r_wa: Resistance between terminals A and W (float)
|
|
198
202
|
:return: Wiper position in the range [0: 1] (float).
|
|
199
203
|
"""
|
|
200
|
-
r_wa =
|
|
204
|
+
r_wa = clamp(r_wa, self.r_w, self.r_w + self.r_ab)
|
|
201
205
|
return 1 - (r_wa - self.r_w) / self.r_ab
|
|
202
206
|
|
|
203
207
|
def r_wb_to_position(self, r_wb: float) -> float:
|
|
@@ -208,7 +212,7 @@ class Potentiometer:
|
|
|
208
212
|
:param r_wb: Resistance between terminals B and W (float)
|
|
209
213
|
:return: Wiper position in the range [0: 1] (float).
|
|
210
214
|
"""
|
|
211
|
-
r_wb =
|
|
215
|
+
r_wb = clamp(r_wb, self.r_w, self.r_w + self.r_ab)
|
|
212
216
|
return (r_wb - self.r_w) / self.r_ab
|
|
213
217
|
|
|
214
218
|
def voltage_out(self, wiper_position: float) -> float:
|
|
@@ -218,6 +222,7 @@ class Potentiometer:
|
|
|
218
222
|
:param wiper_position: Wiper position as float number between 0 and 1.
|
|
219
223
|
:return: Voltage (float).
|
|
220
224
|
"""
|
|
225
|
+
wiper_position = clamp(wiper_position, 0, 1)
|
|
221
226
|
if self.rheostat:
|
|
222
227
|
r_total = self.r_load + self.r_lim + self.r_wb(wiper_position)
|
|
223
228
|
return self.voltage_in * self.r_load / r_total
|
|
@@ -234,6 +239,7 @@ class Potentiometer:
|
|
|
234
239
|
:param voltage_out: Output voltage (float).
|
|
235
240
|
:return: Wiper position as float number between 0 and 1.
|
|
236
241
|
"""
|
|
242
|
+
voltage_out = check_number(voltage_out)
|
|
237
243
|
if voltage_out == 0 or self.r_load == 0 or self.voltage_in == 0:
|
|
238
244
|
return 0
|
|
239
245
|
if (self.voltage_in / voltage_out) / abs(self.voltage_in / voltage_out) < 0:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: BDPotentiometer
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: BD Digital Potentiometer
|
|
5
5
|
Author-email: Anton Bondarenko <bond.anton@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -32,10 +32,17 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
32
32
|
Classifier: Operating System :: OS Independent
|
|
33
33
|
Requires-Python: >=3.9
|
|
34
34
|
Description-Content-Type: text/markdown
|
|
35
|
+
License-File: LICENSE
|
|
36
|
+
Requires-Dist: gpiozero>=1.6.2
|
|
35
37
|
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: gpiozero>=1.6.2; extra == "dev"
|
|
39
|
+
Requires-Dist: numpy; extra == "dev"
|
|
36
40
|
Provides-Extra: test
|
|
41
|
+
Requires-Dist: flake8; extra == "test"
|
|
42
|
+
Requires-Dist: pytest; extra == "test"
|
|
43
|
+
Requires-Dist: numpy; extra == "test"
|
|
37
44
|
Provides-Extra: lint
|
|
38
|
-
|
|
45
|
+
Requires-Dist: pylint; extra == "lint"
|
|
39
46
|
|
|
40
47
|
# BDPotentiometer
|
|
41
48
|
|
|
@@ -16,8 +16,4 @@ src/BDPotentiometer/mcp4xxx/__init__.py
|
|
|
16
16
|
src/BDPotentiometer/mcp4xxx/mcp4xx1.py
|
|
17
17
|
src/BDPotentiometer/mcp4xxx/mcp4xx2.py
|
|
18
18
|
src/BDPotentiometer/mcp4xxx/mcp4xxx.py
|
|
19
|
-
tests/test_digital_potentiometer.py
|
|
20
|
-
tests/test_digital_wiper.py
|
|
21
|
-
tests/test_helpers.py
|
|
22
|
-
tests/test_potentiometer.py
|
|
23
19
|
tests/test_version.py
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
""" Test package version is correctly set """
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
from src.BDPotentiometer import __version__ as version
|
|
6
|
+
except ModuleNotFoundError:
|
|
7
|
+
from BDPotentiometer import __version__ as version
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestVersion(unittest.TestCase):
|
|
11
|
+
"""
|
|
12
|
+
Test _version.py has correct version number assigned to global __version__ variable.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def test_check_version_numbering(self) -> None:
|
|
16
|
+
"""
|
|
17
|
+
Version must be string with three integers separated by dot.
|
|
18
|
+
Example: `0.1.4` or `2.0.0`.
|
|
19
|
+
All three digits must not negative and must not be equal to zero at the same time.
|
|
20
|
+
"""
|
|
21
|
+
# Assert version is a string.
|
|
22
|
+
self.assertTrue(isinstance(version, str))
|
|
23
|
+
|
|
24
|
+
# Assert no extra space present. Example: " 0.1.1 " is not correct, "0.1.1" is correct.
|
|
25
|
+
self.assertEqual(len(version), len(version.strip()))
|
|
26
|
+
|
|
27
|
+
# Assert all three parts are present
|
|
28
|
+
v_l = version.split(".")
|
|
29
|
+
self.assertEqual(len(v_l), 3)
|
|
30
|
+
|
|
31
|
+
# Assert no extra space present in each part.
|
|
32
|
+
# Example: "0. 1 .1" is not correct, "0.1.1" is correct
|
|
33
|
+
for i in range(3):
|
|
34
|
+
self.assertEqual(len(v_l[i]), len(v_l[i].strip()))
|
|
35
|
+
|
|
36
|
+
# Convert all parts of the version to int values
|
|
37
|
+
v_maj = int(v_l[0])
|
|
38
|
+
v_min = int(v_l[1])
|
|
39
|
+
v_patch = int(v_l[2])
|
|
40
|
+
|
|
41
|
+
# Assert all version numbers are not negative
|
|
42
|
+
self.assertTrue(v_maj >= 0)
|
|
43
|
+
self.assertTrue(v_min >= 0)
|
|
44
|
+
self.assertTrue(v_patch >= 0)
|
|
45
|
+
|
|
46
|
+
# Assert all version numbers are not equal to zero at the same time
|
|
47
|
+
self.assertTrue(v_maj + v_min + v_patch > 0)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
unittest.main()
|