Float into uint8_t array - c

I want to make 4 different ADC measurements. I want to be that those readed values are floats. After that I want to put those 4 float values in an uint8_t to send it over the function:
uint32_t ble_nus_data_send(ble_nus_t * p_nus,
uint8_t * p_data,
uint16_t * p_length,
uint16_t conn_handle)
This minimal code works:
static void app_timer_handler(void * p_context)
{
uint8_t p_dat[] = "hi";
uint16_t len = sizeof(p_dat)-1;
ret_code_t err_code = ble_nus_data_send(&m_nus, p_dat, &len, m_conn_handle);
APP_ERROR_CHECK(err_code);
}
But when I try to send a val it goes wrong:
static void app_timer_handler(void * p_context)
{
if(m_conn_handle != BLE_CONN_HANDLE_INVALID)
{
uint8_t values[20];
ret_code_t err_code;
err_code = nrfx_saadc_sample_convert(0, &m_sample);
APP_ERROR_CHECK(err_code);
float value1 = m_sample * 3.0 / 4096;
sprintf((char*)values, "%.2f", value1);
uint16_t len = sizeof(values);
err_code = ble_nus_data_send(&m_nus, values, &len, m_conn_handle);
APP_ERROR_CHECK(err_code);
}
}
After getting the sample value of the ADC I will make calculations to get the voltage. I read it out with a 12-bit adc.
The expected received output of the uint8_t must be like [float1 float2 float3 float4]. This because I want to process the values in Python and plot them in a graph.
To receive the sended data I do use a dongle.
I did try this:
static void app_timer_handler(void * p_context)
{
if(m_conn_handle != BLE_CONN_HANDLE_INVALID)
{
float values[4];
ret_code_t err_code;
err_code = nrfx_saadc_sample_convert(0, &m_sample);
APP_ERROR_CHECK(err_code);
values[0] = m_sample * 3.0 / 4096;
err_code = nrfx_saadc_sample_convert(1, &m_sample);
APP_ERROR_CHECK(err_code);
values[1] = m_sample * 3.0 / 4096;
err_code = nrfx_saadc_sample_convert(2, &m_sample);
APP_ERROR_CHECK(err_code);
values[2] = m_sample * 3.0 / 4096;
err_code = nrfx_saadc_sample_convert(3, &m_sample);
APP_ERROR_CHECK(err_code);
values[3] = m_sample * 3.0 / 4096;
uint16_t len = sizeof(values);
err_code = ble_nus_data_send(&m_nus, (uint8_t*)values, &len, m_conn_handle);
APP_ERROR_CHECK(err_code);
}
}
Received values1
And also tried this:
static void app_timer_handler(void * p_context)
{
if(m_conn_handle != BLE_CONN_HANDLE_INVALID)
{
uint8_t values[4];
ret_code_t err_code;
err_code = nrfx_saadc_sample_convert(0, &m_sample);
APP_ERROR_CHECK(err_code);
float value1 = m_sample * 3.0 / 4096;
err_code = nrfx_saadc_sample_convert(1, &m_sample);
APP_ERROR_CHECK(err_code);
float value2 = m_sample * 3.0 / 4096;
err_code = nrfx_saadc_sample_convert(2, &m_sample);
APP_ERROR_CHECK(err_code);
float value3 = m_sample * 3.0 / 4096;
err_code = nrfx_saadc_sample_convert(3, &m_sample);
APP_ERROR_CHECK(err_code);
float value4 = m_sample * 3.0 / 4096;
sprintf(values, "%.2f %.2f %.2f %.2f\n", value1, value2, value3, value4);
uint16_t len = sizeof(values);
err_code = ble_nus_data_send(&m_nus, (uint8_t*)values, &len, m_conn_handle);
APP_ERROR_CHECK(err_code);
}
}
Received values2
Both ways didn't worked out for the output.
The output expected on Python is [val1 val2 val3 val4]. So the values can be seperated.
This is the function that handles the received data on the dongle.
static void ble_nus_chars_received_uart_print(uint8_t * p_data, uint16_t data_len)
{
bsp_board_led_invert(LED_BLE_NUS_RX);
NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on CDC ACM.");
NRF_LOG_HEXDUMP_DEBUG(p_data, data_len);
memcpy(m_nus_data_array, p_data, data_len);
// Add endline characters
uint16_t length = data_len;
if (length + sizeof(ENDLINE_STRING) < BLE_NUS_MAX_DATA_LEN)
{
memcpy(m_nus_data_array + length, ENDLINE_STRING, sizeof(ENDLINE_STRING));
length += sizeof(ENDLINE_STRING);
}
// Send data through CDC ACM
ret_code_t ret = app_usbd_cdc_acm_write(&m_app_cdc_acm,
m_nus_data_array,
length);
if(ret != NRF_SUCCESS)
{
NRF_LOG_INFO("CDC ACM unavailable, data received: %s", m_nus_data_array);
}
}
The received values are random chars. I think this might be because the receiving function. Do I have to make changes there or somewhere else?

