# Crystals, Oscillators & ceramic resonators. All freq in MHz # TODO: find way to specify tolerance: currently just using stated frequency # and can count number of zeros... # TODO: Modify to allow M or K to be decimal point to specify other freq ranges # Oscillators (cans) OSC 20.0000 OSC 28.3220 OSC 42.0000 OSC 36.0000 OSC 36.00000 OSC 50.000 OSC 60.0 # Bare crystals XTAL .032768 XTAL .0384 XTAL 1.8432 XTAL 3.58 (tiny) XTAL 4.00 (tiny) XTAL 4.5 XTAL 7.3728 XTAL 6.000 XTAL 6.5536 XTAL 8.0000 XTAL 14.31818 XTAL 20.000 XTAL 27.115 XTAL 27.125 XTAL 30.000 XTAL 31 # UNKNOWNS: # BOMAF C-6 1W14T | MEW 31 PU47931-2 # A set marked 301-502 on side, top: (14,4,3,2,-1,-5,-8,-9,-11,-12,-13) # Ceramic resonators RES 4.000 RES 8.000 RES 7.37 # ? "737 Cm 219" RES 10.7 RES 16.93 #### actualFreq (error) = startFreq / divisor "div =" factors #### $ ./find_xtal.pl 175k k5 175,000 ( 0) = 42,000,000 / 240 div = 2^4 * 3 * 5 = 27,125,000 / 155 div = 5 * 31 174,935 ( 65) = 27,115,000 / 155 div = 5 * 31 174,927 ( 73) = 60,000,000 / 343 div = 7^3 175,141 ( 141) = 31,000,000 / 177 div = 3 * 59 174,827 ( 173) = 28,322,000 / 162 div = 2 * 3^4 174,825 ( 175) = 50,000,000 / 286 div = 2 * 11 * 13 174,757 ( 243) = 36,000,000 / 206 div = 2 * 103 174,611 ( 389) = 14,318,180 / 82 div = 2 * 41 175,409 ( 409) = 10,700,000 / 61 div = 61 175,438 ( 438) = 20,000,000 / 114 div = 2 * 3 * 19 = 30,000,000 / 171 div = 3^2 * 19 = 60,000,000 / 342 div = 2 * 3^2 * 19 = 50,000,000 / 285 div = 3 * 5 * 19 174,536 ( 464) = 16,930,000 / 97 div = 97 175,476 ( 476) = 7,370,000 / 42 div = 2 * 3 * 7 #### #!/usr/bin/perl my $usage = < [] Find the crystal (and integral divisor(s)) to come within Hz of . Specify frequency as #.# or #m# for MHz, or #k# for kHz. If you don't specify , 5% is assumed. EOHDR # # 20130216 MCMason: Added tolerance, sort by error (asc) and funkiness within error. # Find *all* divs within TOL range # 20120429 MCMason: original version # # TODO: Trim report: If there are multiple ways to get same frequency, keep only # the top N of best one(s) (based on funkiness) # TODO: Improve funkiness calculation: e.g. try to get something proportional # to # dividers & difficulty. Perhaps something like $e * (log($f)/log(2)) # use strict; use warnings; use autodie; my $dbg_funkiness=0; my $Freq = shift or die $usage; $Freq = txt_2_MHz($Freq); my $Tol = shift; if (defined $Tol) { $Tol = txt_2_MHz($Tol); } else { $Tol = $Freq * .05; } my @crystals = read_crystals(); my @results; sub compute { my ($Fx, $d) = @_; return undef unless $d; my $f = int($Fx / $d); my $err = abs($Freq - $f); return undef if $err > $Tol; my $ar = [ factorize($d) ]; my $funk = factor_funkiness(@$ar); return [ $Fx, $d, $f, $err, $ar, $funk ]; } for my $Fx (@crystals) { # 'perfect' divisor my $div = $Fx / $Freq; # Surrounding integral divisors my $d = int $div; while (my $ar = compute($Fx, $d)) { last if ! defined $ar; my ($xFx, $xd, $xf, $xerr, $xar, $xfunk) = @$ar; print "Fx:$xFx, d:$xd, f:$xf, err:$xerr, funk:$xfunk (div:$div)\n" if $dbg_funkiness; push @results, $ar; --$d; } $d = int $div+1; while (my $ar = compute($Fx, $d)) { last if ! defined $ar; my ($xFx, $xd, $xf, $xerr, $xar, $xfunk) = @$ar; print "Fx:$xFx, d:$xd, f:$xf, err:$xerr, funk:$xfunk (div:$div)\n" if $dbg_funkiness; push @results, $ar; ++$d; } } print "\n\n"; @results = sort { $$a[3] <=> $$b[3] or $$a[5] <=> $$b[5] } @results; my $prev_freq = -1; for my $ar (@results) { my $funk = $dbg_funkiness ? " \tfunkiness=$$ar[5]" : ""; if ($prev_freq ne $$ar[2]) { printf "%11.11s (%7s) = %11s / %4s div = %-16s$funk\n", commify($$ar[2]), commify($$ar[3]), commify($$ar[0]), commify($$ar[1]), join(" * ", @{$$ar[4]}); $prev_freq = $$ar[2]; } else { printf " = %11s / %4s div = %-16s$funk\n", commify($$ar[0]), commify($$ar[1]), join(" * ", factorize($$ar[1])); } } sub txt_2_MHz { my $t = shift; my $orig = $t; # Multiplier (default is MHz for /\d+\.\d*/) my $mult = 1000000; if ($t =~ /m/i) { # 1M8432 => 1,843,000 $t =~ s/m/./; $mult = 1000000; } elsif ($t =~ /k/i) { # 32k768 => 32,768 $t =~ s/k/./; $mult = 1000; } if ($t =~ /\d+(\.\d*)?|\.\d+/) { return $t * $mult; } die "txt_2_MHz: Unexpected input '$orig' => '$t', can't determine frequency."; } sub read_crystals { open my $FH, '<', 'crystals.txt'; my %freqs; while (<$FH>) { next if /^\s*#/; next if /^\s*$/; if (/([.0-9]+)/) { $freqs{$1*1_000_000}=0; } } return sort { $a <=> $b } keys %freqs; } sub commify { my $s = shift; $s =~ s/(\d)(\d\d\d)$/$1,$2/; $s =~ s/(\d)(\d\d\d,)/$1,$2/g; return $s; } sub factor_funkiness { my @factors = @_; my $funkiness = 0; print "funkiness(",join(", ", @factors),")\n" if $dbg_funkiness; for my $t (@factors) { my ($f, $e) = split /\^/, $t; $e=1 if ! defined $e; if ($f == 2) { $funkiness += .05*$e; # no change } elsif ($f == 3) { $funkiness += 0.5 * $e; } elsif ($f == 5) { $funkiness += 0.6 * $e; } elsif ($f < 32) { $funkiness += 1 * $e; } else { $funkiness += 2 * $e; } print "\t$t => $funkiness\n" if $dbg_funkiness; } print "\tfinal == $funkiness\n" if $dbg_funkiness; return $funkiness; } sub factorize { my $num = shift; my @factors = (); my $factor=2; my $exp=0; while ($num%$factor == 0) { ++$exp; $num /= $factor; } if ($exp > 1) { push @factors, "$factor^$exp" } elsif ($exp) { push @factors, $factor } $factor=3; while ($factor*$factor <= $num) { $exp = 0; while ($num%$factor == 0) { ++$exp; $num /= $factor; } if ($exp > 1) { push @factors, "$factor^$exp" } elsif ($exp) { push @factors, $factor } $factor += 2; } push @factors, $num if $num>1; return @factors; }