#!/usr/local/bin/perl
###############################################################################
###############################################################################
##                                                                           ##
##  cabrillo_vhf_rescore.pl                          Kenneth E. Harker WM5R  ##
##                                                   wm5r@arrl.net           ##
##                                                   11 March 2004           ##
##  A tool to rescore Cabrillo log files under                               ##
##  different scoring formulas for VHF contests.                             ##
##                                                                           ##
###############################################################################
###############################################################################


$SEEN_START_OF_LOG                        = 0;
$VERBOSE                                  = 1;
$DELIM                                    = ",";

$callsign                                 = "";
$contest                                  = "";
$arrl_section                             = "";
$category                                 = "";
$claimed_score                            = "";
$operators                                = "";
$year                                     = "";

$QSOs                                     = "";
$current_qso_points                       = 0;
$current_qso_mults                        = 0;
$current_oldrover_qso_points              = 0;
$current_oldrover_qso_mults               = 0;
$current_oldrover_1ptrover_qso_points     = 0;
$current_oldrover_1ptrover_qso_mults      = 0;
$distance1_qso_points                     = 0;
$distance1_qso_mults                      = 0;
$distance1_oldrover_qso_points            = 0;
$distance1_oldrover_qso_mults             = 0;
$distance1_oldrover_1ptrover_qso_points   = 0;
$distance1_oldrover_1ptrover_qso_mults    = 0;
$current_lowbandsonly_qso_points          = 0;
$current_lowbandsonly_qso_mults           = 0;
$qsos1pt_lowbandsonly_qso_points          = 0;
$qsos1pt_lowbandsonly_qso_mults           = 0;
$multipliersCurrent                       = "";
$multipliersCurrentOldRover               = "";
$multipliersCurrentOldRover1ptRoverQSOs   = "";
$multipliersDistance1                     = "";
$multipliersDistance1OldRover             = "";
$multipliersDistance1OldRover1ptRoverQSOs = "";
$multipliersCurrentLowBandsOnly           = "";
$multipliers1ptQSOsLowBandsOnly           = "";
$visited_grids                            = "";
$visited_mult                             = 0;

##  Used for determining adjacent grid locators
$letter_up{"a"}   = "b";
$letter_up{"b"}   = "c";
$letter_up{"c"}   = "d";
$letter_up{"d"}   = "e";
$letter_up{"e"}   = "f";
$letter_up{"f"}   = "g";
$letter_up{"g"}   = "h";
$letter_up{"h"}   = "i";
$letter_up{"i"}   = "j";
$letter_up{"j"}   = "k";
$letter_up{"k"}   = "l";
$letter_up{"l"}   = "m";
$letter_up{"m"}   = "n";
$letter_up{"n"}   = "o";
$letter_up{"o"}   = "p";
$letter_up{"p"}   = "q";
$letter_up{"q"}   = "r";
$letter_up{"r"}   = "a";
$letter_down{"a"} = "r";
$letter_down{"b"} = "a";
$letter_down{"c"} = "b";
$letter_down{"d"} = "c";
$letter_down{"e"} = "d";
$letter_down{"f"} = "e";
$letter_down{"g"} = "f";
$letter_down{"h"} = "g";
$letter_down{"i"} = "h";
$letter_down{"j"} = "i";
$letter_down{"k"} = "j";
$letter_down{"l"} = "k";
$letter_down{"m"} = "l";
$letter_down{"n"} = "m";
$letter_down{"o"} = "n";
$letter_down{"p"} = "o";
$letter_down{"q"} = "p";
$letter_down{"r"} = "q";


###############################################################################
###############################################################################
##                                                                           ##
##  Process Command Line Parameters                                          ##
##                                                                           ##
###############################################################################
###############################################################################
 

##  If the first parameter asks for help, print usage.
while ($param = shift) {
 
    if (($param eq "-v") ||
        ($param eq "-verbose") ||
        ($param eq "--verbose")) {
        $VERBOSE = 1;
 
    } elsif (($param eq "-q") ||
        ($param eq "-quiet") ||
        ($param eq "--quiet")) {
        $VERBOSE = 0;
 
    } elsif (($param eq "-c") ||
        ($param eq "-comma") ||
        ($param eq "--comma")) {
        $DELIM = ",";
 
    } elsif (($param eq "-s") ||
        ($param eq "-space") ||
        ($param eq "--space")) {
        $DELIM = " ";
 
    } elsif (($param eq "-h") ||
        ($param eq "-help") ||
        ($param eq "--help")) {
        usage();
 
    }
}


###############################################################################
###############################################################################
##                                                                           ##
##  Main Processing Loop                                                     ##
##                                                                           ##
###############################################################################
###############################################################################


