changeset 265:c7efd12c27c5

Window fixes and tests
author Chris Cannam
date Thu, 23 May 2013 13:21:05 +0100
parents 046a2e323716
children 46d2923a04ab
files yetilab/signal/test/test_window.yeti yetilab/signal/window.yeti yetilab/test/all.yeti yetilab/vector/test/test_vector.yeti yetilab/vector/vector.yeti
diffstat 5 files changed, 156 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yetilab/signal/test/test_window.yeti	Thu May 23 13:21:05 2013 +0100
@@ -0,0 +1,107 @@
+
+module yetilab.signal.test.test_window;
+
+win = load yetilab.signal.window;
+vec = load yetilab.vector.vector;
+
+{ compare, compareUsing } = load yetilab.test.test;
+
+functions = [
+    Hann () : win.hann,
+    Hamming () : win.hamming,
+    Blackman () : win.blackman,
+    Nuttall () : win.nuttall,
+    BlackmanNuttall () : win.blackmanNuttall,
+    BlackmanHarris () : win.blackmanHarris,
+    Boxcar () : win.boxcar,
+    Bartlett () : win.bartlett,
+];
+
+close aa bb = all id (map2 do a b: (abs (a - b) < 0.00001) done aa bb);
+
+isSymmetric a =
+   (len = (vec.length a);
+    b = if len % 2 == 0 then
+            half = vec.slice a 0 (len/2);
+            vec.concat [half, vec.reversed half];
+        else
+            half = vec.slice a 0 (int (len/2));
+            mid = vec.slice a (int (len/2)) (int (len/2) + 1);
+            vec.concat [half, mid, vec.reversed half];
+        fi;
+    compareUsing close (vec.list a) (vec.list b));
+
+[
+
+"windowFunction": \(
+    all id (map do type:
+        f = functions[type];
+        a = f 10;
+        b = win.windowFunction type [ Symmetric false ] 10;
+        compareUsing close (vec.list a) (vec.list b)
+            or (eprintln "** failed window type: \(type)"; false)
+    done (keys functions));
+),
+
+"symmetric-even": \(
+    len = 10;
+    all id (map do type:
+        f = win.windowFunction type [ Symmetric true ];
+        v = f len;
+        (compare (vec.length v) len and isSymmetric v) or
+            (eprintln "** failed window type: \(type)"; false);
+    done (keys functions));
+),
+
+"symmetric-odd": \(
+    len = 11;
+    all id (map do type:
+        f = win.windowFunction type [ Symmetric true ];
+        v = f len;
+        (compare (vec.length v) len and isSymmetric v) or
+            (eprintln "** failed window type: \(type)"; false);
+    done (keys functions));
+),
+      
+"periodic-even": \(
+    // We can't actually test whether a function is periodic, given
+    // only one cycle of it! But we can make sure that all but the
+    // first sample is symmetric, which is what a symmetric window
+    // becomes when generated in periodic mode
+    len = 10;
+    all id (map do type:
+        f = win.windowFunction type [ Symmetric false ];
+        v = f len;
+        (compare (vec.length v) len and isSymmetric (vec.slice v 1 len)) or
+            (eprintln "** failed window type: \(type)"; false);
+    done (keys functions));
+),
+
+"periodic-odd": \(
+    len = 11;
+    all id (map do type:
+        f = win.windowFunction type [ Symmetric false ];
+        v = f len;
+        (compare (vec.length v) len and isSymmetric (vec.slice v 1 len)) or
+            (eprintln "** failed window type: \(type)"; false);
+    done (keys functions));
+),
+
+"bartlett-periodic": \(
+    compare (vec.list (win.bartlett 1)) [1] and
+       compare (vec.list (win.bartlett 2)) [0,1] and
+       compare (vec.list (win.bartlett 3)) [0,2/3,2/3] and
+       compare (vec.list (win.bartlett 4)) [0,1/2,1,1/2]
+),
+
+"bartlett-symmetric": \(
+    b = win.windowFunction (Bartlett ()) [ Symmetric true ];
+    compare (vec.list (b 1)) [1] and
+       compare (vec.list (b 2)) [0,0] and
+       compare (vec.list (b 3)) [0,1,0] and
+       compare (vec.list (b 4)) [0,2/3,2/3,0] and
+       compare (vec.list (b 5)) [0,1/2,1,1/2,0]
+),
+
+] is hash<string, () -> boolean>;
+
--- a/yetilab/signal/window.yeti	Thu May 23 11:21:30 2013 +0100
+++ b/yetilab/signal/window.yeti	Thu May 23 13:21:05 2013 +0100
@@ -29,6 +29,34 @@
     Periodic (): cosineWindowPeriodic;
     esac a0 a1 a2 a3 n;
 
