Fast Signal Sampling (Part 5)

Part 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 11, 12

Scanning data from an analog port inevitably leads to some compromises: time resolution or signal resolution. We know that converting analog signal in digital counts takes time, whatever the conversion mode except may be the flash conversion (which is neither cost, nor power effective). This time is proportional to the number of bits used for converting the analog signal. On the other hand, and this is critical on Arduino, using higher signal resolution means larger vectors of data!

In my first approach of sound capture, my goal was to privilege the scanning rate to the detriment of signal resolution (8 bits). The need for more resolution in some other applications forced me to rework the PlainADC library. For this reason, I introduced 10 bits resolution stored in consecutive 2 bytes.

Then, after some thoughts about the signals that I want to measure, I realized that the changes in intensities are small from one data point to the next one: e.g. temperature monitoring. Sine waves are a little bit more challenging, since the difference between consecutive points varies along the cycle. The function which applies to this type of signal analysis is called Delta Encoding. It is a reversible process that we will exercise on a pure sine wave signal, which amplitude ranges from 0 to 1024 counts, sampled at various frequencies.

Sine wave sampled 16 times

Plot of delta encoding function

Note that the first measurement is some sort of absolute measurement, and that the others are the relative measurements, ranging from -200 to +200 counts.

Sine wave sampled 31 times

Plot of delta encoding function

Note that the relative measurements now range from -100 to +100 counts. These values can be converted in a signed 8 bits binary value.

Sine wave sampled 63 times

Plot of delta encoding function

Note that the relative measurements now range from -50 to +50 counts.

Letâ€™s convert this delta encoding function in some applicable code. Firstly, we need to record the first absolute measurement which shall be a 10 bits unsigned integer. That is easy, and we will reserve the first two bytes of the data vector for this record. Then we must record each relative measurement. This means that we will have to keep in memory the value from the last a/d conversion and subtract it from the current one. The difference shall be recorded as signed byte. Unfortunately, Arduino IDE does not allow this, so that we will have to use two custom functions for converting a signed 16 bits integer in a signed 8 bits integer and vice versa.

```uint8_t PlainADC::signedIntToSignedByte(int16_t signedIntValue) {
/* Converts a signed 16 bits integer in a signed byte */
uint8_t result = (signedIntValue & 0x7F); /* Extract the 7 LSBs */
if (signedIntValue & 0x8000) {
/* Negative value */
result |= 0x80;
}
return(result);
}

/* Converts a signed byte in a signed 16 bits integer */
uint16_t result = (signedByteValue & 0x7F); /* Extract the 7 LSBs */
if (signedByteValue & 0x80) {
/* Negative value, using 1s mask */
result |= 0xFF80;
}
return(result);
}
```

Conclusion
Delta encoding allows storage of higher resolution data at the cost of one more byte. However, care shall be taken that the difference between two consecutives measurements does not exceed the signed byte capacity (-128 to 127).

Next post on same subject

1. […] Previous post on same subject […]

2. DE8MSH says:

Hallo Didier,

I’m planning a small FFT based spectrum analyser with Arduino Uno and a SSD 1325 OLED display. You can see a demo here:

and here

The FFT routine I use there produces mirror signals etc. So I decided to use you code. But: how to make the output?

Here’s the reduced code:

###################################################
#include “PlainFFT.h”
#include

PlainFFT FFT = PlainFFT(); // Create FFT object
// These values can be changed in order to evaluate the functions
const uint16_t samples = 64;
double signalFrequency = 1000;
double samplingFrequency = 8192;
uint8_t signalIntensity = 100;
// These are input and output vectors
double vReal[samples];
double vImag[samples];
uint8_t runOnce = 0x00;

#define SCL_INDEX 0x00
#define SCL_TIME 0x01
#define SCL_FREQUENCY 0x02

void setup(){
OLED.init();
OLED.clear_screen();
analogReference(INTERNAL); /// Put analogue port to 1.1V to read Audio from source (PC/RX).
}

void loop() {
if (runOnce == 0x00) {
runOnce = 0x00;
// Build raw data
for (uint8_t i = 0; i < samples; i++) {
}

FFT.windowing(vReal, samples, FFT_WIN_TYP_HAMMING, FFT_FORWARD); // Weigh data
FFT.compute(vReal, vImag, samples, FFT_FORWARD); // Compute FFT
FFT.complexToMagnitude(vReal, vImag, samples); // Compute magnitudes
double x = FFT.majorPeak(vReal, samples, samplingFrequency);
Serial.println(x, 6);
// Here my OLED set pixel function – but howto? //
}
}
###################################################

Where can I place the output routine? Goal ist to read every vReal and draw it on display with

OLED.drawLine(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2, unsigned char c)

for or

OLED.set2pixels(unsigned char x, unsigned char y,unsigned char first,unsigned char second)

I used OLED.set2pixels(unsigned char x, unsigned char y,unsigned char first,unsigned char second) in the demos you can see in Youtube videos…

Thank you for support.

Marco
DE8MSH

• Didier says:

Hi Marco,
Funny project indeed. Unfortunately I do not have this sort of dsiplay and I do not plan to develop this type of application for the time being. However, I suggest that you carefully check my recent posts and code which contain really usefull information regarding your project. Good luck and keep well.