Хранилища Subversion geo-modmetar

Редакция

Редакция 11 | Редакция 13 | К новейшей редакции | Только различия | Не учитывать пробелы | Содержимое файла | Авторство | Последнее изменение | Открыть журнал | RSS

Редакция 11 Редакция 12
1
# AW: create fork Geo::METAR as Geo::ModMETAR
1
# AW: create fork Geo::METAR as Geo::ModMETAR
2
#
2
#
3
# KH: fix the parser
3
# KH: fix the parser
4
# should be a finite state machine
4
# should be a finite state machine
5
# - metar has rules what comes after what. but codes can be missing.
5
# - metar has rules what comes after what. but codes can be missing.
6
# (measurement not done) or //// (measurement broken at the moment)
6
# (measurement not done) or //// (measurement broken at the moment)
7
# so given a state counter, it can stay the same or go up one or more states,
7
# so given a state counter, it can stay the same or go up one or more states,
8
# but it can never go down
8
# but it can never go down
9
#
9
#
10
# info on the last bit which is actually a forecast: (German)
10
# info on the last bit which is actually a forecast: (German)
11
# http://www.wetterklima.de/flug/metar/Metarvorhersage.htm
11
# http://www.wetterklima.de/flug/metar/Metarvorhersage.htm
12
#
12
#
13
# more info here (dutch, and txt 707 is not standard metar)
13
# more info here (dutch, and txt 707 is not standard metar)
14
# http://www.vwkweb.nl/index.html?http://www.vwkweb.nl/weerinfo/weerinfo_teletekst707.html
14
# http://www.vwkweb.nl/index.html?http://www.vwkweb.nl/weerinfo/weerinfo_teletekst707.html
15
# and also (dutch)
15
# and also (dutch)
16
# http://www.gids.nl/weather/eheh/metari.html
16
# http://www.gids.nl/weather/eheh/metari.html
17
#
17
#
18
# 'METAR decoding in Europe'
18
# 'METAR decoding in Europe'
19
# http://users.hol.gr/~chatos/VATSIM/TM/metar.html
19
# http://users.hol.gr/~chatos/VATSIM/TM/metar.html
20
#
20
#
21
# english explanation
21
# english explanation
22
# http://booty.org.uk/booty.weather/metinfo/codes/METAR_decode.htm
22
# http://booty.org.uk/booty.weather/metinfo/codes/METAR_decode.htm
23
#
23
#
24
# canadian explanation
24
# canadian explanation
25
# http://meteocentre.com/doc/metar.html
25
# http://meteocentre.com/doc/metar.html
26
#
26
#
27
# 'METAR decoding, TAF decoding'
27
# 'METAR decoding, TAF decoding'
28
# http://stoivane.kapsi.fi/metar/
28
# http://stoivane.kapsi.fi/metar/
29
#
29
#
30
30
31
# This module is used for decoding NWS METAR code.
31
# This module is used for decoding NWS METAR code.
32
32
33
# Example METARs
33
# Example METARs
34
#
34
#
35
# Findlay, Ohio
35
# Findlay, Ohio
36
# KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK SLP201 57014
36
# KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK SLP201 57014
37
#
37
#
38
# Toledo, Ohio
38
# Toledo, Ohio
39
# KTOL 251451Z 23016G22KT 8SM CLR 04/00 A3006 RMK AO2 SLP185 T00440000 56016 
39
# KTOL 251451Z 23016G22KT 8SM CLR 04/00 A3006 RMK AO2 SLP185 T00440000 56016 
40
#
40
#
41
# Cleveland, Ohio
41
# Cleveland, Ohio
42
# KCLE 251554Z 20015KT 10SM FEW055 OVC070 03/M02 A3011 RMK AO2 SLP205 T00331017
42
# KCLE 251554Z 20015KT 10SM FEW055 OVC070 03/M02 A3011 RMK AO2 SLP205 T00331017
43
#
43
#
44
# Houston, Texas
44
# Houston, Texas
45
# KHST 251455Z 06017G22KT 7SM FEW040 BKN330 25/18 A3016 RMK SLP213 8/508
45
# KHST 251455Z 06017G22KT 7SM FEW040 BKN330 25/18 A3016 RMK SLP213 8/508
46
# 9/205 51007
46
# 9/205 51007
47
#
47
#
48
# LA
48
# LA
49
#
49
#
50
# KLAX 251450Z 07004KT 7SM SCT100 BKN200 14/11 A3005 RMK AO2 SLP173
50
# KLAX 251450Z 07004KT 7SM SCT100 BKN200 14/11 A3005 RMK AO2 SLP173
51
# T01390111 56005
51
# T01390111 56005
52
#
52
#
53
# Soesterberg
53
# Soesterberg
54
#
54
#
55
# EHSB 181325Z 24009KT 8000 -RA BR FEW011 SCT022 OVC030 07/06 Q1011 WHT WHT TEMPO GRN
55
# EHSB 181325Z 24009KT 8000 -RA BR FEW011 SCT022 OVC030 07/06 Q1011 WHT WHT TEMPO GRN
56
56
57
# For METAR info, please see
57
# For METAR info, please see
58
# http://tgsv5.nws.noaa.gov/oso/oso1/oso12/metar.htm
58
# http://tgsv5.nws.noaa.gov/oso/oso1/oso12/metar.htm
59
# moved
59
# moved
60
# http://metar.noaa.gov/
60
# http://metar.noaa.gov/
61
#
61
#
62
# in scary detail (metar coding)
62
# in scary detail (metar coding)
63
#
63
#
64
# http://metar.noaa.gov/table_master.jsp?sub_menu=yes&show=fmh1ch12.htm&dir=./handbook/&title=title_handbook
64
# http://metar.noaa.gov/table_master.jsp?sub_menu=yes&show=fmh1ch12.htm&dir=./handbook/&title=title_handbook
65
#
65
#
66
66
67
67
68
# The METAR specification is dictated in the Federal Meteorological Handbook
68
# The METAR specification is dictated in the Federal Meteorological Handbook
69
# which is available on-line at:
69
# which is available on-line at:
70
# http://tgsv5.nws.noaa.gov/oso/oso1/oso12/fmh1.htm
70
# http://tgsv5.nws.noaa.gov/oso/oso1/oso12/fmh1.htm
71
71
72
# General Structure is:
72
# General Structure is:
73
# TYPE, SITE, DATE/TIME, WIND, VISIBILITY, CLOUDS, TEMPERATURE, PRESSURE, REMARKS
73
# TYPE, SITE, DATE/TIME, WIND, VISIBILITY, CLOUDS, TEMPERATURE, PRESSURE, REMARKS
74
74
75
# Specifically:
75
# Specifically:
76
76
77
# TYPE (optional)
77
# TYPE (optional)
78
# METAR or SPECI
78
# METAR or SPECI
79
# METAR: regular report
79
# METAR: regular report
80
# SPECI: special report
80
# SPECI: special report
81
81
82
# SITE (required, only once)
82
# SITE (required, only once)
83
#
83
#
84
# 4-Char site identifier (KLAX for LA, KHST for Houston)
84
# 4-Char site identifier (KLAX for LA, KHST for Houston)
85
85
86
# DATE/TIME (required, only once)
86
# DATE/TIME (required, only once)
87
#
87
#
88
# 6-digit time followed by "Z", indicating UTC
88
# 6-digit time followed by "Z", indicating UTC
89
89
90
# REPORT MODIFIER (optional)
90
# REPORT MODIFIER (optional)
91
# AUTO or COR
91
# AUTO or COR
92
# AUTO = Automatic report (no human intervention)
92
# AUTO = Automatic report (no human intervention)
93
# COR = Corrected METAR or SPECI
93
# COR = Corrected METAR or SPECI
94
94
95
# WIND (group)
95
# WIND (group)
96
#
96
#
97
# Wind direction (\d\d\d) and speed (\d?\d\d) and optionaling gusting
97
# Wind direction (\d\d\d) and speed (\d?\d\d) and optionaling gusting
98
# information denoted by "G" and speed (\d?\d\d) followed by "KT", for knots.
98
# information denoted by "G" and speed (\d?\d\d) followed by "KT", for knots.
99
#
99
#
100
# Wind direction MAY be "VRB" (variable) instead of a compass direction.
100
# Wind direction MAY be "VRB" (variable) instead of a compass direction.
101
#
101
#
102
# Variable Wind Direction (Speeds greater than 6 knots).  Variable wind
102
# Variable Wind Direction (Speeds greater than 6 knots).  Variable wind
103
# direction with wind speed greater than 6 knots shall be coded in the
103
# direction with wind speed greater than 6 knots shall be coded in the
104
# format, dndndnVdxdxdx
104
# format, dndndnVdxdxdx
105
#
105
#
106
# Calm wind is recorded as 00000KT.
106
# Calm wind is recorded as 00000KT.
107
107
108
# VISIBILITY (group)
108
# VISIBILITY (group)
109
#
109
#
110
# Visibility (\d+) followed by "SM" for statute miles or no 'SM' for meters
110
# Visibility (\d+) followed by "SM" for statute miles or no 'SM' for meters
111
# (european)
111
# (european)
112
#
112
#
113
# May be 1/(\d)SM for a fraction.
113
# May be 1/(\d)SM for a fraction.
114
#
114
#
115
# May be M1/\d)SM for less than a given fraction. (M="-")
115
# May be M1/\d)SM for less than a given fraction. (M="-")
116
#
116
#
117
# \d\d\d\d according to KNMI
117
# \d\d\d\d according to KNMI
118
# lowest horizontal visibility (looking around)
118
# lowest horizontal visibility (looking around)
119
# round down
119
# round down
120
# 0000 - 0500m in steps of 0050m
120
# 0000 - 0500m in steps of 0050m
121
# 0500 - 5000m in steps of 0100m
121
# 0500 - 5000m in steps of 0100m
122
# 5000 - 9999m in steps of 1000m
122
# 5000 - 9999m in steps of 1000m
123
# 10km or more is 9999
123
# 10km or more is 9999
124
124
125
# RUNWAY Visual Range (Group)
125
# RUNWAY Visual Range (Group)
126
#
126
#
127
# R(\d\d\d)(L|C|R)?/((M|P)?\d\d\d\d){1,2}FT
127
# R(\d\d\d)(L|C|R)?/((M|P)?\d\d\d\d){1,2}FT
128
#
128
#
129
# Where:
129
# Where:
130
#  $1 is the runway number.
130
#  $1 is the runway number.
131
#  $2 is the runway (Left/Center/Right) for parallel runways.
131
#  $2 is the runway (Left/Center/Right) for parallel runways.
132
#  $3 is the reported visibility in feet.
132
#  $3 is the reported visibility in feet.
133
#  $4 is the MAXIMUM reported visibility, making $3 the MINIMUM.
133
#  $4 is the MAXIMUM reported visibility, making $3 the MINIMUM.
134
#
134
#
135
#  "M" beginning a value means less than the reportable value of \d\d\d\d.
135
#  "M" beginning a value means less than the reportable value of \d\d\d\d.
136
#  "P" beginning a value means more than the reportable value of \d\d\d\d.
136
#  "P" beginning a value means more than the reportable value of \d\d\d\d.
137
#
137
#
138
#  new
138
#  new
139
#
139
#
140
#  R(\d\d\d[LCR]?)/([MP]?\d\d\d\d)(V[MP]?\d\d\d\d)?FT
140
#  R(\d\d\d[LCR]?)/([MP]?\d\d\d\d)(V[MP]?\d\d\d\d)?FT
141
#
141
#
142
# $1 runway number + Left/Center/Right
142
# $1 runway number + Left/Center/Right
143
# $2 visibility feet
143
# $2 visibility feet
144
# $3 Varying feet
144
# $3 Varying feet
145
# M = less than
145
# M = less than
146
# P = more than
146
# P = more than
147
147
148
# WEATHER (Present Weather Group)
148
# WEATHER (Present Weather Group)
149
#
149
#
150
# See table in Chapter 12 of FMH-1.
150
# See table in Chapter 12 of FMH-1.
151
151
152
# CLOUDS (Sky Condition Group)
152
# CLOUDS (Sky Condition Group)
153
#
153
#
154
# A space-separated grouping of cloud conditions which will contain at least
154
# A space-separated grouping of cloud conditions which will contain at least
155
# one cloud report. Examples: "CLR", "BKN330", "SCT100", "FEW055", "OVC070"
155
# one cloud report. Examples: "CLR", "BKN330", "SCT100", "FEW055", "OVC070"
156
# The three-letter codes represent the condition (Clear, Broken, Scattered,
156
# The three-letter codes represent the condition (Clear, Broken, Scattered,
157
# Few, Overcast) and the numbers (\d\d\d) represent altitlude/100.
157
# Few, Overcast) and the numbers (\d\d\d) represent altitlude/100.
158
#
158
#
159
# The report may have a trailing CB (cumulonimbus) or TCU (towering
159
# The report may have a trailing CB (cumulonimbus) or TCU (towering
160
# cumulus) appended. ([A-Z]{2,3})?(\d\d\d)(CB|TCU)?
160
# cumulus) appended. ([A-Z]{2,3})?(\d\d\d)(CB|TCU)?
161
161
162
# Vertical visibility (VV)
162
# Vertical visibility (VV)
163
#
163
#
164
# VV
164
# VV
165
# This group is reported when the sky is obscured. VV is the group indicator,
165
# This group is reported when the sky is obscured. VV is the group indicator,
166
# and hshshs is the vertical visibility in units of 30 metres
166
# and hshshs is the vertical visibility in units of 30 metres
167
# (hundreds of feet).
167
# (hundreds of feet).
168
#  
168
#  
169
#  hshshs - Examples of Encoding
169
#  hshshs - Examples of Encoding
170
#  HEIGHT               METAR CODE
170
#  HEIGHT               METAR CODE
171
#  100 ft       (30 metres)     001
171
#  100 ft       (30 metres)     001
172
#  450 ft       (135 metres)    004
172
#  450 ft       (135 metres)    004
173
#  2,700 ft     (810 metres)    027
173
#  2,700 ft     (810 metres)    027
174
#  12,600 ft    (3,780 metres)  1300
174
#  12,600 ft    (3,780 metres)  1300
175
#
175
#
176
# source http://meteocentre.com/doc/metar.html
176
# source http://meteocentre.com/doc/metar.html
177
# 
177
# 
178
# TEMPERATURE and DEW POINT
178
# TEMPERATURE and DEW POINT
179
#
179
#
180
# (M?\d\d)/(M?\d\d) where $1 is the current temperature in degrees celcius,
180
# (M?\d\d)/(M?\d\d) where $1 is the current temperature in degrees celcius,
181
# and $2 is the current dewpoint in degrees celcius.
181
# and $2 is the current dewpoint in degrees celcius.
182
#
182
#
183
# The "M" signifies a negative temperature, so converting the "M" to a
183
# The "M" signifies a negative temperature, so converting the "M" to a
184
# "-" ought to suffice.
184
# "-" ought to suffice.
185
185
186
# PRESSURE
186
# PRESSURE
187
#
187
#
188
# The pressure, or altimeter setting, at the reporting site recorded in whole
188
# The pressure, or altimeter setting, at the reporting site recorded in whole
189
# hectopascals (starts with a Q) or inches of mercury (Hg) minus the decimal
189
# hectopascals (starts with a Q) or inches of mercury (Hg) minus the decimal
190
# point (starts with an A). It should always look like ([AQ]\d\d\d\d).
190
# point (starts with an A). It should always look like ([AQ]\d\d\d\d).
191
#
191
#
192
# KNMI: Q\d\d\d\d pressure in hPa calculated for sea level
192
# KNMI: Q\d\d\d\d pressure in hPa calculated for sea level
193
193
194
# REMARKS
194
# REMARKS
195
#
195
#
196
# Remarks contain additional information. They are optional but often
196
# Remarks contain additional information. They are optional but often
197
# informative of special conditions.
197
# informative of special conditions.
198
#
198
#
199
# Remarks begin with the "RMK" keyword and continue to the end of the line.
199
# Remarks begin with the "RMK" keyword and continue to the end of the line.
200
#
200
#
201
# trend group
201
# trend group
202
#
202
#
203
# color codes BLU WHT GRN YLO AMB RED
203
# color codes BLU WHT GRN YLO AMB RED
204
# BLACK: vliegveld dicht
204
# BLACK: vliegveld dicht
205
# future trend 
205
# future trend 
206
# NOSIG no significant change
206
# NOSIG no significant change
207
# TEMPO temporary change
207
# TEMPO temporary change
208
# WHT WHT TEMPO GRN = current white, prediction white temporary green
208
# WHT WHT TEMPO GRN = current white, prediction white temporary green
209
# NSW no significant weather
209
# NSW no significant weather
210
# AT at a given time
210
# AT at a given time
211
# PROB30 probability 30%
211
# PROB30 probability 30%
212
# BECMG becoming
212
# BECMG becoming
213
# BECMG (weather) FM \d\d\d\d TL \d\d\d\d = from until utc times
213
# BECMG (weather) FM \d\d\d\d TL \d\d\d\d = from until utc times
214
# BECMG (weather) AT \d\d\d\d = at utc time
214
# BECMG (weather) AT \d\d\d\d = at utc time
215
# BECMG (weather) TL \d\d\d\d = change until utc time
215
# BECMG (weather) TL \d\d\d\d = change until utc time
216
# BECMG 2000 visibility
216
# BECMG 2000 visibility
217
# BECMG NSW weather type
217
# BECMG NSW weather type
218
# etc etc
218
# etc etc
219
# FCST CANCEL (2 tokens!) Forecast cancel: no further forecasts for a while
219
# FCST CANCEL (2 tokens!) Forecast cancel: no further forecasts for a while
220
220
221
### Package Definition
221
### Package Definition
222
222
223
package Geo::ModMETAR; # Package based on Debian Geo::METAR
223
package Geo::ModMETAR; # Package based on Debian Geo::METAR
224
224
225
## Required Modules
225
## Required Modules
226
226
227
use 5.005;
227
use 5.005;
228
use strict;
228
use strict;
229
use vars qw($AUTOLOAD $VERSION);
229
use vars qw($AUTOLOAD $VERSION);
230
use Carp 'cluck';
230
use Carp 'cluck';
231
231
232
$VERSION = '1.0'; # Based on Debian Geo::METAR 1.15
232
$VERSION = '1.1'; # Based on Debian Geo::METAR 1.15
233
233
234
##
234
##
235
## Lookup tables
235
## Lookup tables
236
##
236
##
237
237
238
my %_weather_types = (
238
my %_weather_types = (
239
    MI => 'shallow',
239
    MI => 'shallow',
240
    PI => 'partial',
240
    PI => 'partial',
241
    BC => 'patches',
241
    BC => 'patches',
242
    DR => 'drizzle',
242
    DR => 'drizzle',
243
    BL => 'blowing',
243
    BL => 'blowing',
244
    SH => 'shower(s)',
244
    SH => 'shower(s)',
245
    TS => 'thunderstorm',
245
    TS => 'thunderstorm',
246
    FZ => 'freezing',
246
    FZ => 'freezing',
247
247
248
    DZ => 'drizzle',
248
    DZ => 'drizzle',
249
    RA => 'rain',
249
    RA => 'rain',
250
    SN => 'snow',
250
    SN => 'snow',
251
    SG => 'snow grains',
251
    SG => 'snow grains',
252
    IC => 'ice crystals',
252
    IC => 'ice crystals',
253
    PE => 'ice pellets',
253
    PE => 'ice pellets',
254
    GR => 'hail',
254
    GR => 'hail',
255
    GS => 'small hail/snow pellets',
255
    GS => 'small hail/snow pellets',
256
    UP => 'unknown precip',
256
    UP => 'unknown precip',
257
257
258
    BR => 'mist',
258
    BR => 'mist',
259
    FG => 'fog',
259
    FG => 'fog',
260
    PRFG => 'fog banks',  # officially PR is a modifier of FG
260
    PRFG => 'fog banks',  # officially PR is a modifier of FG
261
    FU => 'smoke',
261
    FU => 'smoke',
262
    VA => 'volcanic ash',
262
    VA => 'volcanic ash',
263
    DU => 'dust',
263
    DU => 'dust',
264
    SA => 'sand',
264
    SA => 'sand',
265
    HZ => 'haze',
265
    HZ => 'haze',
266
    PY => 'spray',
266
    PY => 'spray',
267
267
268
    PO => 'dust/sand whirls',
268
    PO => 'dust/sand whirls',
269
    SQ => 'squalls',
269
    SQ => 'squalls',
270
    FC => 'funnel cloud(tornado/waterspout)',
270
    FC => 'funnel cloud(tornado/waterspout)',
271
    SS => 'sand storm',
271
    SS => 'sand storm',
272
    DS => 'dust storm',
272
    DS => 'dust storm',
273
);
273
);
274
274
275
my $_weather_types_pat = join("|", keys(%_weather_types));
275
my $_weather_types_pat = join("|", keys(%_weather_types));
276
276
277
my %_weather_types_ru = (
277
my %_weather_types_ru = (
278
    MI => 'мелкий',
278
    MI => 'мелкий',
279
    PI => 'частный',
279
    PI => 'частный',
280
    BC => 'местами',
280
    BC => 'местами',
281
    DR => 'мелкий дождь',
281
    DR => 'мелкий дождь',
282
    BL => 'порывистый ветер',
282
    BL => 'порывистый ветер',
283
    SH => 'ливневый дождь',
283
    SH => 'ливневый дождь',
284
    TS => 'гроза',
284
    TS => 'гроза',
285
    FZ => 'заморозки',
285
    FZ => 'заморозки',
286
286
287
    DZ => 'мелкий дождь',
287
    DZ => 'мелкий дождь',
288
    RA => 'дождь',
288
    RA => 'дождь',
289
    SN => 'снег',
289
    SN => 'снег',
290
    SG => 'снег гранулами',
290
    SG => 'снег гранулами',
291
    IC => 'ледяные кристаллы',
291
    IC => 'ледяные кристаллы',
292
    PE => 'ледяные шарики',
292
    PE => 'ледяные шарики',
293
    GR => 'град',
293
    GR => 'град',
294
    GS => 'небольшой град',
294
    GS => 'небольшой град',
295
    UP => 'осадки',
295
    UP => 'осадки',
296
296
297
    BR => 'легкий туман',
297
    BR => 'легкий туман',
298
    FG => 'туман',
298
    FG => 'туман',
299
    PRFG => 'образование тумана',
299
    PRFG => 'образование тумана',
300
    FU => 'дым',
300
    FU => 'дым',
301
    VA => 'вулканический пепел',
301
    VA => 'вулканический пепел',
302
    DU => 'пыль',
302
    DU => 'пыль',
303
    SA => 'песок',
303
    SA => 'песок',
304
    HZ => 'дымка',
304
    HZ => 'дымка',
305
    PY => 'водяная пыль',
305
    PY => 'водяная пыль',
306
    PO => 'песчаные или пылевые вихри',
306
    PO => 'песчаные или пылевые вихри',
307
    SQ => 'шквалистый ветер',
307
    SQ => 'шквалистый ветер',
308
    FC => 'торнадо',
308
    FC => 'торнадо',
309
    SS => 'песчанная буря',
309
    SS => 'песчанная буря',
310
    DS => 'пылевая буря',
310
    DS => 'пылевая буря',
311
);
311
);
312
312
313
my $_weather_types_ru_pat = join("|", keys(%_weather_types_ru));
313
my $_weather_types_ru_pat = join("|", keys(%_weather_types_ru));
314
314
315
my %_sky_types = (
315
my %_sky_types = (
316
    SKC => "Sky Clear",
316
    SKC => "Sky Clear",
317
    CLR => "Sky Clear",
317
    CLR => "Sky Clear",
318
    SCT => "Scattered",
318
    SCT => "Scattered",
319
    BKN => "Broken",
319
    BKN => "Broken",
320
    FEW => "Few",
320
    FEW => "Few",
321
    OVC => "Solid Overcast",
321
    OVC => "Solid Overcast",
322
    NSC => "No significant clouds",
322
    NSC => "No significant clouds",
323
    NCD => "No cloud detected",
323
    NCD => "No cloud detected",
324
);
324
);
325
325
326
my %_sky_types_ru = (
326
my %_sky_types_ru = (
327
    SKC => "ясно",
327
    SKC => "ясно",
328
    CLR => "ясно",
328
    CLR => "ясно",
329
    SCT => "переменная облачность",
329
    SCT => "переменная облачность",
330
    BKN => "переменная облачность",
330
    BKN => "переменная облачность",
331
    FEW => "слабая облачность",
331
    FEW => "слабая облачность",
332
    OVC => "сплошная облачность",
332
    OVC => "сплошная облачность",
333
    NSC => "нет существенной облачности",
333
    NSC => "нет существенной облачности",
334
    NCD => "безоблачно",
334
    NCD => "безоблачно",
335
);
335
);
336
336
337
my %_trend_types = (
337
my %_trend_types = (
338
    BLU => "8 km view",
338
    BLU => "8 km view",
339
    WHT => "5 km view",
339
    WHT => "5 km view",
340
    GRN => "3.7 km view",
340
    GRN => "3.7 km view",
341
    YLO => "1.6 km view",
341
    YLO => "1.6 km view",
342
    AMB => "0.8 km view",
342
    AMB => "0.8 km view",
343
    RED => "< 0.8 km view",
343
    RED => "< 0.8 km view",
344
    BLACK => "airport closed",
344
    BLACK => "airport closed",
345
    NOSIG => "No significant change",
345
    NOSIG => "No significant change",
346
    TEMPO => "Temporary change",
346
    TEMPO => "Temporary change",
347
    NSW => "No significant weather",
347
    NSW => "No significant weather",
348
    PROB => "Probability",
348
    PROB => "Probability",
349
    BECMG => "Becoming",
349
    BECMG => "Becoming",
350
    LAST => "Last",
350
    LAST => "Last",
351
);
351
);
352
352
353
my $_trend_types_pat = join("|", keys(%_trend_types));
353
my $_trend_types_pat = join("|", keys(%_trend_types));
354
354
355
##
355
##
356
## Constructor.
356
## Constructor.
357
##
357
##
358
358
359
sub new
359
sub new
360
{
360
{
361
    my $this = shift;
361
    my $this = shift;
362
    my $class = ref($this) || $this;
362
    my $class = ref($this) || $this;
363
    my $self = {};
363
    my $self = {};
364
364
365
    ##
365
    ##
366
    ## UPPERCASE items have documented accssor functions (methods) or
366
    ## UPPERCASE items have documented accssor functions (methods) or
367
    ## use AUTOLOAD, while lowercase items are reserved for internal
367
    ## use AUTOLOAD, while lowercase items are reserved for internal
368
    ## use.
368
    ## use.
369
    ##
369
    ##
370
370
371
    $self->{VERSION}       = $VERSION;          # version number
371
    $self->{VERSION}       = $VERSION;          # version number
372
    $self->{METAR}         = undef;             # the actual, raw METAR
372
    $self->{METAR}         = undef;             # the actual, raw METAR
373
    $self->{TYPE}          = undef;             # the type of report
373
    $self->{TYPE}          = undef;             # the type of report
374
    $self->{SITE}          = undef;             # site code
374
    $self->{SITE}          = undef;             # site code
375
    $self->{DATE}          = undef;             # when it was issued
375
    $self->{DATE}          = undef;             # when it was issued
376
    $self->{TIME}          = undef;             # time it was issued
376
    $self->{TIME}          = undef;             # time it was issued
377
    $self->{MOD}           = undef;             # modifier (AUTO/COR)
377
    $self->{MOD}           = undef;             # modifier (AUTO/COR)
378
    $self->{WIND_DIR_DEG}  = undef;             # wind dir in degrees
378
    $self->{WIND_DIR_DEG}  = undef;             # wind dir in degrees
379
    $self->{WIND_DIR_ENG}  = undef;             # wind dir in english (Northwest/Southeast)
379
    $self->{WIND_DIR_ENG}  = undef;             # wind dir in english (Northwest/Southeast)
380
    $self->{WIND_DIR_RUS}  = undef;             # wind dir in russian (Северо-западный/Юго-восточный)
380
    $self->{WIND_DIR_RUS}  = undef;             # wind dir in russian (Северо-западный/Юго-восточный)
381
    $self->{WIND_DIR_ABB}  = undef;             # wind dir in abbreviated english (NW/SE)
381
    $self->{WIND_DIR_ABB}  = undef;             # wind dir in abbreviated english (NW/SE)
382
    $self->{WIND_KTS}      = undef;             # wind speed (knots)
382
    $self->{WIND_KTS}      = undef;             # wind speed (knots)
383
    $self->{WIND_GUST_KTS} = undef;             # wind gusts (knots)
383
    $self->{WIND_GUST_KTS} = undef;             # wind gusts (knots)
384
    $self->{WIND_MPH}      = undef;             # wind speed (MPH)
384
    $self->{WIND_MPH}      = undef;             # wind speed (MPH)
385
    $self->{WIND_GUST_MPH} = undef;             # wind gusts (MPH)
385
    $self->{WIND_GUST_MPH} = undef;             # wind gusts (MPH)
386
    $self->{WIND_MS}       = undef;             # wind speed (m/s)
386
    $self->{WIND_MS}       = undef;             # wind speed (m/s)
387
    $self->{WIND_GUST_MS}  = undef;             # wind gusts (m/s)
387
    $self->{WIND_GUST_MS}  = undef;             # wind gusts (m/s)
388
    $self->{WIND_VAR}      = undef;             # wind variation (text)
388
    $self->{WIND_VAR}      = undef;             # wind variation (text)
389
    $self->{WIND_VAR_1}    = undef;             # wind variation (direction 1)
389
    $self->{WIND_VAR_1}    = undef;             # wind variation (direction 1)
390
    $self->{WIND_VAR_2}    = undef;             # wind variation (direction 2)
390
    $self->{WIND_VAR_2}    = undef;             # wind variation (direction 2)
391
    $self->{WIND_VAR_ENG_1}= undef;             # wind variation (text, direction 1)
391
    $self->{WIND_VAR_ENG_1}= undef;             # wind variation (text, direction 1)
392
    $self->{WIND_VAR_ENG_2}= undef;             # wind variation (text, direction 2)
392
    $self->{WIND_VAR_ENG_2}= undef;             # wind variation (text, direction 2)
393
    $self->{VISIBILITY}    = undef;             # visibility info
393
    $self->{VISIBILITY}    = undef;             # visibility info
394
    $self->{RUNWAY}        = [ ];               # runway vis.
394
    $self->{RUNWAY}        = [ ];               # runway vis.
395
    $self->{RH}            = undef;             # relative humidity
395
    $self->{RH}            = undef;             # relative humidity
396
    $self->{WEATHER}       = [ ];               # current weather
396
    $self->{WEATHER}       = [ ];               # current weather
397
    $self->{WEATHER_LOG}   = [ ];               # weather log
397
    $self->{WEATHER_LOG}   = [ ];               # weather log
398
    $self->{SKY}           = [ ];               # current sky (cloudcover)
398
    $self->{SKY}           = [ ];               # current sky (cloudcover)
399
    $self->{TEMP_F}        = undef;             # current temp, celsius
399
    $self->{TEMP_F}        = undef;             # current temp, celsius
400
    $self->{TEMP_C}        = undef;             # converted to fahrenheit
400
    $self->{TEMP_C}        = undef;             # converted to fahrenheit
401
    $self->{TEMP_WC}       = undef;             # current windchill temp, celsius
401
    $self->{TEMP_WC}       = undef;             # current windchill temp, celsius
402
    $self->{DEW_F}         = undef;             # dew point, celcius
402
    $self->{DEW_F}         = undef;             # dew point, celcius
403
    $self->{DEW_C}         = undef;             # dew point, fahrenheit
403
    $self->{DEW_C}         = undef;             # dew point, fahrenheit
404
    $self->{HOURLY_TEMP_F} = undef;             # hourly current temp, celcius
404
    $self->{HOURLY_TEMP_F} = undef;             # hourly current temp, celcius
405
    $self->{HOURLY_TEMP_C} = undef;             # hourly converted to fahrenheit
405
    $self->{HOURLY_TEMP_C} = undef;             # hourly converted to fahrenheit
406
    $self->{HOURLY_DEW_F}  = undef;             # hourly dew point, celcius
406
    $self->{HOURLY_DEW_F}  = undef;             # hourly dew point, celcius
407
    $self->{HOURLY_DEW_C}  = undef;             # hourly dew point, fahrenheit
407
    $self->{HOURLY_DEW_C}  = undef;             # hourly dew point, fahrenheit
408
    $self->{HOURLY_PRECIP} = undef;             # hourly precipitation
408
    $self->{HOURLY_PRECIP} = undef;             # hourly precipitation
409
    $self->{ALT}           = undef;             # altimeter setting (Hg)
409
    $self->{ALT}           = undef;             # altimeter setting (Hg)
410
    $self->{ALT_HP}        = undef;             # altimeter setting (hPa)
410
    $self->{ALT_HP}        = undef;             # altimeter setting (hPa)
411
    $self->{ALT_PL}        = undef;             # pressure (Hg)
411
    $self->{ALT_PL}        = undef;             # pressure (Hg)
412
    $self->{SLP}           = undef;             # sea level pressure
412
    $self->{SLP}           = undef;             # sea level pressure
413
    $self->{REMARKS}       = undef;             # remarks
413
    $self->{REMARKS}       = undef;             # remarks
414
    $self->{WEATHER_RAW}   = [ ];               # RAW data for weather
414
    $self->{WEATHER_RAW}   = [ ];               # RAW data for weather
415
    $self->{WEATHER_RUS}   = [ ];               # current weather in Russian
415
    $self->{WEATHER_RUS}   = [ ];               # current weather in Russian
416
    $self->{SKY_RAW}       = [ ];               # RAW data for sky
416
    $self->{SKY_RAW}       = [ ];               # RAW data for sky
417
    $self->{SKY_RUS}       = [ ];               # current sky in Russian
417
    $self->{SKY_RUS}       = [ ];               # current sky in Russian
418
    $self->{VISIBILITY_RUS}= undef;             # visibility info    
418
    $self->{VISIBILITY_RUS}= undef;             # visibility info    
419
419
420
    $self->{tokens}        = [ ];               # the "token" list
420
    $self->{tokens}        = [ ];               # the "token" list
421
    $self->{type}          = "METAR";           # the report type (METAR/SPECI)
421
    $self->{type}          = "METAR";           # the report type (METAR/SPECI)
422
                                                # default=METAR
422
                                                # default=METAR
423
    $self->{site}          = undef;             # the site code (4 chars)
423
    $self->{site}          = undef;             # the site code (4 chars)
424
    $self->{date_time}     = undef;             # date/time
424
    $self->{date_time}     = undef;             # date/time
425
    $self->{modifier}      = undef;             # the AUTO/COR modifier
425
    $self->{modifier}      = undef;             # the AUTO/COR modifier
426
    $self->{wind}          = undef;             # the wind information
426
    $self->{wind}          = undef;             # the wind information
427
    $self->{windtype}      = undef;             # the wind speed type (knots/meterpersecond/kilometersperhour)
427
    $self->{windtype}      = undef;             # the wind speed type (knots/meterpersecond/kilometersperhour)
428
    $self->{windvar}       = undef;             # the wind variation
428
    $self->{windvar}       = undef;             # the wind variation
429
    $self->{visibility}    = undef;             # visibility information
429
    $self->{visibility}    = undef;             # visibility information
430
    $self->{runway}        = undef;             # runway visibility
430
    $self->{runway}        = undef;             # runway visibility
431
    $self->{weather}       = [ ];               # current weather conditions
431
    $self->{weather}       = [ ];               # current weather conditions
432
    $self->{sky}           = [ ];               # sky conditions (cloud cover)
432
    $self->{sky}           = [ ];               # sky conditions (cloud cover)
433
    $self->{temp_dew}      = undef;             # temp and dew pt.
433
    $self->{temp_dew}      = undef;             # temp and dew pt.
434
    $self->{alt}           = undef;             # altimeter setting
434
    $self->{alt}           = undef;             # altimeter setting
435
    $self->{pressure}      = undef;             # pressure (HPa)
435
    $self->{pressure}      = undef;             # pressure (HPa)
436
    $self->{slp}           = undef;             # sea level pressure
436
    $self->{slp}           = undef;             # sea level pressure
437
    $self->{remarks}       = [ ];               # remarks
437
    $self->{remarks}       = [ ];               # remarks
438
438
439
    $self->{debug}         = undef;             # enable debug trace
439
    $self->{debug}         = undef;             # enable debug trace
440
440
441
    bless $self, $class;
441
    bless $self, $class;
442
    return $self;
442
    return $self;
443
}
443
}
444
444
445
##
445
##
446
## Autoload for access methods to stuff in %fields hash. We should
446
## Autoload for access methods to stuff in %fields hash. We should
447
## probably disallow access to the lower-case items as stated above,
447
## probably disallow access to the lower-case items as stated above,
448
## but I don't feel like being a Nazi about it. Besides, I haven't
448
## but I don't feel like being a Nazi about it. Besides, I haven't
449
## checked to see what that might break.
449
## checked to see what that might break.
450
##
450
##
451
451
452
sub AUTOLOAD
452
sub AUTOLOAD
453
{
453
{
454
    my $self = shift;
454
    my $self = shift;
455
455
456
    if (not ref $self)
456
    if (not ref $self)
457
    {
457
    {
458
        cluck "bad AUTOLOAD for obj [$self]";
458
        cluck "bad AUTOLOAD for obj [$self]";
459
    }
459
    }
460
460
461
    if ($AUTOLOAD =~ /.*::(.*)/)
461
    if ($AUTOLOAD =~ /.*::(.*)/)
462
    {
462
    {
463
        my $key = $1;
463
        my $key = $1;
464
464
465
465
466
        ## Backward compatible temps...
466
        ## Backward compatible temps...
467
467
468
        my %compat = (
468
        my %compat = (
469
                      F_TEMP    =>  'TEMP_F',
469
                      F_TEMP    =>  'TEMP_F',
470
                      C_TEMP    =>  'TEMP_C',
470
                      C_TEMP    =>  'TEMP_C',
471
                      F_DEW     =>  'DEW_F',
471
                      F_DEW     =>  'DEW_F',
472
                      C_DEW     =>  'DEW_C',
472
                      C_DEW     =>  'DEW_C',
473
                     );
473
                     );
474
474
475
        if ($compat{$key})
475
        if ($compat{$key})
476
        {
476
        {
477
            $key = $compat{$key};
477
            $key = $compat{$key};
478
        }
478
        }
479
479
480
        ## Check for the items...
480
        ## Check for the items...
481
481
482
        if (exists $self->{$key})
482
        if (exists $self->{$key})
483
        {
483
        {
484
            return $self->{$key};
484
            return $self->{$key};
485
        }
485
        }
486
        else
486
        else
487
        {
487
        {
488
            return undef;
488
            return undef;
489
        }
489
        }
490
    }
490
    }
491
    else
491
    else
492
    {
492
    {
493
        warn "strange AUTOLOAD problem!";
493
        warn "strange AUTOLOAD problem!";
494
        return undef;
494
        return undef;
495
    }
495
    }
496
}
496
}
497
497
498
##
498
##
499
## Get current version number.
499
## Get current version number.
500
##
500
##
501
501
502
sub version
502
sub version
503
{
503
{
504
    my $self = shift;
504
    my $self = shift;
505
    print "version() called.\n" if $self->{debug};
505
    print "version() called.\n" if $self->{debug};
506
    return $self->{VERSION};
506
    return $self->{VERSION};
507
}
507
}
508
508
509
##
509
##
510
## Take a METAR, tokenize, and process it.
510
## Take a METAR, tokenize, and process it.
511
##
511
##
512
512
513
sub metar
513
sub metar
514
{
514
{
515
    my $self = shift;
515
    my $self = shift;
516
516
517
    if (@_)
517
    if (@_)
518
    {
518
    {
519
        $self->{METAR} = shift;
519
        $self->{METAR} = shift;
520
        $self->{METAR} =~ s/\n//g;    ## nuke any newlines
520
        $self->{METAR} =~ s/\n//g;    ## nuke any newlines
521
        _tokenize($self);
521
        _tokenize($self);
522
        _process($self);
522
        _process($self);
523
    }
523
    }
524
    return $self->{METAR};
524
    return $self->{METAR};
525
}
525
}
526
526
527
##
527
##
528
## Break {METAR} into parts. Stuff into @tokens.
528
## Break {METAR} into parts. Stuff into @tokens.
529
##
529
##
530
530
531
sub _tokenize
531
sub _tokenize
532
{
532
{
533
    my $self = shift;
533
    my $self = shift;
534
    my $tok;
534
    my $tok;
535
    my @toks;
535
    my @toks;
536
536
537
    # Split tokens on whitespace.
537
    # Split tokens on whitespace.
538
    @toks = split(/\s+/, $self->{METAR});
538
    @toks = split(/\s+/, $self->{METAR});
539
    $self->{tokens} = \@toks;
539
    $self->{tokens} = \@toks;
540
}
540
}
541
541
542
## Process @tokens to populate METAR values.
542
## Process @tokens to populate METAR values.
543
##
543
##
544
## This is a long and involved subroutine. It basically copies the
544
## This is a long and involved subroutine. It basically copies the
545
## @tokens array and treats it as a stack, popping off items,
545
## @tokens array and treats it as a stack, popping off items,
546
## examining them, and see what they look like.  Based on their
546
## examining them, and see what they look like.  Based on their
547
## "apppearance" it takes care populating the proper fields
547
## "apppearance" it takes care populating the proper fields
548
## internally.
548
## internally.
549
549
550
sub _process
550
sub _process
551
{
551
{
552
    my $self = shift;
552
    my $self = shift;
553
553
554
    my @toks = @{$self->{tokens}};      # copy tokens array...
554
    my @toks = @{$self->{tokens}};      # copy tokens array...
555
555
556
    my $tok;
556
    my $tok;
557
557
558
    ## This is a semi-brute-force way of doing things, but the amount
558
    ## This is a semi-brute-force way of doing things, but the amount
559
    ## of data is relatively small, so it shouldn't be a big deal.
559
    ## of data is relatively small, so it shouldn't be a big deal.
560
    ##
560
    ##
561
    ## Ideally, I'd have it skip checks for items which have been
561
    ## Ideally, I'd have it skip checks for items which have been
562
    ## found, but that would make this more "linear" and I'd remove
562
    ## found, but that would make this more "linear" and I'd remove
563
    ## the pretty while loop.
563
    ## the pretty while loop.
564
        #
564
        #
565
        # KH: modified to maintain state to not get lost in remarks and stuff
565
        # KH: modified to maintain state to not get lost in remarks and stuff
566
        # and be a lot better at parsing
566
        # and be a lot better at parsing
567
       
567
       
568
        # states
568
        # states
569
569
570
        my $expect_type = 0;
570
        my $expect_type = 0;
571
        my $expect_site = 1;
571
        my $expect_site = 1;
572
        my $expect_datetime = 2;
572
        my $expect_datetime = 2;
573
        my $expect_modifier = 3;
573
        my $expect_modifier = 3;
574
        my $expect_wind = 4;
574
        my $expect_wind = 4;
575
        my $expect_visibility = 5;
575
        my $expect_visibility = 5;
576
        my $expect_runwayvisual = 6;
576
        my $expect_runwayvisual = 6;
577
        my $expect_presentweather = 7;
577
        my $expect_presentweather = 7;
578
        my $expect_clouds = 8;
578
        my $expect_clouds = 8;
579
        my $expect_temperature = 9;
579
        my $expect_temperature = 9;
580
        my $expect_pressure = 10;
580
        my $expect_pressure = 10;
581
        my $expect_recentweather = 11;
581
        my $expect_recentweather = 11;
582
        my $expect_remarks = 12;
582
        my $expect_remarks = 12;
583
        my $expect_usremarks = 13;
583
        my $expect_usremarks = 13;
584
584
585
        my $parsestate = $expect_type;
585
        my $parsestate = $expect_type;
586
586
587
        # windtypes
587
        # windtypes
588
       
588
       
589
        my $wt_knots = 1;
589
        my $wt_knots = 1;
590
        my $wt_mps = 2;
590
        my $wt_mps = 2;
591
        my $wt_kph = 3;
591
        my $wt_kph = 3;
592
592
593
    ## Assume standard report by default
593
    ## Assume standard report by default
594
594
595
    $self->{type} = "METAR";
595
    $self->{type} = "METAR";
596
    $self->{TYPE} = "Routine Weather Report";
596
    $self->{TYPE} = "Routine Weather Report";
597
597
598
    while (defined($tok = shift(@toks))) ## as long as there are tokens
598
    while (defined($tok = shift(@toks))) ## as long as there are tokens
599
    {
599
    {
600
        print "trying to match [$tok] state is $parsestate\n" if $self->{debug};
600
        print "trying to match [$tok] state is $parsestate\n" if $self->{debug};
601
601
602
        ##
602
        ##
603
        ## is it a report type?
603
        ## is it a report type?
604
        ##
604
        ##
605
605
606
        if (($parsestate == $expect_type) and ($tok =~ /(METAR|SPECI)/i))
606
        if (($parsestate == $expect_type) and ($tok =~ /(METAR|SPECI)/i))
607
        {
607
        {
608
            $self->{type} = $tok;
608
            $self->{type} = $tok;
609
609
610
            if ($self->{type} eq "METAR")
610
            if ($self->{type} eq "METAR")
611
            {
611
            {
612
                $self->{TYPE} = "Routine Weather Report";
612
                $self->{TYPE} = "Routine Weather Report";
613
            }
613
            }
614
            elsif ($self->{type} eq "SPECI")
614
            elsif ($self->{type} eq "SPECI")
615
            {
615
            {
616
                $self->{TYPE} = "Special Weather Report";
616
                $self->{TYPE} = "Special Weather Report";
617
            }
617
            }
618
            print "[$tok] is a report type.\n" if $self->{debug};
618
            print "[$tok] is a report type.\n" if $self->{debug};
619
                        $parsestate = $expect_site;
619
                        $parsestate = $expect_site;
620
            next;
620
            next;
621
        }
621
        }
622
622
623
        ##
623
        ##
624
        ## is is a site ID?
624
        ## is is a site ID?
625
        ##
625
        ##
626
626
627
        elsif (($parsestate <= $expect_site) and ($tok =~ /([A-Z]{4}|K[A-Z0-9]{3})/))
627
        elsif (($parsestate <= $expect_site) and ($tok =~ /([A-Z]{4}|K[A-Z0-9]{3})/))
628
        {
628
        {
629
            $self->{site} = $tok;
629
            $self->{site} = $tok;
630
            print "[$tok] is a site ID.\n" if $self->{debug};
630
            print "[$tok] is a site ID.\n" if $self->{debug};
631
                        $parsestate = $expect_datetime;
631
                        $parsestate = $expect_datetime;
632
            next;
632
            next;
633
        }
633
        }
634
634
635
        ##
635
        ##
636
        ## is it a date/time?
636
        ## is it a date/time?
637
        ##
637
        ##
638
638
639
        elsif (($parsestate == $expect_datetime) and ($tok =~ /\d{6,6}Z/i))
639
        elsif (($parsestate == $expect_datetime) and ($tok =~ /\d{6,6}Z/i))
640
        {
640
        {
641
            $self->{date_time} = $tok;
641
            $self->{date_time} = $tok;
642
            print "[$tok] is a date/time.\n" if $self->{debug};
642
            print "[$tok] is a date/time.\n" if $self->{debug};
643
                        $parsestate = $expect_modifier;
643
                        $parsestate = $expect_modifier;
644
            next;
644
            next;
645
645
646
646
647
        }
647
        }
648
648
649
        ##
649
        ##
650
        ## is it a report modifier?
650
        ## is it a report modifier?
651
        ##
651
        ##
652
652
653
        elsif (($parsestate == $expect_modifier) and ($tok =~ /AUTO|COR|CC[A-Z]/i))
653
        elsif (($parsestate == $expect_modifier) and ($tok =~ /AUTO|COR|CC[A-Z]/i))
654
        {
654
        {
655
            $self->{modifier} = $tok;
655
            $self->{modifier} = $tok;
656
            print "[$tok] is a report modifier.\n" if $self->{debug};
656
            print "[$tok] is a report modifier.\n" if $self->{debug};
657
                        $parsestate = $expect_wind;
657
                        $parsestate = $expect_wind;
658
            next;
658
            next;
659
        }
659
        }
660
660
661
        ##
661
        ##
662
        ## is it wind information in knots?
662
        ## is it wind information in knots?
663
        #
663
        #
664
                # eew: KT seems to be optional
664
                # eew: KT seems to be optional
665
                # but making it optional fails on other stuff
665
                # but making it optional fails on other stuff
666
                # sortafix: wind needs to be \d\d\d\d\d or VRB\d\d
666
                # sortafix: wind needs to be \d\d\d\d\d or VRB\d\d
667
                #      optional \d\d\d\d\dG\d\d\d (gust direction)
667
                #      optional \d\d\d\d\dG\d\d\d (gust direction)
668
668
669
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /(\d{3}|VRB)\d{2}(G\d{1,3})?(KT)?$/i))
669
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /(\d{3}|VRB)\d{2}(G\d{1,3})?(KT)?$/i))
670
        {
670
        {
671
            $self->{wind} = $tok;
671
            $self->{wind} = $tok;
672
                        $self->{windtype} = $wt_knots;
672
                        $self->{windtype} = $wt_knots;
673
            print "[$tok] is wind information in knots.\n" if $self->{debug};
673
            print "[$tok] is wind information in knots.\n" if $self->{debug};
674
                        $parsestate = $expect_wind; # stay in wind, it can have variation
674
                        $parsestate = $expect_wind; # stay in wind, it can have variation
675
            next;
675
            next;
676
        }
676
        }
