Series Type Stepped Attenuators

Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.
I want to build a 32 steps series attenuator but can't find a spreadsheet for calculating the resistor values. Goldpoint only have the values for 24 steps. Could anyone help please?

Please also recommend the brand / type resistor for the attenuator. Is SMD type resistor good?
 
Code:
1:  10500.00 ohms   -2.05 dB
2:  8200.00 ohms   -4.07 dB
3:  6490.00 ohms   -6.09 dB
4:  5100.00 ohms   -8.09 dB
5:  4020.00 ohms   -10.07 dB
6:  3160.00 ohms   -12.02 dB
7:  2610.00 ohms   -14.05 dB
8:  2050.00 ohms   -16.06 dB
9:  1620.00 ohms   -18.06 dB
10:  1300.00 ohms   -20.09 dB
11:  1000.00 ohms   -22.05 dB
12:  820.00 ohms   -24.07 dB
13:  649.00 ohms   -26.09 dB
14:  510.00 ohms   -28.09 dB
15:  402.00 ohms   -30.07 dB
16:  316.00 ohms   -32.02 dB
17:  261.00 ohms   -34.05 dB
18:  205.00 ohms   -36.06 dB
19:  162.00 ohms   -38.06 dB
20:  130.00 ohms   -40.09 dB
21:  100.00 ohms   -42.05 dB
22:   82.00 ohms   -44.07 dB
23:   64.90 ohms   -46.09 dB
24:   51.00 ohms   -48.09 dB
25:   40.20 ohms   -50.07 dB
26:   31.60 ohms   -52.02 dB
27:   26.10 ohms   -54.05 dB
28:   20.50 ohms   -56.06 dB
29:   16.20 ohms   -58.06 dB
30:   13.00 ohms   -60.09 dB
31:   10.00 ohms   -62.05 dB
32:  39.00 ohms to ground
total 49960.5 ohms
Python code:
Code:
from math import log10

E48 = [
    1.00, 1.05, 1.10, 1.15, 1.21, 1.27, 1.33, 1.40,
    1.47, 1.54, 1.62, 1.69, 1.78, 1.87, 1.96, 2.05,
    2.15, 2.26, 2.37, 2.49, 2.61, 2.74, 2.87, 3.01,
    3.16, 3.32, 3.48, 3.65, 3.83, 4.02, 4.22, 4.42,
    4.64, 4.87, 5.11, 5.36, 5.62, 5.90, 6.19, 6.49,
    6.81, 7.15, 7.50, 7.87, 8.25, 8.66, 9.09, 9.53
]
E24 = [
    1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0,
    2.2, 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.3,
    4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1
]
E12 = E24 [0:24:2]


def choose_best (value):
    best = None
    for p in range (6):
        for ent in E48:
            resistor = ent * pow(10, p)
            if best is None or abs(value-best) > abs(value-resistor):
                best = resistor
        for ent in E24:
            resistor = ent * pow(10, p)
            if best is None or abs(value-best) > abs(value-resistor):
                best = resistor
    return best

TOTAL_RESISTANCE = 50000
STEP = 2.0
STEP_COUNT = 32

def dB_2_ratio(dB):
    return pow(10.0, dB/20.0)


def calculate_steps(count, step, resistance):
    total = 0.0
    remaining_resistance = resistance
    target_resistance = resistance
    for i in range(1, count):
        target_resistance = remaining_resistance / dB_2_ratio (step)
        ideal_resistor = remaining_resistance - target_resistance
        actual_resistor = choose_best (ideal_resistor)
        total += actual_resistor
        remaining_resistance -= actual_resistor
        atten = 20.0 * log10 (remaining_resistance / resistance)
        print ("%s:  %6.2f ohms   %5.2f dB" % (i, actual_resistor, atten))

    print ("%s: %6.2f ohms to ground" % (count, choose_best (remaining_resistance)))
    print ("total %6.1f ohms" % total)

