Public
Edited
Jun 8, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof freq = Inputs.range([0, 10], {step: 0.1, label: "Input frequency ${tex`\omega`} "})

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Plot.plot({
x: {
domain:[0,300],
grid: true
},
y: {
domain: [-0.1, 1.0],
grid: true
},
marks: [
Plot.ruleY([0]),
Plot.dot(industries, {x: 10*form.freqA, y: xformHere, fill: "red"}),
//Plot.line(industries, {x: getCosineArray(1000, form.freq1)[0], y: calcXformArray(form.freq1)}),
Plot.line(industries, {x: timebase, y: xformArray})
]
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
#Phase
We played pretty fast and loose with the math here. What we are showing is the DFT -- the Discrete Fourier Transform, rather than the continuous time Fourier Transform, for a number of reasons but mostly the latter requires an infinite integral which is hard to do in Javascript!
Insert cell
Type Markdown, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
function raisedCosineWindow(windowLength) {
var window = new Float64Array(windowLength);
for (var i = 0; i < windowLength; i++) {
window[i] = Math.cos(Math.PI * (i - (windowLength / 2)) / (windowLength - 1));
}
return window;
}
Insert cell
// Generate an array of cosine values, centered midway,at the given frequency. Extent is assumed to be -pi to pi.
function getCosineArray(length, frequency) {
const linearArray = [];
const cosineArray = [];

for (let i = 0; i < length; i++) {
let time = (i - length/2) / length;
linearArray.push(i);
cosineArray.push(Math.cos(2 * Math.PI * frequency * time));
}

return [linearArray, cosineArray];
}
Insert cell
// Generate an array of rectified cosine values, centered midway,at the given frequency. Extent is assumed to be -pi to pi.
function getCosineRect(length, frequency) {
const cosineArray = [];

for (let i = 0; i < length; i++) {
let time = (i - length/2) / length;
let temp = Math.cos(2 * Math.PI * frequency * time)
if (temp > 0.0) {
cosineArray.push(temp);
} else {
cosineArray.push(0.0);
}
}

return cosineArray;
}
Insert cell
// element-by-element array multiplication. Assumes lengths are the same but does not check.
function multiplyArrays(array1, array2) {
const result = [];

for (let i = 0; i < array1.length; i++) {
result.push(array1[i] * array2[i]);
}

return result;
}
Insert cell
xformArray = calcXformArrayWin(form.freqI)
Insert cell
// Calculate naively (FFT would speed this up!) the fourier transform of a cosine wave for frequency f2. Result
// is an array of length 10*(freqMax - freqMin) which in this case is 300.
function calcXformArray(fIn) {
const result = [];

for (let f1 = freqMin; f1 < freqMax; f1 += 0.1) {
result.push(averageArrays(multiplyArrays(getCosineArray(arrayLen, f1)[1],getCosineArray(arrayLen, fIn)[1])))
}

return result;
}
Insert cell
// Calculate naively (FFT would speed this up!) the fourier transform of a cosine wave for frequency f2. Result
// is an array of length 10*(freqMax - freqMin) which in this case is 300.
// Like above but input is windowed with a raised cosine function
function calcXformArrayWin(fIn) {
const result = [];

for (let f1 = freqMin; f1 < freqMax; f1 += 0.1) {
result.push(averageArrays(multiplyArrays(multiplyArrays(getCosineArray(arrayLen, f1)[1],cosWindow),getCosineArray(arrayLen, fIn)[1])))
}
return result;
}
Insert cell
// Given an array, return the average
function averageArrays(array) {
let result = 0;

for (let i = 0; i < array.length; i++) {
result += array[i];
}

return result/array.length;
}
Insert cell
Insert cell
// value of the fourier transform computed at a single frequency (freqA) given an input signal that's a cosine at freqI
xformHereOld = averageArrays(multiplyArrays(multiplyArrays(cosWindow, getCosineArray(1000, form.freqI)[1]),getCosineArray(1000, form.freqA)[1]))
Insert cell
// value of the fourier transform computed at a single frequency (freq2) given an input signal that's a cosine at freq1
xformHere = xformArray[form.freqA*10]
Insert cell
// transform results in an array. Only need to recompute this when input frequency freq1 changes.
Insert cell
// length of arrays to use in plots
arrayLen = 512
Insert cell
// this is the timebase, the argument of the cosine functions and the x axis.
timebase = getCosineArray(arrayLen, 0)[0]
Insert cell
// raised cosine window function -- reduces ripple in fourier transform by zeroing out edge effects
cosWindow = raisedCosineWindow(arrayLen)
Insert cell
// Maximum frequency for input controls
freqMax = 30
Insert cell
// Minimum frequency for input controls
freqMin = 0

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