Shop OBEX P1 Docs P2 Docs Learn Events
Calculate decibels from a ratio or range of bits — Parallax Forums

Calculate decibels from a ratio or range of bits

MarcGMarcG Posts: 14
edited 2011-03-09 12:57 in Propeller 1
I want to display, in decibels, the 14bit value that I am sending to an audio MDAC digital attenuator.

To calculate the gain of my MDAC in dB, I use the formula log10(<MDAC value>/16383)*20

My dB reading only needs to display an accuracy of 1 decimal point (ex -5.5dB). I am trying to save cogs and maintain speed and I thought that this might best be accomplished, in spin, utilizing the log table. I have modified my formula to use log base 2, and get answers that are accurate enough for my application.
log2(<MDAC value>/16383)*6 This results in an answer that is +/- a tenth dB.

I can do this with a lookup table, but I would like to save memory.

Comments

  • lonesocklonesock Posts: 917
    edited 2011-03-02 11:13
    Well, the simplest way is a hack...the ">|" (Bitwise Encode) operator finds the highest bit, sort of an integer log2 approximation. So something like this (untested)

    dB := ( ( >| value ) - 14 ) * 6

    should do the trick. The -14 is, of course, the division. Also, you may want to add in 0.707 * 16384 to value 1st, to do a sort of rounding in pre-log space.

    dB := ( ( >| (value + 11585) ) - 14 ) * 6

    I'm not sure of operator precedence, so you may not need some of the parens.

    Jonathan
  • MarcGMarcG Posts: 14
    edited 2011-03-02 11:48
    This < dB := ( ( >| value ) - 14 ) * 6 > got me displaying the the integer portion in 6dB increments (0,-6,-12,ect.). Do I need to convert to a floating point number?
  • lonesocklonesock Posts: 917
    edited 2011-03-02 14:50
    Switching to floating point is definitely simpler (though it takes up another cog, or is slow):

    dB := f32.FMul( f32.Log10( f32.FFloat( val ) ), 20.0 )

    You can do a fairly simple integer approximation to return milliBells (dB * 100):

    dB := >|val - 1
    frac := val >< dB >< 16
    dB *= scale_dB
    dB += frac / scale_frac

    where scale_frac = 109 and scale_dB = 602. Still just an approximation, of course, good to the 1st 2 decimal places or so. This does not divide by 16384...you can simple subtract 8429 [milliBells] at the end.

    Jonathan
  • Tracy AllenTracy Allen Posts: 6,666
    edited 2011-03-02 15:46
    The bitlog function can fill in additional bits. Here is a step by step for an 8 bit interpolation:

    Z := >| x - 1 ' Highest bit of the number, 0 to 15, do not allow x=0. Integer part of log2(x)
    R := x - |< Z ' This is the remainder when the greatest power of 2 less than x is subtracted from x
    M := R >> ((Z #> 8) - 8) << (8 - (Z <# 8)) ' This shifts the remainder over to fill one byte.
    ' shift right for large numbers, left for small numbers (based on Z). 8 is the turning point
    ' this is the fractional part of log2(x), but as linear interpolation between 2^z and 2^(z+1)
    ' The bitLog function stops there, with a the integer value and a linear interpolation to z bits

    Now, on the propeller, you have the log table in rom. One more step to turn the linear interpolation into more accurate log interpolation. Justify the remainder R into 12 bits (instead of 8) and use that as the basis of the lookup into the log table at $C000 to $CFFF.
    M := R >> ((Z #> 12) - 12) << (12 - (Z <# 12)) ' Justifies the remainder into 12 bits.

    L := WORD[$C000][M & $FFE] ' fractional part of log2(x), 16 bits mantissa, lsb=0 for word address

    Subtract the 14 from the characteristic (integer part) at the very end.
  • MarcGMarcG Posts: 14
    edited 2011-03-03 14:40
    lonesock, Thanks, this got me where I need to be. How did you come up with the scale factors?

    Tracy, There seems to be a gap in my knowledge. I know that if I take (Z-13)*6, I get my dB readings in powers of 2 (i.e. 0,-6,-12,-18, etc.). How do I combine Z & L in such a fashion to get full resolution. Also, since I am working with integers in spin, I will need the dB value in a form where I can extract the decimal integer side as one integer, and the decimal fractional side as another integer. Convert them both to text strings, and combine them together for my display. Example -6.5dB. Or is there a better approach?
  • Tracy AllenTracy Allen Posts: 6,666
    edited 2011-03-03 15:40
    You start with
    20 * log10(value / 16383)
    Base 10 log is 0.30103 times the base 2 log:
    20 *0.30103 * log2( value /16383)
    and 16384 is a power of two so it becomes:
    6.0206 * (log2(value) - 14)
    Johnathan rounded off to 6. I guess the 0.707 has to do with RMS conversion, but I'm not sure.

    If you have an integer part Z and a 16 bit mantissa M, the two can be combined as
    Z << 16 + M
    That is a fixed point number with the binary radix implied in front of the mantissa.
    Now subtract 14 and multiply times 6.0206 to give decibels with 16 binary digits of fractional part to play with.
    dB := (Z - 14) * 6 ' in binary
    (It is possible to do 6.0206 too)
  • lonesocklonesock Posts: 917
    edited 2011-03-03 17:30
    @MarcG: scaleDB is 602 because the each power of 2 is actual 6.0206 dB, and I'm converting to mB, so scale that by 100. scale_frac takes the fractional part of the number (scaled to 16 bits) and converts that to a fraction of 602 mB, so (2^16)/602.06 is 108.85. This is just a linear approximation of the fractional part, of course, so it's consistently low between exact powers of 2.

    @Tracy: Actually, the 0.707 was an error on my part...the idea was to add in a value to pre-round...it should have actually been 0.5858 because:

    log2( 1 ) = 0
    log2( 1.414 ) = 0.5 (you want this to round up to 1)
    log2( 2 ) = 1

    Jonathan
  • MarcGMarcG Posts: 14
    edited 2011-03-04 09:22
    Jonathon, thanks for the helpful explanation.

    Tracy, Right after you stated that the integer and mantissa can be combined by Z << 16 + M. Then you stated dB := (Z - 14) * 6 ' in binary. Is this after or before left shifting Z by 16? Also, when you refer to mantissa M, are you actually referring to L := WORD[$C000][M & $FFE] ' fractional part of log2(x), 16 bits mantissa, lsb=0 for word address? Or is the Log Table not being used?
    Thanks again, for your help.
  • lonesocklonesock Posts: 917
    edited 2011-03-04 11:05
    Here's an updated approximation:

    ' get the "integer" portion of the log2 computation
    dB := >|val - 1
    ' get the fractional portion, scaled to 16 bits
    frac := val >< dB >< 16
    ' reconstruct
    dB *= 602
    dB += (1386 - frac / 181) * frac / 111465

    Just a hint where the constants came from: fit a quadratic through 3 points to approximate the log2 function: 0,0 & 0.414,0.5 & 1,1, but scaled to 16-bits. I juggled a few terms to keep the intermediate values from overflowing a 31-bit number.

    So, the results are still in mB, but are much more accurate.

    Jonathan
  • MarcGMarcG Posts: 14
    edited 2011-03-04 11:59
    Thanks, Jonathon! This is awesome. I'll give it a try when I get back into town on Tuesday.
  • Tracy AllenTracy Allen Posts: 6,666
    edited 2011-03-05 17:16
    Hi MarcG,

    I've attached a little program that runs through how to use the log table in ROM to calculate dB. I hope this is less confusing, better signal to noise ratio! The main pub is here:
    PUB log10(value) | characteristic, mantissa
      characteristic := >| value - 1     ' integer part of log2(value)
      mantissa := value >< characteristic >< 12 ' left justify to 12 bits; this is the "bitlog" mantissa
      mantissa := WORD[$C000 + mantissa]   ' fractional part of log2(x) from hub, 16 bits, (it drops bit 0 for word address)
      result := characteristic << 16 + mantissa   ' combine integer & fractional part, base 2 log, fixed point radix at bit 16
      result := result ** 1292913986   ' convert to base 10 logarithm  (multiply * 0.30103)
    
    Once you have the fractional bits, the first mantissa line, it is one more easy step to look up the value in the hub table. Then combine the integer and fractional parts back into one long, fixed point, then multiply it times the constant that converts it from base 2 to base 10. The attached program goes on to the ratio of two values, which is what you want to do for dB, and printing it out. I used the old math terms "characteristic" and "mantissa" for the integer and fractional parts.

    I adopted the way that Jonathan used the >< operator to justify the bits, much slicker than the way I had been doing it before. The quadratic approximation is neat, too, but I figure since you have the log table, you might as well use it.
  • lonesocklonesock Posts: 917
    edited 2011-03-07 08:11
    Nice, Tracy. Yes, I definitely recommend using the resources available, which in this case includes a log table [8^)

    Jonathan
  • AriAri Posts: 63
    edited 2011-03-07 15:24
    First you need to know what scale you measure "db" in. dbFS, weighted, VU, PPM etc etc etc (I am an audio acoustics engineer by trade, so I would be happy to elaborate). Once you have a reference then you should be able to more easily give an accurate reading. Also consider that a 14-bit value will be very limited in resolution. I would suggest a minimum of 16 allocated bits, and preferably 24. Most modern DAW's run in the 24 bit domain. If you are relying on samples (audio) with a bit depth of 14 bit, then your scale should be fine, but it will be coarse....

    P.S. If it helps anyone at all....0vu (at a ref of +4) = 1.228 volts. This is considered "professional" scale. The issue of db then becomes weighting (A,C, Full Scale?)
    Since dB is a measure of specific frequency ranges, it would be smart to use a weighting that is similar to the fletcher munson curves (within the human ear). The task of building an accurate meter is no easy one, and has baffled (and caused much argument within) the pro-audio community for many decades....
    Another tidbit 0vu = -16 (to -18) dbfs....
  • MarcGMarcG Posts: 14
    edited 2011-03-07 16:52
    Ari,

    I'm building a digitally controlled analog level control. The MDAC's I am using are 14 bit. This is good enough for my application, which are for cue only.
  • AriAri Posts: 63
    edited 2011-03-07 17:30
    MarcG wrote: »
    Ari,

    I'm building a digitally controlled analog level control. The MDAC's I am using are 14 bit. This is good enough for my application, which are for cue only.


    Ahh, I see....for cues coarse level should be fine....still the problem remains.....what scale do you intend the meter ans logical steps to read? VU, PPm, dbFS? also how are you going to translate the output voltage to 1.228v? What is your ref. voltage and are you employing a comparator for accuracy? I could see an error in the digital world causing a blast of full scale noise and a very angry session player....

    Curious about how you are going to implement this....John Oram built a console a while back that was 100% digitally controlled analog components.....it was a bit of a disaster (although a fine console)

    I think a digitally controlled pot would be simpler and then you could use a standard VU meter on a paralleled leg.....since resolution isn't that important.....
  • MarcGMarcG Posts: 14
    edited 2011-03-08 18:04
    Ari, I'm not building a dB meter. My goal is to display the gain through my circuit. As far as how I am going to implement this, I am not at liberty to discuss the details at this time.
  • AriAri Posts: 63
    edited 2011-03-09 01:02
    MarcG wrote: »
    Ari, I'm not building a dB meter. My goal is to display the gain through my circuit. As far as how I am going to implement this, I am not at liberty to discuss the details at this time.


    you want a cue out with no meter or relative (industry standard scale)? *scratches head*

    the digital pot is still the way to go....this has been beat to death on most "hi-fi" and DIY audio forums...there is really no way an ADC can match the linearity of a digital pot, feeding a transistor....unless you aren't trying to achieve "analog gain"......

    maybe that's my own fault for assuming you are trying to build a cue pre-amp?
  • MarcGMarcG Posts: 14
    edited 2011-03-09 12:57
    Ari,
    I am working in concert with a product manufacturer, and in order to maintain consistency with their product, I am using their circuit standards.

    Tracy,
    I've checked out the decibel.spin. Very nice. This is a useful learning tool for the Log Table and how to utilize exponents. Thanks
Sign In or Register to comment.