General additive synthesis program - c

I'm attempting to write a general additive synthesis c program that will generate a complex sinusoid created from a sequence of pure sine waves of arbitrary frequency following a single envelope.
The input file will be something of this nature
F0 P0 // a list of up to 100 floats indicating frequencies and
F1 P1 // % contribution of this frequency to the sound
F2 P2
....
-1 // sentinal value to indicate end of frequency list
B0 A0 // first breakpoint
B1 A1
... // There can be an arbitary number of breakpoints
I want my program to generate a WAV file up to the last breakpoint where all the sine waves will be generated at the given frequencies, scaled to the % contribution as listed, and added together to make the final sound
I've attempted to try some of the c programming out but I'm not naturally a C programming so this is what I've done so far:
#include <stdio.h>
#include <stdlib.h>
#include <portsf.h>
#include <math.h>
#ifndef M_PI
#define M_PI (3.141592654)
#endif
// Additive Synthesis
PSF_PROPS props;
props.srate = 44100;
props.chans = 2;
props.samptype = PSF_SAMP_IEEE_FLOAT;
props.format = PSF_STDWAVE;
props.chformat = STDWAVE;
float* frame;
int sampleNumber;
double angleIncrement;
double frequency;
double sampleRate;
int i;
int twopi = 2.0 * M_PI;
ofd = psf_sndCreate(argv[2],&props,0,0,PSF_CREATE_RDWR);
int main (int argc, char* argv[]) {
sampleNumber = 0;
angleIncrement = twopi * frequency/sampleRate;
while (i < sampleNumber) {
frame[0] = frame[1] = sin(sampleNumber * angleIncrement);
sampleNumber++;
if (fwrite(&sampleout,sizeof(float),1,rawfile) !=1) {
printf("Error writing to output file.\n");
return 1;
}
if (i <=1000)
fprintf(bpfile, "%ld\t%f\n", i, frame);
phase += angleIncrement;
if (phase >= twopi)
phase -= twopi;
}
phase_offset = -1 * PI / 2;
sample_buffer = (float*) malloc(samples * sizeof(float));
// sample_buffer set back to 0
memset(sample_buffer, 0, sizeof(float)*sampleNumber);
// go through the number of harmonics
for (i = 1; i <= NHARMS; i++) {
amp = 1.0 / i;
// go through number of sinusoid components
for (n = 0; n < sampleNumber; n++) {
sample_buffer[n] += amp * cos(i * angleIncrement * n + phase_offset);
}
}
}
However, I'm not really sure if I'm doing this right at all. Any ideas on how I can fix this and proceed?

