ul-gh / PiPyADC

Python classes for interfacing Texas Instruments analog-to-digital converters with the Raspberry Pi
GNU Lesser General Public License v2.1
69 stars 27 forks source link

proper way to sample at high speeds #16

Open asafosci opened 5 years ago

asafosci commented 5 years ago

Suppose i want to sample at high rates ~1000 Hz and get a time series like array of [timestamp, measurement value] ? what is the proper way? Currently I'm using the following mechanism expanding on example.py:

while True: raw_channels = ads.read_continue(CH_SEQUENCE) data = [int(i1000 ads.v_per_digit) for i in raw_channels] cur_time = datetime.utcnow() ... and then i store the pair of cur_time, data in an array.

Unfortunately , this does not seem like a good method. there are fluctuations in the cur_time and samples are not evenly spaced. in addition, every additional code in the loop like, data checking, signalling to other threads to save the data etc, may cause loss of points or further inaccuracy in tiume measurements.

Is there a better way ?

ul-gh commented 5 years ago

Hi,

asaf oron wrote:> there are fluctuations in the cur_time and samples are not evenly spaced.

Is there a better way ? If you only need evenly spaced samples, you could leave the ADS1256 in free-running mode and receive the samples with the fixed, set sample rate.

However, this does not work if you need to read different channels by changing the input multiplexer between samples, because for this you need the SPI bus commands.

The ADS1256 has a hardware SYNC-pin, i.e. you could generate the sync pulse using external hardware. But still no timestamp.

Recording a time stamp in very close (millisecond-microsecond) relation to the actual sampling point in time, AFAIK this is not possible using Python or even on a multi-user system at all.

This is a job for a real-time system, e.g you yould try:

Using the system with unmodified hardware, you could try:

This reduces maximum latency from some hundred microseconds (up to more than a millisecond in some cases) down to maximum of approx. 100 microseconds, according to this source for a Raspberry Pi 3 B+:

https://hackaday.io/project/123415-real-time-kernel-preempt-rt-for-raspberry-pi/details

Btw, there is an error in the end of the article, total performance is not reduched /to/ 11%, but /by" 11% ..

Apart from that, you could write a kernel driver as a module with much improved performance.

HTH, Ulrich

asafosci commented 5 years ago

Ulrich

First thank you for the comprehensive explanation. i read the same two channels every time. Can i use the free running mode in this case ?

Regards Asaf

ul-gh commented 5 years ago

Dear Asaf,

Am 18.10.18 um 10:45 schrieb asaf oron:

First thank you for the comprehensive explanation. i read the same two channels every time. Can i use the free running mode in this case ?

I would say this is only possible in case the two inputs are switched together to form a differential pair. I.e. if the input multiplexer does not need to be changed in between.

The reason for this behaviour is that you do not get accurate timing control for the SPI bus when using the pure python interface. This means after switching the input multiplexer in order to read different channels, you get a non-identical length delay, or Jitter. This is normally only then not an issue when using dedicated hardware to do the timing.

Regards, Ulrich

ul-gh commented 2 years ago

Added clearer note in the README.md about the limitation in version 2.

This library does neither support fast continuous streams from the ADC, nor low-latency.

This features configurable individual ADC reads with time overhead of typically slightly lower than one millisecond range which is caused by the operating system and the Python interpreter and otherwise unavoidable.