Skip to content

Commit c8bf568

Browse files
authored
Merge branch 'master' into enviro-non-plus
2 parents 20442c9 + 02c9f28 commit c8bf568

File tree

5 files changed

+182
-5
lines changed

5 files changed

+182
-5
lines changed

examples/noise-amps-at-freqs.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import ST7735
2+
from PIL import Image, ImageDraw
3+
from enviroplus.noise import Noise
4+
5+
print("""noise-amps-at-freqs.py - Measure amplitude from specific frequency bins
6+
7+
This example retrieves the median amplitude from 3 user-specified frequency ranges and plots them in Blue, Green and Red on the Enviro+ display.
8+
9+
As you play a continuous rising tone on your phone, you should notice peaks that correspond to the frequency entering each range.
10+
11+
Press Ctrl+C to exit!
12+
13+
""")
14+
15+
noise = Noise()
16+
17+
disp = ST7735.ST7735(
18+
port=0,
19+
cs=ST7735.BG_SPI_CS_FRONT,
20+
dc=9,
21+
backlight=12,
22+
rotation=90)
23+
24+
disp.begin()
25+
26+
img = Image.new('RGB', (disp.width, disp.height), color=(0, 0, 0))
27+
draw = ImageDraw.Draw(img)
28+
29+
30+
while True:
31+
amps = noise.get_amplitudes_at_frequency_ranges([
32+
(100, 200),
33+
(500, 600),
34+
(1000, 1200)
35+
])
36+
amps = [n * 32 for n in amps]
37+
img2 = img.copy()
38+
draw.rectangle((0, 0, disp.width, disp.height), (0, 0, 0))
39+
img.paste(img2, (1, 0))
40+
draw.line((0, 0, 0, amps[0]), fill=(0, 0, 255))
41+
draw.line((0, 0, 0, amps[1]), fill=(0, 255, 0))
42+
draw.line((0, 0, 0, amps[2]), fill=(255, 0, 0))
43+
44+
disp.display(img)

examples/noise-profile.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import ST7735
2+
from PIL import Image, ImageDraw
3+
from enviroplus.noise import Noise
4+
5+
print("""noise-profile.py - Get a simple noise profile.
6+
7+
This example grabs a basic 3-bin noise profile of low, medium and high frequency noise, plotting the noise characteristics as coloured bars.
8+
9+
Press Ctrl+C to exit!
10+
11+
""")
12+
13+
noise = Noise()
14+
15+
disp = ST7735.ST7735(
16+
port=0,
17+
cs=ST7735.BG_SPI_CS_FRONT,
18+
dc=9,
19+
backlight=12,
20+
rotation=90)
21+
22+
disp.begin()
23+
24+
img = Image.new('RGB', (disp.width, disp.height), color=(0, 0, 0))
25+
draw = ImageDraw.Draw(img)
26+
27+
28+
while True:
29+
low, mid, high, amp = noise.get_noise_profile()
30+
low *= 128
31+
mid *= 128
32+
high *= 128
33+
amp *= 64
34+
35+
img2 = img.copy()
36+
draw.rectangle((0, 0, disp.width, disp.height), (0, 0, 0))
37+
img.paste(img2, (1, 0))
38+
draw.line((0, 0, 0, amp), fill=(int(low), int(mid), int(high)))
39+
40+
disp.display(img)

examples/particulates.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
try:
2424
readings = pms5003.read()
2525
logging.info(readings)
26-
time.sleep(1.0)
2726
except ReadTimeoutError:
2827
pms5003 = PMS5003()
2928
except KeyboardInterrupt:

