metar-parser 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +7 -6
- data/lib/metar/data.rb +34 -29
- data/lib/metar/parser.rb +73 -132
- data/lib/metar/raw.rb +6 -0
- data/lib/metar/report.rb +5 -0
- data/lib/metar/version.rb +1 -1
- data/locales/en.yml +2 -0
- data/locales/it.yml +2 -0
- data/spec/unit/parser_spec.rb +22 -6
- data/spec/unit/report_spec.rb +5 -2
- data/spec/unit/runway_visible_range_spec.rb +2 -1
- data/spec/unit/sky_condition_spec.rb +6 -4
- data/spec/unit/visibility_spec.rb +4 -3
- data/spec/unit/weather_phenomenon_spec.rb +4 -1
- metadata +35 -18
data/README.md
CHANGED
@@ -63,15 +63,10 @@ Use Your Own Raw Data
|
|
63
63
|
---------------------
|
64
64
|
```ruby
|
65
65
|
metar_string = "KHWD 280554Z AUTO 29007KT 10SM OVC008 14/12 A3002 RMK AO2 SLP176 T01390117 10211\n"
|
66
|
-
raw = Metar::Raw.new( metar_string )
|
66
|
+
raw = Metar::Raw::Data.new( metar_string )
|
67
67
|
parser = Metar::Parser.new( raw )
|
68
68
|
```
|
69
69
|
|
70
|
-
Implementation
|
71
|
-
==============
|
72
|
-
|
73
|
-
* Parses METAR strings using a state machine.
|
74
|
-
|
75
70
|
Changelog
|
76
71
|
=========
|
77
72
|
|
@@ -84,6 +79,12 @@ The old functionality has been moved to Metar::Raw::Noaa. The new class,
|
|
84
79
|
Metar::Raw::Data accepts a METAR string as a parameter - allowing the user to
|
85
80
|
parse METAR strings without necessarily contacting the NOAA.
|
86
81
|
|
82
|
+
Contributors
|
83
|
+
============
|
84
|
+
|
85
|
+
* [Joe Yates](https://github.com/joeyates)
|
86
|
+
* [Derek Johnson](https://github.com/EpicDraws)
|
87
|
+
|
87
88
|
Alternative Software
|
88
89
|
====================
|
89
90
|
|
data/lib/metar/data.rb
CHANGED
@@ -193,7 +193,7 @@ module Metar
|
|
193
193
|
new( Distance.new( 10000 ), nil, :more_than )
|
194
194
|
when s =~ /(\d{4})NDV/ # WMO
|
195
195
|
new( Distance.new( $1.to_f ) ) # Assuming meters
|
196
|
-
when (s =~ /^((1|2)\s|)([
|
196
|
+
when (s =~ /^((1|2)\s|)([1357])\/([248]|16)SM$/) # US
|
197
197
|
miles = $1.to_f + $3.to_f / $4.to_f
|
198
198
|
distance = Distance.miles( miles )
|
199
199
|
distance.units = :miles
|
@@ -208,10 +208,10 @@ module Metar
|
|
208
208
|
new( distance, nil, :less_than )
|
209
209
|
when s =~ /^(\d+)KM$/
|
210
210
|
new( Distance.kilometers( $1 ) )
|
211
|
-
when s =~ /^(\d+)$/ #
|
212
|
-
new( Distance.
|
211
|
+
when s =~ /^(\d+)$/ # We assume meters
|
212
|
+
new( Distance.new( $1 ) )
|
213
213
|
when s =~ /^(\d+)(N|NE|E|SE|S|SW|W|NW)$/
|
214
|
-
new( Distance.
|
214
|
+
new( Distance.meters( $1 ), M9t::Direction.compass( $2 ) )
|
215
215
|
else
|
216
216
|
nil
|
217
217
|
end
|
@@ -263,7 +263,7 @@ module Metar
|
|
263
263
|
distance = Distance.send( units, count )
|
264
264
|
visibility = Visibility.new(distance, nil, comparator)
|
265
265
|
new(designator, visibility, nil, tendency)
|
266
|
-
when runway_visible_range =~ /^R(\d+[RLC]?)\/(P|M|)(\d{4})V(P|M|)(\d{4})(N|U|D)?(FT)
|
266
|
+
when runway_visible_range =~ /^R(\d+[RLC]?)\/(P|M|)(\d{4})V(P|M|)(\d{4})(N|U|D)?(FT|)$/
|
267
267
|
designator = $1
|
268
268
|
comparator1 = COMPARATOR[$2]
|
269
269
|
count1 = $3.to_f
|
@@ -316,9 +316,11 @@ module Metar
|
|
316
316
|
class WeatherPhenomenon
|
317
317
|
|
318
318
|
Modifiers = {
|
319
|
-
'+'
|
320
|
-
'-'
|
321
|
-
'VC'
|
319
|
+
'+' => 'heavy',
|
320
|
+
'-' => 'light',
|
321
|
+
'VC' => 'nearby',
|
322
|
+
'-VC' => 'nearby light',
|
323
|
+
'+VC' => 'nearby heavy',
|
322
324
|
}
|
323
325
|
|
324
326
|
Descriptors = {
|
@@ -350,7 +352,6 @@ module Metar
|
|
350
352
|
'SH' => 'shower',
|
351
353
|
'SN' => 'snow',
|
352
354
|
'SG' => 'snow grains',
|
353
|
-
'SNRA' => 'snow and rain',
|
354
355
|
'SQ' => 'squall',
|
355
356
|
'UP' => 'unknown phenomenon', # => AUTO
|
356
357
|
'VA' => 'volcanic ash',
|
@@ -358,28 +359,24 @@ module Metar
|
|
358
359
|
'SS' => 'sand storm',
|
359
360
|
'DS' => 'dust storm',
|
360
361
|
'TS' => 'thunderstorm',
|
361
|
-
'TSGR' => 'thunderstorm and hail',
|
362
|
-
'TSGS' => 'thunderstorm and small hail',
|
363
|
-
'TSRA' => 'thunderstorm and rain',
|
364
|
-
'TSRA' => 'thunderstorm and snow',
|
365
|
-
'TSRA' => 'thunderstorm and unknown phenomenon', # => AUTO
|
366
362
|
}
|
367
363
|
|
368
364
|
# Accepts all standard (and some non-standard) present weather codes
|
369
365
|
def WeatherPhenomenon.parse(s)
|
370
|
-
|
366
|
+
phenomena = Phenomena.keys.join('|')
|
371
367
|
descriptors = Descriptors.keys.join('|')
|
372
|
-
modifiers
|
368
|
+
modifiers = Modifiers.keys.join('|')
|
373
369
|
modifiers.gsub!(/([\+\-])/) { "\\#$1" }
|
374
|
-
rxp = Regexp.new("^(#{ modifiers })?(#{ descriptors })?(
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
370
|
+
rxp = Regexp.new("^(#{ modifiers })?(#{ descriptors })?((?:#{ phenomena }){1,2})$")
|
371
|
+
m = rxp.match(s)
|
372
|
+
return nil if m.nil?
|
373
|
+
|
374
|
+
modifier_code = m[1]
|
375
|
+
descriptor_code = m[2]
|
376
|
+
phenomena_codes = m[3].scan(/../)
|
377
|
+
phenomena_phrase = phenomena_codes.map{ |c| Phenomena[c] }.join(' and ')
|
378
|
+
|
379
|
+
Metar::WeatherPhenomenon.new(phenomena_phrase, Modifiers[modifier_code], Descriptors[descriptor_code])
|
383
380
|
end
|
384
381
|
|
385
382
|
attr_reader :phenomenon, :modifier, :descriptor
|
@@ -399,7 +396,7 @@ module Metar
|
|
399
396
|
CONDITION = {
|
400
397
|
'CB' => 'cumulonimbus',
|
401
398
|
'TCU' => 'towering cumulus',
|
402
|
-
'///' => nil,
|
399
|
+
'///' => nil, # cloud type unknown as observed by automatic system (15.9.1.7)
|
403
400
|
'' => nil
|
404
401
|
}
|
405
402
|
CLEAR_SKIES = [
|
@@ -413,11 +410,19 @@ module Metar
|
|
413
410
|
case
|
414
411
|
when CLEAR_SKIES.include?( sky_condition )
|
415
412
|
new
|
416
|
-
when sky_condition =~ /^(BKN|FEW|OVC|SCT)(\d
|
413
|
+
when sky_condition =~ /^(BKN|FEW|OVC|SCT)(\d+|\/{3})(CB|TCU|\/{3}|)?$/
|
417
414
|
quantity = QUANTITY[ $1 ]
|
418
|
-
height =
|
419
|
-
|
415
|
+
height =
|
416
|
+
if $2 == '///'
|
417
|
+
nil
|
418
|
+
else
|
419
|
+
Distance.new( $2.to_i * 30.48 )
|
420
|
+
end
|
421
|
+
type = CONDITION[ $3 ]
|
420
422
|
new(quantity, height, type)
|
423
|
+
when sky_condition =~ /^(CB|TCU)$/
|
424
|
+
type = CONDITION[ $1 ]
|
425
|
+
new(nil, nil, type)
|
421
426
|
else
|
422
427
|
nil
|
423
428
|
end
|
data/lib/metar/parser.rb
CHANGED
@@ -1,87 +1,8 @@
|
|
1
|
-
require 'aasm'
|
2
1
|
require File.join(File.dirname(__FILE__), 'data')
|
3
2
|
|
4
3
|
module Metar
|
5
4
|
|
6
5
|
class Parser
|
7
|
-
include AASM
|
8
|
-
|
9
|
-
aasm_initial_state :start
|
10
|
-
|
11
|
-
aasm_state :start, :after_enter => :seek_location
|
12
|
-
aasm_state :location, :after_enter => :seek_datetime
|
13
|
-
aasm_state :datetime, :after_enter => [:seek_cor_auto, :seek_wind]
|
14
|
-
aasm_state :wind, :after_enter => :seek_variable_wind
|
15
|
-
aasm_state :variable_wind, :after_enter => :seek_visibility
|
16
|
-
aasm_state :visibility, :after_enter => :seek_runway_visible_range
|
17
|
-
aasm_state :runway_visible_range, :after_enter => :seek_present_weather
|
18
|
-
aasm_state :present_weather, :after_enter => :seek_sky_conditions
|
19
|
-
aasm_state :sky_conditions, :after_enter => :seek_vertical_visibility
|
20
|
-
aasm_state :vertical_visibility, :after_enter => :seek_temperature_dew_point
|
21
|
-
aasm_state :temperature_dew_point, :after_enter => :seek_sea_level_pressure
|
22
|
-
aasm_state :sea_level_pressure, :after_enter => :seek_remarks
|
23
|
-
aasm_state :remarks, :after_enter => :seek_end
|
24
|
-
aasm_state :end
|
25
|
-
|
26
|
-
aasm_event :location do
|
27
|
-
transitions :from => :start, :to => :location
|
28
|
-
end
|
29
|
-
|
30
|
-
aasm_event :datetime do
|
31
|
-
transitions :from => :location, :to => :datetime
|
32
|
-
end
|
33
|
-
|
34
|
-
aasm_event :wind do
|
35
|
-
transitions :from => :datetime, :to => :wind
|
36
|
-
end
|
37
|
-
|
38
|
-
aasm_event :cavok do
|
39
|
-
transitions :from => :variable_wind, :to => :sky_conditions
|
40
|
-
end
|
41
|
-
|
42
|
-
aasm_event :variable_wind do
|
43
|
-
transitions :from => :wind, :to => :variable_wind
|
44
|
-
end
|
45
|
-
|
46
|
-
aasm_event :visibility do
|
47
|
-
transitions :from => [:wind, :variable_wind], :to => :visibility
|
48
|
-
end
|
49
|
-
|
50
|
-
aasm_event :runway_visible_range do
|
51
|
-
transitions :from => [:visibility], :to => :runway_visible_range
|
52
|
-
end
|
53
|
-
|
54
|
-
aasm_event :present_weather do
|
55
|
-
transitions :from => [:runway_visible_range],
|
56
|
-
:to => :present_weather
|
57
|
-
end
|
58
|
-
|
59
|
-
aasm_event :sky_conditions do
|
60
|
-
transitions :from => [:present_weather, :visibility, :sky_conditions],
|
61
|
-
:to => :sky_conditions
|
62
|
-
end
|
63
|
-
|
64
|
-
aasm_event :vertical_visibility do
|
65
|
-
transitions :from => [:present_weather, :visibility, :sky_conditions],
|
66
|
-
:to => :vertical_visibility
|
67
|
-
end
|
68
|
-
|
69
|
-
aasm_event :temperature_dew_point do
|
70
|
-
transitions :from => [:wind, :sky_conditions, :vertical_visibility], :to => :temperature_dew_point
|
71
|
-
end
|
72
|
-
|
73
|
-
aasm_event :sea_level_pressure do
|
74
|
-
transitions :from => :temperature_dew_point, :to => :sea_level_pressure
|
75
|
-
end
|
76
|
-
|
77
|
-
aasm_event :remarks do
|
78
|
-
transitions :from => [:temperature_dew_point, :sea_level_pressure],
|
79
|
-
:to => :remarks
|
80
|
-
end
|
81
|
-
|
82
|
-
aasm_event :done do
|
83
|
-
transitions :from => [:remarks], :to => :end
|
84
|
-
end
|
85
6
|
|
86
7
|
def self.for_cccc(cccc)
|
87
8
|
raw = Metar::Raw::Noaa.new(cccc)
|
@@ -89,8 +10,10 @@ module Metar
|
|
89
10
|
end
|
90
11
|
|
91
12
|
attr_reader :raw, :metar
|
92
|
-
attr_reader :station_code, :observer, :wind, :variable_wind, :visibility,
|
93
|
-
|
13
|
+
attr_reader :station_code, :observer, :wind, :variable_wind, :visibility,
|
14
|
+
:minimum_visibility, :runway_visible_range, :present_weather, :sky_conditions,
|
15
|
+
:vertical_visibility, :temperature, :dew_point, :sea_level_pressure,
|
16
|
+
:recent_weather, :remarks
|
94
17
|
|
95
18
|
def initialize(raw)
|
96
19
|
@raw = raw
|
@@ -112,6 +35,7 @@ module Metar
|
|
112
35
|
@wind = nil
|
113
36
|
@variable_wind = nil
|
114
37
|
@visibility = nil
|
38
|
+
@minimum_visibility = nil
|
115
39
|
@runway_visible_range = []
|
116
40
|
@present_weather = []
|
117
41
|
@sky_conditions = []
|
@@ -119,18 +43,35 @@ module Metar
|
|
119
43
|
@temperature = nil
|
120
44
|
@dew_point = nil
|
121
45
|
@sea_level_pressure = nil
|
46
|
+
@recent_weather = []
|
122
47
|
@remarks = []
|
123
48
|
|
124
|
-
|
49
|
+
seek_location
|
50
|
+
seek_datetime
|
51
|
+
seek_cor_auto
|
52
|
+
seek_wind
|
53
|
+
seek_variable_wind
|
54
|
+
cavok = seek_cavok
|
55
|
+
if not cavok
|
56
|
+
seek_visibility
|
57
|
+
seek_minimum_visibility
|
58
|
+
seek_runway_visible_range
|
59
|
+
seek_present_weather
|
60
|
+
seek_sky_conditions
|
61
|
+
end
|
62
|
+
seek_vertical_visibility
|
63
|
+
seek_temperature_dew_point
|
64
|
+
seek_sea_level_pressure
|
65
|
+
seek_recent_weather
|
66
|
+
seek_remarks
|
125
67
|
end
|
126
68
|
|
127
69
|
def seek_location
|
128
70
|
if @chunks[0] =~ /^[A-Z][A-Z0-9]{3}$/
|
129
71
|
@station_code = @chunks.shift
|
130
72
|
else
|
131
|
-
raise ParseError.new("Expecting location, found '#{ @chunks[0] }'")
|
73
|
+
raise ParseError.new("Expecting location, found '#{ @chunks[0] }' in #{@metar}")
|
132
74
|
end
|
133
|
-
location!
|
134
75
|
end
|
135
76
|
|
136
77
|
def seek_datetime
|
@@ -139,9 +80,8 @@ module Metar
|
|
139
80
|
@chunks.shift
|
140
81
|
@day, @hour, @minute = $1.to_i, $2.to_i, $3.to_i
|
141
82
|
else
|
142
|
-
raise ParseError.new("Expecting datetime, found '#{ @chunks[0] }'")
|
83
|
+
raise ParseError.new("Expecting datetime, found '#{ @chunks[0] }' in #{@metar}")
|
143
84
|
end
|
144
|
-
datetime!
|
145
85
|
end
|
146
86
|
|
147
87
|
def seek_cor_auto
|
@@ -152,6 +92,11 @@ module Metar
|
|
152
92
|
when @chunks[0] == 'COR'
|
153
93
|
@chunks.shift
|
154
94
|
@observer = :corrected
|
95
|
+
when @chunks[0] == 'CCA'
|
96
|
+
@chunks.shift
|
97
|
+
@observer = :corrected
|
98
|
+
when @chunks[0] == 'RTD' # Delayed observation, no comments on observer
|
99
|
+
@chunks.shift
|
155
100
|
else
|
156
101
|
nil
|
157
102
|
end
|
@@ -163,7 +108,6 @@ module Metar
|
|
163
108
|
@chunks.shift
|
164
109
|
@wind = wind
|
165
110
|
end
|
166
|
-
wind!
|
167
111
|
end
|
168
112
|
|
169
113
|
def seek_variable_wind
|
@@ -172,23 +116,25 @@ module Metar
|
|
172
116
|
@chunks.shift
|
173
117
|
@variable_wind = variable_wind
|
174
118
|
end
|
175
|
-
variable_wind!
|
176
119
|
end
|
177
120
|
|
178
|
-
def
|
121
|
+
def seek_cavok
|
179
122
|
if @chunks[0] == 'CAVOK'
|
180
123
|
@chunks.shift
|
181
|
-
@visibility
|
124
|
+
@visibility = Visibility.new(M9t::Distance.kilometers(10), nil, :more_than)
|
182
125
|
@present_weather << Metar::WeatherPhenomenon.new('No significant weather')
|
183
|
-
@sky_conditions
|
184
|
-
|
185
|
-
|
126
|
+
@sky_conditions << SkyCondition.new # = 'clear skies'
|
127
|
+
return true
|
128
|
+
else
|
129
|
+
return false
|
186
130
|
end
|
131
|
+
end
|
187
132
|
|
133
|
+
# 15.10, 15.6.1
|
134
|
+
def seek_visibility
|
188
135
|
if @observer == :auto # WMO 15.4
|
189
136
|
if @chunks[0] == '////'
|
190
137
|
@chunks.shift # Simply dispose of it
|
191
|
-
visibility!
|
192
138
|
return
|
193
139
|
end
|
194
140
|
end
|
@@ -207,29 +153,23 @@ module Metar
|
|
207
153
|
@visibility = visibility
|
208
154
|
end
|
209
155
|
end
|
210
|
-
visibility!
|
211
156
|
end
|
212
157
|
|
213
|
-
|
214
|
-
|
215
|
-
|
158
|
+
# Optional after visibility: 15.6.2
|
159
|
+
def seek_minimum_visibility
|
160
|
+
minimum_visibility = Visibility.parse(@chunks[0])
|
161
|
+
if minimum_visibility
|
216
162
|
@chunks.shift
|
217
|
-
@
|
218
|
-
collect_runway_visible_range
|
163
|
+
@minimum_visibility = minimum_visibility
|
219
164
|
end
|
220
165
|
end
|
221
166
|
|
222
167
|
def seek_runway_visible_range
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
def collect_present_weather
|
228
|
-
wtp = WeatherPhenomenon.parse(@chunks[0])
|
229
|
-
if wtp
|
168
|
+
loop do
|
169
|
+
runway_visible_range = RunwayVisibleRange.parse(@chunks[0])
|
170
|
+
break if runway_visible_range.nil?
|
230
171
|
@chunks.shift
|
231
|
-
@
|
232
|
-
collect_present_weather
|
172
|
+
@runway_visible_range << runway_visible_range
|
233
173
|
end
|
234
174
|
end
|
235
175
|
|
@@ -238,35 +178,33 @@ module Metar
|
|
238
178
|
if @chunks[0] == '//' # WMO 15.4
|
239
179
|
@chunks.shift # Simply dispose of it
|
240
180
|
@present_weather << Metar::WeatherPhenomenon.new('not observed')
|
241
|
-
present_weather!
|
242
181
|
return
|
243
182
|
end
|
244
183
|
end
|
245
184
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
def collect_sky_conditions
|
251
|
-
sky_condition = SkyCondition.parse(@chunks[0])
|
252
|
-
if sky_condition
|
185
|
+
loop do
|
186
|
+
wtp = WeatherPhenomenon.parse(@chunks[0])
|
187
|
+
break if wtp.nil?
|
253
188
|
@chunks.shift
|
254
|
-
@
|
255
|
-
collect_sky_conditions
|
189
|
+
@present_weather << wtp
|
256
190
|
end
|
257
191
|
end
|
258
192
|
|
193
|
+
# Repeatable: 15.9.1.3
|
259
194
|
def seek_sky_conditions
|
260
195
|
if @observer == :auto # WMO 15.4
|
261
196
|
if @chunks[0] == '///' or @chunks[0] == '//////'
|
262
197
|
@chunks.shift # Simply dispose of it
|
263
|
-
sky_conditions!
|
264
198
|
return
|
265
199
|
end
|
266
200
|
end
|
267
201
|
|
268
|
-
|
269
|
-
|
202
|
+
loop do
|
203
|
+
sky_condition = SkyCondition.parse(@chunks[0])
|
204
|
+
break if sky_condition.nil?
|
205
|
+
@chunks.shift
|
206
|
+
@sky_conditions << sky_condition
|
207
|
+
end
|
270
208
|
end
|
271
209
|
|
272
210
|
def seek_vertical_visibility
|
@@ -275,7 +213,6 @@ module Metar
|
|
275
213
|
@chunks.shift
|
276
214
|
@vertical_visibility = vertical_visibility
|
277
215
|
end
|
278
|
-
vertical_visibility!
|
279
216
|
end
|
280
217
|
|
281
218
|
def seek_temperature_dew_point
|
@@ -284,10 +221,7 @@ module Metar
|
|
284
221
|
@chunks.shift
|
285
222
|
@temperature = Metar::Temperature.parse($1)
|
286
223
|
@dew_point = Metar::Temperature.parse($2)
|
287
|
-
else
|
288
|
-
raise ParseError.new("Expecting temperature/dew point, found '#{ @chunks[0] }'")
|
289
224
|
end
|
290
|
-
temperature_dew_point!
|
291
225
|
end
|
292
226
|
|
293
227
|
def seek_sea_level_pressure
|
@@ -296,7 +230,19 @@ module Metar
|
|
296
230
|
@chunks.shift
|
297
231
|
@sea_level_pressure = sea_level_pressure
|
298
232
|
end
|
299
|
-
|
233
|
+
end
|
234
|
+
|
235
|
+
def seek_recent_weather
|
236
|
+
loop do
|
237
|
+
return if @chunks.size == 0
|
238
|
+
m = /^RE/.match(@chunks[0])
|
239
|
+
break if m.nil?
|
240
|
+
recent_weather = Metar::WeatherPhenomenon.parse(m.post_match)
|
241
|
+
if recent_weather
|
242
|
+
@chunks.shift
|
243
|
+
@recent_weather << recent_weather
|
244
|
+
end
|
245
|
+
end
|
300
246
|
end
|
301
247
|
|
302
248
|
def seek_remarks
|
@@ -305,11 +251,6 @@ module Metar
|
|
305
251
|
end
|
306
252
|
@remarks += @chunks.clone
|
307
253
|
@chunks = []
|
308
|
-
remarks!
|
309
|
-
end
|
310
|
-
|
311
|
-
def seek_end
|
312
|
-
done!
|
313
254
|
end
|
314
255
|
|
315
256
|
end
|
data/lib/metar/raw.rb
CHANGED
data/lib/metar/report.rb
CHANGED
@@ -10,6 +10,7 @@ module Metar
|
|
10
10
|
:time,
|
11
11
|
:wind,
|
12
12
|
:visibility,
|
13
|
+
:minimum_visibility,
|
13
14
|
:present_weather,
|
14
15
|
:sky_summary,
|
15
16
|
:temperature
|
@@ -58,6 +59,10 @@ module Metar
|
|
58
59
|
@parser.visibility.to_s
|
59
60
|
end
|
60
61
|
|
62
|
+
def minimum_visibility
|
63
|
+
@parser.minimum_visibility.to_s
|
64
|
+
end
|
65
|
+
|
61
66
|
def runway_visible_range
|
62
67
|
@parser.runway_visible_range.collect { |rvr| rvr.to_s }.join(', ')
|
63
68
|
end
|
data/lib/metar/version.rb
CHANGED
data/locales/en.yml
CHANGED
data/locales/it.yml
CHANGED
data/spec/unit/parser_spec.rb
CHANGED
@@ -64,6 +64,12 @@ describe Metar::Parser do
|
|
64
64
|
|
65
65
|
parser.observer. should == :corrected
|
66
66
|
end
|
67
|
+
|
68
|
+
it 'corrected (Canadian)' do
|
69
|
+
parser = setup_parser('CYZU 310100Z CCA 26004KT 15SM FEW009 BKN040TCU BKN100 OVC210 15/12 A2996 RETS RMK SF1TCU4AC2CI1 SLP149')
|
70
|
+
|
71
|
+
parser.observer. should == :corrected
|
72
|
+
end
|
67
73
|
|
68
74
|
end
|
69
75
|
|
@@ -108,6 +114,13 @@ describe Metar::Parser do
|
|
108
114
|
should be_within( 0.01 ).of( 1.75 )
|
109
115
|
end
|
110
116
|
|
117
|
+
it 'in meters' do
|
118
|
+
parser = setup_parser('VABB 282210Z 22005KT 4000 HZ SCT018 FEW025TCU BKN100 28/25 Q1003 NOSIG')
|
119
|
+
|
120
|
+
parser.visibility.distance.value.
|
121
|
+
should be_within(0.01).of(4000)
|
122
|
+
end
|
123
|
+
|
111
124
|
it '//// with automatic observer' do
|
112
125
|
parser = setup_parser("CYXS 151034Z AUTO 09003KT //// FZFG VV001 M03/M03 A3019 RMK SLP263 ICG")
|
113
126
|
|
@@ -211,12 +224,6 @@ describe Metar::Parser do
|
|
211
224
|
should == 30.48
|
212
225
|
end
|
213
226
|
|
214
|
-
it 'temperature_obligatory' do
|
215
|
-
expect do
|
216
|
-
setup_parser("PAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 A2910 RMK AO2 P0000")
|
217
|
-
end. to raise_error( Metar::ParseError )
|
218
|
-
end
|
219
|
-
|
220
227
|
it 'temperature' do
|
221
228
|
parser = setup_parser("PAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
222
229
|
parser.temperature.value. should == -17
|
@@ -233,6 +240,15 @@ describe Metar::Parser do
|
|
233
240
|
should == 29.10
|
234
241
|
end
|
235
242
|
|
243
|
+
it 'recent weather' do
|
244
|
+
parser = setup_parser("CYQH 310110Z 00000KT 20SM SCT035CB BKN050 RETS RMK CB4SC1")
|
245
|
+
|
246
|
+
parser.recent_weather. should be_a Array
|
247
|
+
parser.recent_weather.size. should == 1
|
248
|
+
parser.recent_weather[0].phenomenon.
|
249
|
+
should == 'thunderstorm'
|
250
|
+
end
|
251
|
+
|
236
252
|
it 'remarks' do
|
237
253
|
parser = setup_parser("PAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
238
254
|
|
data/spec/unit/report_spec.rb
CHANGED
@@ -79,6 +79,7 @@ describe Metar::Report do
|
|
79
79
|
:wind,
|
80
80
|
:variable_wind,
|
81
81
|
:visibility,
|
82
|
+
:minimum_visibility,
|
82
83
|
:vertical_visibility,
|
83
84
|
:temperature,
|
84
85
|
:dew_point,
|
@@ -161,10 +162,11 @@ describe Metar::Report do
|
|
161
162
|
end
|
162
163
|
|
163
164
|
it '#to_s' do
|
164
|
-
|
165
|
-
|
165
|
+
sky1 = stub( 'sky1', :to_summary => 'sky1' )
|
166
|
+
sky2 = stub( 'sky2', :to_summary => 'sky2' )
|
166
167
|
@parser.stub!( :wind => 'wind',
|
167
168
|
:visibility => 'visibility',
|
169
|
+
:minimum_visibility => 'min visibility',
|
168
170
|
:present_weather => ['pw'],
|
169
171
|
:sky_conditions => [ sky1, sky2 ],
|
170
172
|
:temperature => 'temp' )
|
@@ -174,6 +176,7 @@ country: Wwwwww
|
|
174
176
|
time: #{@metar_time}
|
175
177
|
wind: wind
|
176
178
|
visibility: visibility
|
179
|
+
minimum visibility: min visibility
|
177
180
|
weather: pw
|
178
181
|
sky: sky2
|
179
182
|
temperature: temp
|
@@ -39,7 +39,8 @@ describe Metar::RunwayVisibleRange do
|
|
39
39
|
[ 'understands comparators: PM', 'R12/P3400', [ '12', [3400.00, nil, :more_than], nil, nil ] ],
|
40
40
|
[ 'understands tendencies: NUD', 'R12/3400U', [ '12', [3400.00, nil, nil], nil, :improving ] ],
|
41
41
|
[ 'understands feet', 'R12/3400FT', [ '12', [1036.32, nil, nil], nil, nil ] ],
|
42
|
-
[ 'understands second
|
42
|
+
[ 'understands second visibilities (m)', 'R26/0750V1200U', [ '12', [ 750.0, nil, nil], [1200.0, nil, nil], :improving ] ],
|
43
|
+
[ 'understands second visibilities (ft)', 'R12/3400V1800FT', [ '12', [1036.32, nil, nil], [548.64, nil, nil], nil ] ],
|
43
44
|
[ 'returns nil for nil', nil, [ nil, nil, nil, nil ] ],
|
44
45
|
].each do | docstring, raw, expected |
|
45
46
|
example docstring do
|
@@ -24,11 +24,13 @@ describe Metar::SkyCondition do
|
|
24
24
|
context '.parse' do
|
25
25
|
|
26
26
|
[
|
27
|
-
[ 'understands clear skies codes', 'NSC', [ nil,
|
28
|
-
[ 'quantity + height', 'BKN12', [ 'broken', 365.76,
|
27
|
+
[ 'understands clear skies codes', 'NSC', [ nil, nil, nil ] ],
|
28
|
+
[ 'quantity + height', 'BKN12', [ 'broken', 365.76, nil ] ],
|
29
29
|
[ 'quantity + height + type', 'BKN12CB', [ 'broken', 365.76, 'cumulonimbus' ] ],
|
30
|
-
[ 'quantity +
|
31
|
-
[ '
|
30
|
+
[ 'quantity + ///', 'BKN///', [ 'broken', nil, nil ] ],
|
31
|
+
[ 'quantity + height + ///', 'FEW038///',[ 'few', 1158.24, nil ] ],
|
32
|
+
[ 'cumulonimbus only', 'CB', [ nil, nil, 'cumulonimbus' ] ], # seems non-standard, but occurs
|
33
|
+
[ 'returns nil for unmatched', 'FUBAR', [ :expect_nil, nil, nil ] ],
|
32
34
|
].each do | docstring, raw, expected |
|
33
35
|
example docstring do
|
34
36
|
Metar::SkyCondition.parse( raw ).should be_sky_condition( *expected )
|
@@ -33,13 +33,14 @@ describe Metar::Visibility do
|
|
33
33
|
[ 'understands 9999', '9999', [ 10000.00, nil, :more_than ] ],
|
34
34
|
[ 'understands nnnn + NDV', '0123NDV', [ 123.00, nil, nil ] ],
|
35
35
|
[ 'understands n/nSM', '3/4SM', [ 1207.01, nil, nil ] ],
|
36
|
+
[ 'understands 3/16SM', '3/16SM', [ 301.752, nil, nil ] ],
|
36
37
|
[ 'understands n n/nSM', '1 1/4SM', [ 2011.68, nil, nil ] ],
|
37
38
|
[ 'understands nSM', '5SM', [ 8046.72, nil, nil ] ],
|
38
39
|
[ 'understands M1/4SM', 'M1/4SM', [ 402.34, nil, :less_than ] ],
|
39
40
|
[ 'understands n + KM', '5KM', [ 5000.00, nil, nil ] ],
|
40
|
-
[ 'understands n', '
|
41
|
-
[ 'understands n + compass', '
|
42
|
-
[ 'returns nil for unmatched', 'FUBAR', [
|
41
|
+
[ 'understands n', '500', [ 500.00, nil, nil ] ],
|
42
|
+
[ 'understands n + compass', '500NW', [ 500.00, 315.0, nil ] ],
|
43
|
+
[ 'returns nil for unmatched', 'FUBAR', [ nil, nil, nil ] ],
|
43
44
|
].each do | docstring, raw, expected |
|
44
45
|
example docstring do
|
45
46
|
Metar::Visibility.parse( raw ).should be_visibility( *expected )
|
@@ -26,7 +26,10 @@ describe Metar::WeatherPhenomenon do
|
|
26
26
|
[
|
27
27
|
[ 'simple phenomenon', 'BR', [ nil, nil, 'mist' ] ],
|
28
28
|
[ 'descriptor + phenomenon', 'BCFG', [ nil, 'patches of', 'fog' ] ],
|
29
|
-
[ '
|
29
|
+
[ 'intensity + phenomenon', '+RA', [ 'heavy', nil, 'rain' ] ],
|
30
|
+
[ 'intensity + proximity + phenomenon', '-VCTSRA', [ 'nearby light', 'thunderstorm and', 'rain' ] ],
|
31
|
+
[ '2 phenomena: SN RA', 'SNRA', [ nil, nil, 'snow and rain' ] ],
|
32
|
+
[ '2 phenomena: RA DZ', 'RADZ', [ nil, nil, 'rain and drizzle' ] ],
|
30
33
|
[ 'modifier + descriptor + phenomenon', 'VCDRFG', [ 'nearby', 'low drifting', 'fog' ] ],
|
31
34
|
[ 'returns nil for unmatched', 'FUBAR', [ nil, nil, nil ] ],
|
32
35
|
].each do | docstring, raw, expected |
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metar-parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -60,45 +60,61 @@ dependencies:
|
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 0.3.5
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
|
-
name:
|
63
|
+
name: m9t
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
65
65
|
none: false
|
66
66
|
requirements:
|
67
|
-
- -
|
67
|
+
- - ~>
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
69
|
+
version: 0.3.1
|
70
70
|
type: :runtime
|
71
71
|
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.3.1
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 2.3.0
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
72
88
|
version_requirements: !ruby/object:Gem::Requirement
|
73
89
|
none: false
|
74
90
|
requirements:
|
75
91
|
- - ! '>='
|
76
92
|
- !ruby/object:Gem::Version
|
77
|
-
version: 2.
|
93
|
+
version: 2.3.0
|
78
94
|
- !ruby/object:Gem::Dependency
|
79
|
-
name:
|
95
|
+
name: simplecov
|
80
96
|
requirement: !ruby/object:Gem::Requirement
|
81
97
|
none: false
|
82
98
|
requirements:
|
83
|
-
- -
|
99
|
+
- - ! '>='
|
84
100
|
- !ruby/object:Gem::Version
|
85
|
-
version: 0
|
86
|
-
type: :
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
87
103
|
prerelease: false
|
88
104
|
version_requirements: !ruby/object:Gem::Requirement
|
89
105
|
none: false
|
90
106
|
requirements:
|
91
|
-
- -
|
107
|
+
- - ! '>='
|
92
108
|
- !ruby/object:Gem::Version
|
93
|
-
version: 0
|
109
|
+
version: '0'
|
94
110
|
- !ruby/object:Gem::Dependency
|
95
|
-
name:
|
111
|
+
name: pry
|
96
112
|
requirement: !ruby/object:Gem::Requirement
|
97
113
|
none: false
|
98
114
|
requirements:
|
99
115
|
- - ! '>='
|
100
116
|
- !ruby/object:Gem::Version
|
101
|
-
version:
|
117
|
+
version: '0'
|
102
118
|
type: :development
|
103
119
|
prerelease: false
|
104
120
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -106,9 +122,9 @@ dependencies:
|
|
106
122
|
requirements:
|
107
123
|
- - ! '>='
|
108
124
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
125
|
+
version: '0'
|
110
126
|
- !ruby/object:Gem::Dependency
|
111
|
-
name:
|
127
|
+
name: pry-doc
|
112
128
|
requirement: !ruby/object:Gem::Requirement
|
113
129
|
none: false
|
114
130
|
requirements:
|
@@ -176,7 +192,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
176
192
|
version: '0'
|
177
193
|
segments:
|
178
194
|
- 0
|
179
|
-
hash:
|
195
|
+
hash: -116852050728515412
|
180
196
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
197
|
none: false
|
182
198
|
requirements:
|
@@ -185,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
185
201
|
version: '0'
|
186
202
|
segments:
|
187
203
|
- 0
|
188
|
-
hash:
|
204
|
+
hash: -116852050728515412
|
189
205
|
requirements: []
|
190
206
|
rubyforge_project: nowarning
|
191
207
|
rubygems_version: 1.8.23
|
@@ -208,3 +224,4 @@ test_files:
|
|
208
224
|
- spec/unit/raw_spec.rb
|
209
225
|
- spec/unit/speed_spec.rb
|
210
226
|
- spec/unit/distance_spec.rb
|
227
|
+
has_rdoc:
|