Created
October 12, 2012 22:02
-
-
Save run4flat/3881822 to your computer and use it in GitHub Desktop.
FFT Pulse Explorer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use strict; | |
| use warnings; | |
| use PDL; | |
| use PDL::Graphics::Prima; | |
| use Prima qw(Application InputLine IniFile); | |
| use PDL::FFT; | |
| my $wDisplay = Prima::MainWindow->create( | |
| text => 'FFT Envelope analysis', | |
| size => [800, 800], | |
| ); | |
| ####################### | |
| # input value caching # | |
| ####################### | |
| my $ini = Prima::IniFile->create (file => 'envelope-params.ini', default => { | |
| inputs => { | |
| input => '-30, 30, 0.01, 60, 15', | |
| } | |
| }); | |
| END { | |
| # Ensure the values are saved to disk before quitting | |
| $ini->write; | |
| } | |
| # This is a reference to a tied hash that updates $ini for me. | |
| my $ini_section = $ini->section('inputs'); | |
| ######### | |
| # Input # | |
| ######### | |
| $wDisplay->insert(Label => | |
| text => 'Min (s), Max (s), dt (s) Frequency (Hz), Sigma (s)', | |
| pack => { anchor => 'nw', side => 'top' }, | |
| ); | |
| my $input_cache = $ini_section->{input}; | |
| my $input_line = $wDisplay->insert(InputLine => | |
| text => $ini_section->{input}, | |
| pack => { anchor => 'nw', side => 'top', fill => 'x' }, | |
| onKeyUp => sub { | |
| my $self = shift; | |
| # Don't do anything unless the text actually changed | |
| return if $self->text eq $input_cache; | |
| my $text = $input_cache = $self->text; | |
| # Split on commas/spaces | |
| my @inputs = split /\s*,\s*/, $text; | |
| # Verify the input | |
| eval { | |
| # five inputs | |
| return 0 if @inputs != 5; | |
| # all floating-points | |
| for my $input (@inputs) { | |
| return 0 if $input !~ /^-?\d+\.?\d*$/; | |
| } | |
| # last three must be positive | |
| for my $input (@inputs[2,3,4]) { | |
| return 0 if $input <= 0; | |
| } | |
| # If we've reached here, we're good to move forward | |
| $self->backColor(cl::White); | |
| $ini_section->{input} = $text; | |
| update_plots(); | |
| } or do { | |
| # Highlight the error: | |
| $self->backColor(cl::Yellow); | |
| }; | |
| }, | |
| ); | |
| ######### | |
| # Plots # | |
| ######### | |
| my $plot_container = $wDisplay->insert(Widget => | |
| pack => {side => 'top', fill => 'both', expand => 1 }, | |
| ); | |
| my $time_series = $plot_container->insert(Plot => | |
| place => { x => 0, relwidth => 1, relheight => 0.5, rely => 0.5, | |
| anchor => 'sw'}, | |
| x => { label => 'Time (s)' }, | |
| y => { label => 'Amplitude' }, | |
| ); | |
| my $freq_plot = $plot_container->insert(Plot => | |
| place => { x => 0, relwidth => 1, relheight => 0.5, rely => 0, | |
| anchor => 'sw'}, | |
| x => { label => 'Frequency (s)' }, | |
| y => { label => 'Power Spectrum', scaling => sc::Log }, | |
| ); | |
| ######################## | |
| # Update Plot Function # | |
| ######################## | |
| use PDL::Constants qw(PI); | |
| sub update_plots { | |
| # This is only called when we know we can trust the input. | |
| my ($min_t, $max_t, $dt, $pulse_freq, $sigma) | |
| = split /\s*,\s*/, $ini_section->{input}; | |
| my $N_times = ($max_t - $min_t) / $dt; | |
| # Make sure it's even | |
| $N_times++ if $N_times % 2 == 1; | |
| my $times = zeroes($N_times)->xlinvals($min_t, $max_t); | |
| my $heights = exp(-($times/$sigma)**2) * sin(2 * PI * $pulse_freq * $times); | |
| $time_series->dataSets->{data} = ds::Pair( | |
| $times, $heights, | |
| plotType => ppair::Lines, | |
| ); | |
| # Compute the fft of the pulse | |
| # Remember, N * df * dt = 1. | |
| my $df = 1 / $dt / $N_times; | |
| my $amp = $heights->copy; | |
| $amp->realfft; | |
| # Compute the power spectrum | |
| use PDL::NiceSlice; | |
| $amp = sqrt($amp(0:$N_times/2-1)**2 + $amp($N_times/2:-1)**2); | |
| no PDL::NiceSlice; | |
| my $freq = $amp->sequence * $df; | |
| $freq_plot->dataSets->{data} = ds::Pair( | |
| $freq, $amp, | |
| plotType => ppair::Lines | |
| ); | |
| } | |
| # call the notification to plot everything the first time: | |
| use Prima::Utils qw(post); | |
| post(\&update_plots); | |
| # Run it! | |
| run Prima; | |
Update to supply a better default dt.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Updated to use logarithmic scaling in y for the frequency plot