Skip to content

Instantly share code, notes, and snippets.

@ak1211
Created April 20, 2020 15:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ak1211/dd150eca991f6bc73f9d33df011e623e to your computer and use it in GitHub Desktop.
Save ak1211/dd150eca991f6bc73f9d33df011e623e to your computer and use it in GitHub Desktop.
電子ボリュームの減衰値を計算するJavaScript
//
// 電子ボリュームの減衰値を計算するJavaScript
// http://ak1211.com
// Copyright (c) 2018 Akihiro Yamamoto
//
//
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
//
// 1次関数 y = ax+b
const linearFn = (arr, a, b) => {
const dy = a/(arr.length-1);
let y = b;
for (let i=0; i<arr.length; i++) {
arr[i] = y;
y += dy;
}
return arr;
};
// logarithmic pot taper
const logPotTaper = (arr, valOfX05) => {
const a = Math.log( valOfX05 ) / (0.5 - 1);
const f = p => Math.exp( a*(p-1) );
for (let i=0; i<arr.length; i++) {
arr[i] = f(i/100) ;
}
return arr;
}
// potentiometer taper graph
nv.addGraph( () => {
const chart = nv.models.lineChart()
.useInteractiveGuideline(true)
.showLegend(true)
.showXAxis(true)
.showYAxis(true)
;
chart.xAxis
.axisLabel('Position (%)');
chart.yAxis
.axisLabel('Output voltage / Input voltage (%)')
.tickFormat(d3.format(',.2f'));
d3.select('#pot-taper')
.append('svg')
.attr('width', 320)
.attr('height', 320)
.datum( [{
color:'deepskyblue',
key:'10%',
values: logPotTaper( new Array(101), 0.10 )
.map( (y,i) => ({x: i, y: y*100.0}) )
},
{
color:'green',
key:'15%',
values: logPotTaper( new Array(101), 0.15 )
.map( (y,i) => ({x: i, y: y*100.0}) )
},
{
color:'slateblue',
key:'20%',
values: logPotTaper( new Array(101), 0.20 )
.map( (y,i) => ({x: i, y: y*100.0}) )
}])
.transition()
.duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
// pivot が log側にいるか確かめる
const isLogarithmicSide = pivot => {
const f = x => x; // 関数 y=x
return (f(pivot.x) > pivot.y);
};
// pivot より100%に近い側の直線
// pivot.x = 1 の値は定義されない
const upperLine = (arr, pivot) => {
const grad = (1.0 - pivot.y) / (1.0 - pivot.x);
return linearFn( arr, grad, pivot.y - grad*pivot.x );
};
// pivot より0%に近い側の直線
// pivot.x = 0 の値は定義されない
const lowerLine = (arr, pivot) => {
const grad = pivot.y / pivot.x;
return linearFn( arr, grad, 0.0 );
};
//
const bendLineWithPivot = (arr, pivot) => {
const lo = lowerLine( new Array(arr.length), pivot );
const up = upperLine( new Array(arr.length), pivot );
const fn = isLogarithmicSide(pivot) ? Math.max : Math.min;
for (let i=0; i<arr.length; i++) {
arr[i] = fn( lo[i], up[i] );
}
return arr;
};
//
const customTaper = (pivot, bias, linerPotTaper) => {
const c = bendLineWithPivot( new Array(linerPotTaper.length), pivot );
const isLineLogSide = (bias < 0);
// 屈折点と直線成分が同一領域にいる場合にのみ合成する
if( isLogarithmicSide(pivot) == isLineLogSide ) {
const fn = isLogarithmicSide(pivot) ? Math.max : Math.min;
return c.map( (_,i,y) => fn( y[i], linerPotTaper[i] ) );
} else {
// 直線成分を無視する
return c;
}
};
const readUserInputs = () => {
return {
pivot: {x: d3.select('#taperPivotX').property('value'),
y: d3.select('#taperPivotY').property('value')},
bias: 1.0 - d3.select('#linerBias').property('value'),
steps: d3.select('#volSteps').property('value'),
};
};
// custom potentiometer taper data
const customPotTaper = () => {
const pivot = readUserInputs().pivot;
const bias = readUserInputs().bias;
// 合成する直線ボリューム成分
const customLinearPotTaper = linearFn( new Array(101), 1.0, bias );
return [
{
color:'deepskyblue',
key:'10%',
values: logPotTaper( new Array(101), 0.10 )
.map((y, x) => ({x: x/100, y: y}))
},
{
color:'green',
key:'15%',
values: logPotTaper( new Array(101), 0.15 )
.map((y, x) => ({x: x/100, y: y}))
},
{
color:'slateblue',
key:'20%',
values: logPotTaper( new Array(101), 0.20 )
.map((y, x) => ({x: x/100, y: y}))
},
{
color: 'lightsteelblue',
key: 'liner pot(typical)',
values: linearFn( new Array(101), 1.0, 0.0 )
.map( (v,i) => ({x: i/100, y: v}) )
},
{
color: 'darkslategray',
key: 'liner pot',
values: customLinearPotTaper
.map( (v,i) => ({x: i/100, y: v}) )
.filter( pt => 0.0 <= pt.y && pt.y <= 1.0 )
},
{
color: 'thistle',
key: 'bended line',
values: bendLineWithPivot( new Array(101), pivot )
.map( (v,i) => ({x: i/100, y: v}) )
},
{
color: 'orangered',
key: 'custom taper',
values: customTaper( pivot, bias, customLinearPotTaper )
.map( (v,i) => ({x: i/100, y: v}) )
}
];
};
const chartCustomTaper = nv.models.lineChart()
.useInteractiveGuideline(true)
.showLegend(true)
.showXAxis(true)
.showYAxis(true)
;
const selectCustomTaper = d3.select('#custom-taper')
.append('svg')
.attr('width', 320)
.attr('height', 320)
;
// カスタムボリュームカーブグラフ
nv.addGraph( () => {
chartCustomTaper.xAxis
.axisLabel('Potentiometer position (Normalized)')
.tickFormat(d3.format(',.2f'));
chartCustomTaper.yAxis
.axisLabel('Voltage Output (Normalized)')
.tickFormat(d3.format(',.2f'));
selectCustomTaper.datum( customPotTaper() )
.transition()
.duration(500)
.call(chartCustomTaper);
d3.select('#volSteps').on("input", updateCustomGraph);
d3.select('#volSteps').on("change", updateCustomGraph);
d3.select('#taperPivotX').on("input", updateCustomGraph);
d3.select('#taperPivotY').on("input", updateCustomGraph);
d3.select('#linerBias').on("input", updateCustomGraph);
d3.select('#taperPivotX').on("change", updateCustomGraph);
d3.select('#taperPivotY').on("change", updateCustomGraph);
d3.select('#linerBias').on("change", updateCustomGraph);
nv.utils.windowResize(chartCustomTaper.update);
return chartCustomTaper;
});
const valuesOfAttenuator = () => {
const pivot = readUserInputs().pivot;
const bias = readUserInputs().bias;
const steps = parseInt(readUserInputs().steps);
const customLinearPotTaper = linearFn( new Array(steps), 1.0, bias );
const taper = customTaper( pivot, bias, customLinearPotTaper )
return {
steps: steps,
LM1972: taper.map( digitalStepLM1972 ),
PGA2311: taper.map( digitalStepPGA2311 ),
};
};
//
const digitalPot = () => {
vs = valuesOfAttenuator();
return [
{
color:'deepskyblue',
key:'10%',
values: logPotTaper( new Array(vs.steps), 0.10 )
.map( (y,i) => ({x: i, y: 20*Math.log10(y)}) )
},
{
color:'green',
key:'15%',
values: logPotTaper( new Array(vs.steps), 0.15)
.map( (y,i) => ({x: i, y: 20*Math.log10(y)}) )
},
{
color:'slateblue',
key:'20%',
values: logPotTaper( new Array(vs.steps), 0.20 )
.map( (y,i) => ({x: i, y: 20*Math.log10(y)}) )
},
{
color: 'purple',
key: 'PGA2311',
values: vs.PGA2311
.map( v => gainPGA2311.find( set=>(set[0]==v) )[1] )
.map( (v,i) => ({x: i, y: v}) )
},
{
color: 'orange',
key: 'LM1972',
values: vs.LM1972
.map( v => gainLM1972.find( set=>(set[0]==v) )[1] )
.map( (v,i) => ({x: i, y: v}) )
},
];
};
const chartDigitalPot = nv.models.scatterChart()
.showLegend(true)
.showXAxis(true)
.showYAxis(true)
;
const selectDigitalPot = d3.select('#digitalpot-graph')
.append('svg')
.attr('width', 320)
.attr('height', 320)
;
// digital potentiometer graph
nv.addGraph( () => {
values = valuesOfAttenuator();
chartDigitalPot.xAxis
.axisLabel('digital pot travel.');
chartDigitalPot.yAxis
.axisLabel('set value')
.tickFormat(d3.format(',.2f'));
selectDigitalPot
.datum( digitalPot() )
.transition()
.duration(500)
.call(chartDigitalPot);
nv.utils.windowResize(chartDigitalPot.update);
return chartDigitalPot;
});
//
const updateCustomGraph = () => {
const d = customPotTaper();
selectCustomTaper
.datum( d )
.transition()
.duration(500)
.call(chartCustomTaper);
nv.utils.windowResize(chartCustomTaper.update);
//
selectDigitalPot
.datum( digitalPot() )
.transition()
.duration(500)
.call(chartDigitalPot);
nv.utils.windowResize(chartDigitalPot.update);
//
updateResultDataArea();
};
//
const updateResultDataArea = () => {
const value = valuesOfAttenuator();
const txt = [
'// Domain of a digital potentiometer [0:' + (value.LM1972.length-1) + ']',
'attLM1972 = [' + value.LM1972 + '];',
'attPGA2311 = [' + value.PGA2311 + '];',
].join('\n');
d3.select('#stepdata').text( txt );
};
window.onload = updateResultDataArea;
// 電子ボリュームICの設定値-減衰量グラフ
nv.addGraph( () => {
//var chart = nv.models.historicalBarChart();
//var chart = nv.models.lineWithFocusChart();
//var chart = nv.models.scatterChart();
const chart = nv.models.lineChart()
.useInteractiveGuideline(true)
.showLegend(true)
.showXAxis(true)
.showYAxis(true)
;
chart.xAxis
.axisLabel('Digital Step Value')
.tickFormat(d3.format(',f'));
chart.yAxis
.axisLabel('Channel Attenuation (dB)')
.tickFormat(d3.format(',.1f'));
d3.select('#ic-curve')
.append('svg')
.attr('width', 320)
.attr('height', 320)
.datum( [{
color:'green',
key:'PGA2311',
values: gainPGA2311.map( v => ({x: v[0], y: v[1]}) )
},
{
color:'blue',
key:'LM1972',
values: gainLM1972.map( v => ({x: v[0], y: v[1]}) )
}])
.transition()
.duration(500)
.call(chart)
;
nv.utils.windowResize(chart.update);
return chart;
});
// クリップ処理(LM1972)
const clipLM1972 = decibel => {
let d = decibel;
d = Math.min( d, 0.0 );
d = Math.max( d, -78.0 );
// 小数2位以下は捨てる
d = Math.round(d*10.0) / 10.0;
// 0.5dB ステップ幅に抑える
d = Math.round(d/0.5) * 0.5;
return d;
};
// クリップ処理(PGA2311)
const clipPGA2311 = decibel => {
let d = decibel;
d = Math.min( d, 31.5 );
d = Math.max( d, -95.5 );
// 小数2位以下は捨てる
d = Math.round(d*10.0) / 10.0;
// 0.5dB ステップ幅に抑える
d = Math.round(d/0.5) * 0.5;
return d;
};
// 値域よりLM1972の定義域へ写す
const digitalStepLM1972 = y => {
let d =
(y == 0.0) ? -78.0 : 20*Math.log10(y);
d = clipLM1972(d);
const target = gainLM1972.find( set => (set[1] == d) );
return target[0];
}
// 値域よりPGA2311の定義域へ写す
const digitalStepPGA2311 = y => {
let d =
(y == 0.0) ? -95.5 : 20*Math.log10(y);
d = clipPGA2311(d);
const target = gainPGA2311.find( set => (set[1] == d) );
return target[0];
};
// 最大減衰量(dB)
const MUTE = -100.0;
// PGA2311の設定値 - 減衰値の対応
const gainPGA2311 =
[
[ 0, MUTE],
[ 1, -95.5], [ 2, -95.0], [ 3, -94.5], [ 4, -94.0], [ 5, -93.5], [ 6, -93.0],
[ 7, -92.5], [ 8, -92.0], [ 9, -91.5], [ 10, -91.0], [ 11, -90.5], [ 12, -90.0],
[ 13, -89.5], [ 14, -89.0], [ 15, -88.5], [ 16, -88.0], [ 17, -87.5], [ 18, -87.0],
[ 19, -86.5], [ 20, -86.0], [ 21, -85.5], [ 22, -85.0], [ 23, -84.5], [ 24, -84.0],
[ 25, -83.5], [ 26, -83.0], [ 27, -82.5], [ 28, -82.0], [ 29, -81.5], [ 30, -81.0],
[ 31, -80.5], [ 32, -80.0], [ 33, -79.5], [ 34, -79.0], [ 35, -78.5], [ 36, -78.0],
[ 37, -77.5], [ 38, -77.0], [ 39, -76.5], [ 40, -76.0], [ 41, -75.5], [ 42, -75.0],
[ 43, -74.5], [ 44, -74.0], [ 45, -73.5], [ 46, -73.0], [ 47, -72.5], [ 48, -72.0],
[ 49, -71.5], [ 50, -71.0], [ 51, -70.5], [ 52, -70.0], [ 53, -69.5], [ 54, -69.0],
[ 55, -68.5], [ 56, -68.0], [ 57, -67.5], [ 58, -67.0], [ 59, -66.5], [ 60, -66.0],
[ 61, -65.5], [ 62, -65.0], [ 63, -64.5], [ 64, -64.0], [ 65, -63.5], [ 66, -63.0],
[ 67, -62.5], [ 68, -62.0], [ 69, -61.5], [ 70, -61.0], [ 71, -60.5], [ 72, -60.0],
[ 73, -59.5], [ 74, -59.0], [ 75, -58.5], [ 76, -58.0], [ 77, -57.5], [ 78, -57.0],
[ 79, -56.5], [ 80, -56.0], [ 81, -55.5], [ 82, -55.0], [ 83, -54.5], [ 84, -54.0],
[ 85, -53.5], [ 86, -53.0], [ 87, -52.5], [ 88, -52.0], [ 89, -51.5], [ 90, -51.0],
[ 91, -50.5], [ 92, -50.0], [ 93, -49.5], [ 94, -49.0], [ 95, -48.5], [ 96, -48.0],
[ 97, -47.5], [ 98, -47.0], [ 99, -46.5], [100, -46.0], [101, -45.5], [102, -45.0],
[103, -44.5], [104, -44.0], [105, -43.5], [106, -43.0], [107, -42.5], [108, -42.0],
[109, -41.5], [110, -41.0], [111, -40.5], [112, -40.0], [113, -39.5], [114, -39.0],
[115, -38.5], [116, -38.0], [117, -37.5], [118, -37.0], [119, -36.5], [120, -36.0],
[121, -35.5], [122, -35.0], [123, -34.5], [124, -34.0], [125, -33.5], [126, -33.0],
[127, -32.5], [128, -32.0], [129, -31.5], [130, -31.0], [131, -30.5], [132, -30.0],
[133, -29.5], [134, -29.0], [135, -28.5], [136, -28.0], [137, -27.5], [138, -27.0],
[139, -26.5], [140, -26.0], [141, -25.5], [142, -25.0], [143, -24.5], [144, -24.0],
[145, -23.5], [146, -23.0], [147, -22.5], [148, -22.0], [149, -21.5], [150, -21.0],
[151, -20.5], [152, -20.0], [153, -19.5], [154, -19.0], [155, -18.5], [156, -18.0],
[157, -17.5], [158, -17.0], [159, -16.5], [160, -16.0], [161, -15.5], [162, -15.0],
[163, -14.5], [164, -14.0], [165, -13.5], [166, -13.0], [167, -12.5], [168, -12.0],
[169, -11.5], [170, -11.0], [171, -10.5], [172, -10.0], [173, -9.5], [174, -9.0],
[175, -8.5], [176, -8.0], [177, -7.5], [178, -7.0], [179, -6.5], [180, -6.0],
[181, -5.5], [182, -5.0], [183, -4.5], [184, -4.0], [185, -3.5], [186, -3.0],
[187, -2.5], [188, -2.0], [189, -1.5], [190, -1.0], [191, -0.5], [192, 0.0],
[193, 0.5], [194, 1.0], [195, 1.5], [196, 2.0], [197, 2.5], [198, 3.0],
[199, 3.5], [200, 4.0], [201, 4.5], [202, 5.0], [203, 5.5], [204, 6.0],
[205, 6.5], [206, 7.0], [207, 7.5], [208, 8.0], [209, 8.5], [210, 9.0],
[211, 9.5], [212, 10.0], [213, 10.5], [214, 11.0], [215, 11.5], [216, 12.0],
[217, 12.5], [218, 13.0], [219, 13.5], [220, 14.0], [221, 14.5], [222, 15.0],
[223, 15.5], [224, 16.0], [225, 16.5], [226, 17.0], [227, 17.5], [228, 18.0],
[229, 18.5], [230, 19.0], [231, 19.5], [232, 20.0], [233, 20.5], [234, 21.0],
[235, 21.5], [236, 22.0], [237, 22.5], [238, 23.0], [239, 23.5], [240, 24.0],
[241, 24.5], [242, 25.0], [243, 25.5], [244, 26.0], [245, 26.5], [246, 27.0],
[247, 27.5], [248, 28.0], [249, 28.5], [250, 29.0], [251, 29.5], [252, 30.0],
[253, 30.5], [254, 31.0], [255, 31.5],
];
// LM1972の設定値 - 減衰値の対応
const gainLM1972 =
[
[ 0, 0.0], [ 1, -0.5], [ 2, -1.0], [ 3, -1.5], [ 4, -2.0], [ 5, -2.5],
[ 6, -3.0], [ 7, -3.5], [ 8, -4.0], [ 9, -4.5], [ 10, -5.0], [ 11, -5.5],
[ 12, -6.0], [ 13, -6.5], [ 14, -7.0], [ 15, -7.5], [ 16, -8.0], [ 17, -8.5],
[ 18, -9.0], [ 19, -9.5], [ 20, -10.0], [ 21, -10.5], [ 22, -11.0], [ 23, -11.5],
[ 24, -12.0], [ 25, -12.5], [ 26, -13.0], [ 27, -13.5], [ 28, -14.0], [ 29, -14.5],
[ 30, -15.0], [ 31, -15.5], [ 32, -16.0], [ 33, -16.5], [ 34, -17.0], [ 35, -17.5],
[ 36, -18.0], [ 37, -18.5], [ 38, -19.0], [ 39, -19.5], [ 40, -20.0], [ 41, -20.5],
[ 42, -21.0], [ 43, -21.5], [ 44, -22.0], [ 45, -22.5], [ 46, -23.0], [ 47, -23.5],
[ 48, -24.0], [ 49, -24.5], [ 50, -25.0], [ 51, -25.5], [ 52, -26.0], [ 53, -26.5],
[ 54, -27.0], [ 55, -27.5], [ 56, -28.0], [ 57, -28.5], [ 58, -29.0], [ 59, -29.5],
[ 60, -30.0], [ 61, -30.5], [ 62, -31.0], [ 63, -31.5], [ 64, -32.0], [ 65, -32.5],
[ 66, -33.0], [ 67, -33.5], [ 68, -34.0], [ 69, -34.5], [ 70, -35.0], [ 71, -35.5],
[ 72, -36.0], [ 73, -36.5], [ 74, -37.0], [ 75, -37.5], [ 76, -38.0], [ 77, -38.5],
[ 78, -39.0], [ 79, -39.5], [ 80, -40.0], [ 81, -40.5], [ 82, -41.0], [ 83, -41.5],
[ 84, -42.0], [ 85, -42.5], [ 86, -43.0], [ 87, -43.5], [ 88, -44.0], [ 89, -44.5],
[ 90, -45.0], [ 91, -45.5], [ 92, -46.0], [ 93, -46.5], [ 94, -47.0], [ 95, -47.5],
[ 96, -48.0], [ 97, -49.0], [ 98, -50.0], [ 99, -51.0], [100, -52.0], [101, -53.0],
[102, -54.0], [103, -55.0], [104, -56.0], [105, -57.0], [106, -58.0], [107, -59.0],
[108, -60.0], [109, -61.0], [110, -62.0], [111, -63.0], [112, -64.0], [113, -65.0],
[114, -66.0], [115, -67.0], [116, -68.0], [117, -69.0], [118, -70.0], [119, -71.0],
[120, -72.0], [121, -73.0], [122, -74.0], [123, -75.0], [124, -76.0], [125, -77.0],
[126, -78.0], [127, MUTE], [128, MUTE], [129, MUTE], [130, MUTE], [131, MUTE],
[132, MUTE], [133, MUTE], [134, MUTE], [135, MUTE], [136, MUTE], [137, MUTE],
[138, MUTE], [139, MUTE], [140, MUTE], [141, MUTE], [142, MUTE], [143, MUTE],
[144, MUTE], [145, MUTE], [146, MUTE], [147, MUTE], [148, MUTE], [149, MUTE],
[150, MUTE], [151, MUTE], [152, MUTE], [153, MUTE], [154, MUTE], [155, MUTE],
[156, MUTE], [157, MUTE], [158, MUTE], [159, MUTE], [160, MUTE], [161, MUTE],
[162, MUTE], [163, MUTE], [164, MUTE], [165, MUTE], [166, MUTE], [167, MUTE],
[168, MUTE], [169, MUTE], [170, MUTE], [171, MUTE], [172, MUTE], [173, MUTE],
[174, MUTE], [175, MUTE], [176, MUTE], [177, MUTE], [178, MUTE], [179, MUTE],
[180, MUTE], [181, MUTE], [182, MUTE], [183, MUTE], [184, MUTE], [185, MUTE],
[186, MUTE], [187, MUTE], [188, MUTE], [189, MUTE], [190, MUTE], [191, MUTE],
[192, MUTE], [193, MUTE], [194, MUTE], [195, MUTE], [196, MUTE], [197, MUTE],
[198, MUTE], [199, MUTE], [200, MUTE], [201, MUTE], [202, MUTE], [203, MUTE],
[204, MUTE], [205, MUTE], [206, MUTE], [207, MUTE], [208, MUTE], [209, MUTE],
[210, MUTE], [211, MUTE], [212, MUTE], [213, MUTE], [214, MUTE], [215, MUTE],
[216, MUTE], [217, MUTE], [218, MUTE], [219, MUTE], [220, MUTE], [221, MUTE],
[222, MUTE], [223, MUTE], [224, MUTE], [225, MUTE], [226, MUTE], [227, MUTE],
[228, MUTE], [229, MUTE], [230, MUTE], [231, MUTE], [232, MUTE], [233, MUTE],
[234, MUTE], [235, MUTE], [236, MUTE], [237, MUTE], [238, MUTE], [239, MUTE],
[240, MUTE], [241, MUTE], [242, MUTE], [243, MUTE], [244, MUTE], [245, MUTE],
[246, MUTE], [247, MUTE], [248, MUTE], [249, MUTE], [250, MUTE], [251, MUTE],
[252, MUTE], [253, MUTE], [254, MUTE], [255, MUTE],
];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment