patternpythonMinor
Read decibel level from a USB meter, display it as a live visualization, and send it via FTP
Viewed 0 times
meterftpreadleveldecibeldisplayusbliveviavisualization
Problem
This question was posed to me by someone in my university's athletics department who had a USB sound level meter and a simple Python script which printed the current sound level (he was provided with that script when he bought the meter):
Can you write a Python script to read the decibel level of the arena during basketball games? We're interested in adding that data as a 'live stat' in our official app.
This script represents my attempt at meeting his requirements. I'm self-taught in Python, and most of what I do in the language uses the NLTK and BeautifulSoup, so building a GUI and handling data from a USB device are outside my 'comfort zone'. So far it does what we need it to do, but I'm sure it can be improved.
Usage
For FTP uploading, the script assumes a
To send output via FTP, call the script with
Note also that the
The visualization itself looks like this:
Output
The script saves two sorts of JSON output files.
The file
kudbs.json
The file totalresults_XX.json is saved when the Stop button is pressed, and contains all data gathered since the Start button was pressed. This file is in
Can you write a Python script to read the decibel level of the arena during basketball games? We're interested in adding that data as a 'live stat' in our official app.
This script represents my attempt at meeting his requirements. I'm self-taught in Python, and most of what I do in the language uses the NLTK and BeautifulSoup, so building a GUI and handling data from a USB device are outside my 'comfort zone'. So far it does what we need it to do, but I'm sure it can be improved.
Usage
For FTP uploading, the script assumes a
ftpconfig.py file structured like this:#!/usr/bin/env python
# -*- coding: utf-8 -*-
FTP_HOST = 'ftp.example.com'
FTP_USERNAME = 'username'
FTP_PASSWORD = 'password'
FTP_DIR = 'subdirectory'To send output via FTP, call the script with
python decibelviz.py --ftp. Calling the script without that --ftp flag won't try to send anything via FTP.Note also that the
DecibelVisualizer.live_dbs() method, which gets the reading from the USB meter, will return random values in the same interval if the meter's not connected. This makes for a useful demo mode.The visualization itself looks like this:
Output
The script saves two sorts of JSON output files.
The file
kudbs.json is the file that's sent via FTP every fifteen seconds. It's saved with no indentation (it's assumed that this will be for machine reading, not human reading) and is overwritten on successful upload.kudbs.json
[[1449674472,52.0],[1449674473,67.0],[1449674473,31.0],[1449674474,91.0],[1449674475,87.0],[1449674475,129.0],[1449674476,108.0],[1449674477,35.0]]The file totalresults_XX.json is saved when the Stop button is pressed, and contains all data gathered since the Start button was pressed. This file is in
Solution
I can answer question 1, and I have some general feedback besides that. First, your question.
Your
I'd also question whether or not you actually need all of those optional parameters. For settings that are almost never supplied, perhaps they could be set up separate to
In particular, I don't think
When defined here it can either be accessed with
You don't need
This will raise a syntax error, because there must be indented code after
In your
It's good that you've documented well, your classes and functions are quite clear. Avoid over commenting though. People will know what
It's not Pythonic to use
Likewise in the ternary, use
Your
DecibelVisualizer's init is very long. I think at the very least you should split some of them up into separate functions to make it easier to see what's happening at each point. Your configure_grid certainly seems like a good candidate.I'd also question whether or not you actually need all of those optional parameters. For settings that are almost never supplied, perhaps they could be set up separate to
__init__ with a different function? Normally I would advise that it's helpful to let the user provide them on __init__ but there are really a lot of parameters there and it harms readability.In particular, I don't think
self.colors should be defined in the __init__ since it's a constant. You should instead have it at class level.class DecibelVisualizer(object):
self.COLORS = {
13: '#E50000',
12: '#E14400',
11: '#DD8700',
10: '#D9C701',
9: '#A7D501',
8: '#65D201',
7: '#25CE02',
6: '#02CA1C',
5: '#02C658',
4: '#03C391',
3: '#03B6BF',
2: '#037ABB',
1: '#0341B7',
0: '#040AB4',
}When defined here it can either be accessed with
DecibelVisualizer.COLORS or still with self.COLORS. Each instance of the class can refer back to it. And since it's always the same there's no need to make a new one with each visualiser.You don't need
pass in your except block here, you may have misunderstood its use. It's merely a keyword that does nothing, used as a placeholder where Python expects a block of code but you don't need to write any. For example, here's what would happen if you tried to remove both print and pass:try:
from ftpconfig import FTP_HOST, FTP_USERNAME, FTP_PASSWORD, FTP_DIR
except ImportError:
# Nothing hereThis will raise a syntax error, because there must be indented code after
except ImportError. However you can just put pass in there to tell Python to do nothing while still satisfying the syntax requirement. But if you have actual code in there, like your print, then there's no longer a need for pass.In your
fibonacci_number function you call range, but since you're using Python 2.7 you should use xrange, which doesn't create a full list in one go and so is more efficient.It's good that you've documented well, your classes and functions are quite clear. Avoid over commenting though. People will know what
__init__ does especially when your initialisations are simple so you could leave out notes like Initialize the ReadoutHeading object..It's not Pythonic to use
if boolean == False, instead use the boolean value directly. Either with if boolean or if not boolean. Also it's not necessary to set a boolean for your while loop since you're just going to break out of it anyway. You can just do while True:if not overwrite:
idx = 1
while True:
str_idx = str(idx).rjust(2, '0')
fname = '{}_{}'.format(filename, str_idx)
if os.path.isfile(fname + '.json'):
idx += 1
else:
filename = fname
breakLikewise in the ternary, use
if not:indent = 3 if not overwrite else NoneCode Snippets
class DecibelVisualizer(object):
self.COLORS = {
13: '#E50000',
12: '#E14400',
11: '#DD8700',
10: '#D9C701',
9: '#A7D501',
8: '#65D201',
7: '#25CE02',
6: '#02CA1C',
5: '#02C658',
4: '#03C391',
3: '#03B6BF',
2: '#037ABB',
1: '#0341B7',
0: '#040AB4',
}try:
from ftpconfig import FTP_HOST, FTP_USERNAME, FTP_PASSWORD, FTP_DIR
except ImportError:
# Nothing hereif not overwrite:
idx = 1
while True:
str_idx = str(idx).rjust(2, '0')
fname = '{}_{}'.format(filename, str_idx)
if os.path.isfile(fname + '.json'):
idx += 1
else:
filename = fname
breakindent = 3 if not overwrite else NoneContext
StackExchange Code Review Q#113389, answer score: 3
Revisions (0)
No revisions yet.