677
677
678
                ##
678
                ##
679
                ## is it wind information in meters per second?
679
                ## is it wind information in meters per second?
680
                ##
680
                ##
681
                ## can be variable too
681
                ## can be variable too
682
682
683
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /^(\d{3}|VRB)\d{2}(G\d{2,3})?MPS$/))
683
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /^(\d{3}|VRB)\d{2}(G\d{2,3})?MPS$/))
684
        {
684
        {
685
            $self->{wind} = $tok;
685
            $self->{wind} = $tok;
686
            print "[$tok] is wind information.\n" if $self->{debug};
686
            print "[$tok] is wind information.\n" if $self->{debug};
687
                        $self->{windtype} = $wt_mps;
687
                        $self->{windtype} = $wt_mps;
688
                        $parsestate = $expect_wind; # stay in wind, it can have variation
688
                        $parsestate = $expect_wind; # stay in wind, it can have variation
689
            next;
689
            next;
690
        }
690
        }
691
691
692
                ##
692
                ##
693
                ## is it wind variation information?
693
                ## is it wind variation information?
694
                ##
694
                ##
695
695
696
                elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\d{3}V\d{3}$/))
696
                elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\d{3}V\d{3}$/))
697
                {
697
                {
698
                        $self->{windvar} = $tok;
698
                        $self->{windvar} = $tok;
699
                        print "[$tok] is wind variation information.\n" if $self->{debug};
699
                        print "[$tok] is wind variation information.\n" if $self->{debug};
700
                        $parsestate = $expect_visibility;
700
                        $parsestate = $expect_visibility;
701
                        next;
701
                        next;
702
                }
702
                }
