#!/usr/bin/perl

$version= "2.0";

# Xtalfind - by Paolo Cravero, IK1ZYW

# Version 2.0b
# Edited by KE0BFF for US Amateur Radio Band Plans
# ITU Region 2


# MAIN

# TODO: Print Band in results
# 	Select calculationsfor individual designs: DC RX, Single or Double Conversion RX, Heterodyning TX, etc
#       Calculate Crystal Images for a given VFO/LO's min & max frequencies (i.e. - 9.000Mhz Xtal Mixed with 5.0 to 5.4Mhz VFO)
#								Maybe Option for VFO.min & VFO.max & calculate all xtals that fall in hamband
#								or Calculate the min & max VFO to cover the band the xtals fundamental falls within
#								obvious logistics to the actual design process needs to be worked out prior to make
#							        any above ideas viable.

use Getopt::Long;
require IO::File;

my $arm=10; # number of harmonics to check against to
my $quartz=10000000; # default quartz frequency 10MHz
my $mix=1; # not used
my $if=0; # extra IF value to check; 0=skip
my $sampleR=48000; #defalt SDRadio sampling rate, 48kHz

GetOptions (	"help",
		"arm=i" => \$arm,
		"quartz=i" => \$quartz,
		"file=s" => \$infile,
		"sample=i" => \$sampleR,
		"if=i" => \$if);

		
# TODO: Band Selection
#       Heterodyne TX unages calculate by integer
#       Calculate if impractical images may results


if ( $opt_help ) {
	printbanner();
	print "\nXtalFind 2.0b - Customized for US Amateur Radio Ham Bands";
	print "\nUsage: xtalfind.pl [-quartz <n>|-file <filename>] [-arm <n>] [-if <n>] [-sample <n>]\n\n";
	print "Valid options:\n";
	print "-help      	 		Get (this) help\n";
	print "-quartz=<number>		XTAL frequency in Hz (default 10000000 Hz)\n";
	print "-file=<filename>		Read XTAL list from file\n";
	print "-arm=<number>			Check matches up to -arm harmonic (default 10)\n";
	print "-if=<number>			Also check against a custom IF value, in Hz\n";
	print "-sample=<number>		Soundcard sample rate for SDR (default 48000 Hz)\n";
	print "\n\n";
	exit;
}

# Check if the DRMfreq.txt list file is present
# if not, ignore DRM tests
$ff = new IO::File;
if ($ff->open("< DRMfreq.txt")) {
        # We've go the file, load it up
        $/="\n";
        $q=0;
        while (<$ff>) {
                chomp;
                $qrg[$q++] = $_;
        }
        $ff->close();
	$haveDRM=1;
} else {
	$haveDRM=0;
}


# Do we have an input file?
if ($infile) {
	$/="\n";
	$fh = new IO::File;
	
	# Can I open the file?
	if ($fh->open("< $infile")) {
		printbanner();
		while (<$fh>) {
			$quartz=$_;
			submain();
		}
	# OPS! File not found! Bailing out...
	} else {
		printbanner();
		printf("ERROR: could not open file $infile ... Aborting.\n\n");
		
	}
# A file name was not provided, going into single XTAL mode
} else {
	printbanner();
	submain();
}


# *** TODO: insert here parameters validity check


sub submain() {

my $i;

# TODO: Print the Band that the results fall within

printf("\n------------------------------------------------------\n");
printf("      ******* Checking %d Hz quartz *******\n",$quartz);
printf("------------------------------------------------------\n");
printf("\n>>> Results for a TX or a direct-conversion RX <<<\n");
$i=comp($quartz,0,$arm);

printf("\n>>> Results for an RX with 455kHz IF <<<\n");
$i=comp($quartz,455000,$arm);

printf("\n>>> Results for an RX with 10.7MHz IF <<<\n");
$i=comp($quartz,10700000,$arm);

#printf("\n>>> Results for an RX with 12kHz IF <<<\n");
#$i=comp($quartz,12000,$arm);

if ($if) {
	print "\n>>> Results for an RX with $if Hz IF <<<\n";
	$i=comp($quartz,$if,$arm);
}

#printf("\n>>> Results for a Software Defined Radio <<<\n");
#printf(">>>  Johnson counter - Center frequency  <<<\n");
#$i=SDcomp($quartz,$arm,$sampleR);

#printf("\n>>> Results for a DRM 455-12kHz Converter <<<\n");
#$i=DRMcomp($quartz,455000,32);

#printf("\n>>> Results for a DRM 9011-12kHz Converter <<<\n");
#$i=DRMcomp($quartz,9011000,32);
#
#if ($haveDRM) {
#	print "\n>>> Results for a direct-conversion DRM RX <<<\n";
#	foreach $line (@qrg) {
#        	$i=DRMcomp($quartz,$line*1000,$arm);
#	}
#} # end IF haveDRM

printf("\n------------------------------------------------------\n");
1;


# END MAIN
}