This is not a full solution but shows errors in your code and a possible fix.
uint8_t values[4];
This is an array of 4 bytes. It can hold a string of 3 characters and a terminating NUL ('\0') character.
sprintf(values, "%.2f %.2f %.2f %.2f\n", value1, value2, value3, value4);
This will write many more characters, so it will write past the end of the array which is undefined behavior.
uint16_t len = sizeof(values);
This is the size of the array which is 4. You probably need the number of characters which would be the return value from sprintf or could be obtained with strlen.
Incomplete and untested example:
/* I did not check the maximum size for the format string.
* Can probably be smaller. */
uint8_t values[100];
/* ... */
/* In contrast to sprintf, snprintf limits the number of characters including
* the terminating '\0' to the specified size. Might not be necessary if you
* correctly calculate the maximum possible string length and define the
* array size accordingly. */
int bytes = snprintf(values, sizeof(values),
"%.2f %.2f %.2f %.2f\n", value1, value2, value3, value4);
if(bytes < 0)
{
/* error occurred */
bytes = 0;
}
else if(bytes >= sizeof(values))
{
/* output was truncated */
bytes = sizeof(values) - 1;
}
uint16_t len = bytes;
err_code = ble_nus_data_send(&m_nus, (uint8_t*)values, &len, m_conn_handle);

You could send the float array as raw data, ie, the bytes they already are:
float values[4];
values[0] = sample * ...
values[1] = sample * ...
values[2] = sample * ...
values[3] = sample * ...
uint16_t d_len = sizeof(values);
err_code = ble_nus_data_send(&m_nus, (uint8_t *)values, &d_len, m_conn_handle);
APP_ERROR_CHECK(err_code);
Note that a typical size of float is 4 bytes.
Also, sending raw float values will work fine if the receiver is using the SAME floating point representation.
Here's a suggestion for the receiving side, just to verify your float values are being received properly:
static void ble_nus_chars_received_uart_print(uint8_t * p_data, uint16_t data_len)
{
float *myFloats = (float *)p_data;
bsp_board_led_invert(LED_BLE_NUS_RX);
NRF_LOG_HEXDUMP_DEBUG(p_data, data_len);
NRF_LOG_INFO("Float value 0: %f", myFloats[0]);
NRF_LOG_INFO("Float value 1: %f", myFloats[1]);
NRF_LOG_INFO("Float value 2: %f", myFloats[2]);
NRF_LOG_INFO("Float value 3: %f", myFloats[3]);
}

Related

Does fmodf() cause a hardfault in stm32?

