File Coverage

blib/lib/PDL/DSP/Windows.pm
Criterion Covered Total %
statement 496 562 88.2
branch 137 242 56.6
condition 25 42 59.5
subroutine 105 113 92.9
pod 58 97 59.7
total 821 1056 77.7


line stmt bran cond sub pod time code
1             package PDL::DSP::Windows;
2              
3             our $VERSION = '0.007003'; # TRIAL
4              
5 6     6   784655 use strict;
  6         46  
  6         154  
6 6     6   27 use warnings;
  6         17  
  6         171  
7              
8 6     6   26 use Carp qw( croak carp );
  6         9  
  6         286  
9 6     6   2127 use PDL::LiteF;
  6         2096  
  6         22  
10 6     6   553579 use PDL::FFT;
  6         39361  
  6         36  
11 6     6   977 use PDL::Math qw( acos cosh acosh );
  6         13  
  6         24  
12 6     6   521 use PDL::Core qw( topdl );
  6         12  
  6         19  
13 6     6   453 use PDL::MatrixOps qw( eigens_sym );
  6         12  
  6         45  
14 6     6   373 use PDL::Options qw( iparse ifhref );
  6         10  
  6         275  
15 6     6   2627 use Variable::Magic qw( wizard cast );
  6         5959  
  6         593  
16              
17             use constant {
18             HAVE_LinearAlgebra => eval { require PDL::LinearAlgebra::Special; 1 } || 0,
19             HAVE_BESSEL => eval { require PDL::GSLSF::BESSEL; 1 } || 0,
20             HAVE_GNUPLOT => eval { require PDL::Graphics::Gnuplot; 1 } || 0,
21 6   50     10 USE_FFTW_DIRECTION => do {
      50        
      50        
22 6     6   2134 use version;
  6         9708  
  6         33  
23 6         921 version->parse($PDL::VERSION) <= version->parse('2.007');
24             },
25 6     6   522 };
  6         13  
26              
27 6     6   2499 use namespace::clean;
  6         64701  
  6         46  
28              
29             # These constants defined after cleaning our namespace to avoid
30             # breaking existing code that might use them.
31 6     6   13451 use constant PI => 4 * atan2(1, 1);
  6         12  
  6         437  
32 6     6   33 use constant TPI => 2 * PI;
  6         11  
  6         231  
33              
34 6     6   27 use Exporter 'import';
  6         12  
  6         50391  
35              
36             my %symmetric_windows = (
37             bartlett => 1,
38             bartlett_hann => 1,
39             blackman => 1,
40             blackman_bnh => 1,
41             blackman_ex => 1,
42             blackman_gen => 1,
43             blackman_gen3 => 1,
44             blackman_gen4 => 1,
45             blackman_gen5 => 1,
46             blackman_harris => 1,
47             blackman_harris4 => 1,
48             blackman_nuttall => 1,
49             bohman => 1,
50             cauchy => 1,
51             chebyshev => 1,
52             cos_alpha => 1,
53             cosine => 1,
54             dpss => 1,
55             exponential => 1,
56             flattop => 1,
57             gaussian => 1,
58             hamming => 1,
59             hamming_ex => 1,
60             hamming_gen => 1,
61             hann => 1,
62             hann_matlab => 1,
63             hann_poisson => 1,
64             kaiser => 1,
65             lanczos => 1,
66             nuttall => 1,
67             nuttall1 => 1,
68             parzen => 1,
69             parzen_octave => 1,
70             poisson => 1,
71             rectangular => 1,
72             triangular => 1,
73             tukey => 1,
74             welch => 1,
75             );
76              
77             my %periodic_windows = (
78             bartlett => 1,
79             bartlett_hann => 1,
80             blackman => 1,
81             blackman_bnh => 1,
82             blackman_ex => 1,
83             blackman_gen => 1,
84             blackman_gen3 => 1,
85             blackman_gen4 => 1,
86             blackman_gen5 => 1,
87             blackman_harris => 1,
88             blackman_harris4 => 1,
89             blackman_nuttall => 1,
90             bohman => 1,
91             cauchy => 1,
92             cos_alpha => 1,
93             cosine => 1,
94             dpss => 1,
95             exponential => 1,
96             flattop => 1,
97             gaussian => 1,
98             hamming => 1,
99             hamming_ex => 1,
100             hamming_gen => 1,
101             hann => 1,
102             hann_poisson => 1,
103             kaiser => 1,
104             lanczos => 1,
105             nuttall => 1,
106             nuttall1 => 1,
107             parzen => 1,
108             poisson => 1,
109             rectangular => 1,
110             triangular => 1,
111             tukey => 1,
112             welch => 1,
113             );
114              
115             my %window_aliases = (
116             bartlett_hann => [ 'Modified Bartlett-Hann' ],
117             bartlett => [ 'fejer' ],
118             blackman_harris4 => [ 'Blackman-Harris' ],
119             blackman_harris => [ 'Minimum three term (sample) Blackman-Harris' ],
120             cauchy => [ 'Abel', 'Poisson' ],
121             chebyshev => [ 'Dolph-Chebyshev' ],
122             cos_alpha => [ 'Power-of-cosine' ],
123             cosine => [ 'sine' ],
124             dpss => [ 'sleppian' ],
125             gaussian => [ 'Weierstrass' ],
126             hann => [ 'hanning' ],
127             kaiser => [ 'Kaiser-Bessel' ],
128             lanczos => [ 'sinc' ],
129             parzen => [ 'Jackson', 'Valle-Poussin' ],
130             rectangular => [ 'dirichlet', 'boxcar' ],
131             tukey => [ 'tapered cosine' ],
132             welch => [ 'Riez', 'Bochner', 'Parzen', 'parabolic' ],
133             );
134              
135             my %window_parameters = (
136             blackman_gen => [ '$alpha' ],
137             blackman_gen3 => [ '$a0', '$a1', '$a2' ],
138             blackman_gen4 => [ '$a0', '$a1', '$a2', '$a3' ],
139             blackman_gen5 => [ '$a0', '$a1', '$a2', '$a3', '$a4' ],
140             cauchy => [ '$alpha' ],
141             chebyshev => [ '$at' ],
142             cos_alpha => [ '$alpha' ],
143             dpss => [ '$beta' ],
144             gaussian => [ '$beta' ],
145             hamming_gen => [ '$a' ],
146             hann_poisson => [ '$alpha' ],
147             kaiser => [ '$beta' ],
148             poisson => [ '$alpha' ],
149             tukey => [ '$alpha' ],
150             );
151              
152             my %window_names = (
153             bartlett_hann => 'Bartlett-Hann',
154             blackman_bnh => '*An improved version of the 3-term Blackman-Harris window given by Nuttall (Ref 2, p. 89).',
155             blackman_ex => q!'exact' Blackman!,
156             blackman_gen => '*A single parameter family of the 3-term Blackman window. ',
157             blackman_gen3 => '*The general form of the Blackman family. ',
158             blackman_gen4 => '*The general 4-term Blackman-Harris window. ',
159             blackman_gen5 => '*The general 5-term Blackman-Harris window. ',
160             blackman_harris4 => 'minimum (sidelobe) four term Blackman-Harris',
161             blackman_harris => 'Blackman-Harris',
162             blackman_nuttall => 'Blackman-Nuttall',
163             blackman => q!'classic' Blackman!,
164             dpss => 'Digital Prolate Spheroidal Sequence (DPSS)',
165             flattop => 'flat top',
166             hamming_ex => q!'exact' Hamming!,
167             hamming_gen => 'general Hamming',
168             hann_matlab => '*Equivalent to the Hann window of N+2 points, with the endpoints (which are both zero) removed.',
169             hann_poisson => 'Hann-Poisson',
170             nuttall1 => '*A window referred to as the Nuttall window.',
171             parzen_octave => 'Parzen',
172             );
173              
174             my %window_print_names = (
175             blackman_bnh => 'Blackman-Harris (bnh)',
176             blackman_gen => 'General classic Blackman',
177             hann_matlab => 'Hann (matlab)',
178             nuttall1 => 'Nuttall (v1)',
179             );
180              
181             our @EXPORT_OK = (
182             keys %symmetric_windows,
183             keys %periodic_windows,
184             qw( window list_windows chebpoly cos_mult_to_pow cos_pow_to_mult ),
185             );
186              
187             $PDL::onlinedoc->scan(__FILE__) if $PDL::onlinedoc;
188              
189             our %winsubs;
190             our %winpersubs;
191             our %window_definitions;
192              
193             =encoding utf8
194              
195             =head1 NAME
196              
197             PDL::DSP::Windows - Window functions for signal processing
198              
199             =head1 SYNOPSIS
200              
201             use PDL;
202             use PDL::DSP::Windows('window');
203             my $samples = window( 10, 'tukey', { params => .5 });
204              
205             use PDL;
206             use PDL::DSP::Windows;
207             my $win = PDL::DSP::Windows->new( 10, 'tukey', { params => .5 });
208             print $win->coherent_gain, "\n";
209             $win->plot;
210              
211             =head1 DESCRIPTION
212              
213             This module provides symmetric and periodic (DFT-symmetric) window functions
214             for use in filtering and spectral analysis. It provides a high-level access
215             subroutine L. This functional interface is sufficient for getting
216             the window samples. For analysis and plotting, etc. an object oriented
217             interface is provided. The functional subroutines must be either explicitly
218             exported, or fully qualified. In this document, the word I refers
219             only to the mathematical window functions, while the word I is
220             used to describe code.
221              
222             Window functions are also known as apodization functions or tapering functions.
223             In this module, each of these functions maps a sequence of C<$N> integers to
224             values called a B. (To confuse matters, the word I also has
225             other meanings when describing window functions.) The functions are often
226             named for authors of journal articles. Be aware that across the literature
227             and software, some functions referred to by several different names, and some
228             names refer to several different functions. As a result, the choice of window
229             names is somewhat arbitrary.
230              
231             The L window function requires L. The
232             L window function requires L. But the
233             remaining window functions may be used if these modules are not installed.
234              
235             The most common and easiest usage of this module is indirect, via some
236             higher-level filtering interface, such as L. The next
237             easiest usage is to return a pdl of real-space samples with the subroutine
238             L. Finally, for analyzing window functions, object methods, such as
239             L, L, L are provided.
240              
241             In the following, first the functional interface (non-object oriented) is
242             described in L. Next, the object methods are
243             described in L. Next the low-level subroutines returning samples
244             for each named window are described in L. Finally, some
245             support routines that may be of interest are described in
246             L.
247              
248             =head1 FUNCTIONAL INTERFACE
249              
250             =head2 window
251              
252             $win = window({ OPTIONS });
253             $win = window( $N, { OPTIONS });
254             $win = window( $N, $name, { OPTIONS });
255             $win = window( $N, $name, $params, { OPTIONS });
256             $win = window( $N, $name, $params, $periodic );
257              
258             Returns an C<$N> point window of type C<$name>. The arguments may be passed
259             positionally in the order C<$N, $name, $params, $periodic>, or they may be
260             passed by name in the hash C.
261              
262             =head3 EXAMPLES
263              
264             # Each of the following return a 100 point symmetric hamming window.
265              
266             $win = window(100);
267             $win = window( 100, 'hamming' );
268             $win = window( 100, { name => 'hamming' });
269             $win = window({ N => 100, name => 'hamming' });
270              
271             # Each of the following returns a 100 point symmetric hann window.
272              
273             $win = window( 100, 'hann' );
274             $win = window( 100, { name => 'hann' });
275              
276             # Returns a 100 point periodic hann window.
277              
278             $win = window( 100, 'hann', { periodic => 1 });
279              
280             # Returns a 100 point symmetric Kaiser window with alpha = 2.
281              
282             $win = window( 100, 'kaiser', { params => 2 });
283              
284             =head3 OPTIONS
285              
286             The options follow default PDL::Options rules. They may be abbreviated, and
287             are case-insensitive.
288              
289             =over
290              
291             =item B
292              
293             (string) name of window function. Default: C. This selects one of
294             the window functions listed below. Note that the suffix '_per', for periodic,
295             may be ommitted. It is specified with the option C<< periodic => 1 >>
296              
297             =item B
298              
299             ref to array of parameter or parameters for the window-function subroutine.
300             Only some window-function subroutines take parameters. If the subroutine takes
301             a single parameter, it may be given either as a number, or a list of one
302             number. For example C<3> or C<[3]>.
303              
304             =item B
305              
306             number of points in window function (the same as the order of the filter).
307             No default value.
308              
309             =item B
310              
311             If value is true, return a periodic rather than a symmetric window function.
312             Defaults to false, meaning "symmetric".
313              
314             =back
315              
316             =cut
317              
318 153     153 1 68432 sub window { PDL::DSP::Windows->new(@_)->samples }
319              
320             =head2 list_windows
321              
322             list_windows
323             list_windows STR
324              
325             C prints the names all of the available windows.
326             C prints only the names of windows matching the string C.
327              
328             =cut
329              
330             sub list_windows {
331 7     7 1 15128 my ($expr) = @_;
332              
333 7         12 my @match;
334 7 100       14 if ($expr) {
335 6         105 for my $name ( sort keys %symmetric_windows ) {
336 228 100       484 if ( $name =~ /$expr/ ) {
337 59         88 push @match, $name;
338 59         73 next;
339             }
340              
341 169   100     173 for my $alias ( grep /$expr/i, @{ $window_aliases{$name} // [] } ) {
  169         463  
342 1         5 push @match, "$name (alias $alias)";
343 1         3 next;
344             }
345             }
346             }
347             else {
348 1         19 @match = sort keys %symmetric_windows;
349             }
350              
351 7         231 print join( ', ', @match ), "\n";
352             }
353              
354             =head1 METHODS
355              
356             =head2 new
357              
358             =for usage
359              
360             my $win = PDL::DSP::Windows->new(ARGS);
361              
362             =for ref
363              
364             Create an instance of a Windows object. If C are given, the instance
365             is initialized. C are interpreted in exactly the same way as arguments
366             the subroutine L.
367              
368             =for example
369              
370             For example:
371              
372             my $win1 = PDL::DSP::Windows->new( 8, 'hann' );
373             my $win2 = PDL::DSP::Windows->new({ N => 8, name => 'hann' });
374              
375             =cut
376              
377             sub new {
378 177     177 1 22653 my $proto = shift;
379 177   33     639 my $self = bless {}, ref $proto || $proto;
380 177 100       569 $self->init(@_) if @_;
381 176         556 return $self;
382             }
383              
384             =head2 init
385              
386             =for usage
387              
388             $win->init(ARGS);
389              
390             =for ref
391              
392             Initialize (or reinitialize) a Windows object. C are interpreted in
393             exactly the same way as arguments the subroutine L.
394              
395             =for example
396              
397             For example:
398              
399             my $win = PDL::DSP::Windows->new( 8, 'hann' );
400             $win->init( 10, 'hamming' );
401              
402             =cut
403              
404             sub init {
405 190     190 1 10726 my $self = shift;
406              
407 190         258 my ( $N, $name, $params, $periodic );
408              
409 190 100       348 $N = shift unless ref $_[0];
410 190 100       342 $name = shift unless ref $_[0];
411 190 100       418 $params = shift unless ref $_[0] eq 'HASH';
412 190 100       322 $periodic = shift unless ref $_[0];
413              
414 190   100     855 my $opts = PDL::Options->new({
415             name => 'hamming',
416             periodic => 0, # symmetric or periodic
417             N => undef, # order
418             params => undef,
419             })->options( shift // {} );
420              
421 190   66     39666 $name ||= $opts->{name};
422 190   100     359 $N ||= $opts->{N};
423 190   66     666 $periodic ||= $opts->{periodic};
424 190   100     629 $params //= $opts->{params};
425 190 100 100     412 $params = [$params] if defined $params && !ref $params;
426              
427 190         350 $name =~ s/_per$//;
428              
429 190 100       366 my $windows = $periodic ? \%periodic_windows : \%symmetric_windows;
430 190 100       510 unless ( $windows->{$name}) {
431 1 50       3 my $type = $periodic ? 'periodic' : 'symmetric';
432 1         7 barf "window: Unknown $type window '$name'.";
433             }
434              
435 189         309 $self->{name} = $name;
436 189         298 $self->{N} = $N;
437 189         256 $self->{periodic} = $periodic;
438 189         235 $self->{params} = $params;
439 189 100       1022 $self->{code} = __PACKAGE__->can( $name . ( $periodic ? '_per' : '' ) );
440 189         684 $self->{samples} = undef;
441 189         252 $self->{modfreqs} = undef;
442              
443 189         459 return $self;
444             }
445              
446             =head2 samples
447              
448             =for usage
449              
450             $win->samples;
451              
452             =for ref
453              
454             Generate and return a reference to the piddle of C<$N> samples for the window
455             C<$win>. This is the real-space representation of the window.
456              
457             The samples are stored in the object C<$win>, but are regenerated every time
458             C is invoked. See the method L below.
459              
460             =for example
461              
462             For example:
463              
464             my $win = PDL::DSP::Windows->new( 8, 'hann' );
465             print $win->samples, "\n";
466              
467             =cut
468              
469             sub samples {
470 180     180 1 245 my $self = shift;
471 180   100     222 my @args = ( $self->{N}, @{ $self->{params} // [] } );
  180         645  
472 180         1096 $self->{samples} = $self->{code}->(@args);
473             }
474              
475             =head2 modfreqs
476              
477             =for usage
478              
479             $win->modfreqs;
480              
481             =for ref
482              
483             Generate and return a reference to the piddle of the modulus of the fourier
484             transform of the samples for the window C<$win>.
485              
486             These values are stored in the object C<$win>, but are regenerated every time
487             C is invoked. See the method L below.
488              
489             =head3 options
490              
491             =over
492              
493             =item min_bins => MIN
494              
495             This sets the minimum number of frequency bins. Defaults to 1000. If necessary,
496             the piddle of window samples are padded with zeros before the fourier transform
497             is performed.
498              
499             =back
500              
501             =cut
502              
503             sub modfreqs {
504 8     8 1 15 my $self = shift;
505 8         32 my %opts = iparse( { min_bins => 1000 }, ifhref(shift) );
506              
507 8         1996 my $data = $self->get_samples;
508              
509 8         1694 my $n = $data->nelem;
510 8 50       26 my $fn = $n > $opts{min_bins} ? 2 * $n : $opts{min_bins};
511              
512 8         12 $n--;
513              
514 8         19 my $freq = zeroes($fn);
515 8         794 $freq->slice("0:$n") .= $data;
516              
517 8         264 PDL::FFT::realfft($freq);
518              
519 8         5971 my $real = zeros($freq);
520 8         463 my $img = zeros($freq);
521 8         522 my $mid = ( $freq->nelem ) / 2 - 1;
522 8         14 my $mid1 = $mid + 1;
523              
524 8         26 $real->slice("0:$mid") .= $freq->slice("$mid:0:-1");
525 8         301 $real->slice("$mid1:-1") .= $freq->slice("0:$mid");
526 8         267 $img->slice("0:$mid") .= $freq->slice("-1:$mid1:-1");
527 8         348 $img->slice("$mid1:-1") .= $freq->slice("$mid1:-1");
528              
529 8         1120 return $self->{modfreqs} = $real ** 2 + $img ** 2;
530             }
531              
532             =head2 get
533              
534             =for usage
535              
536             my $windata = $win->get('samples');
537              
538             =for ref
539              
540             Get an attribute (or list of attributes) of the window C<$win>. If attribute
541             C is requested, then the samples are created with the method
542             L if they don't exist.
543              
544             =for example
545              
546             For example:
547              
548             my $win = PDL::DSP::Windows->new( 8, 'hann' );
549             print $win->get('samples'), "\n";
550              
551             =cut
552              
553             sub get {
554 5     5 1 3112 my $self = shift;
555 5         9 my @res;
556              
557 5         12 for (@_) {
558 7 100       20 if ($_ eq 'samples') {
    100          
559 2         7 push @res, $self->get_samples;
560             }
561             elsif ($_ eq 'modfreqs') {
562 2         4 push @res, $self->get_modfreqs;
563             }
564             else {
565 3         6 push @res, $self->{$_};
566             }
567             };
568              
569 5 100       183 return wantarray ? @res : $res[0];
570             }
571              
572             =head2 get_samples
573              
574             =for usage
575              
576             my $windata = $win->get_samples;
577              
578             =for ref
579              
580             Return a reference to the pdl of samples for the Window instance C<$win>. The
581             samples will be generated with the method L if and only if they have
582             not yet been generated.
583              
584             =cut
585              
586             sub get_samples {
587 33     33 1 4573 my $self = shift;
588 33 100       98 return $self->{samples} if defined $self->{samples};
589 21         39 return $self->samples;
590             }
591              
592             =head2 get_modfreqs
593              
594             =for usage
595              
596             my $winfreqs = $win->get_modfreqs;
597             my $winfreqs = $win->get_modfreqs(OPTS);
598              
599             =for ref
600              
601             Return a reference to the pdl of the frequency response (modulus of the DFT)
602             for the Window instance C<$win>.
603              
604             Options passed as a hash reference will be passed to the L. The
605             data are created with L if they don't exist. The data are also
606             created even if they already exist if options are supplied. Otherwise the
607             cached data are returned.
608              
609             =head3 options
610              
611             =over
612              
613             =item min_bins => MIN
614              
615             This sets the minimum number of frequency bins. See L. Defaults
616             to 1000.
617              
618             =back
619              
620             =cut
621              
622             sub get_modfreqs {
623 7     7 1 3591 my $self = shift;
624 7 100       20 return $self->modfreqs(@_) if @_;
625 5 100       20 return $self->{modfreqs} if defined $self->{modfreqs};
626 2         5 return $self->modfreqs;
627             }
628              
629             =head2 get_params
630              
631             =for usage
632              
633             my $params = $win->get_params;
634              
635             =for ref
636              
637             Create a new array containing the parameter values for the instance C<$win>
638             and return a reference to the array. Note that not all window types take
639             parameters.
640              
641             =cut
642              
643 2     2 1 8 sub get_params { shift->{params} }
644              
645 1     1 0 5 sub get_N { shift->{N} }
646              
647             =head2 get_name
648              
649             =for usage
650              
651             print $win->get_name, "\n";
652              
653             =for ref
654              
655             Return a name suitable for printing associated with the window C<$win>. This is
656             something like the name used in the documentation for the particular window
657             function. This is static data and does not depend on the instance.
658              
659             =cut
660              
661             sub get_name {
662 4     4 1 7 my $self = shift;
663              
664 4 100       11 if ( my $name = $window_print_names{ $self->{name} } ) {
665 1         6 return "$name window";
666             }
667              
668 3 100       10 if ( my $name = $window_names{ $self->{name} } ) {
669 2 100       12 return "$name window" unless $name =~ /^\*/;
670 1         5 return $name;
671             }
672              
673 1         7 return ucfirst $self->{name} . ' window';
674             }
675              
676             sub get_param_names {
677 4     4 0 6 my $self = shift;
678 4         9 my $params = $window_parameters{ $self->{name} };
679 4 100       12 return undef unless $params;
680 3         5 return [ @{$params} ];
  3         11  
681             }
682              
683             sub format_param_vals {
684 2     2 0 3 my $self = shift;
685              
686 2 50       3 my @params = @{ $self->{params} || [] };
  2         7  
687 2 50       8 return '' unless @params;
688              
689 2 50       3 my @names = @{ $self->get_param_names || [] };
  2         5  
690 2 50       6 return '' unless @names;
691              
692             join ', ', map {
693 2         6 my $name = $names[$_];
  4         5  
694 4         12 $name =~ s/^\$//;
695 4         19 join' = ', $name, $params[$_];
696             } 0 .. $#params;
697             }
698              
699             sub format_plot_param_vals {
700 0     0 0 0 my $ps = shift->format_param_vals;
701 0 0       0 return $ps ? ": $ps" : $ps;
702             }
703              
704             =head2 plot
705              
706             =for usage
707              
708             $win->plot;
709              
710             =for ref
711              
712             Plot the samples. Currently, only L is supported. The
713             default display type is used.
714              
715             =cut
716              
717             sub plot {
718 0     0 1 0 my $self = shift;
719 0         0 barf 'PDL::DSP::Windows::plot Gnuplot not available!' unless HAVE_GNUPLOT;
720              
721 0         0 PDL::Graphics::Gnuplot::plot(
722             title => $self->get_name . $self->format_plot_param_vals,
723             xlabel => 'Time (samples)',
724             ylabel => 'amplitude',
725             $self->get_samples,
726             );
727              
728 0         0 return $self;
729             }
730              
731             =head2 plot_freq
732              
733             =for usage
734              
735             Can be called like this
736              
737             $win->plot_freq;
738              
739             Or this
740              
741             $win->plot_freq({ ordinate => ORDINATE });
742              
743             =for ref
744              
745             Plot the frequency response (magnitude of the DFT of the window samples).
746             The response is plotted in dB, and the frequency (by default) as a fraction of
747             the Nyquist frequency. Currently, only L is supported.
748             The default display type is used.
749              
750             =head3 options
751              
752             =over
753              
754             =item coord => COORD
755              
756             This sets the units of frequency of the co-ordinate axis. C must be one
757             of C, for fraction of the nyquist frequency (range C<-1, 1>);
758             C, for fraction of the sampling frequncy (range C<-0.5, 0.5>); or
759             C for frequency bin number (range C<0, $N - 1>). The default value is
760             C.
761              
762             =item min_bins => MIN
763              
764             This sets the minimum number of frequency bins. See L.
765             Defaults to 1000.
766              
767             =back
768              
769             =cut
770              
771             sub plot_freq {
772 0     0 1 0 my $self = shift;
773              
774 0         0 barf 'PDL::DSP::Windows::plot Gnuplot not available!' unless HAVE_GNUPLOT;
775              
776 0 0       0 my $opts = new PDL::Options({
777             coord => 'nyquist',
778             min_bins => 1000
779             })->options( @_ ? shift : {} );
780              
781 0         0 my $mf = $self->get_modfreqs({ min_bins => $opts->{min_bins} });
782 0         0 $mf /= $mf->max;
783              
784 0         0 my $title = $self->get_name . $self->format_plot_param_vals
785             . ', frequency response. ENBW=' . sprintf( '%2.3f', $self->enbw );
786              
787 0         0 my $coord = $opts->{coord};
788              
789 0         0 my ( $coordinate_range, $xlab );
790              
791 0 0       0 if ($coord eq 'nyquist') {
    0          
    0          
792 0         0 $coordinate_range = 1;
793 0         0 $xlab = 'Fraction of Nyquist frequency';
794             }
795             elsif ($coord eq 'sample') {
796 0         0 $coordinate_range = 0.5;
797 0         0 $xlab = 'Fraction of sampling frequency';
798             }
799             elsif ($coord eq 'bin') {
800 0         0 $coordinate_range = $self->{N} / 2;
801 0         0 $xlab = 'bin';
802             }
803             else {
804 0         0 barf "plot_freq: Unknown ordinate unit specification $coord";
805             }
806              
807 0         0 my $ylab = 'frequency response (dB)';
808 0         0 my $coordinates = zeroes($mf)
809             ->xlinvals( -$coordinate_range, $coordinate_range );
810              
811 0         0 PDL::Graphics::Gnuplot::plot(
812             title => $title,
813             xmin => -$coordinate_range,
814             xmax => $coordinate_range,
815             xlabel => $xlab,
816             ylabel => $ylab,
817             with => 'line',
818             $coordinates,
819             20 * log10($mf),
820             );
821              
822 0         0 return $self;
823             }
824              
825             =head2 enbw
826              
827             =for usage
828              
829             $win->enbw;
830              
831             =for ref
832              
833             Compute and return the equivalent noise bandwidth of the window.
834              
835             =cut
836              
837             sub enbw {
838 17     17 1 117 my $w = shift->get_samples;
839 17         27076 $w->nelem * ( $w ** 2 )->sum / $w->sum ** 2;
840             }
841              
842             =head2 coherent_gain
843              
844             =for usage
845              
846             $win->coherent_gain;
847              
848             =for ref
849              
850             Compute and return the coherent gain (the dc gain) of the window. This is just
851             the average of the samples.
852              
853             =cut
854              
855             sub coherent_gain {
856 1     1 1 29 my $w = shift->get_samples;
857 1         236 $w->sum / $w->nelem;
858             }
859              
860              
861             =head2 process_gain
862              
863             =for usage
864              
865             $win->coherent_gain;
866              
867             =for ref
868              
869             Compute and return the processing gain (the dc gain) of the window. This is
870             just the multiplicative inverse of the C.
871              
872             =cut
873              
874 1     1 1 5 sub process_gain { 1 / shift->enbw }
875              
876             # not quite correct for some reason.
877             # Seems like 10*log10(this) / 1.154
878             # gives the correct answer in decibels
879              
880             =head2 scallop_loss
881              
882             =for usage
883              
884             $win->scallop_loss;
885              
886             =for ref
887              
888             **BROKEN**.
889             Compute and return the scalloping loss of the window.
890              
891             =cut
892              
893             sub scallop_loss {
894 0     0 1 0 my ($w) = @_;
895 0         0 my $x = sequence($w) * ( PI / $w->nelem );
896 0         0 sqrt( ( $w * cos($x) )->sum ** 2 + ( $w * sin($x) )->sum ** 2 ) / $w->sum;
897             }
898              
899             =head1 WINDOW FUNCTIONS
900              
901             These window-function subroutines return a pdl of C<$N> samples. For most
902             windows, there are a symmetric and a periodic version. The symmetric versions
903             are functions of C<$N> points, uniformly spaced, and taking values from x_lo
904             through x_hi. Here, a periodic function of C< $N > points is equivalent to its
905             symmetric counterpart of C<$N + 1> points, with the final point omitted. The
906             name of a periodic window-function subroutine is the same as that for the
907             corresponding symmetric function, except it has the suffix C<_per>. The
908             descriptions below describe the symmetric version of each window.
909              
910             The term 'Blackman-Harris family' is meant to include the Hamming family and
911             the Blackman family. These are functions of sums of cosines.
912              
913             Unless otherwise noted, the arguments in the cosines of all symmetric window
914             functions are multiples of C<$N> numbers uniformly spaced from C<0> through
915             C<2 pi>.
916              
917             =cut
918              
919             sub bartlett {
920 2 50   2 1 7 barf 'bartlett: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
921 2         4 my ($N) = @_;
922 2         7 1 - abs( zeroes($N)->xlinvals( -1, 1 ) );
923             }
924              
925             sub bartlett_per {
926 2 50   2 0 7 barf 'bartlett: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
927 2         4 my ($N) = @_;
928 2         8 1 - abs( zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) );
929             }
930              
931             sub bartlett_hann {
932 3 50   3 1 10 barf 'bartlett_hann: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
933 3         8 my ($N) = @_;
934 3         10 0.62 - 0.48 * abs( zeroes($N)->xlinvals( -0.5, 0.5 ) )
935             + 0.38 * cos( zeroes($N)->xlinvals( - PI, PI ) );
936             }
937              
938             sub bartlett_hann_per {
939 2 50   2 0 6 barf 'bartlett_hann: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
940 2         5 my ($N) = @_;
941 2         7 0.62 - 0.48 * abs( zeroes($N)->xlinvals( -0.5, ( -0.5 + 0.5 * ( $N - 1 ) ) / $N ) )
942             + 0.38 * cos( zeroes($N)->xlinvals( - PI, ( - PI + PI * ( $N - 1 ) ) / $N ) );
943             }
944              
945             sub blackman {
946 3 50   3 1 12 barf 'blackman: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
947 3         6 my ($N) = @_;
948 3         11 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
949 3         1747 0.34 + $cx * ( -0.5 + $cx * 0.16 );
950             }
951              
952             sub blackman_per {
953 2 50   2 0 7 barf 'blackman: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
954 2         5 my ($N) = @_;
955 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
956 2         307 0.34 + $cx * ( -0.5 + $cx * 0.16 );
957             }
958              
959             sub blackman_bnh {
960 2 50   2 1 6 barf 'blackman_bnh: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
961 2         5 my ($N) = @_;
962 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
963 2         310 0.3461008 + $cx * ( -0.4973406 + $cx * 0.1565586 );
964             }
965              
966             sub blackman_bnh_per {
967 2 50   2 0 7 barf 'blackman_bnh: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
968 2         5 my ($N) = @_;
969 2         6 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
970 2         305 0.3461008 + $cx * ( -0.4973406 + $cx * 0.1565586 );
971             }
972              
973             sub blackman_ex {
974 2 50   2 1 7 barf 'blackman_ex: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
975 2         5 my ($N) = @_;
976 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
977 2         313 0.349742046431642 + $cx * ( -0.496560619088564 + $cx * 0.153697334479794 );
978             }
979              
980             sub blackman_ex_per {
981 2 50   2 0 9 barf 'blackman_ex: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
982 2         5 my ($N) = @_;
983 2         6 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
984 2         304 0.349742046431642 + $cx * ( -0.496560619088564 + $cx * 0.153697334479794 );
985             }
986              
987             sub blackman_gen {
988 2 50   2 1 10 barf 'blackman_gen: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
989 2         5 my ( $N, $alpha ) = @_;
990 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
991 2         346 0.5 - $alpha + $cx * ( -0.5 + $cx * $alpha );
992             }
993              
994             sub blackman_gen_per {
995 2 50   2 0 7 barf 'blackman_gen: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
996 2         6 my ($N,$alpha) = @_;
997 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
998 2         321 0.5 - $alpha + $cx * ( -0.5 + $cx * $alpha );
999             }
1000              
1001             sub blackman_gen3 {
1002 2 50   2 1 9 barf 'blackman_gen3: 4 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 4;
1003 2         6 my ($N,$a0,$a1,$a2) = @_;
1004 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1005 2         329 $a0 - $a2 + ( $cx * ( -$a1 + $cx * 2 * $a2 ) );
1006             }
1007              
1008             sub blackman_gen3_per {
1009 2 50   2 0 8 barf 'blackman_gen3: 4 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 4;
1010 2         5 my ( $N, $a0, $a1, $a2 ) = @_;
1011 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1012 2         324 $a0 - $a2 + ( $cx * ( -$a1 + $cx * 2 * $a2 ) );
1013             }
1014              
1015             sub blackman_gen4 {
1016 2 50   2 1 8 barf 'blackman_gen4: 5 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 5;
1017 2         6 my ( $N, $a0, $a1, $a2, $a3 ) = @_;
1018 2         6 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1019 2         338 $a0 - $a2 + $cx * ( ( -$a1 + 3 * $a3 ) + $cx * ( 2 * $a2 + $cx * -4 * $a3 ) );
1020             }
1021              
1022             sub blackman_gen4_per {
1023 2 50   2 0 8 barf 'blackman_gen4: 5 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 5;
1024 2         5 my ( $N, $a0, $a1, $a2, $a3 ) = @_;
1025 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1026 2         340 $a0 - $a2 + $cx * ( ( -$a1 + 3 * $a3 ) + $cx * ( 2 * $a2 + $cx * -4 * $a3 ) );
1027             }
1028              
1029             sub blackman_gen5 {
1030 2 50   2 1 8 barf 'blackman_gen5: 6 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 6;
1031 2         7 my ( $N, $a0, $a1, $a2, $a3, $a4 ) = @_;
1032 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1033 2         362 $a0 - $a2 + $a4 + $cx * ( ( -$a1 + 3 * $a3 )
1034             + $cx * ( 2 * $a2 - 8 * $a4 + $cx * ( -4 * $a3 + $cx * 8 * $a4 ) ) );
1035             }
1036              
1037             sub blackman_gen5_per {
1038 2 50   2 0 8 barf 'blackman_gen5: 6 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 6;
1039 2         6 my ( $N, $a0, $a1, $a2, $a3, $a4 ) = @_;
1040 2         6 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1041 2         357 $a0 - $a2 + $a4 + $cx * ( ( -$a1 + 3 * $a3 )
1042             + $cx * ( 2 * $a2 - 8 * $a4 + $cx * ( -4 * $a3 + $cx * 8 * $a4 ) ) );
1043             }
1044              
1045             sub blackman_harris {
1046 2 50   2 1 6 barf 'blackman_harris: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1047 2         5 my ($N) = @_;
1048 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1049 2         443 0.343103 + $cx * ( -0.49755 + $cx * 0.15844 );
1050             }
1051              
1052             sub blackman_harris_per {
1053 2 50   2 0 8 barf 'blackman_harris: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1054 2         5 my ($N) = @_;
1055 2         6 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1056 2         301 0.343103 + $cx * ( -0.49755 + $cx * 0.15844 );
1057             }
1058              
1059             sub blackman_harris4 {
1060 4 50   4 1 14 barf 'blackman_harris4: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1061 4         10 my ($N) = @_;
1062 4         14 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1063 4         1720 0.21747 + $cx * ( -0.45325 + $cx * ( 0.28256 + $cx * -0.04672 ) );
1064             }
1065              
1066             sub blackman_harris4_per {
1067 2 50   2 0 7 barf 'blackman_harris4: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1068 2         4 my ($N) = @_;
1069 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1070 2         321 0.21747 + $cx * ( -0.45325 + $cx * ( 0.28256 + $cx * -0.04672 ) );
1071             }
1072              
1073             sub blackman_nuttall {
1074 3 50   3 1 10 barf 'blackman_nuttall: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1075 3         7 my ($N) = @_;
1076 3         9 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1077 3         486 0.2269824 + $cx * ( -0.4572542 + $cx * ( 0.273199 + $cx * -0.0425644 ) );
1078             }
1079              
1080             sub blackman_nuttall_per {
1081 2 50   2 0 8 barf 'blackman_nuttall: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1082 2         6 my ($N) = @_;
1083 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1084 2         338 0.2269824 + $cx * ( -0.4572542 + $cx * ( 0.273199 + $cx * -0.0425644 ) );
1085             }
1086              
1087             sub bohman {
1088 4 50   4 1 13 barf 'bohman: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1089 4         10 my ($N) = @_;
1090 4         31 my $x = abs( zeroes($N)->xlinvals( -1, 1 ) );
1091 4         807 ( 1 - $x ) * cos( PI * $x ) + ( 1 / PI ) * sin( PI * $x );
1092             }
1093              
1094             sub bohman_per {
1095 2 50   2 0 9 barf 'bohman: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1096 2         5 my ($N) = @_;
1097 2         7 my $x = abs( zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) );
1098 2         270 ( 1 - $x ) * cos( PI * $x ) + ( 1 / PI ) * sin( PI * $x );
1099             }
1100              
1101             sub cauchy {
1102 3 50   3 1 13 barf 'cauchy: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1103 3         6 my ( $N, $alpha ) = @_;
1104 3         10 1 / ( 1 + ( zeroes($N)->xlinvals( -1, 1 ) * $alpha ) ** 2 );
1105             }
1106              
1107             sub cauchy_per {
1108 2 50   2 0 8 barf 'cauchy: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1109 2         4 my ( $N, $alpha ) = @_;
1110 2         7 1 / ( 1 + ( zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) * $alpha ) ** 2 );
1111             }
1112              
1113             sub chebyshev {
1114 2 50   2 1 6 barf 'chebyshev: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1115 2         4 my ( $N, $at ) = @_;
1116              
1117 2         3 my ( $M, $M1, $pos, $pos1 );
1118              
1119 2         91 my $beta = cosh( 1 / ( $N - 1 ) * acosh( 1 / ( 10 ** ( -$at / 20 ) ) ) );
1120 2         19 my $x = $beta * cos( PI * sequence($N) / $N );
1121              
1122 2         235 my $cw = chebpoly( $N - 1, $x );
1123              
1124 2 100       9 if ( $N % 2 ) { # odd
1125 1         4 $M1 = ( $N + 1 ) / 2;
1126 1         3 $M = $M1 - 1;
1127 1         2 $pos = 0;
1128 1         1 $pos1 = 1;
1129              
1130 1         5 PDL::FFT::realfft($cw);
1131             }
1132             else { # half-sample delay (even order)
1133 1         4 my $arg = PI / $N * sequence($N);
1134 1         90 my $cw_im = $cw * sin($arg);
1135 1         18 $cw *= cos($arg);
1136              
1137 1         17 if (USE_FFTW_DIRECTION) {
1138             PDL::FFT::fftnd( $cw, $cw_im );
1139             }
1140             else {
1141 1         7 PDL::FFT::ifftnd( $cw, $cw_im );
1142             }
1143              
1144 1         115 $M1 = $N / 2;
1145 1         3 $M = $M1 - 1;
1146 1         2 $pos = 1;
1147 1         4 $pos1 = 0;
1148             }
1149              
1150 2         127 $cw /= $cw->at($pos);
1151              
1152 2         38 my $cwout = zeroes($N);
1153              
1154 2         91 $cwout->slice("0:$M") .= $cw->slice("$M:0:-1");
1155 2         87 $cwout->slice("$M1:-1") .= $cw->slice("$pos1:$M");
1156 2         68 $cwout /= max($cwout);
1157              
1158 2         130 $cwout;
1159             }
1160              
1161             sub cos_alpha {
1162 5 50   5 1 15 barf 'cos_alpha: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1163 5         12 my ( $N, $alpha ) = @_;
1164 5         14 ( sin( zeroes($N)->xlinvals( 0, PI ) ) ) ** $alpha ;
1165             }
1166              
1167             sub cos_alpha_per {
1168 2 50   2 0 8 barf 'cos_alpha: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1169 2         5 my ( $N, $alpha ) = @_;
1170 2         7 sin( zeroes($N)->xlinvals( 0, PI * ( $N - 1 ) / $N ) ) ** $alpha ;
1171             }
1172              
1173             sub cosine {
1174 3 50   3 1 14 barf 'cosine: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1175 3         8 my ($N) = @_;
1176 3         11 sin( zeroes($N)->xlinvals( 0, PI ) );
1177             }
1178              
1179             sub cosine_per {
1180 2 50   2 0 8 barf 'cosine: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1181 2         6 my ($N) = @_;
1182 2         6 sin( zeroes($N)->xlinvals( 0, PI * ( $N - 1 ) / $N ) );
1183             }
1184              
1185             sub dpss {
1186 0 0   0 1 0 barf 'dpss: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1187 0         0 my ( $N, $beta ) = @_;
1188              
1189 0         0 barf 'dpss: PDL::LinearAlgebra not installed.' unless HAVE_LinearAlgebra;
1190 0 0 0     0 barf "dpss: $beta not between 0 and $N." unless $beta >= 0 and $beta <= $N;
1191              
1192 0         0 $beta /= $N / 2;
1193              
1194 0         0 my $k = sequence($N);
1195 0         0 my $s = sin( PI * $beta * $k ) / $k;
1196              
1197 0         0 $s->slice('0') .= $beta;
1198              
1199 0         0 my ( $ev, $e ) = eigens_sym(PDL::LinearAlgebra::Special::mtoeplitz($s));
1200 0         0 my $i = $e->maximum_ind;
1201              
1202 0         0 $ev->slice("($i)")->copy;
1203             }
1204              
1205             sub dpss_per {
1206 0 0   0 0 0 barf 'dpss: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1207 0         0 my ( $N, $beta ) = @_;
1208 0         0 $N++;
1209              
1210 0         0 barf 'dpss: PDL::LinearAlgebra not installed.' unless HAVE_LinearAlgebra;
1211 0 0 0     0 barf "dpss: $beta not between 0 and $N." unless $beta >= 0 and $beta <= $N;
1212              
1213 0         0 $beta /= $N / 2;
1214              
1215 0         0 my $k = sequence($N);
1216 0         0 my $s = sin( PI * $beta * $k ) / $k;
1217              
1218 0         0 $s->slice('0') .= $beta;
1219              
1220 0         0 my ( $ev, $e ) = eigens_sym(PDL::LinearAlgebra::Special::mtoeplitz($s));
1221 0         0 my $i = $e->maximum_ind;
1222              
1223 0         0 $ev->slice("($i),0:-2")->copy;
1224             }
1225              
1226             sub exponential {
1227 2 50   2 1 8 barf 'exponential: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1228 2         5 my ($N) = @_;
1229 2         6 2 ** ( 1 - abs( zeroes($N)->xlinvals( -1, 1 ) ) ) - 1;
1230             }
1231              
1232             sub exponential_per {
1233 2 50   2 0 6 barf 'exponential: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1234 2         6 my ($N) = @_;
1235 2         7 2 ** ( 1 - abs( zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) ) ) - 1;
1236             }
1237              
1238             sub flattop {
1239 4 50   4 1 14 barf 'flattop: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1240 4         7 my ($N) = @_;
1241 4         15 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1242 4         1666 -0.05473684 + $cx * ( -0.165894739 + $cx * ( 0.498947372 + $cx * ( -0.334315788 + $cx * 0.055578944 ) ) );
1243             }
1244              
1245             sub flattop_per {
1246 2 50   2 0 8 barf 'flattop: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1247 2         4 my ($N) = @_;
1248 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1249 2         385 -0.05473684 + $cx * ( -0.165894739 + $cx * ( 0.498947372 + $cx * ( -0.334315788 + $cx * 0.055578944 ) ) );
1250             }
1251              
1252             sub gaussian {
1253 2 50   2 1 6 barf 'gaussian: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1254 2         6 my ( $N, $beta ) = @_;
1255 2         8 exp( -0.5 * ( $beta * zeroes($N)->xlinvals( -1, 1 ) ) ** 2);
1256             }
1257              
1258             sub gaussian_per {
1259 2 50   2 0 13 barf 'gaussian: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1260 2         5 my ( $N, $beta ) = @_;
1261 2         8 exp( -0.5 * ( $beta * zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) ) ** 2 );
1262             }
1263              
1264             sub hamming {
1265 15 50   15 1 45 barf 'hamming: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1266 15         29 my ($N) = @_;
1267 15         56 0.54 + -0.46 * cos( zeroes($N)->xlinvals( 0, TPI ) );
1268             }
1269              
1270             sub hamming_per {
1271 2 50   2 0 8 barf 'hamming: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1272 2         5 my ($N) = @_;
1273 2         7 0.54 + -0.46 * cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1274             }
1275              
1276             sub hamming_ex {
1277 2 50   2 1 7 barf 'hamming_ex: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1278 2         5 my ($N) = @_;
1279 2         8 0.53836 + -0.46164 * cos( zeroes($N)->xlinvals( 0, TPI ) );
1280             }
1281              
1282             sub hamming_ex_per {
1283 2 50   2 0 8 barf 'hamming_ex: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1284 2         24 my ($N) = @_;
1285 2         8 0.53836 + -0.46164 * cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1286             }
1287              
1288             sub hamming_gen {
1289 2 50   2 1 9 barf 'hamming_gen: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1290 2         5 my ( $N, $a ) = @_;
1291 2         16 $a - ( 1 - $a ) * cos( zeroes($N)->xlinvals( 0, TPI ) );
1292             }
1293              
1294             sub hamming_gen_per {
1295 2 50   2 0 7 barf 'hamming_gen: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1296 2         7 my ( $N, $a ) = @_;
1297 2         8 $a - ( 1 - $a ) * cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1298             }
1299              
1300             sub hann {
1301 5 50   5 1 13 barf 'hann: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1302 5         10 my ($N) = @_;
1303 5         14 0.5 + -0.5 * cos( zeroes($N)->xlinvals( 0, TPI ) );
1304             }
1305              
1306             sub hann_per {
1307 2 50   2 0 7 barf 'hann: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1308 2         6 my ($N) = @_;
1309 2         6 0.5 + -0.5 * cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1310             }
1311              
1312             sub hann_matlab {
1313 1 50   1 1 6 barf 'hann_matlab: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1314 1         2 my ($N) = @_;
1315 1         3 0.5 - 0.5 * cos( zeroes($N)->xlinvals( TPI / ( $N + 1 ), TPI * $N / ( $N + 1 ) ) );
1316             }
1317              
1318             sub hann_poisson {
1319 3 50   3 1 12 barf 'hann_poisson: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1320 3         7 my ( $N, $alpha ) = @_;
1321 3         11 0.5 * ( 1 + cos( zeroes($N)->xlinvals( - PI, PI ) ) )
1322             * exp( -$alpha * abs( zeroes($N)->xlinvals( -1, 1 ) ) );
1323              
1324             }
1325              
1326             sub hann_poisson_per {
1327 2 50   2 0 8 barf 'hann_poisson: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1328 2         5 my ( $N, $alpha ) = @_;
1329 2         9 0.5 * ( 1 + cos( zeroes($N)->xlinvals( - PI, ( - PI + PI * ( $N - 1 ) ) / $N ) ) )
1330             * exp( -$alpha * abs( zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) ) );
1331             }
1332              
1333             sub kaiser {
1334 0 0   0 1 0 barf 'kaiser: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1335 0         0 my ( $N, $beta ) = @_;
1336              
1337 0         0 barf 'kaiser: PDL::GSLSF not installed' unless HAVE_BESSEL;
1338              
1339 0         0 $beta *= PI;
1340              
1341 0         0 my ($n) = PDL::GSLSF::BESSEL::gsl_sf_bessel_In(
1342             $beta * sqrt( 1 - zeroes($N)->xlinvals( -1, 1 ) ** 2 ), 0 );
1343              
1344 0         0 my ($d) = PDL::GSLSF::BESSEL::gsl_sf_bessel_In( $beta, 0 );
1345              
1346 0         0 $n / $d;
1347             }
1348              
1349             sub kaiser_per {
1350 0 0   0 0 0 barf 'kaiser: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1351 0         0 my ($N,$beta) = @_;
1352              
1353 0         0 barf 'kaiser: PDL::GSLSF not installed' unless HAVE_BESSEL;
1354              
1355 0         0 $beta *= PI;
1356              
1357 0         0 my ($n) = PDL::GSLSF::BESSEL::gsl_sf_bessel_In(
1358             $beta * sqrt( 1 - zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) ** 2 ), 0);
1359              
1360 0         0 my ($d) = PDL::GSLSF::BESSEL::gsl_sf_bessel_In( $beta, 0 );
1361              
1362 0         0 $n / $d;
1363             }
1364              
1365             sub lanczos {
1366 3 50   3 1 11 barf 'lanczos: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1367 3         6 my ($N) = @_;
1368              
1369 3         10 my $x = PI * zeroes($N)->xlinvals( -1, 1 );
1370 3         526 my $res = sin($x) / $x;
1371              
1372 3 100       625 $res->slice( int( $N / 2 ) ) .= 1 if $N % 2;
1373              
1374 3         43 $res;
1375             }
1376              
1377             sub lanczos_per {
1378 2 50   2 0 7 barf 'lanczos: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1379 2         4 my ($N) = @_;
1380              
1381 2         8 my $x = PI * zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N );
1382 2         250 my $res = sin($x) / $x;
1383              
1384 2 100       46 $res->slice( int( $N / 2 ) ) .= 1 unless $N % 2;
1385              
1386 2         39 $res;
1387             }
1388              
1389             sub nuttall {
1390 2 50   2 1 7 barf 'nuttall: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1391 2         4 my ($N) = @_;
1392 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1393 2         322 0.2269824 + $cx * ( -0.4572542 + $cx * ( 0.273199 + $cx * -0.0425644 ) );
1394             }
1395              
1396             sub nuttall_per {
1397 2 50   2 0 7 barf 'nuttall: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1398 2         5 my ($N) = @_;
1399 2         6 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1400 2         319 0.2269824 + $cx * ( -0.4572542 + $cx * ( 0.273199 + $cx * -0.0425644 ) );
1401             }
1402              
1403             sub nuttall1 {
1404 2 50   2 1 7 barf 'nuttall1: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1405 2         48 my ($N) = @_;
1406 2         24 my $cx = (cos(zeroes($N)->xlinvals(0,TPI)));
1407              
1408 2         327 (0.211536) + ($cx * ((-0.449584) + ($cx * (0.288464 + $cx * (-0.050416) ))));
1409             }
1410              
1411             sub nuttall1_per {
1412 2 50   2 0 6 barf 'nuttall1: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1413 2         4 my ($N) = @_;
1414 2         6 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1415 2         324 0.211536 + $cx * ( -0.449584 + $cx * ( 0.288464 + $cx * -0.050416 ) );
1416             }
1417              
1418             sub parzen {
1419 4 50   4 1 15 barf 'parzen: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1420 4         10 my ($N) = @_;
1421              
1422 4         14 my $x = zeroes($N)->xlinvals( -1, 1 );
1423              
1424 4         734 my $x1 = $x->where( $x <= -0.5 );
1425 4         487 my $x2 = $x->where( ( $x < 0.5 ) & ( $x > -0.5 ) );
1426 4         281 my $x3 = $x->where( $x >= 0.5 );
1427              
1428 4         201 $x1 .= 2 * ( 1 - abs($x1) ) ** 3;
1429 4         754 $x2 .= 1 - 6 * $x2 ** 2 * ( 1 - abs($x2) );
1430 4         253 $x3 .= $x1->slice('-1:0:-1');
1431              
1432 4         190 $x;
1433             }
1434              
1435             sub parzen_per {
1436 2 50   2 0 8 barf 'parzen: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1437 2         6 my ($N) = @_;
1438              
1439 2         7 my $x = zeroes($N)->xlinvals( -1, ( -1 + ( $N - 1 ) ) / $N);
1440              
1441 2         254 my $x1 = $x->where( $x <= -0.5 );
1442 2         122 my $x2 = $x->where( ( $x < 0.5 ) & ( $x > -0.5 ) );
1443 2         98 my $x3 = $x->where( $x >= 0.5 );
1444              
1445 2         75 $x1 .= 2 * ( 1 - abs($x1)) ** 3;
1446 2         123 $x2 .= 1 - 6 * $x2 ** 2 * ( 1 - abs($x2) );
1447 2         82 $x3 .= $x1->slice('-1:1:-1');
1448              
1449 2         69 $x;
1450             }
1451              
1452             sub parzen_octave {
1453 2 50   2 1 9 barf 'parzen_octave: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1454 2         5 my ($N) = @_;
1455              
1456 2         6 my $r = ( $N - 1 ) / 2;
1457 2         3 my $N2 = $N / 2;
1458 2         5 my $r4 = $r / 2;
1459 2         11 my $n = sequence( 2 * $r + 1 ) - $r;
1460              
1461 2         298 my $n1 = $n->where( abs($n) <= $r4 );
1462 2         268 my $n2 = $n->where( $n > $r4 );
1463 2         172 my $n3 = $n->where( $n < -$r4 );
1464              
1465 2         124 $n1 .= 1 - 6 * ( abs($n1) / $N2 ) ** 2 + 6 * ( abs($n1) / $N2 ) ** 3;
1466 2         1151 $n2 .= 2 * ( 1 - abs($n2) / $N2 ) ** 3;
1467 2         491 $n3 .= 2 * ( 1 - abs($n3) / $N2 ) ** 3;
1468              
1469 2         500 $n;
1470             }
1471              
1472             sub poisson {
1473 3 50   3 1 10 barf 'poisson: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1474 3         7 my ( $N, $alpha ) = @_;
1475 3         12 exp( -$alpha * abs( zeroes($N)->xlinvals( -1, 1 ) ) );
1476             }
1477              
1478             sub poisson_per {
1479 2 50   2 0 8 barf 'poisson: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1480 2         4 my ( $N, $alpha ) = @_;
1481 2         9 exp( -$alpha * abs( zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) ) );
1482             }
1483              
1484             sub rectangular {
1485 4 50   4 1 13 barf 'rectangular: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1486 4         8 my ($N) = @_;
1487 4         15 ones($N);
1488             }
1489              
1490             sub rectangular_per {
1491 2 50   2 0 8 barf 'rectangular: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1492 2         5 my ($N) = @_;
1493 2         6 ones($N);
1494             }
1495              
1496             sub triangular {
1497 4 50   4 1 16 barf 'triangular: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1498 4         10 my ($N) = @_;
1499 4         14 1 - abs( zeroes($N)->xlinvals( -( $N - 1 ) / $N, ( $N - 1 ) / $N ) );
1500             }
1501              
1502             sub triangular_per {
1503 2 50   2 0 8 barf 'triangular: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1504 2         5 my ($N) = @_;
1505 2         7 1 - abs( zeroes($N)->xlinvals( -$N / ( $N + 1 ), -1 / ( $N + 1 ) + ( $N - 1 ) / ( $N + 1 ) ) );
1506             }
1507              
1508             sub tukey {
1509 4 50   4 1 13 barf 'tukey: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1510 4         11 my ( $N, $alpha ) = @_;
1511              
1512 4 50 33     20 barf('tukey: alpha must be between 0 and 1') unless $alpha >=0 and $alpha <= 1;
1513              
1514 4 50       13 return ones($N) if $alpha == 0;
1515              
1516 4         12 my $x = zeroes($N)->xlinvals( 0, 1 );
1517              
1518 4         757 my $x1 = $x->where( $x < $alpha / 2 );
1519 4         556 my $x2 = $x->where( ( $x <= 1 - $alpha / 2 ) & ( $x >= $alpha / 2 ) );
1520 4         296 my $x3 = $x->where( $x > 1 - $alpha / 2 );
1521              
1522 4         325 $x1 .= 0.5 * ( 1 + cos( PI * ( 2 * $x1 / $alpha - 1 ) ) );
1523 4         281 $x2 .= 1;
1524 4         173 $x3 .= $x1->slice('-1:0:-1');
1525              
1526 4         215 return $x;
1527             }
1528              
1529             sub tukey_per {
1530 2 50   2 0 8 barf 'tukey: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1531 2         5 my ( $N, $alpha ) = @_;
1532              
1533 2 50 33     11 barf('tukey: alpha must be between 0 and 1') unless $alpha >=0 and $alpha <= 1;
1534              
1535 2 50       7 return ones($N) if $alpha == 0;
1536              
1537 2         6 my $x = zeroes($N)->xlinvals( 0, ( $N - 1 ) / $N );
1538              
1539 2         264 my $x1 = $x->where( $x < $alpha / 2 );
1540 2         120 my $x2 = $x->where( ( $x <= 1 - $alpha / 2 ) & ( $x >= $alpha / 2 ) );
1541 2         95 my $x3 = $x->where( $x > 1 - $alpha / 2 );
1542              
1543 2         114 $x1 .= 0.5 * ( 1 + cos( PI * ( 2 * $x1 / $alpha - 1 ) ) );
1544 2         88 $x2 .= 1;
1545 2         37 $x3 .= $x1->slice('-1:1:-1');
1546              
1547 2         67 return $x;
1548             }
1549              
1550             sub welch {
1551 3 50   3 1 9 barf 'welch: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1552 3         8 my ($N) = @_;
1553 3         8 1 - zeroes($N)->xlinvals( -1, 1 ) ** 2;
1554             }
1555              
1556             sub welch_per {
1557 2 50   2 0 8 barf 'welch: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1558 2         6 my ($N) = @_;
1559 2         8 1 - zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) ** 2;
1560             }
1561              
1562             # Legacy static data
1563              
1564             $window_definitions{$_} = {} for keys %symmetric_windows;
1565              
1566             $window_definitions{$_}{alias} = [ @{ $window_aliases{$_} } ]
1567             for keys %window_aliases;
1568              
1569             $window_definitions{$_}{params} = [ @{ $window_parameters{$_} } ]
1570             for keys %window_parameters;
1571              
1572             $window_definitions{$_}{fn} = $window_names{$_}
1573             for keys %window_names;
1574              
1575             $window_definitions{$_}{pfn} = $window_print_names{$_}
1576             for keys %window_print_names;
1577              
1578             %winpersubs = map { $_ => __PACKAGE__->can("${_}_per") } keys %periodic_windows;
1579             %winsubs = map { $_ => __PACKAGE__->can($_) } keys %symmetric_windows;
1580              
1581             my $wizard = do {
1582             my $msg = 'Package variables from PDL::DSP::Windows are deprecated and will be removed in the future.';
1583              
1584             my $read = sub {
1585             carp $msg
1586             . ' This attempt to read from them will soon become an error';
1587             };
1588              
1589             my $write = sub {
1590             carp $msg
1591             . ' This attempt to write to them will soon become an error';
1592             };
1593              
1594             wizard(
1595             fetch => $read,
1596             exists => $read,
1597             store => $write,
1598             delete => $write,
1599             );
1600             };
1601              
1602             cast %winsubs, $wizard;
1603             cast %winpersubs, $wizard;
1604             cast %window_definitions, $wizard;
1605              
1606             =head1 Symmetric window functions
1607              
1608             =head2 bartlett($N)
1609              
1610             The Bartlett window. (Ref 1). Another name for this window is the fejer window.
1611             This window is defined by
1612              
1613             1 - abs(arr)
1614              
1615             where the points in arr range from -1 through 1. See also
1616             L.
1617              
1618             =head2 bartlett_hann($N)
1619              
1620             The Bartlett-Hann window. Another name for this window is the Modified
1621             Bartlett-Hann window. This window is defined by
1622              
1623             0.62 - 0.48 * abs(arr) + 0.38 * arr1
1624              
1625             where the points in arr range from -1/2 through 1/2, and arr1 are the cos of
1626             points ranging from -PI through PI.
1627              
1628             =head2 blackman($N)
1629              
1630             The 'classic' Blackman window. (Ref 1). One of the Blackman-Harris family, with coefficients
1631              
1632             a0 = 0.42
1633             a1 = 0.5
1634             a2 = 0.08
1635              
1636             =head2 blackman_bnh($N)
1637              
1638             The Blackman-Harris (bnh) window. An improved version of the 3-term
1639             Blackman-Harris window given by Nuttall (Ref 2, p. 89). One of the
1640             Blackman-Harris family, with coefficients
1641              
1642             a0 = 0.4243801
1643             a1 = 0.4973406
1644             a2 = 0.0782793
1645              
1646             =head2 blackman_ex($N)
1647              
1648             The 'exact' Blackman window. (Ref 1). One of the Blackman-Harris family, with
1649             coefficients
1650              
1651             a0 = 0.426590713671539
1652             a1 = 0.496560619088564
1653             a2 = 0.0768486672398968
1654              
1655             =head2 blackman_gen($N,$alpha)
1656              
1657             The General classic Blackman window. A single parameter family of the 3-term
1658             Blackman window. This window is defined by
1659              
1660             my $cx = arr;
1661             .5 - $alpha + ($cx * (-.5 + $cx * $alpha));
1662              
1663             where the points in arr are the cos of points ranging from 0 through 2PI.
1664              
1665             =head2 blackman_gen3($N,$a0,$a1,$a2)
1666              
1667             The general form of the Blackman family. One of the Blackman-Harris family,
1668             with coefficients
1669              
1670             a0 = $a0
1671             a1 = $a1
1672             a2 = $a2
1673              
1674             =head2 blackman_gen4($N,$a0,$a1,$a2,$a3)
1675              
1676             The general 4-term Blackman-Harris window. One of the Blackman-Harris family,
1677             with coefficients
1678              
1679             a0 = $a0
1680             a1 = $a1
1681             a2 = $a2
1682             a3 = $a3
1683              
1684             =head2 blackman_gen5($N,$a0,$a1,$a2,$a3,$a4)
1685              
1686             The general 5-term Blackman-Harris window. One of the Blackman-Harris family,
1687             with coefficients
1688              
1689             a0 = $a0
1690             a1 = $a1
1691             a2 = $a2
1692             a3 = $a3
1693             a4 = $a4
1694              
1695             =head2 blackman_harris($N)
1696              
1697             The Blackman-Harris window. (Ref 1). One of the Blackman-Harris family, with
1698             coefficients
1699              
1700             a0 = 0.422323
1701             a1 = 0.49755
1702             a2 = 0.07922
1703              
1704             Another name for this window is the Minimum three term (sample) Blackman-Harris
1705             window.
1706              
1707             =head2 blackman_harris4($N)
1708              
1709             The minimum (sidelobe) four term Blackman-Harris window. (Ref 1). One of the
1710             Blackman-Harris family, with coefficients
1711              
1712             a0 = 0.35875
1713             a1 = 0.48829
1714             a2 = 0.14128a3 = 0.01168
1715              
1716             Another name for this window is the Blackman-Harris window.
1717              
1718             =head2 blackman_nuttall($N)
1719              
1720             The Blackman-Nuttall window. One of the Blackman-Harris family, with
1721             coefficients
1722              
1723             a0 = 0.3635819
1724             a1 = 0.4891775
1725             a2 = 0.1365995
1726             a3 = 0.0106411
1727              
1728             =head2 bohman($N)
1729              
1730             The Bohman window. (Ref 1). This window is defined by
1731              
1732             my $x = abs(arr);
1733             (1 - $x) * cos(PI * $x) + (1 / PI) * sin(PI * $x)
1734              
1735             where the points in arr range from -1 through 1.
1736              
1737             =head2 cauchy($N,$alpha)
1738              
1739             The Cauchy window. (Ref 1). Other names for this window are: Abel, Poisson.
1740             This window is defined by
1741              
1742             1 / (1 + (arr * $alpha) ** 2)
1743              
1744             where the points in arr range from -1 through 1.
1745              
1746             =head2 chebyshev($N,$at)
1747              
1748             The Chebyshev window. The frequency response of this window has C<$at> dB of
1749             attenuation in the stop-band. Another name for this window is the
1750             Dolph-Chebyshev window. No periodic version of this window is defined. This
1751             routine gives the same result as the routine B in Octave 3.6.2.
1752              
1753             =head2 cos_alpha($N,$alpha)
1754              
1755             The Cos_alpha window. (Ref 1). Another name for this window is the
1756             Power-of-cosine window. This window is defined by
1757              
1758             arr ** $alpha
1759              
1760             where the points in arr are the sin of points ranging from 0 through PI.
1761              
1762             =head2 cosine($N)
1763              
1764             The Cosine window. Another name for this window is the sine window. This
1765             window is defined by
1766              
1767             arr
1768              
1769             where the points in arr are the sin of points ranging from 0 through PI.
1770              
1771             =head2 dpss($N,$beta)
1772              
1773             The Digital Prolate Spheroidal Sequence (DPSS) window. The parameter C<$beta>
1774             is the half-width of the mainlobe, measured in frequency bins. This window
1775             maximizes the power in the mainlobe for given C<$N> and C<$beta>. Another
1776             name for this window is the sleppian window.
1777              
1778             =head2 exponential($N)
1779              
1780             The Exponential window. This window is defined by
1781              
1782             2 ** (1 - abs arr) - 1
1783              
1784             where the points in arr range from -1 through 1.
1785              
1786             =head2 flattop($N)
1787              
1788             The flat top window. One of the Blackman-Harris family, with coefficients
1789              
1790             a0 = 0.21557895
1791             a1 = 0.41663158
1792             a2 = 0.277263158
1793             a3 = 0.083578947
1794             a4 = 0.006947368
1795              
1796             =head2 gaussian($N,$beta)
1797              
1798             The Gaussian window. (Ref 1). Another name for this window is the Weierstrass
1799             window. This window is defined by
1800              
1801             exp (-0.5 * ($beta * arr )**2),
1802              
1803             where the points in arr range from -1 through 1.
1804              
1805             =head2 hamming($N)
1806              
1807             The Hamming window. (Ref 1). One of the Blackman-Harris family, with
1808             coefficients
1809              
1810             a0 = 0.54
1811             a1 = 0.46
1812              
1813             =head2 hamming_ex($N)
1814              
1815             The 'exact' Hamming window. (Ref 1). One of the Blackman-Harris family, with
1816             coefficients
1817              
1818             a0 = 0.53836
1819             a1 = 0.46164
1820              
1821             =head2 hamming_gen($N,$a)
1822              
1823             The general Hamming window. (Ref 1). One of the Blackman-Harris family, with
1824             coefficients
1825              
1826             a0 = $a
1827             a1 = (1-$a)
1828              
1829             =head2 hann($N)
1830              
1831             The Hann window. (Ref 1). One of the Blackman-Harris family, with coefficients
1832              
1833             a0 = 0.5
1834             a1 = 0.5
1835              
1836             Another name for this window is the hanning window. See also
1837             L.
1838              
1839             =head2 hann_matlab($N)
1840              
1841             The Hann (matlab) window. Equivalent to the Hann window of N+2 points, with the
1842             endpoints (which are both zero) removed. No periodic version of this window is
1843             defined. This window is defined by
1844              
1845             0.5 - 0.5 * arr
1846              
1847             where the points in arr are the cosine of points ranging from 2PI/($N+1)
1848             through 2PI*$N/($N+1). This routine gives the same result as the routine
1849             B in Matlab. See also L.
1850              
1851             =head2 hann_poisson($N,$alpha)
1852              
1853             The Hann-Poisson window. (Ref 1). This window is defined by
1854              
1855             0.5 * (1 + arr1) * exp (-$alpha * abs arr)
1856              
1857             where the points in arr range from -1 through 1, and arr1 are the cos of points
1858             ranging from -PI through PI.
1859              
1860             =head2 kaiser($N,$beta)
1861              
1862             The Kaiser window. (Ref 1). The parameter C<$beta> is the approximate
1863             half-width of the mainlobe, measured in frequency bins. Another name for this
1864             window is the Kaiser-Bessel window. This window is defined by
1865              
1866             $beta *= PI;
1867             my @n = gsl_sf_bessel_In($beta * sqrt(1 - arr ** 2), 0);
1868             my @d = gsl_sf_bessel_In($beta, 0);
1869             $n[0] / $d[0];
1870              
1871             where the points in arr range from -1 through 1.
1872              
1873             =head2 lanczos($N)
1874              
1875             The Lanczos window. Another name for this window is the sinc window. This
1876             window is defined by
1877              
1878             my $x = PI * arr;
1879             my $res = sin($x) / $x;
1880             my $mid;
1881             $mid = int($N / 2), $res->slice($mid) .= 1 if $N % 2;
1882             $res;
1883              
1884             where the points in arr range from -1 through 1.
1885              
1886             =head2 nuttall($N)
1887              
1888             The Nuttall window. One of the Blackman-Harris family, with coefficients
1889              
1890             a0 = 0.3635819
1891             a1 = 0.4891775
1892             a2 = 0.1365995
1893             a3 = 0.0106411
1894              
1895             See also L.
1896              
1897             =head2 nuttall1($N)
1898              
1899             The Nuttall (v1) window. A window referred to as the Nuttall window. One of the
1900             Blackman-Harris family, with coefficients
1901              
1902             a0 = 0.355768
1903             a1 = 0.487396
1904             a2 = 0.144232
1905             a3 = 0.012604
1906              
1907             This routine gives the same result as the routine B in Octave 3.6.2.
1908             See also L.
1909              
1910             =head2 parzen($N)
1911              
1912             The Parzen window. (Ref 1). Other names for this window are: Jackson,
1913             Valle-Poussin. This function disagrees with the Octave subroutine B,
1914             but agrees with Ref. 1. See also L.
1915              
1916             =head2 parzen_octave($N)
1917              
1918             The Parzen window. No periodic version of this window is defined. This routine
1919             gives the same result as the routine B in Octave 3.6.2. See also
1920             L.
1921              
1922             =head2 poisson($N,$alpha)
1923              
1924             The Poisson window. (Ref 1). This window is defined by
1925              
1926             exp(-$alpha * abs(arr))
1927              
1928             where the points in arr range from -1 through 1.
1929              
1930             =head2 rectangular($N)
1931              
1932             The Rectangular window. (Ref 1). Other names for this window are: dirichlet,
1933             boxcar.
1934              
1935             =head2 triangular($N)
1936              
1937             The Triangular window. This window is defined by
1938              
1939             1 - abs(arr)
1940              
1941             where the points in arr range from -$N/($N-1) through $N/($N-1).
1942             See also L.
1943              
1944             =head2 tukey($N,$alpha)
1945              
1946             The Tukey window. (Ref 1). Another name for this window is the tapered cosine
1947             window.
1948              
1949             =head2 welch($N)
1950              
1951             The Welch window. (Ref 1). Other names for this window are: Riez, Bochner,
1952             Parzen, parabolic. This window is defined by
1953              
1954             1 - arr**2
1955              
1956             where the points in arr range from -1 through 1.
1957              
1958             =head1 AUXILIARY SUBROUTINES
1959              
1960             These subroutines are used internally, but are also available for export.
1961              
1962             =head2 cos_mult_to_pow
1963              
1964             Convert Blackman-Harris coefficients. The BH windows are usually defined via
1965             coefficients for cosines of integer multiples of an argument. The same windows
1966             may be written instead as terms of powers of cosines of the same argument.
1967             These may be computed faster as they replace evaluation of cosines with
1968             multiplications. This subroutine is used internally to implement the
1969             Blackman-Harris family of windows more efficiently.
1970              
1971             This subroutine takes between 1 and 7 numeric arguments a0, a1, ...
1972              
1973             It converts the coefficients of this
1974              
1975             a0 - a1 cos(arg) + a2 cos(2 * arg) - a3 cos(3 * arg) + ...
1976              
1977             To the cofficients of this
1978              
1979             c0 + c1 cos(arg) + c2 cos(arg)**2 + c3 cos(arg)**3 + ...
1980              
1981             =head2 cos_pow_to_mult
1982              
1983             This function is the inverse of L.
1984              
1985             This subroutine takes between 1 and 7 numeric arguments c0, c1, ...
1986              
1987             It converts the coefficients of this
1988              
1989             c0 + c1 cos(arg) + c2 cos(arg)**2 + c3 cos(arg)**3 + ...
1990              
1991             To the cofficients of this
1992              
1993             a0 - a1 cos(arg) + a2 cos( 2 * arg) - a3 cos( 3 * arg) + ...
1994              
1995             =cut
1996              
1997             sub cos_pow_to_mult {
1998 9     9 1 10190 my @cin = @_;
1999 9 100       27 barf 'cos_pow_to_mult: number of args not less than 8.' if @cin > 7;
2000              
2001 8         12 my $ex = 7 - @cin;
2002              
2003 8         15 my @c = ( @cin, (0) x $ex );
2004              
2005 8         26 my @as = (
2006             10 * $c[6] + 12 * $c[4] + 16 * $c[2] + 32 * $c[0],
2007             20 * $c[5] + 24 * $c[3] + 32 * $c[1],
2008             15 * $c[6] + 16 * $c[4] + 16 * $c[2],
2009             10 * $c[5] + 8 * $c[3],
2010             6 * $c[6] + 4 * $c[4],
2011             2 * $c[5],
2012             $c[6],
2013             );
2014              
2015 8         24 pop @as for 1 .. $ex;
2016              
2017 8         11 my $sign = -1;
2018              
2019 8         12 foreach (@as) {
2020 28         37 $_ /= -$sign * 32;
2021 28         32 $sign *= -1;
2022             }
2023              
2024 8         36 @as;
2025             }
2026              
2027             =head2 chebpoly
2028              
2029             =for usage
2030              
2031             chebpoly($n,$x)
2032              
2033             =for ref
2034              
2035             Returns the value of the C<$n>-th order Chebyshev polynomial at point C<$x>.
2036             $n and $x may be scalar numbers, pdl's, or array refs. However, at least one
2037             of $n and $x must be a scalar number.
2038              
2039             All mixtures of pdls and scalars could be handled much more easily as a PP
2040             routine. But, at this point PDL::DSP::Windows is pure perl/pdl, requiring no
2041             C/Fortran compiler.
2042              
2043             =cut
2044              
2045             sub chebpoly {
2046 5 50   5 1 1015 barf 'chebpoly: Two arguments expected. Got ' .scalar(@_) . "\n" unless @_ == 2;
2047              
2048 5         9 my ( $n, $x ) = @_;
2049              
2050 5 100       13 if ( ref $x ) {
2051 4 50       11 barf "chebpoly: neither $n nor $x is a scalar number" if ref($n);
2052 4         13 $x = topdl($x);
2053              
2054 4         77 my $tn = zeroes($x);
2055              
2056 4         316 my ( $ind1, $ind2 ) = which_both( abs($x) <= 1 );
2057              
2058 4         491 $tn->index($ind1) .= cos( $n * acos( $x->index($ind1) ) );
2059 4         248 $tn->index($ind2) .= cosh( $n * acosh( $x->index($ind2) ) );
2060              
2061 4         95 return $tn;
2062             }
2063              
2064 1 50       4 $n = topdl($n) if ref $n;
2065 1 50       34 return abs($x) <= 1 ? cos( $n * acos($x) ) : cosh( $n * acosh($x) );
2066             }
2067              
2068              
2069             sub cos_mult_to_pow {
2070 9     9 1 1325 my( @ain ) = @_;
2071 9 100       21 barf('cos_mult_to_pow: number of args not less than 8.') if @ain > 7;
2072              
2073 8         13 my $ex = 7 - @ain;
2074              
2075 8         13 my @a = ( @ain, (0) x $ex );
2076              
2077 8         45 my (@cs) = (
2078             -$a[6] + $a[4] - $a[2] + $a[0],
2079             -5 * $a[5] + 3 * $a[3] - $a[1],
2080             18 * $a[6] - 8 * $a[4] + 2 * $a[2],
2081             20 * $a[5] - 4 * $a[3],
2082             8 * $a[4] - 48 * $a[6],
2083             -16 * $a[5],
2084             32 * $a[6]
2085             );
2086              
2087 8         18 pop @cs for 1 .. $ex;
2088              
2089 8         29 @cs;
2090             }
2091              
2092             1;
2093              
2094             __END__