Generate a Test Data Buffer
Contents
1 Generate a Buffer with Test Data
To debug and test signal processing algorithms it is recommended to use a test data set instead of "real world" data acquired by an ADC. The following code shows an example to generate a buffer filled with a sine wave. The data is truncated to the desired resolution and optionally dithered.
1.1 Includes
#include <stdint.h>
#include <math.h>
1.2 Defines
The following defines control the signal generation.
#define PI 3.1415926535898 /**< pi */
#define FS_IN 44100 /**< input sampling frequency in Hz */
#define RES_IN 16 /**< input resolution in bits */
#define SIG_IN 1000 /**< input signal frequency in Hz */
#define AMPL_IN 0.995 /**< input signal amplitude (0.0 - 1.0) */
#define PHASE_IN (PI/2) /**< input signal phase (-pi to pi) */
#define INBUF_SIZE 32768
#define DITHER_NONE 0 /**< no dither */
#define DITHER_RECT 1 /**< flat spectrum 1/2 LSB dither */
#define DITHER_TRIANGLE 2 /**< high-pass filtered triangular dither*/
#define DITHER_SHAPED 3 /**< Lipschitz minimal audible dither */
#define DITHER_TYPE (DITHER_SHAPED)
#define DITHERSCALE (1.0/((double)RAND_MAX - 0.5))
1.3 Buffer
Depending on the selected resolution the generated test data set is stored in an int8_t, int16_t or int32_t buffer. Data is always stored left justified, i.e. if RES_IN was set to 12, data is stored in a 16-bit buffer with D[15]=MSB, D[4] = LSB, and D[3:0] = 0
#if RES_IN <= 8
int8_t acqbuf[INBUF_SIZE];
#elif RES_IN <= 16
int16_t acqbuf[INBUF_SIZE];
#else
int32_t acqbuf[INBUF_SIZE];
#endif
1.4 Functions
1.4.1 quantisize()
This function quantisizes a normalized double float (range -1.0 to 1.0) to the desired integer size and optionally performs dithering
/*******************************************************************//**
@brief quantisize a normalized double (-1 to +1) to
N-bit integer and store in buffer
@param data - double data
@param bits - resolution in bits
@param buffer_addr - storage location
@return next buffer address
@note requires the following defines:
@note - DITHER_TYPE (DITHER_NONE, DITHER_RECT,
DITHER_TRIANGLE, or DITHER_SHAPED)
***********************************************************************/
uint32_t quantisize (double data, uint32_t bits, uint32_t buffer_addr)
{
/*******************************************************************
locals
*******************************************************************/
double scale;
uint32_t addr = buffer_addr;
#if DITHER_TYPE != DITHER_NONE
double r;
#endif
#if DITHER_TYPE == DITHER_TRIANGLE
static double dither_last = 0.0;
#endif
#if DITHER_TYPE == DITHER_SHAPED
/* Lipshitz's minimally audible FIR */
static double dither_coeff[5] = {2.033, -2.165, 1.959, -1.590, 0.6149};
static double dither_buffer[8] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
static uint32_t dither_phase = 0;
uint32_t idx;
double x;
#endif
/*******************************************************************
scale data
*******************************************************************/
scale = pow(2.0, (double)(bits-1)) - 2.0;
data = data * scale;
#if DITHER_TYPE == DITHER_NONE
/*******************************************************************
round to nearest integer
*******************************************************************/
data = floor (data + 0.5);
#endif
#if (DITHER_TYPE == DITHER_RECT)
/*******************************************************************
add dither and round to nearest integer
*******************************************************************/
r = (double)rand() * DITHERSCALE;
data = floor (data + r + 0.5);
#endif
#if (DITHER_TYPE == DITHER_TRIANGLE)
/*******************************************************************
add high-pass filtered dither and round to nearest integer
*******************************************************************/
r = (double)rand() * DITHERSCALE;
data = floor (data + r - dither_last + 0.5);
dither_last = r;
#endif
#if DITHER_TYPE == DITHER_SHAPED
/*******************************************************************
dither noise shaping in FIR filter
add noise shaped + triangular dither
store last error and roll buffer
*******************************************************************/
x = data;
for (idx = 0; idx < 5; idx++)
{
x = x + dither_buffer[(dither_phase - idx) & 7] * dither_coeff[idx];
}
r = (double)rand() * DITHERSCALE;
r += (double)rand() * DITHERSCALE;
data = x + r;
dither_phase++;
dither_phase &= 7;
data = floor (data + 0.5);
dither_buffer[dither_phase] = x - data;
#endif
/*******************************************************************
saturate
*******************************************************************/
if (data > scale) data = scale;
if (data < -scale) data = -scale;
/*******************************************************************
store in buffer
*******************************************************************/
if (bits <= 8)
{
*(int8_t *)addr = (int8_t)data << (8-bits);
addr += 1;
}
else if (bits <= 16)
{
*(int16_t *)addr = (int16_t)data << (16-bits);
addr +=2;
}
else
{
*(int32_t *)addr = (int32_t)data << (32-bits);
addr += 4;
}
/*******************************************************************
update address
*******************************************************************/
return addr;
}
1.4.2 Sinewave Generator
generate a sinewave, calls quantisize
/*******************************************************************//**
@brief generate a sinewave signal
@param fs - sampling frequency in Hz
@param fsig - signal frequency in Hz
@param asig - signal amplitude (0.0 to 1.0)
@param psig - signal phase (-pi to pi)
@param bits - signal resolution in bits
@param buffer - pointer to buffer memory
@param buffersize - size of buffer in samples
@note output array is left justified
@note calls quantisize()
***********************************************************************/
void sinegen ( uint32_t fs, double fsig, double asig, double psig, uint32_t bits, void* buffer, uint32_t buffersize)
{
/*******************************************************************
locals
*******************************************************************/
double arg;
double tmp;
uint32_t idx;
uint32_t addr = (uint32_t)buffer;
/*******************************************************************
calculate argument
*******************************************************************/
arg = 2.0 * PI * fsig / (double)fs;
/*******************************************************************
calculate sinewave
*******************************************************************/
for (idx = 0; idx < buffersize; idx++)
{
tmp = asig * sin (arg * (double)idx + psig);
addr = quantisize (tmp, bits, addr);
}
}
1.4.3 Example
/***********************************************************************
MAIN PROGRAM
***********************************************************************/
void main (void)
{
/*******************************************************************
generate acquisition sample buffer
*******************************************************************/
sinegen (FS_IN, SIG_IN, AMPL_IN, PHASE_IN, RES_IN, acqbuf, INBUF_SIZE);
}
1.5 Dither Modes
The following spectrum plots show the effect of dithering:
2 Additional Tags
Sinewave Dither Noiseshaping