calculate_steps (STEP_COUNT, STEP, TOTAL_RESISTANCE)
 

PRR

Member
Joined 2003
Paid Member
Loaded or unloaded?

A 100k pot is likely to be "loaded" in most systems unless a high-Z buffer is right after it.

For UN-loaded: re-write your dB as voltages. 0 -2 -4 -6 -8... becomes 0 0.794 0.631 0.501 0.398...

Say 100k. Then from -2dB tap to ground must be 100k*0.794 or 79.4k, and the 0 to -2 resistor must be 100k-79.4k or 20.6k. Go on from there.

Some cheats and checks. -6(.02)dB is half. So you know the sum of resistors above -6 must be 50k, and the sum below -6 must be 50k. Likewise -20 is 1/10 so top is 90k and bottom is 10k; -40dB is 1/100 so top must be 99k and bottom is 1k.
 
Code:
1:  5900.00 ohms    0.00 dB loaded by 47000,  0.00 dB unloaded
2:  6490.00 ohms   -2.00 dB loaded by 47000, -1.09 dB unloaded
3:  6490.00 ohms   -4.03 dB loaded by 47000, -2.46 dB unloaded
4:  6190.00 ohms   -6.04 dB loaded by 47000, -4.10 dB unloaded
5:  5360.00 ohms   -8.07 dB loaded by 47000, -6.01 dB unloaded
6:  4420.00 ohms   -10.07 dB loaded by 47000, -8.09 dB unloaded
7:  3480.00 ohms   -12.07 dB loaded by 47000, -10.29 dB unloaded
8:  2700.00 ohms   -14.06 dB loaded by 47000, -12.52 dB unloaded
9:  2050.00 ohms   -16.05 dB loaded by 47000, -14.76 dB unloaded
10:  1600.00 ohms   -18.02 dB loaded by 47000, -16.96 dB unloaded
11:  1210.00 ohms   -20.04 dB loaded by 47000, -19.17 dB unloaded
12:  953.00 ohms   -22.02 dB loaded by 47000, -21.32 dB unloaded
13:  715.00 ohms   -24.05 dB loaded by 47000, -23.49 dB unloaded
14:  562.00 ohms   -26.02 dB loaded by 47000, -25.57 dB unloaded
15:  442.00 ohms   -28.01 dB loaded by 47000, -27.64 dB unloaded
16:  348.00 ohms   -30.00 dB loaded by 47000, -29.72 dB unloaded
17:  270.00 ohms   -32.02 dB loaded by 47000, -31.79 dB unloaded
18:  215.00 ohms   -34.01 dB loaded by 47000, -33.82 dB unloaded
19:  169.00 ohms   -36.02 dB loaded by 47000, -35.87 dB unloaded
20:  133.00 ohms   -38.03 dB loaded by 47000, -37.92 dB unloaded
21:  105.00 ohms   -40.04 dB loaded by 47000, -39.95 dB unloaded
22:   82.50 ohms   -42.04 dB loaded by 47000, -41.97 dB unloaded
23:   64.90 ohms   -44.03 dB loaded by 47000, -43.97 dB unloaded
24:   51.10 ohms   -46.01 dB loaded by 47000, -45.96 dB unloaded
25:   42.20 ohms   -47.96 dB loaded by 47000, -47.92 dB unloaded
26:   33.00 ohms   -49.99 dB loaded by 47000, -49.96 dB unloaded
27:   26.10 ohms   -52.00 dB loaded by 47000, -51.97 dB unloaded
28:   20.50 ohms   -54.00 dB loaded by 47000, -53.98 dB unloaded
29:   16.20 ohms   -55.98 dB loaded by 47000, -55.96 dB unloaded
30:   13.00 ohms   -57.94 dB loaded by 47000, -57.93 dB unloaded
31:   10.50 ohms   -59.92 dB loaded by 47000, -59.91 dB unloaded
32:   40.20 ohms   -61.94 dB loaded by 47000, -61.93 dB unloaded
total 50202.2 ohms
With reworked code using a load resistance:
Code:
from math import log10, sqrt

