Skip to content

Commit 9fee157

Browse files
fix copyright, docs
1 parent d32a20c commit 9fee157

File tree

6 files changed

+392
-55
lines changed

6 files changed

+392
-55
lines changed

README.rst

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -31,35 +31,12 @@ Please ensure all dependencies are available on the CircuitPython filesystem.
3131
This is easily achieved by downloading
3232
`the Adafruit library and driver bundle <https://circuitpython.org/libraries>`_
3333
or individual libraries can be installed using
34-
`circup <https://github.com/adafruit/circup>`_.Installing from PyPI
35-
=====================
36-
.. note:: This library is not available on PyPI yet. Install documentation is included
37-
as a standard element. Stay tuned for PyPI availability!
38-
39-
.. todo:: Remove the above note if PyPI version is/will be available at time of release.
40-
41-
On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from
42-
PyPI <https://pypi.org/project/Cedargrove-circuitpython-rangeslicer/>`_.
43-
To install for current user:
44-
45-
.. code-block:: shell
46-
47-
pip3 install Cedargrove-circuitpython-rangeslicer
34+
`circup <https://github.com/adafruit/circup>`_.
4835

49-
To install system-wide (this may be required in some cases):
50-
51-
.. code-block:: shell
52-
53-
sudo pip3 install Cedargrove-circuitpython-rangeslicer
54-
55-
To install in a virtual environment in your current project:
56-
57-
.. code-block:: shell
36+
Installing from PyPI
37+
=====================
38+
.. note:: This library is not available on PyPI.
5839

59-
mkdir project-name && cd project-name
60-
python3 -m venv .venv
61-
source .env/bin/activate
62-
pip3 install Cedargrove-circuitpython-rangeslicer
6340

6441
Installing to a Connected CircuitPython Device with Circup
6542
==========================================================
@@ -87,15 +64,44 @@ Or the following command to update an existing version:
8764
Usage Example
8865
=============
8966

90-
.. todo:: Add a quick, simple example. It and other examples should live in the
91-
examples folder and be included in docs/examples.rst.
67+
.. code-block:: Python
68+
69+
"""Reads two potentiometer inputs and produces -1.0 to +1.0 normalized outputs,
70+
one with an inverted output range."""
71+
72+
import time
73+
import board
74+
import cedargrove_range_slicer as rs
75+
from analogio import AnalogIn
76+
print("Two Normalized Outputs: Range_Slicer example 01")
77+
78+
"""Establish range_slicer instances, one for each analog potentiometer
79+
input. Input ranges are adjusted for unique potentiometer inaccuracies and
80+
noise. Slice size divides the output into 10 slices. Hysteresis factor is
81+
25% of a slice."""
82+
83+
ctrl_0 = rs.Slicer(200, 65335, -1.0, +1.0, 0.2, 0.25)
84+
ctrl_1 = rs.Slicer(375, 65520, +1.0, -1.0, 0.2, 0.25)
85+
86+
# establish analog inputs
87+
pot_0 = AnalogIn(board.A0)
88+
pot_1 = AnalogIn(board.A1)
89+
90+
while True: # read potentiometer values
91+
control_0 = pot_0.value
92+
control_1 = pot_1.value
93+
94+
# calculate output values and print (or plot in Mu)
95+
out_0 = ctrl_0.range_slicer(control_0)
96+
out_1 = ctrl_1.range_slicer(control_1)
97+
print( (control_0 / 65535, control_1 / 65535, out_0, out_1) )
98+
99+
time.sleep(0.1) # pause for 0.1 second
100+
92101
93102
Documentation
94103
=============
95-
API documentation for this library can be found on `Read the Docs <https://circuitpython-rangeslicer.readthedocs.io/>`_.
96-
97-
For information on building library documentation, please check out
98-
`this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.
104+
`RangeSlicer CircuitPython Driver API Class Description <https://github.com/CedarGroveStudios/Cedargrove_CircuitPython_RangeSlicer/blob/media/pseudo_readthedocs_cedargrove_range_slicer.pdf>`_
99105

100106
Contributing
101107
============

cedargrove_rangeslicer.py

100644100755
Lines changed: 233 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,250 @@
1-
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
2-
# SPDX-FileCopyrightText: Copyright (c) 2022 JG for Cedar Grove Maker Studios
1+
# SPDX-FileCopyrightText: Copyright (c) 2020, 2021, 2022 JG for Cedar Grove Maker Studios
32
#
43
# SPDX-License-Identifier: MIT
54
"""
65
`cedargrove_rangeslicer`
76
================================================================================
7+
cedargrove_rangeslicer.py 2022-09-14 v3.10 05:58PM
8+
RangeSlicer is a CircuitPython class for scaling a range of input values into
9+
indexed/quantized output values. Output slice hysteresis is used to provide
10+
dead-zone squelching.
811
9-
A CircuitPython class for scaling a range of input values into indexed/quantized output values. Adjustable output slice hysteresis is used to provide dead-zone squelching.
10-
11-
12-
* Author(s): JG
12+
* Author(s): JG for Cedar Grove Maker Studios
1313
1414
Implementation Notes
1515
--------------------
16+
** Software and Dependencies: **
17+
* Adafruit CircuitPython firmware for the supported boards:
18+
https://github.com/adafruit/circuitpython/releases
19+
"""
1620

