dsp/window: add lookup table window functions

This commit is contained in:
Dan Kortschak
2020-02-20 11:15:43 +10:30
parent efc4dabf2a
commit c9a7355ed9
4 changed files with 125 additions and 18 deletions

View File

@@ -323,3 +323,32 @@ func Gaussian(seq []float64, sigma float64) []float64 {
} }
return seq return seq
} }
// Values is an arbitrary real window function.
type Values []float64
// NewValues returns a Values of length n with weights corresponding to the
// provided window function.
func NewValues(window func([]float64) []float64, n int) Values {
v := make(Values, n)
for i := range v {
v[i] = 1
}
return window(v)
}
// Transform applies the weights in the receiver to seq in place, returning the
// result. If v is nil, Transform is a no-op, otherwise the length of v must
// match the length of seq.
func (v Values) Transform(seq []float64) []float64 {
if v == nil {
return seq
}
if len(v) != len(seq) {
panic("window: length mismatch")
}
for i, w := range v {
seq[i] *= w
}
return seq
}

View File

@@ -323,3 +323,32 @@ func GaussianComplex(seq []complex128, sigma float64) []complex128 {
} }
return seq return seq
} }
// ValuesComplex is an arbitrary complex window function.
type ValuesComplex []complex128
// NewValuesComplex returns a ValuesComplex of length n with weights corresponding
// to the provided window function.
func NewValuesComplex(window func([]complex128) []complex128, n int) ValuesComplex {
v := make(ValuesComplex, n)
for i := range v {
v[i] = 1
}
return window(v)
}
// Transform applies the weights in the receiver to seq in place, returning the
// result. If v is nil, Transform is a no-op, otherwise the length of v must
// match the length of seq.
func (v ValuesComplex) Transform(seq []complex128) []complex128 {
if v == nil {
return seq
}
if len(v) != len(seq) {
panic("window: length mismatch")
}
for i, w := range v {
seq[i] *= w
}
return seq
}

View File

@@ -103,3 +103,18 @@ func ExampleHamming() {
// srcCpy: [0.092577 0.136714 0.220669 0.336222 0.472063 0.614894 0.750735 0.866288 0.950242 0.994379 0.994379 0.950242 0.866288 0.750735 0.614894 0.472063 0.336222 0.220669 0.136714 0.092577] // srcCpy: [0.092577 0.136714 0.220669 0.336222 0.472063 0.614894 0.750735 0.866288 0.950242 0.994379 0.994379 0.950242 0.866288 0.750735 0.614894 0.472063 0.336222 0.220669 0.136714 0.092577]
// dst: [0.092577 0.136714 0.220669 0.336222 0.472063 0.614894 0.750735 0.866288 0.950242 0.994379 0.994379 0.950242 0.866288 0.750735 0.614894 0.472063 0.336222 0.220669 0.136714 0.092577] // dst: [0.092577 0.136714 0.220669 0.336222 0.472063 0.614894 0.750735 0.866288 0.950242 0.994379 0.994379 0.950242 0.866288 0.750735 0.614894 0.472063 0.336222 0.220669 0.136714 0.092577]
} }
func ExampleValues() {
src := []float64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
// Create a Sine Window lookup table.
sine := window.NewValues(window.Sine, len(src))
// Apply the transformation to the src.
fmt.Printf("dst: %f\n", sine.Transform(src))
// Output:
//
// dst: [0.078459 0.233445 0.382683 0.522499 0.649448 0.760406 0.852640 0.923880 0.972370 0.996917 0.996917 0.972370 0.923880 0.852640 0.760406 0.649448 0.522499 0.382683 0.233445 0.078459]
}

View File

