#!/usr/bin/perl
#use Cwd;
use bignum ( p => -1 );
use File::Copy;
use Getopt::Long;
use Inline C;

### config ###

## defaults
my $min = 52;
#$min = $ARGV[0] if $ARGV[0];
my $max = 1108;
#$max = $ARGV[1] if $ARGV[1];
my $start = 0;

my $spacing = 1.2; # 1.2 Mhz interval between graphs, 2.4 Mhz bandwidth, 1/2 overlap.
my $samplerate = 2400000;
my $gain = 30;

my @freqs;
my $freqlist; # E4000 "52-1108,1248-2200"

# $scriptdir is where graphfreq.py script is. A copy of pyrtlsdr is also there,
# but you could just have it in your path.
#my $scriptdir = '/home/superkuh/app_installs/gnuradio/pyrtlsdr/aug-2012/pyrtlsdr_mod';
my $scriptdir = '.'; # pwd
#my $scriptdir = getcwd();
#my $script = "python $scriptdir/graphfreqs_offset.py";
my $script = "python $scriptdir/graphfreqs.py";

# Use a known LTE Cell station to get frequency error for logs.
my $enableLTECellScanner = 0;
my $freqoffset_passed;

## webpage index autogeneration and upload (via rsync)
my $enablewebpagegeneration = 0; # use -w or set to 1 to enable.
my $webdir = '/home/superkuh/www/gnuradio/live';
# set $rsync to 0 to disable.
my $rsync = 'rsync -avi -e "ssh" --bwlimit=200 /home/you/www/gnuradio/live/ you@yourserver.com:/home/you/www/gnuradio/live/';
my $htmlfile = "$webdir/index.html";

## cli arguments
GetOptions(
	'f1:s' => \$min, #minimum frequency
	'f2:s' => \$max, #max
	'flist:s' => \$flist, #a list of frequency pairs like, --flist "52-1108,1248-2200"
	'g:i' => \$gain, 
	's:s' => \$spacing, # distance between center frequencies
	'r:s' => \$samplerate,
	'd1:s' => \$scriptdir, # where graphfreqs.py is
	'd2:s' => \$webdir, # where to put the images and logs
	'c:s' => \$enableLTECellScanner,  # use frequency offset correction
	'w' => \$enablewebpagegeneration, # generate an html webpage
	'p' => \$gnuplotenabled, # generate gnuplot timeseries charts too
	'start:s' => \$start, # not implemented yet
	'm' => \$spectralmapenabled, # make an all frequency spectral map
	'mr:s' => \$ranges # takes comma seperated list of ranges for "52-1108,1248-2200" spectral maps.
);

### end config ###

if ($flist) {
	print "Running graphfreq batch jobs $flist Mhz at $spacing Mhz spacings.\n";
} else {
	print "Running graphfreq batch job $min to $max Mhz at $spacing Mhz spacings.\n";
}

if ($enableLTECellScanner) {
	my $ltefreq = $enableLTECellScanner;
	$freqoffset = getfreqoffset($ltefreq);
	$freqoffset =~ /(-?\d+\.?\d?)\w/;
	{
    	no bignum;
		$freqoffset_passed = $1; # remove Kilo
		$freqoffset_passed *= '1000'; # add Kilo
		$ltefreq = $enableLTECellScanner * 1000000;
	#	print "\n$freqoffset_passed / $ltefreq";
		$freqoffset_passed = (($freqoffset_passed/($ltefreq))*1000000) # hertz to ppm
	}
	$freqoffset_passed = int($freqoffset_passed);
}
sub getfreqoffset {
	my ($freq) = @_;
	print "Using LTE Cell Scanner to find frequency offset from $freq Mhz station...";
	$freq .= "e6";
	my $command = "CellSearch -v -s $freq -e $freq";
	my $info = `$command`;
	$info =~ /CrystalCorrectionFactor\n\d+\s+\w+\s+(-?\d+\.?\d?\w)/;
	my $freqoffset = $1;
	return $freqoffset;
}
print "$freqoffset frequency offset. Correcting $freqoffset_passed PPM.\n" if $enableLTECellScanner;

# if all.log exists in $webdir, use it with gnuplot to generate a spectral map first.
spectralmap() if $spectralmapenabled;
# generate combined logs for arbitrary spectral maps, then plot them. File names are ~ spectral-map_$ranges.png
arbitraryspectralmap($ranges) if $ranges;
print "\n";


