pfalstad / circuitjs1

Electronic Circuit Simulator in the Browser
GNU General Public License v2.0
1.75k stars 295 forks source link

FFT does not use a window function #96

Open mordae opened 3 months ago

mordae commented 3 months ago

Hi, I was taking the fact that the FFT always "jumps around" as a harsh reality of life, but I've just realized it's doing that only because we are not windowing the data. Can we please add a window function to the FFT so that it stabilizes and one can actually read and compare it?

I've tested the triangle window and it retains too much of the original jiggling, cosine window looks pretty good as does Hann window.

Cosine window patch:

diff --git a/src/com/lushprojects/circuitjs1/client/FFT.java b/src/com/lushprojects/circuitjs1/client/FFT>
index e9243a3..84aae03 100644
--- a/src/com/lushprojects/circuitjs1/client/FFT.java
+++ b/src/com/lushprojects/circuitjs1/client/FFT.java
@@ -24,6 +24,7 @@ class FFT {
     private int bits;
     private double[] cosTable;
     private double[] sinTable;
+    private double[] winTable;

     FFT(int n) {
       size = n;
@@ -35,6 +36,15 @@ class FFT {
         cosTable[i] = Math.cos(dtheta * i);
         sinTable[i] = Math.sin(dtheta * i);
       }
+
+      /* Scale the sine window up for unity gain. */
+      double gainCompensation = 1.5707963267961471;
+
+      winTable = new double[size];
+      for (int i = 0; i < winTable.length; i++) {
+        double weight = Math.sin(i * Math.PI / winTable.length);
+        winTable[i] = weight * gainCompensation;
+      }
     }

     /*
@@ -47,6 +57,11 @@ class FFT {
      * http://cnx.rice.edu/content/m12016/latest/
      */
     void fft(double[] real, double[] imag) {
+        for (int i = 0; i < real.length; i++) {
+          real[i] *= winTable[i];
+          imag[i] *= winTable[i];
+        }
+
         int j = 0;
         int n2 = real.length / 2;
         for (int i=1; i < real.length - 1; i++) {

Although it might be interesting to add window function selection to scope settings. Let me know what you think. I'll draft an actual pull request then, @pfalstad.

cedricp commented 3 months ago

Interesting, but I guess amplitude should be corrected after windowing see : https://community.sw.siemens.com/s/article/window-correction-factors

mordae commented 3 months ago

Yeah, you are obviously right. :sweat_smile: I guess multiplying taps with reciprocal of the average original tap gain should do the trick.

pfalstad commented 2 weeks ago

Love this, thanks! I don't need a pull request.