703
703
704
                ##
704
                ##
705
                ## wind information missing at the moment?
705
                ## wind information missing at the moment?
706
                ##
706
                ##
707
707
708
                elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\/\/\/\/\/(KT|MPS)$/)){
708
                elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\/\/\/\/\/(KT|MPS)$/)){
709
                        print "[$tok] is missing wind information.\n" if $self->{debug};
709
                        print "[$tok] is missing wind information.\n" if $self->{debug};
710
                        $parsestate = $expect_visibility;
710
                        $parsestate = $expect_visibility;
711
                        next;
711
                        next;
712
                }
712
                }
713
713
714
                ##
714
                ##
715
                ## is it visibility information in meters?
715
                ## is it visibility information in meters?
716
                ##
716
                ##
717
       
717
       
718
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}$/))
718
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}$/))
719
                {
719
                {
720
                        $self->{visibility} = $tok;
720
                        $self->{visibility} = $tok;
721
            print "[$tok] is numerical visibility information.\n" if $self->{debug};
721
            print "[$tok] is numerical visibility information.\n" if $self->{debug};
722
                        $parsestate = $expect_visibility;
722
                        $parsestate = $expect_visibility;
723
            next;
723
            next;
724
        }
724
        }
725
725
726
                ## auto visibility information in meters?
