feat: implement free-field CPU beamforming
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
function assertPowerOfTwo(value) {
|
||||
if (!Number.isInteger(value) || value < 2 || (value & (value - 1)) !== 0) {
|
||||
throw new Error(`FFT 크기는 2 이상의 2의 거듭제곱이어야 합니다: ${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function fftInPlace(real, imaginary, inverse = false) {
|
||||
const size = real.length;
|
||||
if (imaginary.length !== size) throw new Error("FFT real/imaginary 길이가 다릅니다.");
|
||||
assertPowerOfTwo(size);
|
||||
|
||||
for (let index = 1, reversed = 0; index < size; index += 1) {
|
||||
let bit = size >> 1;
|
||||
while (reversed & bit) {
|
||||
reversed ^= bit;
|
||||
bit >>= 1;
|
||||
}
|
||||
reversed ^= bit;
|
||||
if (index < reversed) {
|
||||
[real[index], real[reversed]] = [real[reversed], real[index]];
|
||||
[imaginary[index], imaginary[reversed]] = [imaginary[reversed], imaginary[index]];
|
||||
}
|
||||
}
|
||||
|
||||
const direction = inverse ? 1 : -1;
|
||||
for (let length = 2; length <= size; length <<= 1) {
|
||||
const angle = (direction * 2 * Math.PI) / length;
|
||||
const stepReal = Math.cos(angle);
|
||||
const stepImaginary = Math.sin(angle);
|
||||
for (let start = 0; start < size; start += length) {
|
||||
let twiddleReal = 1;
|
||||
let twiddleImaginary = 0;
|
||||
const halfLength = length >> 1;
|
||||
for (let offset = 0; offset < halfLength; offset += 1) {
|
||||
const evenIndex = start + offset;
|
||||
const oddIndex = evenIndex + halfLength;
|
||||
const oddReal = real[oddIndex] * twiddleReal - imaginary[oddIndex] * twiddleImaginary;
|
||||
const oddImaginary = real[oddIndex] * twiddleImaginary + imaginary[oddIndex] * twiddleReal;
|
||||
real[oddIndex] = real[evenIndex] - oddReal;
|
||||
imaginary[oddIndex] = imaginary[evenIndex] - oddImaginary;
|
||||
real[evenIndex] += oddReal;
|
||||
imaginary[evenIndex] += oddImaginary;
|
||||
const nextTwiddleReal = twiddleReal * stepReal - twiddleImaginary * stepImaginary;
|
||||
twiddleImaginary = twiddleReal * stepImaginary + twiddleImaginary * stepReal;
|
||||
twiddleReal = nextTwiddleReal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inverse) {
|
||||
for (let index = 0; index < size; index += 1) {
|
||||
real[index] /= size;
|
||||
imaginary[index] /= size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function fftReal(samples) {
|
||||
const real = Float64Array.from(samples);
|
||||
const imaginary = new Float64Array(real.length);
|
||||
fftInPlace(real, imaginary);
|
||||
return { real, imaginary };
|
||||
}
|
||||
Reference in New Issue
Block a user