Skip to content

Instantly share code, notes, and snippets.

@run4flat
Created October 12, 2012 22:02
Show Gist options
  • Save run4flat/3881822 to your computer and use it in GitHub Desktop.
Save run4flat/3881822 to your computer and use it in GitHub Desktop.
FFT Pulse Explorer
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;
@run4flat
Copy link
Author

Updated to use logarithmic scaling in y for the frequency plot

@run4flat
Copy link
Author

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