successful zplane drawing draggable poles and zeros

main
Ahmed Badra 2021-06-17 23:07:42 +02:00
rodzic 00bc86eb87
commit 8fc310095f
5 zmienionych plików z 2941 dodań i 90 usunięć

Wyświetl plik

@ -1,108 +1,342 @@
<!DOCTYPE html>
<!-- https://stackoverflow.com/questions/42720488/d3-v4-drag-line-chart-with-x-and-y-axes -->
<!-- Thanks to Mark - https://stackoverflow.com/users/16363/mark -->
<svg width="500" height="350"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
#zplane_polezero2{border:2px solid black;}
</style>
<body>
<canvas id="zplane_polezero2" width=300 height=300></canvas>
<div id="zeros"></div>
<div id="poles"></div>
<button onclick="addNewPole()">New Pole</button>
<button onclick="addNewZero()">New Zero</button>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
// variables used to get mouse position on the canvas
var $canvas = $("#zplane_polezero2");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
let points = d3.range(1, 10).map(function(i) {
return [i * width / 10, 50 + Math.random() * (height - 100)];
// variables to save last mouse position
// used to see how far the user dragged the mouse
// and then move the text by that distance
var startX;
var startY;
var zeros = new Array;
var poles = new Array;
var zerosNum = 0;
var polesNum = 0;
function addNewPole() {
// console.log("I'm here!");
points = document.getElementById("poles");
var div = document.createElement("div");
div.id = 'pole' + polesNum + '_polezero2';
points.appendChild(div);
poles.push([0, 0]);
polesNum = polesNum + 1;
setZplane(poles, zeros);
}
function addNewZero() {
points = document.getElementById("zeros");
var div = document.createElement('div');
div.id = 'zero' + zerosNum + '_polezero2';
points.appendChild(div);
zeros.push([0, 0]);
zerosNum = zerosNum + 1;
setZplane(poles, zeros);
}
// function updateZplane() {
// let p = [];
// let z = [];
// // for(let i = 1; i < zeros.length; i++){
// // p.append(document.getElementById("pole" + i + "_polezero2").value / 200);
// // }
// // for(let i = 1; i < zeros.length; i++){
// // z.append(document.getElementById("zero" + i + "_polezero2").value / 200);
// // }
// // var p1 = document.getElementById("pole1_polezero2").value / 200;
// // var p2 = document.getElementById("pole2_polezero2").value / 200;
// // var z1 = document.getElementById("zero1_polezero2").value / 200;
// // var z2 = document.getElementById("zero2_polezero2").value / 200;
// // var poles = [];
// // var zeros = [];
// // var polesPaired = document.getElementById("polePair_polezero2").checked;
// // var zerosPaired = document.getElementById("zeroPair_polezero2").checked;
// var x1, y1;
// if (polesPaired) {
// // complex conjugate poles and zeros
// x1 = Math.cos(p2 * Math.PI) * p1;
// if (Math.abs(x1) < 1E-14)
// x1 = 0;
// y1 = Math.sin(p2 * Math.PI) * p1;
// if (Math.abs(y1) < 1E-14)
// y1 = 0;
// poles.push([x1, y1]);
// poles.push([x1, -y1]);
// }
// else {
// poles.push([p1 * 2 - 1, 0]);
// poles.push([p2 * 2 - 1, 0]);
// }
// if (zerosPaired) {
// x1 = Math.cos(z2 * Math.PI) * z1;
// if (Math.abs(x1) < 1E-14)
// x1 = 0;
// y1 = Math.sin(z2 * Math.PI) * z1;
// if (Math.abs(y1) < 1E-14)
// y1 = 0;
// zeros.push([x1, y1]);
// zeros.push([x1, -y1]);
// }
// else {
// zeros.push([z1 * 2 - 1, 0]);
// zeros.push([z2 * 2 - 1, 0]);
// }
// setZplane(poles, zeros);
// }
// this var will hold the index of the selected text
var selectedPoint = -1;
// test if x,y is inside the bounding box of texts[textIndex]
function pointHittest(x, y, textIndex) {
console.log([x, y]);
console.log("poles.length is " + poles.length);
if(textIndex >= polesNum){
console.log("check some zero!");
return (x >= zeros[textIndex - polesNum][0] - 0.05 && x <= zeros[textIndex - polesNum][0] + 0.05 && y >= zeros[textIndex - polesNum][1] - 0.05 && y <= zeros[textIndex - polesNum][1] + 0.05);
}
if(textIndex < polesNum){
console.log("check some pole!");
return (x >= poles[textIndex][0] - 0.05 && x <= poles[textIndex][0] + 0.05 && y >= poles[textIndex][1] - 0.05 && y <= poles[textIndex][1] + 0.05);
}
}
// handle mousedown events
// iterate through texts[] and see if the user
// mousedown'ed on one of them
// If yes, set the selectedText to the index of that text
function handleMouseDown(e) {
e.preventDefault();
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
console.log("you selected point [" + startX + ", " + startY + "]");
console.log("which is point [" + (startX-150)/100 + ", " + -(startY-150)/100 + "]");
console.log("poles number is " + poles.length);
console.log("zeros number is " + zeros.length);
totalLength = polesNum + zerosNum;
// Put your mousedown stuff here
for (var i = 0; i < totalLength; i++) {
if (pointHittest((startX-150)/100, -(startY-150)/100, i)) {
selectedPoint = i;
if(i >= polesNum){
console.log("selected zero" + (i - polesNum));
}else if(i < polesNum){
console.log("selected pole" + (i));
}
}
}
}
// handle mousemove events
// calc how far the mouse has been dragged since
// the last mousemove event and move the selected text
// by that distance
function handleMouseMove(e) {
if (selectedPoint < 0) {
return;
}
console.log("Mouse is Moving!...");
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mousemove stuff here
var dx = (mouseX - startX)/100;
var dy = -(mouseY - startY)/100;
console.log("MouseXY -> [" + mouseX + ", " + mouseY + "]");
console.log("MouseXY -> [" + startX + ", " + startY + "]");
startX = mouseX;
startY = mouseY;
console.log("moved to point [" + startX + ", " + startY + "]");
console.log("which is point [" + (startX-150)/100 + ", " + -(startY-150)/100 + "]");
console.log("moved by [" + dx + ", " + dy + "]");
if(selectedPoint >= poles.length){
zeros[selectedPoint - poles.length][0] += dx;
zeros[selectedPoint - poles.length][1] += dy;
}else if(selectedPoint < poles.length){
poles[selectedPoint][0] += dx;
poles[selectedPoint][1] += dy;
}
// addNewPole([dx, dy]);
setZplane(poles, zeros);
}
// done dragging
function handleMouseUp(e) {
e.preventDefault();
selectedPoint = -1;
}
// also done dragging
function handleMouseOut(e) {
e.preventDefault();
selectedPoint = -1;
}
// listen for mouse events
$("#zplane_polezero2").mousedown(function (e) {
handleMouseDown(e);
});
$("#zplane_polezero2").mousemove(function (e) {
handleMouseMove(e);
});
$("#zplane_polezero2").mouseup(function (e) {
handleMouseUp(e);
});
$("#zplane_polezero2").mouseout(function (e) {
handleMouseOut(e);
});
var x = d3.scaleLinear()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var line = d3.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
let drag = d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended);
svg.append('rect')
.attr('class', 'zoom')
.attr('cursor', 'move')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(points, function(d) { return d[0]; }));
y.domain(d3.extent(points, function(d) { return d[1]; }));
// focus.append("path")
// .datum(points)
// .attr("fill", "none")
// .attr("stroke", "steelblue")
// .attr("stroke-linejoin", "round")
// .attr("stroke-linecap", "round")
// .attr("stroke-width", 1.5)
// .attr("d", line);
function setZplane(poles, zeros) {
focus.selectAll('circle')
.data(points)
.enter()
.append('circle')
.attr('r', 5.0)
.attr('cx', function(d) { return x(d[0]); })
.attr('cy', function(d) { return y(d[1]); })
.style('cursor', 'pointer')
.style('stroke', 'black')
.style('fill', 'blue')
// console.log("drawing..");
// console.log("poles number is " + poles.length);
// console.log("zeros number is " + zeros.length);
focus.selectAll('circle')
.call(drag);
var radius = 100; // radius of unit circle
var pSize = 4; // size of pole and zero graphic
var zSize = 4;
focus.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height/2 + ')')
.call(xAxis.ticks(0));
focus.append('g')
.attr('class', 'axis axis--y')
.attr('transform', 'translate(' + width/2 + ',0)')
.call(yAxis.ticks(0));
var c=document.getElementById("zplane_polezero2");
var ctx=c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
focus.append('g')
.append('circle')
.style('stroke', 'red')
.style('fill', 'none')
.attr('r', 100.0)
.attr('transform', 'translate(' + width/2 + ',' + height/2 + ')')
var pad = (c.width - 2 * radius) / 2; // padding on each side
// unit circle
ctx.beginPath();
ctx.strokeStyle="red";
ctx.arc(radius+pad,radius+pad,radius,0,2*Math.PI);
ctx.stroke();
function dragstarted(d) {
d3.select(this).raise().classed('active', true);
// y axis
ctx.beginPath();
//ctx.lineWidth="1";
ctx.strokeStyle="lightgray";
ctx.moveTo(radius+pad,0);
ctx.lineTo(radius+pad,c.height);
ctx.font = "italic 8px sans-serif";
ctx.fillText("Im", radius+pad+2, pad-2);
// x axis
ctx.moveTo(0,radius+pad);
ctx.lineTo(c.width,radius+pad);
ctx.fillText("Re", radius+radius+pad+2, radius+pad-2);
ctx.stroke(); // Draw it
// poles
ctx.strokeStyle="blue";
var idx;
for (idx = 0; idx < poles.length; idx++) {
var x = radius + Math.round(radius * poles[idx][0]);
var y = radius - Math.round(radius * poles[idx][1]);
ctx.beginPath();
ctx.moveTo(x - pSize + pad, y - pSize + pad);
ctx.lineTo(x + pSize + pad, y + pSize + pad);
ctx.moveTo(x - pSize + pad, y + pSize + pad);
ctx.lineTo(x + pSize + pad, y - pSize + pad);
ctx.stroke();
}
// zeros
for (idx = 0; idx < zeros.length; idx++) {
var x = radius + Math.round(radius * zeros[idx][0]);
var y = radius - Math.round(radius * zeros[idx][1]);
ctx.beginPath();
ctx.arc(x + pad, y + pad, zSize, 0, 2*Math.PI);
ctx.stroke();
}
}
function dragged(d) {
d[0] = x.invert(d3.event.x);
d[1] = y.invert(d3.event.y);
d3.select(this)
.attr('cx', x(d[0]))
.attr('cy', y(d[1]))
focus.select('path').attr('d', line);
function showZplane(poles, zeros) {
var radius = 100; // radius of unit circle
var pSize = 4; // size of pole and zero graphic
var zSize = 4;
var c=document.getElementById("all_pass_filter");
var ctx=c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
var pad = (c.width - 2 * radius) / 2; // padding on each side
// unit circle
ctx.beginPath();
ctx.strokeStyle="red";
ctx.arc(radius+pad,radius+pad,radius,0,2*Math.PI);
ctx.stroke();
// y axis
ctx.beginPath();
//ctx.lineWidth="1";
ctx.strokeStyle="lightgray";
ctx.moveTo(radius+pad,0);
ctx.lineTo(radius+pad,c.height);
ctx.font = "italic 8px sans-serif";
ctx.fillText("Im", radius+pad+2, pad-2);
// x axis
ctx.moveTo(0,radius+pad);
ctx.lineTo(c.width,radius+pad);
ctx.fillText("Re", radius+radius+pad+2, radius+pad-2);
ctx.stroke(); // Draw it
// poles
ctx.strokeStyle="blue";
var idx;
for (idx = 0; idx < poles.length; idx++) {
var x = radius + Math.round(radius * poles[idx][0]);
var y = radius - Math.round(radius * poles[idx][1]);
ctx.beginPath();
ctx.moveTo(x - pSize + pad, y - pSize + pad);
ctx.lineTo(x + pSize + pad, y + pSize + pad);
ctx.moveTo(x - pSize + pad, y + pSize + pad);
ctx.lineTo(x + pSize + pad, y - pSize + pad);
ctx.stroke();
}
// zeros
for (idx = 0; idx < zeros.length; idx++) {
var x = radius + Math.round(radius * zeros[idx][0]);
var y = radius - Math.round(radius * zeros[idx][1]);
ctx.beginPath();
ctx.arc(x + pad, y + pad, zSize, 0, 2*Math.PI);
ctx.stroke();
}
}
function dragended(d) {
d3.select(this).classed('active', false);
}
setZplane(poles, zeros);
addNewPole([0, 0]);
</script>

682
index2.html 100644
Wyświetl plik

@ -0,0 +1,682 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8" />
<title>Pole-Zero placement v2 | EarLevel Engineering</title>
<link rel="profile" href="http://gmpg.org/xfn/11" />
<link rel="stylesheet" type="text/css" media="all" href="https://www.earlevel.com/main/wp-content/themes/twentyten-child/style.css" />
<link rel="pingback" href="https://www.earlevel.com/main/xmlrpc.php" />
<link rel='dns-prefetch' href='//s.w.org' />
<link rel="alternate" type="application/rss+xml" title="EarLevel Engineering &raquo; Feed" href="https://www.earlevel.com/main/feed/" />
<link rel="alternate" type="application/rss+xml" title="EarLevel Engineering &raquo; Comments Feed" href="https://www.earlevel.com/main/comments/feed/" />
<link rel="alternate" type="application/rss+xml" title="EarLevel Engineering &raquo; Pole-Zero placement v2 Comments Feed" href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/feed/" />
<script type="text/javascript">
window._wpemojiSettings = {"baseUrl":"https:\/\/s.w.org\/images\/core\/emoji\/12.0.0-1\/72x72\/","ext":".png","svgUrl":"https:\/\/s.w.org\/images\/core\/emoji\/12.0.0-1\/svg\/","svgExt":".svg","source":{"concatemoji":"https:\/\/www.earlevel.com\/main\/wp-includes\/js\/wp-emoji-release.min.js?ver=5.3.8"}};
!function(e,a,t){var n,r,o,i=a.createElement("canvas"),p=i.getContext&&i.getContext("2d");function s(e,t){var a=String.fromCharCode;p.clearRect(0,0,i.width,i.height),p.fillText(a.apply(this,e),0,0);e=i.toDataURL();return p.clearRect(0,0,i.width,i.height),p.fillText(a.apply(this,t),0,0),e===i.toDataURL()}function c(e){var t=a.createElement("script");t.src=e,t.defer=t.type="text/javascript",a.getElementsByTagName("head")[0].appendChild(t)}for(o=Array("flag","emoji"),t.supports={everything:!0,everythingExceptFlag:!0},r=0;r<o.length;r++)t.supports[o[r]]=function(e){if(!p||!p.fillText)return!1;switch(p.textBaseline="top",p.font="600 32px Arial",e){case"flag":return s([127987,65039,8205,9895,65039],[127987,65039,8203,9895,65039])?!1:!s([55356,56826,55356,56819],[55356,56826,8203,55356,56819])&&!s([55356,57332,56128,56423,56128,56418,56128,56421,56128,56430,56128,56423,56128,56447],[55356,57332,8203,56128,56423,8203,56128,56418,8203,56128,56421,8203,56128,56430,8203,56128,56423,8203,56128,56447]);case"emoji":return!s([55357,56424,55356,57342,8205,55358,56605,8205,55357,56424,55356,57340],[55357,56424,55356,57342,8203,55358,56605,8203,55357,56424,55356,57340])}return!1}(o[r]),t.supports.everything=t.supports.everything&&t.supports[o[r]],"flag"!==o[r]&&(t.supports.everythingExceptFlag=t.supports.everythingExceptFlag&&t.supports[o[r]]);t.supports.everythingExceptFlag=t.supports.everythingExceptFlag&&!t.supports.flag,t.DOMReady=!1,t.readyCallback=function(){t.DOMReady=!0},t.supports.everything||(n=function(){t.readyCallback()},a.addEventListener?(a.addEventListener("DOMContentLoaded",n,!1),e.addEventListener("load",n,!1)):(e.attachEvent("onload",n),a.attachEvent("onreadystatechange",function(){"complete"===a.readyState&&t.readyCallback()})),(n=t.source||{}).concatemoji?c(n.concatemoji):n.wpemoji&&n.twemoji&&(c(n.twemoji),c(n.wpemoji)))}(window,document,window._wpemojiSettings);
</script>
<style type="text/css">
img.wp-smiley,
img.emoji {
display: inline !important;
border: none !important;
box-shadow: none !important;
height: 1em !important;
width: 1em !important;
margin: 0 .07em !important;
vertical-align: -0.1em !important;
background: none !important;
padding: 0 !important;
}
</style>
<link rel='stylesheet' id='wp-block-library-css' href='https://www.earlevel.com/main/wp-includes/css/dist/block-library/style.min.css?ver=5.3.8' type='text/css' media='all' />
<link rel='stylesheet' id='wp-block-library-theme-css' href='https://www.earlevel.com/main/wp-includes/css/dist/block-library/theme.min.css?ver=5.3.8' type='text/css' media='all' />
<link rel='stylesheet' id='parent-style-css' href='https://www.earlevel.com/main/wp-content/themes/twentyten/style.css?ver=5.3.8' type='text/css' media='all' />
<link rel='stylesheet' id='child-style-css' href='https://www.earlevel.com/main/wp-content/themes/twentyten-child/style.css?ver=1.1' type='text/css' media='all' />
<link rel='stylesheet' id='twentyten-block-style-css' href='https://www.earlevel.com/main/wp-content/themes/twentyten/blocks.css?ver=20181018' type='text/css' media='all' />
<script type='text/javascript' src='https://www.earlevel.com/main/wp-includes/js/jquery/jquery.js?ver=1.12.4-wp'></script>
<script type='text/javascript' src='https://www.earlevel.com/main/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.4.1'></script>
<link rel='https://api.w.org/' href='https://www.earlevel.com/main/wp-json/' />
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://www.earlevel.com/main/xmlrpc.php?rsd" />
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="https://www.earlevel.com/main/wp-includes/wlwmanifest.xml" />
<link rel='prev' title='Biquad calculator v2' href='https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/' />
<link rel='next' title='More about source code' href='https://www.earlevel.com/main/2014/01/09/more-about-source-code/' />
<link rel="canonical" href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/" />
<link rel='shortlink' href='https://www.earlevel.com/main/?p=219' />
<link rel="alternate" type="application/json+oembed" href="https://www.earlevel.com/main/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fwww.earlevel.com%2Fmain%2F2013%2F10%2F28%2Fpole-zero-placement-v2%2F" />
<link rel="alternate" type="text/xml+oembed" href="https://www.earlevel.com/main/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fwww.earlevel.com%2Fmain%2F2013%2F10%2F28%2Fpole-zero-placement-v2%2F&#038;format=xml" />
<script type="text/javascript">
(function(url){
if(/(?:Chrome\/26\.0\.1410\.63 Safari\/537\.31|WordfenceTestMonBot)/.test(navigator.userAgent)){ return; }
var addEvent = function(evt, handler) {
if (window.addEventListener) {
document.addEventListener(evt, handler, false);
} else if (window.attachEvent) {
document.attachEvent('on' + evt, handler);
}
};
var removeEvent = function(evt, handler) {
if (window.removeEventListener) {
document.removeEventListener(evt, handler, false);
} else if (window.detachEvent) {
document.detachEvent('on' + evt, handler);
}
};
var evts = 'contextmenu dblclick drag dragend dragenter dragleave dragover dragstart drop keydown keypress keyup mousedown mousemove mouseout mouseover mouseup mousewheel scroll'.split(' ');
var logHuman = function() {
if (window.wfLogHumanRan) { return; }
window.wfLogHumanRan = true;
var wfscr = document.createElement('script');
wfscr.type = 'text/javascript';
wfscr.async = true;
wfscr.src = url + '&r=' + Math.random();
(document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(wfscr);
for (var i = 0; i < evts.length; i++) {
removeEvent(evts[i], logHuman);
}
};
for (var i = 0; i < evts.length; i++) {
addEvent(evts[i], logHuman);
}
})('//www.earlevel.com/main/?wordfence_lh=1&hid=0DCA615A2402BA36C006CC14427AAE7C');
</script></head>
<body class="post-template-default single single-post postid-219 single-format-standard">
<div id="wrapper" class="hfeed">
<div id="header">
<div id="masthead">
<div id="branding" role="banner">
<div id="site-title">
<span>
<a href="https://www.earlevel.com/main" rel="home" title="EarLevel Engineering">
<img src="https://www.earlevel.com/main/wp-content/uploads/2010/11/EarLevel-logo.png" alt="EarLevel Engineering" />
</a>
</span>
</div>
<div id="site-description">Practical digital audio signal processing</div>
<img src="https://www.earlevel.com/main/wp-content/themes/twentyten-child/images/EarLevel-header.png" width="940" height="198" alt="" />
</div><!-- #branding -->
<div id="access" role="navigation">
<div class="skip-link screen-reader-text"><a href="#content" title="Skip to content">Skip to content</a></div>
<div class="menu-header"><ul id="menu-main" class="menu"><li id="menu-item-431" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-home menu-item-431"><a href="http://www.earlevel.com/main/">Home</a></li>
<li id="menu-item-432" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-432"><a href="https://www.earlevel.com/main/about/">About</a></li>
<li id="menu-item-435" class="menu-item menu-item-type-taxonomy menu-item-object-category current-post-ancestor current-menu-parent current-post-parent menu-item-435"><a href="https://www.earlevel.com/main/category/widgets/">Widgets</a></li>
</ul></div> </div><!-- #access -->
</div><!-- #masthead -->
</div><!-- #header -->
<div id="main">
<div id="container">
<div id="content" role="main">
<div id="nav-above" class="navigation">
<div class="nav-previous"><a href="https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/" rel="prev"><span class="meta-nav">&larr;</span> Biquad calculator v2</a></div>
<div class="nav-next"><a href="https://www.earlevel.com/main/2014/01/09/more-about-source-code/" rel="next">More about source code <span class="meta-nav">&rarr;</span></a></div>
</div><!-- #nav-above -->
<div id="post-219" class="post-219 post type-post status-publish format-standard hentry category-biquads category-digital-audio category-filters category-iir-filters category-widgets">
<h1 class="entry-title">Pole-Zero placement v2</h1>
<div class="entry-meta">
<span class="meta-prep meta-prep-author">Posted on</span> <a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/" title="5:50 pm" rel="bookmark"><span class="entry-date">October 28, 2013</span></a> <span class="meta-sep">by</span> <span class="author vcard"><a class="url fn n" href="https://www.earlevel.com/main/author/codehead/" title="View all posts by Nigel Redmon">Nigel Redmon</a></span> </div><!-- .entry-meta -->
<div class="entry-content">
<div id="magnitude_polezero2" style="width:600px; height:200px;"></div>
</p>
<table style="border: none; border-collapse: collapse; padding: 0px 0px; vertical-align: center;">
<tr style="border:none; border-collapse: collapse; padding: 0px 0px;">
<td style="border:none; border-collapse: collapse; padding: 0px 0px; width: 228px; height: 228px;">
<canvas id="zplane_polezero2" width="228" height="228"></canvas>
</td>
<td style="border: none; border-collapse: collapse; padding: 0px 0px; vertical-align: top">
<table style="border: none; border-collapse: collapse; padding: 0px 0px;">
<tr>
<td rowspan="2" style="border:none; border-collapse: collapse; padding: 0px 6px; vertical-align: middle; width: 40px;">
<label><input type="checkbox" id="polePair_polezero2" checked="true" onchange='updateSliderNames(); updateValues(); updateZplane();'> Pair </label>
</td>
<td style="border: none; border-collapse: collapse; padding: 0px 0px; width: 80px;">
<input id="pole1_polezero2_field" value="0.5" style="text-align:right;width:4em;margin: 0 6px 6px 0;" type="text" onchange='updateSlider("pole1_polezero2"); updateZplane();' />
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px; width: 150px;">
<input type="range" min="0" max="200" value="100" id="pole1_polezero2" onchange='updateValue("pole1_polezero2"); updateZplane();' oninput='updateValue("pole1_polezero2"); updateZplane();'>
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
<output id="p1_name_polezero2" rows="1" cols="12" readonly="readonly">Pole mag</output>
</td>
</tr>
<tr>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
<input id="pole2_polezero2_field" value="0.25" style="text-align:right;width:4em;margin: 0 6px 6px 0;" type="text" onchange='updateSlider("pole2_polezero2"); updateZplane();' />
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
<input type="range" min="0" max="200" value="50" id="pole2_polezero2" onchange='updateValue("pole2_polezero2"); updateZplane();' oninput='updateValue("pole2_polezero2"); updateZplane();'>
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
<output id="p2_name_polezero2" rows="1" cols="12" readonly="readonly">Pole angle</output>
</td>
</tr>
<tr>
<td rowspan="2" style="border:none; border-collapse: collapse; padding: 0px 6px; vertical-align: middle;">
<label><input type="checkbox" id="zeroPair_polezero2" checked="true" onchange='updateSliderNames(); updateValues(); updateZplane();'> Pair </label>
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
<input id="zero1_polezero2_field" value="1" style="text-align:right;width:4em;margin: 0 6px 6px 0;" type="text" onchange='updateSlider("zero1_polezero2"); updateZplane();' />
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
<input type="range" min="0" max="200" value="200" id="zero1_polezero2" onchange='updateValue("zero1_polezero2"); updateZplane();' oninput='updateValue("zero1_polezero2"); updateZplane();'>
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
<output id="z1_name_polezero2" rows="1" cols="12" readonly="readonly">Zero mag</output>
</td>
</tr>
<tr>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
<input id="zero2_polezero2_field" value="1" style="text-align:right;width:4em;margin: 0 6px 6px 0;" type="text" onchange='updateSlider("zero2_polezero2"); updateZplane();' />
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
<input type="range" min="0" max="200" value="200" id="zero2_polezero2" onchange='updateValue("zero2_polezero2"); updateZplane();' oninput='updateValue("zero2_polezero2"); updateZplane();'>
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
<output id="z2_name_polezero2" rows="1" cols="12" readonly="readonly">Zero angle</output>
</td>
</tr>
<tr>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
<input id="Fs_polezero2" value="44100" style="text-align:right;width:4em;margin: 0 6px 6px 0;" type="text" onchange='updateZplane();'>
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
Sample rate (Hz)
</td>
</tr>
<tr>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
<select id="plotType_polezero2" onchange='updateZplane();'><option value="linear">linear</option><option value="log">log</option></select>
</td>
<td style="border:none; border-collapse: collapse; padding: 0px 0px;">
Plot
</td>
</tr>
</table>
</td>
</tr>
</table>
<p><textarea id="coefsList_polezero2" rows="8" cols="64" readonly="readonly"></textarea></p>
<h3>A new pole-zero calculator</h3>
<p>An JavaScript remake of the old Java-based pole-zero placement <a href="/main/2003/02/27/pole-zero-placement/">applet</a>—visit that page for tips on pole-zero locations for standard biquads. The main additions are input fields for precision pole-zero placement, and an option to display the response with a log frequency scale.</p>
<p>The basic idea is that poles blow, zeros suck. Think of poles as controlling a frequency-dependent feedback or resonance—the impulse response of a pole inside the unit circle decays, while one outside is like runaway feedback (think of a mic feeding back into a loudspeaker). A pole on the unit circle gives a sustained oscillation (but watch out for numerical errors—keep your poles inside the unit circle, typically). Zeros absorb a particular frequency; when on the unit circle, they absorb the corresponding frequency completely.</p>
<p>So, poles push the frequency response up around their corresponding frequency, and zeros pull down around theirs. Keep in mind that the frequency response graph is normalized, just as the filter coefficients are. So, while a pole pushes up the response, it appears as though all other frequencies are being pushed down instead. Of course, normalization is important in practical application, but be aware of it when visualizing how poles and zeros interact.<br />
<script type="text/javascript" src="/scripts/utils/flotr2.min.js"></script><script type="text/javascript" src="/scripts/widgets/20131028/PoleZero2.js"></script></p>
</div><!-- .entry-content -->
<div class="entry-utility">
This entry was posted in <a href="https://www.earlevel.com/main/category/digital-audio/filters/iir-filters/biquads/" rel="category tag">Biquads</a>, <a href="https://www.earlevel.com/main/category/digital-audio/" rel="category tag">Digital Audio</a>, <a href="https://www.earlevel.com/main/category/digital-audio/filters/" rel="category tag">Filters</a>, <a href="https://www.earlevel.com/main/category/digital-audio/filters/iir-filters/" rel="category tag">IIR Filters</a>, <a href="https://www.earlevel.com/main/category/widgets/" rel="category tag">Widgets</a>. Bookmark the <a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/" title="Permalink to Pole-Zero placement v2" rel="bookmark">permalink</a>. </div><!-- .entry-utility -->
</div><!-- #post-219 -->
<div id="nav-below" class="navigation">
<div class="nav-previous"><a href="https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/" rel="prev"><span class="meta-nav">&larr;</span> Biquad calculator v2</a></div>
<div class="nav-next"><a href="https://www.earlevel.com/main/2014/01/09/more-about-source-code/" rel="next">More about source code <span class="meta-nav">&rarr;</span></a></div>
</div><!-- #nav-below -->
<div id="comments">
<h3 id="comments-title">
13 Responses to <em>Pole-Zero placement v2</em> </h3>
<ol class="commentlist">
<li class="comment even thread-even depth-1" id="li-comment-25077">
<div id="comment-25077">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/622ea3008b2bbcd4dd67d966b2c622cb?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/622ea3008b2bbcd4dd67d966b2c622cb?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn">Piezo</cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-25077">
April 17, 2014 at 3:47 am </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>I found a very nice web app showing interactive filter design with direct visualization in frequency domain and z-domain ( poles and zeros ) :<br />
<a href="http://www.micromodeler.com/dsp/" rel="nofollow ugc">http://www.micromodeler.com/dsp/</a></p>
<p>This is intended for embedded dsp applications, but it&#8217;s still a incredibly useful pedagogical material.</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=25077#respond' data-commentid="25077" data-postid="219" data-belowelement="comment-25077" data-respondelement="respond" aria-label='Reply to Piezo'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
<ul class="children">
<li class="comment byuser comment-author-codehead bypostauthor odd alt depth-2" id="li-comment-25084">
<div id="comment-25084">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/eac3fa758886f87242004fc60f9a6d40?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/eac3fa758886f87242004fc60f9a6d40?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn">Nigel Redmon</cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-25084">
April 17, 2014 at 10:13 am </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>Nice! Thanks</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=25084#respond' data-commentid="25084" data-postid="219" data-belowelement="comment-25084" data-respondelement="respond" aria-label='Reply to Nigel Redmon'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
</li><!-- #comment-## -->
</ul><!-- .children -->
</li><!-- #comment-## -->
<li class="comment even thread-odd thread-alt depth-1" id="li-comment-27127">
<div id="comment-27127">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/d69c56ac6c3cd947d49a13a6df3caeca?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/d69c56ac6c3cd947d49a13a6df3caeca?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn"><a href='http://www.patchmorpher.com' rel='external nofollow ugc' class='url'>Richard</a></cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-27127">
July 4, 2014 at 9:26 am </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>Hi there,</p>
<p>Very nice applet.</p>
<p>Any chance you could add the phase graph too? i.e.: atan(Im(H)/Re(H)). I&#8217;d like to get a better intuitive idea of how that works.</p>
<p>(And then, for a future version, could we manually select the number of poles &amp; zeros? Add support for all-pass filters&#8230; :o)</p>
<p>Many thanks,<br />
Richard.</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=27127#respond' data-commentid="27127" data-postid="219" data-belowelement="comment-27127" data-respondelement="respond" aria-label='Reply to Richard'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
<ul class="children">
<li class="comment byuser comment-author-codehead bypostauthor odd alt depth-2" id="li-comment-27451">
<div id="comment-27451">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/eac3fa758886f87242004fc60f9a6d40?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/eac3fa758886f87242004fc60f9a6d40?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn">Nigel Redmon</cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-27451">
July 23, 2014 at 3:56 pm </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>Hi Richard. I&#8217;ve thought many times about some of these features, and as you noted, one leads to another, and the only sound solution would be to go into the business of a making a commercial filter design software package, and I&#8217;d be heading far off track from what I&#8217;m trying to do&#8230;</p>
<p>The phase plot is the most obvious, but in the end we&#8217;ve got a second order filter, for which you can look up the unexciting phase characteristics elsewhere, and they are simply an accepted byproduct of this type of filter. (That is, the parametric EQs in your analog mixing console and their digital equivalents in your DAW do the same thing—do you demand to see their phase response before purchasing? No, because you accept what that type of filter gives you.) Now, it would be very helpful as you add some of the other things you mentioned—more poles and zeros, all pass filter&#8230;then you&#8217;re back in the business of making a filter design package. 😉</p>
<p>Nigel</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=27451#respond' data-commentid="27451" data-postid="219" data-belowelement="comment-27451" data-respondelement="respond" aria-label='Reply to Nigel Redmon'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
</li><!-- #comment-## -->
</ul><!-- .children -->
</li><!-- #comment-## -->
<li class="comment even thread-even depth-1" id="li-comment-38218">
<div id="comment-38218">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/634b50775d1b4d63feac21a6e413c0ec?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/634b50775d1b4d63feac21a6e413c0ec?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn">Antony</cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-38218">
April 15, 2015 at 7:36 am </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>This tool seems to be getting the signs for b1 and b2 the wrong way round, although that depends on how you write your equation;<br />
In your other material you write y[n] = &#8230;. &#8211; b1 * y[n-1] &#8211; b2 * y[n-2]<br />
In that case the signs are wrong, or rather, inconsistent with how you write the direct forms.</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=38218#respond' data-commentid="38218" data-postid="219" data-belowelement="comment-38218" data-respondelement="respond" aria-label='Reply to Antony'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
<ul class="children">
<li class="comment byuser comment-author-codehead bypostauthor odd alt depth-2" id="li-comment-38232">
<div id="comment-38232">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/eac3fa758886f87242004fc60f9a6d40?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/eac3fa758886f87242004fc60f9a6d40?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn">Nigel Redmon</cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-38232">
April 15, 2015 at 5:05 pm </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>Thank you for catching that, Anthony. While I was at it, I improved the log tick value scaling. I also took the opportunity to restore continuous update on slider movement (broken when Safari and Chrome fixed their errors in HTML5 interpretation).</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=38232#respond' data-commentid="38232" data-postid="219" data-belowelement="comment-38232" data-respondelement="respond" aria-label='Reply to Nigel Redmon'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
</li><!-- #comment-## -->
</ul><!-- .children -->
</li><!-- #comment-## -->
<li class="comment even thread-odd thread-alt depth-1" id="li-comment-42410">
<div id="comment-42410">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/3b6df7e42f91dd74ea4dd14ee367a0d6?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/3b6df7e42f91dd74ea4dd14ee367a0d6?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn">mclaren</cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-42410">
September 30, 2015 at 3:41 am </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>Clearest description I&#8217;ve ever read of poles and zeroes in the real world. Excellent!</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=42410#respond' data-commentid="42410" data-postid="219" data-belowelement="comment-42410" data-respondelement="respond" aria-label='Reply to mclaren'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
</li><!-- #comment-## -->
<li class="comment odd alt thread-even depth-1" id="li-comment-45481">
<div id="comment-45481">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/89240171a6427ef4a55d6023555ef1a7?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/89240171a6427ef4a55d6023555ef1a7?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn">Matthijs van Delft</cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-45481">
April 15, 2016 at 12:58 am </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>To get a more complete example it would be great is the cut off frequency would be part of the parameters. It would also be very nice if the frequency on the -3dB point of the graph would be readable in some way.</p>
<p>Nevertheless, thanks for the great explaination!</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=45481#respond' data-commentid="45481" data-postid="219" data-belowelement="comment-45481" data-respondelement="respond" aria-label='Reply to Matthijs van Delft'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
<ul class="children">
<li class="comment byuser comment-author-codehead bypostauthor even depth-2" id="li-comment-45485">
<div id="comment-45485">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/eac3fa758886f87242004fc60f9a6d40?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/eac3fa758886f87242004fc60f9a6d40?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn">Nigel Redmon</cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-45485">
April 15, 2016 at 9:23 am </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>Good idea, Matthijs. Ill keep that in mind for the next time I have a chance to improve things.</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=45485#respond' data-commentid="45485" data-postid="219" data-belowelement="comment-45485" data-respondelement="respond" aria-label='Reply to Nigel Redmon'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
</li><!-- #comment-## -->
</ul><!-- .children -->
</li><!-- #comment-## -->
<li class="comment odd alt thread-odd thread-alt depth-1" id="li-comment-51389">
<div id="comment-51389">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/317fdbefb209ca4b1cc713cc5294da84?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/317fdbefb209ca4b1cc713cc5294da84?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn">Eugene</cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-51389">
December 26, 2016 at 9:41 am </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>Here &#8220;a&#8221; coefficients represents numerator, right? In most sources b is a numerator.</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=51389#respond' data-commentid="51389" data-postid="219" data-belowelement="comment-51389" data-respondelement="respond" aria-label='Reply to Eugene'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
<ul class="children">
<li class="comment byuser comment-author-codehead bypostauthor even depth-2" id="li-comment-51812">
<div id="comment-51812">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/eac3fa758886f87242004fc60f9a6d40?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/eac3fa758886f87242004fc60f9a6d40?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn">Nigel Redmon</cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-51812">
January 3, 2017 at 10:41 pm </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>Hi Eugene—asked and answered a few times in comments on the site, but since you bring it up, Ill put together a short article explaining the choice. Short version: In the internet age, I dont doubt that <em>b</em>-in-the-numerator has become most common. But Im not going to edit articles going back to 2003, so yes, <em>a</em> in the numerator here 😉</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=51812#respond' data-commentid="51812" data-postid="219" data-belowelement="comment-51812" data-respondelement="respond" aria-label='Reply to Nigel Redmon'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
</li><!-- #comment-## -->
</ul><!-- .children -->
</li><!-- #comment-## -->
<li class="comment odd alt thread-even depth-1" id="li-comment-64617">
<div id="comment-64617">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/d2d45c33273533bf50c9e9cbedd93860?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/d2d45c33273533bf50c9e9cbedd93860?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn">dino</cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-64617">
June 17, 2018 at 10:35 am </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>How do you calculate the coefficients from the poles to get the frequency response? I know to use the quadratic formula to get the opposite so I naively attempted making a quadratic using the poles but couldn&#8217;t get the same result as the calculator. I&#8217;m guessing it&#8217;s something obvious I&#8217;m missing but I couldn&#8217;t find anywhere that shows it being done other than an uncommented line of code&#8230;</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=64617#respond' data-commentid="64617" data-postid="219" data-belowelement="comment-64617" data-respondelement="respond" aria-label='Reply to dino'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
<ul class="children">
<li class="comment byuser comment-author-codehead bypostauthor even depth-2" id="li-comment-64619">
<div id="comment-64619">
<div class="comment-author vcard">
<img alt='' src='https://secure.gravatar.com/avatar/eac3fa758886f87242004fc60f9a6d40?s=40&#038;d=mm&#038;r=g' srcset='https://secure.gravatar.com/avatar/eac3fa758886f87242004fc60f9a6d40?s=80&#038;d=mm&#038;r=g 2x' class='avatar avatar-40 photo' height='40' width='40' /> <cite class="fn">Nigel Redmon</cite> <span class="says">says:</span> </div><!-- .comment-author .vcard -->
<div class="comment-meta commentmetadata"><a href="https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/#comment-64619">
June 17, 2018 at 11:14 am </a>
</div><!-- .comment-meta .commentmetadata -->
<div class="comment-body"><p>On this one, I&#8217;m calculating the frequency response directly from the locations of the poles and zeros. But since I also calculated and display the coefficients, of course it could have been derived from the coefficients (as in <a href="/main/2016/12/01/evaluating-filter-frequency-response/" rel="nofollow">Evaluating filter frequency response</a>).</p>
<p>You can look at the Javascript source with your browsers developer tools, or directly <a href="/scripts/widgets/20131028/PoleZero2.js" rel="nofollow">here</a>. The function getBiquadCoefs_polezero2 converts from pole-zero to coefficients (OK, I see I multiply and add a term I&#8217;ve already determined to be zero, but I do these things quickly.)</p>
</div>
<div class="reply">
<a rel='nofollow' class='comment-reply-link' href='https://www.earlevel.com/main/2013/10/28/pole-zero-placement-v2/?replytocom=64619#respond' data-commentid="64619" data-postid="219" data-belowelement="comment-64619" data-respondelement="respond" aria-label='Reply to Nigel Redmon'>Reply</a> </div><!-- .reply -->
</div><!-- #comment-## -->
</li><!-- #comment-## -->
</ul><!-- .children -->
</li><!-- #comment-## -->
</ol>
<div id="respond" class="comment-respond">
<h3 id="reply-title" class="comment-reply-title">Leave a Reply <small><a rel="nofollow" id="cancel-comment-reply-link" href="/main/2013/10/28/pole-zero-placement-v2/#respond" style="display:none;">Cancel reply</a></small></h3><form action="https://www.earlevel.com/main/wp-comments-post.php" method="post" id="commentform" class="comment-form"><p class="comment-notes"><span id="email-notes">Your email address will not be published.</span> Required fields are marked <span class="required">*</span></p><p class="comment-form-comment"><label for="comment">Comment</label> <textarea id="comment" name="comment" cols="45" rows="8" maxlength="65525" required="required"></textarea></p><p class="comment-form-author"><label for="author">Name <span class="required">*</span></label> <input id="author" name="author" type="text" value="" size="30" maxlength="245" required='required' /></p>
<p class="comment-form-email"><label for="email">Email <span class="required">*</span></label> <input id="email" name="email" type="text" value="" size="30" maxlength="100" aria-describedby="email-notes" required='required' /></p>
<p class="comment-form-url"><label for="url">Website</label> <input id="url" name="url" type="text" value="" size="30" maxlength="200" /></p>
<p class="comment-form-cookies-consent"><input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes" /> <label for="wp-comment-cookies-consent">Save my name, email, and website in this browser for the next time I comment.</label></p>
<p class="form-submit"><input name="submit" type="submit" id="submit" class="submit" value="Post Comment" /> <input type='hidden' name='comment_post_ID' value='219' id='comment_post_ID' />
<input type='hidden' name='comment_parent' id='comment_parent' value='0' />
</p><p style="display: none;"><input type="hidden" id="akismet_comment_nonce" name="akismet_comment_nonce" value="1db742a7b0" /></p><input type="hidden" id="ak_js" name="ak_js" value="85"/><textarea name="ak_hp_textarea" cols="45" rows="8" maxlength="100" style="display: none !important;"></textarea></form> </div><!-- #respond -->
</div><!-- #comments -->
</div><!-- #content -->
</div><!-- #container -->
<div id="primary" class="widget-area" role="complementary">
<ul class="xoxo">
<li id="search-2" class="widget-container widget_search"><form role="search" method="get" id="searchform" class="searchform" action="https://www.earlevel.com/main/">
<div>
<label class="screen-reader-text" for="s">Search for:</label>
<input type="text" value="" name="s" id="s" />
<input type="submit" id="searchsubmit" value="Search" />
</div>
</form></li> <li id="recent-posts-2" class="widget-container widget_recent_entries"> <h3 class="widget-title">Recent Posts</h3> <ul>
<li>
<a href="https://www.earlevel.com/main/2021/04/08/time-resolution-in-digital-audio/">Time resolution in digital audio</a>
</li>
<li>
<a href="https://www.earlevel.com/main/2020/10/29/amplitude-modulation-principles-and-interactive-widget-video/">Amplitude Modulation Principles and Interactive Widget video</a>
</li>
<li>
<a href="https://www.earlevel.com/main/2020/10/29/amplitude-modulation-deep-and-fast-video/">Amplitude Modulation Deep and Fast video</a>
</li>
<li>
<a href="https://www.earlevel.com/main/2020/10/02/am-widget/">AM widget</a>
</li>
<li>
<a href="https://www.earlevel.com/main/2020/01/04/further-thoughts-on-wave-table-oscillators/">Further thoughts on wave table oscillators</a>
</li>
<li>
<a href="https://www.earlevel.com/main/2019/04/30/waveutils-updated/">WaveUtils updated</a>
</li>
<li>
<a href="https://www.earlevel.com/main/2019/04/28/wavetableosc-optimized/">WaveTableOsc optimized</a>
</li>
<li>
<a href="https://www.earlevel.com/main/2019/04/26/how-i-write-code/">How I write code</a>
</li>
</ul>
</li><li id="archives-2" class="widget-container widget_archive"><h3 class="widget-title">Archives</h3> <ul>
<li><a href='https://www.earlevel.com/main/2021/04/'>April 2021</a></li>
<li><a href='https://www.earlevel.com/main/2020/10/'>October 2020</a></li>
<li><a href='https://www.earlevel.com/main/2020/01/'>January 2020</a></li>
<li><a href='https://www.earlevel.com/main/2019/04/'>April 2019</a></li>
<li><a href='https://www.earlevel.com/main/2018/09/'>September 2018</a></li>
<li><a href='https://www.earlevel.com/main/2017/08/'>August 2017</a></li>
<li><a href='https://www.earlevel.com/main/2017/05/'>May 2017</a></li>
<li><a href='https://www.earlevel.com/main/2017/01/'>January 2017</a></li>
<li><a href='https://www.earlevel.com/main/2016/12/'>December 2016</a></li>
<li><a href='https://www.earlevel.com/main/2016/09/'>September 2016</a></li>
<li><a href='https://www.earlevel.com/main/2016/02/'>February 2016</a></li>
<li><a href='https://www.earlevel.com/main/2015/09/'>September 2015</a></li>
<li><a href='https://www.earlevel.com/main/2015/01/'>January 2015</a></li>
<li><a href='https://www.earlevel.com/main/2014/03/'>March 2014</a></li>
<li><a href='https://www.earlevel.com/main/2014/01/'>January 2014</a></li>
<li><a href='https://www.earlevel.com/main/2013/10/'>October 2013</a></li>
<li><a href='https://www.earlevel.com/main/2013/06/'>June 2013</a></li>
<li><a href='https://www.earlevel.com/main/2013/05/'>May 2013</a></li>
<li><a href='https://www.earlevel.com/main/2013/04/'>April 2013</a></li>
<li><a href='https://www.earlevel.com/main/2013/03/'>March 2013</a></li>
<li><a href='https://www.earlevel.com/main/2013/02/'>February 2013</a></li>
<li><a href='https://www.earlevel.com/main/2012/12/'>December 2012</a></li>
<li><a href='https://www.earlevel.com/main/2012/11/'>November 2012</a></li>
<li><a href='https://www.earlevel.com/main/2012/05/'>May 2012</a></li>
<li><a href='https://www.earlevel.com/main/2012/03/'>March 2012</a></li>
<li><a href='https://www.earlevel.com/main/2011/01/'>January 2011</a></li>
<li><a href='https://www.earlevel.com/main/2010/12/'>December 2010</a></li>
<li><a href='https://www.earlevel.com/main/2010/11/'>November 2010</a></li>
<li><a href='https://www.earlevel.com/main/2007/07/'>July 2007</a></li>
<li><a href='https://www.earlevel.com/main/2003/03/'>March 2003</a></li>
<li><a href='https://www.earlevel.com/main/2003/02/'>February 2003</a></li>
<li><a href='https://www.earlevel.com/main/2002/08/'>August 2002</a></li>
<li><a href='https://www.earlevel.com/main/1997/01/'>January 1997</a></li>
<li><a href='https://www.earlevel.com/main/1996/11/'>November 1996</a></li>
<li><a href='https://www.earlevel.com/main/1996/10/'>October 1996</a></li>
<li><a href='https://www.earlevel.com/main/1996/08/'>August 1996</a></li>
<li><a href='https://www.earlevel.com/main/1996/07/'>July 1996</a></li>
</ul>
</li><li id="categories-2" class="widget-container widget_categories"><h3 class="widget-title">Categories</h3> <ul>
<li class="cat-item cat-item-4"><a href="https://www.earlevel.com/main/category/digital-audio/">Digital Audio</a> (61)
<ul class='children'>
<li class="cat-item cat-item-11"><a href="https://www.earlevel.com/main/category/digital-audio/aliasing/">Aliasing</a> (9)
</li>
<li class="cat-item cat-item-14"><a href="https://www.earlevel.com/main/category/digital-audio/convolution/">Convolution</a> (3)
</li>
<li class="cat-item cat-item-5"><a href="https://www.earlevel.com/main/category/digital-audio/dither-digital-audio/">Dither</a> (7)
</li>
<li class="cat-item cat-item-8"><a href="https://www.earlevel.com/main/category/digital-audio/filters/">Filters</a> (23)
<ul class='children'>
<li class="cat-item cat-item-22"><a href="https://www.earlevel.com/main/category/digital-audio/filters/dc-blocker/">DC Blocker</a> (1)
</li>
<li class="cat-item cat-item-19"><a href="https://www.earlevel.com/main/category/digital-audio/filters/fir-filters/">FIR Filters</a> (7)
</li>
<li class="cat-item cat-item-9"><a href="https://www.earlevel.com/main/category/digital-audio/filters/iir-filters/">IIR Filters</a> (17)
<ul class='children'>
<li class="cat-item cat-item-21"><a href="https://www.earlevel.com/main/category/digital-audio/filters/iir-filters/biquads/">Biquads</a> (11)
</li>
</ul>
</li>
</ul>
</li>
<li class="cat-item cat-item-16"><a href="https://www.earlevel.com/main/category/digital-audio/fourier/">Fourier</a> (3)
<ul class='children'>
<li class="cat-item cat-item-17"><a href="https://www.earlevel.com/main/category/digital-audio/fourier/fft/">FFT</a> (3)
</li>
</ul>
</li>
<li class="cat-item cat-item-15"><a href="https://www.earlevel.com/main/category/digital-audio/impulse-response/">Impulse Response</a> (5)
</li>
<li class="cat-item cat-item-10"><a href="https://www.earlevel.com/main/category/digital-audio/jitter/">Jitter</a> (2)
</li>
<li class="cat-item cat-item-20"><a href="https://www.earlevel.com/main/category/digital-audio/oscillators/">Oscillators</a> (12)
<ul class='children'>
<li class="cat-item cat-item-24"><a href="https://www.earlevel.com/main/category/digital-audio/oscillators/wavetable-oscillators/">Wavetable Oscillators</a> (12)
</li>
</ul>
</li>
<li class="cat-item cat-item-12"><a href="https://www.earlevel.com/main/category/digital-audio/phase/">Phase</a> (2)
</li>
<li class="cat-item cat-item-13"><a href="https://www.earlevel.com/main/category/digital-audio/reverb/">Reverb</a> (2)
</li>
<li class="cat-item cat-item-6"><a href="https://www.earlevel.com/main/category/digital-audio/sample-rate-conversion/">Sample Rate Conversion</a> (10)
</li>
<li class="cat-item cat-item-7"><a href="https://www.earlevel.com/main/category/digital-audio/sampling-theory/">Sampling Theory</a> (6)
</li>
</ul>
</li>
<li class="cat-item cat-item-33"><a href="https://www.earlevel.com/main/category/effects/">Effects</a> (1)
<ul class='children'>
<li class="cat-item cat-item-35"><a href="https://www.earlevel.com/main/category/effects/guitar-amp-simulation/">Guitar Amp Simulation</a> (1)
</li>
</ul>
</li>
<li class="cat-item cat-item-25"><a href="https://www.earlevel.com/main/category/envelope-generators/">Envelope Generators</a> (5)
</li>
<li class="cat-item cat-item-38"><a href="https://www.earlevel.com/main/category/math/">Math</a> (5)
<ul class='children'>
<li class="cat-item cat-item-39"><a href="https://www.earlevel.com/main/category/math/amplitude-modulation/">Amplitude Modulation</a> (3)
</li>
</ul>
</li>
<li class="cat-item cat-item-18"><a href="https://www.earlevel.com/main/category/midi/">MIDI</a> (2)
</li>
<li class="cat-item cat-item-26"><a href="https://www.earlevel.com/main/category/source-code/">Source Code</a> (10)
</li>
<li class="cat-item cat-item-23"><a href="https://www.earlevel.com/main/category/synthesizers/">Synthesizers</a> (9)
</li>
<li class="cat-item cat-item-1"><a href="https://www.earlevel.com/main/category/uncategorized/">Uncategorized</a> (3)
</li>
<li class="cat-item cat-item-29"><a href="https://www.earlevel.com/main/category/video/">Video</a> (6)
</li>
<li class="cat-item cat-item-30"><a href="https://www.earlevel.com/main/category/widgets/">Widgets</a> (13)
</li>
</ul>
</li> </ul>
</div><!-- #primary .widget-area -->
</div><!-- #main -->
<div id="footer" role="contentinfo">
<div id="colophon">
<div id="site-info">
<a href="https://www.earlevel.com/main/" title="EarLevel Engineering" rel="home">
EarLevel Engineering </a>
</div><!-- #site-info -->
<div id="site-generator">
</div><!-- #site-generator -->
</div><!-- #colophon -->
</div><!-- #footer -->
</div><!-- #wrapper -->
<script type='text/javascript' src='https://www.earlevel.com/main/wp-includes/js/comment-reply.min.js?ver=5.3.8'></script>
<script type='text/javascript' src='https://www.earlevel.com/main/wp-includes/js/wp-embed.min.js?ver=5.3.8'></script>
<script async="async" type='text/javascript' src='https://www.earlevel.com/main/wp-content/plugins/akismet/_inc/form.js?ver=4.1.9'></script>
</body>
</html>

96
index_old.html 100644
Wyświetl plik

@ -0,0 +1,96 @@
<!DOCTYPE html>
<!-- https://stackoverflow.com/questions/42720488/d3-v4-drag-line-chart-with-x-and-y-axes -->
<!-- Thanks to Mark - https://stackoverflow.com/users/16363/mark -->
<svg width="500" height="350"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
// make initial poles and zeros state
let points = d3.range(1, 10).map(function(i) {
return [0, 50 + Math.random() * (height - 100)];
});
// generate x and y axis ranges
var x = d3.scaleLinear()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
let drag = d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended);
svg.append('rect')
.attr('class', 'zoom')
.attr('cursor', 'move')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(points, function(d) { return d[0]; }));
y.domain(d3.extent(points, function(d) { return d[1]; }));
focus.selectAll('circle')
.data(points)
.enter()
.append('circle')
.attr('r', 5.0)
.attr('cx', function(d) { return x(d[0]); })
.attr('cy', function(d) { return y(d[1]); })
.style('cursor', 'pointer')
.style('stroke', 'black')
.style('fill', 'blue');
focus.selectAll('circle')
.call(drag);
focus.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height/2 + ')')
.call(xAxis.ticks(0));
focus.append('g')
.attr('class', 'axis axis--y')
.attr('transform', 'translate(' + width/2 + ',0)')
.call(yAxis.ticks(0));
focus.append('g')
.append('circle')
.style('stroke', 'red')
.style('fill', 'none')
.attr('r', 100.0)
.attr('transform', 'translate(' + width/2 + ',' + height/2 + ')');
function dragstarted(d) {
d3.select(this).raise().classed('active', true);
}
function dragged(d) {
d[0] = x.invert(d3.event.x);
d[1] = y.invert(d3.event.y);
d3.select(this)
.attr('cx', x(d[0]))
.attr('cy', y(d[1]))
}
function dragended(d) {
d3.select(this).classed('active', false);
}
</script>

400
script2.js 100644
Wyświetl plik

@ -0,0 +1,400 @@
/*
Pole-Zero Plot
copyright 2013 Nigel Redmon
2015-04-15 improved log scaling x tick values, flipped sign of b1,b2
*/
jQuery(document).ready(function( $ ) {
updateZplane();
});
function updateValues() {
updateValue("pole1_polezero2");
updateValue("pole2_polezero2");
updateValue("zero1_polezero2");
updateValue("zero2_polezero2");
}
function updateValue(which) {
var val = document.getElementById(which).value;
var paired;
if (which.substr(0, 4) == "pole")
paired = document.getElementById("polePair_polezero2").checked;
else
paired = document.getElementById("zeroPair_polezero2").checked;
if (paired)
val /= 200;
else
val = val / 100 - 1;
document.getElementById(which + "_field").value = val;
}
function updateSlider(which) {
var paired;
if (which.substr(0, 4) == "pole")
paired = document.getElementById("polePair_polezero2").checked;
else
paired = document.getElementById("zeroPair_polezero2").checked;
var val = parseFloat(document.getElementById(which + "_field").value);
// enforce limits
var changed;
if (changed = (val > 1))
val = 1;
if (paired) {
if (changed = (val < 0))
val = 0;
}
else {
if (changed = (val < -1))
val = -1;
}
if (changed)
document.getElementById(which + "_field").value = val;
// update slider
if (paired)
val *= 200;
else
val = (val + 1) * 100;
document.getElementById(which).value = val;
}
function updateSliderNames() {
if (document.getElementById("polePair_polezero2").checked) {
document.getElementById("p1_name_polezero2").value = "Pole mag";
document.getElementById("p2_name_polezero2").value = "Pole angle";
}
else {
document.getElementById("p1_name_polezero2").value = "Pole 1";
document.getElementById("p2_name_polezero2").value = "Pole 2";
}
if (document.getElementById("zeroPair_polezero2").checked) {
document.getElementById("z1_name_polezero2").value = "Zero mag";
document.getElementById("z2_name_polezero2").value = "Zero angle";
}
else {
document.getElementById("z1_name_polezero2").value = "Zero 1";
document.getElementById("z2_name_polezero2").value = "Zero 2";
}
}
function updateZplane() {
var p1 = document.getElementById("pole1_polezero2").value / 200;
var p2 = document.getElementById("pole2_polezero2").value / 200;
var z1 = document.getElementById("zero1_polezero2").value / 200;
var z2 = document.getElementById("zero2_polezero2").value / 200;
var poles = [];
var zeros = [];
var polesPaired = document.getElementById("polePair_polezero2").checked;
var zerosPaired = document.getElementById("zeroPair_polezero2").checked;
var x1, y1;
if (polesPaired) {
// complex conjugate poles and zeros
x1 = Math.cos(p2 * Math.PI) * p1;
if (Math.abs(x1) < 1E-14)
x1 = 0;
y1 = Math.sin(p2 * Math.PI) * p1;
if (Math.abs(y1) < 1E-14)
y1 = 0;
poles.push([x1, y1]);
poles.push([x1, -y1]);
}
else {
poles.push([p1 * 2 - 1, 0]);
poles.push([p2 * 2 - 1, 0]);
}
if (zerosPaired) {
x1 = Math.cos(z2 * Math.PI) * z1;
if (Math.abs(x1) < 1E-14)
x1 = 0;
y1 = Math.sin(z2 * Math.PI) * z1;
if (Math.abs(y1) < 1E-14)
y1 = 0;
zeros.push([x1, y1]);
zeros.push([x1, -y1]);
}
else {
zeros.push([z1 * 2 - 1, 0]);
zeros.push([z2 * 2 - 1, 0]);
}
setZplane(poles, zeros);
}
function setZplane(poles, zeros) {
var radius = 100; // radius of unit circle
var pSize = 4; // size of pole and zero graphic
var zSize = 4;
var c=document.getElementById("zplane_polezero2");
var ctx=c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
var pad = (c.width - 2 * radius) / 2; // padding on each side
// unit circle
ctx.beginPath();
ctx.strokeStyle="red";
ctx.arc(radius+pad,radius+pad,radius,0,2*Math.PI);
ctx.stroke();
// y axis
ctx.beginPath();
//ctx.lineWidth="1";
ctx.strokeStyle="lightgray";
ctx.moveTo(radius+pad,0);
ctx.lineTo(radius+pad,c.height);
ctx.font = "italic 8px sans-serif";
ctx.fillText("Im", radius+pad+2, pad-2);
// x axis
ctx.moveTo(0,radius+pad);
ctx.lineTo(c.width,radius+pad);
ctx.fillText("Re", radius+radius+pad+2, radius+pad-2);
ctx.stroke(); // Draw it
// poles
ctx.strokeStyle="blue";
var idx;
for (idx = 0; idx < poles.length; idx++) {
var x = radius + Math.round(radius * poles[idx][0]);
var y = radius - Math.round(radius * poles[idx][1]);
ctx.beginPath();
ctx.moveTo(x - pSize + pad, y - pSize + pad);
ctx.lineTo(x + pSize + pad, y + pSize + pad);
ctx.moveTo(x - pSize + pad, y + pSize + pad);
ctx.lineTo(x + pSize + pad, y - pSize + pad);
ctx.stroke();
}
// zeros
for (idx = 0; idx < zeros.length; idx++) {
var x = radius + Math.round(radius * zeros[idx][0]);
var y = radius - Math.round(radius * zeros[idx][1]);
ctx.beginPath();
ctx.arc(x + pad, y + pad, zSize, 0, 2*Math.PI);
ctx.stroke();
}
// calc max and min, because we may not end up sampling at that exact spot later (an issue at high Q, especially log plot)
var polePt = [poles[0][0], poles[0][1]];
if (Math.abs(poles[0][0]) < Math.abs(poles[1][0]))
polePt[0] = poles[1][0];
var poleRadius = Math.sqrt(Math.pow(polePt[0], 2) + Math.pow(polePt[1], 2));
var poleAngle = Math.atan2(polePt[1], polePt[0]);
var zeroPt = [zeros[0][0], zeros[0][1]];
if (Math.abs(zeros[0][0]) < Math.abs(zeros[1][0]))
zeroPt[0] = zeros[1][0];
var zeroRadius = Math.sqrt(Math.pow(zeroPt[0], 2) + Math.pow(zeroPt[1], 2));
var zeroAngle = Math.atan2(zeroPt[1], zeroPt[0]);
// plot response
// on the unit circle, the ratio of products of distances from zeros to the products of distances from poles
var linearPlot = true;
var FsField = document.getElementById("Fs_polezero2");
var Fs = parseFloat(FsField.value);
var angle;
var plotType = document.getElementById("plotType_polezero2");
linearPlot = (plotType.value == "linear");
var mag = [];
// find max gain: at the pole angle, or 0 or pi
var maxAngle = poleAngle;
var magMax = magResponse_polezero2(poleAngle, poles, zeros);
var magAt0 = magResponse_polezero2(0, poles, zeros);
if (magMax < magAt0) {
maxAngle = 0;
magMax = magAt0;
}
var magAtPI = magResponse_polezero2(Math.PI, poles, zeros);
if (magMax < magAtPI) {
maxAngle = Math.PI;
magMax = magAtPI;
}
var magNorm = magMax;
// gain at min
var minAngle = zeroAngle;
var magMin = magResponse_polezero2(zeroAngle, poles, zeros);
if (magMin > magAt0) {
minAngle = Math.PI;
magMin = magAt0;
}
if (magMin > magAtPI) {
minAngle = 0;
magMin = magAtPI;
}
var maxInserted = false, minInserted = false;
var maxPoint = 600;
for (idx = 0; idx <= maxPoint; idx++) {
// step through from 0-pi
if (linearPlot)
angle = Math.PI * idx / maxPoint;
else
angle = Math.exp(Math.log(1 / 0.001) * idx / maxPoint) * 0.001 * Math.PI; // 0.001 to 1, times pi, log scale
// if we've just passed the angle of max (poleAngle) or min (zeroAngle)
// insert that angle before inserting the current angle
// note: this is important, because as the user sweeps the control, max could fall between graph points and make the response appear to jitter
if (!maxInserted) {
if (!linearPlot && (maxAngle < (0.001 * Math.PI)))
maxInserted = true;
else if (angle >= maxAngle) {
maxInserted = true;
mag.push([idx / maxPoint / 2, magMax])
}
}
if (!minInserted) {
if (!linearPlot && (minAngle < (0.001 * Math.PI)))
minInserted = true;
else if (angle >= minAngle) {
minInserted = true;
mag.push([idx / maxPoint / 2, magMin])
}
}
var val = magResponse_polezero2(angle, poles, zeros);
mag.push([idx / maxPoint / 2, val])
// track max
if (val > magNorm)
magNorm = val;
}
// normalize
for (idx = 0; idx < mag.length; idx++)
mag[idx][1] -= magNorm;
// plot
var container = document.getElementById('magnitude_polezero2');
if (linearPlot) {
for (idx = 0; idx < mag.length; idx++)
mag[idx][0] *= Fs;
graph = Flotr.draw(container, [ mag ], { yaxis: { max : 0, min : -120 } });
}
else
graph = Flotr.draw(container, [ mag ], { yaxis: { max : 0, min : -120 }, xaxis: { tickFormatter: nullTickFormatter_polezero2 } });
// show coefficients
var coefsList = "poles at " + poles[0][0];
var temp = poles[0][1];
if (temp != 0)
coefsList += " \xB1 " + Math.abs(temp) + "i\n";
else
coefsList += ", " + poles[1][0] + "\n";
coefsList += "zeros at " + zeros[0][0];
var temp = zeros[0][1];
if (temp != 0)
coefsList += " \xB1 " + Math.abs(temp) + "i\n";
else
coefsList += ", " + zeros[1][0] + "\n";
var coefs = getBiquadCoefs_polezero2(poles, zeros, magMax);
coefsList += "\na0 = " + coefs[0] + "\n";
coefsList += "a1 = " + coefs[1] + "\n";
coefsList += "a2 = " + coefs[2] + "\n";
coefsList += "b1 = " + coefs[4] + "\n";
coefsList += "b2 = " + coefs[5];
document.getElementById("coefsList_polezero2").value = coefsList;
}
function getBiquadCoefs_polezero2(poles, zeros, gainMax) {
var out = [6];
out[0] = 1.0;
if (zeros[0][1] == 0) {
out[1] = -(zeros[0][0] + zeros[1][0]);
out[2] = zeros[0][0] * zeros[1][0] + zeros[0][1] * zeros[1][1];
}
else {
var r = Math.sqrt(zeros[0][0] * zeros[0][0] + zeros[0][1] * zeros[0][1]);
var cosTheta = Math.sign(zeros[0][0]) * 1 / Math.sqrt((zeros[0][1] * zeros[0][1]) / (zeros[0][0] * zeros[0][0]) + 1);
out[1] = -2 * r * cosTheta;
out[2] = r * r;
}
out[3] = 1.0;
if (poles[0][1] == 0) {
out[4] = -(poles[0][0] + poles[1][0]);
out[5] = poles[0][0] * poles[1][0] + poles[0][1] * poles[1][1];
}
else {
var r = Math.sqrt(poles[0][0] * poles[0][0] + poles[0][1] * poles[0][1]);
var cosTheta = Math.sign(poles[0][0]) * 1 / Math.sqrt((poles[0][1] * poles[0][1]) / (poles[0][0] * poles[0][0]) + 1);
out[4] = -2 * r * cosTheta;
out[5] = r * r;
}
var norm = Math.pow(10, -gainMax / 20.0);
out[0] *= norm;
out[1] *= norm;
out[2] *= norm;
return out;
}
function nullTickFormatter_polezero2(xval) {
var FsField = document.getElementById("Fs_polezero2");
var Fs = parseFloat(FsField.value);
var val = Math.exp(Math.log(1 / 0.001) * xval * 2) * 0.001 * Fs * 0.5;
if (val < 1)
return val.toFixed(3);
if (val < 10)
return val.toFixed(2);
return val.toFixed(1);
}
function pointDistance(point1, point2) {
return Math.sqrt(Math.pow(point1[0] - point2[0], 2) + Math.pow(point1[1] - point2[1], 2));
}
function magResponse_polezero2(angle, poles, zeros) {
var x = Math.cos(angle);
var y = Math.sin(angle);
var idx;
var pProduct = 1;
for (idx = 0; idx < poles.length; idx++) {
pProduct *= pointDistance([x, y], poles[idx]);
}
var zProduct = 1;
for (idx = 0; idx < zeros.length; idx++) {
zProduct *= pointDistance([x, y], zeros[idx]);
}
if (zProduct == 0) zProduct = 0.00000000001;
if (pProduct == 0) pProduct = 0.00000000001;
return 20 * Math.log(zProduct / pProduct) / Math.LN10;
}
function phaseResponse(angle, poles, zeros) {
var x = Math.cos(angle);
var y = Math.sin(angle);
var idx;
var acc = 0;
for (idx = 0; idx < poles.length; idx++) {
acc -= atan2(y - poles[idx][1], x - poles[idx][0]);
}
for (idx = 0; idx < zeros.length; idx++) {
acc += atan2(y - zeros[idx][1], x - zeros[idx][0]);
}
return sum;
}

1439
style2.css 100644

Plik diff jest za duży Load Diff