File Coverage

blib/lib/PDL/DSP/Windows.pm
Criterion Covered Total %
statement 496 562 88.2
branch 208 242 85.9
condition 31 42 73.8
subroutine 105 113 92.9
pod 58 97 59.7
total 898 1056 85.0


line stmt bran cond sub pod time code
1             package PDL::DSP::Windows;
2              
3             our $VERSION = '0.008';
4              
5 7     7   963112 use strict;
  7         64  
  7         214  
6 7     7   48 use warnings;
  7         23  
  7         225  
7              
8 7     7   38 use Carp qw( croak carp );
  7         10  
  7         401  
9 7     7   3040 use PDL::LiteF;
  7         3254  
  7         34  
10 7     7   789614 use PDL::FFT;
  7         49228  
  7         53  
11 7     7   1151 use PDL::Math qw( acos cosh acosh );
  7         14  
  7         35  
12 7     7   626 use PDL::Core qw( topdl );
  7         33  
  7         32  
13 7     7   512 use PDL::MatrixOps qw( eigens_sym );
  7         16  
  7         42  
14 7     7   531 use PDL::Options qw( iparse ifhref );
  7         50  
  7         411  
15 7     7   3903 use Variable::Magic qw( wizard cast );
  7         8369  
  7         811  
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 7   50     15 USE_FFTW_DIRECTION => do {
      50        
      50        
22 7     7   3197 use version;
  7         13590  
  7         44  
23 7         1285 version->parse($PDL::VERSION) <= version->parse('2.007');
24             },
25 7     7   772 };
  7         18  
26              
27 7     7   3458 use namespace::clean;
  7         91173  
  7         64  
28              
29             # These constants defined after cleaning our namespace to avoid
30             # breaking existing code that might use them.
31 7     7   18656 use constant PI => 4 * atan2(1, 1);
  7         18  
  7         593  
32 7     7   50 use constant TPI => 2 * PI;
  7         18  
  7         338  
