forked from NectGmbH/go-dsp
/
radix2.go
199 lines (162 loc) · 4.25 KB
/
radix2.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*
* Copyright (c) 2011 Matt Jibson <matt.jibson@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package fft
import (
"math"
"runtime"
"sync"
)
var (
radix2Lock sync.RWMutex
radix2Factors = map[int][]complex128{
4: {complex(1, 0), complex(0, -1), complex(-1, 0), complex(0, 1)},
}
)
// EnsureRadix2Factors ensures that all radix 2 factors are computed for inputs
// of length input_len. This is used to precompute needed factors for known
// sizes. Generally should only be used for benchmarks.
func EnsureRadix2Factors(input_len int) {
getRadix2Factors(input_len)
}
func getRadix2Factors(input_len int) []complex128 {
radix2Lock.RLock()
if hasRadix2Factors(input_len) {
defer radix2Lock.RUnlock()
return radix2Factors[input_len]
}
radix2Lock.RUnlock()
radix2Lock.Lock()
defer radix2Lock.Unlock()
if !hasRadix2Factors(input_len) {
for i, p := 8, 4; i <= input_len; i, p = i<<1, i {
if radix2Factors[i] == nil {
radix2Factors[i] = make([]complex128, i)
for n, j := 0, 0; n < i; n, j = n+2, j+1 {
radix2Factors[i][n] = radix2Factors[p][j]
}
for n := 1; n < i; n += 2 {
sin, cos := math.Sincos(-2 * math.Pi / float64(i) * float64(n))
radix2Factors[i][n] = complex(cos, sin)
}
}
}
}
return radix2Factors[input_len]
}
func hasRadix2Factors(idx int) bool {
return radix2Factors[idx] != nil
}
type fft_work struct {
start, end int
}
// radix2FFT returns the FFT calculated using the radix-2 DIT Cooley-Tukey algorithm.
func radix2FFT(x []complex128) []complex128 {
lx := len(x)
factors := getRadix2Factors(lx)
t := make([]complex128, lx) // temp
r := reorderData(x)
var blocks, stage, s_2 int
jobs := make(chan *fft_work, lx)
wg := sync.WaitGroup{}
num_workers := worker_pool_size
if (num_workers) == 0 {
num_workers = runtime.GOMAXPROCS(0)
}
idx_diff := lx / num_workers
if idx_diff < 2 {
idx_diff = 2
}
worker := func() {
for work := range jobs {
for nb := work.start; nb < work.end; nb += stage {
if stage != 2 {
for j := 0; j < s_2; j++ {
idx := j + nb
idx2 := idx + s_2
ridx := r[idx]
w_n := r[idx2] * factors[blocks*j]
t[idx] = ridx + w_n
t[idx2] = ridx - w_n
}
} else {
n1 := nb + 1
rn := r[nb]
rn1 := r[n1]
t[nb] = rn + rn1
t[n1] = rn - rn1
}
}
wg.Done()
}
}
for i := 0; i < num_workers; i++ {
go worker()
}
defer close(jobs)
for stage = 2; stage <= lx; stage <<= 1 {
blocks = lx / stage
s_2 = stage / 2
for start, end := 0, stage; ; {
if end-start >= idx_diff || end == lx {
wg.Add(1)
jobs <- &fft_work{start, end}
if end == lx {
break
}
start = end
}
end += stage
}
wg.Wait()
r, t = t, r
}
return r
}
// reorderData returns a copy of x reordered for the DFT.
func reorderData(x []complex128) []complex128 {
lx := uint(len(x))
r := make([]complex128, lx)
s := log2(lx)
var n uint
for ; n < lx; n++ {
r[reverseBits(n, s)] = x[n]
}
return r
}
// log2 returns the log base 2 of v
// from: http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
func log2(v uint) uint {
var r uint
for v >>= 1; v != 0; v >>= 1 {
r++
}
return r
}
// reverseBits returns the first s bits of v in reverse order
// from: http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious
func reverseBits(v, s uint) uint {
var r uint
// Since we aren't reversing all the bits in v (just the first s bits),
// we only need the first bit of v instead of a full copy.
r = v & 1
s--
for v >>= 1; v != 0; v >>= 1 {
r <<= 1
r |= v & 1
s--
}
return r << s
}