Merge remote-tracking branch 'string_tensions/master'

This commit is contained in:
Connor Olding 2018-10-11 16:45:37 +02:00
commit 07c587287a
5 changed files with 488 additions and 0 deletions

156
data.py Executable file
View file

@ -0,0 +1,156 @@
# (product name, unit weight)
# D'Addario stock via http://www.daddario.com/upload/tension_chart_13934.pdf
# Circle K String stock http://circlekstrings.com/CKSIMAGES/UnitWeightChart130105.pdf
# string names don't necessarily match up with their actual product names
daddario_plain_steel = (
('DAPL007' , .00001085),
('DAPL008' , .00001418),
('DAPL0085', .00001601),
('DAPL009' , .00001794),
('DAPL0095', .00001999),
('DAPL010' , .00002215),
('DAPL0105', .00002442),
('DAPL011' , .00002680),
('DAPL0115', .00002930),
('DAPL012' , .00003190),
('DAPL013' , .00003744),
('DAPL0135', .00004037),
('DAPL014' , .00004342),
('DAPL015' , .00004984),
('DAPL016' , .00005671),
('DAPL017' , .00006402),
('DAPL018' , .00007177),
('DAPL019' , .00007997),
('DAPL020' , .00008861),
('DAPL022' , .00010722),
('DAPL024' , .00012760),
('DAPL027' , .00014975),
)
daddario_nickle_wound = ( # XL
('DANW017' , .00005524),
('DANW018' , .00006215),
('DANW019' , .00006947),
('DANW020' , .00007495),
('DANW021' , .00008293),
('DANW022' , .00009184),
('DANW024' , .00010857),
('DANW025*', .00011875), # from the EXL110BT, an approximation
('DANW026' , .00012671),
('DANW028' , .00014666),
('DANW030' , .00017236),
('DANW032' , .00019347),
('DANW034' , .00021590),
('DANW036' , .00023964),
('DANW037*', .00024872), # from the EXL115BT, an approximation
('DANW038' , .00026471),
('DANW039' , .00027932),
('DANW040*', .00029570), # from the EXL120BT, an approximation
('DANW042' , .00032279),
('DANW044' , .00035182),
('DANW046' , .00038216),
('DANW048' , .00041382),
('DANW049' , .00043014),
('DANW050*', .00044720), # from the EXL115BT, an approximation
('DANW052' , .00048109),
('DANW054' , .00053838),
('DANW056' , .00057598),
('DANW059' , .00064191),
('DANW060' , .00066542),
('DANW062' , .00070697),
('DANW064' , .00074984),
('DANW066' , .00079889),
('DANW068' , .00084614),
('DANW070' , .00089304),
('DANW072' , .00094124),
('DANW074' , .00098869),
('DANW080' , .00115011),
)
kalium_plain = (
('CKPL008 ',.0000142401458191),
('CKPL0085',.00001607510288),
('CKPL009', .0000180219146483),
('CKPL0095',.00002008032129),
('CKPL010', .0000222518914107),
('CKPL0105',.00002453144932),
('CKPL011', .0000269251480883),
('CKPL0115',.00002942561205),
('CKPL012', .0000320389593746),
('CKPL0125',.00003476567932),
('CKPL013', .0000376052948255),
('CKPL0135',.00004055150041),
('CKPL014', .000043607),
('CKPL015', .000050050),
('CKPL016', .000056961),
('CKPL017', .000064300),
('CKPL018', .000072088),
('CKPL019', .000080360),
('CKPL020', .000089031),
('CKPL021', .000098155),
('CKPL022', .000107666),
('CKPL023', .000117702),
)
kalium_hybrid_wound = (
('CKHW021', .000093873),
('CKHW022', .000103500),
('CKHW023', .000113985),
('CKHW024', .000124963),
('CKHW025', .000136054),
('CKHW026', .000144691),
('CKHW027', .000153146),
('CKHW028', .000161203),
('CKHW029', .000178551),
('CKHW031', .000198902),
('CKHW033', .000223217),
('CKHW035', .000249034),
('CKHW037', .000276237),
('CKHW039', .000304788),
('CKHW041', .000334965),
('CKHW043', .000366357),
('CKHW045', .000404956),
('CKHW047', .000447408),
('CKHW049', .000475438),
('CKHW051', .000512645),
('CKHW053', .000551898),
('CKHW055', .000584407),
('CKHW057', .000625704),
('CKHW059', .000679149),
('CKHW061', .000720293),
('CKHW063', .000765973),
('CKHW065', .000821116),
('CKHW067', .000870707),
('CKHW070', .000939851),
('CKHW073', .001021518),
('CKHW076', .001110192),
('CKHW079', .001188974),
('CKHW082', .001293598),
('CKHW086', .001416131),
('CKHW090', .001544107),
('CKHW094', .001677765),
('CKHW098', .001831487),
('CKHW102', .001986524),
('CKHW106', .002127413),
('CKHW112', .002367064),
('CKHW118', .002616406),
('CKHW124', .002880915),
('CKHW130', .003154996),
('CKHW136', .003441822),
('CKHW142', .003741715),
('CKHW150', .004051506),
('CKHW158', .004375389),
('CKHW166', .005078724),
('CKHW174', .005469937),
('CKHW182', .006071822),
('CKHW190', .006605072),
('CKHW200', .007311717),
('CKHW210', .008037439),
('CKHW222', .009091287),
('CKHW232', .009888443),
('CKHW244', .010907182),
('CKHW254', .011787319),
)