726
                ## auto visibility information in meters?
727
727
728
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}NDV$/))
728
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}NDV$/))
729
                {
729
                {
730
                        $self->{visibility} = $tok;
730
                        $self->{visibility} = $tok;
731
            print "[$tok] is automatic numerical visibility information.\n" if $self->{debug};
731
            print "[$tok] is automatic numerical visibility information.\n" if $self->{debug};
732
                        $parsestate = $expect_visibility;
732
                        $parsestate = $expect_visibility;
733
            next;
733
            next;
734
        }
734
        }
735
735
736
        ##
736
        ##
737
        ## is it visibility information in statute miles?
737
        ## is it visibility information in statute miles?
738
        ##
738
        ##
739
739
740
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /.*?SM$/i))
740
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /.*?SM$/i))
741
        {
741
        {
742
            $self->{visibility} = $tok;
742
            $self->{visibility} = $tok;
743
            print "[$tok] is statute miles visibility information.\n" if $self->{debug};
743
            print "[$tok] is statute miles visibility information.\n" if $self->{debug};
744
                        $parsestate = $expect_visibility;
744
                        $parsestate = $expect_visibility;
745
            next;
745
            next;
746
        }
746
        }
747
747
748
        ##
748
        ##
749
        ## is it visibility information with a leading digit?
749
        ## is it visibility information with a leading digit?
750
                ##
750
                ##
751
                ## sample:
751
                ## sample:
752
                ## KERV 132345Z AUTO 07008KT 1 1/4SM HZ 34/11 A3000 RMK AO2
752
                ## KERV 132345Z AUTO 07008KT 1 1/4SM HZ 34/11 A3000 RMK AO2
753
                ##                           ^^^^^^^
753
                ##                           ^^^^^^^
754
        ##
754
        ##
755
755
756
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and  ($tok =~ /^\d$/))
756
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and  ($tok =~ /^\d$/))
757
        {
757
        {
758
            $tok .= " " . shift(@toks);
758
            $tok .= " " . shift(@toks);
759
            $self->{visibility} = $tok;
759
            $self->{visibility} = $tok;
760
            print "[$tok] is multi-part visibility information.\n" if $self->{debug};
760
            print "[$tok] is multi-part visibility information.\n" if $self->{debug};
761
                        $parsestate = $expect_visibility;
761
                        $parsestate = $expect_visibility;
762
            next;
762
            next;
763
        }
763
        }
764
764
765
                ## visibility modifier
765
                ## visibility modifier
766
766
767
                elsif (($parsestate == $expect_visibility) and ($tok =~ /^\d{4}(N|S|E|W|NE|NW|SE|SW)$/))
767
                elsif (($parsestate == $expect_visibility) and ($tok =~ /^\d{4}(N|S|E|W|NE|NW|SE|SW)$/))
768
                {
768
                {
769
            print "[$tok] is a visibility modifier.\n" if $self->{debug};
769
            print "[$tok] is a visibility modifier.\n" if $self->{debug};
770
            next;
770
            next;
771
                }
771
                }
772
772
773
        ##
773
        ##
774
        ## is it runway visibility info?
774
        ## is it runway visibility info?
775
        ##
775
        ##
776
                # KH: I've seen runway visibility with 'U' units
776
                # KH: I've seen runway visibility with 'U' units
777
                # EHSB 121425Z 22010KT 1200 R27/1600U -DZ BKN003 OVC007 07/07 Q1016 AMB FCST CANCEL
777
                # EHSB 121425Z 22010KT 1200 R27/1600U -DZ BKN003 OVC007 07/07 Q1016 AMB FCST CANCEL
778
                # U= going up, D= going down, N= no change
778
                # U= going up, D= going down, N= no change
779
                # tendency of visual range, http://stoivane.kapsi.fi/metar/
779
                # tendency of visual range, http://stoivane.kapsi.fi/metar/
780
780
781
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_presentweather) and ($tok =~ /R\d+(L|R|C)?\/P?\d+(VP?\d+)?(FT|D|U|N|\/)?$/i))
781
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_presentweather) and ($tok =~ /R\d+(L|R|C)?\/P?\d+(VP?\d+)?(FT|D|U|N|\/)?$/i))
782
        {
782
        {
783
            push (@{$self->{RUNWAY}},$tok);
783
            push (@{$self->{RUNWAY}},$tok);
784
            print "[$tok] is runway visual information.\n" if $self->{debug};
784
            print "[$tok] is runway visual information.\n" if $self->{debug};
785
                        $parsestate = $expect_runwayvisual;
785
                        $parsestate = $expect_runwayvisual;
786
                        # there can be multiple runways, so stay at this state
786
                        # there can be multiple runways, so stay at this state
787
            next;
787
            next;
788
        }
788
        }
789
789
790
        ##
790
        ##
791
        ## is it current weather info?
791
        ## is it current weather info?
792
        ##
792
        ##
793
793
794
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_clouds) and ($tok =~ /^(-|\+)?(VC)?($_weather_types_pat)+/i))
794
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_clouds) and ($tok =~ /^(-|\+)?(VC)?($_weather_types_pat)+/i))
795
        {
795
        {
796
            my $engl = "";
796
            my $engl = "";
797
            my $rusl = "";
797
            my $rusl = "";
798
            my $rawl = "";
798
            my $rawl = "";
799
            my $qual = $1;
799
            my $qual = $1;
800
            my $addlqual = $2;
800
            my $addlqual = $2;
801
801
802
            ## qualifier
802
            ## qualifier
803
803
804
            if (defined $qual)
804
            if (defined $qual)
805
            {
805
            {
806
                if ( $qual eq "-" ) {
806
                if ( $qual eq "-" ) {
807
                    $engl = "light";
807
                    $engl = "light";
808
                    $rusl = "легкий";
808
                    $rusl = "легкий";
809
                } elsif ( $qual eq "+" ) {
809
                } elsif ( $qual eq "+" ) {
810
                    $engl = "heavy";
810
                    $engl = "heavy";
811
                    $rusl = "сильный";
811
                    $rusl = "сильный";
812
                } else {
812
                } else {
813
                    $engl = ""; ## moderate
813
                    $engl = ""; ## moderate
814
                    $rusl = "";
814
                    $rusl = "";
815
                }
815
                }
816
                $rawl = $qual;
816
                $rawl = $qual;
817
            }
817
            }
818
            else
818
            else
819
            {
819
            {
820
                $engl = ""; ## moderate
820
                $engl = ""; ## moderate
821
                $rusl = "";
821
                $rusl = "";
822
                $rawl = "";
822
                $rawl = "";
823
            }
823
            }
824
824
825
            while ( $tok =~ /($_weather_types_pat)/gi )
825
            while ( $tok =~ /($_weather_types_pat)/gi )
826
            {
826
            {
827
                $engl .= " " . $_weather_types{$1}; ## figure out weather                
827
                $engl .= " " . $_weather_types{$1}; ## figure out weather                
828
                $rusl .= " " . $_weather_types_ru{$1};
828
                $rusl .= " " . $_weather_types_ru{$1};
829
                $rawl .= " " . $1;
829
                $rawl .= " " . $1;
830
            }
830
            }
831
           
831
           
832
832
833
            ## addl qualifier
833
            ## addl qualifier
834
834
835
            if (defined $addlqual)
835
            if (defined $addlqual)
836
            {
836
            {
837
                if ( $addlqual eq "VC" )
837
                if ( $addlqual eq "VC" )
838
                {
838
                {
839
                    $engl .= " in vicinity";
839
                    $engl .= " in vicinity";
840
                    $rusl .= " в окрестностях";
840
                    $rusl .= " в окрестностях";
841
                }
841
                }
842
            }
842
            }
843
843
844
            $engl =~ s/^\s//gio;
844
            $engl =~ s/^\s//gio;
845
            $engl =~ s/\s\s/ /gio;
845
            $engl =~ s/\s\s/ /gio;
846
            $rusl =~ s/^\s//gio;
846
            $rusl =~ s/^\s//gio;
847
            $rusl =~ s/\s\s/ /gio;
847
            $rusl =~ s/\s\s/ /gio;
848
            $rawl =~ s/^\s//gio;
848
            $rawl =~ s/^\s//gio;
849
            $rawl =~ s/\s\s/ /gio;
849
            $rawl =~ s/\s\s/ /gio;
850
850
851
            push(@{$self->{WEATHER}},$engl);
851
            push(@{$self->{WEATHER}},$engl);
852
            push(@{$self->{WEATHER_RUS}},$rusl);
852
            push(@{$self->{WEATHER_RUS}},$rusl);
853
            push(@{$self->{WEATHER_RAW}},$rawl);
853
            push(@{$self->{WEATHER_RAW}},$rawl);
854
            push(@{$self->{weather}},$tok);
854
            push(@{$self->{weather}},$tok);
855
            print "[$tok] is current weather.\n" if $self->{debug};
855
            print "[$tok] is current weather.\n" if $self->{debug};
856
                        $parsestate = $expect_presentweather;
856
                        $parsestate = $expect_presentweather;
857
                        # there can be multiple current weather types, so stay at this state
857
                        # there can be multiple current weather types, so stay at this state
858
            next;
858
            next;
859
        }
859
        }
860
860
861
                ##
861
                ##
862
                ## special case: CAVOK
862
                ## special case: CAVOK
863
                ##
863
                ##
864
               
864
               
865
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok eq 'CAVOK' ))
865
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok eq 'CAVOK' ))
866
                {
866
                {
867
            push(@{$self->{sky}},$tok);
867
            push(@{$self->{sky}},$tok);
868
            push(@{$self->{SKY}}, "Sky Clear");
868
            push(@{$self->{SKY}}, "Sky Clear");
869
            push(@{$self->{SKY_RUS}}, "Ясно");
869
            push(@{$self->{SKY_RUS}}, "Ясно");
870
            push(@{$self->{SKY_RAW}},$tok);
870
            push(@{$self->{SKY_RAW}},$tok);
871
            push(@{$self->{weather}},$tok);
871
            push(@{$self->{weather}},$tok);
872
            push(@{$self->{WEATHER}},"No significant weather");
872
            push(@{$self->{WEATHER}},"No significant weather");
873
                        $self->{visibility} = '9999';
873
                        $self->{visibility} = '9999';
874
                        $parsestate = $expect_temperature;
874
                        $parsestate = $expect_temperature;
875
                        next;
875
                        next;
876
                }
876
                }
877
877
878
        ##
878
        ##
879
        ## is it sky conditions (clear)?
879
        ## is it sky conditions (clear)?
880
        ##
880
        ##
881
881
882
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /SKC|CLR/ ))
882
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /SKC|CLR/ ))
883
        {
883
        {
884
            push(@{$self->{sky}},$tok);
884
            push(@{$self->{sky}},$tok);
885
            push(@{$self->{SKY}}, "Sky Clear");
885
            push(@{$self->{SKY}}, "Sky Clear");
886
            push(@{$self->{SKY_RUS}}, "Ясно");
886
            push(@{$self->{SKY_RUS}}, "Ясно");
887
            push(@{$self->{SKY_RAW}},$tok);
887
            push(@{$self->{SKY_RAW}},$tok);
888
            print "[$tok] is a sky condition.\n" if $self->{debug};
888
            print "[$tok] is a sky condition.\n" if $self->{debug};
889
                        $parsestate = $expect_clouds;
889
                        $parsestate = $expect_clouds;
890
                        next;
890
                        next;
891
        }
891
        }
892
892
893
        ##
893
        ##
894
        ## is it sky conditions (clouds)?
894
        ## is it sky conditions (clouds)?
895
        ##
895
        ##
896
                ## sky conditions can end with ///
896
                ## sky conditions can end with ///
