Black Lives Matter - Action and Equality. ... Adafruit is open and shipping.
0

Analogue Sampling at high rates plus ulab
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Analogue Sampling at high rates plus ulab

by kevinjwalters on Thu Apr 23, 2020 3:48 pm

What's the best approach for analogue sampling at high rates? This was previously discussed in ADC / DAC very slow for SAMD51 (M4) and Is there a blocking analog read and what is sample rate?. Former mentions GitHub: adafruit/circuitpython DMA'd ADC for high-speed sampling -> buffer #487.

I've tried a few things and I can't get better than about 21-21ksps on a CLUE (nRF52840). At the moment I'm only after an average across the samples but it might be nice to leave them in an array of some sort for some further processing. ulab seems tempting but the performance is actually the worst.

I'm curious if alist[:] = [whatever for x in range(n)] does any memory allocation too if alist is an existing list of the same size? I presume it must do because an exception could be thrown half way through and without a new list for the RHS it would make a mess of alist?

I have noticed that ulab allows anarray[:] = p1.value so if there was an efficient way to force the RHS to be re-evaluated each time that would probably be very quick.

One solution that would work well for me is continuous sampling in the background (DMA?) ideally at a fixed, precise rate into a ulab compatible/efficient CircularBuffer and without any data format conversion. Without good rate control spectral analysis with things like FFT is going to yield junk for high end frequencies.

Python's recently given in to assignment in expressions with PEP 572 -- Assignment Expressions. Perhaps that could be of use when/if it works its way into MicroPython/CircuitPython?

Here's some timing data indicating performance. From CircuitPython 5.2.0 REPL with board.DISPLAY.auto_refresh=False (prevent displayio delays) and printing time in ms for reading a 1000 samples from #1 pad left floating:

Code: Select all | TOGGLE FULL SIZE
>>> def samples_v1(pin, num):
...     total = 0
...     for _ in range(num):
...         total += pin.value
...     return (pin.value / num)

>>> t1=time.monotonic_ns() ; samples_v1(p1, 1000) ; t2=time.monotonic_ns(); print((t2 - t1) / 1e6)
0.512
45.933


Code: Select all | TOGGLE FULL SIZE
>>> sample_store_list = []
>>> def samples_v3(pin, num):
...     global sample_store_list
...     sample_store_list = [pin.value for _ in range(num)]
...     return sum(sample_store_list) / num

>>> t1=time.monotonic_ns() ; samples_v3(p1, 1000) ; t2=time.monotonic_ns(); print((t2 - t1) / 1e6)
144.528
47.484


Code: Select all | TOGGLE FULL SIZE
>>> sample_store_array = array.array('H', [0] * 1000)
>>> def samples_v5(pin, num):
...     global sample_store_array
...     sample_store_array = array.array('H', [pin.value for _ in range(num)])
...     return sum(sample_store_array) / num

>>> t1=time.monotonic_ns() ; samples_v5(p1, 1000) ; t2=time.monotonic_ns(); print((t2 - t1) / 1e6)
136.544
54.242


Code: Select all | TOGGLE FULL SIZE
>>> def samples_v6(pin, num):
...     global sample_store_array
...     for idx in range(num):
...         sample_store_array[idx] = pin.value
...     return sum(sample_store_array) / num

>>> t1=time.monotonic_ns() ; samples_v6(p1, 1000) ; t2=time.monotonic_ns(); print((t2 - t1) / 1e6)
154.064
61.428


Code: Select all | TOGGLE FULL SIZE
>>> sample_store_ulabarray = ulab.array(sample_store_list, dtype=ulab.uint16)
>>> def samples_v7(pin, num):
...     global sample_store_ulabarray
...     for idx in range(num):
...         sample_store_ulabarray[idx] = pin.value
...     return ulab.numerical.mean(sample_store_ulabarray)

>>> t1=time.monotonic_ns() ; samples_v7(p1, 1000) ; t2=time.monotonic_ns(); print((t2 - t1) / 1e6)
136.48
92.829

kevinjwalters
 
Posts: 635
Joined: Sun Oct 01, 2017 3:15 pm

Re: Analogue Sampling at high rates plus ulab

by siddacious on Fri Apr 24, 2020 5:42 pm

I'll forward this to someone who can help

siddacious
 
Posts: 271
Joined: Fri Apr 21, 2017 3:09 pm

Re: Analogue Sampling at high rates plus ulab

by jepler on Fri Apr 24, 2020 6:26 pm

Hi!

It looks like you've pretty thoroughly explored and benchmarked the options, so I don't have a whole lot to add.

We would like at some point to add this kind of high(er) speed data acquisition, but haven't scheduled work on it, which is why the issue is labeled "long-term". Check it out on GitHub: https://github.com/adafruit/circuitpython/issues/487

If you can adapt your design, another possibility is that we have a "PDM in" function on both SAMD51 (M4) boards and I think on nRF boards, and also i2s. However, these essentially move the processing of analog signals to external chips and are really directed at audio frequency signals. I'm not sure it gets you a huge benefit in speed, our tutorials show using e.g., 8000Hz sampling for audio vu-meter functionality.

jepler
 
Posts: 23
Joined: Mon Oct 28, 2013 4:16 pm

Please be positive and constructive with your questions and comments.