As I've started noticing more and more little details, the comments start getting annoying to read (but make sure you still answer my compiling Hello World question), so I've coalesced them into this answer and will update it as I see more:
as of right now, frequency is used only once, and at that time is only 0.0 ever. is that what you want?
i is initalized to 0, and sampleNumber is also started at 0. Thus the while condition (i < sampleNumber) will never execute
i is never incremented yet you have a if ( i <= 1000 ) condition, which will thus always evaluate true
sampleRate is initialized to 0.0, and the first operation on it is to divide by it, which, obviously, is not happy times
Obviously pedantic, but the n in the bottom most for loop is never declared ( int n; for (n = ....) as an example.
in your sample_buffer = (float*) malloc(samples * sizeof(float)); line, samples should most probably be sampleNumber, right? samples is never defined

I agree with AKA4749, and have one more thing you should do: clip the samples when you add them or you get distortion:
if ( sample[i] > 1 ) sample[i] = 1;
else if ( sample[i] < -1 ) sample[i] = -1;

Related

C Code compiles but does not ask for user input

I'm a beginner in C doing the harvard cs50x course and this code was written a lot of help but I still can't seem to figure out why it compiles on vscode but does not ask for text from the user.
The prompt is on writing a code that can estimate the difficulty of a text according to grade level based on a given equation that I have included below. Some of the comments include my previous mistakes so they are not descriptions that I purposely added.
#include <cs50.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
// Declare variables to count letters, words, and sentences
// Check if character is an alphabet
// letter++
// If character is not an alphabet or punctuation
// word++
// If character is punctuation
// sentence++
int count_letter = 0;
int count_word = 1;
int count_sentence = 0;
string text = get_string("What is your chosen text?\n");
int text_length = strlen(text);
for(int i = 0; i < text_length; i++) // Run through the length of the text?
{
if(isalpha(text[i])) // check if alphabet
{
count_letter++; // count the number of letters in the text
}
}
for(int i = 0; i < text_length; i++)
{
if(isspace(text[i])) {
count_word++;
}
}
for(int i = 0; i < text_length; i++)
{
// if(text[i]=="." || text[i]=="?" || text[i]=="!")
if(text[i] == '.' || text[i] == '?' || text[i] == '!') {
count_sentence++;
}
}
// L = letters/word*100, S = sentence/word*100
int L = count_letter / count_word * 100;
int S = count_sentence / count_word * 100;
// index = 0.0588 * L - 0.296 * S - 15.8
int index = round(0.0588 * L - 0.296 * S - 15.8);
printf("%d\n", index);
}
In
// L = letters/word*100, S = sentence/word*100
int L = count_letter / count_word * 100;
int S = count_sentence / count_word * 100;
you are doing integer arithmetic, so you are dividing the number of letters by the number of words (the numer of letters is supposed to be larger) but the division is done as integers, so once multiplying it by 100 you will get no decimals (you will get a number ended in two final 00 digits)
you have a chance here, is to multiply first by 100, and then you will get the closest result as an integer and you will get decimals.
Another chance is to convert the numbers to floating point as soon as possible, as in:
// L = letters/word*100, S = sentence/word*100
/* using a floating point literal forces the calculation to be done as real numbers */
int L = 100.0 * count_letter / count_word;
int S = 100.0 * count_sentence / count_word;
or even better (get and use double floating point numbers all the time) after calculations:
// L = letters/word*100, S = sentence/word*100
/* using a floating point number forces the calculation to be done as real numbers */
double L = 100.0 * count_letter / count_word;
double S = 100.0 * count_sentence / count_word;
The data numbers continue to be integers, but as the first operation consists of multiplying a real number (the 100.0 floating point literal) the first product is calculating as floating point (the second argument, the first variable in the expression, is converted to double before doing the calculation, and the third, are both converted to double before doing the calculations) resulting in double precision values.
So finally you can show the result as a floating point number with:
// index = 0.0588 * L - 0.296 * S - 15.8
/* everything is calculated as floating point numbers (L and S have been defined as double above */
double index = round(0.0588 * L - 0.296 * S - 15.8);
/* the formatting specifier must be g below, once index is floating point */
printf("%g\n", index);

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.

I'm designing a guitar tuner through ATmega16p and CodeVisionAVR and i just can't get my code to run

