Index: /trunk/ippScripts/scripts/phase4_diff.pl
===================================================================
--- /trunk/ippScripts/scripts/phase4_diff.pl	(revision 11671)
+++ /trunk/ippScripts/scripts/phase4_diff.pl	(revision 11671)
@@ -0,0 +1,233 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+use Data::Dumper;
+
+use PS::IPP::Config qw(
+    $PS_EXIT_SUCCESS
+    $PS_EXIT_UNKNOWN_ERROR
+    $PS_EXIT_SYS_ERROR
+    $PS_EXIT_CONFIG_ERROR
+    $PS_EXIT_PROG_ERROR
+    $PS_EXIT_DATA_ERROR
+    $PS_EXIT_TIMEOUT_ERROR
+    );
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+use File::Spec;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($det_id, $iter, $exp_tag, $class_id, $det_type, $detrend,
+        $input_uri, $camera, $mode, $dbname, $workdir, $no_update);
+GetOptions(
+    'det_id|d=s'        => \$det_id,
+    'iteration=s'       => \$iter,
+    'exp_tag|e=s'       => \$exp_tag,
+    'class_id|i=s'      => \$class_id,
+    'det_type|t=s'      => \$det_type,
+    'detrend=s'         => \$detrend,
+    'input_uri|u=s'     => \$input_uri,
+    'camera|c=s'        => \$camera,
+    'mode|m=s'          => \$mode,
+    'dbname|d=s'        => \$dbname, # Database name
+    'workdir|w=s'       => \$workdir,	# Working directory, for output files
+    'no-update'         => \$no_update,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --det_id --iteration --exp_tag --class_id --det_type --camera --input_uri --mode --detrend (not for 'verify' mode)",
+    -exitval => 3,
+) unless defined $det_id
+    and defined $iter
+    and defined $exp_tag
+    and defined $class_id
+    and defined $det_type
+    and defined $input_uri
+    and defined $camera
+    and defined $mode
+    and (defined $detrend or lc($mode) eq 'verify');
+
+$ipprc->define_camera($camera);
+
+# Recipes to use, as a function of the detrend type
+use constant RECIPES => {
+    'master' => {		# We're creating a master --- already processed the input
+	'bias' => 'PPIMAGE_B',	# Bias only
+	'dark' => 'PPIMAGE_D',	# Dark only
+	'shutter' => 'PPIMAGE_S',	# Shutter only
+	'flat' => 'PPIMAGE_F',	# Flat-field only
+	'domeflat' => 'PPIMAGE_F',	# Flat-field only
+	'skyflat' => 'PPIMAGE_F',	# Flat-field only
+	'fringe' => 'PPIMAGE_R',	# Fringe only
+    },
+    'verify' => {		# We're checking the master --- input is not already processed
+	'bias' => 'PPIMAGE_OB',	# Bias only
+	'dark' => 'PPIMAGE_OBD',	# Dark only
+	'shutter' => 'PPIMAGE_OBDS',	# Shutter only
+	'flat' => 'PPIMAGE_OBDSF',	# Flat-field only
+	'domeflat' => 'PPIMAGE_OBDSF',	# Flat-field only
+	'skyflat' => 'PPIMAGE_OBDSF',	# Flat-field only
+	'fringe' => 'PPIMAGE_OBDSFR',	# Fringe only
+    },
+};
+
+# Flags to specify the particular detrend to use
+use constant DETRENDS => {
+    'bias' => '-bias',		# Specify the bias frame
+    'dark' => '-dark',		# Specify the dark frame
+    'shutter' => '-shutter',	# Specify the shutter frame
+    'flat' => '-flat',		# Specify the flat frame
+    'domeflat' => '-flat',		# Specify the flat frame
+    'skyflat' => '-flat',		# Specify the flat frame
+    'fringe' => '-fringe',	# Specify the fringe frame
+};
+
+
+use constant DELETE_STATS => 0;	# Delete the statistics file when done?
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $ppImage = can_run('ppImage') or (warn "Can't find ppImage" and $missing_tools = 1);
+if ($missing_tools) { 
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR); 
+}
+$ppImage .= " -dbname $dbname" if defined $dbname;
+
+# Recipe to use in processing
+&my_die("Unrecognised mode: $mode", $det_id, $iter, $exp_tag, $class_id, $PS_EXIT_PROG_ERROR) unless defined RECIPES->{$mode};
+my $recipe = RECIPES->{$mode}->{lc($det_type)};
+&my_die("Unrecognised detrend type: $det_type", $det_id, $iter, $exp_tag, $class_id, $PS_EXIT_PROG_ERROR) unless defined $recipe;
+
+### Output file names --- must match camera configuration!
+if (defined $workdir) {
+    $workdir = $ipprc->convert_filename_absolute( $workdir );
+} else {
+    my ($vol, $dir, $file) = File::Spec->splitpath( $input_uri );
+    $workdir = $dir;
+}
+
+my $outputFile = "$exp_tag.detresid.$det_id.$iter"; # Root name
+my $outputRoot = File::Spec->catfile( $workdir, $exp_tag, $outputFile );
+
+my $outputName = $ipprc->filename("PPIMAGE.OUTPUT", $outputRoot, $class_id);
+my $bin1Name =  $ipprc->filename("PPIMAGE.BIN1", $outputRoot, $class_id);
+my $bin2Name =  $ipprc->filename("PPIMAGE.BIN2", $outputRoot, $class_id);
+
+my $outputStats = $outputRoot . '.' . $class_id . '.stats';
+
+$detrend = $ipprc->convert_filename_absolute( $detrend ) if defined $detrend;
+$input_uri = $ipprc->convert_filename_absolute( $input_uri );
+
+# Run ppImage
+{
+    my $command = "$ppImage -file $input_uri $outputRoot -recipe PPIMAGE $recipe" .
+	" -stat $outputStats "; # Command to run ppImage
+
+    # Detrend to use in processing
+    if (lc($mode) ne 'verify') {
+	my $detFlag = DETRENDS->{lc($det_type)};
+	&my_die("Unrecognised detrend type: $det_type", $det_id, $iter, $exp_tag, $class_id, $PS_EXIT_PROG_ERROR) unless defined $detFlag;
+	$command .= "$detFlag $detrend";
+    }
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die("Unable to perform ppImage: $error_code", $det_id, $iter, $exp_tag, $class_id, $error_code);
+    }
+    &my_die("Couldn't find expected output file: $outputName", $det_id, $iter, $exp_tag, $class_id, $PS_EXIT_SYS_ERROR) unless -f $outputName;
+    &my_die("Couldn't find expected output file: $outputStats", $det_id, $iter, $exp_tag, $class_id, $PS_EXIT_SYS_ERROR) unless -f $outputStats;
+    &my_die("Couldn't find expected output file: $bin1Name", $det_id, $iter, $exp_tag, $class_id, $PS_EXIT_SYS_ERROR) unless -f $bin1Name;
+    &my_die("Couldn't find expected output file: $bin2Name", $det_id, $iter, $exp_tag, $class_id, $PS_EXIT_SYS_ERROR) unless -f $bin2Name;
+}
+
+# Get the statistics on the residual image
+my $stats;			# Statistics from ppImage
+{
+    my $statsFile;		# File handle
+    open $statsFile, $outputStats or &my_die("Can't open statistics file $outputStats: $!", $det_id, $iter, $exp_tag, $class_id, $PS_EXIT_SYS_ERROR);
+    my @contents = <$statsFile>; # Contents of file
+    close $statsFile;
+    my $mdcParser = PS::IPP::Metadata::Config->new;	# Parser for metadata config files
+    my $metadata = $mdcParser->parse(join "", @contents) or
+	&my_die("Unable to parse metadata config doc", $det_id, $iter, $exp_tag, $class_id, $PS_EXIT_PROG_ERROR);
+    $stats = PS::IPP::Metadata::Stats->new(); # Stats parser
+    $stats->parse($metadata) or &my_die("Unable to find all values in statistics output.", $det_id, $iter, $exp_tag, $class_id, $PS_EXIT_PROG_ERROR);
+}
+
+# Add the processed file to the database
+$outputName = $ipprc->convert_filename_relative( $outputName );
+$bin1Name = $ipprc->convert_filename_relative( $bin1Name );
+$bin2Name = $ipprc->convert_filename_relative( $bin2Name );
+unless ($no_update) {
+    my $command = "$dettool -addresidimfile -det_id $det_id -iteration $iter -exp_tag $exp_tag " .
+	"-class_id $class_id -recip $recipe -uri $outputName -b1_uri $outputRoot"; # Command to run dettool
+    $command .= " -bg " . $stats->bg_mean();
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    if (defined($stats->bg_mean_stdev())) {
+	$command .= " -bg_mean_stdev " . $stats->bg_mean_stdev();
+    } else {
+	# May be undefined if there is only a single imfile
+	$command .= ' -bg_stdev 0';
+    }
+
+    $command .= " -bg_stdev " . $stats->bg_stdev();
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	warn("Unable to perform dettool -addresidimfile: $error_code\n");
+	exit($error_code);
+    }
+
+    unlink $outputStats;    
+}
+
+
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $det_id = shift;		# Detrend identifier
+    my $iter = shift;		# Iteration
+    my $exp_tag = shift; # Exposure tag
+    my $class_id = shift; # Class identifier
+    my $exit_code = shift; # Exit code to add
+
+    warn($msg);
+    if ($det_id and $iter and $exp_tag) {
+	my $command = "$dettool -addresidimfile -det_id $det_id -iteration $iter -exp_tag $exp_tag -class_id $class_id -code $exit_code";
+	$command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $exit = $?;
+    system("sync") == 0 or die "failed to execute sync: $!";
+    $? = $exit;
+}
+
+__END__
Index: /trunk/ippScripts/scripts/phase4_stack.pl
===================================================================
--- /trunk/ippScripts/scripts/phase4_stack.pl	(revision 11671)
+++ /trunk/ippScripts/scripts/phase4_stack.pl	(revision 11671)
@@ -0,0 +1,201 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Metadata::Stats;
+
+use PS::IPP::Config qw(
+    $PS_EXIT_SUCCESS
+    $PS_EXIT_UNKNOWN_ERROR
+    $PS_EXIT_SYS_ERROR
+    $PS_EXIT_CONFIG_ERROR
+    $PS_EXIT_PROG_ERROR
+    $PS_EXIT_DATA_ERROR
+    $PS_EXIT_TIMEOUT_ERROR
+    );
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+use File::Spec;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($p4_id, $class_id, $det_type, $camera, $dbname, $workdir, $no_update);
+GetOptions(
+    'det_id|d=s'        => \$det_id,
+    'iteration=s'       => \$iter,
+    'class_id|i=s'      => \$class_id,
+    'det_type|t=s'      => \$det_type,
+    'camera|c=s'        => \$camera,
+    'dbname|d=s'        => \$dbname, # Database name
+    'workdir|w=s'       => \$workdir,	# Working directory, for output files
+    'no-update'         => \$no_update
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --det_id --iteration --class_id --det_type --camera",
+    -exitval => 3,
+) unless defined $det_id
+    and defined $iter
+    and defined $class_id
+    and defined $det_type
+    and defined $camera;
+
+# Recipes to use as a function of detrend type
+use constant RECIPES => {
+    'bias' => 'PPMERGE_BIAS',
+    'dark' => 'PPMERGE_DARK',
+    'shutter' => 'PPMERGE_SHUTTER',
+    'flat' => 'PPMERGE_FLAT',
+    'domeflat' => 'PPMERGE_FLAT',
+    'skyflat' => 'PPMERGE_FLAT',
+    'fringe' => 'PPMERGE_FRINGE',
+    };
+
+# Look for programs we need
+my $missing_tools;
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
+my $ppMerge = can_run('ppMerge') or (warn "Can't find ppMerge" and $missing_tools = 1);
+if ($missing_tools) { 
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR); 
+}
+
+my $recipe = RECIPES()->{lc($det_type)}; # Recipe to use in stacking
+&my_die("Unrecognised detrend type: $det_type", $det_id, $iter, $class_id, $PS_EXIT_PROG_ERROR) unless defined $recipe;
+
+my $mdcParser = PS::IPP::Metadata::Config->new;	# Parser for metadata config files
+
+# Get list of files to stack
+my $files;			# Array of files to be stacked
+{
+    my $command = "$dettool -processedimfile -det_id $det_id -class_id $class_id -included"; # Command to run
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die("Unable to perform dettool -processedimfile: $error_code", $det_id, $iter, $class_id, $error_code);
+    }
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+	&my_die("Unable to parse metadata config doc", $det_id, $iter, $class_id, $PS_EXIT_PROG_ERROR);
+    $files = parse_md_list($metadata) or 
+	&my_die("Unable to parse metadata list", $det_id, $iter, $class_id, $PS_EXIT_PROG_ERROR);
+}
+
+if (defined $workdir) {
+    $workdir = $ipprc->convert_filename_absolute( $workdir );
+} else {
+    my $example = ${$files}[0]->{uri}; # Example file
+    my ($vol, $dir, $file) = File::Spec->splitpath( $example );
+    $workdir = $dir;
+}
+
+# Stack the files
+my $outputFile = "$camera.$det_type.$det_id.$iter.$class_id"; # Root name
+my $outputSubD = "$camera.$det_type.$det_id";
+my $outputRoot = File::Spec->catfile( $workdir, $outputSubD, $outputFile );
+my $outputStack = $outputRoot . '.fits'; # Output name
+my $outputStats = $outputRoot . '.stats'; # Statistics name
+
+{
+    my $command = "$ppMerge $outputStack"; # Command to run
+    foreach my $file (@$files) {
+	my $uri = $file->{uri};	# URI for input file
+	$uri = $ipprc->convert_filename_absolute($uri);
+	$command .= ' ' . $uri;
+    }
+    $command .= " -recipe PPMERGE $recipe";
+    $command .= ' -type ' . uc($det_type); # Type of stacking to perform
+    $command .= " -stats $outputStats";	# Statistics output filename
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die("Unable to perform ppMerge: $error_code", $det_id, $iter, $class_id, $error_code);
+    }
+    &my_die("Unable to find expected output file: $outputStack\n", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless -f $outputStack;
+    &my_die("Unable to find expected output file: $outputStats\n", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR) unless -f $outputStats;
+}
+
+# Get the statistics on the stacked image
+my $stats;			# Statistics from ppMerge
+{
+    open(my $statsFile, "$outputStats") or
+	&my_die("Can't open statistics file $outputStats: $!", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR);
+    my $contents = do { local $/; <$statsFile> }; # Contents of file
+    close($statsFile);
+
+    my $mdcParser = PS::IPP::Metadata::Config->new;	# Parser for metadata config files
+    my $metadata = $mdcParser->parse($contents) or
+	&my_die("Unable to parse metadata config doc", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR);
+    $stats = PS::IPP::Metadata::Stats->new(); # Stats parser
+    $stats->parse($metadata)  or
+	&my_die("Unable to find all values in statistics output.", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR);
+}
+
+# Add the resultant into the database
+$outputStack = $ipprc->convert_filename_relative($outputStack);
+unless ($no_update) {
+    my $command = "$dettool -addstacked -det_id $det_id -iteration $iter -class_id $class_id" .
+	" -uri $outputStack -recip $recipe"; # Command to run
+    $command .= ' -bg ' . $stats->bg_mean();
+    if (defined($stats->bg_stdev())) {
+    $command .= ' -bg_stdev ' . $stats->bg_stdev();
+    } else {
+	# May be undefined if there is only a single imfile
+	$command .= ' -bg_stdev 0';
+    }
+    $command .= ' -bg_mean_stdev ' . $stats->bg_mean_stdev();
+    $command .= " -dbname $dbname" if defined $dbname;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	warn("Unable to perform dettool -addstacked: $error_code\n");
+	exit($error_code);
+    }
+
+    unlink $outputStats;
+}
+
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $det_id = shift;		# Detrend identifier
+    my $iter = shift;		# Iteration
+    my $class_id = shift; # Class identifier
+    my $exit_code = shift; # Exit code to add
+
+    warn($msg);
+    if ($det_id and $iter and $class_id) {
+	my $command = "$dettool -addstacked -det_id $det_id -iteration $iter -class_id $class_id -code $exit_code";
+	$command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: /trunk/ippScripts/scripts/phase4_warp.pl
===================================================================
--- /trunk/ippScripts/scripts/phase4_warp.pl	(revision 11671)
+++ /trunk/ippScripts/scripts/phase4_warp.pl	(revision 11671)
@@ -0,0 +1,165 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::Stats;
+
+use PS::IPP::Config qw(
+    $PS_EXIT_SUCCESS
+    $PS_EXIT_UNKNOWN_ERROR
+    $PS_EXIT_SYS_ERROR
+    $PS_EXIT_CONFIG_ERROR
+    $PS_EXIT_PROG_ERROR
+    $PS_EXIT_DATA_ERROR
+    $PS_EXIT_TIMEOUT_ERROR
+    );
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+use File::Spec;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ($det_id, $exp_tag, $class_id, $p3_version, $uri, $camera, $dbname, $workdir, $no_update);
+GetOptions(
+    'p4_id|i=s'         => \$p4_id, # Phase 4 identifier
+    'exp_tag|e=s'       => \$exp_tag, # Exposure tag
+    'class_id|i=s'      => \$class_id, # Class identifier
+    'p3_version|i=s'    => \$p3_version, # Phaser 3 version
+    'uri|u=s'           => \$uri, # URI for input file
+    'camera|c=s'        => \$camera, # Camera name
+    'dbname|d=s'        => \$dbname, # Database name
+    'workdir|w=s'       => \$workdir, # Working directory, for output files
+    'no-update'         => \$no_update
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --p4_id --exp_tag --class_id --p3_version --uri --camera",
+    -exitval => 3,
+) unless defined $p4_id
+    and defined $exp_tag
+    and defined $class_id
+    and defined $p3_version
+    and defined $uri
+    and defined $camera;
+
+# XXX this exits with status = 0 on failure
+$ipprc->define_camera($camera);
+
+# Look for programs we need
+my $missing_tools;
+my $p4tool = can_run('p4tool') or (warn "Can't find p4tool" and $missing_tools = 1);
+my $pswarp = can_run('pswarp') or (warn "Can't find pswarp" and $missing_tools = 1);
+if ($missing_tools) { 
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR); 
+}
+
+### Output file name
+if (defined $workdir) {
+    $workdir = $ipprc->convert_filename_absolute( $workdir );
+} else {
+    my ($vol, $dir, $file) = File::Spec->splitpath( $uri );
+    $workdir = $dir;
+}
+my $outputFile = "$exp_tag.warp.$det_id"; # Root name
+my $outputRoot = File::Spec->catfile( $workdir, $exp_tag, $outputFile );
+my $outputImage = $ipprc->filename("PSWARP.OUTPUT", $outputRoot, $class_id);
+my $outputBin1 = $ipprc->filename("PSWARP.BIN1", $outputRoot, $class_id);
+my $outputBin2 = $ipprc->filename("PSWARP.BIN2", $outputRoot, $class_id);
+
+my $outputStats = $outputRoot . '.' . $class_id . '.stats';
+
+$uri = $ipprc->convert_filename_absolute( $uri );
+
+# Run pswarp
+{
+    my $command = "$pswarp -file $uri $outputRoot -stat $outputStats"; # Command to run pswarp
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die("Unable to perform pswarp: $error_code", $p4_id, $exp_tag, $class_id, $p3_version, $error_code);
+    }
+    &my_die("Couldn't find expected output file: $outputImage", $p4_id, $exp_tag, $class_id, $p3_version, $PS_EXIT_SYS_ERROR) unless -f $outputImage;
+    &my_die("Couldn't find expected output file: $outputStats", $p4_id, $exp_tag, $class_id, $p3_version, $PS_EXIT_SYS_ERROR) unless -f $outputStats;
+    &my_die("Couldn't find expected output file: $outputBin1", $p4_id, $exp_tag, $class_id, $p3_version, $PS_EXIT_SYS_ERROR) unless -f $outputBin1;
+    &my_die("Couldn't find expected output file: $outputBin2", $p4_id, $exp_tag, $class_id, $p3_version, $PS_EXIT_SYS_ERROR) unless -f $outputBin2;
+}
+
+# Get the statistics on the warped image
+my $stats;			# Statistics from pswarp
+{
+    my $statsFile;		# File handle
+    open $statsFile, "$outputStats" or die "Can't open statistics file $outputStats: $!\n";
+    my @contents = <$statsFile>; # Contents of file
+    close $statsFile;
+    my $mdcParser = PS::IPP::Metadata::Config->new;	# Parser for metadata config files
+    my $metadata = $mdcParser->parse(join "", @contents)
+        or &my_die("Unable to parse metadata config", $p4_id, $exp_tag, $class_id, $p3_version, $PS_EXIT_PROG_ERROR);
+    $stats = PS::IPP::Metadata::Stats->new(); # Stats parser
+    $stats->parse($metadata) or &my_die("Unable to find all values in statistics output.", $p4_id, $exp_tag, $class_id, $p3_version, $PS_EXIT_PROG_ERROR);
+}
+
+# Take off the absolute path, to stuff into the database
+$outputImage = $ipprc->convert_filename_relative( $outputImage );
+$outputBin1  = $ipprc->convert_filename_relative( $outputBin1  );
+$outputBin2  = $ipprc->convert_filename_relative( $outputBin2  );
+
+# Add the processed file to the database
+unless ($no_update) {
+    my $command = "$p4tool -addwarpedimfile -p4_id $p4_id -exp_tag $exp_tag " .
+	"-class_id $class_id -p3_version $p3_version -uri $outputImage -b1_uri $outputRoot";  # Command to run dettool
+    $command .= " -bg " . $stats->bg_mean();
+    $command .= " -bg_mean_stdev " . $stats->bg_mean_stdev();
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => 1);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	warn("Unable to perform p4tool -addwarpedimfile: $error_code\n");
+	exit($error_code);
+    }
+
+    unlink $outputStats;
+}
+
+sub my_die
+{
+    my $msg = shift;		# Warning message on die
+    my $p4_id = shift;		# Phase 4 identifier
+    my $exp_tag = shift;	# Exposure tag
+    my $class_id = shift;	# Class identifier
+    my $p3_version = shift;	# Phase 3 version
+    my $exit_code = shift;	# Exit code to add
+
+    warn($msg);
+    if ($det_id and $exp_tag and $class_id) {
+	my $command = "$p4tool -addwarpedimfile -p4_id $p4_id -exp_tag $exp_tag -class_id $class_id -p3_version $p3_version -code $exit_code";
+	$command .= " -dbname $dbname" if defined $dbname;
+        system ($command);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
