绘制离散时间信号显示调幅
Plotting a discrete-time signal shows amplitude modulation
我正在尝试使用 canvas
元素渲染一个简单的离散时间信号。但是,表示似乎不准确。正如您在代码片段中看到的那样,信号 出现 在频率达到特定阈值后进行幅度调制。即使它远低于 <50Hz
的奈奎斯特极限(假设本例中的采样率为 100Hz
)。
对于像 5Hz
这样的非常低的频率,它看起来非常好。
我该如何正确渲染它?它是否适用于更复杂的信号(例如,歌曲的波形)?
window.addEventListener('load', () => {
const canvas = document.querySelector('canvas');
const frequencyElem = document.querySelector('#frequency');
const ctx = canvas.getContext('2d');
const renderFn = t => {
const signal = new Array(100);
const sineOfT = Math.sin(t / 1000 / 8 * Math.PI * 2) * 0.5 + 0.5;
const frequency = sineOfT * 20 + 3;
for (let i = 0; i < signal.length; i++) {
signal[i] = Math.sin(i / signal.length * Math.PI * 2 * frequency);
}
frequencyElem.innerText = `${frequency.toFixed(3)}Hz`
render(ctx, signal);
requestAnimationFrame(renderFn);
};
requestAnimationFrame(renderFn);
});
function render(ctx, signal) {
const w = ctx.canvas.width;
const h = ctx.canvas.height;
ctx.clearRect(0, 0, w, h);
ctx.strokeStyle = 'red';
ctx.beginPath();
signal.forEach((value, i) => {
const x = i / (signal.length - 1) * w;
const y = h - (value + 1) / 2 * h;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
}
@media (prefers-color-scheme: dark) {
body {
background-color: #333;
color: #f6f6f6;
}
}
<canvas></canvas>
<br/>
Frequency: <span id="frequency"></span>
我觉得很合适。在更高的频率下,当峰值落在两个样本之间时,采样点可以比峰值低很多。
如果信号仅具有小于奈奎斯特的频率,则可以从其样本中重建信号。这并不意味着样本 看起来像 信号。
只要您的信号被过采样 2 倍或更多(或更多),您就可以通过在采样点之间使用三次插值来非常准确地绘制它。例如,请参见此处的 Catmull-Rom 插值:https://en.wikipedia.org/wiki/Cubic_Hermite_spline
您可以使用HTML Canvas中的bezierCurveTo
方法绘制这些插值曲线。如果您需要使用线,那么您应该找到样本之间出现的任何最大或最小点,并将它们包含在您的路径中。
我已经编辑了您的代码段以使用 bezierCurveTo
方法和下面的 Catmull-Rom 插值:
window.addEventListener('load', () => {
const canvas = document.querySelector('canvas');
const frequencyElem = document.querySelector('#frequency');
const ctx = canvas.getContext('2d');
const renderFn = t => {
const signal = new Array(100);
const sineOfT = Math.sin(t / 1000 / 8 * Math.PI * 2) * 0.5 + 0.5;
const frequency = sineOfT * 20 + 3;
for (let i = 0; i < signal.length; i++) {
signal[i] = Math.sin(i / signal.length * Math.PI * 2 * frequency);
}
frequencyElem.innerText = `${frequency.toFixed(3)}Hz`
render(ctx, signal);
requestAnimationFrame(renderFn);
};
requestAnimationFrame(renderFn);
});
function render(ctx, signal) {
const w = ctx.canvas.width;
const h = ctx.canvas.height;
ctx.clearRect(0, 0, w, h);
ctx.strokeStyle = 'red';
ctx.beginPath();
const dx = w/(signal.length - 1);
const dy = -(h-2)/2.0;
const c = 1.0/2.0;
for (let i=0; i < signal.length-1; ++i) {
const x0 = i * dx;
const y0 = h*0.5 + signal[i]*dy;
const x3 = x0 + dx;
const y3 = h*0.5 + signal[i+1]*dy;
let x1,y1,x2,y2;
if (i>0) {
x1 = x0 + dx*c;
y1 = y0 + (signal[i+1] - signal[i-1])*dy*c/2;
} else {
x1 = x0;
y1 = y0;
ctx.moveTo(x0, y0);
}
if (i < signal.length-2) {
x2 = x3 - dx*c;
y2 = y3 - (signal[i+2] - signal[i])*dy*c/2;
} else {
x2 = x3;
y2 = y3;
}
ctx.bezierCurveTo(x1,y1,x2,y2,x3,y3);
}
ctx.stroke();
}
@media (prefers-color-scheme: dark) {
body {
background-color: #333;
color: #f6f6f6;
}
}
<canvas></canvas>
<br/>
Frequency: <span id="frequency"></span>
我正在尝试使用 canvas
元素渲染一个简单的离散时间信号。但是,表示似乎不准确。正如您在代码片段中看到的那样,信号 出现 在频率达到特定阈值后进行幅度调制。即使它远低于 <50Hz
的奈奎斯特极限(假设本例中的采样率为 100Hz
)。
对于像 5Hz
这样的非常低的频率,它看起来非常好。
我该如何正确渲染它?它是否适用于更复杂的信号(例如,歌曲的波形)?
window.addEventListener('load', () => {
const canvas = document.querySelector('canvas');
const frequencyElem = document.querySelector('#frequency');
const ctx = canvas.getContext('2d');
const renderFn = t => {
const signal = new Array(100);
const sineOfT = Math.sin(t / 1000 / 8 * Math.PI * 2) * 0.5 + 0.5;
const frequency = sineOfT * 20 + 3;
for (let i = 0; i < signal.length; i++) {
signal[i] = Math.sin(i / signal.length * Math.PI * 2 * frequency);
}
frequencyElem.innerText = `${frequency.toFixed(3)}Hz`
render(ctx, signal);
requestAnimationFrame(renderFn);
};
requestAnimationFrame(renderFn);
});
function render(ctx, signal) {
const w = ctx.canvas.width;
const h = ctx.canvas.height;
ctx.clearRect(0, 0, w, h);
ctx.strokeStyle = 'red';
ctx.beginPath();
signal.forEach((value, i) => {
const x = i / (signal.length - 1) * w;
const y = h - (value + 1) / 2 * h;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
}
@media (prefers-color-scheme: dark) {
body {
background-color: #333;
color: #f6f6f6;
}
}
<canvas></canvas>
<br/>
Frequency: <span id="frequency"></span>
我觉得很合适。在更高的频率下,当峰值落在两个样本之间时,采样点可以比峰值低很多。
如果信号仅具有小于奈奎斯特的频率,则可以从其样本中重建信号。这并不意味着样本 看起来像 信号。
只要您的信号被过采样 2 倍或更多(或更多),您就可以通过在采样点之间使用三次插值来非常准确地绘制它。例如,请参见此处的 Catmull-Rom 插值:https://en.wikipedia.org/wiki/Cubic_Hermite_spline
您可以使用HTML Canvas中的bezierCurveTo
方法绘制这些插值曲线。如果您需要使用线,那么您应该找到样本之间出现的任何最大或最小点,并将它们包含在您的路径中。
我已经编辑了您的代码段以使用 bezierCurveTo
方法和下面的 Catmull-Rom 插值:
window.addEventListener('load', () => {
const canvas = document.querySelector('canvas');
const frequencyElem = document.querySelector('#frequency');
const ctx = canvas.getContext('2d');
const renderFn = t => {
const signal = new Array(100);
const sineOfT = Math.sin(t / 1000 / 8 * Math.PI * 2) * 0.5 + 0.5;
const frequency = sineOfT * 20 + 3;
for (let i = 0; i < signal.length; i++) {
signal[i] = Math.sin(i / signal.length * Math.PI * 2 * frequency);
}
frequencyElem.innerText = `${frequency.toFixed(3)}Hz`
render(ctx, signal);
requestAnimationFrame(renderFn);
};
requestAnimationFrame(renderFn);
});
function render(ctx, signal) {
const w = ctx.canvas.width;
const h = ctx.canvas.height;
ctx.clearRect(0, 0, w, h);
ctx.strokeStyle = 'red';
ctx.beginPath();
const dx = w/(signal.length - 1);
const dy = -(h-2)/2.0;
const c = 1.0/2.0;
for (let i=0; i < signal.length-1; ++i) {
const x0 = i * dx;
const y0 = h*0.5 + signal[i]*dy;
const x3 = x0 + dx;
const y3 = h*0.5 + signal[i+1]*dy;
let x1,y1,x2,y2;
if (i>0) {
x1 = x0 + dx*c;
y1 = y0 + (signal[i+1] - signal[i-1])*dy*c/2;
} else {
x1 = x0;
y1 = y0;
ctx.moveTo(x0, y0);
}
if (i < signal.length-2) {
x2 = x3 - dx*c;
y2 = y3 - (signal[i+2] - signal[i])*dy*c/2;
} else {
x2 = x3;
y2 = y3;
}
ctx.bezierCurveTo(x1,y1,x2,y2,x3,y3);
}
ctx.stroke();
}
@media (prefers-color-scheme: dark) {
body {
background-color: #333;
color: #f6f6f6;
}
}
<canvas></canvas>
<br/>
Frequency: <span id="frequency"></span>