I am trying to create a modulated waveform out of 2 sine waves.
To do this I need the modulo(fmodf) to know what amplitude a sine with a specific frequency(lo_frequency) has at that time(t). But I get a hardfault when the following line is executed:
j = fmodf(2 * PI * lo_frequency * t, 2 * PI);
Do you have an idea why this gives me a hardfault ?
Edit 1:
I exchanged fmodf with my_fmodf:
float my_fmodf(float x, float y){
if(y == 0){
return 0;
}
float n = x / y;
return x - n * y;
}
But still the hardfault occurs, and when I debug it it doesn't even jump into this function(my_fmodf).
Heres the whole function in which this error occurs:
int* create_wave(int* message){
/* Mixes the message signal at 10kHz and the carrier at 40kHz.
* When a bit of the message is 0 the amplitude is lowered to 10%.
* When a bit of the message is 1 the amplitude is 100%.
* The output of the STM32 can't be negative, thats why the wave swings between
* 0 and 256 (8bit precision for faster DAC)
*/
static int rf_frequency = 10000;
static int lo_frequency = 40000;
static int sample_rate = 100000;
int output[sample_rate];
int index, mix;
float j, t;
for(int i = 0; i <= sample_rate; i++){
t = i * 0.00000001f; // i * 10^-8
j = my_fmodf(2 * PI * lo_frequency * t, 2 * PI);
if (j < 0){
j += (float) 2 * PI;
}
index = floor((16.0f / (lo_frequency/rf_frequency * 0.0001f)) * t);
if (index < 16) {
if (!message[index]) {
mix = 115 + sin1(j) * 0.1f;
} else {
mix = sin1(j);
}
} else {
break;
}
output[i] = mix;
}
return output;
}
Edit 2:
I fixed the warning: function returns address of local variable [-Wreturn-local-addr] the way "chux - Reinstate Monica" suggested.
int* create_wave(int* message){
static uint16_t rf_frequency = 10000;
static uint32_t lo_frequency = 40000;
static uint32_t sample_rate = 100000;
int *output = malloc(sizeof *output * sample_rate);
uint8_t index, mix;
float j, n, t;
for(int i = 0; i < sample_rate; i++){
t = i * 0.00000001f; // i * 10^-8
j = fmodf(2 * PI * lo_frequency * t, 2 * PI);
if (j < 0){
j += 2 * PI;
}
index = floor((16.0f / (lo_frequency/rf_frequency * 0.0001f)) * t);
if (index < 16) {
if (!message[index]) {
mix = (uint8_t) floor(115 + sin1(j) * 0.1f);
} else {
mix = sin1(j);
}
} else {
break;
}
output[i] = mix;
}
return output;
}
But now I get the hardfault on this line:
output[i] = mix;
EDIT 3:
Because the previous code contained a very large buffer array that did not fit into the 16KB SRAM of the STM32F303K8 I needed to change it.
Now I use a "ping-pong" buffer where I use the callback of the DMA for "first-half-transmitted" and "completly-transmitted":
void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef * hdac){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
for(uint16_t i = 0; i < 128; i++){
new_value = sin_table[(i * 8) % 256];
if (message[message_index] == 0x0){
dac_buf[i] = new_value * 0.1f + 115;
} else {
dac_buf[i] = new_value;
}
}
}
void HAL_DAC_ConvCpltCallbackCh1 (DAC_HandleTypeDef * hdac){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
for(uint16_t i = 128; i < 256; i++){
new_value = sin_table[(i * 8) % 256];
if (message[message_index] == 0x0){
dac_buf[i] = new_value * 0.1f + 115;
} else {
dac_buf[i] = new_value;
}
}
message_index++;
if (message_index >= 16) {
message_index = 0;
// HAL_DAC_Stop_DMA (&hdac1, DAC_CHANNEL_1);
}
}
And it works the way I wanted:
But the frequency of the created sine is too low.
I cap at around 20kHz but I'd need 40kHz.
I allready increased the clock by a factor of 8 so that one is maxed out:
.
I can still decrease the counter period (it is 50 at the moment), but when I do so the interrupt callback seems to take longer than the period to the next one.
At least it seems so as the output becomes very distorted when I do that.
I also tried to decrease the precision by taking only every 8th sine value but
I cant do this any more because then the output does not look like a sine wave anymore.
Any ideas how I could optimize the callback so that it takes less time ?
Any other ideas ?
Does fmodf() cause a hardfault in stm32?
It is other code problems causing the hard fault here.
Failing to compile with ample warnings
Best code tip: enable all warnings. #KamilCuk
Faster feedback than Stackoverflow.
I'd expect something like below on a well enabled compiler.
return output;
warning: function returns address of local variable [-Wreturn-local-addr]
Returning a local Object
Cannot return a local array. Allocate instead.
// int output[sample_rate];
int *output = malloc(sizeof *output * sample_rate);
return output;
Calling code will need to free() the pointer.
Out of range array access
static int sample_rate = 100000;
int output[sample_rate];
// for(int i = 0; i <= sample_rate; i++){
for(int i = 0; i < sample_rate; i++){
...
output[i] = mix;
}
Stack overflow?
static int sample_rate = 100000; int output[sample_rate]; is a large local variable. Maybe allocate or try something smaller?
Advanced: loss of precision
A good fmodf() does not lose precision. For a more precise answer consider double math for the intermediate results. An even better approach is more involved.
float my_fmodf(float x, float y){
if(y == 0){
return 0;
}
double n = 1.0 * x / y;
return (float) (x - n * y);
}
Can I not use any function within another ?
Yes. Code has other issues.
1 value every 10uS makes only 100kSPS whis is not too much for this macro. In my designs I generate > 5MSPS signals without any problems. Usually I have one buffer and DMA in circular mode. First I fill the buffer and start generation. When the half transmition DMA interrupt is trigerred I fill the first half of the buffer with fresh data. The the transmition complete interrupt is trigerred I fill the second half and this process repeats all over again.

