Initial commit

This commit is contained in:
2026-02-18 21:32:22 +01:00
commit ea3a39f550
15 changed files with 903 additions and 0 deletions

28
.clang-format Normal file
View File

@@ -0,0 +1,28 @@
BasedOnStyle: LLVM
Language: Cpp
IndentWidth: 4
UseTab: Never
#NamespaceIndentation: All
PointerAlignment: Left
AccessModifierOffset: -4
AlwaysBreakTemplateDeclarations: true
LambdaBodyIndentation: Signature
MaxEmptyLinesToKeep: 3
#ColumnLimit: 128
CompactNamespaces: true
FixNamespaceComments: true
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: true
AlignConsecutiveAssignments: true
AlignConsecutiveBitFields: true
AlignConsecutiveDeclarations: true
AlignConsecutiveMacros: true
#BraceWrapping:
# BeforeElse: true

11
CMakeLists.txt Normal file
View File

@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.26)
project(R_S)
set(CMAKE_CXX_STANDARD 23)
add_executable(recursive_fft
fft/recursive_fft.cpp)
add_executable(recursive_fft_cplx
fft/recursive_fft_cplx.cpp)
add_executable(iterative_fft_cplx
fft/iterative_fft_cplx.cpp)

3
fft/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

12
fft/.idea/fft.iml generated Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

View File

@@ -0,0 +1,23 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E741" />
<option value="W605" />
<option value="E125" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N806" />
<option value="N802" />
<option value="N803" />
</list>
</option>
</inspection_tool>
</profile>
</component>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

4
fft/.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (control_test)" project-jdk-type="Python SDK" />
</project>

8
fft/.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/fft.iml" filepath="$PROJECT_DIR$/.idea/fft.iml" />
</modules>
</component>
</project>

31
fft/fft.py Normal file
View File

