zeroclickinfo-goodies/lib/DDG/Goodie/FrequencySpectrum.pm

252 lines
8.5 KiB
Perl

package DDG::Goodie::FrequencySpectrum;
# ABSTRACT: describe the nature of various wave frequencies.
use strict;
use DDG::Goodie;
triggers end => "hz","khz","mhz","ghz","thz","hertz","kilohertz","gigahertz","megahertz","terahertz";
zci answer_type => "frequency_spectrum";
zci is_cached => 1;
primary_example_queries '50 hz';
secondary_example_queries '400 thz';
description 'Returns information about light, radio, and sound frequencies';
name 'FrequencySpectrum';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/FrequencySpectrum.pm';
category 'physical_properties';
topics 'science';
attribution web => "https://machinepublishers.com",
twitter => 'machinepub';
sub THOUSAND { 1000 };
sub MILLION { 1000000 };
sub BILLION { 1000000000 };
sub TRILLION { 1000000000000 };
#reference: https://en.wikipedia.org/wiki/Radio_spectrum
#Radio spectrum ranges along with example uses
my $radio_ranges =
[
[ "3", "29", "ELF band used by pipeline inspection gauges."],
[ "30", "299", "SLF band used by submarine communication systems."],
[ "300", "2999", "ULF band used by mine cave communication systems."],
[ "3000", "29999", "VLF band used by government time stations and navigation systems."],
[ "30000", "299999", "LF band used by AM broadcasts, government time stations, navigation systems, and weather alert systems."],
[ "300000", "2999999", "MF band used by AM broadcasts, navigation systems, and ship-to-shore communication systems."],
[ "3000000", "29999999", "HF band used by international shortwave broadcasts, aviation systems, government time stations, weather stations, and amateur radio."],
[ "30000000", "299999999", "VHF band used by FM broadcasts, televisions, amateur radio, marine communication systems, and air traffic control."],
[ "300000000", "2999999999", "UHF band used by televisions, cordless phones, cell phones, pagers, walkie-talkies, and satellites."],
[ "3000000000", "29999999999", "SHF band used by microwave ovens, wireless LANs, cell phones, and satellites."],
[ "30000000000", "299999999999", "EHF band used by radio telescopes, security screening systems, and point-to-point high-bandwidth devices."],
[ "300000000000", "3000000000000", "THF band used by satellites and radio telescopes."],
];
#reference: https://en.wikipedia.org/wiki/Color
#Color ranges. Some colors are controversial but these are fairly well accepted.
my $color_ranges =
[
[ "400000000000000", "479999999999999", "red" ],
[ "480000000000000", "504999999999999", "orange" ],
[ "505000000000000", "524999999999999", "yellow" ],
[ "525000000000000", "574999999999999", "green" ],
[ "575000000000000", "609999999999999", "cyan" ],
[ "610000000000000", "667999999999999", "blue" ],
[ "668000000000000", "714999999999999", "indigo" ],
[ "715000000000000", "800000000000000", "violet" ],
];
# reference: https://en.wikipedia.org/wiki/Musical_acoustics
#Ranges for common instruments
my $instrument_ranges =
[
[ "87", "1046", "human voice" ],
[ "82.407", "329.63", "bass vocalists" ],
[ "87.307", "349.23", "baritone vocalists" ],
[ "130.81", "440.00", "tenor vocalists" ],
[ "196.00", "698.46", "alto vocalists" ],
[ "220.00", "880.00", "mezzo-soprano vocalists" ],
[ "261.63", "880.00", "soprano vocalists" ],
[ "41.203", "523.25", "double-bass" ],
[ "130.81", "1760.00", "viola" ],
[ "196.00", "2637.00", "violin" ],
[ "82.41", "1046.5", "guitar" ],
[ "196.00", "1396.9", "mandolin" ],
[ "130.81", "1046.5", "banjo" ],
[ "27.500", "4186.0", "piano" ],
[ "38.891", "440.00", "tuba" ],
[ "82.407", "523.25", "trombone" ],
[ "164.81", "932.33", "trumpet" ],
[ "207.65", "1244.5", "saxophone" ],
[ "261.63", "2093.0", "flute" ],
[ "146.83", "1864.7", "clarinet" ],
[ "58.270", "783.99", "bassoon" ],
[ "233.08", "1760.0", "oboe" ],
];
# Reference: https://en.wikipedia.org/wiki/Ultraviolet
my $ultraviolet_ranges =
[
[ 7.495*(10**14), 3*(10**16), "UV light is found in sunlight and is emitted by electric arcs and specialized lights such as mercury lamps and black lights." ],
];
# Reference: https://en.wikipedia.org/wiki/X-ray
my $xray_ranges =
[
[ 3*(10**16), 3*(10**19), "X-rays are used for various medical and industrial uses such as radiographs and CT scans. "],
];
# Reference:
my $gamma_ranges =
[
[ 10**19, 10**24, "Gamma rays are can be used to treat cancer and for diagnostic purposes." ],
];
#Query is intitially processed here. First normalize the query format,
#normalize the units, and then calculate information about the frequency range.
handle query => sub {
return unless $_ =~ m/^[\d,.]+\s\w+$/;
return unless my $freq = normalize_freq($_);
my $freq_hz;
my $hz_abbrev;
my $freq_formatted;
if($freq =~ m/^(.+?)\s(?:hz|hertz)$/i) {
$freq_hz = $1;
} elsif($freq =~ m/^(.+?)\s(?:khz|kilohertz)$/i) {
$freq_hz = $1 * THOUSAND;
} elsif($freq =~ m/^(.+?)\s(?:mhz|megahertz)$/i) {
$freq_hz = $1 * MILLION;
} elsif($freq =~ m/^(.+?)\s(?:ghz|gigahertz)$/i) {
$freq_hz = $1 * BILLION;
} elsif($freq =~ m/^(.+?)\s(?:thz|terahertz)$/i) {
$freq_hz = $1 * TRILLION;
} else {
#unexpected case
return;
}
if($freq_hz >= TRILLION){
$hz_abbrev = "THz";
$freq_formatted = $freq_hz / TRILLION;
} elsif($freq_hz >= BILLION) {
$hz_abbrev = "GHz";
$freq_formatted = $freq_hz / BILLION;
} elsif($freq_hz >= MILLION) {
$hz_abbrev = "MHz";
$freq_formatted = $freq_hz / MILLION;
} elsif($freq_hz >= THOUSAND) {
$hz_abbrev = "kHz";
$freq_formatted = $freq_hz / THOUSAND;
} else {
$hz_abbrev = "Hz";
$freq_formatted = $freq_hz;
}
$freq = $freq_formatted . " " . $hz_abbrev;
return prepare_result($freq, $freq_hz);
};
#Normalize the frequency, attempting to discern between region differences
#in number formatting. Filter out clearly invalid queries.
sub normalize_freq{
my $freq = $_;
if($freq =~ /(\d+\.){2,}|([.]{2,})|([,]{2,})/) {
return;
}
# Remove commas.
$freq =~ s/,//g;
return $freq;
};
#Take the frequency and look at which ranges it falls in.
#Build up the result string.
sub prepare_result {
my $freq = $_[0];
my $freq_hz = $_[1];
my $color = match_in_ranges(int($freq_hz), $color_ranges);
my $radio = match_in_ranges(int($freq_hz), $radio_ranges) unless $color;
my $instruments = matches_in_ranges($freq_hz, $instrument_ranges) unless $color;
my $ultraviolet = matches_in_ranges($freq_hz, $ultraviolet_ranges);
my $xray = matches_in_ranges($freq_hz, $xray_ranges);
my $gamma = matches_in_ranges($freq_hz, $gamma_ranges);
my $text_result = "";
my $more_at = '';
if($radio) {
$text_result = $freq . " is a radio frequency in the " . $radio;
$more_at = 'https://en.wikipedia.org/wiki/Radio_spectrum';
} elsif($color) {
$text_result = $freq . " is an electromagnetic frequency of " . $color . " light.";
$more_at = 'https://en.wikipedia.org/wiki/Color';
}
if($instruments) {
$more_at = 'https://en.wikipedia.org/wiki/Musical_acoustics';
$instruments =~ s/,\s([a-zA-Z\s-]+)$/, and $1/;
if($radio) {
$text_result = $text_result . "\n" . $freq . " is also an audible frequency which can be produced by " . $instruments . ".";
} else {
$text_result = $freq . " is an audible frequency which can be produced by " . $instruments . ".";
}
}
if($ultraviolet) {
$more_at = 'https://en.wikipedia.org/wiki/Ultraviolet';
$text_result = $ultraviolet;
}
if($xray) {
$more_at = 'https://en.wikipedia.org/wiki/X-ray';
$text_result = $xray;
}
if($gamma) {
$more_at = 'https://en.wikipedia.org/wiki/Gamma_ray';
$text_result = $gamma;
}
if($text_result) {
(my $html_result = $text_result) =~ s/\n/<br>/g;
$html_result .= "<br><a href='$more_at'>More at Wikipedia</a>";
$text_result .= "\nMore at $more_at";
return $text_result, html => $html_result, heading => "$freq (Frequency Spectrum)";
}
return;
};
#Find which single range applies.
sub match_in_ranges {
my $freq = $_[0];
my $ranges = $_[1];
foreach my $range (@$ranges) {
if($freq >= $range->[0] && $freq <= $range->[1]){
return $range->[2];
}
}
return "";
};
#Find any number of ranges which apply.
sub matches_in_ranges {
my $freq = $_[0];
my $ranges = $_[1];
my @matches;
foreach my $range (@$ranges) {
if($freq >= $range->[0] && $freq <= $range->[1]) {
push(@matches, $range->[2]);
}
}
return join(", ", @matches);
};
1;