#!/usr/bin/perl
use POSIX qw(ceil);

#  Copyright (C) 2012 Brian Standley <brian@brianstandley.com>
#
#  This program is free software: you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation, either version 3 of the License, or (at your
#  option) any later version.
#
#  This program is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
#  Public License for more details.
#
#  You should have received a copy of the GNU General Public License along
#  with this program. If not, see <http://www.gnu.org/licenses/>.

# awcgen v1.1

# Read filenames (required) and flags (optional).

$infilename  = shift @ARGV;
$outfilename = shift @ARGV;
$flags       = shift @ARGV;

$test_mode = ($flags =~ /t/);
$N_request = ($flags =~ /m/) ? 16000 : 1600;
$gain      = ($flags =~ /f/) ? 1.0   : 0.5;

# Print usage.

if ($infilename eq "--help" || $infilename eq "-h")
{
	print "\nUsage: awcgen [input file] [output file] [flags]\n\n";
	print "  Flags:  t  \'Test mode\'    Produce two-column output for easy plotting.\n";
	print "          m  \'More points\'  Attempt to use ~16000 points instead of ~1600.\n";
	print "          f  \'Full scale\'   Produce a full-size waveform when driving a 50 Ohm load.\n\n";
	exit 0;
}

# Set up file IO.

!($infilename =~ $outfilename)  || die "Input and output files must be different.\n";
open(INFILE,  "< $infilename")  || die "Cannot open file: $infilename.\n";
open(OUTFILE, "> $outfilename") || die "Cannot open file: $outfilename.\n";

# Calculate frequency and number of points based on the total waveform time.

sub ReadLine
{
	my $line  = shift(@_);

	if    ($line =~ /#/)         { return (0, 0, 1);    }
	elsif ($line =~ /(.+) (.+)/) { return ($1, $2, 0);  }
	else                         { die "Bad syntax.\n"; }
}

$t_total = 0.0;
while ($line = <INFILE>)
{
	($dt, $v, $comment) = ReadLine($line);
	if (!$comment) { $t_total += $dt; }
}

sub IsUgly
{
	my ($num, $decimal_places) = @_;

	sprintf("%e", $num) =~ /(.+)e(.+)/;
	my $scaled_mantissa  = $1 * (10.0**$decimal_places);
	return ceil($scaled_mantissa) - $scaled_mantissa > 1e-9;
}

$freq_request = $N_request/$t_total;
$div_request = 40e6/$freq_request;
$div_max = 2**34 - 1;
for ($div = ceil($div_request); IsUgly(40e6/$div, 2); $div++) { if ($div > $div_max) { die "Pulse too long to handle. Try the 'm' option if you aren't already.\n"; } }
$freq = 40e6/$div;
$N = int($t_total*$freq + 0.5) + 1;  # round to nearest integer then add one (I forget why)

# Print configuration.

printf "\n             Request       Actual\n";
printf "             ------------  ------------\n";
printf "Divider:     %-12.3f  %1.0f\n", $div_request,  $div;
printf "Frequency:   %-12.3e  %1.3e\n", $freq_request, $freq;
printf "N_points:    %-12d  %d\n\n",    $N_request,    $N;
printf "Total time:  %1.3e\n",          $t_total;
printf "Output:      %s-commpatible\n", $test_mode ? "Plot":"AWC";

if ($N < 8) { die "Pulse too short to handle.\n"; }

# Write header.

if (!$test_mode)
{
	printf OUTFILE "%d\n", $N;
	printf OUTFILE "%0.6e\n", $freq;
	printf OUTFILE "1\n";
	printf OUTFILE "1000.000000\n";
}

# Write points.

sub PrintPoint
{
	my ($t, $v) = @_;

	if ($test_mode) { printf OUTFILE "%1.6e\t%0.6f\n", $t, $v; }
	else            { printf OUTFILE "%0.6f\n", $v;            }
}

($t, $v_x) = (0.0, 0.0);
PrintPoint($t, $v_x);

seek(INFILE, 0, 0);  # set file pointer back to start
while ($line = <INFILE>)
{
	($dt, $v_f, $comment) = ReadLine($line);

	if (!$comment)
	{
		$v_f *= $gain;

		if ($dt < 1e-9) { $v_x = $v_f; }
		else
		{
			$slope = ($v_f - $v_x)/$dt;
			for ($i = 0; $i < $dt*$freq; $i++)
			{
				$t += 1.0/$freq;
				$v_x += $slope/$freq;
				PrintPoint($t, $v_x);
			}
		}
	}
}

close(INFILE);
close(OUTFILE);

exit 0;
