Last active
May 17, 2017 22:54
-
-
Save alexymik/825e3dc27dbde382d367a8667d894453 to your computer and use it in GitHub Desktop.
Ruby Class/script to decode METAR Reports
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/ruby | |
unless ARGV.any? | |
puts 'Usage: metar.rb <paste your METAR report here>' | |
exit | |
end | |
# Example METAR report: | |
# METAR KITH 162256Z 15006KT 10SM BKN080 19/08 A2997 RMK AO2 SLP149 T01940078 | |
class Metar | |
require 'time' | |
# General storage fields | |
attr_accessor :original, :metar | |
# METAR specific fields | |
attr_accessor :airport_code, :time, :wind, :visibility, | |
:weather, :clouds, :temp_dew, :altimeter, :remarks | |
def initialize(metar) | |
raise ArgumentError, 'Expected METAR to be a String' unless metar.instance_of? String | |
self.original = metar.upcase | |
self.metar = metar.upcase.split | |
remove_report_type | |
# raise ArgumentError, 'Incomplete METAR report' if report_incomplete? | |
end | |
def print_report | |
puts "#{report_type} Weather report for #{airport_code}" | |
puts "Observed at #{report_time}" | |
puts "Wind #{combined_wind_speed} #{wind_direction_cardinal} (#{wind_direction} deg.)" | |
puts "Visibility #{visibility_distance} #{visibility_units}" | |
puts "Clouds: #{clouds}" | |
puts "Temperature: #{temperature}C - Dew Point #{dew_point}C" | |
puts "Altimeter: #{pressure}Hg" | |
end | |
def report_type | |
if %w[SPECI METAR].include? original.split.first | |
original.split.first | |
else | |
'UNKNOWN' | |
end | |
end | |
def report_type_unknown? | |
report_type == 'UNKNOWN' | |
end | |
def report_incomplete? | |
metar.count < 11 | |
end | |
def airport_code | |
# KITH | |
@airport_code || metar.first | |
end | |
def report_time | |
# 162256Z | |
Time.strptime(metar[1].sub('Z', '+0000'), '%d%H%M%z') | |
end | |
def wind | |
# 15006KT | |
metar[2] | |
end | |
def combined_wind_speed | |
if wind_gusting? | |
"#{wind_speed} #{wind_units} with gusts up to #{wind_gust_speed} #{wind_units}" | |
else | |
wind_speed | |
end | |
end | |
def wind_speed | |
wind[3, 2].to_i | |
end | |
def wind_units | |
speed_to_english wind[-2, 2] | |
end | |
def wind_gusting? | |
!wind_gust.nil? | |
end | |
def wind_gust | |
metar[2].match(/(G\d+)/).to_s | |
end | |
def wind_gust_speed | |
wind_gust.match(/(\d+)/).to_s | |
end | |
def wind_direction | |
wind[0, 3].to_i | |
end | |
def wind_direction_cardinal | |
if wind_direction > 315 || wind_direction < 45 | |
'N' | |
elsif wind_direction == 45 | |
'NE' | |
elsif wind_direction > 45 && wind_direction < 135 | |
'E' | |
elsif wind_direction == 135 | |
'SE' | |
elsif wind_direction > 135 && wind_direction < 225 | |
'S' | |
elsif wind_direction == 225 | |
'NE' | |
elsif wind_direction > 225 && wind_direction < 315 | |
'W' | |
elsif wind_direction == 315 | |
'NE' | |
else 'U' | |
end | |
end | |
def visibility | |
# 10SM | |
metar[3] | |
end | |
def visibility_distance | |
visibility[0...-2] | |
end | |
def visibility_units | |
speed_to_english visibility[-2, 2] | |
end | |
def clouds | |
metar[4] | |
end | |
def temperature | |
temperature_dew_point.match(/^\d+/).to_s | |
end | |
def dew_point | |
temperature_dew_point.match(/\d+$/).to_s | |
end | |
def altimeter | |
metar[6] | |
end | |
def pressure | |
altimeter.match(/\d+$/).to_s.to_f / 100 | |
end | |
private | |
def temperature_dew_point | |
metar[5] | |
end | |
def speed_to_english(acronym) | |
case acronym | |
when 'KT' then 'Knots' | |
when 'SM' then 'Miles' | |
else '' | |
end | |
end | |
def precipitation_to_english(acronym) | |
case acronym | |
when 'DZ' then 'drizzle' | |
when 'RA' then 'rain' | |
when 'SN' then 'snow' | |
when 'SG' then 'snow grains' | |
when 'IC' then 'ice crystals' | |
when 'PE' then 'ice pellets' | |
when 'GR' then 'hail' | |
when 'GS' then 'small hail/snow pellets' | |
when 'UP' then 'unknown' | |
else 'unknown' | |
end | |
end | |
def remove_report_type | |
unless report_type_unknown? | |
self.metar = original.split | |
self.metar.shift | |
end | |
end | |
end | |
Metar.new(ARGV.join(' ')).print_report |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment