Analog Sampling using LIBPRU over beaglebone - python

I need to sample analog signal (50-60Hz) through AIN0 on Beaglebone Black.
What I need is a sample size of 10,000 at 1 millisecond sampling rate (should run for 10 secs).
The python example I found in for beaglebone_pru_adc library is as below -
import beaglebone_pru_adc as adc
import time
numsamples = 10000 # how many samples to capture
capture = adc.Capture()
capture.oscilloscope_init(adc.OFF_VALUES, numsamples) # captures AIN0 - the first elt in AIN array
capture.cap_delay = 200000 # I don't understand how to write this part to achieve desired sampling rate
capture.start()
for _ in range(10):
if capture.oscilloscope_is_complete():
break
print '.'
time.sleep(0.1)
capture.stop()
capture.wait()
print 'Saving oscilloscope values to "data.csv"'
with open('data.csv', 'w') as f:
for x in capture.oscilloscope_data(numsamples):
f.write(str(x) + '\n')
print 'done'
capture.close()
I think I am wrong somewhere as I don't expect the acquisition to finish before 10 secs. Any help regarding how to achieve specific sample size at specific sample rate for BB ADC would be great help.

Related

tqdm: simple loop on iterations, show MB/s

Question
I want to do a simple loop over n iterations. Each iteration contains a copy operation of which I know how many bytes were copied. My question is: how can I also show the number of MB/s in the progress bar?
Example
I'm showing a progress bar around rsync. I modified this answer as follows:
import subprocess
import sys
import re
import tqdm
n = len(open('myfiles.txt').readlines())
your_command = 'rsync -aP --files-from="myfiles.txt" myhost:mysource .'
pbar = tqdm.trange(n)
process = subprocess.Popen(your_command, stdout=subprocess.PIPE, shell=True)
for line in iter(process.stdout.readline, ''):
line = line.decode("utf-8")
if re.match(r'(.*)(xfr\#)([0-9])(.*)(to\-chk\=)([0-9])(.*)', line):
pbar.update()
Whereby myfiles.txt contains a list of files. This gives me a good progress bar, showing the number of iterations per second.
However, the summary line that I match on to signal that a file was copied, e.g.
682,356 100% 496.92kB/s 0:00:01 (xfr#5, to-chk=16756/22445)
also contains the number of bytes that were copied, which I want to use to show a copying speed.
Below I provided code doing what you need.
Because I didn't have your example data I created simple generator function that emulates process of retrieving data. It generates at random points of time (on each iteration) value equal to random number of bytes (not mega-bytes).
There are two tqdm bars below, one is used for progress, it measures iterations per second and total number of iterations done and percentage. And second bar that measures speed in megabytes per second and total number of megabytes received.
Try it online!
import tqdm
def gen(cnt):
import time, random
for i in range(cnt):
time.sleep(random.random() * 0.125)
yield random.randrange(1 << 20)
total_iterations = 150
pbar = tqdm.tqdm(total = total_iterations, ascii = True)
sbar = tqdm.tqdm(unit = 'B', ascii = True, unit_scale = True)
for e in gen(total_iterations):
pbar.update()
sbar.update(e)
Output:
9%|█████████▉ | 89/1000 [00:11<01:53, 8.05it/s]
40.196MiB [00:11, 3.63MiB/s]
ASCII video (+link):

Gstreamer amplify specific frequencies

I need to gain volume to specific frequency (or range of frequencies). Closest I get is using audiochebband with band-reject. Sample pipeline would be:
gst-launch-1.0 audiotestsrc wave=9 samplesperbuffer=44100 num-buffers=30 volume=0.05 ! \
audio/x-raw,channels=2 ! audioconvert ! \
audiochebband lower-frequency=980 upper-frequency=1000 mode="band-reject" type=1 ripple=60 ! \
vorbisenc ! oggmux ! filesink location=amp.ogg
It gives me some 'some' results. After looking at the plot spectrum, there is huge cut-off after initial amplify of the given frequencies.
I need result like if you use , let say, equilizer inside audacidy, select points between two frequencies and move points up, like this.
Edit:
As an illustration of the requirements I have, here is the simple python script that reads wav file, and amplify range of frequencies.
from __future__ import print_function, division
import wave
import numpy as np
import time
start_time = time.time()
wr = wave.open('input-file.wav', 'r')
par = list(wr.getparams()) # Get the parameters from the input.
# This file is stereo, 2 bytes/sample, 44.1 kHz.
par[3] = 0 # The number of samples will be set by writeframes.
# Open the output file
ww = wave.open('output-file.wav', 'w')
ww.setparams(tuple(par)) # Use the same parameters as the input file.
lowcut = 980 # Select frequencies from.
highcut = 1000 # Select frequencies to.
sz = wr.getframerate() # Read and process 1 second at a time.
c = int(wr.getnframes()/sz) # whole file
for num in range(c):
print('Processing {}/{} s'.format(num+1, c))
da = np.frombuffer(wr.readframes(sz), dtype=np.int16)
left, right = da[0::2], da[1::2] # left and right channel
lf, rf = np.fft.rfft(left), np.fft.rfft(right)
lf[lowcut:highcut] *= 2
rf[lowcut:highcut] *= 2
nl, nr = np.fft.irfft(lf), np.fft.irfft(rf)
ns = np.column_stack((nl,nr)).ravel().astype(np.int16)
ww.writeframes(ns.tostring())
# Close the files.
wr.close()
ww.close()
print("--- %s seconds ---" % (time.time() - start_time))
Generating with noise, and use it as input file with script, there is a perfect gain in selected rage.
Yes. It must be user configurable. It can range from one to n range.
Using this method for one or two frequencies, the result is ok because
by the docs "some ferquencies may be amplified", but with wider range
it amplifies only couple of first and last. I know about
equilizer-nbands (or 3,10...) but I can't fine tune which to amplify
because all band range is amplified that way.
The following is just a suggestion (I can't test as I'm not set up), but as you are successful with one or two frequencies, perhaps you use multiple invocations to amplify subsets of your intending frequencies into different outputs / streams, and then mix the output into a single stream / file.
For example, say you need to amplify 980-2000. Lets start with what you say works:
gst-launch-1.0 audiotestsrc wave=9 samplesperbuffer=44100 num-buffers=30 volume=0.05 ! \
audio/x-raw,channels=2 ! audioconvert ! \
audiochebband lower-frequency=980 upper-frequency=1000 mode="band-reject" type=1 ripple=60 ! \
vorbisenc ! oggmux ! filesink location=amp.ogg
In the above example you are specifying 980-1000.
You can have separate invocations for:
1000-1020
1020-1040
....
1980-2000
All outputting to different files.
Then you can use something like the following to mix several streams into one by adding the data:
https://gstreamer.freedesktop.org/documentation/audiomixer/audiomixer.html?gi-language=c
or:
https://gstreamer.freedesktop.org/documentation/adder/index.html?gi-language=c

How to increases the CPU usage of my Python program?

I am writing a program in Raspberry Pi 3 to read the inputs using an MCP3008 ADC from a function generator. I want to read two channels at the same time and then write it to a file. When I have just one channel, I get about 12000 samples per second, however, when I have two channels, I get about 6000 samples per second for each channel. To get 12000 samples per second for each channels, I tried to write two different scripts, one for each channel and then run them simultaneously in two different terminals. However, that does not solve the problem and gives me 6000 samples per second for each channel.
When I have just one script, I get about 9% CPU usage. When I run two scripts at the same time in two different terminals, I get about 5% CPU usage for each process. How do I get each process to use 9% CPU each or higher?
Thank you!
Here is my code:
import spidev, time, os
# opens SPI bus
spi = spidev.SpiDev()
spi.open(0, 0)
#spi1 = spidev.SpiDev()
#spi1.open(0, 1)
ch0 = 0
#ch1 = 1
now = time.time() * 1000
data = open("test_plot.dat", "w")
#data1 = open("test_plot_1.dat", "w")
def getReading(channel):
# pull raw data from chip
rawData = spi.xfer([1, (8 + channel) << 4, 0])
# process raw data to something we understand
processedData = ((rawData[1]&3) << 8) + rawData[2]
return processedData
"""
def getReading1(channel):
# pull raw data from chip
rawData = spi1.xfer([1, (8 + channel) << 4, 0])
# process raw data to something we understand
processedData = ((rawData[1]&3) << 8) + rawData[2]
return processedData
"""
while True:
if (((time.time() * 1000) - now) < 10001):
data.write("%d\n" % getReading(ch0))
#data1.write("%d\n" % getReading1(ch1))
else:
break
# http://www.takaitra.com/posts/492

Alsaaudio record and playback

I was just playing around with sound input and output on a raspberry pi using python.
My plan was to read the input of a microphone, manipulate it and playback the manipulated audio. At the moment I tried to read and playback the audio.
The reading seems to work, since i wrote the read data into a wave file in the last step, and the wave file seemed fine.
But the playback is noise sounds only.
Playing the wave file worked as well, so the headset is fine.
I think maybe I got some problem in my settings or the output format.
The code:
import alsaaudio as audio
import time
import audioop
#Input & Output Settings
periodsize = 1024
audioformat = audio.PCM_FORMAT_FLOAT_LE
channels = 16
framerate=8000
#Input Device
inp = audio.PCM(audio.PCM_CAPTURE,audio.PCM_NONBLOCK,device='hw:1,0')
inp.setchannels(channels)
inp.setrate(framerate)
inp.setformat(audioformat)
inp.setperiodsize(periodsize)
#Output Device
out = audio.PCM(audio.PCM_PLAYBACK,device='hw:0,0')
out.setchannels(channels)
out.setrate(framerate)
out.setformat(audioformat)
out.setperiodsize(periodsize)
#Reading the Input
allData = bytearray()
count = 0
while True:
#reading the input into one long bytearray
l,data = inp.read()
for b in data:
allData.append(b)
#Just an ending condition
count += 1
if count == 4000:
break
time.sleep(.001)
#splitting the bytearray into period sized chunks
list1 = [allData[i:i+periodsize] for i in range(0, len(allData), periodsize)]
#Writing the output
for arr in list1:
# I tested writing the arr's to a wave file at this point
# and the wave file was fine
out.write(arr)
Edit: Maybe I should mention, that I am using python 3
I just found the answer. audioformat = audio.PCM_FORMAT_FLOAT_LE this format isn't the one used by my Headset (just copied and pasted it without a second thought).
I found out about my microphones format (and additional information) by running speaker-test in the console.
Since my speakers format is S16_LE the code works fine with audioformat = audio.PCM_FORMAT_S16_LE
consider using plughw (alsa subsystem supporting resampling/conversion) for the sink part of the chain at least:
#Output Device
out = audio.PCM(audio.PCM_PLAYBACK,device='plughw:0,0')
this should help to negotiate sampling rate as well as the data format.
periodsize is better to estimate based on 1/times of the sample rate like:
periodsize = framerate / 8 (8 = times for 8000 KHz sampling rate)
and sleeptime is better to estimate as a half of the time necessary to play periodsize:
sleeptime = 1.0 / 16 (1.0 - is a second, 16 = 2*times for 8000 KHz sampling rate)

search a 2GB WAV file for dropouts using wave module

`What is the best way to analyze a 2GB WAV file (1khz Tone) for audio dropouts using wave module? I tried the script below
import wave
file1 = wave.open("testdropout.wav", "r")
file2 = open("silence.log", "w")
for i in xrange(file1.getnframes()):
frame = file1.readframes(i)
zero = True
for j in xrange(len(frame)):
# check if amplitude is greater than 0
# the ord() function converts the hex values to integers
if ord(frame[j]) > 0:
zero = False
break
if zero:
print >> file2, 'dropout at second %s' % (file1.tell()/file1.getframerate())
file1.close()
file2.close()
I haven't used the wave module before, but file1.readframes(i) looks like it's reading 1 frame when you're at the first frame, 2 frames when you're at the second frame, 10 frames when you're in the tenth frame, and a 2Gb CD quality file might have a million frames - by the time you're at frame 100,000 reading 100,000 frames ... getting slower each time through the loop as well?
And from my comment, in Python 2 range() generates an in-memory array of the full size first, and xrange() doesn't, but not using range at all helps even more.
And push the looping down into the lower layers with any() to make the code shorter, and possibly faster:
import wave
file1 = wave.open("testdropout.wav", "r")
file2 = open("silence.log", "w")
chunksize = file1.getframerate()
chunk = file1.readframes(chunksize)
while chunk:
if not any(ord(sample) for sample in chunk):
print >> file2, 'dropout at second %s' % (file1.tell()/chunksize)
chunk = file1.readframes(chunksize)
file1.close()
file2.close()
This should read the file in 1-second chunks.
I think a simple solution to this would be to consider that the frame rate on audio files is pretty high. A sample file on my computer happens to have a framerate of 8,000. That means for every second of audio, I have 8,000 samples. If you have missing audio, I'm sure it will exist across multiple frames within a second, so you can essentially reduce your comparisons as drastically as your standards would allow. If I were you, I would try iterating over every 1,000th sample instead of every single sample in the audio file. That basically means it will examine every 1/8th of a second of audio to see if it's dead. Not as precise, but hopefully it will get the job done.
import wave
file1 = wave.open("testdropout.wav", "r")
file2 = open("silence.log", "w")
for i in range(file1.getnframes()):
frame = file1.readframes(i)
zero = True
for j in range(0, len(frame), 1000):
# check if amplitude is greater than 0
# the ord() function converts the hex values to integers
if ord(frame[j]) > 0:
zero = False
break
if zero:
print >> file2, 'dropout at second %s' % (file1.tell()/file1.getframerate())
file1.close()
file2.close()
At the moment, you're reading the entire file into memory, which is not ideal. If you look at the methods available for a "Wave_read" object, one of them is setpos(pos), which sets the position of the file pointer to pos. If you update this position, you should be able to only keep the frame you want in memory at any given time, preventing errors. Below is a rough outline:
import wave
file1 = wave.open("testdropout.wav", "r")
file2 = open("silence.log", "w")
def scan_frame(frame):
for j in range(len(frame)):
# check if amplitude is less than 0
# It makes more sense here to check for the desired case (low amplitude)
# rather than breaking at higher amplitudes
if ord(frame[j]) <= 0:
return True
for i in range(file1.getnframes()):
frame = file1.readframes(1) # only read the frame at the current file position
zero = scan_frame(frame)
if zero:
print >> file2, 'dropout at second %s' % (file1.tell()/file1.getframerate())
pos = file1.tell() # States current file position
file1.setpos(pos + len(frame)) # or pos + 1, or whatever a single unit in a wave
# file is, I'm not entirely sure
file1.close()
file2.close()
Hope this can help!

Categories

Resources