566 lines
11 KiB
Text
566 lines
11 KiB
Text
|
<html>
|
||
|
<head>
|
||
|
<title>Type 3 Designer</title>
|
||
|
<script type="text/javascript">
|
||
|
|
||
|
/* support Opera and others that don't have Array foreach.
|
||
|
* These are not ECMA compliant. */
|
||
|
if (! Array.prototype.forEach) {
|
||
|
Array.prototype.forEach = function(f) {
|
||
|
for (var i = 0; i < this.length; i ++)
|
||
|
f(this[i]);
|
||
|
};
|
||
|
}
|
||
|
if (! Array.prototype.map) {
|
||
|
Array.prototype.map = function(f) {
|
||
|
var result = [];
|
||
|
for (var i = 0; i < this.length; i ++)
|
||
|
result.push(f(this[i]));
|
||
|
return result;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/* "jQuery". Maybe I start to use it for real later on. */
|
||
|
var $ = function(selector) {
|
||
|
if (selector.match(/^#/) != null) {
|
||
|
return document.getElementById(selector.replace("#", ""));
|
||
|
}
|
||
|
return null;
|
||
|
};
|
||
|
|
||
|
var cap = 470e-12;
|
||
|
var ymin = 140;
|
||
|
var ymax = 24000;
|
||
|
var cwidth = 512;
|
||
|
var cheight = 512;
|
||
|
|
||
|
function parse_type1(text) {
|
||
|
var list = [];
|
||
|
text.split(/\x0d?\x0a/).forEach(function(line) {
|
||
|
var res = line.match(/^\s*(\d+)\s+(\d+)\s*$/);
|
||
|
if (res == null)
|
||
|
return;
|
||
|
|
||
|
list.push([parseFloat(res[1]), parseFloat(res[2])])
|
||
|
});
|
||
|
return list;
|
||
|
}
|
||
|
|
||
|
function make_type3_list(vals) {
|
||
|
var approximate_dac = function(x) {
|
||
|
var value = 0;
|
||
|
var bit = 1;
|
||
|
var weight = 1;
|
||
|
var dir = 2 * vals.dac;
|
||
|
for (var i = 0; i < 11; i ++) {
|
||
|
if (x & bit)
|
||
|
value += weight;
|
||
|
bit <<= 1;
|
||
|
weight *= dir;
|
||
|
}
|
||
|
return value / (weight / vals.dac / vals.dac) * (1 << 11);
|
||
|
};
|
||
|
|
||
|
var list = [];
|
||
|
for (var x = 0; x < 2048; x ++) {
|
||
|
var kink = approximate_dac(x);
|
||
|
var dynamic = vals.minimumfetresistance + vals.offset / Math.pow(vals.steepness, kink);
|
||
|
var resistance = (vals.baseresistance * dynamic) / (vals.baseresistance + dynamic);
|
||
|
list.push([x, 1 / (2 * Math.PI * cap * resistance)])
|
||
|
}
|
||
|
|
||
|
return list;
|
||
|
}
|
||
|
|
||
|
function viewtransform(coords) {
|
||
|
var logscale = function(x) {
|
||
|
if (x < 1)
|
||
|
return 0;
|
||
|
return Math.log(x) / Math.log(10);
|
||
|
}
|
||
|
|
||
|
var x = coords[0];
|
||
|
var y = logscale(coords[1]);
|
||
|
|
||
|
y -= logscale(ymin);
|
||
|
y /= (logscale(ymax) - logscale(ymin)) / 512;
|
||
|
|
||
|
return [x / 4., 512 - y];
|
||
|
}
|
||
|
|
||
|
function clear_area(ctx) {
|
||
|
ctx.fillStyle = 'rgba(255, 255, 255, 255)';
|
||
|
ctx.fillRect(0, 0, cwidth, cheight);
|
||
|
}
|
||
|
|
||
|
function make_grid(ctx) {
|
||
|
ctx.strokeStyle = 'rgba(0, 0, 0, 255)';
|
||
|
ctx.beginPath();
|
||
|
ctx.moveTo(0, 0);
|
||
|
ctx.lineTo(cwidth, 0);
|
||
|
ctx.lineTo(cwidth, cheight);
|
||
|
ctx.lineTo(0, cheight);
|
||
|
ctx.closePath();
|
||
|
ctx.stroke();
|
||
|
|
||
|
for (var x = 0; x < 2048; x += 128) {
|
||
|
ctx.strokeStyle = x % 512 == 0 ? 'rgba(32, 32, 32, 128)' : 'rgba(128, 128, 128, 128)';
|
||
|
|
||
|
var coord1 = viewtransform([x, ymin]);
|
||
|
var coord2 = viewtransform([x, ymax]);
|
||
|
ctx.beginPath();
|
||
|
ctx.moveTo(Math.floor(coord1[0]), coord1[1]);
|
||
|
ctx.lineTo(Math.floor(coord2[0]), coord2[1]);
|
||
|
ctx.stroke();
|
||
|
}
|
||
|
|
||
|
for (var y = 0; y < ymax; y += 100) {
|
||
|
var color = 'rgba(192, 192, 192, 128)';
|
||
|
if (y == 1000 || y == 10000)
|
||
|
color = 'rgba(0, 0, 0, 128)';
|
||
|
ctx.strokeStyle = color;
|
||
|
if (y > 1000 && y % 1000 != 0)
|
||
|
continue;
|
||
|
if (y > 10000 && y % 10000 != 0)
|
||
|
continue;
|
||
|
var coord1 = viewtransform([0, y]);
|
||
|
var coord2 = viewtransform([2048, y]);
|
||
|
ctx.beginPath();
|
||
|
ctx.moveTo(coord1[0], Math.floor(coord1[1] + 0.5));
|
||
|
ctx.lineTo(coord2[0], Math.floor(coord2[1] + 0.5));
|
||
|
ctx.stroke();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function draw_lines(ctx, list) {
|
||
|
if (list.length < 2)
|
||
|
return;
|
||
|
|
||
|
var coords = viewtransform(list[0]);
|
||
|
ctx.beginPath();
|
||
|
ctx.moveTo(coords[0], coords[1]);
|
||
|
for (var i = 1; i < list.length; i ++) {
|
||
|
coords = viewtransform(list[i]);
|
||
|
ctx.lineTo(coords[0], coords[1]);
|
||
|
}
|
||
|
ctx.stroke();
|
||
|
}
|
||
|
|
||
|
function update_view() {
|
||
|
var canvas = $('#renderarea');
|
||
|
var ctx = canvas.getContext("2d");
|
||
|
clear_area(ctx);
|
||
|
make_grid(ctx);
|
||
|
|
||
|
var list1 = parse_type1($('#type1def').value);
|
||
|
ctx.strokeStyle = 'rgba(255, 0, 0, 255)';
|
||
|
draw_lines(ctx, list1);
|
||
|
|
||
|
ctx.strokeStyle = 'rgba(255, 128, 128, 255)';
|
||
|
draw_lines(ctx, list1.map(function(x) { return [x[0], x[1]*1.1]; }));
|
||
|
draw_lines(ctx, list1.map(function(x) { return [x[0], x[1]/1.1]; }));
|
||
|
|
||
|
var vals = {};
|
||
|
['baseresistance', 'offset', 'steepness', 'minimumfetresistance', 'dac'].forEach(function(x) {
|
||
|
vals[x] = parseFloat($("#" + x).value);
|
||
|
});
|
||
|
|
||
|
var list2 = make_type3_list(vals);
|
||
|
ctx.strokeStyle = 'rgba(0, 255, 0, 255)';
|
||
|
draw_lines(ctx, list2);
|
||
|
}
|
||
|
|
||
|
function optimize() {
|
||
|
var list1 = parse_type1($('#type1def').value);
|
||
|
for (var i = 0; i < list1.length; i ++) {
|
||
|
len = i;
|
||
|
/* mistrust fits for > 20 kHz */
|
||
|
if (list1[i][0] > 20000)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
var scoring = function(list2) {
|
||
|
var j = 0;
|
||
|
var score = 0;
|
||
|
for (var i = 0; i < len; i ++) {
|
||
|
/* find corresponding spot on calculated type3 */
|
||
|
while (list2[j][0] < list1[i][0]) {
|
||
|
j ++;
|
||
|
}
|
||
|
var diff = Math.log(list1[i][1]) - Math.log(list2[j][1]);
|
||
|
score += diff * diff;
|
||
|
}
|
||
|
return score;
|
||
|
};
|
||
|
|
||
|
var bestvals = {};
|
||
|
['baseresistance', 'offset', 'steepness', 'minimumfetresistance', 'dac'].forEach(function(x) {
|
||
|
bestvals[x] = parseFloat($("#" + x).value);
|
||
|
});
|
||
|
|
||
|
var bestscore = scoring(make_type3_list(bestvals));
|
||
|
|
||
|
var i = 250;
|
||
|
while (i --) {
|
||
|
var tryvals = {};
|
||
|
var changed = false;
|
||
|
for (var key in bestvals) {
|
||
|
if (Math.random(1) > 0.5) {
|
||
|
tryvals[key] = bestvals[key] * (0.95 + Math.random(1) * 0.1);
|
||
|
changed = true;
|
||
|
} else {
|
||
|
tryvals[key] = bestvals[key];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var tryscore = scoring(make_type3_list(tryvals));
|
||
|
if (tryscore < bestscore) {
|
||
|
bestvals = tryvals;
|
||
|
bestscore = tryscore;
|
||
|
|
||
|
for (var key in bestvals) {
|
||
|
$('#' + key).value = bestvals[key];
|
||
|
}
|
||
|
update_view();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
onload = function() {
|
||
|
['type1def', 'baseresistance', 'offset', 'steepness', 'minimumfetresistance', 'dac'
|
||
|
].forEach(function(x) {
|
||
|
$("#" + x).onchange = update_view;
|
||
|
});
|
||
|
$('#theform').onsubmit = function() {
|
||
|
return false;
|
||
|
};
|
||
|
$('#type1defreset').onclick = function() {
|
||
|
$('#type1def').value = '';
|
||
|
update_view();
|
||
|
};
|
||
|
$('#optimize').onclick = optimize;
|
||
|
|
||
|
update_view();
|
||
|
};
|
||
|
|
||
|
</script>
|
||
|
</head>
|
||
|
<body>
|
||
|
|
||
|
<h1>Type 3 designer (for Firefox and compatibles)</h1>
|
||
|
<p>You can use this program to approximate type 1 curves as type 3 curves.
|
||
|
Copypaste your type 1 definitions into the textarea below. The format is
|
||
|
simply two values on each line as in "50 1234" which means CF=50 gives 1234 Hz.
|
||
|
Then tune the type 3 params until a fit is obtained.
|
||
|
</p>
|
||
|
|
||
|
<p>Usually, the type3 equation is not a perfect fit to measured curves.
|
||
|
Firstly, the chip itself has nonlinear filtering effects that tend to
|
||
|
modify the frequency response between the lp and hp outputs. This often biases
|
||
|
hp level down with increasing frequency, but I have seen chips where there
|
||
|
is very little bias and sometimes the bias is U-shaped function. It follows
|
||
|
that the measurements themselves are imperfect. The error bars below are 10 %.
|
||
|
Results above 20 kHz are probably completely unreliable, because many
|
||
|
soundcards have imperfect frequency response in that range during recording.
|
||
|
</p>
|
||
|
|
||
|
<form id="theform" action="." method="GET">
|
||
|
<table>
|
||
|
<tr valign="top">
|
||
|
<td><textarea id="type1def" cols="10" rows="30">
|
||
|
0 256
|
||
|
8 257
|
||
|
16 264
|
||
|
24 248
|
||
|
32 253
|
||
|
40 267
|
||
|
48 262
|
||
|
56 260
|
||
|
64 259
|
||
|
72 254
|
||
|
80 259
|
||
|
88 267
|
||
|
96 275
|
||
|
104 265
|
||
|
112 252
|
||
|
120 258
|
||
|
128 260
|
||
|
136 253
|
||
|
144 261
|
||
|
152 268
|
||
|
160 263
|
||
|
168 260
|
||
|
176 261
|
||
|
184 256
|
||
|
192 258
|
||
|
200 272
|
||
|
208 262
|
||
|
216 265
|
||
|
224 267
|
||
|
232 257
|
||
|
240 275
|
||
|
248 260
|
||
|
256 265
|
||
|
264 260
|
||
|
272 268
|
||
|
280 267
|
||
|
288 270
|
||
|
296 263
|
||
|
304 271
|
||
|
312 271
|
||
|
320 286
|
||
|
328 260
|
||
|
336 276
|
||
|
344 278
|
||
|
352 275
|
||
|
360 279
|
||
|
368 264
|
||
|
376 291
|
||
|
384 282
|
||
|
392 284
|
||
|
400 289
|
||
|
408 297
|
||
|
416 285
|
||
|
424 297
|
||
|
432 289
|
||
|
440 288
|
||
|
448 288
|
||
|
456 302
|
||
|
464 306
|
||
|
472 307
|
||
|
480 298
|
||
|
488 311
|
||
|
496 311
|
||
|
504 335
|
||
|
512 309
|
||
|
520 324
|
||
|
528 312
|
||
|
536 319
|
||
|
544 324
|
||
|
552 331
|
||
|
560 324
|
||
|
568 341
|
||
|
576 340
|
||
|
584 349
|
||
|
592 343
|
||
|
600 360
|
||
|
608 351
|
||
|
616 364
|
||
|
624 370
|
||
|
632 378
|
||
|
640 382
|
||
|
648 394
|
||
|
656 392
|
||
|
664 393
|
||
|
672 411
|
||
|
680 413
|
||
|
688 450
|
||
|
696 446
|
||
|
704 457
|
||
|
712 469
|
||
|
720 492
|
||
|
728 499
|
||
|
736 489
|
||
|
744 536
|
||
|
752 521
|
||
|
760 559
|
||
|
768 528
|
||
|
776 553
|
||
|
784 563
|
||
|
792 562
|
||
|
800 603
|
||
|
808 607
|
||
|
816 633
|
||
|
824 645
|
||
|
832 678
|
||
|
840 688
|
||
|
848 727
|
||
|
856 743
|
||
|
864 776
|
||
|
872 788
|
||
|
880 840
|
||
|
888 875
|
||
|
896 883
|
||
|
904 905
|
||
|
912 967
|
||
|
920 1007
|
||
|
928 1039
|
||
|
936 1136
|
||
|
944 1157
|
||
|
952 1234
|
||
|
960 1292
|
||
|
968 1385
|
||
|
976 1422
|
||
|
984 1577
|
||
|
992 1613
|
||
|
1000 1733
|
||
|
1008 1812
|
||
|
1016 1975
|
||
|
1024 1342
|
||
|
1032 1371
|
||
|
1040 1485
|
||
|
1048 1536
|
||
|
1056 1638
|
||
|
1064 1680
|
||
|
1072 1788
|
||
|
1080 1911
|
||
|
1088 1974
|
||
|
1096 2057
|
||
|
1104 2198
|
||
|
1112 2267
|
||
|
1120 2449
|
||
|
1128 2582
|
||
|
1136 2776
|
||
|
1144 2865
|
||
|
1152 2878
|
||
|
1160 3091
|
||
|
1168 3220
|
||
|
1176 3349
|
||
|
1184 3550
|
||
|
1192 3739
|
||
|
1200 3890
|
||
|
1208 4113
|
||
|
1216 4254
|
||
|
1224 4414
|
||
|
1232 4627
|
||
|
1240 4856
|
||
|
1248 5043
|
||
|
1256 5294
|
||
|
1264 5528
|
||
|
1272 5807
|
||
|
1280 5483
|
||
|
1288 5789
|
||
|
1296 6016
|
||
|
1304 6258
|
||
|
1312 6415
|
||
|
1320 6729
|
||
|
1328 6898
|
||
|
1336 7218
|
||
|
1344 7328
|
||
|
1352 7634
|
||
|
1360 7779
|
||
|
1368 8161
|
||
|
1376 8314
|
||
|
1384 8568
|
||
|
1392 8866
|
||
|
1400 9106
|
||
|
1408 9247
|
||
|
1416 9490
|
||
|
1424 9688
|
||
|
1432 9920
|
||
|
1440 10199
|
||
|
1448 10514
|
||
|
1456 10743
|
||
|
1464 11090
|
||
|
1472 11255
|
||
|
1480 11596
|
||
|
1488 11802
|
||
|
1496 12046
|
||
|
1504 12312
|
||
|
1512 12513
|
||
|
1520 12818
|
||
|
1528 13100
|
||
|
1536 12511
|
||
|
1544 12836
|
||
|
1552 13022
|
||
|
1560 13347
|
||
|
1568 13552
|
||
|
1576 13649
|
||
|
1584 14055
|
||
|
1592 14371
|
||
|
1600 14390
|
||
|
1608 14706
|
||
|
1616 14946
|
||
|
1624 15049
|
||
|
1632 15296
|
||
|
1640 15565
|
||
|
1648 15805
|
||
|
1656 16068
|
||
|
1664 16156
|
||
|
1672 16314
|
||
|
1680 16589
|
||
|
1688 16864
|
||
|
1696 17123
|
||
|
1704 17304
|
||
|
1712 17508
|
||
|
1720 17755
|
||
|
1728 17973
|
||
|
1736 18210
|
||
|
1744 18462
|
||
|
1752 18723
|
||
|
1760 18831
|
||
|
1768 19116
|
||
|
1776 19483
|
||
|
1784 19583
|
||
|
1792 19454
|
||
|
1800 19577
|
||
|
1808 19865
|
||
|
1816 20068
|
||
|
1824 20309
|
||
|
1832 20321
|
||
|
1840 20522
|
||
|
1848 20760
|
||
|
1856 20941
|
||
|
1864 21191
|
||
|
1872 21209
|
||
|
1880 21483
|
||
|
1888 21510
|
||
|
1896 21687
|
||
|
1904 21926
|
||
|
1912 21974
|
||
|
1920 22119
|
||
|
1928 22260
|
||
|
1936 22265
|
||
|
1944 22422
|
||
|
1952 22441
|
||
|
1960 22575
|
||
|
1968 22656
|
||
|
1976 22729
|
||
|
1984 22824
|
||
|
1992 22896
|
||
|
2000 22974
|
||
|
2008 22989
|
||
|
2016 23055
|
||
|
2024 23091
|
||
|
2032 23090
|
||
|
2040 23151
|
||
|
</textarea><br/>
|
||
|
<input type="button" id="type1defreset" value="Clear"/>
|
||
|
</td>
|
||
|
<td><canvas id="renderarea" width="512" height="512">
|
||
|
<span style="color:red">Unsupported browser! No canvas element!</span>
|
||
|
</canvas></td>
|
||
|
<td>
|
||
|
<table>
|
||
|
<tr>
|
||
|
<td>BaseResistance:</td>
|
||
|
<td><input type="text" id="baseresistance" value="1.30e6" title="controls the start of curve -- get this right first"/></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>Offset:</td>
|
||
|
<td><input type="text" id="offset" value="2.9e8" title="partially controls the point when the curve starts to rise"/>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>Steepness:</td>
|
||
|
<td><input type="text" id="steepness" value="1.007" title="controls the steepness around the midpoint and also the point it starts to rise"/></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>MinimumFETResistance:</td>
|
||
|
<td><input type="text" id="minimumfetresistance" value="1.9e4" title="controls behaviour at the very top end"/></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>DAC:</td>
|
||
|
<td><input type="text" id="dac" value="0.96" title="Kinkedness"/></td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
<input type="button" id="optimize" value="Optimize"/>
|
||
|
<br/>
|
||
|
<p>All the graphs update automatically on changes in input.</p>
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
</form>
|
||
|
|
||
|
</body>
|
||
|
</html>
|