@@ -0,0 +1,31 @@
import numpy as np
def fft(x: np.array) -> np.array:
N = len(x)
if N == 1:
return x
else:
even_fft = fft(x[::2])
odd_fft = fft(x[1::2])
factors = np.exp(-2j * np.pi * np.arange(N) / N)
result = np.concatenate([even_fft + factors[:N // 2] * odd_fft,
even_fft + factors[N // 2:] * odd_fft])
return result
def main():
x = np.arange(8)
fft(x)
# # x = np.random.normal(size=4)
# x = np.array([1, 0, 1, 0, 0, 0, 0, 0])
# print(f"own:\t{fft(x)}")
# print(f"numpy:\t{np.fft.fft(x)}")
if __name__ == "__main__":
main()

48
fft/iterative_fft.py Normal file
View File

@@ -0,0 +1,48 @@
import numpy as np
def bit_reverse(val: int, num_bits: int) -> None:
b = '{:0{num_bits}b}'.format(val, num_bits=num_bits)
return int(b[::-1], 2)
def fft_reorder(x: np.array) -> np.array:
result = np.zeros(x.size)
for i in range(x.size):
result[i] = x[bit_reverse(i, int(np.log2(x.size)))]
return result
def fft(x: np.array) -> np.array:
x = fft_reorder(x).astype('complex64')
N0 = x.size
N = 2
# while N <= N0:
for N in [2, 4, 8]:
first_sub_fft_index = 0
while first_sub_fft_index < N0:
for k in range(N // 2):
factor = np.exp(-2j * np.pi * k / N)
temp = factor * x[first_sub_fft_index + N // 2 + k]
x[first_sub_fft_index + N//2 + k]\
= x[first_sub_fft_index + k] - temp
x[first_sub_fft_index + k]\
= x[first_sub_fft_index + k] + temp
first_sub_fft_index += N
# N *= 2
return x
def main():
x = np.arange(8)
print(fft(x))
print(np.fft.fft(np.arange(8)))
if __name__ == "__main__":
main()

212
fft/iterative_fft_cplx.cpp Normal file
View File

@@ -0,0 +1,212 @@
#include <cassert>
#include <concepts>
#include <format>
#include <iostream>
#include <math.h>
#include <numbers>
#include <ranges>
#include <vector>
/*
*
* Helper classes and functions
*
*/
template <typename value_t>
class Complex {
public:
constexpr Complex() noexcept : mRe(), mIm() {
}
constexpr Complex(const value_t& re, const value_t& im) noexcept
: mRe(re), mIm(im) {
}
constexpr Complex operator-() const {
return {-mRe, -mIm};
}
constexpr Complex operator+(const Complex& other) const {
return {mRe + other.mRe, mIm + other.mIm};
}
constexpr Complex operator-(const Complex& other) const {
return *this + (-other);
}
constexpr Complex operator*(const Complex& other) const {
return {mRe * other.mRe - mIm * other.mIm,
mIm * other.mRe + mRe * other.mIm};
}
operator std::string() {
return std::format("{:.2f} + j*{:.2f}", mRe, mIm);
}
constexpr value_t& real() {
return mRe;
}
constexpr value_t& imag() {
return mIm;
}
private:
value_t mRe;
value_t mIm;
};
using ComplexFloat = Complex<float>;
template <typename value_t>
constexpr void print(std::vector<Complex<value_t>> vec) {
for (auto& elem : vec) {
std::cout << "\t" << elem.real() << "; " << elem.imag() << std::endl;
}
}
/*
*
* FFT implementation
*
*/
template <std::integral value_t>
constexpr value_t bit_reverse(value_t n, std::size_t b = sizeof(value_t) * 8) {
assert(b <= std::numeric_limits<value_t>::digits);
value_t rv = 0;
for (size_t i = 0; i < b; ++i, n >>= 1) {
rv = (rv << 1) | (n & 0x01);
}
return rv;
}
constexpr std::vector<ComplexFloat>
fft_reorder(const std::vector<ComplexFloat>& x) {
std::vector<ComplexFloat> result;
for (int n = 0; n < x.size(); ++n) {
const unsigned rev_n = bit_reverse(n, log2(x.size()));
result.push_back(x[rev_n]);
}
return result;
}
//// TODO: Precompute Factors
//// TODO: Avoid unnecessary initialization cost of second buffer
//// TODO: Stream- or Vector-Hardware
// constexpr std::vector<ComplexFloat> fft(const std::vector<ComplexFloat>& x) {
// const unsigned N0 = x.size();
//
// std::array<std::vector<ComplexFloat>, 2> buffers;
// buffers[0] = fft_reorder(x);
// buffers[1].resize(N0);
//
// for (int i_iter = 1; i_iter <= log2(N0); ++i_iter) {
// std::vector<ComplexFloat>& input = buffers[(i_iter - 1) % 2];
// std::vector<ComplexFloat>& output = buffers[i_iter % 2];
//
// const unsigned N = std::pow(2, i_iter);
// const unsigned num_sub_ffts = N0 / N;
//
// for (int i_sub_fft = 0; i_sub_fft < num_sub_ffts; ++i_sub_fft) {
// for (int k = 0; k < N; ++k) {
// float factor_re = std::cos((2 * std::numbers::pi * k) / N);
// float factor_im = -std::sin((2 * std::numbers::pi * k) / N);
// ComplexFloat factor = {factor_re, factor_im};
//
// const unsigned index1 = i_sub_fft * N + k % (N / 2);
// const unsigned index2 = i_sub_fft * N + N / 2 + k % (N / 2);
//
// output[i_sub_fft * N + k] =
// input[index1] + factor * input[index2];
// }
// }
// }
//
// return buffers[static_cast<int>(log2(N0)) % 2];
// }
std::vector<ComplexFloat> precompute_factors(unsigned N0) {
std::vector<ComplexFloat> result;
for (int N = 2; N <= N0; N *= 2) {
for (int k = 0; k < N / 2; k++) {
float factor_re = std::cos((2 * std::numbers::pi * k) / N);
float factor_im = -std::sin((2 * std::numbers::pi * k) / N);
result.push_back({factor_re, factor_im});
}
}
return result;
}
constexpr unsigned get_factor_index(unsigned N, unsigned k) {
return (N - 2) / 2 + k;
}
const std::vector<ComplexFloat> factors = precompute_factors(8);
// TODO: Stream- or Vector-Hardware
std::vector<ComplexFloat> fft(const std::vector<ComplexFloat>& input) {
const unsigned N0 = input.size();
std::vector<ComplexFloat> x = fft_reorder(input);
for (int N = 2; N <= N0; N *= 2) {
for (int i_sub_fft = 0; i_sub_fft < N0; i_sub_fft += N) {
for (int k = 0; k < N / 2; k++) {
const ComplexFloat& factor = factors[get_factor_index(N, k)];
ComplexFloat z = factor * x[i_sub_fft + k + N / 2];
x[i_sub_fft + k + N / 2] = x[i_sub_fft + k] - z;
x[i_sub_fft + k] = x[i_sub_fft + k] + z;
}
}
}
return x;
}
/*
*
* Usage
*
*/
int main() {
std::vector<ComplexFloat> x = {{1, 0}, {2, 0}, {3, 0}, {4, 0},
{5, 0}, {6, 0}, {7, 0}, {8, 0}};
// std::vector<ComplexFloat> x = {{1, 0}, {2, 0}, {3, 0}, {4, 0}};
const auto& X = fft(x);
// int N0 = 8;
// for (int N = 2; N <= N0; N *= 2) {
// for (int k = 0; k < N / 2; k++) {
// std::cout << get_factor_index(N, k) << std::endl;
// }
// }
std::cout << "================" << std::endl;
std::cout << "result: " << std::endl;
print(X);
return 0;
}

83
fft/real_fft_test.py Normal file
View File

@@ -0,0 +1,83 @@
"""This script is used to visualize the behavior of the fft if the input is
purely real."""
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
def bit_reverse(val: int, num_bits: int) -> None:
b = '{:0{num_bits}b}'.format(val, num_bits=num_bits)
return int(b[::-1], 2)
def fft_reorder(x: np.array) -> np.array:
result = np.zeros(x.size)
for i in range(x.size):
result[i] = x[bit_reverse(i, int(np.log2(x.size)))]
return result
def fft(x: np.array) -> np.array:
x = fft_reorder(x).astype('complex64')
N0 = x.size
# fig, axes = plt.subplots(2, int(np.log2(N0)))
N = 2
while N <= N0:
first_subfft_index = 0
while first_subfft_index < N0:
temp = x[first_subfft_index + N // 2]
x[first_subfft_index + N // 2] = x[first_subfft_index] - temp
x[first_subfft_index] = x[first_subfft_index] + temp
for k in range(1, N // 2):
factor = np.exp(-2j * np.pi * k / N)
x[first_subfft_index + k] \
= x[first_subfft_index + k] \
+ factor * x[first_subfft_index + k + N // 2]
for k in range(1, N // 2):
x[first_subfft_index + N - k] \
= x[first_subfft_index + k].conj()
# \
# = x[first_subfft_index + k] + temp
# for k in range(N // 2):
# factor = np.exp(- 2j * np.pi * k / N)
#
# temp = factor * x[first_subfft_index + k + N // 2]
# x[first_subfft_index + k + N // 2] \
# = x[first_subfft_index + k] - temp
# x[first_subfft_index + k] \
# = x[first_subfft_index + k] + temp
first_subfft_index += N
# axes[0][int(np.log2(N)) - 1].plot(x.real[1:])
# axes[1][int(np.log2(N)) - 1].plot(x.imag[1:])
N *= 2
plt.show()
return x
def main():
sns.set_theme()
x = np.arange(16) + 1
X = fft(x)
X_reference = np.fft.fft(x)
for X_i, X_ref_i in zip(X, X_reference):
print(
f"{X_i.real:06.2f} + j({X_i.imag:06.2f})\t\t{X_ref_i.real:06.2f} "
f"+ j({X_ref_i.imag:06.2f})")
print(np.allclose(X, X_reference, rtol=1e-05, atol=1e-08))
if __name__ == "__main__":
main()

85
fft/recursive_fft.cpp Normal file
View File

@@ -0,0 +1,85 @@
#include <cassert>
#include <iostream>
#include <math.h>
#include <numbers>
#include <ranges>
#include <tuple>
#include <vector>
constexpr void print(auto& x_re, auto& x_im) {
for (std::tuple<const float&, const float&> elem :
std::views::zip(x_re, x_im)) {
const auto re = std::get<0>(elem);
const auto im = std::get<1>(elem);
std::cout << "\t" << re << "; " << im << std::endl;
}
}
constexpr std::pair<std::vector<float>, std::vector<float>>
fft(const std::vector<float>& x_re, const std::vector<float>& x_im) {
// TODO: Make sure this is ignored in release mode
assert(x_re.size() == x_im.size());
const unsigned N = x_re.size();
if (N == 1) {
return {x_re, x_im};
} else {
const auto& even_x_re = x_re | std::views::stride(2);
const auto& even_x_im = x_im | std::views::stride(2);
const auto& odd_x_re =
x_re | std::views::drop(1) | std::views::stride(2);
const auto& odd_x_im =
x_im | std::views::drop(1) | std::views::stride(2);
const auto& [even_fft_re, even_fft_im] =
fft(std::vector<float>(even_x_re.begin(), even_x_re.end()),
std::vector<float>(even_x_im.begin(), even_x_im.end()));
const auto& [odd_fft_re, odd_fft_im] =
fft(std::vector<float>(odd_x_re.begin(), odd_x_re.end()),
std::vector<float>(odd_x_im.begin(), odd_x_im.end()));
/// Combine even and odd ffts
std::vector<float> factors_re(N);
std::vector<float> factors_im(N);
for (int k = 0; k < N; ++k) {
factors_re[k] = std::cos((2 * std::numbers::pi * k) / N);
factors_im[k] = -std::sin((2 * std::numbers::pi * k) / N);
}
std::vector<float> result_re(N);
std::vector<float> result_im(N);
for (int k = 0; k < N / 2; ++k) {
result_re[k] = even_fft_re[k] + factors_re[k] * odd_fft_re[k] -
factors_im[k] * odd_fft_im[k];
result_im[k] = even_fft_im[k] + factors_im[k] * odd_fft_re[k] +
factors_re[k] * odd_fft_im[k];
}
for (int k = 0; k < N / 2; ++k) {
result_re[N / 2 + k] = even_fft_re[k] +
factors_re[N / 2 + k] * odd_fft_re[k] -
factors_im[k] * odd_fft_im[k];
result_im[N / 2 + k] = even_fft_im[k] +
factors_im[N / 2 + k] * odd_fft_re[k] +
factors_re[k] * odd_fft_im[k];
}
return {result_re, result_im};
}
}
int main() {
std::vector<float> x_re = {1, 2, 3, 4};
std::vector<float> x_im = {0, 0, 0, 0};
const auto [X_re, X_im] = fft(x_re, x_im);
return 0;
}

127
fft/recursive_fft_cplx.cpp Normal file
View File

@@ -0,0 +1,127 @@
#include <iostream>
#include <math.h>
#include <numbers>
#include <ranges>
#include <vector>
/*
*
* Helper classes and functions
*
*/
template <typename value_t>
class Complex {
public:
constexpr Complex() noexcept : mRe(), mIm() {
}
constexpr Complex(const value_t& re, const value_t& im) noexcept
: mRe(re), mIm(im) {
}
constexpr Complex operator-() const {
return {-mRe, -mIm};
}
constexpr Complex operator+(const Complex& other) const {
return {mRe + other.mRe, mIm + other.mIm};
}
constexpr Complex operator-(const Complex& other) const {
return *this + (-other);
}
constexpr Complex operator*(const Complex& other) const {
return {mRe * other.mRe - mIm * other.mIm,
mIm * other.mRe + mRe * other.mIm};
}
constexpr value_t& real() {
return mRe;
}
constexpr value_t& imag() {
return mIm;
}
private:
value_t mRe;
value_t mIm;
};
using ComplexFloat = Complex<float>;
template <typename value_t>
constexpr void print(std::vector<Complex<value_t>> vec) {
for (auto& elem : vec) {
std::cout << "\t" << elem.real() << "; " << elem.imag() << std::endl;
}
}
/*
*
* FFT implementation
*
*/
constexpr std::vector<ComplexFloat> fft(const std::vector<ComplexFloat>& x) {
const unsigned N = x.size();
if (N == 1) {
return x;
} else {
/// Compute even and odd ffts
const auto& even_x = x | std::views::stride(2);
const auto& odd_x = x | std::views::drop(1) | std::views::stride(2);
const auto& even_fft =
fft(std::vector<ComplexFloat>(even_x.begin(), even_x.end()));
const auto& odd_fft =
fft(std::vector<ComplexFloat>(odd_x.begin(), odd_x.end()));
/// Combine even and odd ffts
std::vector<ComplexFloat> factors(N);
for (int k = 0; k < N; ++k) {
factors[k].real() = std::cos((2 * std::numbers::pi * k) / N);
factors[k].imag() = -std::sin((2 * std::numbers::pi * k) / N);
}
std::vector<ComplexFloat> result(N);
for (int k = 0; k < N; ++k) {
result[k] =
even_fft[k % (N / 2)] + factors[k] * odd_fft[k % (N / 2)];
}
return result;
}
}
/*
*
* Usage
*
*/
int main() {
std::vector<ComplexFloat> x = {{1, 0}, {2, 0}, {3, 0}, {4, 0}};
const auto& X = fft(x);
std::cout << "================" << std::endl;
std::cout << "result: " << std::endl;
print(X);
return 0;
}

222
fft/vector_fft.py Normal file
View File

@@ -0,0 +1,222 @@
import numpy as np
import sys
import typing
buffer = np.zeros(1024).astype('float32')
# TODO temp memory
class MemoryWrapper:
"""Class allowing for easier access to the memory buffer."""
def __init__(self, N0: int, imag_offset: int):
self._current_offset = 0
self._num_sub_ffts = 0
self._N0 = N0
self._imag_offset = imag_offset
def set_kN(self, k, num_sub_ffts):
N = self._N0 / num_sub_ffts
i = bit_reverse(k, int(np.log2(N)))
self._current_offset = i * num_sub_ffts
self._num_sub_ffts = num_sub_ffts
@property
def OddReal(self) -> np.array:
start_index = self._current_offset + self._num_sub_ffts
end_index = self._current_offset + self._num_sub_ffts * 2
return buffer[start_index:end_index]
@OddReal.setter
def OddReal(self, o):
start_index = self._current_offset + self._num_sub_ffts
end_index = self._current_offset + self._num_sub_ffts * 2
buffer[start_index:end_index] = o
@property
def OddImag(self) -> np.array:
start_index = self._imag_offset \
+ self._current_offset + self._num_sub_ffts
end_index = self._imag_offset \
+ self._current_offset + self._num_sub_ffts * 2
return buffer[start_index:end_index]
@OddImag.setter
def OddImag(self, o):
start_index = self._imag_offset \
+ self._current_offset + self._num_sub_ffts
end_index = self._imag_offset \
+ self._current_offset + self._num_sub_ffts * 2
buffer[start_index: end_index] = o
@property
def EvenReal(self) -> np.array:
start_index = self._current_offset
end_index = self._current_offset + self._num_sub_ffts
return buffer[start_index:end_index]
@EvenReal.setter
def EvenReal(self, e):
start_index = self._current_offset
end_index = self._current_offset + self._num_sub_ffts
buffer[start_index:end_index] = e
@property
def EvenImag(self) -> np.array:
start_index = self._imag_offset + self._current_offset
end_index = self._imag_offset \
+ self._current_offset + self._num_sub_ffts
return buffer[start_index:end_index]
@EvenImag.setter
def EvenImag(self, e):
start_index = self._imag_offset + self._current_offset
end_index = self._imag_offset \
+ self._current_offset + self._num_sub_ffts
buffer[start_index:end_index] = e
# def mul_n(l_start: int, r_start: int, out_start, num: int):
# for i in range(num):
# buffer[out_start + i] = buffer[l_start + i] * buffer[r_start * i]
def mul_32_factor(l_start: int, factor: float, out_start, num: int):
if num > 32:
raise ValueError('A maximum of 32 elements can be multiplied')
for i in range(num):
buffer[out_start + i] = buffer[l_start + i] * factor
def add_32(l_start: int, r_start: int, out_start, num: int):
if num > 32:
raise ValueError('A maximum of 32 elements can be added')
for i in range(num):
buffer[out_start + i] = buffer[l_start + i] + buffer[r_start * i]
def sub_32(l_start: int, r_start: int, out_start: int, num: int):
if num > 32:
raise ValueError('A maximum of 32 elements can be subtracted')
for i in range(num):
buffer[out_start + i] = buffer[l_start + i] - buffer[r_start * i]
def bit_reverse(val: int, num_bits: int):
"""Reverse the bits of an input integer.
:param val: Value to reverse
:param num_bits: Number of bits to consider
"""
b = '{:0{width}b}'.format(val, width=num_bits)
return int(b[::-1], 2)
def fft_reorder(x: np.array, N: int) -> np.array:
"""Reorder the elements of an array so that the indices are bit-inverse
values of the original ones.
"""
result = np.zeros(x.size)
for i in range(x.size):
result[i] = x[bit_reverse(i, int(np.log2(N)))]
return result
def fft(x_re: np.array, x_im: np.array) -> typing.Tuple[np.array, np.array]:
"""Perform the fft algorithm on a given vector.
"""
imag_mem_offset = x_re.size
temp_mem_offset = x_re.size * 2
# Copy data to hardware buffer
for i in range(x_re.size):
buffer[i] = x_re[i]
# for i in range(x_im.size):
# buffer[i + imag_offset] = x_im[i]
N0 = x_re.size
mem_wrapper = MemoryWrapper(N0, x_re.size)
num_sub_ffts = N0
while num_sub_ffts > 0:
N = N0 / num_sub_ffts
for k in range(int(N // 2)):
# mem_wrapper.set_kN(k, num_sub_ffts)
#
# factor_re = np.exp(-2j * np.pi * k / N).real
# factor_im = np.exp(-2j * np.pi * k / N).imag
#
# temp_re = factor_re * mem_wrapper.OddReal \
# - factor_im * mem_wrapper.OddImag
# temp_im = factor_re * mem_wrapper.OddImag \
# + factor_im * mem_wrapper.OddReal
#
# mem_wrapper.OddReal = mem_wrapper.EvenReal - temp_re
# mem_wrapper.OddImag = mem_wrapper.EvenImag - temp_im
# mem_wrapper.EvenReal = mem_wrapper.EvenReal + temp_re
# mem_wrapper.EvenImag = mem_wrapper.EvenImag + temp_im
i = bit_reverse(k, int(np.log2(N)))
fft_offset = i * num_sub_ffts
odd_offset = num_sub_ffts
factor_re = np.exp(-2j * np.pi * k / N).real
factor_im = np.exp(-2j * np.pi * k / N).imag
# Compute temp variables
# TODO: Don't hardcode 32
temp_re_start = temp_mem_offset
temp_im_start = temp_re_start + imag_mem_offset
even_re_start = fft_offset
even_im_start = fft_offset + imag_mem_offset
odd_re_start = fft_offset + odd_offset
odd_im_start = fft_offset + imag_mem_offset
mul_32_factor(l_start=odd_re_start, factor=factor_re,
out_start=temp_re_start, num=4)
mul_32_factor(l_start=odd_im_start, factor=-factor_im,
out_start=temp_im_start, num=4)
add_32(l_start=temp_re_start, r_start=temp_mem_offset + 32,
out_start=temp_mem_offset, num=4)
mul_32_factor(l_start=(fft_offset + odd_offset + imag_mem_offset),
factor=factor_re,
out_start=temp_mem_offset + 32, num=4)
mul_32_factor(l_start=(fft_offset + odd_offset + imag_mem_offset),
factor=factor_im,
out_start=temp_mem_offset + 64, num=4)
add_32(l_start=temp_mem_offset + 32, r_start=temp_mem_offset + 64,
out_start=temp_mem_offset + 32, num=4)
num_sub_ffts = num_sub_ffts // 2
buffer[0:imag_mem_offset] = fft_reorder(buffer[0:imag_mem_offset],
imag_mem_offset)
buffer[imag_mem_offset:2 * imag_mem_offset] = fft_reorder(
buffer[imag_mem_offset:2 * imag_mem_offset], imag_mem_offset)
return buffer[0:imag_mem_offset], buffer[
imag_mem_offset:imag_mem_offset * 2]
def main():
x_re = np.arange(8)
x_im = np.zeros(x_re.size)
X_re, X_im = fft(x_re, x_im)
# for k, (X_k, X_exp_k) in enumerate(zip(X_re, np.fft.fft(x_re))):
# print(
# f"X[{k:02}]: {X_k.real:07.2f} + j({X_k.imag:07.2f})\t\tX_exp["
# f"{k:02}]: {X_exp_k.real:07.2f} + j({X_exp_k.imag:07.2f})")
for k, ((X_re_k, X_im_k), X_exp_k) in enumerate(
zip(zip(X_re, X_im), np.fft.fft(x_re))):
print(
f"X[{k:02}]: {X_re_k:07.2f} + j({X_im_k:07.2f})\t\tX_exp["
f"{k:02}]: {X_exp_k.real:07.2f} + j({X_exp_k.imag:07.2f})")
if __name__ == "__main__":
main()