Public
Edited
Feb 18, 2023
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
return Plot.plot({
width: 800,
height: 800,
y: {
grid: true,
domain: [-form.amp / 2, form.amp / 2],
nice: true
},
x: {
nice: true
},
color: {
legend: true
},
facet: { data: spectrum, y: "type", marginRight: 90 },

marks: [
// Plot.frame(),
Plot.ruleX([0]),
Plot.ruleX([form.omega]),
Plot.ruleX([-form.omega]),
Plot.line(spectrum, {
x: "Freq",
y: "value",
stroke: "part",
strokeWidth: 2
}),
Plot.dot(spectrum, {
x: "Freq",
y: (d) => {
if (d.type === "tanh")
return Math.abs(d.value) > 1e-2 ? d.value : undefined;
return Math.abs(d.value) > form.amp * 1e-2 ? d.value : undefined;
},
r: (d) => Math.pow(Math.abs(d.value), 1 / 5),
stroke: "part"
}),
Plot.text(spectrum, {
filter: (d) => d.type === "diff" && Math.abs(d.value) > 1e-2,
x: "Freq",
y: "value",
text: "value",
fill: "part",
dy: -8
})
]
});
}
Insert cell
spectrum = {
const { amp } = form,
fftTanh = [],
fftSignal = [],
scale = 1 / signal.length,
neq = fs / 2;

//
fft.realTransform(
fftTanh,
signal.map((d) => d.tanh * scale)
);
fft.completeSpectrum(fftTanh);

//
fft.realTransform(
fftSignal,
signal.map((d) => d.signal * scale)
);
fft.completeSpectrum(fftSignal);

const output = [];

var Omega, Freq;
for (let i = 0; i < fft.size; ++i) {
Omega = (Math.PI * i) / fft.size;
Freq = (fs * i) / fft.size;
Freq = Freq < neq ? Freq : Freq - fs;

[
{ type: "tanh", part: "real", value: fftTanh[i * 2] },
{ type: "tanh", part: "imag", value: fftTanh[i * 2 + 1] },
{ type: "signal", part: "real", value: fftSignal[i * 2] },
{ type: "signal", part: "imag", value: fftSignal[i * 2 + 1] },
{
type: "diff",
part: "real",
value: fftTanh[i * 2] - fftSignal[i * 2] / (amp > 1 ? amp : 1)
},
{
type: "diff",
part: "imag",
value: fftTanh[i * 2 + 1] - fftSignal[i * 2 + 1] / (amp > 1 ? amp : 1)
}
].map((obj) => {
output.push(Object.assign({ Omega, Freq }, obj));
});
}

return output;
}
Insert cell
spectrum
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
fft = new FFT(signal.length)
Insert cell
signal
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
signal = {
const { omega, amp, type } = form,
interval = 1 / omega,
phi = 0.0;

function signal(second) {
var v;
if (type === "cos") {
v = amp * Math.cos(2 * Math.PI * omega * second + phi);
}
if (type === "triangle") {
v = (amp * (second % interval)) / interval;
}
return v;
}

series.map((s) => {
const { second } = s;
Object.assign(s, { signal: signal(second), tanh: tanh(signal(second)) });
});

return series;
}
Insert cell
tanh = (x) => {
return (Math.exp(x) - Math.exp(-x)) / (Math.exp(x) + Math.exp(-x));
}
Insert cell
series = {
const interval = 1 / fs;

const series = [];

for (let i = 0; i < duration; i += interval) {
series.push({ second: i });
}

const nPower2 = 2 ** parseInt(Math.log(series.length) / Math.log(2));

return series.slice(0, nPower2);
}
Insert cell
FFT = (await import("https://jspm.dev/npm:fft.js@4.0.4")).default
Insert cell
d3 = require("d3")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more