I'm designing a guitar tuner through an atmel mega16 processor and CodeVisionAVR for my university's second project. I have connected a mono jack to the processor's PINA.7 (ADC converter) and GND. I have 7 LEDs (PORTB.0..6) that should turn on through a series of if/elseif based on the frequency of the fundamental of the signal.
I'm taking the fundamental of the signal through a DFT (i know there are faster FTs but our university told us we should use a DFT, they know why) of 800 samples. Out of the 800 samples selected, it calculates the frequency spectrum. Then the next for is used to calculate the absolute value of each frequency, and picks the largest, so it can be a good refrence point for a guitar tuner.
Momentairly, i have included in the main function just a large frequency condition to see if the LED lights up, but it doesn't.
I have tried switching on LEDs from 0 to 6 throughout the code and it seems to stop at F = computeDft();, so i removed the variable, and just let the computeDft(); run, but the next leds did not light up. Is the function never getting called? I have tried the function in Visual Studio with a generated cosine function and it works perfectly. It always detects the fundamental. Why doesn't it work in CVAVR?
#define M_PI 3.1415926f
#define N 800
unsigned char read_adc(void)
{
ADCSRA |= 0x40; //start conversion;
while (ADCSRA&(0x40)); //wait conversion end
return (float)ADCH;
}
typedef struct
{
float re;
float im;
} Complex;
float computeDft()
{
unsigned char x[N] = {0};
float max = 0;
float maxi = 0;
float magnitude = 0;
Complex X1[N] = {0};
int n = N;
int k;
for (n = 0; n < N; ++n)
{
for (k = 0; k < n; k++)
{
x[k] = read_adc();
X1[n].re += x[k] * cos(n * k * M_PI / N);
X1[n].im -= x[k] * sin(n * k * M_PI / N);
}
}
for (k = 0; k < n; k++)
{
magnitude = sqrt(X1[k].re * X1[k].re + X1[k].im * X1[k].im);
if (magnitude > maxi)
{
maxi = magnitude;
max = k;
}
}
return max;
}
/*
* main function of program
*/
void main (void)
{
float F = 0;
Init_initController(); // this must be the first "init" action/call!
#asm("sei") // enable interrupts
LED1 = 1; // initial state, will be changed by timer 1
L0 = 0;
L1 = 0;
L2 = 0;
L3 = 0;
L4 = 0;
L5 = 0;
L6 = 0;
ADMUX = 0b10100111; // set ADC0
ADCSRA = 0b10000111; //set ADEN, precale by 128
while(TRUE)
{
wdogtrig(); // call often else processor will reset ;
F = computeDft();
if (F > 50 && F < 200)
{
L3 = 1;
}
}
}// end main loop
The result i'm trying to achieve is a signal from a phone or a computer (probably a YouTube video of a guy tuning his guitar) is sent through the jack to the processor in the AD converter (PINA.7). The main function calls the computeDft; function, which will ask the read_adc(); to add to x[k] the value of the voltage that is being sent through the cable, then compute it's Dft. The same function then selects the frequency of the fundamental (the one with the highest absolute value), then returns it. Inside the main function, a variable will be assigned the value of the fundamental, and through a series of ifs, it will compare it's value to the standard guitar strings frequencies of 82.6, 110, etc...
1. First of all: just picking the bigger harmonic in DFT, is not good as a tuner, since, depending on the instrument played, overtones may have a larger amplitude. The decent tuner may be done by using e.g. auto-correlation algorithm.
2. I see this line in your project:
wdogtrig(); // call often else processor will reset ;
Why you need the watchdog in the first place? Where it is configured? What timeout it is set for? How you think, how long would it take to perform both nested loops in computeDft()? With a lot of floating point operations inside including calculation of sine and cosine at each step? On a 16MHz 8-bit MCU? I think that will take several seconds at least, so do not use the watchdog at all, or reset it more often.
3. Look at
cos(n * k * M_PI / N);
(by the way, are you sure it is cos(n * k * M_PI / N); not cos(n * k * 2 * M_PI / N);?)
since cos(x) = cos(x + 2 * M_PI), you can see this formula can be expressed as cos((n * k * 2) % (2 * N) * M_PI / N). I.e. you can precalculate all 2*N possible values and put them as a constant table into the flash memory.
4. Look at nested loops in computeDft()
Inside the inner loop, you are calling read_adc() each time!
You want to pick the signal into the buffer once, and then perform DFT over the saved signal. I.e. first you read ADC values into x[k] array:
for (k = 0; k < N; k++)
{
x[k] = read_adc();
}
and only then you perform DFT calculations over it:
for (n = 0; n < N; ++n)
{
for (k = 0; k < n; k++)
{
X1[n].re += x[k] * cos(n * k * M_PI / N);
X1[n].im -= x[k] * sin(n * k * M_PI / N);
}
}
5. Look carefully at two cycles:
for (n = 0; n < N; ++n)
..
X1[n].re += x[k] * cos(n * k * M_PI / N);
X1[n].im -= x[k] * sin(n * k * M_PI / N);
}
Here at each step, you are calculating the value of X1[n], none of the previous X1 values are used.
And another loop below:
for (k = 0; k < n; k++)
{
magnitude = sqrt(X1[k].re * X1[k].re + X1[k].im * X1[k].im);
...
}
here you are calculating the magnitude of X1[k] and no previous of next values of X1 are used. So, you can simply combine them together:
for (n = 0; n < N; ++n)
{
for (k = 0; k < n; k++)
{
X1[n].re += x[k] * cos(n * k * M_PI / N);
X1[n].im -= x[k] * sin(n * k * M_PI / N);
}
magnitude = sqrt(X1[n].re * X1[n].re + X1[n].im * X1[n].im);
if (magnitude > maxi)
{
maxi = magnitude;
max = k;
}
}
Here you can clearly see, you need no reason to store X1[n].re and X1[n].im in any array. Just get rid of them!
for (n = 0; n < N; ++n)
{
float re = 0;
float im = 0;
for (k = 0; k < n; k++)
{
re += x[k] * cos(n * k * M_PI / N);
im -= x[k] * sin(n * k * M_PI / N);
}
magnitude = sqrt(re * re + im * im);
if (magnitude > maxi)
{
maxi = magnitude;
max = k;
}
}
That's all! You have saved 6 KB by removing pointless Complex X1[N] array
6. There is a error in your initialization code:
ADMUX = 0b10100111; // set ADC0
I don't know what is "ATmega16P", I assume it works the same as "ATmega16". So most significant bits of this register, called REFS1 and REFS0 are used to select the reference voltage. Possible values are:
00 - external voltage from AREF pin;
01 - AVCC voltage taken as reference
11 - internal regulator (2.56V for ATmega16, 1.1V for ATmega168PA)
10 is an incorrect value.
7. the guitar output is a small signal, maybe several dozens of millivolts. Also, it is an AC signal, which can be as positive, so negative as well. So, before putting the signal onto MCU's input you have to shift it (otherwise you'll see only the positive half wave) and amplify it.
I.e. it is not enough just to connect jack plug to GND and ADC input, you need some schematics which will make the signal of the appropriate level.
You can google for it. For example this:
(from This project)