E48 = [
    1.00, 1.05, 1.10, 1.15, 1.21, 1.27, 1.33, 1.40,
    1.47, 1.54, 1.62, 1.69, 1.78, 1.87, 1.96, 2.05,
    2.15, 2.26, 2.37, 2.49, 2.61, 2.74, 2.87, 3.01,
    3.16, 3.32, 3.48, 3.65, 3.83, 4.02, 4.22, 4.42,
    4.64, 4.87, 5.11, 5.36, 5.62, 5.90, 6.19, 6.49,
    6.81, 7.15, 7.50, 7.87, 8.25, 8.66, 9.09, 9.53
]
E24 = [
    1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0,
    2.2, 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.3,
    4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1
]
E12 = E24 [0:24:2]


def choose_best (value, res_values):
    best = None
    for p in range (6):
        for ent in res_values:
            resistor = ent * pow(10, p)
            if best is None or abs(value-best) > abs(value-resistor):
                best = resistor
    return best


def dB_2_ratio(dB):
    return pow(10.0, dB/20.0)

def ratio_2_dB(num, denom):
    return 20.0 * log10 (num / denom)


def calc_loaded_resistances (total, load, ratio):
    # Split a total resistance for a given voltage ratio when loaded by load - quadratic solving
    a = -ratio
    b = ratio * total - load
    c = ratio * total * load
    temp = b*b - 4*a*c
    lower = (-b - sqrt(temp)) / (2*a)
    upper = total - lower
    return upper, lower


def parallel (r1, r2):
    # utility fn for combining impedances
    return r1 * r2 / (r1 + r2)


def calculate_steps(count, step, resistance, res_values, load = 47000.0):
    # first calculate ideal resistances in chain
    target_ratio = 1.0
    ideal_resistors = []
    last_upper = 0.0
    for i in range(1, count):
        target_ratio /= dB_2_ratio (step)
        upper, lower = calc_loaded_resistances (resistance, load, target_ratio)
        ideal_resistors.append (upper - last_upper)
        last_upper = upper

    # fit closest available values
    actual_resistors = []
    for i in range (1, count):
        actual_resistors.append (choose_best (ideal_resistors[i-1], res_values))
    actual_resistors.append (choose_best (lower, res_values))

    # calculate actual attenuation factors, loaded or unloaded
    total = 0.0
    remaining_resistance = sum (actual_resistors)
    i = 1
    for act in actual_resistors:
        atten = ratio_2_dB (parallel (remaining_resistance, load), total + parallel (remaining_resistance, load))
        unload_atten = ratio_2_dB (remaining_resistance, remaining_resistance + total)
        print ("%s:  %6.2f ohms   %5.2f dB loaded by %i, %5.2f dB unloaded" % (i, act, atten, load, unload_atten))
        total += act
        remaining_resistance -= act
        i += 1

    print ("total %6.1f ohms" % total)

calculate_steps (32, 2, 50000, E48+E24)
# calculate_steps (32, 2, 50000, E24)
 
My friend gave me a pair of this excellent switches.
 

Attachments

  • Top.jpg
    Top.jpg
    420 KB · Views: 153
  • bottom.jpg
    bottom.jpg
    501.4 KB · Views: 152

PRR

Member
Joined 2003
Paid Member
> What makes it so good?

The Shallcross/Shallco (and another brand, Daven, also IRC) were PROFESSIONAL attenuators invented in the 1930s for talking-movie and broadcast applications, where reliability was more important than the cost. Find pictures online. Solid contacts with selected metal plating, real springs, solid wafers.
http://www.technicalaudio.com/pdf/Daven/Daven_attenuator_switch_lab_Catalog.pdf
 
Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.