line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package PDL::DSP::Fir::Simple; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
247894
|
use 5.006; |
|
1
|
|
|
|
|
5
|
|
4
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
26
|
|
5
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
17
|
|
|
1
|
|
|
|
|
58
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '0.005'; |
8
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
|
6
|
use base 'Exporter'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
122
|
|
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
6
|
use PDL::LiteF; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
9
|
|
12
|
1
|
|
|
1
|
|
3803
|
use PDL::ImageND('convolveND'); |
|
1
|
|
|
|
|
4378
|
|
|
1
|
|
|
|
|
10
|
|
13
|
1
|
|
|
1
|
|
192
|
use PDL::NiceSlice; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
10
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# first line is compat. with older pdl. |
16
|
1
|
|
|
1
|
|
2669
|
use constant PI => 4 * atan2(1, 1); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
88
|
|
17
|
|
|
|
|
|
|
#use PDL::Constants qw(PI); |
18
|
|
|
|
|
|
|
|
19
|
1
|
|
|
1
|
|
702
|
use PDL::DSP::Fir; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
430
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
22
|
|
|
|
|
|
|
our @EXPORT_OK = qw( filter testdata ); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
$PDL::onlinedoc->scan(__FILE__) if $PDL::onlinedoc; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 NAME |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
PDL::DSP::Simple - Simple interface to windowed sinc filters. |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head1 SYNOPSIS |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
use PDL::LiteF; |
33
|
|
|
|
|
|
|
use PDL::DSP::Fir::Simple; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 DESCRIPTION |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
At present, this module provides one filtering |
38
|
|
|
|
|
|
|
routine. The main purpose is to provide an easy-to-use |
39
|
|
|
|
|
|
|
lowpass filter that only requires the user to provide the |
40
|
|
|
|
|
|
|
data and the cutoff frequency. However, the routines take |
41
|
|
|
|
|
|
|
options to give the user more control over the |
42
|
|
|
|
|
|
|
filtering. The module implements the filters via convolution |
43
|
|
|
|
|
|
|
with a kernel representing a finite impulse response |
44
|
|
|
|
|
|
|
function, either directly or with fft. The filter kernel is |
45
|
|
|
|
|
|
|
constructed from windowed sinc functions. Available filters |
46
|
|
|
|
|
|
|
are lowpass, highpass, bandpass, and bandreject. All window |
47
|
|
|
|
|
|
|
functions in L are available. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
See L for a moving average filter. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
Some of this functionality is already available in the PDL core. |
52
|
|
|
|
|
|
|
The modules L and L (time series) also have |
53
|
|
|
|
|
|
|
filtering functions. |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
Below, the word B refers to the number of elements in the filter |
56
|
|
|
|
|
|
|
kernel. The default value is equal to the number of elements in the data |
57
|
|
|
|
|
|
|
to be filtered. |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
No functions are exported by default. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head1 FUNCTIONS |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=head2 filter |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
$xf = filter($x, {OPTIONS}); |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
or |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
$xf = filter($x, $kern); |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=head3 Examples |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=for example |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
Apply lowpass filter to signal $x with a cutoff frequency of 90% of the |
76
|
|
|
|
|
|
|
Nyquist frequency (i.e. 45% of the sample frequency). |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
$xf = filter($x, { fc => 0.9 }); |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
Apply a highpass filter rather than the default lowpass filter |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
$xf = filter($x, {fc => 0.9 , type => 'highpass' }); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
Apply a lowpass filter of order 20 with a blackman window, rather than the default hamming window. |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
$xf = filter($x, {fc => 0.9 , window => 'blackman' , N => 20 }); |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
Apply a 10 point moving average. Note that this moving averaging is implemented via |
91
|
|
|
|
|
|
|
convolution. This is a relatively inefficient implementation. |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
$xf = filter($x, {window => 'rectangular', type => 'window', N => 10 }); |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
Return the kernel used in the convolution. |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
($xf, $kern) = filter($x, { fc => 0.9 }); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Apply a lowpass filter of order 20 with a tukey window with parameter I = 0.5. |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
$xf = filter($x, {fc => 0.9 , |
103
|
|
|
|
|
|
|
window => { name => 'tukey', params => 0.5 } , N => 20 }); |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head3 OPTIONS |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=over |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=item N |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Order of filter. I.e. the number of points in the filter kernel. |
112
|
|
|
|
|
|
|
If this option is not given, or is undefined, or false, or less than |
113
|
|
|
|
|
|
|
zero, then the order of the filter is equal to the number of points |
114
|
|
|
|
|
|
|
in the data C<$x>. |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=item kern |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
A kernel to use for convolution rather than calculating a kernel |
119
|
|
|
|
|
|
|
from other parameters. |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=item boundary |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Boundary condition passed to C. Must be one of |
124
|
|
|
|
|
|
|
'extend', 'truncate', 'periodic'. See L. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=back |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
All other options to C are passed to the function L which creates the filter kernel. |
129
|
|
|
|
|
|
|
L in turn passes options to L. |
130
|
|
|
|
|
|
|
The option C is either a string giving the name of the window function, or |
131
|
|
|
|
|
|
|
an anonymous hash of options to pass to L. |
132
|
|
|
|
|
|
|
For example C<< { name => 'window_name', ... } >>. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
If the second argument is not a hash of options then it is interpreted as a |
135
|
|
|
|
|
|
|
kernel C<$kern> to be convolved with the C<$data>. |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
If called in a list context, the filtered data and kernel ($dataf,$kern) |
138
|
|
|
|
|
|
|
are returned. |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=cut |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub filter { |
143
|
1
|
|
|
1
|
1
|
1429
|
my ($dat,$iopts) = @_; |
144
|
1
|
|
|
|
|
36
|
my ($kern, $boundary); |
145
|
1
|
50
|
|
|
|
10
|
if (ref $iopts eq 'HASH') { |
146
|
1
|
|
50
|
|
|
14
|
$boundary = delete $iopts->{boundary} || 'periodic'; |
147
|
1
|
50
|
33
|
|
|
19
|
$iopts->{N} = ($iopts->{N} and $iopts->{N} > 0) ? $iopts->{N} : $dat->nelem; |
148
|
1
|
|
33
|
|
|
14
|
$kern = $iopts->{kern} || PDL::DSP::Fir::firwin($iopts); |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
else { |
151
|
0
|
|
|
|
|
0
|
$kern = $iopts; |
152
|
|
|
|
|
|
|
} |
153
|
0
|
|
|
|
|
0
|
my $fdat = convolveND($dat, $kern, { boundary => $boundary}); |
154
|
0
|
0
|
|
|
|
0
|
return wantarray ? ($fdat,$kern) : $fdat; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# hmm, this is almost like exporting. maybe don't do it. |
158
|
|
|
|
|
|
|
# *PDL::filter = \&filter; |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=head2 testdata |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
$x = testdata($Npts, $freqs, $amps) |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
For example: |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
$x = testdata(1000, [5,100], [1, .1] ); |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
Generate a signal by summing sine functions of differing |
169
|
|
|
|
|
|
|
frequencies. The signal has $Npts |
170
|
|
|
|
|
|
|
elements. $freqs is an array of frequencies, and $amps an |
171
|
|
|
|
|
|
|
array of amplitudes for each frequency. The frequencies should |
172
|
|
|
|
|
|
|
be between 0 and 1, with 1 representing the nyquist frequency. |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=cut |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub testdata { |
177
|
1
|
|
|
1
|
1
|
11
|
my ($M, $f, $amp) = @_; |
178
|
1
|
|
|
|
|
7
|
my $x = zeroes($M); |
179
|
1
|
|
|
|
|
203
|
my $n = sequence($M); |
180
|
1
|
|
|
|
|
146
|
foreach (@$f) { |
181
|
3
|
|
|
|
|
3505
|
$x = $x + (shift @$amp)*sin(PI*$n*($_)); |
182
|
|
|
|
|
|
|
} |
183
|
1
|
|
|
|
|
464
|
return $x; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
=head1 AUTHOR |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
John Lapeyre, C<< >> |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
Copyright 2012 John Lapeyre. |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
195
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published |
196
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
See http://dev.perl.org/licenses/ for more information. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=cut |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
1; # End of PDL::DSP::Fir::Simple |