17-
**Hardware:**
21+
__version__ = "0.0.0-auto.0"
22+
__repo__ = "https://github.com/CedarGroveStudios/Range_Slicer.git"
1823

19-
.. todo:: Add links to any specific hardware product page(s), or category page(s).
20-
Use unordered list & hyperlink rST inline format: "* `Link Text <url>`_"
2124

22-
**Software and Dependencies:**
25+
# pylint: disable=too-many-instance-attributes
26+
class Slicer:
27+
"""The RangeSlicer class."""
2328

24-
* Adafruit CircuitPython firmware for the supported boards:
25-
https://circuitpython.org/downloads
29+
# pylint: disable=too-many-arguments
30+
def __init__(
31+
self,
32+
in_min=0,
33+
in_max=65535,
34+
out_min=0,
35+
out_max=65535,
36+
out_slice=1.0,
37+
hyst_factor=0.10,
38+
out_integer=False,
39+
):
40+
"""Instantiate the Slicer class.
2641
27-
.. todo:: Uncomment or remove the Bus Device and/or the Register library dependencies
28-
based on the library's use of either.
42+
:param union(float, int) in_min: The minimum value of the range input.
43+
Can be a positive or negative floating point or integer value.
44+
Defaults to 0.
45+
:param union(float, int) in_max: The maximum value of the range input.
46+
Can be a positive or negative floating point or integer value.
47+
Defaults to 65535.
48+
:param union(float, int) out_min: The minimum value of the index output.
49+
Can be a positive or negative floating point or integer value.
50+
Defaults to 0.
51+
:param union(float, int) out_max: The maximum value of the index output.
52+
Can be a positive or negative floating point or integer value.
53+
Defaults to 65535.
54+
:param union(float, int) out_slice: The index output slice value. Must be a
55+
positive floating point or integer value greater than zero. Defaults
56+
to 1.0.
57+
:param float hyst_factor: The hysteresis factor as related to the span
58+
of the range input. For example, a factor of 0.50 is a hysteresis
59+
setting of 50%. Defaults to 0.10 (10% of the range input span).
60+
:parm bool out_integer: The index output data type; True to convert
61+
the index output value to an integer, False for floating point.
62+
Defaults to False."""
2963

