1
1
#!python3
2
+ # Victor O. Costa (nov, 2019)
2
3
3
4
import numpy as np
5
+ import collections
4
6
5
7
class overlap_save :
6
8
""" Class containing the overlap-save algorithm for long 1D convolution betweem x and h signals """
@@ -12,15 +14,20 @@ def __init__(self):
12
14
13
15
self .block_size = 0 # Commonly appears as N in the literature
14
16
self .overlap = 0
15
-
17
+
18
+ def reset (self ):
19
+ self .x = []
20
+ self .h = []
21
+ self .block_size = 0
22
+
16
23
def set_signals (self , x , h ):
17
- """ """
24
+ """ Define long signal x and the corresponding filter impulse response h """
18
25
19
26
# Checks for invalid signals
20
27
if len (x ) < len (h ):
21
28
print ("Please enter the long signal as x" )
22
29
exit (- 1 )
23
- if len (x ) == 0 or len (y ) == 0 :
30
+ if len (x ) == 0 or len (h ) == 0 :
24
31
print ("Please enter valid signals" )
25
32
exit (- 1 )
26
33
@@ -29,33 +36,29 @@ def set_signals(self, x, h):
29
36
self .overlap = len (h ) - 1
30
37
31
38
32
- def reset (self ):
33
- self .x = []
34
- self .h = []
35
-
36
-
37
39
def set_block_size (self , block_size ):
38
- """ Define the block """
40
+ """ Define the size of each data block """
39
41
40
- if len (x ) == 0 or len (y ) == 0 :
42
+ if len (self . x ) == 0 or len (self . h ) == 0 :
41
43
print ("Please set signals before block size" )
42
44
exit (- 1 )
43
45
44
46
if block_size <= 0 :
45
47
print ("Please enter a valid block size" )
46
48
exit (- 1 )
47
49
48
- if block_size <= len (h ) - 1 :
50
+ if block_size <= len (self . h ) - 1 :
49
51
print ("Block size should be greater than length of h - 1" )
50
52
exit (- 1 )
51
53
52
54
self .block_size = block_size
53
55
54
56
55
57
# Irregular list flattening (Josh Lee, https://stackoverflow.com/a/2158522/6129362
56
- def flatten (x ):
58
+ def _flatten_list (self , x ):
59
+ """ Flattens a list composed of lists and elements """
57
60
if isinstance (x , collections .Iterable ):
58
- return [a for i in x for a in flatten (i )]
61
+ return [a for i in x for a in self . _flatten_list (i )]
59
62
else :
60
63
return [x ]
61
64
@@ -66,27 +69,49 @@ def flatten(x):
66
69
def circular_convolution (self , x_block , h ):
67
70
""" Computes circular convolution theorem using the fft """
68
71
## Padding
69
- # Append zeroes in the begining of h to equal h_padded and block_len sizes
70
- h_padded = h
71
- h_padded .append ([0 ]* (len (x_block )- len (h )))
72
- h_padded = flatten (h_padded )
73
- print (h_padded )
72
+ # Appends zeroes in the begining of h to equal h_padded and block_len sizes
73
+ h_padded = list (h )
74
+ h_padded .extend ([0 ] * (len (x_block )- len (h )))
74
75
75
76
# From circular convolution theorem
76
- x_convolved = np .real (np .fft .ifft ( np .fft .fft (x_block ) * np .fft .fft (h_padded ) ))
77
+ x_convolved = np .real (np .fft .ifft (np .fft .fft (x_block ) * np .fft .fft (h_padded )))
77
78
return x_convolved
78
79
79
80
80
- def apply_convolution (self ):
81
- """ """
81
+ def overlap_save_convolution (self ):
82
+ """ Apply the filter defined by impulse response h over the long signal x using the overlap-save method """
82
83
83
84
if self .block_size == 0 :
84
85
print ("Please set block size before applying convolution" )
85
86
86
- #loop over X, len(h)-1 steps each time, with each subX being the past block_size positions
87
-
88
- for
89
-
90
- return
91
-
92
-
87
+ # In the beginning there is no past block to overlap from, so the first M-1 positions are set to zero
88
+ x_padded = list (self .x )
89
+ x_padded .insert (0 ,[0 ]* (len (self .h ) - 1 ))
90
+ x_padded = self ._flatten_list (x_padded )
91
+
92
+ # Defines convolution result
93
+ y = []
94
+
95
+ # loop over x len(h) steps at time, creating a overlap of M-1 positions
96
+ for i in range (0 , len (x_padded ), len (self .h )):
97
+ ## Define x block
98
+ # Covers the border case of the last block not being N-sized by appending N - (Len(x_padded) - i) zeroes to it
99
+ if len (x_padded ) - i < self .block_size :
100
+ x_block = x_padded [i : len (x_padded )]
101
+ x_block .extend ([0 ] * (self .block_size - (len (x_padded ) - i )))
102
+
103
+ # For all other blocks, simply copy x_padded from i to i+N-1
104
+ else :
105
+ x_block = x_padded [i : i + self .block_size ]
106
+
107
+ print ("X_bloc, y_local" )
108
+ print (x_block )
109
+ # The partial result for a given block is the circular convolution between the signal block and filter impulse response
110
+ y_local = self .circular_convolution (x_block , self .h )
111
+ print (y_local )
112
+ print (len (self .h ))
113
+
114
+ # Keeps only values unnafected by the filter delay of circular convolution, achieving similar result as the linear convolution
115
+ y .extend (y_local [len (self .h ) - 1 :])
116
+
117
+ return y
0 commit comments