Writing a wave generator with SDL

I've coded a simple sequencer in C with SDL 1.2 and SDL_mixer(to play .wav file). It works well and I want to add some audio synthesis to this program. I've look up the and I found this sinewave code using SDL2(https://github.com/lundstroem/synth-samples-sdl2/blob/master/src/synth_samples_sdl2_2.c)
Here's how the sinewave is coded in the program:
static void build_sine_table(int16_t *data, int wave_length)
{
/*
Build sine table to use as oscillator:
Generate a 16bit signed integer sinewave table with 1024 samples.
This table will be used to produce the notes.
Different notes will be created by stepping through
the table at different intervals (phase).
*/
double phase_increment = (2.0f * pi) / (double)wave_length;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = (int)(sin(current_phase) * INT16_MAX);
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
static double get_pitch(double note) {
/*
Calculate pitch from note value.
offset note by 57 halfnotes to get correct pitch from the range we have chosen for the notes.
*/
double p = pow(chromatic_ratio, note - 57);
p *= 440;
return p;
}
static void audio_callback(void *unused, Uint8 *byte_stream, int byte_stream_length) {
/*
This function is called whenever the audio buffer needs to be filled to allow
for a continuous stream of audio.
Write samples to byteStream according to byteStreamLength.
The audio buffer is interleaved, meaning that both left and right channels exist in the same
buffer.
*/
// zero the buffer
memset(byte_stream, 0, byte_stream_length);
if(quit) {
return;
}
// cast buffer as 16bit signed int.
Sint16 *s_byte_stream = (Sint16*)byte_stream;
// buffer is interleaved, so get the length of 1 channel.
int remain = byte_stream_length / 2;
// split the rendering up in chunks to make it buffersize agnostic.
long chunk_size = 64;
int iterations = remain/chunk_size;
for(long i = 0; i < iterations; i++) {
long begin = i*chunk_size;
long end = (i*chunk_size) + chunk_size;
write_samples(s_byte_stream, begin, end, chunk_size);
}
}
static void write_samples(int16_t *s_byteStream, long begin, long end, long length) {
if(note > 0) {
double d_sample_rate = sample_rate;
double d_table_length = table_length;
double d_note = note;
/*
get correct phase increment for note depending on sample rate and table length.
*/
double phase_increment = (get_pitch(d_note) / d_sample_rate) * d_table_length;
/*
loop through the buffer and write samples.
*/
for (int i = 0; i < length; i+=2) {
phase_double += phase_increment;
phase_int = (int)phase_double;
if(phase_double >= table_length) {
double diff = phase_double - table_length;
phase_double = diff;
phase_int = (int)diff;
}
if(phase_int < table_length && phase_int > -1) {
if(s_byteStream != NULL) {
int16_t sample = sine_wave_table[phase_int];
sample *= 0.6; // scale volume.
s_byteStream[i+begin] = sample; // left channel
s_byteStream[i+begin+1] = sample; // right channel
}
}
}
}
}
I don't understand how I could change the sinewave formula to genrate other waveform like square/triangle/saw ect...
EDIT:
Because I forgot to explain it, here's what I tried.
I followed the example I've seen on this video series(https://www.youtube.com/watch?v=tgamhuQnOkM). The source code of the method provided by the video is on github, and the wave generation code is looking like this:
double w(double dHertz)
{
return dHertz * 2.0 * PI;
}
// General purpose oscillator
double osc(double dHertz, double dTime, int nType = OSC_SINE)
{
switch (nType)
{
case OSC_SINE: // Sine wave bewteen -1 and +1
return sin(w(dHertz) * dTime);
case OSC_SQUARE: // Square wave between -1 and +1
return sin(w(dHertz) * dTime) > 0 ? 1.0 : -1.0;
case OSC_TRIANGLE: // Triangle wave between -1 and +1
return asin(sin(w(dHertz) * dTime)) * (2.0 / PI);
}
Because the C++ code here uses windows soun api I could not copy/paste this method to make it work on the piece of code I've found using SDL2.
So I tried to this in order to obtain a square wave:
static void build_sine_table(int16_t *data, int wave_length)
{
double phase_increment = ((2.0f * pi) / (double)wave_length) > 0 ? 1.0 : -1.0;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = (int)(sin(current_phase) * INT16_MAX);
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
This didn't gave me a square wave but more a saw wave.
Here's what I tried to get a triangle wave:
static void build_sine_table(int16_t *data, int wave_length)
{
double phase_increment = (2.0f * pi) / (double)wave_length;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = (int)(asin(sin(current_phase) * INT16_MAX)) * (2 / pi);
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
This also gave me another type of waveform, not triangle.
You’d replace the sin function call with call to one of the following:
// this is a helper function only
double normalize(double phase)
{
double cycles = phase/(2.0*M_PI);
phase -= trunc(cycles) * 2.0 * M_PI;
if (phase < 0) phase += 2.0*M_PI;
return phase;
}
double square(double phase)
{ return (normalize(phase) < M_PI) ? 1.0 : -1.0; }
double sawtooth(double phase)
{ return -1.0 + normalize(phase) / M_PI; }
double triangle(double phase)
{
phase = normalize(phase);
if (phase >= M_PI)
phase = 2*M_PI - phase;
return -1.0 + 2.0 * phase / M_PI;
}
You’d be building tables just like you did for the sine, except they’d be the square, sawtooth and triangle tables, respectively.

arm_cfft_sR_q31_len4096 undeclared

I am doing some FFT calculations on a STM32F407 and I want to compare the different FFT functions that is available in the CMSIS DSP library. When I am using the f32 CFFT functions it works as one would expect but when I try to use the q31/q15 functions I get an error saying "arm_cfft_sR_q31_len4096" or arm_cfft_sR_q15_len4096 undeclared when I call their respective cfft functions. I have included arm_const_structs.h where it should be defined but apparently it isn't? It works with arm_cfft_sR_f32_len4096 for the f32 version of the functions so what might be the problem?
Here is how the f32 version of my fft calculations look:
#include "arm_const_structs.h"
float32_t fft_data[FFT_SIZE * 2];
uint16_t util_calculate_fft_value(uint16_t *buffer, uint32_t len, uint32_t fft_freq, uint32_t fft_freq2)
{
uint16_t i;
float32_t maxValue; // Max FFT value is stored here
uint32_t maxIndex; // Index in Output array where max value is
tmStartMeasurement(&time); // Record clock cycles
// Ensure in buffer is not longer than FFT buffer
if (len > FFT_SIZE)
len = FFT_SIZE;
// Convert buffer uint16 to fft input float32
for (i = 0; i < len ; i++)
{
fft_data[i*2] = (float32_t)buffer[i] / 2048.0 - 1.0; // Real part
fft_data[i*2 + 1] = 0; // Imaginary part
}
// Process the data through the CFFT module intFlag = 0, doBitReverse = 1
arm_cfft_f32(&arm_cfft_sR_f32_len4096, fft_data, 0, 1);
// Process the data through the Complex Magniture Module for calculating the magnitude at each bin
arm_cmplx_mag_f32(fft_data, fft_data, FFT_SIZE / 2);
// Find maxValue as max in fft_data
arm_max_f32(fft_data, FFT_SIZE, &maxValue, &maxIndex);
if (fft_freq == 0)
{ // Find maxValue as max in fft data
arm_max_f32(fft_data, FFT_SIZE, &maxValue, &maxIndex);
}
else
{ // Grab maxValue from fft data at freq position
arm_max_f32(&fft_data[fft_freq * FFT_SIZE / ADC_SAMP_SPEED - 1], 3, &maxValue, &maxIndex);
if (fft_freq2 != 0)
{
// Grab maxValue from fft data at freq2 position
float32_t maxValue2; // Max FFT value is stored here
uint32_t maxIndex2; // Index in Output array where max value is
arm_max_f32(&fft_data[fft_freq * FFT_SIZE / ADC_SAMP_SPEED - 1], 3, &maxValue2, &maxIndex2);
maxValue = (maxValue + maxValue2) / 2.0;
}
}
tmStopMeasurement(&time); // Get number of clock cycles
// Convert output back to uint16 for plotting
for (i = 0; i < len / 2; i++)
{
buffer[i] = (uint16_t)(fft_data[i] * 10.0);
}
// Zero the rest of the buffer
for (i = len / 2; i < len; i++)
{
buffer[i] = 0;
}
LOG_INFO("FFT number of cycles: %i\n", time.worst);
return ((uint16_t)(maxValue * 10.0));
}
I found a copy of arm_const_structs.h online. It includes the following line:
extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len4096;
The extern keyword means that this line is a declaration of arm_cfft_SR_q31_len4096, not a definition. The variable must also be defined somewhere else in your code.
I found the definition in arm_const_structs.c.
const arm_cfft_instance_q31 arm_cfft_sR_q31_len4096 = {
4096, twiddleCoef_4096_q31, armBitRevIndexTable_fixed_4096, ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH
};
Make sure you have included arm_const_structs.c in your project so that it compiles and links with your program.

CMSIS FIR bandpass filter

I am trying to implement a 60kHz bandpass filter on the STM32F407 microcontroller and I'm having some issues. I have generated the filter with the help of MATLABs fdatool and then simulated it in MATLAB as well. The following MATLAB script simlates it.
% FIR Window Bandpass filter designed using the FIR1 function.
% All frequency values are in Hz.
Fs = 5250000; % Sampling Frequency
N = 1800; % Order
Fc1 = 59950; % First Cutoff Frequency
Fc2 = 60050; % Second Cutoff Frequency
flag = 'scale'; % Sampling Flag
% Create the window vector for the design algorithm.
win = hamming(N+1);
% Calculate the coefficients using the FIR1 function.
b = fir1(N, [Fc1 Fc2]/(Fs/2), 'bandpass', win, flag);
Hd = dfilt.dffir(b);
%----------------------------------------------------------
%----------------------------------------------------------
T = 1 / Fs; % sample time
L = 4500; % Length of signal
t = (0:L-1)*T; % Time vector
% Animate the passband frequency span
for f=55500:50:63500
signal = sin(2*pi*f*t);
plot(filter(Hd, signal));
axis([0 L -1 1]);
str=sprintf('Signal frequency (Hz) %d', f);
title(str);
drawnow;
end
pause;
close all;
signal = sin(2*pi*50000*t) + sin(2*pi*60000*t) + sin(2*pi*78000*t);
signal = signal / 3;
signal = signal(1:1:4500);
filterInput = signal;
filterOutput = filter(Hd,signal);
subplot(2,1,1);
plot(filterInput);
axis([0 4500 -1 1]);
subplot(2,1,2);
plot(filterOutput)
axis([0 4500 -1 1]);
pause;
close all;
From the fdatool I extract the filter co-efficents to 16-bit unsigned integers in q15 format, this because of the 12-bit ADC that I'm using. The filter co-efficents header that is generated by MATLAB is here and the resulting plot of the co-efficents can be seen in the following picture
Below is the code for the filter implementation which obviously isn't working and I don't really know what I can do differently, I've looked at some examples online Example 1 and Example 2
#include "fdacoefs.h"
#define FILTER_SAMPLES 4500
#define BLOCK_SIZE 900
static uint16_t firInput[FILTER_SAMPLES];
static uint16_t firOutput[FILTER_SAMPLES];
static uint16_t firState[NUM_TAPS + BLOCK_SIZE - 1];
uint16_t util_calculate_filter(uint16_t *buffer, uint32_t len)
{
uint16_t i;
uint16_t max;
uint16_t min;
uint32_t index;
// Create filter instance
arm_fir_instance_q15 instance;
// Ensure that the buffer length isn't longer than the sample size
if (len > FILTER_SAMPLES)
len = FILTER_SAMPLES;
for (i = 0; i < len ; i++)
{
firInput[i] = buffer[i];
}
// Call Initialization function for the filter
arm_fir_init_q15(&instance, NUM_TAPS, &firCoeffs, &firState, BLOCK_SIZE);
// Call the FIR process function, num of blocks to process = (FILTER_SAMPLES / BLOCK_SIZE)
for (i = 0; i < (FILTER_SAMPLES / BLOCK_SIZE); i++) //
{
// BLOCK_SIZE = samples to process per call
arm_fir_q15(&instance, &firInput[i * BLOCK_SIZE], &firOutput[i * BLOCK_SIZE], BLOCK_SIZE);
}
arm_max_q15(&firOutput, len, &max, &index);
arm_min_q15(&firOutput, len, &min, &index);
// Convert output back to uint16 for plotting
for (i = 0; i < (len); i++)
{
buffer[i] = (uint16_t)(firOutput[i] - 30967);
}
return (uint16_t)((max+min));
}
The ADC is sampling at 5.25 MSPS and it is sampling a 60kHz signal 4500 times and here you can see the Input to the filter and then the Output of the filter which is pretty weird..
Is there anything obvious that I've missed? Because I'm completely lost and any pointers and tips are helpful!
As Lundin pointed out I changed it to work with 32 bit integers instead and that actually solved my problem. Ofcourse I generated new filter co-efficents with MATLABS fdatool as signed 32 bit integers instead.
static signed int firInput[FILTER_SAMPLES];
static signed int firOutput[FILTER_SAMPLES];
static signed int firState[NUM_TAPS + BLOCK_SIZE -1];
uint16_t util_calculate_filter(uint16_t *buffer, uint32_t len)
{
uint16_t i;
int power;
uint32_t index;
// Create filter instance
arm_fir_instance_q31 instance;
// Ensure that the buffer length isn't longer than the sample size
if (len > FILTER_SAMPLES)
len = FILTER_SAMPLES;
for (i = 0; i < len ; i++)
{
firInput[i] = (int)buffer[i];
}
// Call Initialization function for the filter
arm_fir_init_q31(&instance, NUM_TAPS, &firCoeffs, &firState, BLOCK_SIZE);
// Call the FIR process function, num of blocks to process = (FILTER_SAMPLES / BLOCK_SIZE)
for (i = 0; i < (FILTER_SAMPLES / BLOCK_SIZE); i++) //
{
// BLOCK_SIZE = samples to process per call
//arm_fir_q31(&instance, &firInput[i * BLOCK_SIZE], &firOutput[i * BLOCK_SIZE], BLOCK_SIZE);
arm_fir_q31(&instance, &firInput[i * BLOCK_SIZE], &firOutput[i * BLOCK_SIZE], BLOCK_SIZE);
}
arm_power_q31(&firOutput, len, &power);
// Convert output back to uint16 for plotting
for (i = 0; i < (len); i++)
{
buffer[i] = (uint16_t)(firOutput[i] - 63500);
}
return (uint16_t)((power/10));
}

Audio resampling bug

I'm trying to construct and use a basic audio resampler, taking WAVE file as input, resampling it to defined fs and returning resampled file. The code runs, but it generates inaudible output file that cannot be opened by Windows Media Player. I honestly don't know what causes this.
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "wavfile.h"
#define FRAME_SIZE 256
void resample(float *in, float *out, int inputFrameSize, int outputFrameSize, float inputFs, float outputFs, float *index, float firstsampleofthenextframe)
{
float increment = inputFs/outputFs;
int i;
float frac;
int intp;
for (i=0;i<outputFrameSize;i++)
{
if ((*index) < inputFrameSize - 1) {
//linear interpolation
intp = (int)(*index);
frac = (*index) - (float)intp;
out[i] = frac * in[intp+1] + (1.0-frac) * in[intp];
} else { //index between inputFrameSize-2 and inputFrameSize-1
intp = (int)(*index);
frac = (*index) - (float)intp;
out[i] = frac * firstsampleofthenextframe + (1.0-frac) * in[intp];
}
//left_out[i] = 0.2*sin(0.001*3.141592635*(*index));
(*index) += increment;
}
}
int main()
{
struct wavfile_header readh;
struct wavfile_header writeh;
float left_in[FRAME_SIZE];
float right_in[FRAME_SIZE];
float left_out[FRAME_SIZE*2];
float right_out[FRAME_SIZE*2];
//float *left_out;
//float *right_out;
float indexL, indexR; //sample index used for interpolation
FILE * infile;
FILE * outfile;
fpos_t position;
int numBytes, numFrames;
int i;
int outFrameSize;
readh.num_channels = 2;
readh.sample_rate = 44100;
writeh.num_channels = 2;
writeh.sample_rate = 48000;
outFrameSize = (int)((float)FRAME_SIZE * (float)(writeh.sample_rate) / (float)(readh.sample_rate));
//left_out = (float*) malloc(sizeof(float)*outFrameSize);
//right_out = (float*) malloc(sizeof(float)*outFrameSize);
infile = wavfile_open_read("input.wav", &readh);
if(!infile) {
printf("read error");
return 1;
}
outfile = wavfile_open_write("output.wav", &writeh);
if(!outfile) {
printf("write error");
return 1;
}
//main processing loop
numBytes = readh.riff_length;
numFrames = numBytes / (2*FRAME_SIZE);
indexL = indexR = 0.f; //start from the 0th sample of the input signal
while(numFrames > 0)
{
float left_overlap, right_overlap;
short sl, sr;
for(i=0;i<FRAME_SIZE;i++) //read frame
{
short sleft, sright;
fread(&sleft ,sizeof(short),1, infile); //deinterleave samples
fread(&sright ,sizeof(short),1, infile);
fflush(infile);
left_in[i] = sleft * 0.0000305185f; // * (1/32767) - 16-bit input automatically normalized to (-1, 1)
right_in[i] = sright * 0.0000305185f; // * (1/32767) - 16-bit input automatically normalized to (-1, 1)
numBytes -= 4;
}
fgetpos (infile, &position);
//one sample ahead is needed to interpolate
fread(&sl ,sizeof(short),1, infile); //deinterleave samples [warning: bug if the input file length is an exact number of frames]
fread(&sr ,sizeof(short),1, infile);
left_overlap = sl * 0.0000305185f; // * (1/32767) - 16-bit input automatically normalized to (-1, 1)
right_overlap = sr * 0.0000305185f; // * (1/32767) - 16-bit input automatically normalized to (-1, 1)
fsetpos (infile, &position);
resample(left_in, left_out, FRAME_SIZE, outFrameSize, 44100, 48000, &indexL, left_overlap);
resample(right_in, right_out, FRAME_SIZE, outFrameSize, 44100, 48000, &indexR, right_overlap);
indexL -= (int)indexL;
indexR -= (int)indexR;
for(i=0;i<outFrameSize;i++) //read frame
{
short left, right;
//left_out[i] = 0.5;
//right_out[i] = 0.5;
left = (short)(left_out[i] * 32767.f);
right = (short)(right_out[i] * 32767.f);
fwrite(&left ,sizeof(short),1, outfile); //deinterleave samples
fwrite(&right ,sizeof(short),1, outfile);
numBytes -= 4;
}
fflush(outfile);
numFrames--;
}
/*
while(numBytes > 0)
{
//below: temp
short left, right;
//left_out[i] = 0.5;
//right_out[i] = 0.5;
left = left_out[i] * 32767;
right = right_out[i] * 32767;
fwrite(&left ,sizeof(short),1, outfile); //deinterleave samples
fwrite(&right ,sizeof(short),1, outfile);
numBytes--;
}
*/
fflush(outfile);
//free(left_out);
//free(right_out);
fclose(outfile);
fclose(infile);
return 0;
}

Resources