library/enviroplus/noise.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import sounddevice
2+
import numpy
3+
4+
5+
class Noise():
6+
def __init__(self,
7+
sample_rate=16000,
8+
duration=0.5):
9+
"""Noise measurement.
10+
11+
:param sample_rate: Sample rate in Hz
12+
:param duraton: Duration, in seconds, of noise sample capture
13+
14+
"""
15+
16+
self.duration = duration
17+
self.sample_rate = sample_rate
18+
19+
def get_amplitudes_at_frequency_ranges(self, ranges):
20+
"""Return the mean amplitude of frequencies in the given ranges.
21+
22+
:param ranges: List of ranges including a start and end range
23+
24+
"""
25+
recording = self._record()
26+
magnitude = numpy.abs(numpy.fft.rfft(recording[:, 0], n=self.sample_rate))
27+
result = []
28+
for r in ranges:
29+
start, end = r
30+
result.append(numpy.mean(magnitude[start:end]))
31+
return result
32+
33+
def get_amplitude_at_frequency_range(self, start, end):
34+
"""Return the mean amplitude of frequencies in the specified range.
35+
36+
:param start: Start frequency (in Hz)
37+
:param end: End frequency (in Hz)
38+
39+
"""
40+
n = self.sample_rate // 2
41+
if start > n or end > n:
42+
raise ValueError("Maxmimum frequency is {}".format(n))
43+
44+
recording = self._record()
45+
magnitude = numpy.abs(numpy.fft.rfft(recording[:, 0], n=self.sample_rate))
46+
return numpy.mean(magnitude[start:end])
47+
48+
def get_noise_profile(self,
49+
noise_floor=100,
50+
low=0.12,
51+
mid=0.36,
52+
high=None):
53+
"""Returns a noise charateristic profile.
54+
55+
Bins all frequencies into 3 weighted groups expressed as a percentage of the total frequency range.
56+
57+
:param noise_floor: "High-pass" frequency, exclude frequencies below this value
58+
:param low: Percentage of frequency ranges to count in the low bin (as a float, 0.5 = 50%)
59+
:param mid: Percentage of frequency ranges to count in the mid bin (as a float, 0.5 = 50%)
60+
:param high: Optional percentage for high bin, effectively creates a "Low-pass" if total percentage is less than 100%
61+
62+
"""
63+
64+
if high is None:
65+
high = 1.0 - low - mid
66+
67+
recording = self._record()
68+
magnitude = numpy.abs(numpy.fft.rfft(recording[:, 0], n=self.sample_rate))
69+
70+
sample_count = (self.sample_rate // 2) - noise_floor
71+
72+
mid_start = noise_floor + int(sample_count * low)
73+
high_start = mid_start + int(sample_count * mid)
74+
noise_ceiling = high_start + int(sample_count * high)
75+
76+
amp_low = numpy.mean(magnitude[self.noise_floor:mid_start])
77+
amp_mid = numpy.mean(magnitude[mid_start:high_start])
78+
amp_high = numpy.mean(magnitude[high_start:noise_ceiling])
79+
amp_total = (low + mid + high) / 3.0
80+
81+
return amp_low, amp_mid, amp_high, amp_total
82+
83+
def _record(self):
84+
return sounddevice.rec(
85+
int(self.duration * self.sample_rate),
86+
samplerate=self.sample_rate,
87+
blocking=True,
88+
channels=1,
89+
dtype='float64'
90+
)

library/setup.cfg

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ install_requires =
3333
ltr559
3434
st7735
3535
ads1015
36-
fonts
37-
font-roboto
38-
astral
39-
pytz
36+
fonts
37+
font-roboto
38+
astral
39+
pytz
40+
sounddevice
4041
4142
[flake8]
4243
exclude =
@@ -55,13 +56,16 @@ py2deps =
5556
python-numpy
5657
python-smbus
5758
python-pil
59+
libportaudio2
5860
py3deps =
5961
python3-pip
6062
python3-numpy
6163
python3-smbus
6264
python3-pil
65+
libportaudio2
6366
configtxt =
6467
dtoverlay=pi3-miniuart-bt
68+
dtoverlay=adau7002-simple
6569
commands =
6670
printf "Setting up i2c and SPI..\n"
6771
raspi-config nonint do_spi 0

0 commit comments

Comments
 (0)