20Log(x/y) - Converting (sound)pressure into decibels

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First of all - Here is the thing (Click image for full size):

It is a relatively simple Decibel meter.

So I need to convert the value from the ADC into decibels.

Does anyone have experience in calculating this?

I have never used the standard floating point library - is it buggy?

There is no hurry in doing the calculation so the floating point approach might do the trick.

Any ideas - or experiences ?

The project files can be found at:
http://furpile.com/Projects/gian...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A dB table? No need for complex maths. I'm sure that it can be done better but it's a start.

prog_uint16_t dB_Table[35] = 

	{
	818, 729, 650, 579, 516, 460, 410, 365, 326, 290,
	259, 231, 205, 183, 163, 145, 130, 116, 103, 92,
	82, 73, 65, 58, 52, 46, 41, 37, 33, 29,
	26, 23, 21, 18, 16,
	};

//Returns dB value in dB. dB is volatile extern
void read_ambient_noise (void)
{
	uint8_t a;
	uint16_t ambient_noise;
	uint32_t b=0;

	for (a=0; a < 120; a++)
	
	{
	ADMUX=(1<<REFS0 | ADC_0);				//Conversion on channel 0, AVCC reference,  10 bit mode
	ADCSRA |= (1<<ADSC);					//Start conversion
	loop_until_bit_is_set(ADCSRA, ADIF);	//Wait for conversion complete
	ADCSRA |= (1<<ADIF);					//Clear conversion flag
	b=b + ADC;								//Add last conversion result.
	_delay_ms(1);

	}

	ambient_noise = b/120;					//Get average 

	if (ambient_noise > 818)
		{
		ambient_noise=818;					//Max 85dB
		}

	if (ambient_noise < 16)
		{
		ambient_noise=16;					//Min ~51dB
		}

		for (dB=0; dB < 29; dB ++)			//29dB minimum for now (max35dB)
			{
				if ((ambient_noise) >= pgm_read_word(&dB_Table[dB]) )
				{
				break;
				}
			}
}

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have 10-bit ADC value, and want dB(SPL) units output.

What kind of output you want (digits or bits of resolution)?

But I would use a short lookup table - you all know voltage ratio of 2 is about 3dB, so you need only log table for certain ratios, and you can just use multiples of it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jepael wrote:
you all know voltage ratio of 2 is about 3dB
A voltage ratio of 2 is about 6dB. A power ratio of 2 is about 3dB.

But Jepael's basic idea is fine.

1) Take a signal sample x.

2) Square x to make a positive number proportional to power.

3) Shift x left (if necessary) until the MSB is 1, remembering the number of shifts n.

4) Shift x left one more time.

5) Use the top m bits of x as a lookup into a 2^m entry table of dBSPL.

6) Subtract 3n (3 because x is a power, not an amplitude) from the number retrieved from the table.

A more complete SPL meter would have a step 1(a) to apply a standard A- or C- response weighting curve, and a step 2(a) to average a number of successive squared sample values, possibly with time constants corresponding to standard fast or slow SPL meter responses.

To calculate the table entries you will need to know what signal sample value x you get in step 1 for a signal at some specific acoustic level.

CH
==
[EDIT: I assume the OP's reference to a "decibel meter" does imply what I would call an SPL meter (ie an instrument to measure sound pressure level), but apologies if not.]

[EDIT2: It occurs to me that steps 1, 1a, 2 and 2a might well already be done for you in hardware. That would simplify things considerably as far as code is concerned. You'd need to find out if the samples from the ADC are proportional to amplitude or power, though, and subtract either 6n or 3n (respectively) at step 6.]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Very Many Thanks for Your Replies and Merry Midsummer Eve !


Click the image to get to the project files

This thing is actually made for my musical friends who want to protect their ears from excessive sound load. The display is a giant panel meter (25 centimeters diameter) scavenged from demolished power plant. The original scale was 0-100 kA but my dear wife made a new look to that. The meter needle is driven by a stepper motor. The original mechanism was long gone. The picture is from another project with similar background. That was a success too. However - that one made the decibel conversion by hardware - attennuators to be exact.

So, what I actually need is a readily calculated table of values of sound pressures or to be exact - values of x in 20log(x/y) where y equals to one. The table does not need to be that big - only 100 members as my display hardware only supports scale 0-100.

Then, with the measures and scaled pressure value I do a search on that table finding first value that is bigger than the measured pressure. And there You are.

The hard thing in this is not the code doing the table scan - it is the table and the values there. Will write a separate program to create that table.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hmmm isn't this an OLD project? Xplain yourself. :)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

js wrote:
hmmm isn't this an OLD project? Xplain yourself. :)

Yes - I already made one. And as it was a success I was requested to make another.

The previous design used digital attenuators to achieve the decibel conversion. That was my mistake. The hardware was complex and the result was barely working. It worked - but barely.

Hence the new design for the controller part and this thread.

Sorry about myself repeating myself.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well Britney you are recovering pretty well from your drug and alchool additcion... :wink:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Done with the logaritmic conversion. It was a lot easier than I was suspecting.

So I wrote a small proggie to read a template file and fill in parts of it with generated table(s). I really like code generators - there is something mystic with those ;)

Then I was fiddling with code optimizer to get this really right. My first attempt failed miserably as the optimizer optimized the entire loop away - it appeared that the optimizer is not able to behave well with usion/structure systems with unused parts. For some reason it thinks that the entire structure is not used and it gets rid of it. Delaring the union as volatile introduced stopping-interrupts-while-executing type of code LOL ;)

