#!/usr/bin/perl
#use Cwd;
use POSIX qw(ceil);
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_gnuplot.py";
my $fasterscript = "python $scriptdir/graphfreqs_faster5.py";
my $fast = 0;

# 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 $rsync = 0;
my $htmlfile = "$webdir/index.html";
my $pointsize = "0.5";

## 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
	'point:s', => \$pointsize, # size of the points in the spectral map plots
	'mr:s' => \$ranges, # takes comma seperated list of ranges for "52-1108,1248-2200" spectral maps.
	'fast' => \$fast # enables fast mode, no spectrograms, just data taking and spectral maps.
);


# 1,000,000

### end config ###

if ($flist) {
	print "Running graphfreq batch jobs $flist Mhz at $spacing Mhz spacings.\n";
} elsif ($fast) {
	print "Running graphfreq in non-batch fast mode $min to $max Mhz at $spacing Mhz spacings.\n Spectrograms and text output disabled.\n";
	$script = $fasterscript;
} else {
	print "Running graphfreq batch job $min to $max Mhz at $spacing Mhz spacings.\n";
}

# um, okay, yeah, here's fine. Let's do this here.
$spacing =int($spacing*10e5);

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);
} else {
	$freqoffset_passed = 0;
}
	
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);
	oldstyle();

} elsif ($fast) {
	die "You need to set -f1 and -f2 frequencies to use fast mode\n" unless ($min and $max);
	$min1 = sprintf("%.0f", $min*1000000);
	$max1 = sprintf("%.0f", $max*1000000);
	my $command = "$script $min1 $samplerate $gain $freqoffset_passed $webdir $max1 $spacing";
	print "$command\n";
	$test = `$command`;
	
} else {
	for ($min; $min < $max; $min += $spacing) {
		$rounded = sprintf("%.0f", $min*1000000);
		push(@freqs,$rounded); # @freqs is global
	}
	oldstyle();

}

webpagegeneration($enablewebpagegeneration);
exit;


sub oldstyle { # takes global list of frequencies, @freqs
	foreach $freq (@freqs) {
	#		if ($start) {
	#			if ($freq < $start) {
	#				next;
	#			} else {
	#				$start = 0;
	#			}
	#		}
	
	# New style call: 				freq	samp	gain	ppm	dir	freq2
	#			python graphfreqs_faster3.py 40000000 2400000 30 -58 /tmp/faster 1700000000
	
			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;
	}
}

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
		}
		
	}

}


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 $freqdisplay;
if ($flist) {
	$freqdisplay = $flist;
} elsif ($fast) {
	$freqdisplay = "$min to $max";
} else {
	$freqdisplay = "$min to $max";
}

my $ytics = 100000000;
# ytics 100000000 , but what if the frequency range is funky?
# this only handles mhz
if ( (($max*10e5)-($min*10e5)) < 100000000 ) {
	$ytics = ((($max*10e5)-($min*10e5))/10);
	$ytics = roundup($ytics,1000000);
	unless ($ytics) {
		$ytics = 100000000;
	}

   #print "ytics: $ytics\n"; 
}
sub roundup {
  my $num = shift;
  my $roundto = shift || 1;
  return int(ceil($num/$roundto))*$roundto;
}

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 \"$freqdisplay Mhz\"\n
set xlabel \'Time\'\n
set ylabel \'Frequency (Hz)\'\n
set cblabel \'Intensity (dB)\'\n
set xdata time\n
set format x \"\%d/\%m \\n\%H\"
set ytics $ytics\n
plot \'$webdir/all.log\' using 1:2:3  with points ps $pointsize 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 $pointsize 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;
}