while (<STDIN>) {
    
    chomp;
    $line = $_;
    if ($line eq "") { next; }

    ## Ignore irrelevant log lines
    if (($line =~ /^CREATED-BY\:/) ||
	($line =~ /^NAME\:/) ||
	($line =~ /^ADDRESS\:/) ||
	($line =~ /^SOAPBOX\:/) ||
	($line =~ /^CLUB\:/)) {
	next;
    }

    ## Check for START-OF-LOG
    if ($SEEN_START_OF_LOG eq 0) {
	if ($line =~ /^START-OF-LOG\:/) {
	    $SEEN_START_OF_LOG = 1;
	} else {
	    print STDERR "No START-OF-LOG: tag.\n";
	    exit(1);
	}
    }

    ## Check for END-OF-LOG
    if ($line =~ /^END-OF-LOG\:/) {

	##  Generate labels for line scores
	$out_callsign = uc($orig_callsign);
	if ($category eq "SINGLE-OP-LOW")      { $out_category = "SOLP"; } 
	if ($category eq "SINGLE-OP-HIGH")     { $out_category = "SOHP"; } 
	if ($category eq "SINGLE-OP-PORTABLE") { $out_category = "SO/P"; }
	if ($category eq "ROVER")              { $out_category = "R"; }
	if ($category eq "MULTI-LIMITED")      { $out_category = "M/L"; }
	if ($category eq "MULTI-UNLIMITED")    { $out_category = "M/U"; }
	if ($category eq "CHECKLOG")           { $out_category = "CHK"; }
	if ($year eq "")                       { $year = "0000"; }
	if ($contest eq "ARRL-UHF-AUG") { $out_contest = $year . "-" ."Aug"; }
	if ($contest eq "ARRL-VHF-JAN") { $out_contest = $year . "-" ."Jan"; }
	if ($contest eq "ARRL-VHF-JUN") { $out_contest = $year . "-" ."Jun"; }
	if ($contest eq "ARRL-VHF-SEP") { $out_contest = $year . "-" ."Sep"; }
	$out_arrl_section = uc($arrl_section);
	
	##  Current score
	if ($category eq "ROVER") {
	    $current_qso_mults = $current_qso_mults + $visited_mult;
	}
	$current_score = 
	    $current_qso_points * 
	    $current_qso_mults;
	
	##  Current+OldRover score
	$current_oldrover_score = 
	    $current_oldrover_qso_points * 
	    $current_oldrover_qso_mults;
	
	##  Current+OldRover+1ptRoverQSOs score
	$current_oldrover_1ptrover_score = 
	    $current_oldrover_1ptrover_qso_points * 
	    $current_oldrover_1ptrover_qso_mults;

	##  Distance1 score
	if ($category eq "ROVER") {
	    $distance1_qso_mults = $distance1_qso_mults + $visited_mult;
	}
	$distance1_score = 
	    $distance1_qso_points * 
	    $distance1_qso_mults;

	##  Distance1+OldRover score
	$distance1_oldrover_score = 
	    $distance1_oldrover_qso_points * 
	    $distance1_oldrover_qso_mults;

	##  Distance1+OldRover+1ptRoverQSOs score
	$distance1_oldrover_1ptrover_score = 
	    $distance1_oldrover_1ptrover_qso_points * 
	    $distance1_oldrover_1ptrover_qso_mults;

	##  Current+LowBandsOnly score
	if ($category eq "ROVER") {
	    $current_lowbandsonly_qso_mults = 
		$current_lowbandsonly_qso_mults + $visited_mult;
	}
	$current_lowbandsonly_score = 
	    $current_lowbandsonly_qso_points * 
	    $current_lowbandsonly_qso_mults;

	##  1ptQSOs+LowBandsOnly score
	if ($category eq "ROVER") {
	    $qsos1pt_lowbandsonly_qso_mults = 
		$qsos1pt_lowbandsonly_qso_mults + $visited_mult;
	}
	$qsos1pt_lowbandsonly_score = 
	    $qsos1pt_lowbandsonly_qso_points * 
	    $qsos1pt_lowbandsonly_qso_mults;

	##  Output line score
	if ($VERBOSE eq 1) {
	    print "$out_contest";                         print "$DELIM";
	    print "$out_callsign";                        print "$DELIM";
	    print "$out_category";                        print "$DELIM";
	    print "$out_arrl_section";                    print "$DELIM";
	    print "$current_score";                       print "$DELIM";
	    print "$current_oldrover_score";              print "$DELIM";
	    print "$current_oldrover_1ptrover_score";     print "$DELIM";
	    print "$distance1_score";                     print "$DELIM";
	    print "$distance1_oldrover_score";            print "$DELIM";
	    print "$distance1_oldrover_1ptrover_score";   print "$DELIM";
	    print "$current_lowbandsonly_score";          print "$DELIM";
	    print "$qsos1pt_lowbandsonly_score\n";
	} else {
	    print "$out_callsign";                        print "$DELIM"; 
	    print "$current_score";                       print "$DELIM";
	    print "$current_oldrover_score";              print "$DELIM";
	    print "$current_oldrover_1ptrover_score";     print "$DELIM";
	    print "$distance1_score";                     print "$DELIM";
	    print "$distance1_oldrover_score";            print "$DELIM";
	    print "$distance1_oldrover_1ptrover_score";   print "$DELIM";
	    print "$current_lowbandsonly_score";          print "$DELIM";
	    print "$qsos1pt_lowbandsonly_score\n";
	}
	
	##  Prepare for next log
	$SEEN_START_OF_LOG = 0;
	$callsign          = "";
	$contest           = "";
	$arrl_section      = "";
	$category          = "";
	$claimed_score     = "";
	$operators         = "";
	$year              = "";
	foreach $k (keys(QSOs)) { 
	    $QSOs{$k} = 0; }
	foreach $k (keys(multipliersCurrent))  { 
	    $multipliersCurrent{$k} = 0; }
	foreach $k (keys(multipliersCurrentOldRover)) { 
	    $multipliersCurrentOldRover{$k} = 0; }
	foreach $k (keys(multipliersCurrentOldRover1ptRoverQSOs)) { 
	    $multipliersCurrentOldRover1ptRoverQSOs{$k} = 0; }
	foreach $k (keys(multipliersDistance1)) { 
	    $multipliersDistance1{$k} = 0; }
	foreach $k (keys(multipliersDistance1OldRover)) { 
	    $multipliersDistance1OldRover{$k} = 0; }
	foreach $k (keys(multipliersDistance1OldRover1ptRoverQSOs)) { 
	    $multipliersDistance1OldRover1ptRoverQSOs{$k} = 0; }
	foreach $k (keys(multipliersCurrentLowBandsOnly)) { 
	    $multipliersCurrentLowBandsOnly{$k} = 0; }
	foreach $k (keys(multipliers1ptQSOsLowBandsOnly)) { 
	    $multipliers1ptQSOsLowBandsOnly{$k} = 0; }
	foreach $k (keys(visited_grids)) { 
	    $visited_grids{$k} = 0; }
	$current_qso_points                     = 0;
	$current_qso_mults                      = 0;
	$current_oldrover_qso_points            = 0;
	$current_oldrover_qso_mults             = 0;
	$current_oldrover_1ptrover_qso_points   = 0;
	$current_oldrover_1ptrover_qso_mults    = 0;
	$distance1_qso_points                   = 0;
	$distance1_qso_mults                    = 0;
	$distance1_oldrover_qso_points          = 0;
	$distance1_oldrover_qso_mults           = 0;
	$distance1_oldrover_1ptrover_qso_points = 0;
	$distance1_oldrover_1ptrover_qso_mults  = 0;
	$current_lowbandsonly_qso_points        = 0;
	$current_lowbandsonly_qso_mults         = 0;
	$qsos1pt_lowbandsonly_qso_points        = 0;
	$qsos1pt_lowbandsonly_qso_mults         = 0;
	$visited_mult                           = 0;

	##  Identify common EOL problem at end of log files when 
	##  multiple logs are on STDIN.
	if ($line =~ /^END-OF-LOG\:.*START-OF-LOG\:/) {
	    $SEEN_START_OF_LOG = 1;
	}
    }

    ## Record basic information
    if ($line =~ /^CALLSIGN\:\s+([\w\d\/]*)/) {
	$callsign = uc($1);	
	if ($callsign =~ /(.*)\/R$/) { $callsign = $1; }
	if ($callsign =~ /(.*)\/ROVER$/) { $callsign = $1; }
	if ($callsign =~ /(.*)\/MM$/) { $callsign = $1; }
	if ($callsign =~ /(.*)\/P$/) { $callsign = $1; }
	if ($callsign =~ /(.*)\/\d$/) { $callsign = $1; }
	if ($callsign =~ /(.*)\/W\d$/) { $callsign = $1; }
	if ($callsign =~ /(.*)\/K\d$/) { $callsign = $1; }
	if ($callsign =~ /(.*)\/N\d$/) { $callsign = $1; }
	if ($callsign =~ /(.*)\/\w\w\d\d$/) { $callsign = $1; }
	$orig_callsign = $callsign; 
	next; 
    }
    if ($line =~ /^CONTEST\:\s+([\w\d\-]*)/) {
	$contest = $1; next; }
    if ($line =~ /^ARRL-SECTION\:\s+([\w]*)/) {
	$arrl_section = $1; next; }
    if ($line =~ /^CATEGORY\:\s+([\w\d\-]*)/) {
	$category = uc($1);
	unless (($category eq "SINGLE-OP") || 
		($category eq "SINGLE-OP-PORTABLE") ||
		($category eq "ROVER") ||
		($category eq "MULTI-LIMITED") ||
		($category eq "MULTI-UNLIMITED") ||
		($category eq "CHECKLOG")) {
	    print STDERR "Invalid entry category: $category\n";
	}
	if ($line =~ /^CATEGORY\:\s+SINGLE-OP\s+.*\s+(\w+)/) {
	    $power = uc($1);
	    $category = $category . "-" . $power;
	}
	next; 
    }
    if ($line =~ /^CLAIMED-SCORE\:\s+(.*)/) {
	$claimed_score = $1; next; }
    if ($line =~ /^OPERATORS\:\s+(.*)/) {
	$operators = $1; next; }
    $callsign = lc($callsign);
    if ($callsign =~ /(.*)\/([^\/]*)$/) {
	$callsign = $1;
    }

    ## Process QSO Lines
    if ($line =~ /^QSO\:\s+(.*)/) {

	##  Check for missing year and dupes
	$qso_data = $1;
	if ($year eq "") { $year = get_year($qso_data);	}
	if (dupe_check($qso_data) eq 1) { next; }

	##  Current
	$current_qso_points += score_qso("Current " . $qso_data);
	$current_qso_mults  += mults_qso("Current " . $qso_data);

	##  Current+OldRoverRules
	$current_oldrover_qso_points += 
	    score_qso("CurrentOldRoverRules " . $qso_data);
	$current_oldrover_qso_mults  += 
	    mults_qso("CurrentOldRoverRules " . $qso_data);

	##  Current+OldRoverRules+1ptRover
	$current_oldrover_1ptrover_qso_points += 
	    score_qso("CurrentOldRoverRules1ptRoverQSOs " . $qso_data);
	$current_oldrover_1ptrover_qso_mults  += 
	    mults_qso("CurrentOldRoverRules1ptRoverQSOs " . $qso_data);

	##  Distance1
	$distance1_qso_points += score_qso("Distance1 " . $qso_data);
	$distance1_qso_mults  += mults_qso("Distance1 " . $qso_data);

	##  Distance1+OldRoverRules
	$distance1_oldrover_qso_points += 
	    score_qso("Distance1OldRoverRules " . $qso_data);
	$distance1_oldrover_qso_mults  += 
	    mults_qso("Distance1OldRoverRules " . $qso_data);

	##  Distance1+OldRoverRules+1ptRover
	$distance1_oldrover_1ptrover_qso_points += 
	    score_qso("Distance1OldRoverRules1ptRoverQSOs " . $qso_data);
	$distance1_oldrover_1ptrover_qso_mults  += 
	    mults_qso("Distance1OldRoverRules1ptRoverQSOs " . $qso_data);

	##  Current+LowBandsOnly
	$current_lowbandsonly_qso_points += 
	    score_qso("CurrentLowBandsOnly " . $qso_data);
	$current_lowbandsonly_qso_mults  += 
	    mults_qso("CurrentLowBandsOnly " . $qso_data);

	##  1ptQSOs+LowBandsOnly
	$qsos1pt_lowbandsonly_qso_points += 
	    score_qso("1ptQSOsLowBandsOnly " . $qso_data);
	$qsos1pt_lowbandsonly_qso_mults  += 
	    mults_qso("1ptQSOsLowBandsOnly " . $qso_data);

    }

}

exit(0);


###############################################################################
###############################################################################
##                                                                           ##
##  get_year                                                                 ##
##                                                                           ##
##  Extracts the year from the date field of a QSO data line                 ##
##                                                                           ##
##  Input: QSO data line                                                     ##
##                                                                           ##
##  Output: four-digit year                                                  ##
##                                                                           ##
##  Error: none.                                                             ##
##                                                                           ##
###############################################################################
###############################################################################


sub get_year {

    ##  Extract data components and lowercase
    my ($qso_data) = @_;
    ($band, $mode, $date, $time, $mycall, $mygrid, $call, $grid) = 
	split(" ",$qso_data);
    ($year,$month,$day) = split("-",$date);

    if ($year ne "") {
	return $year;
    } else {
	return 0;
    }

}
              
                                                                 
###############################################################################
###############################################################################
##                                                                           ##
##  dupe_check                                                               ##
##                                                                           ##
##  Checks a QSO to see if it is a duplicate                                 ##
##                                                                           ##
##  Input: QSO data line                                                     ##
##                                                                           ##
##  Output: 0 for non-dupe, 1 for dupe                                       ##
##                                                                           ##
##  Error: none.                                                             ##
##                                                                           ##
###############################################################################
###############################################################################


sub dupe_check {

    ##  Extract data components and lowercase
    my ($qso_data) = @_;
    ($band, $mode, $date, $time, $mycall, $mygrid, $call, $grid) = 
	split(" ",$qso_data);
    $band   = lc($band);
    $mode   = lc($mode);
    $mycall = lc($mycall);
    $mygrid = lc($mygrid);
    $call   = lc($call);
    
    ##  Handle potential wierdness at end of line
    $grid   = lc($grid);
    if ($grid =~ /(\w\w\d\d)/) { $grid = $1; }

    ##  Massage and check callsigns
    if ($mycall =~ /(.*)\/r$/)        { $mycall = $1; }
    if ($mycall =~ /(.*)\/rover$/)    { $mycall = $1; }
    if ($mycall =~ /(.*)\/mm$/)       { $mycall = $1; }
    if ($mycall =~ /(.*)\/p$/)        { $mycall = $1; }
    if ($mycall =~ /(.*)\/\d$/)       { $mycall = $1; }
    if ($mycall =~ /(.*)\/w\d$/)      { $mycall = $1; }
    if ($mycall =~ /(.*)\/k\d$/)      { $mycall = $1; }
    if ($mycall =~ /(.*)\/n\d$/)      { $mycall = $1; }
    if ($mycall =~ /(.*)\/\w\w\d\d$/) { $mycall = $1; }
    if ($call =~ /(.*)\/r$/)          { $call = $1; }
    if ($call =~ /(.*)\/rover$/)      { $call = $1; }
    if ($call =~ /(.*)\/mm$/)         { $call = $1; }
    if ($call =~ /(.*)\/p$/)          { $call = $1; }
    if ($call =~ /(.*)\/\d$/)         { $call = $1; }
    if ($call =~ /(.*)\/w\d$/)        { $call = $1; }
    if ($call =~ /(.*)\/k\d$/)        { $call = $1; }
    if ($call =~ /(.*)\/n\d$/)        { $call = $1; }
    if ($call =~ /(.*)\/\w\w\d\d$/)   { $call = $1; }

    ##  Compute a unique label for the QSO and check against DB
    $current = $call . "_" . $grid . "_" . $band . "_" . $mygrid;
    if ($QSOs{$current} eq 1) {
	return 1;
    } else {
	$QSOs{$current}  = 1;
	return 0;
    }

    return 0;

}
              
                                                                 
###############################################################################
###############################################################################
##                                                                           ##
##  score_qso                                                                ##
##                                                                           ##
##  Computes the QSO points for a QSO                                        ##
##                                                                           ##
##  Input: QSO data line                                                     ##
##                                                                           ##
##  Output: points to assign for the QSO                                     ##
##                                                                           ##
##  Error: none.                                                             ##
##                                                                           ##
###############################################################################
###############################################################################


sub score_qso {

    ##  Extract data components and lowercase
    my ($qso_data) = @_;
    ($formula, $band, $mode, $date, $time, $mycall, $mygrid, $call, $grid) = 
	split(" ",$qso_data);

    ##  Normalize the band indication
    $band   = lc($band);
    $band   = normalize_band($band);
    if ($band eq 0) { return 0; }
	
    ##  Lowercase everything
    $mode   = lc($mode);
    $mycall = lc($mycall);
    $mygrid = lc($mygrid);
    $call   = lc($call);
    $grid   = lc($grid);

    ##  Handle potential wierdness
    $grid   = lc($grid);
    if ($grid =~ /(\w\w\d\d)/) { $grid = $1; }
    if ($call =~ /(.*)\/\w\w\d\d$/)   { $call = $1 . "/r"; }
    if ($call =~ /(.*)\/\d$/)         { $call = $1; }
    if ($call =~ /(.*)\/w\d$/)        { $call = $1; }
    if ($call =~ /(.*)\/k\d$/)        { $call = $1; }
    if ($call =~ /(.*)\/n\d$/)        { $call = $1; }
    if ($call =~ /(.*)\/mm$/)         { $call = $1; }
    if ($call =~ /(.*)\/m$/)          { $call = $1 . "/r"; }

    ##  1ptRoverQSOs formula
    if (($formula eq "CurrentOldRoverRules1ptRoverQSOs") ||
	($formula eq "Distance1OldRoverRules1ptRoverQSOs")) {
	if (($call =~ /.+\/r/) || ($call =~ /.+\/rover/)) {
	    return 1;
	}
    }

    ##  Current QSO point scoring formulas
    if (($formula eq "Current") || 
	($formula eq "CurrentOldRoverRules") ||
	($formula eq "CurrentOldRoverRules1ptRoverQSOs")) {

	if (($contest eq "ARRL-VHF-JUN") ||
	    ($contest eq "ARRL-VHF-SEP")) { 
	    if (($band eq "50")       || ($band eq "144")) {
		return 1; 
	    } elsif (($band eq "222") || ($band eq "432")) { 
		return 2; 
	    } elsif (($band eq "902") || ($band eq "1.2")) {
		return 3; 
	    } elsif (($band eq "2.3") || ($band eq "3.4")   ||
		     ($band eq "5.7") || ($band eq "10")    ||
		     ($band eq "24")  || ($band eq "47")    ||
		     ($band eq "75")  || ($band eq "119")   ||
		     ($band eq "142") || ($band eq "241")   ||
		     ($band eq "300") || ($band eq "light")) {
		return 4; 
	    } else {
		return 0;
	    }
	} elsif ($contest eq "ARRL-VHF-JAN") {
	    if (($band eq "50")       || ($band eq "144")) {
		return 1; 
	    } elsif (($band eq "222") || ($band eq "432")) { 
		return 2; 
	    } elsif (($band eq "902") || ($band eq "1.2")) {
		return 4; 
	    } elsif (($band eq "2.3") || ($band eq "3.4")   ||
		     ($band eq "5.7") || ($band eq "10")    ||
		     ($band eq "24")  || ($band eq "47")    ||
		     ($band eq "75")  || ($band eq "119")   ||
		     ($band eq "142") || ($band eq "241")   ||
		     ($band eq "300") || ($band eq "light")) {
		return 8; 
	    } else {
		return 0;
	    }
	}
    }

    ##  Distance-based QSO point scoring formulas
    if (($formula eq "Distance1") ||
	($formula eq "Distance1OldRoverRules") ||
	($formula eq "Distance1OldRoverRules1ptRoverQSOs")) {
	if (is_grid_adjacent($mygrid . " " . $grid) eq 1) {
	    return 2;
	} else {
	    return 3;
	}
    }
    
    ##  Current QSO point scoring formulas
    if ($formula eq "CurrentLowBandsOnly") {

	if (($contest eq "ARRL-VHF-JUN") ||
	    ($contest eq "ARRL-VHF-SEP")) {
	    if (($band eq "50")       || ($band eq "144")) {
		return 1; 
	    } elsif (($band eq "222") || ($band eq "432")) { 
		return 2; 
	    } elsif (($band eq "902") || ($band eq "1.2")) {
		return 4; 
	    } else {
		return 0;
	    } 
	} elsif ($contest eq "ARRL-VHF-JAN") {
	    if (($band eq "50")       || ($band eq "144")) {
		return 1; 
	    } elsif (($band eq "222") || ($band eq "432")) { 
		return 2; 
	    } elsif (($band eq "902") || ($band eq "1.2")) {
		return 3; 
	    } else {
		return 0;
	    } 
	}    
    }

    ##  Current QSO point scoring formulas
    if ($formula eq "1ptQSOsLowBandsOnly") {

	if (($band eq "50")  || ($band eq "144") ||
	    ($band eq "222") || ($band eq "432") ||
	    ($band eq "902") || ($band eq "1.2")) {
	    return 1; 
	} else {
	    return 0;
	}
    }

    return 0;

}
              
                                                                 
###############################################################################
###############################################################################
##                                                                           ##
##  mults_qso                                                                ##
##                                                                           ##
##  Computes the multipliers associated with a QSO                           ##
##                                                                           ##
##  Input: QSO data line                                                     ##
##                                                                           ##
##  Output: 1, if a new multiplier, 0 otherwise                              ##
##                                                                           ##
##  Error: none.                                                             ##
##                                                                           ##
###############################################################################
###############################################################################


sub mults_qso {

    ##  Extract data components and lowercase
    my ($qso_data) = @_;
    ($formula, $band, $mode, $date, $time, $mycall, $mygrid, $call, $grid) = 
	split(" ",$qso_data);
    $band = lc($band);
    $band = normalize_band($band);
    if ($band eq 0) { return 0;	}
	
    $mode   = lc($mode);
    $mycall = lc($mycall);
    $mygrid = lc($mygrid);
    $call   = lc($call);
    $grid   = lc($grid);

    ##  Handle potential wierdness
    $grid   = lc($grid);
    if ($grid =~ /(\w\w\d\d)/) { $grid = $1; }
    if ($call =~ /(.*)\/\w\w\d\d$/)   { $call = $1 . "/r"; }
    if ($call =~ /(.*)\/\d$/)         { $call = $1; }
    if ($call =~ /(.*)\/w\d$/)        { $call = $1; }
    if ($call =~ /(.*)\/k\d$/)        { $call = $1; }
    if ($call =~ /(.*)\/n\d$/)        { $call = $1; }
    if ($call =~ /(.*)\/mm$/)         { $call = $1; }
    if ($call =~ /(.*)\/m$/)          { $call = $1 . "/r"; }

    ##  Current scoring formula
    if ($formula eq "Current") {

	##  For rovers only
	if ($category eq "ROVER") {
	    if ($visited_grids{$mygrid} ne 1) {
		$visited_grids{$mygrid} = 1;
		$visited_mult++;
	    }
	}

	##  Each worked grid, per band, is a mult
	$grid_band = $grid . "_" . $band;
	if ($multipliersCurrent{$grid_band} eq 1) {
	    return 0;
	} else {
	    $multipliersCurrent{$grid_band} = 1;
	    return 1;
	}
    }

    ##  Current+OldROverRules scoring formula
    if ($formula eq "CurrentOldRoverRules") {

	##  Each worked grid per band per band-activated, is a mult
	$grid_band = $mygrid . "_" . $grid . "_" . $band;
	if ($multipliersCurrentOldRover{$grid_band} eq 1) {
	    return 0;
	} else {
	    $multipliersCurrentOldRover{$grid_band} = 1;
	    return 1;
	}
    }

    ##  Current+OldRoverRules+1ptRoverQSOs scoring formula
    if ($formula eq "CurrentOldRoverRules1ptRoverQSOs") {

	##  Each worked grid per band per band-activated, is a mult
	$grid_band = $mygrid . "_" . $grid . "_" . $band;
	if ($multipliersCurrentOldRover1ptRoverQSOs{$grid_band} eq 1) {
	    return 0;
	} else {
	    $multipliersCurrentOldRover1ptRoverQSOs{$grid_band} = 1;
	    return 1;
	}
    }

    ##  Distance1 scoring formula
    if ($formula eq "Distance1") {

	##  For rovers only
	if ($category eq "ROVER") {
	    if ($visited_grids{$mygrid} ne 1) {
		$visited_grids{$mygrid} = 1;
		$visited_mult++;
	    }
	}

	##  Each worked grid, per band, is a mult
	$grid_band = $grid . "_" . $band;
	if ($multipliersDistance1{$grid_band} eq 1) {
	    return 0;
	} else {
	    $multipliersDistance1{$grid_band} = 1;
	    return 1;
	}
    }

    ##  Distance1+OldROverRules scoring formula
    if ($formula eq "Distance1OldRoverRules") {

	##  Each worked grid per band per band-activated, is a mult
	$grid_band = $mygrid . "_" . $grid . "_" . $band;
	if ($multipliersDistance1OldRover{$grid_band} eq 1) {
	    return 0;
	} else {
	    $multipliersDistance1OldRover{$grid_band} = 1;
	    return 1;
	}
    }

    ##  Distance1+OldROverRules+1ptRoverQSOs scoring formula
    if ($formula eq "Distance1OldRoverRules1ptRoverQSOs") {

	##  Each worked grid per band per band-activated, is a mult
	$grid_band = $mygrid . "_" . $grid . "_" . $band;
	if ($multipliersDistance1OldRover1ptRoverQSOs{$grid_band} eq 1) {
	    return 0;
	} else {
	    $multipliersDistance1OldRover1ptRoverQSOs{$grid_band} = 1;
	    return 1;
	}
    }

    ##  Current scoring formula
    if ($formula eq "CurrentLowBandsOnly") {

	if (($band eq "50")  || ($band eq "144") || ($band eq "222") ||
	    ($band eq "432") || ($band eq "902") || ($band eq "1.2")) {

	    ##  For rovers only
	    if ($category eq "ROVER") {
		if ($visited_grids{$mygrid} ne 1) {
		    $visited_grids{$mygrid} = 1;
		    $visited_mult++;
		}
	    }
	    
	    ##  Each worked grid, per band, is a mult
	    $grid_band = $grid . "_" . $band;
	    if ($multipliersCurrentLowBandsOnly{$grid_band} eq 1) {
		return 0;
	    } else {
		$multipliersCurrentLowBandsOnly{$grid_band} = 1;
		return 1;
	    }

	} else {
	    return 0;
	}

    }
    ##  Current scoring formula
    if ($formula eq "1ptQSOsLowBandsOnly") {

	if (($band eq "50")  || ($band eq "144") || ($band eq "222") ||
	    ($band eq "432") || ($band eq "902") || ($band eq "1.2")) {

	    ##  For rovers only
	    if ($category eq "ROVER") {
		if ($visited_grids{$mygrid} ne 1) {
		    $visited_grids{$mygrid} = 1;
		    $visited_mult++;
		}
	    }
	    
	    ##  Each worked grid, per band, is a mult
	    $grid_band = $grid . "_" . $band;
	    if ($multipliers1ptQSOsLowBandsOnly{$grid_band} eq 1) {
		return 0;
	    } else {
		$multipliers1ptQSOsLowBandsOnly{$grid_band} = 1;
		return 1;
	    }

	} else {
	    return 0;
	}

    }
}


###############################################################################
###############################################################################
##                                                                           ##
##  normalize_band                                                           ##
##                                                                           ##
##  Takes in a string meant to represent a VHF/UHF band and outputs a        ##
##  correct band designator.                                                 ##
##                                                                           ##
##  Input: string                                                            ##
##                                                                           ##
##  Output: string                                                           ##
##                                                                           ##
##  Error: returns a 0 if the band input is not recognized                   ##
##                                                                           ##
###############################################################################
###############################################################################


sub normalize_band {

    ##  Extract band data
    my ($band) = @_;
    $band = lc($band);
    if ($band =~ /^(50)\..*/)      { return "50" }
    elsif ($band =~ /^50\d\d\d/)   { return "50" }
    elsif ($band =~ /^50\..*/)     { return "50" }
    elsif ($band eq "50")          { return "50" }
    elsif ($band eq "a")           { return "50" }
    elsif ($band =~ /^144\d\d\d/)  { return "144" }
    elsif ($band =~ /^144\d\d/)    { return "144" }
    elsif ($band =~ /^144\..*/)    { return "144" }
    elsif ($band =~ /^145\d\d\d/)  { return "144" }
    elsif ($band =~ /^145\..*/)    { return "144" }
    elsif ($band =~ /^146\d\d\d/)  { return "144" }
    elsif ($band =~ /^146\..*/)    { return "144" }
    elsif ($band =~ /^147\d\d\d/)  { return "144" }
    elsif ($band =~ /^147\..*/)    { return "144" }
    elsif ($band eq "144")         { return "144" }
    elsif ($band eq "145")         { return "144" }
    elsif ($band eq "146")         { return "144" }
    elsif ($band eq "147")         { return "144" }
    elsif ($band eq "b")           { return "144" }
    elsif ($band =~ /^222\d\d\d/)  { return "222" }
    elsif ($band =~ /^222\..*/)    { return "222" }
    elsif ($band =~ /^223\d\d\d/)  { return "222" }
    elsif ($band =~ /^223\..*/)    { return "222" }
    elsif ($band eq "220")         { return "222" }
    elsif ($band eq "221")         { return "222" }
    elsif ($band eq "222")         { return "222" }
    elsif ($band eq "223")         { return "222" }
    elsif ($band eq "224")         { return "222" }
    elsif ($band eq "c")           { return "222" }
    elsif ($band =~ /^432\d\d\d/)  { return "432" }
    elsif ($band =~ /^432\..*/)    { return "432" }
    elsif ($band eq "440")         { return "432" }
    elsif ($band eq "446")         { return "432" }
    elsif ($band eq "432")         { return "432" }
    elsif ($band eq "d")           { return "432" }
    elsif ($band eq "902")         { return "902" }
    elsif ($band eq "903")         { return "902" }
    elsif ($band eq "9")           { return "902" }
    elsif ($band eq "1.2")         { return "1.2" }
    elsif ($band eq "1200")        { return "1.2" }
    elsif ($band eq "1294")        { return "1.2" }
    elsif ($band eq "1296")        { return "1.2" }
    elsif ($band eq "e")           { return "1.2" }
    elsif ($band eq "2.3")         { return "2.3" }
    elsif ($band eq "f")           { return "2.3" }
    elsif ($band eq "3.4")         { return "3.4" }
    elsif ($band eq "3.5")         { return "3.4" }
    elsif ($band eq "g")           { return "3.4" }
    elsif ($band eq "5.7")         { return "5.7" }
    elsif ($band eq "h")           { return "5.7" }
    elsif ($band eq "10")          { return "10" }
    elsif ($band eq "10g")         { return "10" }
    elsif ($band eq "i")           { return "10" }
    elsif ($band eq "24")          { return "24" }
    elsif ($band eq "24g")         { return "24" }
    elsif ($band eq "j")           { return "24" }
    elsif ($band eq "47")          { return "47" }
    elsif ($band eq "47g")         { return "47" }
    elsif ($band eq "k")           { return "47" }
    elsif ($band eq "75")          { return "75" }
    elsif ($band eq "75g")         { return "75" }
    elsif ($band eq "76g")         { return "75" }
    elsif ($band eq "l")           { return "75" }
    elsif ($band eq "119")         { return "119" }
    elsif ($band eq "m")           { return "119" }
    elsif ($band eq "142")         { return "142" }
    elsif ($band eq "n")           { return "142" }
    elsif ($band eq "241")         { return "241" }
    elsif ($band eq "o")           { return "241" }
    elsif ($band eq "300")         { return "300" }
    elsif ($band eq "p")           { return "300" }
    elsif ($band eq "light")       { return "light" }
    elsif ($band eq "laser")       { return "light" }
    elsif ($band eq "lht")         { return "light" }
    elsif ($band eq "ph")          { return 0 }
    elsif ($band eq "cw")          { return 0 }
    elsif ($band eq "ry")          { return 0 }
    else { 
	printf STDERR "Band designation $band unrecognized in log $callsign\n";
	return 0; 
    }
}


###############################################################################
###############################################################################
##                                                                           ##
##  is_grid_adjacent                                                         ##
##                                                                           ##
##  Compares two four-digit grid locators and determines if they are         ##
##  adjacent to one another.                                                 ##
##                                                                           ##
##  Input: Two grid locators                                                 ##
##                                                                           ##
##  Output: 1 if adjacent, 0 otherwise                                       ##
##                                                                           ##
##  Error: none.                                                             ##
##                                                                           ##
###############################################################################
###############################################################################


sub is_grid_adjacent {

    ##  Extract data components and lowercase
    my ($two_grids) = @_;
    ($grid1, $grid2) = 	split(" ",$two_grids);

    ##  If grid1 equals grid2, they are adjacent
    if ($grid1 eq $grid2) { return 1; }

    ##  Check for poles
    if ((grid_north($grid1) eq "NPOL") &&
	(grid_north($grid2) eq "NPOL")) { return 1; }
    if ((grid_south($grid1) eq "SPOL") &&
	(grid_south($grid2) eq "SPOL")) { return 1; }

    ##  Test adjacent grids
    if (grid_north($grid1) eq $grid2)            { return 1; } 
    if (grid_north(grid_east($grid1)) eq $grid2) { return 1; } 
    if (grid_east($grid1) eq $grid2)             { return 1; } 
    if (grid_south(grid_east($grid1)) eq $grid2) { return 1; } 
    if (grid_south($grid1) eq $grid2)            { return 1; } 
    if (grid_south(grid_west($grid1)) eq $grid2) { return 1; } 
    if (grid_west($grid1) eq $grid2)             { return 1; } 
    if (grid_north(grid_west($grid1)) eq $grid2) { return 1; } 

    ##  If we haven't returned yet, grid1 and grid2 are not adjacent
    return 0;

}
              

###############################################################################
###############################################################################
##                                                                           ##
##  grid_north                                                               ##
##                                                                           ##
##  Compute the grid locator to the north of the grid locator sent as        ##
##  the parameter value.                                                     ##
##                                                                           ##
##  Input: grid locator                                                      ##
##                                                                           ##
##  Output: grid locator                                                     ##
##                                                                           ##
##  Error: none.                                                             ##
##                                                                           ##
###############################################################################
###############################################################################


sub grid_north {

    ##  Extract data components and lowercase
    my ($current_grid) = @_;
    $current_grid = lc($current_grid);

    ##  Break down to components.
    if ($current_grid =~ /(\w)(\w)(\d)(\d)/) {
        $l1 = $1;
        $l2 = $2;
        $n1 = $3;
        $n2 = $4;
    }

    ##  Check for north pole
    if (($l2 eq "r") && ($n2 eq 9)) {
	return "NPOL";
    }

    ##  Compute north grid
    if ($n2 eq 9) {
	$n2 = 0;
	$l2 = $letter_up{$l2};
    } else {
	$n2++;
    }

    ##  Output result
    $north_grid = $l1 . $l2 . $n1 . $n2;
    return $north_grid;

}


###############################################################################
###############################################################################
##                                                                           ##
##  grid_south                                                               ##
##                                                                           ##
##  Compute the grid locator to the south of the grid locator sent as        ##
##  the parameter value.                                                     ##
##                                                                           ##
##  Input: grid locator                                                      ##
##                                                                           ##
##  Output: grid locator                                                     ##
##                                                                           ##
##  Error: none.                                                             ##
##                                                                           ##
###############################################################################
###############################################################################


sub grid_south {

    ##  Extract data components and lowercase
    my ($current_grid) = @_;
    $current_grid = lc($current_grid);

    ##  Break down to components.
    if ($current_grid =~ /(\w)(\w)(\d)(\d)/) {
        $l1 = $1;
        $l2 = $2;
        $n1 = $3;
        $n2 = $4;
    }

    ##  Check for south pole
    if (($l2 eq "a") && ($n2 eq 0)) {
	return "SPOL";
    }

    ##  Compute south grid
    if ($n2 eq 0) {
	$n2 = 9;
	$l2 = $letter_down{$l2};
    } else {
	$n2--;
    }

    ##  Output result
    $south_grid = $l1 . $l2 . $n1 . $n2;
    return $south_grid;

}


###############################################################################
###############################################################################
##                                                                           ##
##  grid_east                                                                ##
##                                                                           ##
##  Compute the grid locator to the east of the grid locator sent as         ##
##  the parameter value.                                                     ##
##                                                                           ##
##  Input: grid locator                                                      ##
##                                                                           ##
##  Output: grid locator                                                     ##
##                                                                           ##
##  Error: none.                                                             ##
##                                                                           ##
###############################################################################
###############################################################################


