frame

Getting a large number of samples from ADC resets the deivce

findal Member
edited May 26 in Project Guidance
Hi,
I'm trying to build a device for environmental noise measurement and for this purpose I need to take samples at 48kHz for at least 0.05s to capture the whole frequency range of interest. However, there is something very peculiar about the way the device behaves. If I'm only acquireing samples, i.e. the rest of the code is commented out, everything works fine. If I add the lines of code that convert the ADC output codes to voltages, the program will make three loops and then the device resets. If I then uncomment the part of code to weight the samples (which I've checked and it works fine), the device won't even manage to acquire the samples. I does however enter the main loop. And finally, if I only try to acquire 100 samples, everything works flawlessly.
I thought that it could have something to do with memory limitations, so I tried to get rid of tuplets/lists I don't use as quick as possible in the program. Am I correct now that this is not possible in Zerynth? Whenever I try
del tuple
I get:
Can't find name [tuple]
 Anyway, it seems unlikely to be the actual reason.
Here's my code:

import streams
import A_weighting2
import rms
import SPLmap
import adc

sampling_freq = 48000
preamp_gain = 125 # voltage gain of the preamp
amp_gain = 100 # voltage gain of the amplifier
mic_sens = 6.5 # [mV/Pa] sensitivity of the microphone
th_sens = 0.00002 # [Pa] threshold of hearing
V_th = th_sens*mic_sens*preamp_gain*amp_gain # [V] RMS voltage at threshold of hearing
sampling_time = 0.05 # [s] sampling time
inputAnalog = A0 # define the analog pin for reading the value ^
conv_const = 3300.0/4095.0

# reload the driver with a custom sampling frequency
adc.done(ADC0)
adc.init(ADC0, sampling_freq)

streams.serial()

pinMode(inputAnalog,INPUT_ANALOG)

pinMode(LED0,OUTPUT)

for i in range(2):
    sleep(1000)               # wait for a second
    digitalWrite(LED0, HIGH)  # turn the LED ON by setting the voltage HIGH
    sleep(1000)               # wait for a second
    digitalWrite(LED0, LOW)   # turn the LED OFF by setting the voltage LOW

print("\rEntering loop")
while True:
    
    samples = adc.read(ADC0, int(sampling_freq*sampling_time)) # get Raw values (0-4095)
    print("\r\tAcquired samples!")
    
    
    samples_conv = [] # list of samples converted to voltage
    
    
    for i in range(len(samples)):
        samples_conv.append(samples[i]*conv_const) # Convert all raw values to millivolts
        
    samples = ()

    print("\r\tSamples converted!")
    
    MS = A_weighting2.a_weight(samples_conv) # Get the Mean Square of the weighted samples using A-weighting
    print("\r\tWeighted samples!\n\n")
    
    #RMS = rms.rms(samples) # get the RMS value of weighted samples
    #print("Calculated RMS value!")
    
    #SPL = SPLmap.map(MS/V_th) # get a logarithm of the argument from a look-up table
    
    #print(SPL, "dBA")
    
    sleep(3000)
Tagged:

Comments

  • Hi findal,

    Which device are you using? Can you post me all your project file so I can better understand and help you to fix this issue?

    At the same time, if you want to check memory usage, you can import the Garbage Collector lib, printing gc.info() and monotoring if the "free memory" (second value of the returned tuple) is not near to 0.

    Let me know :)
    Matteo Cipriani
    Zerynth Support Team
  • findal Member
    edited June 2
    I'm using Particle Photon.
    I've tried monitoring the memory usage and it looks like the problem is something else. This is the output of gc.info() after each step. I got from the device when collecting just 100 samples, before it reset itself:
    (120352, 114076, 1, 251, 18, 1500, 0)
    Entering loop
            Acquired samples!
            (120352, 110968, 0, 328, 1, 25, 1)
            Samples converted!
            (120352, 83388, 0, 794, 1, 25, 6)
            Weighted samples!
            (120352, 47964, 92, 3690, 10, 25, 44)
    
            Acquired samples!
            (120352, 45040, 91, 3763, 10, 25, 3045)
            Samples converted!
            (120352, 17432, 77, 4229, 10, 25, 3049)
            Weighted samples!
            (120352, 89488, 96, 1541, 116, 25, 16)
    
     When I try to collect full 2400 samples it doesn't even go past the data acquisition phase.

    In the attachment are all the .py files I'm using.
  • Hi findal,

    Yes seems like the problem is not the memory usage.
    In these days I will study your code and work to find a solution for your issue.

    Stay tuned for good news :)
    Matteo Cipriani
    Zerynth Support Team
  • anbaanba Member
    Hi findal,
    we succesfully ran tests using a slightly modified firmware.

    A good practice to optimize memory usage is to define lists with their final length since the beginning instead of building them up with append.

    So I would reccomend you to use this:
    samples_conv = [0] * len(samples)
    for i in range(len(samples)):
    samples_conv[i] = samples[i] * conv_const
    instead of this:
    samples_conv = [] # list of samples converted to voltage
    for i in range(len(samples)):
    samples_conv.append(samples[i]*conv_const) # Convert all raw values to millivolts
    Then, to free the memory used for (what is referred by) samples you can just call
    samples = None
    In the same way you can call
    samples_conv = None
    after using the variable to calculate the rms value.

    With these changes, the firmware should go through the loop without problems.

    Said this, below is some consideration about the rest of the code.

    1) I noted that rms sometimes almost reach the representability limit and MS is printed as "Inf". This seems to be a bug of the print function and do not affects the rms results.
    Currently Zerynth uses 31 and 32 bits for integer and floating point numbers respectively; in the future, 64-bit floats will be supported.

    However, the following snippet show how to avoid the print-Inf / overflow condition (on the other hand this workaround worsen the performances a bit)
    def rms2(samples):
    MS = 0
    l = len(samples)
    for i in range(l):
    MS += (samples[i]**2) / l
    RMS = MS**(0.5)
    return RMS

    2) The a-weighting algorithm take more or less 3 seconds to complete its work (you can find yourself the actual value importing Zerynth standard library timers and playing with it).
    We all know that convolution processing is computationally intensive but, in this specific case, you can cut a few tenths of a second really easily with 2 small changes:

    This division is performed at every iteration of the main loop (line 53):
    weighted_sample = weighted_sample / a2[0]
    but a2[0] is equal to 1 so you can simply comment this line.

    Another operation that is performed at each iteration is (line 38):
    for i in range(len(b2)):
    but b2 never changes. I would define a new variable before the loop to use some lines later:
    b2_length = len(b2)

    for sample_no in range(len(samples)):
    for i in range(b2_length):
    ...
    Maybe you can further optimize the algorithm (I guess that some if check can be saved) to decrease the run time.

    We look forward to receive an update on your project!
    Andrea Baù
    Zerynth Support Team
  • findal Member
    Hi anba,

    thank you for your expertise, it looks rather promising :) I'll let you know in a couple of days how it went.

    Greetings
  • findal Member
    Dear anba,

    I've implemented everything you've suggested and now it runs smoothly. Thank you very much!

    Greetings
Sign In or Register to comment.

ZERYNTH Community

@ 2016 Zerynth.com, all rights reserved.