if ($flist) {
#	frequencylist($flist);
	@freqs = frequencylist($flist);
} else {
	for ($min; $min < $max; $min += $spacing) {
		$rounded = sprintf("%.0f", $min*1000000);
		push(@freqs,$rounded); # @freqs is global
	}

}
sub frequencylist {
	my $list = shift;
	my @pairs = split(",",$list);
	my @temp;

	foreach my $range (@pairs) {
		my ($min1,$max1) = split('-',$range);
		for ($min1; $min1 < $max1; $min1 += $spacing) {
			$rounded = sprintf("%.0f", $min1*1000000);
			push(@temp,$rounded); # @freqs is global
		}
		
	}
	return @temp;
}
sub frequencylist_old {
	my $list = shift;
	my @pairs = split(",",$list);
	foreach my $range (@pairs) {
		my ($min1,$max1) = split('-',$range);
		for ($min1; $min1 < $max1; $min1 += $spacing) {
			$rounded = sprintf("%.0f", $min1*1000000);
			push(@freqs,$rounded); # @freqs is global
		}
		
	}

}
foreach $freq (@freqs) {
#		if ($start) {
#			if ($freq < $start) {
#				next;
#			} else {
#				$start = 0;
#			}
#		}
		my $command = "$script $freq $samplerate $gain $freqoffset_passed $webdir";
		print "$command\n";
#		`$command`; # use eval wrapped timer instead to prevent freezing due to children.
		eval {
			local $SIG{ALRM} = sub {die "alarm\n"};
			alarm 10;
 			$test = `$command`;
 			alarm 0;
		};
		if ($@) {
			die unless $@ eq "alarm\n";
			print "Dongle froze, reseting it's USB device...\n";
			donglefrozen();
		}
		gnuplot($freq) if $gnuplotenabled;
}

webpagegeneration($enablewebpagegeneration);
exit;