30-
# * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
31-
# * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
32-
"""
64+
self._in_min = in_min # Range input min/max parameters
65+
self._in_max = in_max
66+
self._out_min = out_min # Index output min/max parameters
67+
self._out_max = out_max
68+
self._slice = out_slice # Index output slice size parameter
69+
self._hyst_factor = hyst_factor # Hysteresis factor parameter
70+
self._out_integer = out_integer # Index output data type parameter
71+
72+
self._in_zone = None # Define variable for later use in the class
73+
self._index = None # Define variable for later use in the class
74+
self._old_idx_mapped = None # Define variable for later use in the class
75+
76+
self._update_param() # Establish the parameters for range_slicer helper
77+
78+
@property
79+
def range_min(self):
80+
"""The range input minimum floating or integer value."""
81+
return self._in_min
82+
83+
@range_min.setter
84+
def range_min(self, in_min=0):
85+
self._in_min = in_min
86+
self._update_param() # Update the parameters for range_slicer helper
87+
88+
@property
89+
def range_max(self):
90+
"""The range input maximum floating or integer value."""
91+
return self._in_max
92+
93+
@range_max.setter
94+
def range_max(self, in_max=65535):
95+
self._in_max = in_max
96+
self._update_param() # Update the parameters for range_slicer helper
97+
98+
@property
99+
def index_min(self):
100+
"""The index output minimum value."""
101+
return self._out_min
102+
103+
@index_min.setter
104+
def index_min(self, out_min=0):
105+
self._out_min = out_min
106+
self._update_param() # Update the parameters for range_slicer helper
107+
108+
@property
109+
def index_max(self):
110+
"""The index output maximum value."""
111+
return self._out_max
112+
113+
@index_max.setter
114+
def index_max(self, out_max=65535):
115+
self._out_max = out_max
116+
self._update_param() # Update the parameters for range_slicer helper
117+
118+
@property
119+
def index_type(self):
120+
"""The index output value integer data type."""
121+
return self._out_integer
122+
123+
@index_type.setter
124+
def index_type(self, out_integer=False):
125+
self._out_integer = out_integer
126+
self._update_param() # Update the parameters for range_slicer helper
127+
128+
@property
129+
def slice(self):
130+
"""The index output slice size value."""
131+
return self._slice
132+
133+
@slice.setter
134+
def slice(self, size=1.0):
135+
if size <= 0:
136+
raise RuntimeError(
137+
"Setter: Invalid Slice setting; value must be greater than zero"
138+
)
139+
self._slice = size
140+
self._update_param() # Update the parameters for range_slicer helper
141+
142+
@property
143+
def hysteresis(self):
144+
"""The hysteresis factor value."""
145+
return self._hyst_factor
146+
147+
@hysteresis.setter
148+
def hysteresis(self, hyst_factor=0.10):
149+
self._hyst_factor = hyst_factor
150+
self._update_param() # Update the parameters for range_slicer helper
151+
152+
def range_slicer(self, range_in=0):
153+
"""range_slicer() is the primary function of the Slicer class.
154+
Determines an index (output) value from the range_in input value.
155+
Returns new index value and a change flag (True/False) if the new index
156+
changed from the previous index. Index value can be optionally converted
157+
to integer data type."""
158+
159+
if self._out_span_dir == 1:
160+
idx_mapped = self._mapper(range_in) + self._hyst_band
161+
slice_num = (
162+
(idx_mapped - self._out_span_min)
163+
- ((idx_mapped - self._out_span_min) % self._slice)
164+
) / self._slice
165+
slice_thresh = (slice_num * self._slice) + self._out_span_min
166+
else:
167+
idx_mapped = self._mapper(range_in) + self._hyst_band
168+
slice_num = (
169+
(idx_mapped - self._out_span_max)
170+
- ((idx_mapped - self._out_span_max) % self._slice)
171+
) / self._slice
172+
slice_thresh = (slice_num * self._slice) + self._out_span_max
173+
upper_zone_limit = slice_thresh + (2 * self._hyst_band)
174+
175+
# if idx_mapped <= upper_zone_limit and idx_mapped >= slice_thresh:
176+
if upper_zone_limit >= idx_mapped >= slice_thresh:
177+
if self._in_zone != slice_thresh:
178+
self._in_zone = slice_thresh
179+
if idx_mapped > self._old_idx_mapped:
180+
self._index = slice_thresh - self._slice
181+
if idx_mapped < self._old_idx_mapped:
182+
self._index = slice_thresh
183+
else:
184+
self._in_zone = None
185+
self._index = slice_thresh
186+
if self._out_span_min <= self._out_span_max:
187+
self._index = max(min(self._index, self._out_span_max), self._out_span_min)
188+
else:
189+
self._index = min(max(self._index, self._out_span_max), self._out_span_min)
190+
"""if self._index != self._old_idx_mapped:
191+
change_flag = True
192+
else:
193+
change_flag = False"""
194+
change_flag = bool(self._index != self._old_idx_mapped)
195+
196+
self._old_idx_mapped = idx_mapped
197+
if self._out_integer:
198+
return int(self._index), change_flag
199+
return self._index, change_flag
200+
201+
def _mapper(self, map_in):
202+
"""_mapper: Determines the linear output value based on the input value
203+
using the linear slope-intercept form y = mx + b ."""
204+
if (self._in_min == self._in_max) or (self._out_span_min == self._out_span_max):
205+
return self._out_span_min
206+
mapped = (
207+
((self._out_span_max - self._out_span_min) / (self._in_max - self._in_min))
208+
* (map_in - self._in_min)
209+
) + self._out_span_min
210+
if self._out_span_min <= self._out_span_max:
211+
return max(min(mapped, self._out_span_max), self._out_span_min)
212+
return min(max(mapped, self._out_span_max), self._out_span_min)
213+
214+
"""def _sign(self, x):
215+
"Determines the sign of a numeric value. Zero is evaluated as a
216+
positive value."
217+
if x >= 0:
218+
return 1
219+
return -1"""
220+
221+
def _update_param(self):
222+
"""Recalculate spans and hysteresis value when parameters change."""
223+
# Update output span parameters
224+
if self._out_min > self._out_max:
225+
self._out_span_min = self._out_min + self._slice
226+
self._out_span_max = self._out_max
227+
else:
228+
self._out_span_min = self._out_min
229+
self._out_span_max = self._out_max + self._slice
230+
self._out_span = self._out_span_max - self._out_span_min
231+
232+
if self._out_span >= 0:
233+
self._out_span_dir = 1
234+
else:
235+
self._out_span_dir = -1
33236

34-
# imports
237+
# self._out_span_dir = self._sign(self._out_span)
35238

36-
__version__ = "0.0.0+auto.0"
37-
__repo__ = "https://github.com/CedarGroveStudios/Cedargrove_CircuitPython_RangeSlicer.git"
239+
# Update slice size parameter
240+
if self._slice <= 0:
241+
raise RuntimeError(
242+
"_update_param: Invalid Slice value; must be greater than zero"
243+
)
244+
# Update hysteresis parameters: calculate hysteresis band size,
245+
# reset in-zone state
246+
self._hyst_band = self._hyst_factor * self._slice
247+
self._in_zone = None
248+
# Update index parameters
249+
self._index = 0
250+
self._old_idx_mapped = 0

0 commit comments

Comments
 (0)