Using the normal or/shift method to read dword from porgram space on the other hand resulted in a very inefficient code.

So - I made some tests and figured out that the optimizer can manage with systems which are not too complex. The following works fine:

typedef	union	dword_u {
	uint8_t		c[4];
	uint32_t	dw;
}	dword_ut;

This code:

// -----------------------------------------------------------------------
// Return Logarithm * 10 for the given value
// -----------------------------------------------------------------------
extern	uint8_t		Uint8Log10		( uint32_t aValue ) {
	uint8_t		result;
	dword_ut	logarithm;
	PGM_P		pp;

	for	( result = 0; result < 215; result++ ) {
		pp = (PGM_P)logarithmic_table + (result * sizeof(uint32_t));
		logarithm.c[0] = pgm_read_byte( pp++ );
		logarithm.c[1] = pgm_read_byte( pp++ );
		logarithm.c[2] = pgm_read_byte( pp++ );
		logarithm.c[3] = pgm_read_byte( pp );

		if	( logarithm.dw >= aValue )	break;
	}

	return	result + 1;
}

Results to assembler like this:

// -----------------------------------------------------------------------
// Return Logarithm * 10 for the given value
// -----------------------------------------------------------------------
extern	uint8_t		Uint8Log10		( uint32_t aValue ) {
 856:	ef 92       	push	r14
 858:	ff 92       	push	r15
 85a:	0f 93       	push	r16
 85c:	1f 93       	push	r17
 85e:	7b 01       	movw	r14, r22
 860:	8c 01       	movw	r16, r24
 862:	a4 e5       	ldi	r26, 0x54	; 84
 864:	b0 e0       	ldi	r27, 0x00	; 0
 866:	90 e0       	ldi	r25, 0x00	; 0
	uint8_t		result;
	dword_ut	logarithm;
	PGM_P		pp;

	for	( result = 0; result < 215; result++ ) {
		pp = (PGM_P)logarithmic_table + (result * sizeof(uint32_t));
		logarithm.c[0] = pgm_read_byte( pp++ );
 868:	fd 01       	movw	r30, r26
 86a:	84 91       	lpm	r24, Z
 86c:	28 2f       	mov	r18, r24
		logarithm.c[1] = pgm_read_byte( pp++ );
 86e:	31 96       	adiw	r30, 0x01	; 1
 870:	e4 91       	lpm	r30, Z
 872:	3e 2f       	mov	r19, r30
		logarithm.c[2] = pgm_read_byte( pp++ );
 874:	fd 01       	movw	r30, r26
 876:	32 96       	adiw	r30, 0x02	; 2
 878:	e4 91       	lpm	r30, Z
 87a:	4e 2f       	mov	r20, r30
		logarithm.c[3] = pgm_read_byte( pp );
 87c:	fd 01       	movw	r30, r26
 87e:	33 96       	adiw	r30, 0x03	; 3
 880:	e4 91       	lpm	r30, Z
 882:	5e 2f       	mov	r21, r30

		if	( logarithm.dw >= aValue )	break;
 884:	2e 15       	cp	r18, r14
 886:	3f 05       	cpc	r19, r15
 888:	40 07       	cpc	r20, r16
 88a:	51 07       	cpc	r21, r17
 88c:	20 f4       	brcc	.+8      	; 0x896 <__stack+0x37>
 88e:	9f 5f       	subi	r25, 0xFF	; 255
 890:	14 96       	adiw	r26, 0x04	; 4
 892:	97 3d       	cpi	r25, 0xD7	; 215
 894:	49 f7       	brne	.-46     	; 0x868 <__stack+0x9>
 896:	89 2f       	mov	r24, r25
 898:	8f 5f       	subi	r24, 0xFF	; 255
	}

	return	result + 1;
}
 89a:	99 27       	eor	r25, r25
 89c:	1f 91       	pop	r17
 89e:	0f 91       	pop	r16
 8a0:	ff 90       	pop	r15
 8a2:	ef 90       	pop	r14
 8a4:	08 95       	ret

I would NOT be able to write the assembler any better manually.

I include the code generator in case someone makes some use for it. Feel free to use it as You wish.

Attachment(s):