sub webpagegeneration {
	exit unless $enablewebpagegeneration;
	print "Generating page, moving images.\n";
	for my $file ( <$scriptdir/*.png> ) {
		move($file, "$webdir" ) or warn "Cannot copy $file: $!";
	}

	open (my $html, ">", "$htmlfile") or die "Can't open $htmlfile to write.\n$!\n";
	print $html "<html><head><title>rtlsdr dongle frequency strength logger, 52-1000 Mhz</title></head><body>\n";
	print $html "<img src=\"spectral-map.png\" /></a>\n";

	my @files = <$webdir/*.png>;
	my @sortedfiles;
	foreach $file (@files) {
		$file =~ s#/home/superkuh/www/gnuradio/live/##;
		next if ($file =~ /spectral/); # don't show spectral map here.
		push(@sortedfiles,$file);
	}

	foreach $file (sort { $a <=> $b } @sortedfiles) { 
	#	print "html: $file\n";
	#	$file =~ /live\/(.*)/;
	#	$file = $1;
		my $htmlfreq;
		next if ($file =~ /graph/); # don't show time series data directly. Link it.
		next if ($file =~ /spectral/); # don't show spectral map here.
		$file =~ /(.*)\.png/;
		$htmlname = $1;
	 	$htmlfreq = ($htmlname/1000000);
		if ($gnuplotenabled) {
			print $html "<p><a href=\"graph_$htmlname.png\"><img src=\"$file\" /></a></p>\n";
		} else {
			print $html "<p><a href=\"$htmlname.png\"><img src=\"$file\" /></a></p>\n";
		}

	}
	print $html "</body></html>";
	close $html;
	
	print "\nstarting rsync...\n";
	`$rsync` if $rsync;

# I should put a function in here to parse the http logs for specific URLs encoding frequency scan requests.
# Add them to @freqs at the end if they occured within $time seconds. That way users can generate output for
# arbitrary frequencies. But, how do I differentiate them when making the index.html so they don't show up?

}

sub gnuplot {

	# it'd be nice if log filesize was checked before running to avoid errors when
	# the first run through a new set of frequencies is made. Like, if -f $frequency.log
	# < 1kB, next;

	my ($frequency) = @_;
	my $timestart;
	my $timeend;
	my @temp;

#	print "DEBUG: $webdir/$frequency\n";

	open(INPUT, "$webdir/$frequency.log") or die "A horrible death.\n$!";
	while (<INPUT>) {
	#	print;
		push @temp, $_;
	}
	close INPUT;
	# Get start and stop times
	$temp[0] =~ /(\d+.\d+?)/;
	$timestart = $1;
	$temp[-1] =~ /(\d+.\d+?)/;
	$timeend = $1;


my $command = "set xdata time\n
set timefmt \"\%s\"\n
set format x \"\%j\"\n
set xlabel \"time (day of year)\"\n
set ylabel \"arbitrary power (dB)\"\n
set key top left\n
set key box\n
set xrange [$timestart-946684800:$timeend-946684800]\n
set datafile separator \",\"\n
set xtics 86400\n
set term png size 1000, 600\n
set output \"$webdir/graph_$frequency.png\"\n
plot \'$webdir/$frequency.log\' using 1:2 with linespoints\n";

#	`echo $command | gnuplot`;
	open GP, '| gnuplot';
	print GP $command;
	close GP;

}

sub spectralmap {

print "Generating spectral map.\n";

my $command = "set timefmt \"\%s\"\n
set view map\n
set terminal png crop\n
set output '$webdir/spectral-map.png'\n
set term png size 1200, 800\n
set key off\n
set title \"$flist Mhz\"\n
set xlabel \'Time\'\n
set ylabel \'Frequency (Hz)\'\n
set cblabel \'Intensity (dB)\'\n
set xdata time\n
set format x \"\%m/\%d\"\n
set ytics 100000000\n
plot \'$webdir/all.log\' using 1:2:3  with points ps 0.5 pt 5 palette\n
replot\n
exit\n";

open GP, '| gnuplot';
print GP $command;
close GP;

}

###############################
sub arbitraryspectralmap {
	my $listofranges = shift;
	my @lines;
	my @files = getfilesinrange($listofranges);
	my $outputlog = "$webdir/all_$ranges.log";

	foreach my $file (@files) {
		$file =~ /$webdir\/(\d+)\.log/;
		my $freq = $1;
		my $log;
#		print "$file-$freq\n";

	    	open(INPUT, $file) or die $!;
		while (<INPUT>) {
			my $fucker = $_;
			next if $file =~ /all/;
	
			my ($time, $power) = split(",", $fucker);
			$log = "$time $freq $power\n";
			push(@lines, $log) if $log;
			$log = undef;		
		}
		close INPUT;
	}
#	print "Generating combined log with " . scalar(@lines) . " lines to put in:\n$outputlog\n";
	open (OUTPUT, ">$outputlog") or die "Shit shit fucking shit.\n$!";
	foreach my $line (@lines) {
		print OUTPUT $line;
		}
	close OUTPUT;

	generalized_spectralmap($outputlog,$listofranges);
}

sub getfilesinrange {
	my $list = shift;
	my @pairs = split(",",$list);
	my @files = <$webdir/*.log>;
	my @results;

	foreach my $range (@pairs) {
		my ($min1,$max1) = split('-',$range);
		$min1 *= 1000000;
		$max1 *= 1000000;
		foreach my $file (@files) {
			next if $file =~ /all/;
			$file =~ /$webdir\/(\d+)\.log/;
			my $freq = $1;
#			print "outside: $file\n";
			if ($freq >= $min1 && $freq <= $max1) {
#				print "inside: $min1,$freq,$max1,$file\n";
				push(@results, $file);
			}
		}
	}
	return @results;
}

sub generalized_spectralmap {
my ($logfile,$range) = @_;

print "Generating another spectral map over only $range.\n";

my $command = "set timefmt \"\%s\"\n
set view map\n
set terminal png crop\n
set output '$webdir/spectral-map_$range.png'\n
set term png size 1200, 800\n
set key off\n
set title \"$range Mhz\"\n
set xlabel \'Time\'\n
set ylabel \'Frequency (Hz)\'\n
set cblabel \'Intensity (dB)\'\n
set xdata time\n
set format x \"\%m/\%d\"\n
set ytics 100000000\n
plot \'$logfile\' using 1:2:3  with points ps 0.5 pt 5 palette\n
replot\n
exit\n";

open GP, '| gnuplot';
print GP $command;
close GP;
}
###############################

sub donglefrozen {
	# calls inline::c code at the end, resetusb(), to reset the dongle if it freezes.
	# alternately you can compile the code in the link below and call the binary.
	# http://askubuntu.com/questions/645/how-do-you-reset-a-usb-device-from-the-command-line
	my $usbreset;
	my @devices = split("\n",`lsusb`);
	foreach my $line (@devices) {
		if ($line =~ /\w+\s(\d+)\s\w+\s(\d+):.+Realtek Semiconductor Corp\./) {
#			$usbreset = "sudo ./usbreset /dev/bus/usb/$1/$2";
			$usbreset = "/dev/bus/usb/$1/$2";
			resetusb($usbreset);
		}
	}

}

__END__
__C__
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>

int resetusb(char *dongleaddress)
{
	const char *filename;
	int fd;
	int rc;
	filename = dongleaddress;

	fd = open(filename, O_WRONLY);
	if (fd < 0) {
		perror("Error opening output file");
		return 1;
	}

	printf("Resetting USB device %s\n", filename);
	rc = ioctl(fd, USBDEVFS_RESET, 0);
	if (rc < 0) {
		perror("Error in ioctl");
		return 1;
	}
	printf("Reset successful\n");

	close(fd);
	return 0;
}