+bartlettSymmetric n =
+    if n < 2 then vec.ones n
+    else
+        vec.fromList
+           (n1 = n - 1;
+            h = int (n1 / 2);
+            concat [
+                map do i:
+                    2 * i / n1
+                    done [0..h],
+                map do i:
+                    2 - (2 * i / n1)
+                    done [h+1..n1]
+                ]);
+    fi;
+
+bartlettPeriodic n = 
+    if n < 2 then vec.ones n
+    else
+        vec.slice (bartlettSymmetric (n+1)) 0 n;
+    fi;
+
+bartlett sampling =
+    case sampling of
+    Symmetric (): bartlettSymmetric;
+    Periodic (): bartlettPeriodic;
+    esac;
+
 hann = cosineWindow 0.5 0.5 0.0 0.0;
 hamming = cosineWindow 0.54 0.46 0.0 0.0;
 blackman = cosineWindow 0.42 0.50 0.08 0.0;
@@ -38,19 +66,7 @@
 
 boxcar = vec.consts 0.5;
 
-bartlett n =
-    vec.fromList
-       (m = n/2;
-        concat [
-            map do i:
-                i / m
-                done [0..m-1],
-            map do i:
-                1.0 - (i / m)
-                done [0..m-1]
-            ]);
-
-windowFunction type options n =
+windowFunction type options =
    (var sampling = Periodic ();
     for options \case of
         Symmetric s: if s then sampling := Symmetric () fi
@@ -63,7 +79,7 @@
     BlackmanNuttall (): blackmanNuttall sampling;
     BlackmanHarris (): blackmanHarris sampling;
     Boxcar (): boxcar;
-    Bartlett (): bartlett;
+    Bartlett (): bartlett sampling;
     esac);
 
 windowed windowFunc frames =
@@ -82,7 +98,8 @@
 nuttall = nuttall (Periodic ()), 
 blackmanNuttall = blackmanNuttall (Periodic ()), 
 blackmanHarris = blackmanHarris (Periodic ()),
-boxcar, bartlett, 
+boxcar,
+bartlett = bartlett (Periodic ()), 
 windowFunction,
 windowed
 };
--- a/yetilab/test/all.yeti	Thu May 23 11:21:30 2013 +0100
+++ b/yetilab/test/all.yeti	Thu May 23 13:21:05 2013 +0100
@@ -18,6 +18,7 @@
 "matrix"     : load yetilab.matrix.test.test_matrix,
 "plot"       : load yetilab.plot.test.test_plot,
 "signal"     : load yetilab.signal.test.test_signal,
+"window"     : load yetilab.signal.test.test_window,
 ];
 
 bad = sum (mapHash do name testHash: runTests name testHash done tests);
--- a/yetilab/vector/test/test_vector.yeti	Thu May 23 11:21:30 2013 +0100
+++ b/yetilab/vector/test/test_vector.yeti	Thu May 23 13:21:05 2013 +0100
@@ -90,6 +90,12 @@
         vec.equal (vec.resizedTo 2 (vec.fromList [1,2,3])) (vec.fromList [1,2]);
 ),
 
+"reversed": \(
+    vec.equal (vec.reversed (vec.fromList [])) (vec.fromList []) and
+        vec.equal (vec.reversed (vec.fromList [1,2,3])) (vec.fromList [3,2,1]) and
+        vec.equal (vec.reversed (vec.fromList [1,2])) (vec.fromList [2,1])
+),
+
 "concat2": \(
     v = vec.fromList [1,2,3];
     w = vec.fromList [4,5,6];
--- a/yetilab/vector/vector.yeti	Thu May 23 11:21:30 2013 +0100
+++ b/yetilab/vector/vector.yeti	Thu May 23 13:21:05 2013 +0100
@@ -77,6 +77,14 @@
 resizedTo n v is number -> ~double[] -> ~double[] =
     Arrays#copyOf(v, n);
 
+reversed v is ~double[] -> ~double[] =
+   (len = length (list v);
+    a = new double[len];
+    for [0..len-1] do i:
+        a[len-i-1] := v[i];
+    done;
+    a);
+
 concat vv is list?<~double[]> -> ~double[] =
    (len = sum (map length' vv);
     vout = zeros len;
@@ -106,6 +114,7 @@
     equalUnder,
     slice,
     resizedTo,
+    reversed,
     concat,
 } as {
     zeros is number -> vector,
@@ -125,6 +134,7 @@
     equalUnder is (number -> number -> boolean) -> vector -> vector -> boolean,
     slice is vector -> number -> number -> vector,
     resizedTo is number -> vector -> vector,
+    reversed is vector -> vector,
     concat is list?<vector> -> vector,
 }