sub grid_east {

    ##  Extract data components and lowercase
    my ($current_grid) = @_;
    $current_grid = lc($current_grid);

    ##  Break down to components.
    if ($current_grid =~ /(\w)(\w)(\d)(\d)/) {
        $l1 = $1;
        $l2 = $2;
        $n1 = $3;
        $n2 = $4;
    }

    ##  Compute east grid
    if ($n1 eq 9) {
	$n1 = 0;
	$l1 = $letter_up{$l1};
    } else {
	$n1++;
    }

    ##  Output result
    $east_grid = $l1 . $l2 . $n1 . $n2;
    return $east_grid;

}


###############################################################################
###############################################################################
##                                                                           ##
##  grid_west                                                                ##
##                                                                           ##
##  Compute the grid locator to the west of the grid locator sent as         ##
##  the parameter value.                                                     ##
##                                                                           ##
##  Input: grid locator                                                      ##
##                                                                           ##
##  Output: grid locator                                                     ##
##                                                                           ##
##  Error: none.                                                             ##
##                                                                           ##
###############################################################################
###############################################################################


sub grid_west {

    ##  Extract data components and lowercase
    my ($current_grid) = @_;
    $current_grid = lc($current_grid);

    ##  Break down to components.
    if ($current_grid =~ /(\w)(\w)(\d)(\d)/) {
        $l1 = $1;
        $l2 = $2;
        $n1 = $3;
        $n2 = $4;
    }

    ##  Compute west grid
    if ($n1 eq 0) {
	$n1 = 9;
	$l1 = $letter_down{$l1};
    } else {
	$n1--;
    }

    ##  Output result
    $west_grid = $l1 . $l2 . $n1 . $n2;
    return $west_grid;

}


###############################################################################
###############################################################################
##                                                                           ##
##  usage                                                                    ##
##                                                                           ##
##  Prints out usage information in response to -h or -help.                 ##
##                                                                           ##
##  Input: none.                                                             ##
##                                                                           ##
##  Output: none.                                                            ##
##                                                                           ##
##  Error: none.                                                             ##
##                                                                           ##
###############################################################################
###############################################################################


sub usage {

    print "\n$0:\n\n  ";

    print "This script reads in Cabrillo format files and computes ";
    print "eight different\n  contest scores based upon eight different ";
    print "scoring formulas for each log.\n  Output is in space or ";
    print "comma-delimited format.  The eight scoring formulas\n  are:\n\n";
    print "    Current\n";
    print "      The current QSO point and multiplier formulas for all ";
    print "categories.\n";
    print "\n";
    print "    Current+OldRoverRules\n";
    print "      QSO points are computed as they are in the current rules.  ";
    print "Multipliers\n      for stations in the rover category are computed";
    print " according to the old\n      rover rules.\n";
    print "\n";
    print "    Current+OldRoverRules+1ptRoverQSOs\n";
    print "      QSO points are computed as they are in the current rules, ";
    print "except that\n      all QSOs with rover stations are 1 point, ";
    print "regardless of band.\n      Multipliers for stations in the rover ";
    print "category are computed according\n      to the old rover rules.\n";
    print "\n";
    print "    Distance1\n";
    print "      QSO points are computed according to the recent proposal: ";
    print "a QSO with\n      a station in the same grid or one grid away ";
    print "from your location earns\n      two points, a QSO with a station ";
    print "further away earns three points,\n      regardless of band.  ";
    print "Multipliers for all categories are computed as\n      they are ";
    print "under the current rules.\n";
    print "\n";
    print "    Distance1+OldRoverRules\n";
    print "      QSO points are computed according to the recent proposal: ";
    print "a QSO with\n      a station in the same grid or one grid away ";
    print "from your location earns\n      two points, a QSO with a station ";
    print "further away earns three points,\n      regardless of band.  ";
    print "Multipliers for stations in the rover category\n      are ";
    print "computed according to the old rover rules.\n";
    print "\n";
    print "    Distance1+OldRoverRules+1ptRoverQSOs\n";
    print "      QSO points are computed according to the recent proposal: ";
    print "a QSO with\n      a station in the same grid or one grid away ";
    print "from your location earns\n      two points, a QSO with a station ";
    print "further away earns three points,\n      regardless of band, ";
    print "except that all QSOs with rover stations earn\n      one point, ";
    print "regardless of band.  Multipliers for stations in the rover\n      ";
    print "category are computed according to the old rover rules.\n";
    print "\n";
    print "    Current+LowBandsOnly\n";
    print "      QSO points are computed as they are in the current rules, ";
    print "except that\n      only QSOs made on the 50 MHz, 144 MHz, 222 ";
    print "MHz, 432 MHz, 902 MHz, and\n      1.2 GHz bands count for QSO ";
    print "point or multiplier credit.\n";
    print "\n";
    print "    1ptQSOs+LowBandsOnly\n";
    print "      All QSOs count one point, regardless of band or distance, ";
    print "and only\n      QSOs made on the 50 MHz, 144 MHz, 222 MHz, ";
    print "432 MHz, 902 MHz, and\n      1.2 GHz bands count for QSO point ";
    print "or multiplier credit.\n";
    print "\n";

    print "Usage:\n\n";
    print "  $0 < logfile.cbr > report.csv\n\n";
    print "Options:\n\n";
    print "  -v, -verbose, --verbose:\n";
    print "    Verbose output: include contest, category, ARRL section ";
    print "(default.)\n\n";
    print "  -q, -quiet, --quiet:\n";
    print "    Reduced output: no contest, category, or ARRL section.\n\n";
    print "  -c, -comma, --comma:\n";
    print "    Separate output with commas (default.)\n\n";
    print "  -s, -space, --space:\n";
    print "    Separate output with spaces.\n\n";
    print "  -h, -help, --help:\n";
    print "    Print this help message.\n\n";

    print "Verbose example output:\n";
    print "  2003-Jun,WM5R,R,STX,49560,96229,95062,88680,172187,";
    print "169156,49560,34920\n\n";

    print "Quiet example output:\n";
    print "  WM5R,49560,96229,95062,88680,172187,169156,49560,34920\n\n";

    print "Contact:\n";
    print "  Ken Harker WM5R\n";
    print "  wm5r@arrl.net\n\n";
    exit(1);
                                                                               
}


###############################################################################
###############################################################################
##                                                                           ##
##  End of rescore.pl                                                        ##
##                                                                           ##
###############################################################################
###############################################################################