50
notes.py Executable file
View file

@ -0,0 +1,50 @@
#!/usr/bin/python
A = 440
notes = {
'C' : 0,
'D' : 2,
'E' : 4,
'F' : 5,
'G' : 7,
'A' : 9,
'B' : 11,
}
rel = (2**(1/12.))
def note2freq(name):
sharp = name[1] == '#'
flat = name[1] == 'b'
if sharp or flat:
note = name[0:1]
octave = int(name[2])
else:
note = name[0]
octave = int(name[1])
num = notes[note]
if sharp:
num += 1
if flat:
num -= 1
fullnum = num + 12*(octave - 5)
return A*rel**(fullnum + 3)
if __name__ == '__main__':
test_fmt = '{:3} gave {: 9.2f} Hz\nexpected {: 9.2f} Hz'
def test(name, expected):
print(test_fmt.format(name, note2freq(name), expected))
test('C2' , 65.41)
test('Ab4', 415.30)
test('A4' , 440.00)
test('B4' , 493.88)
test('B#4', 523.25)
test('Cb5', 493.88)
test('C5' , 523.25)
print()
test('E4' , 329.63)
test('B3' , 246.94)
test('G3' , 196.00)
test('D3' , 146.83)
test('A2' , 110.00)
test('E2' , 82.41)

81
run.py Executable file
View file

@ -0,0 +1,81 @@
#!/usr/bin/python
from strings import print_ideal_stock
in_mm = 25.4
default_scale_length = 648/in_mm
sets = """
regular light
E4 16 PD
B3 16 PD
G3 16 PD
D3 16 WD
A2 16 WD
E2 16 WD
jazz medium
E4 24 PD
B3 24 PD
G3 24 WD
D3 24 WD
A2 24 WD
E2 24 WD
regular light (DADGAD)
D4 16 PD
A3 16 PD
G3 16 PD
D3 16 WD
A2 16 WD
D2 16 WD
fanned-fret bass
G2 35 WC 34.00
D2 35 WC 34.75
A1 35 WC 35.50
E1 35 WC 36.25
B0 35 WC 37.00
regular light (Open G 7-string)
D4 16 PD
B3 16 PD
G3 16 PD
D3 16 WD
B2 16 WD
G2 16 WD
D2 16 WD
"""
string_sets = {}
sets = sets+'\n\n'
title = None
for line in sets.splitlines():
if not line:
title = None
continue
if not title:
title = line
string_sets[title] = []
else:
fields = line.split()
note, tension = fields[0:2]
tension = int(tension)
req = ''
if len(fields) > 2:
req = fields[2]
length = None
if len(fields) > 3:
length = float(fields[3])
string = (note, tension, req, length)
string_sets[title].append(string)
print('for a scale length of {:>5.2f} inches'.format(default_scale_length))
for name, strings in sorted(string_sets.items()):
print()
print('"{}"'.format(name))
print_ideal_stock(strings, default_scale_length)

84
run2.py Executable file
View file