@@ -150,10 +150,18 @@ func TestWindows(t *testing.T) {
} }
dst := test.fn(src) dst := test.fn(src)
if !floats.EqualApprox(dst, test.want, tol) { if !floats.EqualApprox(dst, test.want, tol) {
t.Errorf("unexpected result for window function %q:\ngot:%#.6v\nwant:%#v", test.name, dst, test.want) t.Errorf("unexpected result for window function %q:\ngot:%#.6v\nwant:%#v", test.name, dst, test.want)
} }
for i := range src {
src[i] = 1
}
dst = NewValues(test.fn, len(src)).Transform(src)
if !floats.EqualApprox(dst, test.want, tol) {
t.Errorf("unexpected result for lookup window function %q:\ngot:%#.6v\nwant:%#.6v", test.name, dst, test.want)
}
}) })
} }
} }
@@ -161,20 +169,29 @@ func TestWindows(t *testing.T) {
func TestGausWindows(t *testing.T) { func TestGausWindows(t *testing.T) {
const tol = 1e-6 const tol = 1e-6
src := make([]float64, 20)
for i := range src {
src[i] = 1
}
for _, test := range gausWindowTests { for _, test := range gausWindowTests {
t.Run(fmt.Sprintf("%s (sigma=%.1f)", test.name, test.sigma), func(t *testing.T) { t.Run(fmt.Sprintf("%s (sigma=%.1f)", test.name, test.sigma), func(t *testing.T) {
srcCpy := make([]float64, len(src)) src := make([]float64, 20)
copy(srcCpy, src) for i := range src {
dst := Gaussian(srcCpy, test.sigma) src[i] = 1
}
dst := Gaussian(src, test.sigma)
if !floats.EqualApprox(dst, test.want, tol) { if !floats.EqualApprox(dst, test.want, tol) {
t.Errorf("unexpected result for window function %q:\ngot:%#.6v\nwant:%#v", test.name, dst, test.want) t.Errorf("unexpected result for window function %q:\ngot:%#.6v\nwant:%#v", test.name, dst, test.want)
} }
for i := range src {
src[i] = 1
}
sigma := test.sigma
dst = NewValues(func(seq []float64) []float64 {
return Gaussian(seq, sigma)
}, len(src)).Transform(src)
if !floats.EqualApprox(dst, test.want, tol) {
t.Errorf("unexpected result for lookup window function %q:\ngot:%#.6v\nwant:%#.6v", test.name, dst, test.want)
}
}) })
} }
} }
@@ -190,10 +207,18 @@ func TestWindowsComplex(t *testing.T) {
} }
dst := test.fnCmplx(src) dst := test.fnCmplx(src)
if !equalApprox(dst, test.want, tol) { if !equalApprox(dst, test.want, tol) {
t.Errorf("unexpected result for window function %q:\ngot:%#.6v\nwant:%#.6v", test.name, dst, test.want) t.Errorf("unexpected result for window function %q:\ngot:%#.6v\nwant:%#.6v", test.name, dst, test.want)
} }
for i := range src {
src[i] = complex(1, 1)
}
dst = NewValuesComplex(test.fnCmplx, len(src)).Transform(src)
if !equalApprox(dst, test.want, tol) {
t.Errorf("unexpected result for lookup window function %q:\ngot:%#.6v\nwant:%#.6v", test.name, dst, test.want)
}
}) })
} }
} }
@@ -201,20 +226,29 @@ func TestWindowsComplex(t *testing.T) {
func TestGausWindowComplex(t *testing.T) { func TestGausWindowComplex(t *testing.T) {
const tol = 1e-6 const tol = 1e-6
src := make([]complex128, 20)
for i := range src {
src[i] = complex(1, 1)
}
for _, test := range gausWindowTests { for _, test := range gausWindowTests {
t.Run(fmt.Sprintf("%sComplex (sigma=%.1f)", test.name, test.sigma), func(t *testing.T) { t.Run(fmt.Sprintf("%sComplex (sigma=%.1f)", test.name, test.sigma), func(t *testing.T) {
srcCpy := make([]complex128, len(src)) src := make([]complex128, 20)
copy(srcCpy, src) for i := range src {
dst := GaussianComplex(srcCpy, test.sigma) src[i] = complex(1, 1)
}
dst := GaussianComplex(src, test.sigma)
if !equalApprox(dst, test.want, tol) { if !equalApprox(dst, test.want, tol) {
t.Errorf("unexpected result for window function %q:\ngot:%#.6v\nwant:%#.6v", test.name, dst, test.want) t.Errorf("unexpected result for window function %q:\ngot:%#.6v\nwant:%#.6v", test.name, dst, test.want)
} }
for i := range src {
src[i] = complex(1, 1)
}
sigma := test.sigma
dst = NewValuesComplex(func(seq []complex128) []complex128 {
return GaussianComplex(seq, sigma)
}, len(src)).Transform(src)
if !equalApprox(dst, test.want, tol) {
t.Errorf("unexpected result for lookup window function %q:\ngot:%#.6v\nwant:%#.6v", test.name, dst, test.want)
}
}) })
} }
} }