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
Related
Hi i had an issue to discuss,
and i dont realy understand to send data with SPI with Python
I want to send data with my Raspberry Pi 4 ver.b using Python to send data to my module named AD9833 DDS. So i found code in internet, writed in Python (sor. https://ez.analog.com/dds/f/q-a/28431/ad9833-programming-in-raspberry-pi-using-python). This is the code :
# The code write by SamMaster, Oct 21 2016
# importing library
import time
import spidev
# activate spidev module and settings SPI
spi = spidev.SpiDev()
spi.open(0,1)
spi.max_speed_hz = 976000
# initialize frequency and another value
freq_out = 400
Two28 = 268435456
phase = 0
after the programmer call all library, function and set the value, his try to define a function to send a data.
def send_data(input):
tx_msb = input >> 8
tx_lsb = input & 0xFF
spi.xfer([tx_msb,txlsb])
print(input)
so that this frequencies value is able to read by AD9833, this frequency must convert to freq word, so programmer write the code,
freq_word = int(round(float(freq_out*Two28)/25000000))
and then the programmer define all of MSB and LSB
MSB = (freq_word & 0xFFC000)>>14
LSB = (freq_word & 0x3FFF)
LSB |= 0x4000
MSB |= 0x4000
phase|= 0xC000
and then, function that the programmer built implement in this blocks of codes
send_data(LSB)
send_data(MSB)
send_data(phase)
send_data(0x2000)
its worked on my Raspberry Pi 4, this is the result on my device,
Result for 400Hz
Result for 500Hz
when i change the frequency so freq_out = 500 there is no changes, just the value is aproximately 400 Hz on my scope. So i try this simple solution, i put the code send_data(0x2000), 0x2000 it mean Reset AD9833 according to datasheet, above the send_data(LSB) code. So the code became,
send_data(0x2000)
send_data(LSB)
send_data(MSB)
send_data(phase)
and this the result,
freq_out = 400 freq_out = 400
freq_out = 500 freq_out = 500
freq_out = 600 freq_out = 600
freq_out = 1000freq_out = 1000
i don't know why when i writing freq_out = 600 the value output frequency not correct with what i'm inputing. So can anyone want to comment / state argument to my issue ?
This problem can be split into a number of sub tasks.
Values to send
Sequence values sent in
How values sent over SPI
As SamMaster pointed out there is an application note from Analog Devices that shows the sequence of values to send to set the frequency to 400 Hz
https://www.analog.com/media/en/technical-documentation/application-notes/AN-1070.pdf
They summarise the five values to send and in what order in the following table:
If I look at the code that SamMaster has written it is writing the correct values in the correct order (I don't have the hardware but I can print the values).
sending: [0x21, 0x00]
sending: [0x50, 0xc7]
sending: [0x40, 0x00]
sending: [0xc0, 0x00]
sending: [0x20, 0x00]
That just leaves bullet 3 that is causing the problems.
The fact that you get changes happening on the hardware suggests that some kind of communication is happening, just not the correct values.
Looking at the limited documentation at https://pypi.org/project/spidev/ there are two likely commands that could be used: xfer or xfer2.
The difference between the two are the value of the chip select pin between blocks.
Figure 4 in the data sheet I think is saying that chip select should not be released between the two bytes.
https://www.analog.com/media/en/technical-documentation/data-sheets/ad9833.pdf
That would suggest that xfer2 should be to used to send the blocks and not xfer as SamMaster has done. Although SamMaster seems to suggest he got it working with xfer and you were able to set the value to 400 Hz successfully. You would need your scope/logic analyser to see if the GPIO is doing the right thing on the hardware.
At some point in your development you seem to have changed the sequence of values to send. It should be:
send_data(0x2100) # Start
send_data(LSB) # Frequency 14 bits (LSB)
send_data(MSB) # Frequency 14 bits (MSB)
send_data(phase) # Phase value
send_data(0x2000) # End
This could be another source of your error.
I looked at what the values should be that get sent for the different frequency you tested. I calculated the values follows:
For Frequency: 400
Frequency setting: 4295 = 0x10c7 = 0001000011000111
send_data(0x2100)
send_data(0x50c7)
send_data(0x4000)
send_data(0xc000)
send_data(0x2000)
For Frequency: 500
Frequency setting: 5369 = 0x14f9 = 0001010011111001
send_data(0x2100)
send_data(0x54f9)
send_data(0x4000)
send_data(0xc000)
send_data(0x2000)
For Frequency: 600
Frequency setting: 6442 = 0x192a = 0001100100101010
send_data(0x2100)
send_data(0x592a)
send_data(0x4000)
send_data(0xc000)
send_data(0x2000)
For Frequency: 1000
Frequency setting: 10737 = 0x29f1 = 0010100111110001
send_data(0x2100)
send_data(0x69f1)
send_data(0x4000)
send_data(0xc000)
send_data(0x2000)
And finally, I refactored the code to make it easier for me to test the different parts of the code. I'm sharing it here for your information. I had to comment out any of the spi communication parts because I don't have the hardware.
import time
import spidev
# activate spidev module and settings SPI
bus = 0
device = 1
spi = spidev.SpiDev()
spi.open(bus, device)
spi.max_speed_hz = 976000
def output_freq(hz_value):
return int(round((hz_value * 2**28) / 25e6))
def freq_change_start():
ctrl_reg = 0
ctrl_reg += 2**13 # set DB13 (28 bit freq reg)
ctrl_reg += 2**8 # set DB8 (Reset)
return ctrl_reg
def freq_reg_lsb(freq_reg):
fourteen_bit_mask = 0b0011111111111111
write_value = 0
write_value += 2**14 # set DB14
lsb = freq_reg & fourteen_bit_mask
write_value += lsb
return write_value
def freq_reg_msb(freq_reg):
fourteen_bit_mask = 0b0011111111111111
write_value = 0
write_value += 2**14 # set DB14
msb = freq_reg >> 14 & fourteen_bit_mask
write_value += msb
return write_value
def phase_register():
# Currently always the same value
write_value = 0
# Set phase register address
write_value += 2 ** 15 # set DB15
write_value += 2 ** 14 # set DB14
return write_value
def freq_change_end():
ctrl_reg = 0
ctrl_reg += 2**13 # set DB13 (28 bit freq reg)
return ctrl_reg
def word_split(word16):
tx_msb = word16 >> 8
tx_lsb = word16 & 0xFF
return tx_msb, tx_lsb
def send_spi_sequence(sequence):
for word16 in sequence:
two_bytes = word_split(word16)
print(f"\tsending:[{two_bytes[0]:#02x}, {two_bytes[1]:#02x}]")
print(f"\tsend_data({word16:#06x})")
spi.xfer(two_bytes)
# spi.xfer2(two_bytes)
def change_freq(freq_hz):
# Calc values to send
print("For Frequency:", freq_hz)
freq_reg = output_freq(freq_hz)
print(f"Frequency setting: {freq_reg} = {freq_reg:#04x} = {freq_reg:016b}")
ctrl_start = freq_change_start()
print(f"Control register write: {ctrl_start:#04x}")
lsb_value = freq_reg_lsb(freq_reg)
print(f"lsb value: {lsb_value:#04x}")
msb_value = freq_reg_msb(freq_reg)
print(f"lsb value: {msb_value:#04x}")
phase_reg = phase_register()
print(f"Phase register write: {phase_reg:#04x}")
ctrl_end = freq_change_end()
print(f"Control register write: {ctrl_end:#04x}")
# Write values to spi
send_spi_sequence([ctrl_start, lsb_value, msb_value, phase_reg, ctrl_end])
def main():
show_freq_for = 20
change_freq(400)
time.sleep(show_freq_for)
change_freq(500)
time.sleep(show_freq_for)
change_freq(600)
time.sleep(show_freq_for)
change_freq(1000)
time.sleep(show_freq_for)
if __name__ == '__main__':
main()
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.
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)
Rather than crawl PubChem's website, I'd prefer to be nice and generate the images locally from the PubChem ftp site:
ftp://ftp.ncbi.nih.gov/pubchem/specifications/
The only problem is that I'm limited to OSX and Linux and I can't seem to find a way of programmatically generating the 2d images that they have on their site. See this example:
https://pubchem.ncbi.nlm.nih.gov/compound/6#section=Top
Under the heading "2D Structure" we have this image here:
https://pubchem.ncbi.nlm.nih.gov/image/imgsrv.fcgi?cid=6&t=l
That is what I'm trying to generate.
If you want something working out of the box I would suggest using molconvert from ChemAxon's Marvin (https://www.chemaxon.com/products/marvin/), which is free for academics. It can be used easily from the command line and it supports plenty of input and output formats. So for your example it would be:
molconvert "png" -s "C1=CC(=C(C=C1[N+](=O)[O-])[N+](=O)[O-])Cl" -o cdnb.png
Resulting in the following image:
It also allows you to set parameters such as width, height, quality, background color and so on.
However, if you are a programmer I would definitely recommend RDKit. Follows a code which generates images for a pair of compounds given as smiles.
from rdkit import Chem
from rdkit.Chem import Draw
ms_smis = [["C1=CC(=C(C=C1[N+](=O)[O-])[N+](=O)[O-])Cl", "cdnb"],
["C1=CC(=CC(=C1)N)C(=O)N", "3aminobenzamide"]]
ms = [[Chem.MolFromSmiles(x[0]), x[1]] for x in ms_smis]
for m in ms: Draw.MolToFile(m[0], m[1] + ".svg", size=(800, 800))
This gives you following images:
So I also emailed the PubChem guys and they got back to me very quickly with this response:
The only bulk access we have to images is through the download
service: https://pubchem.ncbi.nlm.nih.gov/pc_fetch/pc_fetch.cgi
You can request up to 50,000 images at a time.
Which is better than I was expecting, but still not amazing since it requires downloading things that I in theory could generate locally. So I'm leaving this question open until some kind soul writes an open source library to do the same.
Edit:
I figure I might as well save people some time if they are doing the same thing as I am. I've created a Ruby Gem backed on Mechanize to automate the downloading of images. Please be kind to their servers and only download what you need.
https://github.com/zachaysan/pubchem
gem install pubchem
An open source option is the Indigo Toolkit, which also has pre-compiled packages for Linux, Windows, and MacOS and language bindings for Python, Java, .NET, and C libraries. I chose the 1.4.0 beta.
I had a similar interest to yours in converting SMILES to 2D structures and adapted my Python to address your question and to capture timing information. It uses the PubChem FTP (Compound/Extras) download of CID-SMILES.gz. The following script is an implementation of a local SMILES-to-2D-structure converter that reads a range of rows from the PubChem CID-SMILES file of isomeric SMILES (which contains over 102 million compound records) and converts the SMILES to PNG images of the 2D structures. In three tests with 1000 SMILES-to-structure conversions, it took 35, 50, and 60 seconds to convert 1000 SMILES at file row offsets of 0, 100,000, and 10,000,000 on my Windows 10 laptop (Intel i7-7500U CPU, 2.70GHz) with a solid state drive and running Python 3.7.4. The 3000 files totaled 100 MB in size.
from indigo import *
from indigo.renderer import *
import subprocess
import datetime
def timerstart():
# start timer and print time, return start time
start = datetime.datetime.now()
print("Start time =", start)
return start
def timerstop(start):
# end timer and print time and elapsed time, return elapsed time
endtime = datetime.datetime.now()
elapsed = endtime - start
print("End time =", endtime)
print("Elapsed time =", elapsed)
return elapsed
numrecs = 1000
recoffset = 0 # 10000000 # record offset
starttime = timerstart()
indigo = Indigo()
renderer = IndigoRenderer(indigo)
# set render options
indigo.setOption("render-atom-color-property", "color")
indigo.setOption("render-coloring", True)
indigo.setOption("render-comment-position", "bottom")
indigo.setOption("render-comment-offset", "20")
indigo.setOption("render-background-color", 1.0, 1.0, 1.0)
indigo.setOption("render-output-format", "png")
# set data path (including data file) and output file path
datapath = r'../Download/CID-SMILES'
pngpath = r'./2D/'
# read subset of rows from data file
mycmd = "head -" + str(recoffset+numrecs) + " " + datapath + " | tail -" + str(numrecs)
print(mycmd)
(out, err) = subprocess.Popen(mycmd, stdout=subprocess.PIPE, shell=True).communicate()
lines = str(out.decode("utf-8")).split("\n")
count = 0
for line in lines:
try:
cols = line.split("\t") # split on tab
key = cols[0] # cid in cols[0]
smiles = cols[1] # smiles in cols[1]
mol = indigo.loadMolecule(smiles)
s = "CID=" + key
indigo.setOption("render-comment", s)
#indigo.setOption("render-image-size", 200, 250)
#indigo.setOption("render-image-size", 400, 500)
renderer.renderToFile(mol, pngpath + key + ".png")
count += 1
except:
print("Error processing line after", str(count), ":", line)
pass
elapsedtime = timerstop(starttime)
print("Converted", str(count), "SMILES to PNG")
I'm running xillinux (more or less ubuntu 12.04) on zedboard. Zedboard combines arm with fpga.
Now have written a python script wich reads data from a device driver (/dev/xillybus_read_32).
Now i know the data i recieve is complete and correct (i used a prewritten program that stored al the data in a dumpfile, i splitted this dumpfile in frames and cheched if the content was correct ( every frame contains an addition, so easy to check).
Now when i try to recieve data from the device driver with the following python script:
#Read data
#Framesize
CONST_FRAMESIZE = (640*480*16)/8
#Info
frame_true = 0
frame_false = 0
#Open file
pipe_in = open("/dev/xillybus_read_32","r")
count = 0
count_false = 0
for z in xrange(1000):
frame = "frame" + str(count) + ".raw"
pipe_out = open(frame,"wb")
for r in xrange(CONST_FRAMESIZE/4):
value = pipe_in.read(4)
pipe_out.write(value)
pipe_out.close()
#Compare goldendata with frame
if filecmp.cmp("goldendata",frame):
frame_true = frame_true + 1
if count >= 1:
os.remove(frame_last)
frame_last = frame
else:
print "frame_true:", frame_true
pipe_in.close()
sys.exit()
#frame_false = frame_false + 1
#os.remove(frame)
count = count + 1;
#Close opend file
pipe_in.close()
I recieve all the data but, sometimes i get my 32 bit words 2 times. It's like it sometimes reads the 32bit word twice. I doesn't lose data, it just reads a 32 bit sometimes 2 times. Very strange.
Thnx