Implementing equations with very small numbers in C - Plank's Law generating blackbody

I have a problem that, after much head scratching, I think is to do with very small numbers in a long-double.
I am trying to implement Planck's law equation to generate a normalised blackbody curve at 1nm intervals between a given wavelength range and for a given temperature. Ultimately this will be a function accepting inputs, for now it is main() with the variables fixed and outputting by printf().
I see examples in matlab and python, and they are implementing the same equation as me in a similar loop with no trouble at all.
This is the equation:
My code generates an incorrect blackbody curve:
I have tested key parts of the code independently. After trying to test the equation by breaking it into blocks in excel I noticed that it does result in very small numbers and I wonder if my implementation of large numbers could be causing the issue? Does anyone have any insight into using C to implement equations? This a new area to me and I have found the maths much harder to implement and debug than normal code.
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
//global variables
const double H = 6.626070040e-34; //Planck's constant (Joule-seconds)
const double C = 299800000; //Speed of light in vacume (meters per second)
const double K = 1.3806488e-23; //Boltzmann's constant (Joules per Kelvin)
const double nm_to_m = 1e-6; //conversion between nm and m
const int interval = 1; //wavelength interval to caculate at (nm)
//typedef structure to hold results
typedef struct {
int *wavelength;
long double *radiance;
long double *normalised;
} results;
int main() {
int min = 100 , max = 3000; //wavelength bounds to caculate between, later to be swaped to function inputs
double temprature = 200; //temprature in kelvin, later to be swaped to function input
double new_valu, old_valu = 0;
static results SPD_data, *SPD; //setup a static results structure and a pointer to point to it
SPD = &SPD_data;
SPD->wavelength = malloc(sizeof(int) * (max - min)); //allocate memory based on wavelength bounds
SPD->radiance = malloc(sizeof(long double) * (max - min));
SPD->normalised = malloc(sizeof(long double) * (max - min));
for (int i = 0; i <= (max - min); i++) {
//Fill wavelength vector
SPD->wavelength[i] = min + (interval * i);
//Computes radiance for every wavelength of blackbody of given temprature
SPD->radiance[i] = ((2 * H * pow(C, 2)) / (pow((SPD->wavelength[i] / nm_to_m), 5))) * (1 / (exp((H * C) / ((SPD->wavelength[i] / nm_to_m) * K * temprature))-1));
//Copy SPD->radiance to SPD->normalised
SPD->normalised[i] = SPD->radiance[i];
//Find largest value
if (i <= 0) {
old_valu = SPD->normalised[0];
} else if (i > 0){
new_valu = SPD->normalised[i];
if (new_valu > old_valu) {
old_valu = new_valu;
}
}
}
//for debug perposes
printf("wavelength(nm) radiance(Watts per steradian per meter squared) normalised radiance\n");
for (int i = 0; i <= (max - min); i++) {
//Normalise SPD
SPD->normalised[i] = SPD->normalised[i] / old_valu;
//for debug perposes
printf("%d %Le %Lf\n", SPD->wavelength[i], SPD->radiance[i], SPD->normalised[i]);
}
return 0; //later to be swaped to 'return SPD';
}
/*********************UPDATE Friday 24th Mar 2017 23:42*************************/
Thank you for the suggestions so far, lots of useful pointers especially understanding the way numbers are stored in C (IEEE 754) but I don't think that is the issue here as it only applies to significant digits. I implemented most of the suggestions but still no progress on the problem. I suspect Alexander in the comments is probably right, changing the units and order of operations is likely what I need to do to make the equation work like the matlab or python examples, but my knowledge of maths is not good enough to do this. I broke the equation down into chunks to take a closer look at what it was doing.
//global variables
const double H = 6.6260700e-34; //Planck's constant (Joule-seconds) 6.626070040e-34
const double C = 299792458; //Speed of light in vacume (meters per second)
const double K = 1.3806488e-23; //Boltzmann's constant (Joules per Kelvin) 1.3806488e-23
const double nm_to_m = 1e-9; //conversion between nm and m
const int interval = 1; //wavelength interval to caculate at (nm)
const int min = 100, max = 3000; //max and min wavelengths to caculate between (nm)
const double temprature = 200; //temprature (K)
//typedef structure to hold results
typedef struct {
int *wavelength;
long double *radiance;
long double *normalised;
} results;
//main program
int main()
{
//setup a static results structure and a pointer to point to it
static results SPD_data, *SPD;
SPD = &SPD_data;
//allocate memory based on wavelength bounds
SPD->wavelength = malloc(sizeof(int) * (max - min));
SPD->radiance = malloc(sizeof(long double) * (max - min));
SPD->normalised = malloc(sizeof(long double) * (max - min));
//break equasion into visible parts for debuging
long double aa, bb, cc, dd, ee, ff, gg, hh, ii, jj, kk, ll, mm, nn, oo;
for (int i = 0; i < (max - min); i++) {
//Computes radiance at every wavelength interval for blackbody of given temprature
SPD->wavelength[i] = min + (interval * i);
aa = 2 * H;
bb = pow(C, 2);
cc = aa * bb;
dd = pow((SPD->wavelength[i] / nm_to_m), 5);
ee = cc / dd;
ff = 1;
gg = H * C;
hh = SPD->wavelength[i] / nm_to_m;
ii = K * temprature;
jj = hh * ii;
kk = gg / jj;
ll = exp(kk);
mm = ll - 1;
nn = ff / mm;
oo = ee * nn;
SPD->radiance[i] = oo;
}
//for debug perposes
printf("wavelength(nm) | radiance(Watts per steradian per meter squared)\n");
for (int i = 0; i < (max - min); i++) {
printf("%d %Le\n", SPD->wavelength[i], SPD->radiance[i]);
}
return 0;
}
Equation variable values during runtime in xcode:
I notice a couple of things that are wrong and/or suspicious about the current state of your program:
You have defined nm_to_m as 10-9,, yet you divide by it. If your wavelength is measured in nanometers, you should multiply it by 10-9 to get it in meters. To wit, if hh is supposed to be your wavelength in meters, it is on the order of several light-hours.
The same is obviously true for dd as well.
mm, being the exponential expression minus 1, is zero, which gives you infinity in the results deriving from it. This is apparently because you don't have enough digits in a double to represent the significant part of the exponential. Instead of using exp(...) - 1 here, try using the expm1() function instead, which implements a well-defined algorithm for calculating exponentials minus 1 without cancellation errors.
Since interval is 1, it doesn't currently matter, but you can probably see that your results wouldn't match the meaning of the code if you set interval to something else.
Unless you plan to change something about this in the future, there shouldn't be a need for this program to "save" the values of all calculations. You could just print them out as you run them.
On the other hand, you don't seem to be in any danger of underflow or overflow. The largest and smallest numbers you use don't seem to be a far way from 10±60, which is well within what ordinary doubles can deal with, let alone long doubles. The being said, it might not hurt to use more normalized units, but at the magnitudes you currently display, I wouldn't worry about it.
Thanks for all the pointers in the comments. For anyone else running into a similar problem with implementing equations in C, I had a few silly errors in the code:
writing a 6 not a 9
dividing when I should be multiplying
an off by one error with the size of my array vs the iterations of for() loop
200 when I meant 2000 in the temperature variable
As a result of the last one particularly I was not getting the results I expected (my wavelength range was not right for plotting the temperature I was calculating) and this was leading me to the assumption that something was wrong in the implementation of the equation, specifically I was thinking about big/small numbers in C because I did not understand them. This was not the case.
In summary, I should have made sure I knew exactly what my equation should be outputting for given test conditions before implementing it in code. I will work on getting more comfortable with maths, particularly algebra and dimensional analysis.
Below is the working code, implemented as a function, feel free to use it for anything but obviously no warranty of any kind etc.
blackbody.c
//
// Computes radiance for every wavelength of blackbody of given temprature
//
// INPUTS: int min wavelength to begin calculation from (nm), int max wavelength to end calculation at (nm), int temperature (kelvin)
// OUTPUTS: pointer to structure containing:
// - spectral radiance (Watts per steradian per meter squared per wavelength at 1nm intervals)
// - normalised radiance
//
//include & define
#include "blackbody.h"
//global variables
const double H = 6.626070040e-34; //Planck's constant (Joule-seconds) 6.626070040e-34
const double C = 299792458; //Speed of light in vacuum (meters per second)
const double K = 1.3806488e-23; //Boltzmann's constant (Joules per Kelvin) 1.3806488e-23
const double nm_to_m = 1e-9; //conversion between nm and m
const int interval = 1; //wavelength interval to calculate at (nm), to change this line 45 also need to be changed
bbresults* blackbody(int min, int max, double temperature) {
double new_valu, old_valu = 0; //variables for normalising result
bbresults *SPD;
SPD = malloc(sizeof(bbresults));
//allocate memory based on wavelength bounds
SPD->wavelength = malloc(sizeof(int) * (max - min));
SPD->radiance = malloc(sizeof(long double) * (max - min));
SPD->normalised = malloc(sizeof(long double) * (max - min));
for (int i = 0; i < (max - min); i++) {
//Computes radiance for every wavelength of blackbody of given temperature
SPD->wavelength[i] = min + (interval * i);
SPD->radiance[i] = ((2 * H * pow(C, 2)) / (pow((SPD->wavelength[i] * nm_to_m), 5))) * (1 / (expm1((H * C) / ((SPD->wavelength[i] * nm_to_m) * K * temperature))));
//Copy SPD->radiance to SPD->normalised
SPD->normalised[i] = SPD->radiance[i];
//Find largest value
if (i <= 0) {
old_valu = SPD->normalised[0];
} else if (i > 0){
new_valu = SPD->normalised[i];
if (new_valu > old_valu) {
old_valu = new_valu;
}
}
}
for (int i = 0; i < (max - min); i++) {
//Normalise SPD
SPD->normalised[i] = SPD->normalised[i] / old_valu;
}
return SPD;
}
blackbody.h
#ifndef blackbody_h
#define blackbody_h
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
//typedef structure to hold results
typedef struct {
int *wavelength;
long double *radiance;
long double *normalised;
} bbresults;
//function declarations
bbresults* blackbody(int, int, double);
#endif /* blackbody_h */
main.c
#include <stdio.h>
#include "blackbody.h"
int main() {
bbresults *TEST;
int min = 100, max = 3000, temp = 5000;
TEST = blackbody(min, max, temp);
printf("wavelength | normalised radiance | radiance |\n");
printf(" (nm) | - | (W per meter squr per steradian) |\n");
for (int i = 0; i < (max - min); i++) {
printf("%4d %Lf %Le\n", TEST->wavelength[i], TEST->normalised[i], TEST->radiance[i]);
}
free(TEST);
free(TEST->wavelength);
free(TEST->radiance);
free(TEST->normalised);
return 0;
}
Plot of output:

How to use step size to find the tone frequency in sine table? STM32

I'm trying to use the Sine Table lookup method to find the tone frequency at different step size, but when I'm converting the floating point to integer and use the oscicopte to view the frequncy, it can't display any things on screen.
Does anyone know what's the solution for this issues. Any help is apperaite.
Below is the code:
// use the formula: StepSize = 360/(Fs/f) Where Fs is the Sample frequency 44.1 kHz and f is the tone frequency.
// example: StepSize = 360/(44100/440) = 3.576, since the STM32 doesn't support the floating point, therefore, we have to use the fixed-point format which multiply it by 1000 to be 3575
int StepSize = 3575;
unsigned int v=0;
signed int sine_table[91] = {
0x800,0x823,0x847,0x86b,
0x88e,0x8b2,0x8d6,0x8f9,
0x91d,0x940,0x963,0x986,
0x9a9,0x9cc,0x9ef,0xa12,
0xa34,0xa56,0xa78,0xa9a,
0xabc,0xadd,0xaff,0xb20,
0xb40,0xb61,0xb81,0xba1,
0xbc1,0xbe0,0xc00,0xc1e,
0xc3d,0xc5b,0xc79,0xc96,
0xcb3,0xcd0,0xcec,0xd08,
0xd24,0xd3f,0xd5a,0xd74,
0xd8e,0xda8,0xdc1,0xdd9,
0xdf1,0xe09,0xe20,0xe37,
0xe4d,0xe63,0xe78,0xe8d,
0xea1,0xeb5,0xec8,0xedb,
0xeed,0xeff,0xf10,0xf20,
0xf30,0xf40,0xf4e,0xf5d,
0xf6a,0xf77,0xf84,0xf90,
0xf9b,0xfa6,0xfb0,0xfba,
0xfc3,0xfcb,0xfd3,0xfda,
0xfe0,0xfe6,0xfec,0xff0,
0xff4,0xff8,0xffb,0xffd,
0xffe,0xfff,0xfff};
unsigned int sin(int x){
x = x % 360;
if(x <= 90)
return sine_table[x];
else if ( x <= 180){
return sine_table[180 - x];
}else if ( x <= 270){
return 4096 - sine_table[x - 180];
}else{
return 4096 - sine_table[360 - x];
}
}
void main(void)
{
while(1){
v+=StepSize; // Don't know why it doesn't work in this way. not display anything on screen.
DAC->DHR12R2 = sin(v/1000); // DAC channel-2 12-bit Right aligned data
if (v >= 360) v = 0;
}
}
But, if I change the StepSize = 3; it shows the frequency:
There are a few issues with your code. But I will start with the one that you asked about.
int StepSize = 3575;
unsigned int v=0;
while(1){
v+=StepSize;
DAC->DHR12R2 = sin(v/1000);
if (v >= 360) v = 0;
}
The reason why this code doesn't work is that v is always set to 0 at the end of the loop because 3575 is greater than 360. So then you always call sin(3) because 3575/1000 is 3 in integer division.
Perhaps, you should rewrite your last line as if ((v/1000) >= 360) v = 0;. Otherwise, I would rewrite your loop like this
while(1){
v+=StepSize;
v/=1000;
DAC->DHR12R2 = sin(v);
if (v >= 360) v = 0;
}
I would also recommend that you declare your lookup table a const. So it would look like
const signed int sine_table[91] = {
Last recommendation is to choose another name for your sin function so as not to confuse with the sin library function. Even though in this case there shouldn't be a problem.

Resources