897
897
898
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(FEW|SCT|BKN|OVC)(\d\d\d)?(CB|TCU)?\/*$/i))
898
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(FEW|SCT|BKN|OVC)(\d\d\d)?(CB|TCU)?\/*$/i))
899
        {
899
        {
900
            push(@{$self->{sky}},$tok);
900
            push(@{$self->{sky}},$tok);
901
            my $engl = "";
901
            my $engl = "";
902
            my $rusl = "";
902
            my $rusl = "";
903
            my $rawl = "";
903
            my $rawl = "";
904
904
905
            $engl = $_sky_types{$1};
905
            $engl = $_sky_types{$1};
906
            $rusl = $_sky_types_ru{$1};
906
            $rusl = $_sky_types_ru{$1};
907
            $rawl = $1;
907
            $rawl = $1;
908
908
909
            if (defined $3)
909
            if (defined $3)
910
            {
910
            {
911
                if ($3 eq "TCU")
911
                if ($3 eq "TCU")
912
                {
912
                {
913
                    $engl .= " Towering Cumulus";
913
                    $engl .= " Towering Cumulus";
914
                    $rusl .= ", кучевые облака";                    
914
                    $rusl .= ", кучевые облака";                    
915
                }
915
                }
916
                elsif ($3 eq "CB")
916
                elsif ($3 eq "CB")
917
                {
917
                {
918
                    $engl .= " Cumulonimbus";
918
                    $engl .= " Cumulonimbus";
919
                    $rusl .= ", кучево-дождевые облака";
919
                    $rusl .= ", кучево-дождевые облака";
920
                }
920
                }
921
                $rawl = $3;
921
                $rawl = $3;
922
            }
922
            }
923
923
924
            if ($2 ne "")
924
            if ($2 ne "")
925
            {
925
            {
926
                my $agl = int($2)*100;
926
                my $agl = int($2)*100;
927
                $engl .= " at $agl" . "ft";
927
                $engl .= " at $agl" . "ft";
928
                $rusl .= " на высоте " . int(($agl*0.3048)/10)*10 . " м";
928
                $rusl .= " на высоте " . int(($agl*0.3048)/10)*10 . " м";
929
            }
929
            }
930
930
931
            push(@{$self->{SKY}}, $engl);
931
            push(@{$self->{SKY}}, $engl);
932
            push(@{$self->{SKY_RUS}}, $rusl);
932
            push(@{$self->{SKY_RUS}}, $rusl);
933
            push(@{$self->{SKY_RAW}}, $rawl);
933
            push(@{$self->{SKY_RAW}}, $rawl);
934
            print "[$tok] is a sky condition.\n" if $self->{debug};
934
            print "[$tok] is a sky condition.\n" if $self->{debug};
935
                        $parsestate = $expect_clouds;
935
                        $parsestate = $expect_clouds;
936
                        # clouds DO repeat. a lot ;)
936
                        # clouds DO repeat. a lot ;)
937
            next;
937
            next;
938
        }
938
        }
939
939
940
                ##
940
                ##
941
                ## auto detected cloud conditions
941
                ## auto detected cloud conditions
942
                ##
942
                ##
943
943
944
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(NSC|NCD)$/ )){
944
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(NSC|NCD)$/ )){
945
            my $engl = "";
945
            my $engl = "";
946
            my $rusl = "";
946
            my $rusl = "";
947
947
948
            $engl = $_sky_types{$tok};
948
            $engl = $_sky_types{$tok};
949
            $rusl = $_sky_types_ru{$tok};
949
            $rusl = $_sky_types_ru{$tok};
950
            push(@{$self->{SKY}}, $engl);
950
            push(@{$self->{SKY}}, $engl);
951
            push(@{$self->{SKY_RUS}}, $rusl);
951
            push(@{$self->{SKY_RUS}}, $rusl);
952
            push(@{$self->{SKY_RAW}}, $tok);
952
            push(@{$self->{SKY_RAW}}, $tok);
953
                        print "[$tok] is an automatic sky condition.\n" if $self->{debug};
953
                        print "[$tok] is an automatic sky condition.\n" if $self->{debug};
954
                        $parsestate = $expect_temperature;
954
                        $parsestate = $expect_temperature;
955
                        next;
955
                        next;
956
                }
956
                }
957
957
958
                ##
958
                ##
959
                ## Vertical visibility
959
                ## Vertical visibility
960
                ##
960
                ##
961
961
962
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^VV\d+$/ )){
962
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^VV\d+$/ )){
963
                        print "[$tok] is vertical visibility.\n" if $self->{debug};
963
                        print "[$tok] is vertical visibility.\n" if $self->{debug};
964
                        $parsestate = $expect_temperature;
964
                        $parsestate = $expect_temperature;
965
                        next;
965
                        next;
966
                }
966
                }
967
967
968
        ##
968
        ##
969
        ## is it temperature and dew point info?
969
        ## is it temperature and dew point info?
970
        ##
970
        ##
971
971
972
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_pressure) and ($tok =~ /^(M?\d\d)\/(M?\d{0,2})/i))
972
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_pressure) and ($tok =~ /^(M?\d\d)\/(M?\d{0,2})/i))
973
        {
973
        {
974
            next if $self->{temp_dew};
974
            next if $self->{temp_dew};
975
            $self->{temp_dew} = $tok;
975
            $self->{temp_dew} = $tok;
976
976
977
            $self->{TEMP_C} = $1;
977
            $self->{TEMP_C} = $1;
978
            $self->{DEW_C} = $2;
978
            $self->{DEW_C} = $2;
979
            $self->{TEMP_C} =~ s/^M/-/;
979
            $self->{TEMP_C} =~ s/^M/-/;
980
            $self->{DEW_C} =~ s/^M/-/;
980
            $self->{DEW_C} =~ s/^M/-/;
981
981
982
            print "[$tok] is temperature/dew point information.\n" if $self->{debug};
982
            print "[$tok] is temperature/dew point information.\n" if $self->{debug};
983
                        $parsestate = $expect_pressure;
983
                        $parsestate = $expect_pressure;
984
            next;
984
            next;
985
        }
985
        }
986
986
987
        ##
987
        ##
988
        ## is it an altimeter setting? (in.Hg)
988
        ## is it an altimeter setting? (in.Hg)
989
        ##
989
        ##
990
990
991
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^A(\d\d)(\d\d)$/i))
991
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^A(\d\d)(\d\d)$/i))
992
        {
992
        {
993
            $self->{alt} = $tok;
993
            $self->{alt} = $tok;
994
            $self->{ALT} = "$1.$2"+0;
994
            $self->{ALT} = "$1.$2"+0;
995
            $self->{ALT_HP} = "$1.$2" * 33.863886;
995
            $self->{ALT_HP} = "$1.$2" * 33.863886;
996
996
997
            print "[$tok] is an altimeter setting.\n" if $self->{debug};
997
            print "[$tok] is an altimeter setting.\n" if $self->{debug};
998
                        $parsestate = $expect_recentweather;
998
                        $parsestate = $expect_recentweather;
999
            next;
999
            next;
1000
        }
1000
        }
1001
1001
1002
                ##
1002
                ##
1003
                ## is it a pressure? (hPa)
1003
                ## is it a pressure? (hPa)
1004
                ##
1004
                ##
1005
1005
1006
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^Q(\d\d\d\d)$/i))
1006
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^Q(\d\d\d\d)$/i))
1007
                {
1007
                {
1008
                        $self->{pressure} = $1;
1008
                        $self->{pressure} = $1;
1009
            $self->{ALT_HP} = $1;
1009
            $self->{ALT_HP} = $1;
1010
                        $self->{ALT} = 0.029529983 * $self->{pressure};
1010
                        $self->{ALT} = 0.029529983 * $self->{pressure};
1011
                        print "[$tok] is an air pressure.\n" if $self->{debug};
1011
                        print "[$tok] is an air pressure.\n" if $self->{debug};
1012
                        $parsestate = $expect_recentweather;
1012
                        $parsestate = $expect_recentweather;
1013
                        next;
1013
                        next;
1014
                }
1014
                }
1015
1015
1016
                ##
1016
                ##
1017
                ## recent weather?
1017
                ## recent weather?
1018
                ##
1018
                ##
1019
1019
1020
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^RE($_weather_types_pat)$/)){
1020
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^RE($_weather_types_pat)$/)){
1021
                        print "[$tok] is recent significant weather.\n" if $self->{debug};
1021
                        print "[$tok] is recent significant weather.\n" if $self->{debug};
1022
                        $parsestate = $expect_remarks;
1022
                        $parsestate = $expect_remarks;
1023
                        next;
1023
                        next;
1024
                }
1024
                }
1025
1025
1026
                ##
1026
                ##
1027
                ## euro type trend?
1027
                ## euro type trend?
1028
                ##
1028
                ##
1029
1029
1030
                elsif (($parsestate >= $expect_modifier) and ($tok =~ /^$_trend_types_pat/)){
1030
                elsif (($parsestate >= $expect_modifier) and ($tok =~ /^$_trend_types_pat/)){
1031
                        print "[$tok] is a trend.\n" if $self->{debug};
1031
                        print "[$tok] is a trend.\n" if $self->{debug};
1032
                        $parsestate = $expect_remarks;
1032
                        $parsestate = $expect_remarks;
1033
                        next;
1033
                        next;
1034
                }
1034
                }
1035
1035
1036
        ##
1036
        ##
1037
        ## us type remarks? .. can happen quite early in the process already
1037
        ## us type remarks? .. can happen quite early in the process already
1038
        ##
1038
        ##
1039
1039
1040
        elsif (($parsestate >= $expect_modifier) and ($tok =~ /^RMK$/i))
1040
        elsif (($parsestate >= $expect_modifier) and ($tok =~ /^RMK$/i))
1041
        {
1041
        {
1042
            push(@{$self->{remarks}},$tok);
1042
            push(@{$self->{remarks}},$tok);
1043
            print "[$tok] is a (US type) remark.\n" if $self->{debug};
1043
            print "[$tok] is a (US type) remark.\n" if $self->{debug};
1044
                        $parsestate  = $expect_usremarks;
1044
                        $parsestate  = $expect_usremarks;
1045
            next;
1045
            next;
1046
        }
1046
        }
1047
1047
1048
        ##
1048
        ##
1049
        ## automatic station type?
1049
        ## automatic station type?
1050
        ##
1050
        ##
1051
1051
1052
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^A(O\d)$/i))
1052
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^A(O\d)$/i))
1053
        {
1053
        {
1054
            $self->{autostationtype} = $tok;
1054
            $self->{autostationtype} = $tok;
1055
            $self->{AUTO_STATIONTYPE} = $1;
1055
            $self->{AUTO_STATIONTYPE} = $1;
1056
            print "[$tok] is an automatic station type remark.\n" if $self->{debug};
1056
            print "[$tok] is an automatic station type remark.\n" if $self->{debug};
1057
            next;
1057
            next;
1058
        }
1058
        }
1059
1059
1060
        ##
1060
        ##
1061
        ## sea level pressure
1061
        ## sea level pressure
1062
        ##
1062
        ##
1063
1063
1064
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^SLP(\d+)/i))
1064
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^SLP(\d+)/i))
1065
        {
1065
        {
1066
            $self->{slp} = $tok;
1066
            $self->{slp} = $tok;
1067
            $self->{SLP} = "$1 mb";
1067
            $self->{SLP} = "$1 mb";
1068
            print "[$tok] is a sea level pressure.\n" if $self->{debug};
1068
            print "[$tok] is a sea level pressure.\n" if $self->{debug};
1069
            next;
1069
            next;
1070
        }
1070
        }
1071
1071
1072
        ##
1072
        ##
1073
        ## sea level pressure not available
1073
        ## sea level pressure not available
1074
        ##
1074
        ##
1075
1075
1076
        elsif (($parsestate == $expect_usremarks) and ($tok eq "SLPNO"))
1076
        elsif (($parsestate == $expect_usremarks) and ($tok eq "SLPNO"))
1077
        {
1077
        {
1078
            $self->{slp} = "SLPNO";
1078
            $self->{slp} = "SLPNO";
1079
            $self->{SLP} = "not available";
1079
            $self->{SLP} = "not available";
1080
            print "[$tok] is a sea level pressure.\n" if $self->{debug};
1080
            print "[$tok] is a sea level pressure.\n" if $self->{debug};
1081
            next;
1081
            next;
1082
        }
1082
        }
1083
1083
1084
        ##
1084
        ##
1085
        ## hourly precipitation
1085
        ## hourly precipitation
1086
        ##
1086
        ##
1087
1087
1088
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^P(\d\d\d\d)$/i))
1088
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^P(\d\d\d\d)$/i))
1089
        {
1089
        {
1090
            $self->{hourlyprecip} = $tok;
1090
            $self->{hourlyprecip} = $tok;
1091
1091
1092
            if ( $1 eq "0000" ) {
1092
            if ( $1 eq "0000" ) {
1093
                $self->{HOURLY_PRECIP} = "Trace";
1093
                $self->{HOURLY_PRECIP} = "Trace";
1094
            } else {
1094
            } else {
1095
                $self->{HOURLY_PRECIP} = $1;
1095
                $self->{HOURLY_PRECIP} = $1;
1096
            }
1096
            }
1097
        }
1097
        }
1098
1098
1099
        ##
1099
        ##
1100
        ## weather begin/end times
1100
        ## weather begin/end times
1101
        ##
1101
        ##
1102
1102
1103
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^($_weather_types_pat)([BE\d]+)$/i))
1103
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^($_weather_types_pat)([BE\d]+)$/i))
1104
        {
1104
        {
1105
            my $engl = "";
1105
            my $engl = "";
1106
            my $times = $2;
1106
            my $times = $2;
1107
1107
1108
            $self->{weatherlog} = $tok;
1108
            $self->{weatherlog} = $tok;
1109
1109
1110
            $engl = $_weather_types{$1};
1110
            $engl = $_weather_types{$1};
1111
1111
1112
            while ( $times =~ /(B|E)(\d\d)/g )
1112
            while ( $times =~ /(B|E)(\d\d)/g )
1113
            {
1113
            {
1114
                if ( $1 eq "B" ) {
1114
                if ( $1 eq "B" ) {
1115
                    $engl .= " began :$2";
1115
                    $engl .= " began :$2";
1116
                } else {
1116
                } else {
1117
                    $engl .= " ended :$2";
1117
                    $engl .= " ended :$2";
1118
                }
1118
                }
1119
            }
1119
            }
1120
1120
1121
            push(@{$self->{WEATHER_LOG}}, $engl);
1121
            push(@{$self->{WEATHER_LOG}}, $engl);
1122
            print "[$tok] is a weather log.\n" if $self->{debug};
1122
            print "[$tok] is a weather log.\n" if $self->{debug};
1123
            next;
1123
            next;
1124
        }
1124
        }
1125
1125
1126
        ##
1126
        ##
1127
        ## remarks for significant cloud types
1127
        ## remarks for significant cloud types
1128
        ##
1128
        ##
1129
1129
1130
        elsif (($parsestate >= $expect_recentweather) and ($tok eq "CB" || $tok eq "TCU"))
1130
        elsif (($parsestate >= $expect_recentweather) and ($tok eq "CB" || $tok eq "TCU"))
1131
        {
1131
        {
1132
            push(@{$self->{sigclouds}}, $tok);
1132
            push(@{$self->{sigclouds}}, $tok);
1133
1133
1134
            if ( $tok eq "CB" ) {
1134
            if ( $tok eq "CB" ) {
1135
                push(@{$self->{SIGCLOUDS}}, "Cumulonimbus");
1135
                push(@{$self->{SIGCLOUDS}}, "Cumulonimbus");
1136
            } elsif ( $tok eq "TCU" ) {
1136
            } elsif ( $tok eq "TCU" ) {
1137
                push(@{$self->{SIGCLOUDS}}, "Towering Cumulus");
1137
                push(@{$self->{SIGCLOUDS}}, "Towering Cumulus");
1138
            }
1138
            }
1139
                        $parsestate = $expect_usremarks;
1139
                        $parsestate = $expect_usremarks;
1140
        }
1140
        }
1141
1141
1142
        ##
1142
        ##
1143
        ## hourly temp/dewpoint
1143
        ## hourly temp/dewpoint
1144
        ##
1144
        ##
1145
1145
1146
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^T(\d)(\d\d)(\d)(\d)(\d\d)(\d)$/i))
1146
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^T(\d)(\d\d)(\d)(\d)(\d\d)(\d)$/i))
1147
        {
1147
        {
1148
            $self->{hourlytempdew} = $tok;
1148
            $self->{hourlytempdew} = $tok;
1149
            if ( $1 == 1 ) {
1149
            if ( $1 == 1 ) {
1150
                $self->{HOURLY_TEMP_C} = "-";
1150
                $self->{HOURLY_TEMP_C} = "-";
1151
            }
1151
            }
1152
            $self->{HOURLY_TEMP_C} .= "$2.$3";
1152
            $self->{HOURLY_TEMP_C} .= "$2.$3";
1153
1153
1154
            $self->{HOURLY_DEW_C} = "";
1154
            $self->{HOURLY_DEW_C} = "";
1155
            if ( $4 == 1 ) {
1155
            if ( $4 == 1 ) {
1156
                $self->{HOURLY_DEW_C} = "-";
1156
                $self->{HOURLY_DEW_C} = "-";
1157
            }
1157
            }
1158
            $self->{HOURLY_DEW_C} .= "$5.$6";
1158
            $self->{HOURLY_DEW_C} .= "$5.$6";
1159
1159
1160
            print "[$tok] is a hourly temp and dewpoint.\n" if $self->{debug};
1160
            print "[$tok] is a hourly temp and dewpoint.\n" if $self->{debug};
1161
            next;
1161
            next;
1162
        }
1162
        }
1163
1163
1164
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^QFE(\d\d\d)/i))
1164
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^QFE(\d\d\d)/i))
1165
        {
1165
        {
1166
            $self->{ALT_PL} = $1;
1166
            $self->{ALT_PL} = $1;
1167
1167
1168
            print "[$tok] is a pressure\n" if $self->{debug};
1168
            print "[$tok] is a pressure\n" if $self->{debug};
1169
            next;
1169
            next;
1170
        }
1170
        }
1171
1171
1172
        ##
1172
        ##
1173
        ## unknown, not in remarks yet
1173
        ## unknown, not in remarks yet
1174
        ##
1174
        ##
1175
1175
1176
        elsif ($parsestate < $expect_remarks)
1176
        elsif ($parsestate < $expect_remarks)
1177
        {
1177
        {
1178
            push(@{$self->{unknown}},$tok);
1178
            push(@{$self->{unknown}},$tok);
1179
            push(@{$self->{UNKNOWN}},$tok);
1179
            push(@{$self->{UNKNOWN}},$tok);
1180
            print "[$tok] is unexpected at this state.\n" if $self->{debug};
1180
            print "[$tok] is unexpected at this state.\n" if $self->{debug};
1181
            next;
1181
            next;
1182
        }
1182
        }
1183
1183
1184
        ##
1184
        ##
1185
        ## unknown. assume remarks
1185
        ## unknown. assume remarks
1186
        ##
1186
        ##
1187
1187
1188
        else
1188
        else
1189
        {
1189
        {
1190
            push(@{$self->{remarks}},$tok);
1190
            push(@{$self->{remarks}},$tok);
1191
            push(@{$self->{REMARKS}},$tok);
1191
            push(@{$self->{REMARKS}},$tok);
1192
            print "[$tok] is unknown remark.\n" if $self->{debug};
1192
            print "[$tok] is unknown remark.\n" if $self->{debug};
1193
            next;
1193
            next;
1194
        }
1194
        }
1195
1195
1196
    }
1196
    }
1197
1197
1198
    ##
1198
    ##
1199
    ## Now that the internal stuff is set, let's do the external
1199
    ## Now that the internal stuff is set, let's do the external
1200
    ## stuff.
1200
    ## stuff.
1201
    ##
1201
    ##
1202
1202
1203
    $self->{SITE} = $self->{site};
1203
    $self->{SITE} = $self->{site};
1204
    $self->{DATE} = substr($self->{date_time},0,2);
1204
    $self->{DATE} = substr($self->{date_time},0,2);
1205
    $self->{TIME} = substr($self->{date_time},2,4) . " UTC";
1205
    $self->{TIME} = substr($self->{date_time},2,4) . " UTC";
1206
    $self->{TIME} =~ s/(\d\d)(\d\d)/$1:$2/o;
1206
    $self->{TIME} =~ s/(\d\d)(\d\d)/$1:$2/o;
1207
    $self->{MOD}  = $self->{modifier};
1207
    $self->{MOD}  = $self->{modifier};
1208
1208
1209
    ##
1209
    ##
1210
    ## Okay, wind finally gets interesting.
1210
    ## Okay, wind finally gets interesting.
1211
    ##
1211
    ##
1212
1212
1213
    if ( defined $self->{wind} )
1213
    if ( defined $self->{wind} )
1214
        {
1214
        {
1215
        my $wind = $self->{wind};
1215
        my $wind = $self->{wind};
1216
        my $dir_deg  = substr($wind,0,3);
1216
        my $dir_deg  = substr($wind,0,3);
1217
        my $wind_speed;
1217
        my $wind_speed;
1218
        my $dir_eng = "";
1218
        my $dir_eng = "";
1219
        my $dir_rus = "";
1219
        my $dir_rus = "";
1220
                my $dir_abb = "";
1220
                my $dir_abb = "";
1221
1221
1222
        $wind_speed = $1 if($wind =~ /...(\d{2,3})/o);
1222
        $wind_speed = $1 if($wind =~ /...(\d{2,3})/o);
1223
        # Check for wind direction
1223
        # Check for wind direction
1224
        if ($dir_deg =~ /VRB/i) {
1224
        if ($dir_deg =~ /VRB/i) {
1225
            $dir_deg = $dir_eng = "Variable";
1225
            $dir_deg = $dir_eng = "Variable";
1226
            $dir_rus = "переменный";
1226
            $dir_rus = "переменный";
1227
        } else {
1227
        } else {
1228
            if ($wind_speed == 0 and $dir_deg == 0) {
1228
            if ($wind_speed == 0 and $dir_deg == 0) {
1229
                # Calm wind (00000KT in METAR)
1229
                # Calm wind (00000KT in METAR)
1230
                $dir_eng = "Calm";
1230
                $dir_eng = "Calm";
1231
                $dir_rus = "штиль";
1231
                $dir_rus = "штиль";
1232
                print "wind is calm\n" if $self->{debug};
1232
                print "wind is calm\n" if $self->{debug};
1233
            } elsif ($dir_deg < 15) {
1233
            } elsif ($dir_deg < 15) {
1234
                $dir_eng = "North";
1234
                $dir_eng = "North";
1235
                                $dir_abb = "N";
1235
                                $dir_abb = "N";
1236
                $dir_rus = "северный";
1236
                $dir_rus = "северный";
1237
            } elsif ($dir_deg < 30) {
1237
            } elsif ($dir_deg < 30) {
1238
                $dir_eng = "North/Northeast";
1238
                $dir_eng = "North/Northeast";
1239
                                $dir_abb = "NNE";
1239
                                $dir_abb = "NNE";
1240
                $dir_rus = "северо-северо-восточный";
1240
                $dir_rus = "северо-северо-восточный";
1241
            } elsif ($dir_deg < 60) {
1241
            } elsif ($dir_deg < 60) {
1242
                $dir_eng = "Northeast";
1242
                $dir_eng = "Northeast";
1243
                                $dir_abb = "NE";
1243
                                $dir_abb = "NE";
1244
                $dir_rus = "северо-восточный";
1244
                $dir_rus = "северо-восточный";
1245
            } elsif ($dir_deg < 75) {
1245
            } elsif ($dir_deg < 75) {
1246
                $dir_eng = "East/Northeast";
1246
                $dir_eng = "East/Northeast";
1247
                                $dir_abb = "ENE";
1247
                                $dir_abb = "ENE";
1248
                $dir_rus = "восточный-северо-восточный";
1248
                $dir_rus = "восточный-северо-восточный";
1249
            } elsif ($dir_deg < 105) {
1249
            } elsif ($dir_deg < 105) {
1250
                $dir_eng = "East";
1250
                $dir_eng = "East";
1251
                                $dir_abb = "E";
1251
                                $dir_abb = "E";
1252
                $dir_rus = "восточный";
1252
                $dir_rus = "восточный";
1253
            } elsif ($dir_deg < 120) {
1253
            } elsif ($dir_deg < 120) {
1254
                $dir_eng = "East/Southeast";
1254
                $dir_eng = "East/Southeast";
1255
                                $dir_abb = "ESE";
1255
                                $dir_abb = "ESE";
1256
                $dir_rus = "восточный-юго-восточный";
1256
                $dir_rus = "восточный-юго-восточный";
1257
            } elsif ($dir_deg < 150) {
1257
            } elsif ($dir_deg < 150) {
1258
                $dir_eng = "Southeast";
1258
                $dir_eng = "Southeast";
1259
                                $dir_abb = "SE";
1259
                                $dir_abb = "SE";
1260
                $dir_rus = "юго-восточный";
1260
                $dir_rus = "юго-восточный";
1261
            } elsif ($dir_deg < 165) {
1261
            } elsif ($dir_deg < 165) {
1262
                $dir_eng = "South/Southeast";
1262
                $dir_eng = "South/Southeast";
1263
                                $dir_abb = "SSE";
1263
                                $dir_abb = "SSE";
1264
                $dir_rus = "юго-юго-восточный";
1264
                $dir_rus = "юго-юго-восточный";
1265
            } elsif ($dir_deg < 195) {
1265
            } elsif ($dir_deg < 195) {
1266
                $dir_eng = "South";
1266
                $dir_eng = "South";
1267
                                $dir_abb = "S";
1267
                                $dir_abb = "S";
1268
                $dir_rus = "южный";
1268
                $dir_rus = "южный";
1269
            } elsif ($dir_deg < 210) {
1269
            } elsif ($dir_deg < 210) {
1270
                $dir_eng = "South/Southwest";
1270
                $dir_eng = "South/Southwest";
1271
                                $dir_abb = "SSW";
1271
                                $dir_abb = "SSW";
1272
                $dir_rus = "юго-юго-западный"
1272
                $dir_rus = "юго-юго-западный"
1273
            } elsif ($dir_deg < 240) {
1273
            } elsif ($dir_deg < 240) {
1274
                $dir_eng = "Southwest";
1274
                $dir_eng = "Southwest";
1275
                                $dir_abb = "SW";
1275
                                $dir_abb = "SW";
1276
                $dir_rus = "юго-западный";
1276
                $dir_rus = "юго-западный";
1277
            } elsif ($dir_deg < 265) {
1277
            } elsif ($dir_deg < 265) {
1278
                $dir_eng = "West/Southwest";
1278
                $dir_eng = "West/Southwest";
1279
                                $dir_abb = "WSW";
1279
                                $dir_abb = "WSW";
1280
                $dir_rus = "западно-юго-западный";
1280
                $dir_rus = "западно-юго-западный";
1281
            } elsif ($dir_deg < 285) {
1281
            } elsif ($dir_deg < 285) {
1282
                $dir_eng = "West";
1282
                $dir_eng = "West";
1283
                                $dir_abb = "W";
1283
                                $dir_abb = "W";
1284
                $dir_rus = "западный";
1284
                $dir_rus = "западный";
1285
            } elsif ($dir_deg < 300) {
1285
            } elsif ($dir_deg < 300) {
1286
                $dir_eng = "West/Northwest";
1286
                $dir_eng = "West/Northwest";
1287
                                $dir_abb = "WNW";
1287
                                $dir_abb = "WNW";
1288
                $dir_rus = "западно-северо-западный";
1288
                $dir_rus = "западно-северо-западный";
1289
            } elsif ($dir_deg < 330) {
1289
            } elsif ($dir_deg < 330) {
1290
                $dir_eng = "Northwest";
1290
                $dir_eng = "Northwest";
1291
                                $dir_abb = "NW";
1291
                                $dir_abb = "NW";
1292
                $dir_rus = "северо-западный";
1292
                $dir_rus = "северо-западный";
1293
            } elsif ($dir_deg < 345) {
1293
            } elsif ($dir_deg < 345) {
1294
                $dir_eng = "North/Northwest";
1294
                $dir_eng = "North/Northwest";
1295
                                $dir_abb = "NNW";
1295
                                $dir_abb = "NNW";
1296
                $dir_rus = "северо-северо-западный";
1296
                $dir_rus = "северо-северо-западный";
1297
            } elsif ($dir_deg < 360) {
1297
            } elsif ($dir_deg < 360) {
1298
                $dir_eng = "North";
1298
                $dir_eng = "North";
1299
                                $dir_abb = "N";
1299
                                $dir_abb = "N";
1300
                $dir_rus = "северный";
1300
                $dir_rus = "северный";
1301
            } else {
1301
            } else {
1302
                # Shouldn't happen, but if for some reason the METAR
1302
                # Shouldn't happen, but if for some reason the METAR
1303
                # information doesn't contain a reasonable direction...
1303
                # information doesn't contain a reasonable direction...
1304
                $dir_eng = "undeterminable";
1304
                $dir_eng = "undeterminable";
1305
                $dir_rus = "неопределенный";
1305
                $dir_rus = "неопределенный";
1306
            }
1306
            }
1307
        }
1307
        }
1308
1308
1309
                my $kts_speed = undef;
1309
                my $kts_speed = undef;
1310
                my $mph_speed = undef;
1310
                my $mph_speed = undef;
1311
                my $mps_speed = undef;
1311
                my $mps_speed = undef;
1312
1312
1313
                my $kts_gust = "";
1313
                my $kts_gust = "";
1314
                my $mph_gust = "";
1314
                my $mph_gust = "";
1315
                my $mps_gust = "";
1315
                my $mps_gust = "";
1316
1316
1317
                # parse knots
1317
                # parse knots
1318
1318
1319
                if ($self->{windtype} == $wt_knots){
1319
                if ($self->{windtype} == $wt_knots){
1320
                        $wind =~ /...(\d\d\d?)/o;
1320
                        $wind =~ /...(\d\d\d?)/o;
1321
                        $kts_speed = $1;
1321
                        $kts_speed = $1;
1322
                        $mph_speed = $kts_speed * 1.15077945;
1322
                        $mph_speed = $kts_speed * 1.15077945;
1323
                        $mps_speed = $kts_speed * 0.514444444;
1323
                        $mps_speed = $kts_speed * 0.514444444;
1324
1324
1325
                        if ($wind =~ /.{5,6}G(\d\d\d?)/o) {
1325
                        if ($wind =~ /.{5,6}G(\d\d\d?)/o) {
1326
                                $kts_gust = $1;
1326
                                $kts_gust = $1;
1327
                                $mph_gust = $kts_gust * 1.15077945;
1327
                                $mph_gust = $kts_gust * 1.15077945;
1328
                                $mps_gust = $kts_gust * 0.514444444;
1328
                                $mps_gust = $kts_gust * 0.514444444;
1329
                        }
1329
                        }
1330
                # else: parse meters/second
1330
                # else: parse meters/second
1331
                } elsif ($self->{windtype} == $wt_mps){
1331
                } elsif ($self->{windtype} == $wt_mps){
1332
                        $wind=~ /...(\d\d\d?)/o;
1332
                        $wind=~ /...(\d\d\d?)/o;
1333
                        $mps_speed = $1;
1333
                        $mps_speed = $1;
1334
                        $kts_speed = $mps_speed * 1.9438445; # units
1334
                        $kts_speed = $mps_speed * 1.9438445; # units
1335
                        $mph_speed = $mps_speed * 2.2369363;
1335
                        $mph_speed = $mps_speed * 2.2369363;
1336
                        if ($wind =~ /\d{5,6}G(\d\d\d?)/o) {
1336
                        if ($wind =~ /\d{5,6}G(\d\d\d?)/o) {
1337
                                $mps_gust = $1;
1337
                                $mps_gust = $1;
1338
                                $kts_gust = $mps_gust * 1.9438445;
1338
                                $kts_gust = $mps_gust * 1.9438445;
1339
                                $mph_gust = $mps_gust * 2.2369363;
1339
                                $mph_gust = $mps_gust * 2.2369363;
1340
                        }
1340
                        }
1341
                } else {
1341
                } else {
1342
                        warn "Geo::ModMETAR Parser error: unknown windtype\n";
1342
                        warn "Geo::ModMETAR Parser error: unknown windtype\n";
1343
                }
1343
                }
1344
1344
1345
        $self->{WIND_KTS} = $kts_speed;
1345
        $self->{WIND_KTS} = $kts_speed;
1346
        $self->{WIND_MPH} = $mph_speed;
1346
        $self->{WIND_MPH} = $mph_speed;
1347
        $self->{WIND_MS}  = $mps_speed;
1347
        $self->{WIND_MS}  = $mps_speed;
1348
1348
1349
        $self->{WIND_GUST_KTS} = $kts_gust;
1349
        $self->{WIND_GUST_KTS} = $kts_gust;
1350
        $self->{WIND_GUST_MPH} = $mph_gust;
1350
        $self->{WIND_GUST_MPH} = $mph_gust;
1351
        $self->{WIND_GUST_MS}  = $mps_gust;
1351
        $self->{WIND_GUST_MS}  = $mps_gust;
1352
1352
1353
        $self->{WIND_DIR_DEG} = $dir_deg;
1353
        $self->{WIND_DIR_DEG} = $dir_deg;
1354
        $self->{WIND_DIR_ENG} = $dir_eng;
1354
        $self->{WIND_DIR_ENG} = $dir_eng;
1355
        $self->{WIND_DIR_ABB} = $dir_abb;
1355
        $self->{WIND_DIR_ABB} = $dir_abb;
1356
        $self->{WIND_DIR_RUS} = $dir_rus;
1356
        $self->{WIND_DIR_RUS} = $dir_rus;
1357
1357
1358
    }
1358
    }
1359
1359
1360
        ##
1360
        ##
1361
        ## wind variation
1361
        ## wind variation
1362
        ##
1362
        ##
1363
1363
1364
        if (defined $self->{windvar})
1364
        if (defined $self->{windvar})
1365
        {
1365
        {
1366
                if ($self->{windvar} =~ /^(\d\d\d)V(\d\d\d)$/){
1366
                if ($self->{windvar} =~ /^(\d\d\d)V(\d\d\d)$/){
1367
                        $self->{WIND_VAR} = "Varying between $1 and $2";
1367
                        $self->{WIND_VAR} = "Varying between $1 and $2";
1368
            $self->{WIND_VAR_1} = $1;
1368
            $self->{WIND_VAR_1} = $1;
1369
            $self->{WIND_VAR_2} = $2;
1369
            $self->{WIND_VAR_2} = $2;
1370
            my @direction = (
1370
            my @direction = (
1371
                15 => "North",
1371
                15 => "North",
1372
                30 => "North/Northeast",
1372
                30 => "North/Northeast",
1373
                60 => "Northeast",
1373
                60 => "Northeast",
1374
                75 => "East/Northeast",
1374
                75 => "East/Northeast",
1375
                105 => "East",
1375
                105 => "East",
1376
                120 => "East/Southeast",
1376
                120 => "East/Southeast",
1377
                150 => "Southeast",
1377
                150 => "Southeast",
1378
                165 => "South/Southeast",
1378
                165 => "South/Southeast",
1379
                195 => "South",
1379
                195 => "South",
1380
                210 => "South/Southwest",
1380
                210 => "South/Southwest",
1381
                240 => "Southwest",
1381
                240 => "Southwest",
1382
                265 => "West/Southwest",
1382
                265 => "West/Southwest",
1383
                285 => "West",
1383
                285 => "West",
1384
                300 => "West/Northwest",
1384
                300 => "West/Northwest",
1385
                330 => "Northwest",
1385
                330 => "Northwest",
1386
                345 => "North/Northwest",
1386
                345 => "North/Northwest",
1387
                360 => "North",
1387
                360 => "North",
1388
                1000 => "undeterminable");
1388
                1000 => "undeterminable");
1389
            for(my $x = 0; $x < $#direction; $x += 2) {
1389
            for(my $x = 0; $x < $#direction; $x += 2) {
1390
                if($self->{WIND_VAR_1} < $direction[$x]) {
1390
                if($self->{WIND_VAR_1} < $direction[$x]) {
1391
                    $self->{WIND_VAR_ENG_1} = $direction[$x+1];
1391
                    $self->{WIND_VAR_ENG_1} = $direction[$x+1];
1392
                    last;
1392
                    last;
1393
                }
1393
                }
1394
            }
1394
            }
1395
            for(my $x = 0; $x < $#direction; $x += 2) {
1395
            for(my $x = 0; $x < $#direction; $x += 2) {
1396
                if($self->{WIND_VAR_2} < $direction[$x]) {
1396
                if($self->{WIND_VAR_2} < $direction[$x]) {
1397
                    $self->{WIND_VAR_ENG_2} = $direction[$x+1];
1397
                    $self->{WIND_VAR_ENG_2} = $direction[$x+1];
1398
                    last;
1398
                    last;
1399
                }
1399
                }
1400
            }
1400
            }
1401
                }
1401
                }
1402
        }
1402
        }
1403
1403
1404
    ##   
1404
    ##   
1405
    ## Calculate relative humidity
1405
    ## Calculate relative humidity
1406
    ##
1406
    ##
1407
1407
1408
    {
1408
    {
1409
        my $esat  = 6.11*(10**((7.5*$self->{TEMP_C})/(237.7+$self->{TEMP_C})));
1409
        my $esat  = 6.11*(10**((7.5*$self->{TEMP_C})/(237.7+$self->{TEMP_C})));
1410
        my $esurf = 6.11*(10**((7.5*$self->{DEW_C})/(237.7+$self->{DEW_C})));
1410
        my $esurf = 6.11*(10**((7.5*$self->{DEW_C})/(237.7+$self->{DEW_C})));
1411
1411
1412
        $self->{RH} = 100.0 * ($esurf/$esat);
1412
        $self->{RH} = 100.0 * ($esurf/$esat);
1413
    }
1413
    }
1414
   
1414
   
1415
    ##
1415
    ##
1416
    ## Calculate windchill temperature
1416
    ## Calculate windchill temperature
1417
    ##
1417
    ##
1418
   
1418
   
1419
    {
1419
    {
1420
        my $windspeed = $self->{WIND_MS}*3.6;
1420
        my $windspeed = $self->{WIND_MS}*3.6;
1421
        $self->{TEMP_WC} = 13.12 + 0.6215*$self->{TEMP_C} - 11.37*($windspeed**0.16) + 0.3965*$self->{TEMP_C}*($windspeed**0.16);
1421
        $self->{TEMP_WC} = 13.12 + 0.6215*$self->{TEMP_C} - 11.37*($windspeed**0.16) + 0.3965*$self->{TEMP_C}*($windspeed**0.16);
1422
    }
1422
    }
1423
1423
1424
    ##
1424
    ##
1425
    ## Visibility.
1425
    ## Visibility.
1426
    ##
1426
    ##
1427
1427
1428
    if($self->{visibility}) {
1428
    if($self->{visibility}) {
1429
        my $vis = $self->{visibility};
1429
        my $vis = $self->{visibility};
1430
                # test for statute miles
1430
                # test for statute miles
1431
                if ($vis =~ /SM$/){
1431
                if ($vis =~ /SM$/){
1432
                        $vis =~ s/SM$//oi;                              # nuke the "SM"
1432
                        $vis =~ s/SM$//oi;                              # nuke the "SM"
1433
                        if ($vis =~ /M(\d\/\d)/o) {
1433
                        if ($vis =~ /M(\d\/\d)/o) {
1434
                                $self->{VISIBILITY} = "Less than $1 statute miles";
1434
                                $self->{VISIBILITY} = "Less than $1 statute miles";
1435
                                $self->{VISIBILITY_RUS} = "Менее чем $1 статутных миль";
1435
                                $self->{VISIBILITY_RUS} = "Менее чем $1 статутных миль";
1436
                        } else {
1436
                        } else {
1437
                                $self->{VISIBILITY} = $vis . " statute miles";
1437
                                $self->{VISIBILITY} = $vis . " statute miles";
1438
                                $self->{VISIBILITY} = $vis . " статутных миль";
1438
                                $self->{VISIBILITY} = $vis . " статутных миль";
1439
                        } # end if
1439
                        } # end if
1440
                # auto metars can have non-directional visibility reports
1440
                # auto metars can have non-directional visibility reports
1441
                } elsif (($self->{MOD} eq 'AUTO') and ($vis =~ /(\d+)NDV$/)){
1441
                } elsif (($self->{MOD} eq 'AUTO') and ($vis =~ /(\d+)NDV$/)){
1442
                        $self->{VISIBILITY} = "$1 meters non-directional visibility";
1442
                        $self->{VISIBILITY} = "$1 meters non-directional visibility";
1443
                        $self->{VISIBILITY_RUS} = "$1 м непрямой видимости";
1443
                        $self->{VISIBILITY_RUS} = "$1 м непрямой видимости";
1444
                } else {
1444
                } else {
1445
                        $self->{VISIBILITY} = $vis . " meters";
1445
                        $self->{VISIBILITY} = $vis . " meters";
1446
            if ($vis<1000) {
1446
            if ($vis<1000) {
1447
                            $self->{VISIBILITY_RUS} = $vis . " м";
1447
                            $self->{VISIBILITY_RUS} = $vis . " м";
1448
            } else {
1448
            } else {
1449
                $vis = $vis/1000;
1449
                $vis = $vis/1000;
1450
                if (abs($vis-int($vis))>=0.5) {
1450
                if (abs($vis-int($vis))>=0.5) {
1451
                    $vis = int($vis)+1;
1451
                    $vis = int($vis)+1;
1452
                } else {
1452
                } else {
1453
                    $vis = int($vis);
1453
                    $vis = int($vis);
1454
                }
1454
                }
1455
                $self->{VISIBILITY_RUS} = $vis . " км";
1455
                $self->{VISIBILITY_RUS} = $vis . " км";
1456
            }
1456
            }
1457
                }
1457
                }
1458
    }
1458
    }
1459
1459
1460
    ##
1460
    ##
1461
    ## Calculate F temps for all C temps
1461
    ## Calculate F temps for all C temps
1462
    ##
1462
    ##
1463
1463
1464
    foreach my $key ( keys(%$self) )
1464
    foreach my $key ( keys(%$self) )
1465
    {
1465
    {
1466
        if ( uc($key) eq $key && $key =~ /^(.*)_C$/ )
1466
        if ( uc($key) eq $key && $key =~ /^(.*)_C$/ )
1467
        {
1467
        {
1468
            my $fkey = $1 . "_F";
1468
            my $fkey = $1 . "_F";
1469
1469
1470
            next unless defined $self->{$key} && $self->{$key};
1470
            next unless defined $self->{$key} && $self->{$key};
1471
1471
1472
            $self->{$fkey} = sprintf("%.1f", (($self->{$key} * (9/5)) + 32));
1472
            $self->{$fkey} = sprintf("%.1f", (($self->{$key} * (9/5)) + 32));
1473
        }
1473
        }
1474
    }
1474
    }
1475
1475
1476
        # join the runway group
1476
        # join the runway group
1477
       
1477
       
1478
        $self->{runway} = join(', ' , @{$self->{RUNWAY}});
1478
        $self->{runway} = join(', ' , @{$self->{RUNWAY}});
1479
       
1479
       
1480
}
1480
}
1481
1481
1482
##
1482
##
1483
## Print the tokens--usually when debugging.
1483
## Print the tokens--usually when debugging.
1484
##
1484
##
1485
1485
1486
sub print_tokens
1486
sub print_tokens
1487
{
1487
{
1488
    my $self = shift;
1488
    my $self = shift;
1489
    my $tok;
1489
    my $tok;
1490
    foreach $tok (@{$self->{tokens}}) {
1490
    foreach $tok (@{$self->{tokens}}) {
1491
        print "> $tok\n";
1491
        print "> $tok\n";
1492
    }
1492
    }
1493
}
1493
}
1494
1494
1495
##
1495
##
1496
## Turn debugging on/off.
1496
## Turn debugging on/off.
1497
##
1497
##
1498
1498
1499
sub debug
1499
sub debug
1500
{
1500
{
1501
    my $self = shift;
1501
    my $self = shift;
1502
    my $flag = shift;
1502
    my $flag = shift;
1503
    return $self->{debug} unless defined $flag;
1503
    return $self->{debug} unless defined $flag;
1504
1504
1505
    if (($flag eq "Y") or ($flag eq "y") or ($flag == 1)) {
1505
    if (($flag eq "Y") or ($flag eq "y") or ($flag == 1)) {
1506
        $self->{debug} = 1;
1506
        $self->{debug} = 1;
1507
    } elsif (($flag eq "N") or ($flag eq "n") or ($flag == 0)) {
1507
    } elsif (($flag eq "N") or ($flag eq "n") or ($flag == 0)) {
1508
        $self->{debug} = 0;
1508
        $self->{debug} = 0;
1509
    }
1509
    }
1510
1510
1511
    return $self->{debug};
1511
    return $self->{debug};
1512
}
1512
}
1513
1513
1514
##
1514
##
1515
## Dump internal data structure. Useful for debugging and such.
1515
## Dump internal data structure. Useful for debugging and such.
1516
##
1516
##
1517
1517
1518
sub dump
1518
sub dump
1519
{
1519
{
1520
    my $self = shift;
1520
    my $self = shift;
1521
1521
1522
    print "Modified METAR dump follows.\n\n";
1522
    print "Modified METAR dump follows.\n\n";
1523
1523
1524
    print "type: $self->{type}\n";
1524
    print "type: $self->{type}\n";
1525
    print "site: $self->{site}\n";
1525
    print "site: $self->{site}\n";
1526
    print "date_time: $self->{date_time}\n";
1526
    print "date_time: $self->{date_time}\n";
1527
    print "modifier: $self->{modifier}\n";
1527
    print "modifier: $self->{modifier}\n";
1528
    print "wind: $self->{wind}\n";
1528
    print "wind: $self->{wind}\n";
1529
    print "variable wind: $self->{vrbwind}\n";
1529
    print "variable wind: $self->{vrbwind}\n";
1530
    print "visibility: $self->{visibility}\n";
1530
    print "visibility: $self->{visibility}\n";
1531
    print "runway: $self->{runway}\n";
1531
    print "runway: $self->{runway}\n";
1532
    print "weather: " . join(', ', @{$self->{weather}}) . "\n";
1532
    print "weather: " . join(', ', @{$self->{weather}}) . "\n";
1533
    print "sky: " . join(', ', @{$self->{sky}}) . "\n";
1533
    print "sky: " . join(', ', @{$self->{sky}}) . "\n";
1534
    print "temp_dew: $self->{temp_dew}\n";
1534
    print "temp_dew: $self->{temp_dew}\n";
1535
    print "alt: $self->{alt}\n";
1535
    print "alt: $self->{alt}\n";
1536
    print "pressure: $self->{pressure}\n";
1536
    print "pressure: $self->{pressure}\n";
1537
    print "slp: $self->{slp}\n";
1537
    print "slp: $self->{slp}\n";
1538
    print "remarks: " . join (', ', @{$self->{remarks}}) . "\n";
1538
    print "remarks: " . join (', ', @{$self->{remarks}}) . "\n";
1539
    print "\n";
1539
    print "\n";
1540
1540
1541
    foreach my $var ( sort(keys(%$self)) )
1541
    foreach my $var ( sort(keys(%$self)) )
1542
    {
1542
    {
1543
        next if ( uc($var) ne $var );
1543
        next if ( uc($var) ne $var );
1544
1544
1545
        if ( ref($self->{$var}) eq "ARRAY" )
1545
        if ( ref($self->{$var}) eq "ARRAY" )
1546
        {
1546
        {
1547
            print "$var: ", join(", ", @{$self->{$var}}), "\n";
1547
            print "$var: ", join(", ", @{$self->{$var}}), "\n";
1548
        }
1548
        }
1549
        else
1549
        else
1550
        {
1550
        {
1551
            print "$var: ", $self->{$var}, "\n";
1551
            print "$var: ", $self->{$var}, "\n";
1552
        }
1552
        }
1553
    }
1553
    }
1554
}
1554
}
1555
1555
1556
1;
1556
1;
1557
1557
1558
__END__
1558
__END__
1559
1559
1560
=head1 NAME
1560
=head1 NAME
1561

1561

1562
Mod::Geo::METAR - Process aviation weather reports in the METAR format.
1562
Mod::Geo::METAR - Process aviation weather reports in the METAR format.
1563

1563

1564
=head1 SYNOPSIS
1564
=head1 SYNOPSIS
1565

1565

1566
  use Mod::Geo::METAR;
1566
  use Mod::Geo::METAR;
1567
  use strict;
1567
  use strict;
1568

1568

1569
  my $m = new Mod::Geo::METAR;
1569
  my $m = new Mod::Geo::METAR;
1570
  $m->metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");
1570
  $m->metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");
1571
  print $m->dump;
1571
  print $m->dump;
1572

1572

1573
  exit;
1573
  exit;
1574

1574

1575
=head1 DESCRIPTION
1575
=head1 DESCRIPTION
1576

1576

1577
METAR reports are available on-line, thanks to the National Weather Service.
1577
METAR reports are available on-line, thanks to the National Weather Service.
1578
Since reading the METAR format isn't easy for non-pilots, these reports are
1578
Since reading the METAR format isn't easy for non-pilots, these reports are
1579
relatively useles to the common man who just wants a quick glace at the
1579
relatively useles to the common man who just wants a quick glace at the
1580
weather. This module tries to parse the METAR reports so the data can be
1580
weather. This module tries to parse the METAR reports so the data can be
1581
used to create readable weather reports and/or process the data in
1581
used to create readable weather reports and/or process the data in
1582
applications.
1582
applications.
1583

1583

1584
=head1 USAGE
1584
=head1 USAGE
1585

1585

1586
=head2 How you might use this
1586
=head2 How you might use this
1587

1587

1588
Here is how you I<might> use the Geo::METAR module.
1588
Here is how you I<might> use the Geo::METAR module.
1589

1589

1590
One use that I have had for this module is to query the NWS METAR page
1590
One use that I have had for this module is to query the NWS METAR page
1591
(using the LWP modules) at:
1591
(using the LWP modules) at:
1592

1592

1593
I<http://weather.noaa.gov/cgi-bin/mgetmetar.pl?cccc=EHSB>
1593
I<http://weather.noaa.gov/cgi-bin/mgetmetar.pl?cccc=EHSB>
1594

1594

1595
to get an
1595
to get an
1596
up-to-date METAR. Then, I scan thru the output, looking for what looks
1596
up-to-date METAR. Then, I scan thru the output, looking for what looks
1597
like a METAR string (that's not hard in Perl). Oh, EHSB can be any site
1597
like a METAR string (that's not hard in Perl). Oh, EHSB can be any site
1598
location code where there is a reporting station.
1598
location code where there is a reporting station.
1599

1599

1600
I then pass the METAR into this module and get the info I want. I can
1600
I then pass the METAR into this module and get the info I want. I can
1601
then update my webcam page with the current temperature, sky conditions, or
1601
then update my webcam page with the current temperature, sky conditions, or
1602
whatnot. See for yourself at http://webcam.idefix.net/
1602
whatnot. See for yourself at http://webcam.idefix.net/
1603

1603

1604
See the BUGS section for a remark about multiple passes with the same
1604
See the BUGS section for a remark about multiple passes with the same
1605
Geo::METAR object.
1605
Geo::METAR object.
1606

1606

1607
=head2 Functions
1607
=head2 Functions
1608

1608

1609
The following functions are defined in the METAR module. Most of
1609
The following functions are defined in the METAR module. Most of
1610
them are I<public>, meaning that you're supposed to use
1610
them are I<public>, meaning that you're supposed to use
1611
them. Some are I<private>, meaning that you're not supposed to use
1611
them. Some are I<private>, meaning that you're not supposed to use
1612
them -- but I won't stop you. Assume that functions are I<public>
1612
them -- but I won't stop you. Assume that functions are I<public>
1613
unless otherwise documented.
1613
unless otherwise documented.
1614

1614

1615
=over
1615
=over
1616

1616

1617
=item metar()
1617
=item metar()
1618

1618

1619
metar() is the function to whwich you should pass a METAR string.  It
1619
metar() is the function to whwich you should pass a METAR string.  It
1620
will take care of decomposing it into its component parts converting
1620
will take care of decomposing it into its component parts converting
1621
the units and so on.
1621
the units and so on.
1622

1622

1623
Example: C<$m-E<gt>metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");>
1623
Example: C<$m-E<gt>metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");>
1624

1624

1625
=item debug()
1625
=item debug()
1626

1626

1627
debug() toggles debugging messages. By default, debugging is turned
1627
debug() toggles debugging messages. By default, debugging is turned
1628
B<off>. Turn it on if you are developing METAR or having trouble with
1628
B<off>. Turn it on if you are developing METAR or having trouble with
1629
it.
1629
it.
1630

1630

1631
debug() understands all of the folloing:
1631
debug() understands all of the folloing:
1632

1632

1633
        Enable       Disable
1633
        Enable       Disable
1634
        ------       -------
1634
        ------       -------
1635
          1             0
1635
          1             0
1636
        'yes'         'no'
1636
        'yes'         'no'
1637
        'on'          'off'
1637
        'on'          'off'
1638

1638

1639
If you contact me for help, I'll likely ask you for some debugging
1639
If you contact me for help, I'll likely ask you for some debugging
1640
output.
1640
output.
1641

1641

1642
Example: C<$m-E<gt>debug(1);>
1642
Example: C<$m-E<gt>debug(1);>
1643

1643

1644
=item dump()
1644
=item dump()
1645

1645

1646
dump() will dump the internal data structure for the METAR in a
1646
dump() will dump the internal data structure for the METAR in a
1647
semi-human readable format.
1647
semi-human readable format.
1648

1648

1649
Example: C<$m-E<gt>dump;>
1649
Example: C<$m-E<gt>dump;>
1650

1650

1651
=item version()
1651
=item version()
1652

1652

1653
version() will print out the current version.
1653
version() will print out the current version.
1654

1654

1655
Example: C<print $m-E<gt>version;>
1655
Example: C<print $m-E<gt>version;>
1656

1656

1657
=item _tokenize()
1657
=item _tokenize()
1658

1658

1659
B<PRIVATE>
1659
B<PRIVATE>
1660

1660

1661
Called internally to break the METAR into its component tokens.
1661
Called internally to break the METAR into its component tokens.
1662

1662

1663
=item _process()
1663
=item _process()
1664

1664

1665
B<PRIVATE>
1665
B<PRIVATE>
1666

1666

1667
Used to make sense of the tokens found in B<_tokenize()>.
1667
Used to make sense of the tokens found in B<_tokenize()>.
1668

1668

1669
=back
1669
=back
1670

1670

1671
=head2 Variables
1671
=head2 Variables
1672

1672

1673
After you've called B<metar()>, you'd probably like to get at
1673
After you've called B<metar()>, you'd probably like to get at
1674
the individual values for things like temperature, dew point,
1674
the individual values for things like temperature, dew point,
1675
and so on. You do that by accessing individual variables via
1675
and so on. You do that by accessing individual variables via
1676
the METAR object.
1676
the METAR object.
1677

1677

1678
This section lists those variables and what they represent.
1678
This section lists those variables and what they represent.
1679

1679

1680
If you call B<dump()>, you'll find that it spits all of these
1680
If you call B<dump()>, you'll find that it spits all of these
1681
out.
1681
out.
1682

1682

1683
=over
1683
=over
1684

1684

1685
=item VERSION
1685
=item VERSION
1686

1686

1687
The version of METAR.pm that you're using.
1687
The version of METAR.pm that you're using.
1688

1688

1689
=item METAR
1689
=item METAR
1690

1690

1691
The actual, raw METAR.
1691
The actual, raw METAR.
1692

1692

1693
=item TYPE
1693
=item TYPE
1694

1694

1695
Report type in English ("Routine Weather Report" or "Special Weather Report")
1695
Report type in English ("Routine Weather Report" or "Special Weather Report")
1696

1696

1697
=item SITE
1697
=item SITE
1698

1698

1699
4-letter site code.
1699
4-letter site code.
1700

1700

1701
=item DATE
1701
=item DATE
1702

1702

1703
The date (just the day of the month) on which the report was issued.
1703
The date (just the day of the month) on which the report was issued.
1704

1704

1705
=item TIME
1705
=item TIME
1706

1706

1707
The time at which the report was issued.
1707
The time at which the report was issued.
1708

1708

1709
=item MOD
1709
=item MOD
1710

1710

1711
Modifier (AUTO/COR) if any.
1711
Modifier (AUTO/COR) if any.
1712

1712

1713
=item WIND_DIR_ENG
1713
=item WIND_DIR_ENG
1714

1714

1715
The current wind direction in English (Southwest, East, North, etc.)
1715
The current wind direction in English (Southwest, East, North, etc.)
1716

1716

1717
=item WIND_DIR_RUS
1717
=item WIND_DIR_RUS
1718

1718

1719
The current wind direction in Russian
1719
The current wind direction in Russian
1720

1720

1721
=item WIND_DIR_ABB
1721
=item WIND_DIR_ABB
1722

1722

1723
The current wind direction in abbreviated English (S, E, N, etc.)
1723
The current wind direction in abbreviated English (S, E, N, etc.)
1724

1724

1725
=item WIND_DIR_DEG
1725
=item WIND_DIR_DEG
1726

1726

1727
The current wind direction in degrees.
1727
The current wind direction in degrees.
1728

1728

1729
=item WIND_KTS
1729
=item WIND_KTS
1730

1730

1731
The current wind speed in Knots.
1731
The current wind speed in Knots.
1732

1732

1733
=item WIND_MPH
1733
=item WIND_MPH
1734

1734

1735
The current wind speed in Miles Per Hour.
1735
The current wind speed in Miles Per Hour.
1736

1736

1737
=item WIND_MS
1737
=item WIND_MS
1738

1738

1739
The current wind speed in Metres Per Second.
1739
The current wind speed in Metres Per Second.
1740

1740

1741
=item WIND_GUST_KTS
1741
=item WIND_GUST_KTS
1742

1742

1743
The current wind gusting speed in Knots.
1743
The current wind gusting speed in Knots.
1744

1744

1745
=item WIND_GUST_MPH
1745
=item WIND_GUST_MPH
1746

1746

1747
The current wind gusting speed in Miles Per Hour.
1747
The current wind gusting speed in Miles Per Hour.
1748

1748

1749
=item WIND_GUST_MS
1749
=item WIND_GUST_MS
1750

1750

1751
The current wind gusting speed in Metres Per Second.
1751
The current wind gusting speed in Metres Per Second.
1752

1752

1753
=item WIND_VAR
1753
=item WIND_VAR
1754

1754

1755
The wind variation in English
1755
The wind variation in English
1756

1756

1757
=item WIND_VAR_1
1757
=item WIND_VAR_1
1758

1758

1759
The first wind variation direction
1759
The first wind variation direction
1760

1760

1761
=item WIND_VAR_ENG_1
1761
=item WIND_VAR_ENG_1
1762

1762

1763
The first wind variation direction in English
1763
The first wind variation direction in English
1764

1764

1765
=item WIND_VAR_2
1765
=item WIND_VAR_2
1766

1766

1767
The second wind variation direction
1767
The second wind variation direction
1768

1768

1769
=item WIND_VAR_ENG_2
1769
=item WIND_VAR_ENG_2
1770

1770

1771
The second wind variation direction in English
1771
The second wind variation direction in English
1772

1772

1773
=item VISIBILITY
1773
=item VISIBILITY
1774

1774

1775
Visibility information.
1775
Visibility information.
1776

1776

1777
=item VISIBILITY_RUS
1777
=item VISIBILITY_RUS
1778

1778

1779
Visibility information in Russian.
1779
Visibility information in Russian.
1780

1780

1781
=item WIND
1781
=item WIND
1782

1782

1783
Wind information.
1783
Wind information.
1784

1784

1785
=item RUNWAY
1785
=item RUNWAY
1786

1786

1787
Runway information.
1787
Runway information.
1788

1788

1789
=item WEATHER
1789
=item WEATHER
1790

1790

1791
Current weather (array)
1791
Current weather (array)
1792

1792

1793
==item WEATHER_RUS
1793
==item WEATHER_RUS
1794

1794

1795
Current weather in Russian (array)
1795
Current weather in Russian (array)
1796

1796

1797
==item WEATHER_RAW
1797
==item WEATHER_RAW
1798

1798

1799
Current weather in RAW-data (array)
1799
Current weather in RAW-data (array)
1800

1800

1801
=item WEATHER_LOG
1801
=item WEATHER_LOG
1802

1802

1803
Current weather log (array)
1803
Current weather log (array)
1804

1804

1805
=item SKY
1805
=item SKY
1806

1806

1807
Current cloud cover (array)
1807
Current cloud cover (array)
1808

1808

1809
=item SKY_RUS
1809
=item SKY_RUS
1810

1810

1811
Current cloud cover in Russian (array)
1811
Current cloud cover in Russian (array)
1812

1812

1813
=item SKY_RAW
1813
=item SKY_RAW
1814

1814

1815
Current cloud cover in RAW-data (array)
1815
Current cloud cover in RAW-data (array)
1816

1816

1817
=item TEMP_C
1817
=item TEMP_C
1818

1818

1819
Temperature in Celsius.
1819
Temperature in Celsius.
1820

1820

1821
=item TEMP_F
1821
=item TEMP_F
1822

1822

1823
Temperature in Fahrenheit.
1823
Temperature in Fahrenheit.
1824

1824

1825
=item TEMP_WC
1825
=item TEMP_WC
1826

1826

1827
Windchill Temperature in Celsius.
1827
Windchill Temperature in Celsius.
1828

1828

1829
=item DEW_C
1829
=item DEW_C
1830

1830

1831
Dew point in Celsius.
1831
Dew point in Celsius.
1832

1832

1833
=item DEW_F
1833
=item DEW_F
1834

1834

1835
Dew point in Fahrenheit.
1835
Dew point in Fahrenheit.
1836

1836

1837
=item HOURLY_TEMP_F
1837
=item HOURLY_TEMP_F
1838

1838

1839
Hourly current temperature, fahrenheit
1839
Hourly current temperature, fahrenheit
1840

1840

1841
=item HOURLY_TEMP_C
1841
=item HOURLY_TEMP_C
1842

1842

1843
Hourly current temperature, celcius
1843
Hourly current temperature, celcius
1844

1844

1845
=item HOURLY_DEW_F
1845
=item HOURLY_DEW_F
1846

1846

1847
Hourly dewpoint, fahrenheit
1847
Hourly dewpoint, fahrenheit
1848

1848

1849
=item HOURLY_DEW_C
1849
=item HOURLY_DEW_C
1850

1850

1851
Hourly dewpoint, celcius
1851
Hourly dewpoint, celcius
1852

1852

1853
=item ALT
1853
=item ALT
1854

1854

1855
Altimeter setting (barometric pressure).
1855
Altimeter setting (barometric pressure).
1856

1856

1857
=item ALT_HP
1857
=item ALT_HP
1858

1858

1859
Altimeter setting in hectopascals.
1859
Altimeter setting in hectopascals.
1860

1860

1861
=item REMARKS
1861
=item REMARKS
1862

1862

1863
Any remarks in the report.
1863
Any remarks in the report.
1864

1864

1865
=back
1865
=back
1866

1866

1867
=head1 NOTES
1867
=head1 NOTES
1868

1868

1869
Test suite is small and incomplete. Needs work yet.
1869
Test suite is small and incomplete. Needs work yet.
1870

1870

1871
Older versions of this module were installed as "METAR" instaed of
1871
Older versions of this module were installed as "METAR" instaed of
1872
"Geo::METAR"
1872
"Geo::METAR"
1873

1873

1874
=head1 BUGS
1874
=head1 BUGS
1875

1875

1876
The Geo::METAR is only initialized once, which means you'll get left-over
1876
The Geo::METAR is only initialized once, which means you'll get left-over
1877
crud in variables when you call the metar() function twice.
1877
crud in variables when you call the metar() function twice.
1878

1878

1879
What is an invalid METAR in one country is a standard one in the next.
1879
What is an invalid METAR in one country is a standard one in the next.
1880
The standard is interpreted and used by meteorologists all over the world,
1880
The standard is interpreted and used by meteorologists all over the world,
1881
with local variations. This means there will always be METARs that will
1881
with local variations. This means there will always be METARs that will
1882
trip the parser.
1882
trip the parser.
1883

1883

1884
=head1 TODO
1884
=head1 TODO
1885

1885

1886
There is a TODO file included in the Geo::METAR distribution listing
1886
There is a TODO file included in the Geo::METAR distribution listing
1887
the outstanding tasks that I or others have devised. Please check that
1887
the outstanding tasks that I or others have devised. Please check that
1888
list before you submit a bug report or request a new feture. It might
1888
list before you submit a bug report or request a new feture. It might
1889
already be on the TODO list.
1889
already be on the TODO list.
1890

1890

1891
=head1 AUTHORS AND COPYRIGHT
1891
=head1 AUTHORS AND COPYRIGHT
1892

1892

1893
Copyright 1997-2000, Jeremy D. Zawodny <Jeremy [at] Zawodny.com>
1893
Copyright 1997-2000, Jeremy D. Zawodny <Jeremy [at] Zawodny.com>
1894

1894

1895
Copyright 2007, Koos van den Hout <koos@kzdoos.xs4all.nl>
1895
Copyright 2007, Koos van den Hout <koos@kzdoos.xs4all.nl>
1896

1896

1897
Copyright 2010, Alexander Wolf <alex.v.wolf@gmail.com>
1897
Copyright 2010, Alexander Wolf <alex.v.wolf@gmail.com>
1898

1898

1899
Geo::ModMETAR is covered under the GNU Public License (GPL) version 2 or
1899
Geo::ModMETAR is covered under the GNU Public License (GPL) version 2 or
1900
later.
1900
later.
1901

1901

1902
The Geo::ModMETAR Web site is located at:
1902
The Geo::ModMETAR Web site is located at:
1903

1903

1904
  http://astro.uni-altai.ru/~aw/perl/Geo-ModMETAR/
1904
  http://astro.uni-altai.ru/~aw/perl/Geo-ModMETAR/
1905

1905

1906
=head1 CREDITS
1906
=head1 CREDITS
1907

1907

1908
In addition to our work on Geo::METAR, We've received ideas, help, and
1908
In addition to our work on Geo::METAR, We've received ideas, help, and
1909
patches from the following folks:
1909
patches from the following folks:
1910

1910

1911
  * Ethan Dicks <ethan.dicks [at] gmail.com>
1911
  * Ethan Dicks <ethan.dicks [at] gmail.com>
1912

1912

1913
    Testing of Geo::METAR at the South Pole. Corrections and pointers
1913
    Testing of Geo::METAR at the South Pole. Corrections and pointers
1914
        to interesting cases to test.
1914
        to interesting cases to test.
1915

1915

1916
  * Otterboy <jong [at] watchguard.com>
1916
  * Otterboy <jong [at] watchguard.com>
1917

1917

1918
    Random script fixes and initial debugging help
1918
    Random script fixes and initial debugging help
1919

1919

1920
  * Remi Lefebvre <remi [at] solaria.dhis.org>
1920
  * Remi Lefebvre <remi [at] solaria.dhis.org>
1921

1921

1922
    Debian packaging as libgeo-metar-perl.deb.
1922
    Debian packaging as libgeo-metar-perl.deb.
1923

1923

1924
  * Mike Engelhart <mengelhart [at] earthtrip.com>
1924
  * Mike Engelhart <mengelhart [at] earthtrip.com>
1925

1925

1926
    Wind direction naming corrections.
1926
    Wind direction naming corrections.
1927

1927

1928
  * Michael Starling <mstarling [at] logic.bm>
1928
  * Michael Starling <mstarling [at] logic.bm>
1929

1929

1930
    Wind direction naming corrections.
1930
    Wind direction naming corrections.
1931

1931

1932
  * Hans Einar Nielssen <hans.einar [at] nielssen.com>
1932
  * Hans Einar Nielssen <hans.einar [at] nielssen.com>
1933

1933

1934
    Wind direction naming corrections.
1934
    Wind direction naming corrections.
1935

1935

1936
  * Nathan Neulinger <nneul [at] umr.edu>
1936
  * Nathan Neulinger <nneul [at] umr.edu>
1937

1937

1938
    Lots of enhancements and corrections. Too many to list here.
1938
    Lots of enhancements and corrections. Too many to list here.
1939

1939

1940
=head1 RELATED PROJECTS
1940
=head1 RELATED PROJECTS
1941

1941

1942
B<lcdproc> at http://www.lcdproc.org/ uses Geo::METAR in lcdmetar.pl to
1942
B<lcdproc> at http://www.lcdproc.org/ uses Geo::METAR in lcdmetar.pl to
1943
display weather data on an lcd.
1943
display weather data on an lcd.
1944

1944

1945
=cut
1945
=cut
1946
1946
1947
1947
1948
# vim:expandtab:sw=4 ts=4
1948
# vim:expandtab:sw=4 ts=4
1949
 
1949