@ -0,0 +1,84 @@
#!/usr/bin/python
from strings import print_tensions
in_mm = 25.4
default_scale_length = 648/in_mm
sets = """
E standard (super light balanced)
E4 DAPL009
B3 DAPL012
G3 DAPL015
D3 DANW022
A2 DANW030
E2 DANW040
E standard (medium balanced)
E4 DAPL011
B3 DAPL015
G3 DAPL019
D3 DANW028
A2 DANW037
E2 DANW050
C standard (medium balanced)
C4 DAPL011
G3 DAPL015
D#3 DAPL019
A#2 DANW028
F2 DANW037
C2 DANW050
C standard (jazz medium)
C4 DAPL013
G3 DAPL017
D#3 DANW026
A#2 DANW036
F2 DANW046
C2 DANW056
C standard (CKS-G6-14-59mb)
C4 CKPL014
G3 CKPL019
D#3 CKHW025
A#2 CKHW033
F2 CKHW045
C2 CKHW059
B standard (CKS-G6-14-59mb)
B3 CKPL014
F#3 CKPL019
D3 CKHW025
A2 CKHW033
E2 CKHW045
B1 CKHW059
D standard drop C (medium balanced)
D4 DAPL011
A3 DAPL015
F3 DAPL019
C3 DANW028
G2 DANW037
C2 DANW050
"""
string_sets = {}
sets = sets+'\n\n'
title = None
for line in sets.splitlines():
if not line:
title = None
continue
if not title:
title = line
string_sets[title] = []
else:
note, string = line.split()
string_sets[title].append((note, string))
print('for a scale length of {:>5.2f} inches'.format(default_scale_length))
for name, strings in sorted(string_sets.items()):
print()
print('"{}"'.format(name))
print_tensions(strings, default_scale_length)

117
strings.py Executable file
View file

@ -0,0 +1,117 @@
#!/usr/bin/python
from data import *
from notes import note2freq
stock = []
stock += daddario_plain_steel
stock += daddario_nickle_wound
stock += kalium_plain
stock += kalium_hybrid_wound
uw_const = 386.4
def uw2tension(uw, freq, SL):
return (uw*(2*SL*freq)**2)/uw_const
def tension2uw(t, freq, SL):
return (t*uw_const)/(2*SL*freq)**2
outfmt = '\
{:<8} at {:>6.2f} lb ({:>+4.2f})'
finalfmt = '\
average: {:>7.2f} lb ({:>+5.2f})\n\
total: {:>7.2f} lb ({:>+5.2f})'
tension_outfmt = '\
{:<3} {:<8} at {:>6.2f} lb'
tension_finalfmt = '\
average: {:>7.2f} lb\n\
total: {:>7.2f} lb'
def print_ideal_stock(strings, scale_length=25.512):
SL = scale_length
total_tension = 0
total_desired_tension = 0
for note, tension, req, length in strings:
freq = note2freq(note)
if length:
SL = length
else:
SL = scale_length
uw = tension2uw(tension, freq, SL)
kind = len(req) > 0 and req[0]
brand = len(req) > 1 and req[1]
closest = ('n/a', 0)
for name, stock_uw in stock:
if kind and kind == 'P' and name[2] != 'P':
continue
if kind and kind == 'W' and name[3] != 'W':
continue
if brand and brand[0] != name[0]:
continue
if abs(stock_uw - uw) < abs(closest[1] - uw):
closest = (name, stock_uw)
closest_tension = uw2tension(closest[1], freq, SL)
diff = closest_tension - tension
print(outfmt.format(closest[0], closest_tension, diff))
total_tension += closest_tension
total_desired_tension += tension
error = total_tension - total_desired_tension
average_tension = total_tension/len(strings)
average_error = error/len(strings)
print(finalfmt.format(average_tension, average_error, total_tension, error))
def print_tensions(strings, scale_length=25.512):
SL = scale_length
total_tension = 0
for note, name in strings:
freq = note2freq(note)
uw = 0
for stock_name, stock_uw in stock:
if name == stock_name or name + '*' == stock_name:
uw = stock_uw
break
if uw:
tension = uw2tension(uw, freq, SL)
else:
tension = 0
print(tension_outfmt.format(note, name, tension))
total_tension += tension
print(tension_finalfmt.format(total_tension/len(strings), total_tension))
if __name__ == '__main__':
# DAd's data is all screwy so we use change scale lengths for a best-fit
SL = 25.4825
D3 = note2freq('D3')
A2 = note2freq('A2')
E2 = note2freq('E2')
test_fmt = '{:8} {:10.8f} == {:10}'
def test(name, unit, tension, note):
print(test_fmt.format(name, tension2uw(tension, note, SL), unit))
test('NW024' , '0.00010857', 15.73, D3)
test('NW025*', '?' , 17.21, D3)
test('NW026' , '0.00012671', 18.38, D3)
print()
test('NW036' , '0.00023964', 19.04, A2)
test('NW037*', '? ', 20.23, A2)
test('NW038' , '0.00026471', 20.96, A2)
print()
SL = 25.18
test('NW039' , '0.00027932', 12.46, E2)
test('NW040*', '? ', 13.18, E2)
test('NW042' , '0.00032279', 14.37, E2)
print()
SL = 25.02
test('NW049' , '0.00043014', 18.97, E2)
test('NW050*', '? ', 19.68, E2)
test('NW052' , '0.00048109', 21.15, E2)