Skip to content

Instantly share code, notes, and snippets.

@alexymik
Last active May 17, 2017 22:54
Show Gist options
  • Save alexymik/825e3dc27dbde382d367a8667d894453 to your computer and use it in GitHub Desktop.
Save alexymik/825e3dc27dbde382d367a8667d894453 to your computer and use it in GitHub Desktop.
Ruby Class/script to decode METAR Reports
#!/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