33              
34 7     7   42 use Exporter 'import';
  7         17  
  7         70128  
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             # These are not exported
78             my %periodic_windows = (
79             bartlett => 1,
80             bartlett_hann => 1,
81             blackman => 1,
82             blackman_bnh => 1,
83             blackman_ex => 1,
84             blackman_gen => 1,
85             blackman_gen3 => 1,
86             blackman_gen4 => 1,
87             blackman_gen5 => 1,
88             blackman_harris => 1,
89             blackman_harris4 => 1,
90             blackman_nuttall => 1,
91             bohman => 1,
92             cauchy => 1,
93             cos_alpha => 1,
94             cosine => 1,
95             dpss => 1,
96             exponential => 1,
97             flattop => 1,
98             gaussian => 1,
99             hamming => 1,
100             hamming_ex => 1,
101             hamming_gen => 1,
102             hann => 1,
103             hann_poisson => 1,
104             kaiser => 1,
105             lanczos => 1,
106             nuttall => 1,
107             nuttall1 => 1,
108             parzen => 1,
109             poisson => 1,
110             rectangular => 1,
111             triangular => 1,
112             tukey => 1,
113             welch => 1,
114             );
115              
116             my %window_aliases = (
117             bartlett_hann => [ 'Modified Bartlett-Hann' ],
118             bartlett => [ 'fejer' ],
119             blackman_harris4 => [ 'Blackman-Harris' ],
120             blackman_harris => [ 'Minimum three term (sample) Blackman-Harris' ],
121             cauchy => [ 'Abel', 'Poisson' ],
122             chebyshev => [ 'Dolph-Chebyshev' ],
123             cos_alpha => [ 'Power-of-cosine' ],
124             cosine => [ 'sine' ],
125             dpss => [ 'sleppian' ],
126             gaussian => [ 'Weierstrass' ],
127             hann => [ 'hanning' ],
128             kaiser => [ 'Kaiser-Bessel' ],
129             lanczos => [ 'sinc' ],
130             parzen => [ 'Jackson', 'Valle-Poussin' ],
131             rectangular => [ 'dirichlet', 'boxcar' ],
132             tukey => [ 'tapered cosine' ],
133             welch => [ 'Riez', 'Bochner', 'Parzen', 'parabolic' ],
134             );
135              
136             my %window_parameters = (
137             blackman_gen => [ '$alpha' ],
138             blackman_gen3 => [ '$a0', '$a1', '$a2' ],
139             blackman_gen4 => [ '$a0', '$a1', '$a2', '$a3' ],
140             blackman_gen5 => [ '$a0', '$a1', '$a2', '$a3', '$a4' ],
141             cauchy => [ '$alpha' ],
142             chebyshev => [ '$at' ],
143             cos_alpha => [ '$alpha' ],
144             dpss => [ '$beta' ],
145             gaussian => [ '$beta' ],
146             hamming_gen => [ '$a' ],
147             hann_poisson => [ '$alpha' ],
148             kaiser => [ '$beta' ],
149             poisson => [ '$alpha' ],
150             tukey => [ '$alpha' ],
151             );
152              
153             my %window_names = (
154             bartlett_hann => 'Bartlett-Hann',
155             blackman_bnh => '*An improved version of the 3-term Blackman-Harris window given by Nuttall (Ref 2, p. 89).',
156             blackman_ex => q!'exact' Blackman!,
157             blackman_gen => '*A single parameter family of the 3-term Blackman window. ',
158             blackman_gen3 => '*The general form of the Blackman family. ',
159             blackman_gen4 => '*The general 4-term Blackman-Harris window. ',
160             blackman_gen5 => '*The general 5-term Blackman-Harris window. ',
161             blackman_harris4 => 'minimum (sidelobe) four term Blackman-Harris',
162             blackman_harris => 'Blackman-Harris',
163             blackman_nuttall => 'Blackman-Nuttall',
164             blackman => q!'classic' Blackman!,
165             dpss => 'Digital Prolate Spheroidal Sequence (DPSS)',
166             flattop => 'flat top',
167             hamming_ex => q!'exact' Hamming!,
168             hamming_gen => 'general Hamming',
169             hann_matlab => '*Equivalent to the Hann window of N+2 points, with the endpoints (which are both zero) removed.',
170             hann_poisson => 'Hann-Poisson',
171             nuttall1 => '*A window referred to as the Nuttall window.',
172             parzen_octave => 'Parzen',
173             );
174              
175             my %window_print_names = (
176             blackman_bnh => 'Blackman-Harris (bnh)',
177             blackman_gen => 'General classic Blackman',
178             hann_matlab => 'Hann (matlab)',
179             nuttall1 => 'Nuttall (v1)',
180             );
181              
182             our @EXPORT_OK = (
183             keys %symmetric_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 167     167 1 78984 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 18364 my ($expr) = @_;
332              
333 7         14 my @match;
334 7 100       18 if ($expr) {
335 6         123 for my $name ( sort keys %symmetric_windows ) {
336 228 100       547 if ( $name =~ /$expr/ ) {
337 59         94 push @match, $name;
338 59         83 next;
339             }
340              
341 169   100     206 for my $alias ( grep /$expr/i, @{ $window_aliases{$name} // [] } ) {
  169         542  
342 1         5 push @match, "$name (alias $alias)";
343 1         5 next;
344             }
345             }
346             }
347             else {
348 1         22 @match = sort keys %symmetric_windows;
349             }
350              
351 7         270 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 193     193 1 15544 my $proto = shift;
379 193   66     860 my $self = bless {}, ref $proto || $proto;
380 193 100       700 $self->init(@_) if @_;
381 192         447 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 206     206 1 11103 my $self = shift;
406              
407 206         338 my ( $N, $name, $params, $periodic );
408              
409 206 100       482 $N = shift unless ref $_[0];
410 206 100       408 $name = shift unless ref $_[0];
411 206 100       479 $params = shift unless ref $_[0] eq 'HASH';
412 206 100       393 $periodic = shift unless ref $_[0];
413              
414 206   100     992 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 206   66     49145 $name ||= $opts->{name};
422 206   100     446 $N ||= $opts->{N};
423 206   100     799 $periodic ||= $opts->{periodic};
424 206   100     771 $params //= $opts->{params};
425 206 100 100     600 $params = [$params] if defined $params && !ref $params;
426              
427 206         512 $name =~ s/_per$//;
428              
429 206 100       470 my $windows = $periodic ? \%periodic_windows : \%symmetric_windows;
430 206 100       549 unless ( $windows->{$name}) {
431 1 50       4 my $type = $periodic ? 'periodic' : 'symmetric';
432 1         9 barf "window: Unknown $type window '$name'.";
433             }
434              
435 205         401 $self->{name} = $name;
436 205         315 $self->{N} = $N;
437 205         310 $self->{periodic} = $periodic;
438 205         327 $self->{params} = $params;
439 205 100       1350 $self->{code} = __PACKAGE__->can( $name . ( $periodic ? '_per' : '' ) );
440 205         907 $self->{samples} = undef;
441 205         329 $self->{modfreqs} = undef;
442              
443 205         479 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 195     195 1 292 my $self = shift;
471 195   100     302 my @args = ( $self->{N}, @{ $self->{params} // [] } );
  195         727  
472 195         604 $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 13 my $self = shift;
505 8         39 my %opts = iparse( { min_bins => 1000 }, ifhref(shift) );
506              
507 8         2003 my $data = $self->get_samples;
508              
509 8         583 my $n = $data->nelem;
510 8 50       25 my $fn = $n > $opts{min_bins} ? 2 * $n : $opts{min_bins};
511              
512 8         14 $n--;
513              
514 8         25 my $freq = zeroes($fn);
515 8         690 $freq->slice("0:$n") .= $data;
516              
517 8         307 PDL::FFT::realfft($freq);
518              
519 8         2049 my $real = zeros($freq);
520 8         542 my $img = zeros($freq);
521 8         630 my $mid = ( $freq->nelem ) / 2 - 1;
522 8         17 my $mid1 = $mid + 1;
523              
524 8         32 $real->slice("0:$mid") .= $freq->slice("$mid:0:-1");
525 8         355 $real->slice("$mid1:-1") .= $freq->slice("0:$mid");
526 8         321 $img->slice("0:$mid") .= $freq->slice("-1:$mid1:-1");
527 8         366 $img->slice("$mid1:-1") .= $freq->slice("$mid1:-1");
528              
529 8         1397 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 843 my $self = shift;
555 5         11 my @res;
556              
557 5         11 for (@_) {
558 7 100       22 if ($_ eq 'samples') {
    100          
559 2         6 push @res, $self->get_samples;
560             }
561             elsif ($_ eq 'modfreqs') {
562 2         6 push @res, $self->get_modfreqs;
563             }
564             else {
565 3         8 push @res, $self->{$_};
566             }
567             };
568              
569 5 100       202 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 2185 my $self = shift;
588 33 100       153 return $self->{samples} if defined $self->{samples};
589 21         45 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 1637 my $self = shift;
624 7 100       26 return $self->modfreqs(@_) if @_;
625 5 100       21 return $self->{modfreqs} if defined $self->{modfreqs};
626 2         7 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 10 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 8 my $self = shift;
663              
664 4 100       13 if ( my $name = $window_print_names{ $self->{name} } ) {
665 1         6 return "$name window";
666             }
667              
668 3 100       13 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         6 return ucfirst $self->{name} . ' window';
674             }
675              
676             sub get_param_names {
677 4     4 0 9 my $self = shift;
678 4         11 my $params = $window_parameters{ $self->{name} };
679 4 100       12 return undef unless $params;
680 3         4 return [ @{$params} ];
  3         14  
681             }
682              
683             sub format_param_vals {
684 2     2 0 5 my $self = shift;
685              
686 2 50       3 my @params = @{ $self->{params} || [] };
  2         9  
687 2 50       8 return '' unless @params;
688              
689 2 50       4 my @names = @{ $self->get_param_names || [] };
  2         5  
690 2 50       7 return '' unless @names;
691              
692             join ', ', map {
693 2         6 my $name = $names[$_];
  4         7  
694 4         13 $name =~ s/^\$//;
695 4         24 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 133 my $w = shift->get_samples;
839 17         33051 $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 30 my $w = shift->get_samples;
857 1         182 $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 4 100   4 1 2460 barf 'bartlett: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
921 2         5 my ($N) = @_;
922 2         9 1 - abs( zeroes($N)->xlinvals( -1, 1 ) );
923             }
924              
925             sub bartlett_per {
926 4 100   4 0 2417 barf 'bartlett: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
927 2         6 my ($N) = @_;
928 2         11 1 - abs( zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) );
929             }
930              
931             sub bartlett_hann {
932 5 100   5 1 2449 barf 'bartlett_hann: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
933 3         9 my ($N) = @_;
934 3         15 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 4 100   4 0 2442 barf 'bartlett_hann: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
940 2         5 my ($N) = @_;
941 2         9 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 5 100   5 1 2447 barf 'blackman: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
947 3         9 my ($N) = @_;
948 3         12 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
949 3         2108 0.34 + $cx * ( -0.5 + $cx * 0.16 );
950             }
951              
952             sub blackman_per {
953 4 100   4 0 2429 barf 'blackman: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
954 2         6 my ($N) = @_;
955 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
956 2         373 0.34 + $cx * ( -0.5 + $cx * 0.16 );
957             }
958              
959             sub blackman_bnh {
960 4 100   4 1 2409 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         366 0.3461008 + $cx * ( -0.4973406 + $cx * 0.1565586 );
964             }
965              
966             sub blackman_bnh_per {
967 4 100   4 0 2404 barf 'blackman_bnh: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
968 2         7 my ($N) = @_;
969 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
970 2         380 0.3461008 + $cx * ( -0.4973406 + $cx * 0.1565586 );
971             }
972              
973             sub blackman_ex {
974 4 100   4 1 2398 barf 'blackman_ex: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
975 2         6 my ($N) = @_;
976 2         9 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
977 2         373 0.349742046431642 + $cx * ( -0.496560619088564 + $cx * 0.153697334479794 );
978             }
979              
980             sub blackman_ex_per {
981 4 100   4 0 2412 barf 'blackman_ex: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
982 2         5 my ($N) = @_;
983 2         9 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
984 2         412 0.349742046431642 + $cx * ( -0.496560619088564 + $cx * 0.153697334479794 );
985             }
986              
987             sub blackman_gen {
988 5 100   5 1 3643 barf 'blackman_gen: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
989 2         7 my ( $N, $alpha ) = @_;
990 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
991 2         390 0.5 - $alpha + $cx * ( -0.5 + $cx * $alpha );
992             }
993              
994             sub blackman_gen_per {
995 5 100   5 0 3616 barf 'blackman_gen: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
996 2         8 my ($N,$alpha) = @_;
997 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
998 2         386 0.5 - $alpha + $cx * ( -0.5 + $cx * $alpha );
999             }
1000              
1001             sub blackman_gen3 {
1002 7 100   7 1 6093 barf 'blackman_gen3: 4 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 4;
1003 2         6 my ($N,$a0,$a1,$a2) = @_;
1004 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1005 2         392 $a0 - $a2 + ( $cx * ( -$a1 + $cx * 2 * $a2 ) );
1006             }
1007              
1008             sub blackman_gen3_per {
1009 7 100   7 0 6059 barf 'blackman_gen3: 4 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 4;
1010 2         8 my ( $N, $a0, $a1, $a2 ) = @_;
1011 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1012 2         386 $a0 - $a2 + ( $cx * ( -$a1 + $cx * 2 * $a2 ) );
1013             }
1014              
1015             sub blackman_gen4 {
1016 8 100   8 1 7247 barf 'blackman_gen4: 5 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 5;
1017 2         7 my ( $N, $a0, $a1, $a2, $a3 ) = @_;
1018 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1019 2         415 $a0 - $a2 + $cx * ( ( -$a1 + 3 * $a3 ) + $cx * ( 2 * $a2 + $cx * -4 * $a3 ) );
1020             }
1021              
1022             sub blackman_gen4_per {
1023 8 100   8 0 7230 barf 'blackman_gen4: 5 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 5;
1024 2         7 my ( $N, $a0, $a1, $a2, $a3 ) = @_;
1025 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1026 2         408 $a0 - $a2 + $cx * ( ( -$a1 + 3 * $a3 ) + $cx * ( 2 * $a2 + $cx * -4 * $a3 ) );
1027             }
1028              
1029             sub blackman_gen5 {
1030 9 100   9 1 8501 barf 'blackman_gen5: 6 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 6;
1031 2         8 my ( $N, $a0, $a1, $a2, $a3, $a4 ) = @_;
1032 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1033 2         433 $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 9 100   9 0 8429 barf 'blackman_gen5: 6 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 6;
1039 2         8 my ( $N, $a0, $a1, $a2, $a3, $a4 ) = @_;
1040 2         10 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1041 2         431 $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 4 100   4 1 2439 barf 'blackman_harris: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1047 2         5 my ($N) = @_;
1048 2         9 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1049 2         376 0.343103 + $cx * ( -0.49755 + $cx * 0.15844 );
1050             }
1051              
1052             sub blackman_harris_per {
1053 4 100   4 0 2457 barf 'blackman_harris: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1054 2         5 my ($N) = @_;
1055 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1056 2         368 0.343103 + $cx * ( -0.49755 + $cx * 0.15844 );
1057             }
1058              
1059             sub blackman_harris4 {
1060 6 100   6 1 2808 barf 'blackman_harris4: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1061 4         11 my ($N) = @_;
1062 4         15 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1063 4         2133 0.21747 + $cx * ( -0.45325 + $cx * ( 0.28256 + $cx * -0.04672 ) );
1064             }
1065              
1066             sub blackman_harris4_per {
1067 4 100   4 0 2425 barf 'blackman_harris4: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1068 2         8 my ($N) = @_;
1069 2         9 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1070 2         389 0.21747 + $cx * ( -0.45325 + $cx * ( 0.28256 + $cx * -0.04672 ) );
1071             }
1072              
1073             sub blackman_nuttall {
1074 5 100   5 1 2439 barf 'blackman_nuttall: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1075 3         8 my ($N) = @_;
1076 3         12 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1077 3         627 0.2269824 + $cx * ( -0.4572542 + $cx * ( 0.273199 + $cx * -0.0425644 ) );
1078             }
1079              
1080             sub blackman_nuttall_per {
1081 4 100   4 0 2417 barf 'blackman_nuttall: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1082 2         6 my ($N) = @_;
1083 2         10 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1084 2         386 0.2269824 + $cx * ( -0.4572542 + $cx * ( 0.273199 + $cx * -0.0425644 ) );
1085             }
1086              
1087             sub bohman {
1088 6 100   6 1 2426 barf 'bohman: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1089 4         12 my ($N) = @_;
1090 4         15 my $x = abs( zeroes($N)->xlinvals( -1, 1 ) );
1091 4         1014 ( 1 - $x ) * cos( PI * $x ) + ( 1 / PI ) * sin( PI * $x );
1092             }
1093              
1094             sub bohman_per {
1095 4 100   4 0 2411 barf 'bohman: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1096 2         5 my ($N) = @_;
1097 2         8 my $x = abs( zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) );
1098 2         333 ( 1 - $x ) * cos( PI * $x ) + ( 1 / PI ) * sin( PI * $x );
1099             }
1100              
1101             sub cauchy {
1102 6 100   6 1 3647 barf 'cauchy: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1103 3         9 my ( $N, $alpha ) = @_;
1104 3         13 1 / ( 1 + ( zeroes($N)->xlinvals( -1, 1 ) * $alpha ) ** 2 );
1105             }
1106              
1107             sub cauchy_per {
1108 5 100   5 0 3636 barf 'cauchy: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1109 2         6 my ( $N, $alpha ) = @_;
1110 2         9 1 / ( 1 + ( zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) * $alpha ) ** 2 );
1111             }
1112              
1113             sub chebyshev {
1114 5 100   5 1 3609 barf 'chebyshev: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1115 2         5 my ( $N, $at ) = @_;
1116              
1117 2         3 my ( $M, $M1, $pos, $pos1 );
1118              
1119 2         108 my $beta = cosh( 1 / ( $N - 1 ) * acosh( 1 / ( 10 ** ( -$at / 20 ) ) ) );
1120 2         20 my $x = $beta * cos( PI * sequence($N) / $N );
1121              
1122 2         297 my $cw = chebpoly( $N - 1, $x );
1123              
1124 2 100       8 if ( $N % 2 ) { # odd
1125 1         4 $M1 = ( $N + 1 ) / 2;
1126 1         3 $M = $M1 - 1;
1127 1         2 $pos = 0;
1128 1         2 $pos1 = 1;
1129              
1130 1         4 PDL::FFT::realfft($cw);
1131             }
1132             else { # half-sample delay (even order)
1133 1         6 my $arg = PI / $N * sequence($N);
1134 1         111 my $cw_im = $cw * sin($arg);
1135 1         21 $cw *= cos($arg);
1136              
1137 1         25 if (USE_FFTW_DIRECTION) {
1138             PDL::FFT::fftnd( $cw, $cw_im );
1139             }
1140             else {
1141 1         6 PDL::FFT::ifftnd( $cw, $cw_im );
1142             }
1143              
1144 1         133 $M1 = $N / 2;
1145 1         3 $M = $M1 - 1;
1146 1         2 $pos = 1;
1147 1         4 $pos1 = 0;
1148             }
1149              
1150 2         150 $cw /= $cw->at($pos);
1151              
1152 2         47 my $cwout = zeroes($N);
1153              
1154 2         152 $cwout->slice("0:$M") .= $cw->slice("$M:0:-1");
1155 2         90 $cwout->slice("$M1:-1") .= $cw->slice("$pos1:$M");
1156 2         81 $cwout /= max($cwout);
1157              
1158 2         161 $cwout;
1159             }
1160              
1161             sub cos_alpha {
1162 8 100   8 1 3655 barf 'cos_alpha: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1163 5         14 my ( $N, $alpha ) = @_;
1164 5         16 ( sin( zeroes($N)->xlinvals( 0, PI ) ) ) ** $alpha ;
1165             }
1166              
1167             sub cos_alpha_per {
1168 5 100   5 0 3684 barf 'cos_alpha: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1169 2         7 my ( $N, $alpha ) = @_;
1170 2         8 sin( zeroes($N)->xlinvals( 0, PI * ( $N - 1 ) / $N ) ) ** $alpha ;
1171             }
1172              
1173             sub cosine {
1174 5 100   5 1 2445 barf 'cosine: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1175 3         9 my ($N) = @_;
1176 3         13 sin( zeroes($N)->xlinvals( 0, PI ) );
1177             }
1178              
1179             sub cosine_per {
1180 4 100   4 0 2415 barf 'cosine: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1181 2         6 my ($N) = @_;
1182 2         9 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 4 100   4 1 2394 barf 'exponential: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1228 2         5 my ($N) = @_;
1229 2         9 2 ** ( 1 - abs( zeroes($N)->xlinvals( -1, 1 ) ) ) - 1;
1230             }
1231              
1232             sub exponential_per {
1233 4 100   4 0 2404 barf 'exponential: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1234 2         6 my ($N) = @_;
1235 2         9 2 ** ( 1 - abs( zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) ) ) - 1;
1236             }
1237              
1238             sub flattop {
1239 6 100   6 1 2522 barf 'flattop: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1240 4         10 my ($N) = @_;
1241 4         16 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1242 4         2036 -0.05473684 + $cx * ( -0.165894739 + $cx * ( 0.498947372 + $cx * ( -0.334315788 + $cx * 0.055578944 ) ) );
1243             }
1244              
1245             sub flattop_per {
1246 4 100   4 0 2415 barf 'flattop: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1247 2         4 my ($N) = @_;
1248 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1249 2         396 -0.05473684 + $cx * ( -0.165894739 + $cx * ( 0.498947372 + $cx * ( -0.334315788 + $cx * 0.055578944 ) ) );
1250             }
1251              
1252             sub gaussian {
1253 5 100   5 1 3636 barf 'gaussian: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1254 2         6 my ( $N, $beta ) = @_;
1255 2         9 exp( -0.5 * ( $beta * zeroes($N)->xlinvals( -1, 1 ) ) ** 2);
1256             }
1257              
1258             sub gaussian_per {
1259 5 100   5 0 3678 barf 'gaussian: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1260 2         8 my ( $N, $beta ) = @_;
1261 2         9 exp( -0.5 * ( $beta * zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) ) ** 2 );
1262             }
1263              
1264             sub hamming {
1265 21 100   21 1 2458 barf 'hamming: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1266 19         36 my ($N) = @_;
1267 19         69 0.54 + -0.46 * cos( zeroes($N)->xlinvals( 0, TPI ) );
1268             }
1269              
1270             sub hamming_per {
1271 4 100   4 0 2380 barf 'hamming: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1272 2         7 my ($N) = @_;
1273 2         9 0.54 + -0.46 * cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1274             }
1275              
1276             sub hamming_ex {
1277 4 100   4 1 2436 barf 'hamming_ex: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1278 2         6 my ($N) = @_;
1279 2         9 0.53836 + -0.46164 * cos( zeroes($N)->xlinvals( 0, TPI ) );
1280             }
1281              
1282             sub hamming_ex_per {
1283 4 100   4 0 2396 barf 'hamming_ex: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1284 2         7 my ($N) = @_;
1285 2         9 0.53836 + -0.46164 * cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1286             }
1287              
1288             sub hamming_gen {
1289 5 100   5 1 3622 barf 'hamming_gen: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1290 2         5 my ( $N, $a ) = @_;
1291 2         10 $a - ( 1 - $a ) * cos( zeroes($N)->xlinvals( 0, TPI ) );
1292             }
1293              
1294             sub hamming_gen_per {
1295 5 100   5 0 3619 barf 'hamming_gen: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1296 2         6 my ( $N, $a ) = @_;
1297 2         10 $a - ( 1 - $a ) * cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1298             }
1299              
1300             sub hann {
1301 7 100   7 1 2423 barf 'hann: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1302 5         14 my ($N) = @_;
1303 5         19 0.5 + -0.5 * cos( zeroes($N)->xlinvals( 0, TPI ) );
1304             }
1305              
1306             sub hann_per {
1307 4 100   4 0 2414 barf 'hann: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1308 2         7 my ($N) = @_;
1309 2         10 0.5 + -0.5 * cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1310             }
1311              
1312             sub hann_matlab {
1313 3 100   3 1 2401 barf 'hann_matlab: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1314 1         3 my ($N) = @_;
1315 1         5 0.5 - 0.5 * cos( zeroes($N)->xlinvals( TPI / ( $N + 1 ), TPI * $N / ( $N + 1 ) ) );
1316             }
1317              
1318             sub hann_poisson {
1319 6 100   6 1 3703 barf 'hann_poisson: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1320 3         8 my ( $N, $alpha ) = @_;
1321 3         13 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 5 100   5 0 3621 barf 'hann_poisson: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1328 2         5 my ( $N, $alpha ) = @_;
1329 2         8 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 5 100   5 1 2397 barf 'lanczos: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1367 3         9 my ($N) = @_;
1368              
1369 3         15 my $x = PI * zeroes($N)->xlinvals( -1, 1 );
1370 3         648 my $res = sin($x) / $x;
1371              
1372 3 100       778 $res->slice( int( $N / 2 ) ) .= 1 if $N % 2;
1373              
1374 3         61 $res;
1375             }
1376              
1377             sub lanczos_per {
1378 4 100   4 0 2765 barf 'lanczos: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1379 2         8 my ($N) = @_;
1380              
1381 2         10 my $x = PI * zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N );
1382 2         306 my $res = sin($x) / $x;
1383              
1384 2 100       59 $res->slice( int( $N / 2 ) ) .= 1 unless $N % 2;
1385              
1386 2         50 $res;
1387             }
1388              
1389             sub nuttall {
1390 4 100   4 1 2438 barf 'nuttall: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1391 2         6 my ($N) = @_;
1392 2         8 my $cx = cos( zeroes($N)->xlinvals( 0, TPI ) );
1393 2         400 0.2269824 + $cx * ( -0.4572542 + $cx * ( 0.273199 + $cx * -0.0425644 ) );
1394             }
1395              
1396             sub nuttall_per {
1397 4 100   4 0 2439 barf 'nuttall: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1398 2         5 my ($N) = @_;
1399 2         7 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1400 2         385 0.2269824 + $cx * ( -0.4572542 + $cx * ( 0.273199 + $cx * -0.0425644 ) );
1401             }
1402              
1403             sub nuttall1 {
1404 4 100   4 1 2439 barf 'nuttall1: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1405 2         5 my ($N) = @_;
1406 2         8 my $cx = (cos(zeroes($N)->xlinvals(0,TPI)));
1407              
1408 2         420 (0.211536) + ($cx * ((-0.449584) + ($cx * (0.288464 + $cx * (-0.050416) ))));
1409             }
1410              
1411             sub nuttall1_per {
1412 4 100   4 0 4281 barf 'nuttall1: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1413 2         7 my ($N) = @_;
1414 2         9 my $cx = cos( zeroes($N)->xlinvals( 0, TPI * ( $N - 1 ) / $N ) );
1415 2         383 0.211536 + $cx * ( -0.449584 + $cx * ( 0.288464 + $cx * -0.050416 ) );
1416             }
1417              
1418             sub parzen {
1419 6 100   6 1 2406 barf 'parzen: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1420 4         12 my ($N) = @_;
1421              
1422 4         18 my $x = zeroes($N)->xlinvals( -1, 1 );
1423              
1424 4         872 my $x1 = $x->where( $x <= -0.5 );
1425 4         573 my $x2 = $x->where( ( $x < 0.5 ) & ( $x > -0.5 ) );
1426 4         345 my $x3 = $x->where( $x >= 0.5 );
1427              
1428 4         243 $x1 .= 2 * ( 1 - abs($x1) ) ** 3;
1429 4         941 $x2 .= 1 - 6 * $x2 ** 2 * ( 1 - abs($x2) );
1430 4         306 $x3 .= $x1->slice('-1:0:-1');
1431              
1432 4         224 $x;
1433             }
1434              
1435             sub parzen_per {
1436 4 100   4 0 2436 barf 'parzen: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1437 2         6 my ($N) = @_;
1438              
1439 2         10 my $x = zeroes($N)->xlinvals( -1, ( -1 + ( $N - 1 ) ) / $N);
1440              
1441 2         305 my $x1 = $x->where( $x <= -0.5 );
1442 2         146 my $x2 = $x->where( ( $x < 0.5 ) & ( $x > -0.5 ) );
1443 2         159 my $x3 = $x->where( $x >= 0.5 );
1444              
1445 2         97 $x1 .= 2 * ( 1 - abs($x1)) ** 3;
1446 2         145 $x2 .= 1 - 6 * $x2 ** 2 * ( 1 - abs($x2) );
1447 2         100 $x3 .= $x1->slice('-1:1:-1');
1448              
1449 2         81 $x;
1450             }
1451              
1452             sub parzen_octave {
1453 4 100   4 1 2428 barf 'parzen_octave: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1454 2         6 my ($N) = @_;
1455              
1456 2         8 my $r = ( $N - 1 ) / 2;
1457 2         5 my $N2 = $N / 2;
1458 2         5 my $r4 = $r / 2;
1459 2         13 my $n = sequence( 2 * $r + 1 ) - $r;
1460              
1461 2         369 my $n1 = $n->where( abs($n) <= $r4 );
1462 2         326 my $n2 = $n->where( $n > $r4 );
1463 2         209 my $n3 = $n->where( $n < -$r4 );
1464              
1465 2         155 $n1 .= 1 - 6 * ( abs($n1) / $N2 ) ** 2 + 6 * ( abs($n1) / $N2 ) ** 3;
1466 2         1428 $n2 .= 2 * ( 1 - abs($n2) / $N2 ) ** 3;
1467 2         608 $n3 .= 2 * ( 1 - abs($n3) / $N2 ) ** 3;
1468              
1469 2         630 $n;
1470             }
1471              
1472             sub poisson {
1473 6 100   6 1 3674 barf 'poisson: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1474 3         10 my ( $N, $alpha ) = @_;
1475 3         13 exp( -$alpha * abs( zeroes($N)->xlinvals( -1, 1 ) ) );
1476             }
1477              
1478             sub poisson_per {
1479 5 100   5 0 3615 barf 'poisson: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1480 2         6 my ( $N, $alpha ) = @_;
1481 2         8 exp( -$alpha * abs( zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) ) );
1482             }
1483              
1484             sub rectangular {
1485 6 100   6 1 2406 barf 'rectangular: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1486 4         10 my ($N) = @_;
1487 4         18 ones($N);
1488             }
1489              
1490             sub rectangular_per {
1491 4 100   4 0 2392 barf 'rectangular: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1492 2         6 my ($N) = @_;
1493 2         8 ones($N);
1494             }
1495              
1496             sub triangular {
1497 6 100   6 1 2432 barf 'triangular: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1498 4         10 my ($N) = @_;
1499 4         16 1 - abs( zeroes($N)->xlinvals( -( $N - 1 ) / $N, ( $N - 1 ) / $N ) );
1500             }
1501              
1502             sub triangular_per {
1503 4 100   4 0 2461 barf 'triangular: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1504 2         5 my ($N) = @_;
1505 2         44 1 - abs( zeroes($N)->xlinvals( -$N / ( $N + 1 ), -1 / ( $N + 1 ) + ( $N - 1 ) / ( $N + 1 ) ) );
1506             }
1507              
1508             sub tukey {
1509 15 100   15 1 6088 barf 'tukey: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1510 12         36 my ( $N, $alpha ) = @_;
1511              
1512 12 100 100     63 barf('tukey: alpha must be between 0 and 1') unless $alpha >= 0 and $alpha <= 1;
1513              
1514 10 50       25 return ones($N) if $alpha == 0;
1515              
1516 10         35 my $x = zeroes($N)->xlinvals( 0, 1 );
1517              
1518 10         1811 my $x1 = $x->where( $x < $alpha / 2 );
1519 10         1126 my $x2 = $x->where( ( $x <= 1 - $alpha / 2 ) & ( $x >= $alpha / 2 ) );
1520 10         704 my $x3 = $x->where( $x > 1 - $alpha / 2 );
1521              
1522 10         803 $x1 .= 0.5 * ( 1 + cos( PI * ( 2 * $x1 / $alpha - 1 ) ) );
1523 10         758 $x2 .= 1;
1524 10         326 $x3 .= $x1->slice('-1:0:-1');
1525              
1526 10         493 return $x;
1527             }
1528              
1529             sub tukey_per {
1530 12 100   12 0 6015 barf 'tukey: 2 arguments expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 2;
1531 9         24 my ( $N, $alpha ) = @_;
1532              
1533 9 100 100     47 barf('tukey: alpha must be between 0 and 1') unless $alpha >= 0 and $alpha <= 1;
1534              
1535 7 50       18 return ones($N) if $alpha == 0;
1536              
1537 7         23 my $x = zeroes($N)->xlinvals( 0, ( $N - 1 ) / $N );
1538              
1539 7         1061 my $x1 = $x->where( $x < $alpha / 2 );
1540 7         495 my $x2 = $x->where( ( $x <= 1 - $alpha / 2 ) & ( $x >= $alpha / 2 ) );
1541 7         392 my $x3 = $x->where( $x > 1 - $alpha / 2 );
1542              
1543 7         465 $x1 .= 0.5 * ( 1 + cos( PI * ( 2 * $x1 / $alpha - 1 ) ) );
1544 7         349 $x2 .= 1;
1545 7         144 $x3 .= $x1->slice('-1:1:-1');
1546              
1547 7         306 return $x;
1548             }
1549              
1550             sub welch {
1551 5 100   5 1 2399 barf 'welch: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1552 3         6 my ($N) = @_;
1553 3         12 1 - zeroes($N)->xlinvals( -1, 1 ) ** 2;
1554             }
1555              
1556             sub welch_per {
1557 4 100   4 0 2372 barf 'welch: 1 argument expected. Got ' . scalar(@_) . ' arguments.' unless @_ == 1;
1558 2         5 my ($N) = @_;
1559 2         10 1 - zeroes($N)->xlinvals( -1, ( -1 + 1 * ( $N - 1 ) ) / $N ) ** 2;
1560             }
1561              
1562             # Deprecated static data
1563             # These package variables will be removed entirely sometime in 2021
1564              
1565             $window_definitions{$_} = {} for keys %symmetric_windows;
1566              
1567             $window_definitions{$_}{alias} = [ @{ $window_aliases{$_} } ]
1568             for keys %window_aliases;
1569              
1570             $window_definitions{$_}{params} = [ @{ $window_parameters{$_} } ]
1571             for keys %window_parameters;
1572              
1573             $window_definitions{$_}{fn} = $window_names{$_}
1574             for keys %window_names;
1575              
1576             $window_definitions{$_}{pfn} = $window_print_names{$_}
1577             for keys %window_print_names;
1578              
1579             %winpersubs = map { $_ => __PACKAGE__->can("${_}_per") } keys %periodic_windows;
1580             %winsubs = map { $_ => __PACKAGE__->can($_) } keys %symmetric_windows;
1581              
1582             my $wizard = do {
1583             my $msg = 'Package variables from PDL::DSP::Windows are deprecated and will be removed in the future.';
1584              
1585             my $read = sub {
1586             carp $msg . ' This attempt to read from them will soon stop working';
1587             };
1588              
1589             my $write = sub {
1590             carp $msg . ' This attempt to write to them no longer has an effect';
1591             };
1592              
1593             wizard(
1594             fetch => $read,
1595             exists => $read,
1596             store => $write,
1597             delete => $write,
1598             );
1599             };
1600              
1601             cast %winsubs, $wizard;
1602             cast %winpersubs, $wizard;
1603             cast %window_definitions, $wizard;
1604              
1605             =head1 Symmetric window functions
1606              
1607             =head2 bartlett($N)
1608              
1609             The Bartlett window. (Ref 1). Another name for this window is the fejer window.
1610             This window is defined by
1611              
1612             1 - abs(arr)
1613              
1614             where the points in arr range from -1 through 1. See also
1615             L.
1616              
1617             =head2 bartlett_hann($N)
1618              
1619             The Bartlett-Hann window. Another name for this window is the Modified
1620             Bartlett-Hann window. This window is defined by
1621              
1622             0.62 - 0.48 * abs(arr) + 0.38 * arr1
1623              
1624             where the points in arr range from -1/2 through 1/2, and arr1 are the cos of
1625             points ranging from -PI through PI.
1626              
1627             =head2 blackman($N)
1628              
1629             The 'classic' Blackman window. (Ref 1). One of the Blackman-Harris family, with coefficients
1630              
1631             a0 = 0.42
1632             a1 = 0.5
1633             a2 = 0.08
1634              
1635             =head2 blackman_bnh($N)
1636              
1637             The Blackman-Harris (bnh) window. An improved version of the 3-term
1638             Blackman-Harris window given by Nuttall (Ref 2, p. 89). One of the
1639             Blackman-Harris family, with coefficients
1640              
1641             a0 = 0.4243801
1642             a1 = 0.4973406
1643             a2 = 0.0782793
1644              
1645             =head2 blackman_ex($N)
1646              
1647             The 'exact' Blackman window. (Ref 1). One of the Blackman-Harris family, with
1648             coefficients
1649              
1650             a0 = 0.426590713671539
1651             a1 = 0.496560619088564
1652             a2 = 0.0768486672398968
1653              
1654             =head2 blackman_gen($N,$alpha)
1655              
1656             The General classic Blackman window. A single parameter family of the 3-term
1657             Blackman window. This window is defined by
1658              
1659             my $cx = arr;
1660             .5 - $alpha + ($cx * (-.5 + $cx * $alpha));
1661              
1662             where the points in arr are the cos of points ranging from 0 through 2PI.
1663              
1664             =head2 blackman_gen3($N,$a0,$a1,$a2)
1665              
1666             The general form of the Blackman family. One of the Blackman-Harris family,
1667             with coefficients
1668              
1669             a0 = $a0
1670             a1 = $a1
1671             a2 = $a2
1672              
1673             =head2 blackman_gen4($N,$a0,$a1,$a2,$a3)
1674              
1675             The general 4-term Blackman-Harris window. One of the Blackman-Harris family,
1676             with coefficients
1677              
1678             a0 = $a0
1679             a1 = $a1
1680             a2 = $a2
1681             a3 = $a3
1682              
1683             =head2 blackman_gen5($N,$a0,$a1,$a2,$a3,$a4)
1684              
1685             The general 5-term Blackman-Harris window. One of the Blackman-Harris family,
1686             with coefficients
1687              
1688             a0 = $a0
1689             a1 = $a1
1690             a2 = $a2
1691             a3 = $a3
1692             a4 = $a4
1693              
1694             =head2 blackman_harris($N)
1695              
1696             The Blackman-Harris window. (Ref 1). One of the Blackman-Harris family, with
1697             coefficients
1698              
1699             a0 = 0.422323
1700             a1 = 0.49755
1701             a2 = 0.07922
1702              
1703             Another name for this window is the Minimum three term (sample) Blackman-Harris
1704             window.
1705              
1706             =head2 blackman_harris4($N)
1707              
1708             The minimum (sidelobe) four term Blackman-Harris window. (Ref 1). One of the
1709             Blackman-Harris family, with coefficients
1710              
1711             a0 = 0.35875
1712             a1 = 0.48829
1713             a2 = 0.14128a3 = 0.01168
1714              
1715             Another name for this window is the Blackman-Harris window.
1716              
1717             =head2 blackman_nuttall($N)
1718              
1719             The Blackman-Nuttall window. One of the Blackman-Harris family, with
1720             coefficients
1721              
1722             a0 = 0.3635819
1723             a1 = 0.4891775
1724             a2 = 0.1365995
1725             a3 = 0.0106411
1726              
1727             =head2 bohman($N)
1728              
1729             The Bohman window. (Ref 1). This window is defined by
1730              
1731             my $x = abs(arr);
1732             (1 - $x) * cos(PI * $x) + (1 / PI) * sin(PI * $x)
1733              
1734             where the points in arr range from -1 through 1.
1735              
1736             =head2 cauchy($N,$alpha)
1737              
1738             The Cauchy window. (Ref 1). Other names for this window are: Abel, Poisson.
1739             This window is defined by
1740              
1741             1 / (1 + (arr * $alpha) ** 2)
1742              
1743             where the points in arr range from -1 through 1.
1744              
1745             =head2 chebyshev($N,$at)
1746              
1747             The Chebyshev window. The frequency response of this window has C<$at> dB of
1748             attenuation in the stop-band. Another name for this window is the
1749             Dolph-Chebyshev window. No periodic version of this window is defined. This
1750             routine gives the same result as the routine B in Octave 3.6.2.
1751              
1752             =head2 cos_alpha($N,$alpha)
1753              
1754             The Cos_alpha window. (Ref 1). Another name for this window is the
1755             Power-of-cosine window. This window is defined by
1756              
1757             arr ** $alpha
1758              
1759             where the points in arr are the sin of points ranging from 0 through PI.
1760              
1761             =head2 cosine($N)
1762              
1763             The Cosine window. Another name for this window is the sine window. This
1764             window is defined by
1765              
1766             arr
1767              
1768             where the points in arr are the sin of points ranging from 0 through PI.
1769              
1770             =head2 dpss($N,$beta)
1771              
1772             The Digital Prolate Spheroidal Sequence (DPSS) window. The parameter C<$beta>
1773             is the half-width of the mainlobe, measured in frequency bins. This window
1774             maximizes the power in the mainlobe for given C<$N> and C<$beta>. Another
1775             name for this window is the sleppian window.
1776              
1777             =head2 exponential($N)
1778              
1779             The Exponential window. This window is defined by
1780              
1781             2 ** (1 - abs arr) - 1
1782              
1783             where the points in arr range from -1 through 1.
1784              
1785             =head2 flattop($N)
1786              
1787             The flat top window. One of the Blackman-Harris family, with coefficients
1788              
1789             a0 = 0.21557895
1790             a1 = 0.41663158
1791             a2 = 0.277263158
1792             a3 = 0.083578947
1793             a4 = 0.006947368
1794              
1795             =head2 gaussian($N,$beta)
1796              
1797             The Gaussian window. (Ref 1). Another name for this window is the Weierstrass
1798             window. This window is defined by
1799              
1800             exp (-0.5 * ($beta * arr )**2),
1801              
1802             where the points in arr range from -1 through 1.
1803              
1804             =head2 hamming($N)
1805              
1806             The Hamming window. (Ref 1). One of the Blackman-Harris family, with
1807             coefficients
1808              
1809             a0 = 0.54
1810             a1 = 0.46
1811              
1812             =head2 hamming_ex($N)
1813              
1814             The 'exact' Hamming window. (Ref 1). One of the Blackman-Harris family, with
1815             coefficients
1816              
1817             a0 = 0.53836
1818             a1 = 0.46164
1819              
1820             =head2 hamming_gen($N,$a)
1821              
1822             The general Hamming window. (Ref 1). One of the Blackman-Harris family, with
1823             coefficients
1824              
1825             a0 = $a
1826             a1 = (1-$a)
1827              
1828             =head2 hann($N)
1829              
1830             The Hann window. (Ref 1). One of the Blackman-Harris family, with coefficients
1831              
1832             a0 = 0.5
1833             a1 = 0.5
1834              
1835             Another name for this window is the hanning window. See also
1836             L.
1837              
1838             =head2 hann_matlab($N)
1839              
1840             The Hann (matlab) window. Equivalent to the Hann window of N+2 points, with the
1841             endpoints (which are both zero) removed. No periodic version of this window is
1842             defined. This window is defined by
1843              
1844             0.5 - 0.5 * arr
1845              
1846             where the points in arr are the cosine of points ranging from 2PI/($N+1)
1847             through 2PI*$N/($N+1). This routine gives the same result as the routine
1848             B in Matlab. See also L.
1849              
1850             =head2 hann_poisson($N,$alpha)
1851              
1852             The Hann-Poisson window. (Ref 1). This window is defined by
1853              
1854             0.5 * (1 + arr1) * exp (-$alpha * abs arr)
1855              
1856             where the points in arr range from -1 through 1, and arr1 are the cos of points
1857             ranging from -PI through PI.
1858              
1859             =head2 kaiser($N,$beta)
1860              
1861             The Kaiser window. (Ref 1). The parameter C<$beta> is the approximate
1862             half-width of the mainlobe, measured in frequency bins. Another name for this
1863             window is the Kaiser-Bessel window. This window is defined by
1864              
1865             $beta *= PI;
1866             my @n = gsl_sf_bessel_In($beta * sqrt(1 - arr ** 2), 0);
1867             my @d = gsl_sf_bessel_In($beta, 0);
1868             $n[0] / $d[0];
1869              
1870             where the points in arr range from -1 through 1.
1871              
1872             =head2 lanczos($N)
1873              
1874             The Lanczos window. Another name for this window is the sinc window. This
1875             window is defined by
1876              
1877             my $x = PI * arr;
1878             my $res = sin($x) / $x;
1879             my $mid;
1880             $mid = int($N / 2), $res->slice($mid) .= 1 if $N % 2;
1881             $res;
1882              
1883             where the points in arr range from -1 through 1.
1884              
1885             =head2 nuttall($N)
1886              
1887             The Nuttall window. One of the Blackman-Harris family, with coefficients
1888              
1889             a0 = 0.3635819
1890             a1 = 0.4891775
1891             a2 = 0.1365995
1892             a3 = 0.0106411
1893              
1894             See also L.
1895              
1896             =head2 nuttall1($N)
1897              
1898             The Nuttall (v1) window. A window referred to as the Nuttall window. One of the
1899             Blackman-Harris family, with coefficients
1900              
1901             a0 = 0.355768
1902             a1 = 0.487396
1903             a2 = 0.144232
1904             a3 = 0.012604
1905              
1906             This routine gives the same result as the routine B in Octave 3.6.2.
1907             See also L.
1908              
1909             =head2 parzen($N)
1910              
1911             The Parzen window. (Ref 1). Other names for this window are: Jackson,
1912             Valle-Poussin. This function disagrees with the Octave subroutine B,
1913             but agrees with Ref. 1. See also L.
1914              
1915             =head2 parzen_octave($N)
1916              
1917             The Parzen window. No periodic version of this window is defined. This routine
1918             gives the same result as the routine B in Octave 3.6.2. See also
1919             L.
1920              
1921             =head2 poisson($N,$alpha)
1922              
1923             The Poisson window. (Ref 1). This window is defined by
1924              
1925             exp(-$alpha * abs(arr))
1926              
1927             where the points in arr range from -1 through 1.
1928              
1929             =head2 rectangular($N)
1930              
1931             The Rectangular window. (Ref 1). Other names for this window are: dirichlet,
1932             boxcar.
1933              
1934             =head2 triangular($N)
1935              
1936             The Triangular window. This window is defined by
1937              
1938             1 - abs(arr)
1939              
1940             where the points in arr range from -$N/($N-1) through $N/($N-1).
1941             See also L.
1942              
1943             =head2 tukey($N,$alpha)
1944              
1945             The Tukey window. (Ref 1). Another name for this window is the tapered cosine
1946             window.
1947              
1948             =head2 welch($N)
1949              
1950             The Welch window. (Ref 1). Other names for this window are: Riez, Bochner,
1951             Parzen, parabolic. This window is defined by
1952              
1953             1 - arr**2
1954              
1955             where the points in arr range from -1 through 1.
1956              
1957             =head1 AUXILIARY SUBROUTINES
1958              
1959             These subroutines are used internally, but are also available for export.
1960              
1961             =head2 cos_mult_to_pow
1962              
1963             Convert Blackman-Harris coefficients. The BH windows are usually defined via
1964             coefficients for cosines of integer multiples of an argument. The same windows
1965             may be written instead as terms of powers of cosines of the same argument.
1966             These may be computed faster as they replace evaluation of cosines with
1967             multiplications. This subroutine is used internally to implement the
1968             Blackman-Harris family of windows more efficiently.
1969              
1970             This subroutine takes between 1 and 7 numeric arguments a0, a1, ...
1971              
1972             It converts the coefficients of this
1973              
1974             a0 - a1 cos(arg) + a2 cos(2 * arg) - a3 cos(3 * arg) + ...
1975              
1976             To the cofficients of this
1977              
1978             c0 + c1 cos(arg) + c2 cos(arg)**2 + c3 cos(arg)**3 + ...
1979              
1980             =head2 cos_pow_to_mult
1981              
1982             This function is the inverse of L.
1983              
1984             This subroutine takes between 1 and 7 numeric arguments c0, c1, ...
1985              
1986             It converts the coefficients of this
1987              
1988             c0 + c1 cos(arg) + c2 cos(arg)**2 + c3 cos(arg)**3 + ...
1989              
1990             To the cofficients of this
1991              
1992             a0 - a1 cos(arg) + a2 cos( 2 * arg) - a3 cos( 3 * arg) + ...
1993              
1994             =cut
1995              
1996             sub cos_pow_to_mult {
1997 9     9 1 12323 my @cin = @_;
1998 9 100       31 barf 'cos_pow_to_mult: number of args not less than 8.' if @cin > 7;
1999              
2000 8         14 my $ex = 7 - @cin;
2001              
2002 8         19 my @c = ( @cin, (0) x $ex );
2003              
2004 8         32 my @as = (
2005             10 * $c[6] + 12 * $c[4] + 16 * $c[2] + 32 * $c[0],
2006             20 * $c[5] + 24 * $c[3] + 32 * $c[1],
2007             15 * $c[6] + 16 * $c[4] + 16 * $c[2],
2008             10 * $c[5] + 8 * $c[3],
2009             6 * $c[6] + 4 * $c[4],
2010             2 * $c[5],
2011             $c[6],
2012             );
2013              
2014 8         28 pop @as for 1 .. $ex;
2015              
2016 8         14 my $sign = -1;
2017              
2018 8         13 foreach (@as) {
2019 28         44 $_ /= -$sign * 32;
2020 28         40 $sign *= -1;
2021             }
2022              
2023 8         43 @as;
2024             }
2025              
2026             =head2 chebpoly
2027              
2028             =for usage
2029              
2030             chebpoly($n,$x)
2031              
2032             =for ref
2033              
2034             Returns the value of the C<$n>-th order Chebyshev polynomial at point C<$x>.
2035             $n and $x may be scalar numbers, pdl's, or array refs. However, at least one
2036             of $n and $x must be a scalar number.
2037              
2038             All mixtures of pdls and scalars could be handled much more easily as a PP
2039             routine. But, at this point PDL::DSP::Windows is pure perl/pdl, requiring no
2040             C/Fortran compiler.
2041              
2042             =cut
2043              
2044             sub chebpoly {
2045 5 50   5 1 1197 barf 'chebpoly: Two arguments expected. Got ' .scalar(@_) . "\n" unless @_ == 2;
2046              
2047 5         13 my ( $n, $x ) = @_;
2048              
2049 5 100       16 if ( ref $x ) {
2050 4 50       13 barf "chebpoly: neither $n nor $x is a scalar number" if ref($n);
2051 4         15 $x = topdl($x);
2052              
2053 4         96 my $tn = zeroes($x);
2054              
2055 4         384 my ( $ind1, $ind2 ) = which_both( abs($x) <= 1 );
2056              
2057 4         495 $tn->index($ind1) .= cos( $n * acos( $x->index($ind1) ) );
2058 4         310 $tn->index($ind2) .= cosh( $n * acosh( $x->index($ind2) ) );
2059              
2060 4         112 return $tn;
2061             }
2062              
2063 1 50       5 $n = topdl($n) if ref $n;
2064 1 50       38 return abs($x) <= 1 ? cos( $n * acos($x) ) : cosh( $n * acosh($x) );
2065             }
2066              
2067              
2068             sub cos_mult_to_pow {
2069 9     9 1 1535 my( @ain ) = @_;
2070 9 100       39 barf('cos_mult_to_pow: number of args not less than 8.') if @ain > 7;
2071              
2072 8         15 my $ex = 7 - @ain;
2073              
2074 8         19 my @a = ( @ain, (0) x $ex );
2075              
2076 8         48 my (@cs) = (
2077             -$a[6] + $a[4] - $a[2] + $a[0],
2078             -5 * $a[5] + 3 * $a[3] - $a[1],
2079             18 * $a[6] - 8 * $a[4] + 2 * $a[2],
2080             20 * $a[5] - 4 * $a[3],
2081             8 * $a[4] - 48 * $a[6],
2082             -16 * $a[5],
2083             32 * $a[6]
2084             );
2085              
2086 8         25 pop @cs for 1 .. $ex;
2087              
2088 8         34 @cs;
2089             }
2090              
2091             1;
2092              
2093             __END__