sub DRMcomp() {

  my $quartz = shift;
  my $if = shift;
  my $arm = shift;

  my $i;
  my $res;

  # check harmonics
  for ($i=1;$i<=$arm;$i++) {
    $res=$quartz*$i-$if;
    if (($res<20000)&&($res>-20000)) {print "".($quartz/1)." at $i harmonic brings to ".abs($res)." Hz\n";}
    $res=$quartz/$i-$if;
    if (($res<20000)&&($res>-20000)) {print "".($quartz/1)." divided by $i brings to ".abs($res)." Hz\n";}
  }
}


sub SDcomp() {

  my $quartz = shift;
#  my $iff = shift;
  my $arm = shift;
  my $samplerate = shift;

  my $i;
  my $res;

  # check harmonics
  for ($i=1;$i<=$arm;$i++) {
    $res=inband($i*$quartz/4);
    if ($res) {print "It works on $res Hz, at $i harmonic (".($quartz*$i/1000000)." MHz), covering ".($res-($samplerate/2))."-".($res+($samplerate/2))." Hz\n";}
  }
}


sub DRMcomp() {

  my $quartz = shift;
  my $if = shift;
  my $arm = shift;

  my $i;
  my $res;

  # check harmonics
  for ($i=1;$i<=$arm;$i++) {
    $res=$quartz*$i-$if;
    if (($res<20000)&&($res>-20000)) {print "At $i harmonic brings ".($if/1000)." kHz to ".abs($res)." Hz\n";}
    $res=$quartz/$i-$if;
    if (($res<20000)&&($res>-20000)) {print "Divided by $i brings ".($if/1000)." kHz to ".abs($res)." Hz\n";}
  }
}


sub comp() {

  my $quartz = shift;
  my $iff = shift;
  my $arm = shift;

  my $i;
  my $res;

  # check harmonics
  for ($i=1;$i<=$arm;$i++) {
    $res=inband($i*$quartz+$iff);
    if ($res) {print "It works on $res Hz, at $i harmonic\n";}
    if ($iff) {
      $res=inband($i*$quartz-$iff);
      if ($res) {print "It works on $res Hz, at $i harmonic\n";}
    }
  }

  # check for sub-harmonics
  for ($i=2;$i<=$arm;$i=$i+1) {
    $res=inband($quartz/$i+$iff);
    if ($res) {printf("It works on %d Hz, if divided by %d\n",$res,$i);}
    if ($iff) {
    	$res=inband($quartz/$i-$iff);
        if ($res) {printf("It works on %d Hz, if divided by %d\n",$res,$i);}
   }
  }

  return 0;

}



sub inband() {

  my $qrg = shift;

#  /* Here you can add or customize frequency bands for which *
#   * the software will check your XTALs                      */

# 160 Meters
  if (($qrg>=1800000)&($qrg<=2000000)) {
    return $qrg;

# 80 Meters    
  }  if (($qrg>=3500000)&($qrg<=4000000)) {
    return $qrg;

# ***TODO: 60 Meters (5 Channels: 5332.0, 5348.0, 5358.5, 5373.0, 5405.0 kHz)
# Not input as range but as individual channels
    
# 40 Meters    
  }
  if (($qrg>=7000000)&($qrg<=7300000)) {
    return $qrg;
    
# 30 Meters    
  }
  if (($qrg>=10100000)&($qrg<=10150000)) {
    return $qrg;
    
# 20 Meters    
  }
  if (($qrg>=14000000)&($qrg<=14350000)) {
    return $qrg;
    
# 17 Meters    
  }
  if (($qrg>=18068000)&($qrg<=18168000)) {
    return $qrg;
  
# 15 Meters  
  }
  if (($qrg>=21000000)&($qrg<=21450000)) {
    return $qrg;
    
# 12 Meters    
  }
  if (($qrg>=24890000)&($qrg<=24990000)) {
    return $qrg;
    
# 10 Meters    
  }
  if (($qrg>=28000000)&($qrg<=29700000)) {
    return $qrg;
 
# 6 Meters 
  }
  if (($qrg>=50000000)&($qrg<=54000000)) {
    return $qrg;
    
# 2 Meters    
  }
  if (($qrg>=144000000)&($qrg<=148000000)) {
    return $qrg;
    
# 70 Centimeters    
  }
  if (($qrg>=420000000)&($qrg<=450000000)) {
    return $qrg;
  }

  return 0;
}


# Print output heading
sub printbanner() {
                printf("\n         xtalfind ik1zyw's XTAL utility");
                printf("\n         based on original version: $version\n\n");
                printf("\n	   US Version - ITU Region 2");
}

