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

Редакция

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

Редакция 7 Редакция 8
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.0'; # 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->{SLP}           = undef;             # sea level pressure
411
    $self->{SLP}           = undef;             # sea level pressure
412
    $self->{REMARKS}       = undef;             # remarks
412
    $self->{REMARKS}       = undef;             # remarks
413
    $self->{WEATHER_RAW}   = [ ];               # RAW data for weather
413
    $self->{WEATHER_RAW}   = [ ];               # RAW data for weather
414
    $self->{WEATHER_RUS}   = [ ];               # current weather in Russian
414
    $self->{WEATHER_RUS}   = [ ];               # current weather in Russian
415
    $self->{SKY_RAW}       = [ ];               # RAW data for sky
415
    $self->{SKY_RAW}       = [ ];               # RAW data for sky
416
    $self->{SKY_RUS}       = [ ];               # current sky in Russian
416
    $self->{SKY_RUS}       = [ ];               # current sky in Russian
417
    $self->{VISIBILITY_RUS}= undef;             # visibility info    
417
    $self->{VISIBILITY_RUS}= undef;             # visibility info    
418
418
419
    $self->{tokens}        = [ ];               # the "token" list
419
    $self->{tokens}        = [ ];               # the "token" list
420
    $self->{type}          = "METAR";           # the report type (METAR/SPECI)
420
    $self->{type}          = "METAR";           # the report type (METAR/SPECI)
421
                                                # default=METAR
421
                                                # default=METAR
422
    $self->{site}          = undef;             # the site code (4 chars)
422
    $self->{site}          = undef;             # the site code (4 chars)
423
    $self->{date_time}     = undef;             # date/time
423
    $self->{date_time}     = undef;             # date/time
424
    $self->{modifier}      = undef;             # the AUTO/COR modifier
424
    $self->{modifier}      = undef;             # the AUTO/COR modifier
425
    $self->{wind}          = undef;             # the wind information
425
    $self->{wind}          = undef;             # the wind information
426
    $self->{windtype}      = undef;             # the wind speed type (knots/meterpersecond/kilometersperhour)
426
    $self->{windtype}      = undef;             # the wind speed type (knots/meterpersecond/kilometersperhour)
427
    $self->{windvar}       = undef;             # the wind variation
427
    $self->{windvar}       = undef;             # the wind variation
428
    $self->{visibility}    = undef;             # visibility information
428
    $self->{visibility}    = undef;             # visibility information
429
    $self->{runway}        = undef;             # runway visibility
429
    $self->{runway}        = undef;             # runway visibility
430
    $self->{weather}       = [ ];               # current weather conditions
430
    $self->{weather}       = [ ];               # current weather conditions
431
    $self->{sky}           = [ ];               # sky conditions (cloud cover)
431
    $self->{sky}           = [ ];               # sky conditions (cloud cover)
432
    $self->{temp_dew}      = undef;             # temp and dew pt.
432
    $self->{temp_dew}      = undef;             # temp and dew pt.
433
    $self->{alt}           = undef;             # altimeter setting
433
    $self->{alt}           = undef;             # altimeter setting
434
    $self->{pressure}      = undef;             # pressure (HPa)
434
    $self->{pressure}      = undef;             # pressure (HPa)
435
    $self->{slp}           = undef;             # sea level pressure
435
    $self->{slp}           = undef;             # sea level pressure
436
    $self->{remarks}       = [ ];               # remarks
436
    $self->{remarks}       = [ ];               # remarks
437
437
438
    $self->{debug}         = undef;             # enable debug trace
438
    $self->{debug}         = undef;             # enable debug trace
439
439
440
    bless $self, $class;
440
    bless $self, $class;
441
    return $self;
441
    return $self;
442
}
442
}
443
443
444
##
444
##
445
## Autoload for access methods to stuff in %fields hash. We should
445
## Autoload for access methods to stuff in %fields hash. We should
446
## probably disallow access to the lower-case items as stated above,
446
## probably disallow access to the lower-case items as stated above,
447
## but I don't feel like being a Nazi about it. Besides, I haven't
447
## but I don't feel like being a Nazi about it. Besides, I haven't
448
## checked to see what that might break.
448
## checked to see what that might break.
449
##
449
##
450
450
451
sub AUTOLOAD
451
sub AUTOLOAD
452
{
452
{
453
    my $self = shift;
453
    my $self = shift;
454
454
455
    if (not ref $self)
455
    if (not ref $self)
456
    {
456
    {
457
        cluck "bad AUTOLOAD for obj [$self]";
457
        cluck "bad AUTOLOAD for obj [$self]";
458
    }
458
    }
459
459
460
    if ($AUTOLOAD =~ /.*::(.*)/)
460
    if ($AUTOLOAD =~ /.*::(.*)/)
461
    {
461
    {
462
        my $key = $1;
462
        my $key = $1;
463
463
464
464
465
        ## Backward compatible temps...
465
        ## Backward compatible temps...
466
466
467
        my %compat = (
467
        my %compat = (
468
                      F_TEMP    =>  'TEMP_F',
468
                      F_TEMP    =>  'TEMP_F',
469
                      C_TEMP    =>  'TEMP_C',
469
                      C_TEMP    =>  'TEMP_C',
470
                      F_DEW     =>  'DEW_F',
470
                      F_DEW     =>  'DEW_F',
471
                      C_DEW     =>  'DEW_C',
471
                      C_DEW     =>  'DEW_C',
472
                     );
472
                     );
473
473
474
        if ($compat{$key})
474
        if ($compat{$key})
475
        {
475
        {
476
            $key = $compat{$key};
476
            $key = $compat{$key};
477
        }
477
        }
478
478
479
        ## Check for the items...
479
        ## Check for the items...
480
480
481
        if (exists $self->{$key})
481
        if (exists $self->{$key})
482
        {
482
        {
483
            return $self->{$key};
483
            return $self->{$key};
484
        }
484
        }
485
        else
485
        else
486
        {
486
        {
487
            return undef;
487
            return undef;
488
        }
488
        }
489
    }
489
    }
490
    else
490
    else
491
    {
491
    {
492
        warn "strange AUTOLOAD problem!";
492
        warn "strange AUTOLOAD problem!";
493
        return undef;
493
        return undef;
494
    }
494
    }
495
}
495
}
496
496
497
##
497
##
498
## Get current version number.
498
## Get current version number.
499
##
499
##
500
500
501
sub version
501
sub version
502
{
502
{
503
    my $self = shift;
503
    my $self = shift;
504
    print "version() called.\n" if $self->{debug};
504
    print "version() called.\n" if $self->{debug};
505
    return $self->{VERSION};
505
    return $self->{VERSION};
506
}
506
}
507
507
508
##
508
##
509
## Take a METAR, tokenize, and process it.
509
## Take a METAR, tokenize, and process it.
510
##
510
##
511
511
512
sub metar
512
sub metar
513
{
513
{
514
    my $self = shift;
514
    my $self = shift;
515
515
516
    if (@_)
516
    if (@_)
517
    {
517
    {
518
        $self->{METAR} = shift;
518
        $self->{METAR} = shift;
519
        $self->{METAR} =~ s/\n//g;    ## nuke any newlines
519
        $self->{METAR} =~ s/\n//g;    ## nuke any newlines
520
        _tokenize($self);
520
        _tokenize($self);
521
        _process($self);
521
        _process($self);
522
    }
522
    }
523
    return $self->{METAR};
523
    return $self->{METAR};
524
}
524
}
525
525
526
##
526
##
527
## Break {METAR} into parts. Stuff into @tokens.
527
## Break {METAR} into parts. Stuff into @tokens.
528
##
528
##
529
529
530
sub _tokenize
530
sub _tokenize
531
{
531
{
532
    my $self = shift;
532
    my $self = shift;
533
    my $tok;
533
    my $tok;
534
    my @toks;
534
    my @toks;
535
535
536
    # Split tokens on whitespace.
536
    # Split tokens on whitespace.
537
    @toks = split(/\s+/, $self->{METAR});
537
    @toks = split(/\s+/, $self->{METAR});
538
    $self->{tokens} = \@toks;
538
    $self->{tokens} = \@toks;
539
}
539
}
540
540
541
## Process @tokens to populate METAR values.
541
## Process @tokens to populate METAR values.
542
##
542
##
543
## This is a long and involved subroutine. It basically copies the
543
## This is a long and involved subroutine. It basically copies the
544
## @tokens array and treats it as a stack, popping off items,
544
## @tokens array and treats it as a stack, popping off items,
545
## examining them, and see what they look like.  Based on their
545
## examining them, and see what they look like.  Based on their
546
## "apppearance" it takes care populating the proper fields
546
## "apppearance" it takes care populating the proper fields
547
## internally.
547
## internally.
548
548
549
sub _process
549
sub _process
550
{
550
{
551
    my $self = shift;
551
    my $self = shift;
552
552
553
    my @toks = @{$self->{tokens}};      # copy tokens array...
553
    my @toks = @{$self->{tokens}};      # copy tokens array...
554
554
555
    my $tok;
555
    my $tok;
556
556
557
    ## This is a semi-brute-force way of doing things, but the amount
557
    ## This is a semi-brute-force way of doing things, but the amount
558
    ## of data is relatively small, so it shouldn't be a big deal.
558
    ## of data is relatively small, so it shouldn't be a big deal.
559
    ##
559
    ##
560
    ## Ideally, I'd have it skip checks for items which have been
560
    ## Ideally, I'd have it skip checks for items which have been
561
    ## found, but that would make this more "linear" and I'd remove
561
    ## found, but that would make this more "linear" and I'd remove
562
    ## the pretty while loop.
562
    ## the pretty while loop.
563
        #
563
        #
564
        # KH: modified to maintain state to not get lost in remarks and stuff
564
        # KH: modified to maintain state to not get lost in remarks and stuff
565
        # and be a lot better at parsing
565
        # and be a lot better at parsing
566
       
566
       
567
        # states
567
        # states
568
568
569
        my $expect_type = 0;
569
        my $expect_type = 0;
570
        my $expect_site = 1;
570
        my $expect_site = 1;
571
        my $expect_datetime = 2;
571
        my $expect_datetime = 2;
572
        my $expect_modifier = 3;
572
        my $expect_modifier = 3;
573
        my $expect_wind = 4;
573
        my $expect_wind = 4;
574
        my $expect_visibility = 5;
574
        my $expect_visibility = 5;
575
        my $expect_runwayvisual = 6;
575
        my $expect_runwayvisual = 6;
576
        my $expect_presentweather = 7;
576
        my $expect_presentweather = 7;
577
        my $expect_clouds = 8;
577
        my $expect_clouds = 8;
578
        my $expect_temperature = 9;
578
        my $expect_temperature = 9;
579
        my $expect_pressure = 10;
579
        my $expect_pressure = 10;
580
        my $expect_recentweather = 11;
580
        my $expect_recentweather = 11;
581
        my $expect_remarks = 12;
581
        my $expect_remarks = 12;
582
        my $expect_usremarks = 13;
582
        my $expect_usremarks = 13;
583
583
584
        my $parsestate = $expect_type;
584
        my $parsestate = $expect_type;
585
585
586
        # windtypes
586
        # windtypes
587
       
587
       
588
        my $wt_knots = 1;
588
        my $wt_knots = 1;
589
        my $wt_mps = 2;
589
        my $wt_mps = 2;
590
        my $wt_kph = 3;
590
        my $wt_kph = 3;
591
591
592
    ## Assume standard report by default
592
    ## Assume standard report by default
593
593
594
    $self->{type} = "METAR";
594
    $self->{type} = "METAR";
595
    $self->{TYPE} = "Routine Weather Report";
595
    $self->{TYPE} = "Routine Weather Report";
596
596
597
    while (defined($tok = shift(@toks))) ## as long as there are tokens
597
    while (defined($tok = shift(@toks))) ## as long as there are tokens
598
    {
598
    {
599
        print "trying to match [$tok] state is $parsestate\n" if $self->{debug};
599
        print "trying to match [$tok] state is $parsestate\n" if $self->{debug};
600
600
601
        ##
601
        ##
602
        ## is it a report type?
602
        ## is it a report type?
603
        ##
603
        ##
604
604
605
        if (($parsestate == $expect_type) and ($tok =~ /(METAR|SPECI)/i))
605
        if (($parsestate == $expect_type) and ($tok =~ /(METAR|SPECI)/i))
606
        {
606
        {
607
            $self->{type} = $tok;
607
            $self->{type} = $tok;
608
608
609
            if ($self->{type} eq "METAR")
609
            if ($self->{type} eq "METAR")
610
            {
610
            {
611
                $self->{TYPE} = "Routine Weather Report";
611
                $self->{TYPE} = "Routine Weather Report";
612
            }
612
            }
613
            elsif ($self->{type} eq "SPECI")
613
            elsif ($self->{type} eq "SPECI")
614
            {
614
            {
615
                $self->{TYPE} = "Special Weather Report";
615
                $self->{TYPE} = "Special Weather Report";
616
            }
616
            }
617
            print "[$tok] is a report type.\n" if $self->{debug};
617
            print "[$tok] is a report type.\n" if $self->{debug};
618
                        $parsestate = $expect_site;
618
                        $parsestate = $expect_site;
619
            next;
619
            next;
620
        }
620
        }
621
621
622
        ##
622
        ##
623
        ## is is a site ID?
623
        ## is is a site ID?
624
        ##
624
        ##
625
625
626
        elsif (($parsestate <= $expect_site) and ($tok =~ /([A-Z]{4}|K[A-Z0-9]{3})/))
626
        elsif (($parsestate <= $expect_site) and ($tok =~ /([A-Z]{4}|K[A-Z0-9]{3})/))
627
        {
627
        {
628
            $self->{site} = $tok;
628
            $self->{site} = $tok;
629
            print "[$tok] is a site ID.\n" if $self->{debug};
629
            print "[$tok] is a site ID.\n" if $self->{debug};
630
                        $parsestate = $expect_datetime;
630
                        $parsestate = $expect_datetime;
631
            next;
631
            next;
632
        }
632
        }
633
633
634
        ##
634
        ##
635
        ## is it a date/time?
635
        ## is it a date/time?
636
        ##
636
        ##
637
637
638
        elsif (($parsestate == $expect_datetime) and ($tok =~ /\d{6,6}Z/i))
638
        elsif (($parsestate == $expect_datetime) and ($tok =~ /\d{6,6}Z/i))
639
        {
639
        {
640
            $self->{date_time} = $tok;
640
            $self->{date_time} = $tok;
641
            print "[$tok] is a date/time.\n" if $self->{debug};
641
            print "[$tok] is a date/time.\n" if $self->{debug};
642
                        $parsestate = $expect_modifier;
642
                        $parsestate = $expect_modifier;
643
            next;
643
            next;
644
644
645
645
646
        }
646
        }
647
647
648
        ##
648
        ##
649
        ## is it a report modifier?
649
        ## is it a report modifier?
650
        ##
650
        ##
651
651
652
        elsif (($parsestate == $expect_modifier) and ($tok =~ /AUTO|COR|CC[A-Z]/i))
652
        elsif (($parsestate == $expect_modifier) and ($tok =~ /AUTO|COR|CC[A-Z]/i))
653
        {
653
        {
654
            $self->{modifier} = $tok;
654
            $self->{modifier} = $tok;
655
            print "[$tok] is a report modifier.\n" if $self->{debug};
655
            print "[$tok] is a report modifier.\n" if $self->{debug};
656
                        $parsestate = $expect_wind;
656
                        $parsestate = $expect_wind;
657
            next;
657
            next;
658
        }
658
        }
659
659
660
        ##
660
        ##
661
        ## is it wind information in knots?
661
        ## is it wind information in knots?
662
        #
662
        #
663
                # eew: KT seems to be optional
663
                # eew: KT seems to be optional
664
                # but making it optional fails on other stuff
664
                # but making it optional fails on other stuff
665
                # sortafix: wind needs to be \d\d\d\d\d or VRB\d\d
665
                # sortafix: wind needs to be \d\d\d\d\d or VRB\d\d
666
                #      optional \d\d\d\d\dG\d\d\d (gust direction)
666
                #      optional \d\d\d\d\dG\d\d\d (gust direction)
667
667
668
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /(\d{3}|VRB)\d{2}(G\d{1,3})?(KT)?$/i))
668
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /(\d{3}|VRB)\d{2}(G\d{1,3})?(KT)?$/i))
669
        {
669
        {
670
            $self->{wind} = $tok;
670
            $self->{wind} = $tok;
671
                        $self->{windtype} = $wt_knots;
671
                        $self->{windtype} = $wt_knots;
672
            print "[$tok] is wind information in knots.\n" if $self->{debug};
672
            print "[$tok] is wind information in knots.\n" if $self->{debug};
673
                        $parsestate = $expect_wind; # stay in wind, it can have variation
673
                        $parsestate = $expect_wind; # stay in wind, it can have variation
674
            next;
674
            next;
675
        }
675
        }
676
676
677
                ##
677
                ##
678
                ## is it wind information in meters per second?
678
                ## is it wind information in meters per second?
679
                ##
679
                ##
680
                ## can be variable too
680
                ## can be variable too
681
681
682
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /^(\d{3}|VRB)\d{2}(G\d{2,3})?MPS$/))
682
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /^(\d{3}|VRB)\d{2}(G\d{2,3})?MPS$/))
683
        {
683
        {
684
            $self->{wind} = $tok;
684
            $self->{wind} = $tok;
685
            print "[$tok] is wind information.\n" if $self->{debug};
685
            print "[$tok] is wind information.\n" if $self->{debug};
686
                        $self->{windtype} = $wt_mps;
686
                        $self->{windtype} = $wt_mps;
687
                        $parsestate = $expect_wind; # stay in wind, it can have variation
687
                        $parsestate = $expect_wind; # stay in wind, it can have variation
688
            next;
688
            next;
689
        }
689
        }
690
690
691
                ##
691
                ##
692
                ## is it wind variation information?
692
                ## is it wind variation information?
693
                ##
693
                ##
694
694
695
                elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\d{3}V\d{3}$/))
695
                elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\d{3}V\d{3}$/))
696
                {
696
                {
697
                        $self->{windvar} = $tok;
697
                        $self->{windvar} = $tok;
698
                        print "[$tok] is wind variation information.\n" if $self->{debug};
698
                        print "[$tok] is wind variation information.\n" if $self->{debug};
699
                        $parsestate = $expect_visibility;
699
                        $parsestate = $expect_visibility;
700
                        next;
700
                        next;
701
                }
701
                }
702
702
703
                ##
703
                ##
704
                ## wind information missing at the moment?
704
                ## wind information missing at the moment?
705
                ##
705
                ##
706
706
707
                elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\/\/\/\/\/(KT|MPS)$/)){
707
                elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\/\/\/\/\/(KT|MPS)$/)){
708
                        print "[$tok] is missing wind information.\n" if $self->{debug};
708
                        print "[$tok] is missing wind information.\n" if $self->{debug};
709
                        $parsestate = $expect_visibility;
709
                        $parsestate = $expect_visibility;
710
                        next;
710
                        next;
711
                }
711
                }
712
712
713
                ##
713
                ##
714
                ## is it visibility information in meters?
714
                ## is it visibility information in meters?
715
                ##
715
                ##
716
       
716
       
717
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}$/))
717
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}$/))
718
                {
718
                {
719
                        $self->{visibility} = $tok;
719
                        $self->{visibility} = $tok;
720
            print "[$tok] is numerical visibility information.\n" if $self->{debug};
720
            print "[$tok] is numerical visibility information.\n" if $self->{debug};
721
                        $parsestate = $expect_visibility;
721
                        $parsestate = $expect_visibility;
722
            next;
722
            next;
723
        }
723
        }
724
724
725
                ## auto visibility information in meters?
725
                ## auto visibility information in meters?
726
726
727
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}NDV$/))
727
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}NDV$/))
728
                {
728
                {
729
                        $self->{visibility} = $tok;
729
                        $self->{visibility} = $tok;
730
            print "[$tok] is automatic numerical visibility information.\n" if $self->{debug};
730
            print "[$tok] is automatic numerical visibility information.\n" if $self->{debug};
731
                        $parsestate = $expect_visibility;
731
                        $parsestate = $expect_visibility;
732
            next;
732
            next;
733
        }
733
        }
734
734
735
        ##
735
        ##
736
        ## is it visibility information in statute miles?
736
        ## is it visibility information in statute miles?
737
        ##
737
        ##
738
738
739
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /.*?SM$/i))
739
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /.*?SM$/i))
740
        {
740
        {
741
            $self->{visibility} = $tok;
741
            $self->{visibility} = $tok;
742
            print "[$tok] is statute miles visibility information.\n" if $self->{debug};
742
            print "[$tok] is statute miles visibility information.\n" if $self->{debug};
743
                        $parsestate = $expect_visibility;
743
                        $parsestate = $expect_visibility;
744
            next;
744
            next;
745
        }
745
        }
746
746
747
        ##
747
        ##
748
        ## is it visibility information with a leading digit?
748
        ## is it visibility information with a leading digit?
749
                ##
749
                ##
750
                ## sample:
750
                ## sample:
751
                ## KERV 132345Z AUTO 07008KT 1 1/4SM HZ 34/11 A3000 RMK AO2
751
                ## KERV 132345Z AUTO 07008KT 1 1/4SM HZ 34/11 A3000 RMK AO2
752
                ##                           ^^^^^^^
752
                ##                           ^^^^^^^
753
        ##
753
        ##
754
754
755
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and  ($tok =~ /^\d$/))
755
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and  ($tok =~ /^\d$/))
756
        {
756
        {
757
            $tok .= " " . shift(@toks);
757
            $tok .= " " . shift(@toks);
758
            $self->{visibility} = $tok;
758
            $self->{visibility} = $tok;
759
            print "[$tok] is multi-part visibility information.\n" if $self->{debug};
759
            print "[$tok] is multi-part visibility information.\n" if $self->{debug};
760
                        $parsestate = $expect_visibility;
760
                        $parsestate = $expect_visibility;
761
            next;
761
            next;
762
        }
762
        }
763
763
764
                ## visibility modifier
764
                ## visibility modifier
765
765
766
                elsif (($parsestate == $expect_visibility) and ($tok =~ /^\d{4}(N|S|E|W|NE|NW|SE|SW)$/))
766
                elsif (($parsestate == $expect_visibility) and ($tok =~ /^\d{4}(N|S|E|W|NE|NW|SE|SW)$/))
767
                {
767
                {
768
            print "[$tok] is a visibility modifier.\n" if $self->{debug};
768
            print "[$tok] is a visibility modifier.\n" if $self->{debug};
769
            next;
769
            next;
770
                }
770
                }
771
771
772
        ##
772
        ##
773
        ## is it runway visibility info?
773
        ## is it runway visibility info?
774
        ##
774
        ##
775
                # KH: I've seen runway visibility with 'U' units
775
                # KH: I've seen runway visibility with 'U' units
776
                # EHSB 121425Z 22010KT 1200 R27/1600U -DZ BKN003 OVC007 07/07 Q1016 AMB FCST CANCEL
776
                # EHSB 121425Z 22010KT 1200 R27/1600U -DZ BKN003 OVC007 07/07 Q1016 AMB FCST CANCEL
777
                # U= going up, D= going down, N= no change
777
                # U= going up, D= going down, N= no change
778
                # tendency of visual range, http://stoivane.kapsi.fi/metar/
778
                # tendency of visual range, http://stoivane.kapsi.fi/metar/
779
779
780
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_presentweather) and ($tok =~ /R\d+(L|R|C)?\/P?\d+(VP?\d+)?(FT|D|U|N|\/)?$/i))
780
        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
        {
781
        {
782
            push (@{$self->{RUNWAY}},$tok);
782
            push (@{$self->{RUNWAY}},$tok);
783
            print "[$tok] is runway visual information.\n" if $self->{debug};
783
            print "[$tok] is runway visual information.\n" if $self->{debug};
784
                        $parsestate = $expect_runwayvisual;
784
                        $parsestate = $expect_runwayvisual;
785
                        # there can be multiple runways, so stay at this state
785
                        # there can be multiple runways, so stay at this state
786
            next;
786
            next;
787
        }
787
        }
788
788
789
        ##
789
        ##
790
        ## is it current weather info?
790
        ## is it current weather info?
791
        ##
791
        ##
792
792
793
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_clouds) and ($tok =~ /^(-|\+)?(VC)?($_weather_types_pat)+/i))
793
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_clouds) and ($tok =~ /^(-|\+)?(VC)?($_weather_types_pat)+/i))
794
        {
794
        {
795
            my $engl = "";
795
            my $engl = "";
796
            my $rusl = "";
796
            my $rusl = "";
797
            my $rawl = "";
797
            my $rawl = "";
798
            my $qual = $1;
798
            my $qual = $1;
799
            my $addlqual = $2;
799
            my $addlqual = $2;
800
800
801
            ## qualifier
801
            ## qualifier
802
802
803
            if (defined $qual)
803
            if (defined $qual)
804
            {
804
            {
805
                if ( $qual eq "-" ) {
805
                if ( $qual eq "-" ) {
806
                    $engl = "light";
806
                    $engl = "light";
807
                    $rusl = "легкий";
807
                    $rusl = "легкий";
808
                } elsif ( $qual eq "+" ) {
808
                } elsif ( $qual eq "+" ) {
809
                    $engl = "heavy";
809
                    $engl = "heavy";
810
                    $rusl = "сильный";
810
                    $rusl = "сильный";
811
                } else {
811
                } else {
812
                    $engl = ""; ## moderate
812
                    $engl = ""; ## moderate
813
                    $rusl = "";
813
                    $rusl = "";
814
                }
814
                }
815
                $rawl = $qual;
815
                $rawl = $qual;
816
            }
816
            }
817
            else
817
            else
818
            {
818
            {
819
                $engl = ""; ## moderate
819
                $engl = ""; ## moderate
820
                $rusl = "";
820
                $rusl = "";
821
                $rawl = "";
821
                $rawl = "";
822
            }
822
            }
823
823
824
            while ( $tok =~ /($_weather_types_pat)/gi )
824
            while ( $tok =~ /($_weather_types_pat)/gi )
825
            {
825
            {
826
                $engl .= " " . $_weather_types{$1}; ## figure out weather                
826
                $engl .= " " . $_weather_types{$1}; ## figure out weather                
827
                $rusl .= " " . $_weather_types_ru{$1};
827
                $rusl .= " " . $_weather_types_ru{$1};
828
                $rawl .= " " . $1;
828
                $rawl .= " " . $1;
829
            }
829
            }
830
           
830
           
831
831
832
            ## addl qualifier
832
            ## addl qualifier
833
833
834
            if (defined $addlqual)
834
            if (defined $addlqual)
835
            {
835
            {
836
                if ( $addlqual eq "VC" )
836
                if ( $addlqual eq "VC" )
837
                {
837
                {
838
                    $engl .= " in vicinity";
838
                    $engl .= " in vicinity";
839
                    $rusl .= " в окрестностях";
839
                    $rusl .= " в окрестностях";
840
                }
840
                }
841
            }
841
            }
842
842
843
            $engl =~ s/^\s//gio;
843
            $engl =~ s/^\s//gio;
844
            $engl =~ s/\s\s/ /gio;
844
            $engl =~ s/\s\s/ /gio;
845
            $rusl =~ s/^\s//gio;
845
            $rusl =~ s/^\s//gio;
846
            $rusl =~ s/\s\s/ /gio;
846
            $rusl =~ s/\s\s/ /gio;
847
            $rawl =~ s/^\s//gio;
847
            $rawl =~ s/^\s//gio;
848
            $rawl =~ s/\s\s/ /gio;
848
            $rawl =~ s/\s\s/ /gio;
849
849
850
            push(@{$self->{WEATHER}},$engl);
850
            push(@{$self->{WEATHER}},$engl);
851
            push(@{$self->{WEATHER_RUS}},$rusl);
851
            push(@{$self->{WEATHER_RUS}},$rusl);
852
            push(@{$self->{WEATHER_RAW}},$rawl);
852
            push(@{$self->{WEATHER_RAW}},$rawl);
853
            push(@{$self->{weather}},$tok);
853
            push(@{$self->{weather}},$tok);
854
            print "[$tok] is current weather.\n" if $self->{debug};
854
            print "[$tok] is current weather.\n" if $self->{debug};
855
                        $parsestate = $expect_presentweather;
855
                        $parsestate = $expect_presentweather;
856
                        # there can be multiple current weather types, so stay at this state
856
                        # there can be multiple current weather types, so stay at this state
857
            next;
857
            next;
858
        }
858
        }
859
859
860
                ##
860
                ##
861
                ## special case: CAVOK
861
                ## special case: CAVOK
862
                ##
862
                ##
863
               
863
               
864
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok eq 'CAVOK' ))
864
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok eq 'CAVOK' ))
865
                {
865
                {
866
            push(@{$self->{sky}},$tok);
866
            push(@{$self->{sky}},$tok);
867
            push(@{$self->{SKY}}, "Sky Clear");
867
            push(@{$self->{SKY}}, "Sky Clear");
868
            push(@{$self->{SKY_RUS}}, "Ясно");
868
            push(@{$self->{SKY_RUS}}, "Ясно");
869
            push(@{$self->{SKY_RAW}},$tok);
869
            push(@{$self->{SKY_RAW}},$tok);
870
            push(@{$self->{weather}},$tok);
870
            push(@{$self->{weather}},$tok);
871
            push(@{$self->{WEATHER}},"No significant weather");
871
            push(@{$self->{WEATHER}},"No significant weather");
872
                        $self->{visibility} = '9999';
872
                        $self->{visibility} = '9999';
873
                        $parsestate = $expect_temperature;
873
                        $parsestate = $expect_temperature;
874
                        next;
874
                        next;
875
                }
875
                }
876
876
877
        ##
877
        ##
878
        ## is it sky conditions (clear)?
878
        ## is it sky conditions (clear)?
879
        ##
879
        ##
880
880
881
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /SKC|CLR/ ))
881
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /SKC|CLR/ ))
882
        {
882
        {
883
            push(@{$self->{sky}},$tok);
883
            push(@{$self->{sky}},$tok);
884
            push(@{$self->{SKY}}, "Sky Clear");
884
            push(@{$self->{SKY}}, "Sky Clear");
885
            push(@{$self->{SKY_RUS}}, "Ясно");
885
            push(@{$self->{SKY_RUS}}, "Ясно");
886
            push(@{$self->{SKY_RAW}},$tok);
886
            push(@{$self->{SKY_RAW}},$tok);
887
            print "[$tok] is a sky condition.\n" if $self->{debug};
887
            print "[$tok] is a sky condition.\n" if $self->{debug};
888
                        $parsestate = $expect_clouds;
888
                        $parsestate = $expect_clouds;
889
                        next;
889
                        next;
890
        }
890
        }
891
891
892
        ##
892
        ##
893
        ## is it sky conditions (clouds)?
893
        ## is it sky conditions (clouds)?
894
        ##
894
        ##
895
                ## sky conditions can end with ///
895
                ## sky conditions can end with ///
896
896
897
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(FEW|SCT|BKN|OVC)(\d\d\d)?(CB|TCU)?\/*$/i))
897
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(FEW|SCT|BKN|OVC)(\d\d\d)?(CB|TCU)?\/*$/i))
898
        {
898
        {
899
            push(@{$self->{sky}},$tok);
899
            push(@{$self->{sky}},$tok);
900
            my $engl = "";
900
            my $engl = "";
901
            my $rusl = "";
901
            my $rusl = "";
902
            my $rawl = "";
902
            my $rawl = "";
903
903
904
            $engl = $_sky_types{$1};
904
            $engl = $_sky_types{$1};
905
            $rusl = $_sky_types_ru{$1};
905
            $rusl = $_sky_types_ru{$1};
906
            $rawl = $1;
906
            $rawl = $1;
907
907
908
            if (defined $3)
908
            if (defined $3)
909
            {
909
            {
910
                if ($3 eq "TCU")
910
                if ($3 eq "TCU")
911
                {
911
                {
912
                    $engl .= " Towering Cumulus";
912
                    $engl .= " Towering Cumulus";
913
                    $rusl .= ", кучевые облака";                    
913
                    $rusl .= ", кучевые облака";                    
914
                }
914
                }
915
                elsif ($3 eq "CB")
915
                elsif ($3 eq "CB")
916
                {
916
                {
917
                    $engl .= " Cumulonimbus";
917
                    $engl .= " Cumulonimbus";
918
                    $rusl .= ", кучево-дождевые облака";
918
                    $rusl .= ", кучево-дождевые облака";
919
                }
919
                }
920
                $rawl = $3;
920
                $rawl = $3;
921
            }
921
            }
922
922
923
            if ($2 ne "")
923
            if ($2 ne "")
924
            {
924
            {
925
                my $agl = int($2)*100;
925
                my $agl = int($2)*100;
926
                $engl .= " at $agl" . "ft";
926
                $engl .= " at $agl" . "ft";
927
                $rusl .= " на высоте " . $agl*0.3048 . " м";
927
                $rusl .= " на высоте " . $agl*0.3048 . " м";
928
            }
928
            }
929
929
930
            push(@{$self->{SKY}}, $engl);
930
            push(@{$self->{SKY}}, $engl);
931
            push(@{$self->{SKY_RUS}}, $rusl);
931
            push(@{$self->{SKY_RUS}}, $rusl);
932
            push(@{$self->{SKY_RAW}}, $rawl);
932
            push(@{$self->{SKY_RAW}}, $rawl);
933
            print "[$tok] is a sky condition.\n" if $self->{debug};
933
            print "[$tok] is a sky condition.\n" if $self->{debug};
934
                        $parsestate = $expect_clouds;
934
                        $parsestate = $expect_clouds;
935
                        # clouds DO repeat. a lot ;)
935
                        # clouds DO repeat. a lot ;)
936
            next;
936
            next;
937
        }
937
        }
938
938
939
                ##
939
                ##
940
                ## auto detected cloud conditions
940
                ## auto detected cloud conditions
941
                ##
941
                ##
942
942
943
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(NSC|NCD)$/ )){
943
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(NSC|NCD)$/ )){
944
            my $engl = "";
944
            my $engl = "";
945
            my $rusl = "";
945
            my $rusl = "";
946
946
947
            $engl = $_sky_types{$tok};
947
            $engl = $_sky_types{$tok};
948
            $rusl = $_sky_types_ru{$tok};
948
            $rusl = $_sky_types_ru{$tok};
949
            push(@{$self->{SKY}}, $engl);
949
            push(@{$self->{SKY}}, $engl);
950
            push(@{$self->{SKY_RUS}}, $rusl);
950
            push(@{$self->{SKY_RUS}}, $rusl);
951
            push(@{$self->{SKY_RAW}}, $tok);
951
            push(@{$self->{SKY_RAW}}, $tok);
952
                        print "[$tok] is an automatic sky condition.\n" if $self->{debug};
952
                        print "[$tok] is an automatic sky condition.\n" if $self->{debug};
953
                        $parsestate = $expect_temperature;
953
                        $parsestate = $expect_temperature;
954
                        next;
954
                        next;
955
                }
955
                }
956
956
957
                ##
957
                ##
958
                ## Vertical visibility
958
                ## Vertical visibility
959
                ##
959
                ##
960
960
961
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^VV\d+$/ )){
961
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^VV\d+$/ )){
962
                        print "[$tok] is vertical visibility.\n" if $self->{debug};
962
                        print "[$tok] is vertical visibility.\n" if $self->{debug};
963
                        $parsestate = $expect_temperature;
963
                        $parsestate = $expect_temperature;
964
                        next;
964
                        next;
965
                }
965
                }
966
966
967
        ##
967
        ##
968
        ## is it temperature and dew point info?
968
        ## is it temperature and dew point info?
969
        ##
969
        ##
970
970
971
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_pressure) and ($tok =~ /^(M?\d\d)\/(M?\d{0,2})/i))
971
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_pressure) and ($tok =~ /^(M?\d\d)\/(M?\d{0,2})/i))
972
        {
972
        {
973
            next if $self->{temp_dew};
973
            next if $self->{temp_dew};
974
            $self->{temp_dew} = $tok;
974
            $self->{temp_dew} = $tok;
975
975
976
            $self->{TEMP_C} = $1;
976
            $self->{TEMP_C} = $1;
977
            $self->{DEW_C} = $2;
977
            $self->{DEW_C} = $2;
978
            $self->{TEMP_C} =~ s/^M/-/;
978
            $self->{TEMP_C} =~ s/^M/-/;
979
            $self->{DEW_C} =~ s/^M/-/;
979
            $self->{DEW_C} =~ s/^M/-/;
980
980
981
            print "[$tok] is temperature/dew point information.\n" if $self->{debug};
981
            print "[$tok] is temperature/dew point information.\n" if $self->{debug};
982
                        $parsestate = $expect_pressure;
982
                        $parsestate = $expect_pressure;
983
            next;
983
            next;
984
        }
984
        }
985
985
986
        ##
986
        ##
987
        ## is it an altimeter setting? (in.Hg)
987
        ## is it an altimeter setting? (in.Hg)
988
        ##
988
        ##
989
989
990
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^A(\d\d)(\d\d)$/i))
990
        elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^A(\d\d)(\d\d)$/i))
991
        {
991
        {
992
            $self->{alt} = $tok;
992
            $self->{alt} = $tok;
993
            $self->{ALT} = "$1.$2"+0;
993
            $self->{ALT} = "$1.$2"+0;
994
            $self->{ALT_HP} = "$1.$2" * 33.863886;
994
            $self->{ALT_HP} = "$1.$2" * 33.863886;
995
995
996
            print "[$tok] is an altimeter setting.\n" if $self->{debug};
996
            print "[$tok] is an altimeter setting.\n" if $self->{debug};
997
                        $parsestate = $expect_recentweather;
997
                        $parsestate = $expect_recentweather;
998
            next;
998
            next;
999
        }
999
        }
1000
1000
1001
                ##
1001
                ##
1002
                ## is it a pressure? (hPa)
1002
                ## is it a pressure? (hPa)
1003
                ##
1003
                ##
1004
1004
1005
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^Q(\d\d\d\d)$/i))
1005
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^Q(\d\d\d\d)$/i))
1006
                {
1006
                {
1007
                        $self->{pressure} = $1;
1007
                        $self->{pressure} = $1;
1008
            $self->{ALT_HP} = $1;
1008
            $self->{ALT_HP} = $1;
1009
                        $self->{ALT} = 0.029529983 * $self->{pressure};
1009
                        $self->{ALT} = 0.029529983 * $self->{pressure};
1010
                        print "[$tok] is an air pressure.\n" if $self->{debug};
1010
                        print "[$tok] is an air pressure.\n" if $self->{debug};
1011
                        $parsestate = $expect_recentweather;
1011
                        $parsestate = $expect_recentweather;
1012
                        next;
1012
                        next;
1013
                }
1013
                }
1014
1014
1015
                ##
1015
                ##
1016
                ## recent weather?
1016
                ## recent weather?
1017
                ##
1017
                ##
1018
1018
1019
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^RE($_weather_types_pat)$/)){
1019
                elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^RE($_weather_types_pat)$/)){
1020
                        print "[$tok] is recent significant weather.\n" if $self->{debug};
1020
                        print "[$tok] is recent significant weather.\n" if $self->{debug};
1021
                        $parsestate = $expect_remarks;
1021
                        $parsestate = $expect_remarks;
1022
                        next;
1022
                        next;
1023
                }
1023
                }
1024
1024
1025
                ##
1025
                ##
1026
                ## euro type trend?
1026
                ## euro type trend?
1027
                ##
1027
                ##
1028
1028
1029
                elsif (($parsestate >= $expect_modifier) and ($tok =~ /^$_trend_types_pat/)){
1029
                elsif (($parsestate >= $expect_modifier) and ($tok =~ /^$_trend_types_pat/)){
1030
                        print "[$tok] is a trend.\n" if $self->{debug};
1030
                        print "[$tok] is a trend.\n" if $self->{debug};
1031
                        $parsestate = $expect_remarks;
1031
                        $parsestate = $expect_remarks;
1032
                        next;
1032
                        next;
1033
                }
1033
                }
1034
1034
1035
        ##
1035
        ##
1036
        ## us type remarks? .. can happen quite early in the process already
1036
        ## us type remarks? .. can happen quite early in the process already
1037
        ##
1037
        ##
1038
1038
1039
        elsif (($parsestate >= $expect_modifier) and ($tok =~ /^RMK$/i))
1039
        elsif (($parsestate >= $expect_modifier) and ($tok =~ /^RMK$/i))
1040
        {
1040
        {
1041
            push(@{$self->{remarks}},$tok);
1041
            push(@{$self->{remarks}},$tok);
1042
            print "[$tok] is a (US type) remark.\n" if $self->{debug};
1042
            print "[$tok] is a (US type) remark.\n" if $self->{debug};
1043
                        $parsestate  = $expect_usremarks;
1043
                        $parsestate  = $expect_usremarks;
1044
            next;
1044
            next;
1045
        }
1045
        }
1046
1046
1047
        ##
1047
        ##
1048
        ## automatic station type?
1048
        ## automatic station type?
1049
        ##
1049
        ##
1050
1050
1051
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^A(O\d)$/i))
1051
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^A(O\d)$/i))
1052
        {
1052
        {
1053
            $self->{autostationtype} = $tok;
1053
            $self->{autostationtype} = $tok;
1054
            $self->{AUTO_STATIONTYPE} = $1;
1054
            $self->{AUTO_STATIONTYPE} = $1;
1055
            print "[$tok] is an automatic station type remark.\n" if $self->{debug};
1055
            print "[$tok] is an automatic station type remark.\n" if $self->{debug};
1056
            next;
1056
            next;
1057
        }
1057
        }
1058
1058
1059
        ##
1059
        ##
1060
        ## sea level pressure
1060
        ## sea level pressure
1061
        ##
1061
        ##
1062
1062
1063
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^SLP(\d+)/i))
1063
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^SLP(\d+)/i))
1064
        {
1064
        {
1065
            $self->{slp} = $tok;
1065
            $self->{slp} = $tok;
1066
            $self->{SLP} = "$1 mb";
1066
            $self->{SLP} = "$1 mb";
1067
            print "[$tok] is a sea level pressure.\n" if $self->{debug};
1067
            print "[$tok] is a sea level pressure.\n" if $self->{debug};
1068
            next;
1068
            next;
1069
        }
1069
        }
1070
1070
1071
        ##
1071
        ##
1072
        ## sea level pressure not available
1072
        ## sea level pressure not available
1073
        ##
1073
        ##
1074
1074
1075
        elsif (($parsestate == $expect_usremarks) and ($tok eq "SLPNO"))
1075
        elsif (($parsestate == $expect_usremarks) and ($tok eq "SLPNO"))
1076
        {
1076
        {
1077
            $self->{slp} = "SLPNO";
1077
            $self->{slp} = "SLPNO";
1078
            $self->{SLP} = "not available";
1078
            $self->{SLP} = "not available";
1079
            print "[$tok] is a sea level pressure.\n" if $self->{debug};
1079
            print "[$tok] is a sea level pressure.\n" if $self->{debug};
1080
            next;
1080
            next;
1081
        }
1081
        }
1082
1082
1083
        ##
1083
        ##
1084
        ## hourly precipitation
1084
        ## hourly precipitation
1085
        ##
1085
        ##
1086
1086
1087
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^P(\d\d\d\d)$/i))
1087
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^P(\d\d\d\d)$/i))
1088
        {
1088
        {
1089
            $self->{hourlyprecip} = $tok;
1089
            $self->{hourlyprecip} = $tok;
1090
1090
1091
            if ( $1 eq "0000" ) {
1091
            if ( $1 eq "0000" ) {
1092
                $self->{HOURLY_PRECIP} = "Trace";
1092
                $self->{HOURLY_PRECIP} = "Trace";
1093
            } else {
1093
            } else {
1094
                $self->{HOURLY_PRECIP} = $1;
1094
                $self->{HOURLY_PRECIP} = $1;
1095
            }
1095
            }
1096
        }
1096
        }
1097
1097
1098
        ##
1098
        ##
1099
        ## weather begin/end times
1099
        ## weather begin/end times
1100
        ##
1100
        ##
1101
1101
1102
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^($_weather_types_pat)([BE\d]+)$/i))
1102
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^($_weather_types_pat)([BE\d]+)$/i))
1103
        {
1103
        {
1104
            my $engl = "";
1104
            my $engl = "";
1105
            my $times = $2;
1105
            my $times = $2;
1106
1106
1107
            $self->{weatherlog} = $tok;
1107
            $self->{weatherlog} = $tok;
1108
1108
1109
            $engl = $_weather_types{$1};
1109
            $engl = $_weather_types{$1};
1110
1110
1111
            while ( $times =~ /(B|E)(\d\d)/g )
1111
            while ( $times =~ /(B|E)(\d\d)/g )
1112
            {
1112
            {
1113
                if ( $1 eq "B" ) {
1113
                if ( $1 eq "B" ) {
1114
                    $engl .= " began :$2";
1114
                    $engl .= " began :$2";
1115
                } else {
1115
                } else {
1116
                    $engl .= " ended :$2";
1116
                    $engl .= " ended :$2";
1117
                }
1117
                }
1118
            }
1118
            }
1119
1119
1120
            push(@{$self->{WEATHER_LOG}}, $engl);
1120
            push(@{$self->{WEATHER_LOG}}, $engl);
1121
            print "[$tok] is a weather log.\n" if $self->{debug};
1121
            print "[$tok] is a weather log.\n" if $self->{debug};
1122
            next;
1122
            next;
1123
        }
1123
        }
1124
1124
1125
        ##
1125
        ##
1126
        ## remarks for significant cloud types
1126
        ## remarks for significant cloud types
1127
        ##
1127
        ##
1128
1128
1129
        elsif (($parsestate >= $expect_recentweather) and ($tok eq "CB" || $tok eq "TCU"))
1129
        elsif (($parsestate >= $expect_recentweather) and ($tok eq "CB" || $tok eq "TCU"))
1130
        {
1130
        {
1131
            push(@{$self->{sigclouds}}, $tok);
1131
            push(@{$self->{sigclouds}}, $tok);
1132
1132
1133
            if ( $tok eq "CB" ) {
1133
            if ( $tok eq "CB" ) {
1134
                push(@{$self->{SIGCLOUDS}}, "Cumulonimbus");
1134
                push(@{$self->{SIGCLOUDS}}, "Cumulonimbus");
1135
            } elsif ( $tok eq "TCU" ) {
1135
            } elsif ( $tok eq "TCU" ) {
1136
                push(@{$self->{SIGCLOUDS}}, "Towering Cumulus");
1136
                push(@{$self->{SIGCLOUDS}}, "Towering Cumulus");
1137
            }
1137
            }
1138
                        $parsestate = $expect_usremarks;
1138
                        $parsestate = $expect_usremarks;
1139
        }
1139
        }
1140
1140
1141
        ##
1141
        ##
1142
        ## hourly temp/dewpoint
1142
        ## hourly temp/dewpoint
1143
        ##
1143
        ##
1144
1144
1145
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^T(\d)(\d\d)(\d)(\d)(\d\d)(\d)$/i))
1145
        elsif (($parsestate == $expect_usremarks) and ($tok =~ /^T(\d)(\d\d)(\d)(\d)(\d\d)(\d)$/i))
1146
        {
1146
        {
1147
            $self->{hourlytempdew} = $tok;
1147
            $self->{hourlytempdew} = $tok;
1148
            if ( $1 == 1 ) {
1148
            if ( $1 == 1 ) {
1149
                $self->{HOURLY_TEMP_C} = "-";
1149
                $self->{HOURLY_TEMP_C} = "-";
1150
            }
1150
            }
1151
            $self->{HOURLY_TEMP_C} .= "$2.$3";
1151
            $self->{HOURLY_TEMP_C} .= "$2.$3";
1152
1152
1153
            $self->{HOURLY_DEW_C} = "";
1153
            $self->{HOURLY_DEW_C} = "";
1154
            if ( $4 == 1 ) {
1154
            if ( $4 == 1 ) {
1155
                $self->{HOURLY_DEW_C} = "-";
1155
                $self->{HOURLY_DEW_C} = "-";
1156
            }
1156
            }
1157
            $self->{HOURLY_DEW_C} .= "$5.$6";
1157
            $self->{HOURLY_DEW_C} .= "$5.$6";
1158
1158
1159
            print "[$tok] is a hourly temp and dewpoint.\n" if $self->{debug};
1159
            print "[$tok] is a hourly temp and dewpoint.\n" if $self->{debug};
1160
            next;
1160
            next;
1161
        }
1161
        }
1162
1162
1163
        ##
1163
        ##
1164
        ## unknown, not in remarks yet
1164
        ## unknown, not in remarks yet
1165
        ##
1165
        ##
1166
1166
1167
        elsif ($parsestate < $expect_remarks)
1167
        elsif ($parsestate < $expect_remarks)
1168
        {
1168
        {
1169
            push(@{$self->{unknown}},$tok);
1169
            push(@{$self->{unknown}},$tok);
1170
            push(@{$self->{UNKNOWN}},$tok);
1170
            push(@{$self->{UNKNOWN}},$tok);
1171
            print "[$tok] is unexpected at this state.\n" if $self->{debug};
1171
            print "[$tok] is unexpected at this state.\n" if $self->{debug};
1172
            next;
1172
            next;
1173
        }
1173
        }
1174
1174
1175
        ##
1175
        ##
1176
        ## unknown. assume remarks
1176
        ## unknown. assume remarks
1177
        ##
1177
        ##
1178
1178
1179
        else
1179
        else
1180
        {
1180
        {
1181
            push(@{$self->{remarks}},$tok);
1181
            push(@{$self->{remarks}},$tok);
1182
            push(@{$self->{REMARKS}},$tok);
1182
            push(@{$self->{REMARKS}},$tok);
1183
            print "[$tok] is unknown remark.\n" if $self->{debug};
1183
            print "[$tok] is unknown remark.\n" if $self->{debug};
1184
            next;
1184
            next;
1185
        }
1185
        }
1186
1186
1187
    }
1187
    }
1188
1188
1189
    ##
1189
    ##
1190
    ## Now that the internal stuff is set, let's do the external
1190
    ## Now that the internal stuff is set, let's do the external
1191
    ## stuff.
1191
    ## stuff.
1192
    ##
1192
    ##
1193
1193
1194
    $self->{SITE} = $self->{site};
1194
    $self->{SITE} = $self->{site};
1195
    $self->{DATE} = substr($self->{date_time},0,2);
1195
    $self->{DATE} = substr($self->{date_time},0,2);
1196
    $self->{TIME} = substr($self->{date_time},2,4) . " UTC";
1196
    $self->{TIME} = substr($self->{date_time},2,4) . " UTC";
1197
    $self->{TIME} =~ s/(\d\d)(\d\d)/$1:$2/o;
1197
    $self->{TIME} =~ s/(\d\d)(\d\d)/$1:$2/o;
1198
    $self->{MOD}  = $self->{modifier};
1198
    $self->{MOD}  = $self->{modifier};
1199
1199
1200
    ##
1200
    ##
1201
    ## Okay, wind finally gets interesting.
1201
    ## Okay, wind finally gets interesting.
1202
    ##
1202
    ##
1203
1203
1204
    if ( defined $self->{wind} )
1204
    if ( defined $self->{wind} )
1205
        {
1205
        {
1206
        my $wind = $self->{wind};
1206
        my $wind = $self->{wind};
1207
        my $dir_deg  = substr($wind,0,3);
1207
        my $dir_deg  = substr($wind,0,3);
1208
        my $wind_speed;
1208
        my $wind_speed;
1209
        my $dir_eng = "";
1209
        my $dir_eng = "";
1210
        my $dir_rus = "";
1210
        my $dir_rus = "";
1211
                my $dir_abb = "";
1211
                my $dir_abb = "";
1212
1212
1213
        $wind_speed = $1 if($wind =~ /...(\d{2,3})/o);
1213
        $wind_speed = $1 if($wind =~ /...(\d{2,3})/o);
1214
        # Check for wind direction
1214
        # Check for wind direction
1215
        if ($dir_deg =~ /VRB/i) {
1215
        if ($dir_deg =~ /VRB/i) {
1216
            $dir_deg = $dir_eng = "Variable";
1216
            $dir_deg = $dir_eng = "Variable";
1217
            $dir_rus = "переменный";
1217
            $dir_rus = "переменный";
1218
        } else {
1218
        } else {
1219
            if ($wind_speed == 0 and $dir_deg == 0) {
1219
            if ($wind_speed == 0 and $dir_deg == 0) {
1220
                # Calm wind (00000KT in METAR)
1220
                # Calm wind (00000KT in METAR)
1221
                $dir_eng = "Calm";
1221
                $dir_eng = "Calm";
1222
                $dir_rus = "штиль";
1222
                $dir_rus = "штиль";
1223
                print "wind is calm\n" if $self->{debug};
1223
                print "wind is calm\n" if $self->{debug};
1224
            } elsif ($dir_deg < 15) {
1224
            } elsif ($dir_deg < 15) {
1225
                $dir_eng = "North";
1225
                $dir_eng = "North";
1226
                                $dir_abb = "N";
1226
                                $dir_abb = "N";
1227
                $dir_rus = "северный";
1227
                $dir_rus = "северный";
1228
            } elsif ($dir_deg < 30) {
1228
            } elsif ($dir_deg < 30) {
1229
                $dir_eng = "North/Northeast";
1229
                $dir_eng = "North/Northeast";
1230
                                $dir_abb = "NNE";
1230
                                $dir_abb = "NNE";
1231
                $dir_rus = "северо-северо-восточный";
1231
                $dir_rus = "северо-северо-восточный";
1232
            } elsif ($dir_deg < 60) {
1232
            } elsif ($dir_deg < 60) {
1233
                $dir_eng = "Northeast";
1233
                $dir_eng = "Northeast";
1234
                                $dir_abb = "NE";
1234
                                $dir_abb = "NE";
1235
                $dir_rus = "северо-восточный";
1235
                $dir_rus = "северо-восточный";
1236
            } elsif ($dir_deg < 75) {
1236
            } elsif ($dir_deg < 75) {
1237
                $dir_eng = "East/Northeast";
1237
                $dir_eng = "East/Northeast";
1238
                                $dir_abb = "ENE";
1238
                                $dir_abb = "ENE";
1239
                $dir_rus = "восточный-северо-восточный";
1239
                $dir_rus = "восточный-северо-восточный";
1240
            } elsif ($dir_deg < 105) {
1240
            } elsif ($dir_deg < 105) {
1241
                $dir_eng = "East";
1241
                $dir_eng = "East";
1242
                                $dir_abb = "E";
1242
                                $dir_abb = "E";
1243
                $dir_rus = "восточный";
1243
                $dir_rus = "восточный";
1244
            } elsif ($dir_deg < 120) {
1244
            } elsif ($dir_deg < 120) {
1245
                $dir_eng = "East/Southeast";
1245
                $dir_eng = "East/Southeast";
1246
                                $dir_abb = "ESE";
1246
                                $dir_abb = "ESE";
1247
                $dir_rus = "восточный-юго-восточный";
1247
                $dir_rus = "восточный-юго-восточный";
1248
            } elsif ($dir_deg < 150) {
1248
            } elsif ($dir_deg < 150) {
1249
                $dir_eng = "Southeast";
1249
                $dir_eng = "Southeast";
1250
                                $dir_abb = "SE";
1250
                                $dir_abb = "SE";
1251
                $dir_rus = "юго-восточный";
1251
                $dir_rus = "юго-восточный";
1252
            } elsif ($dir_deg < 165) {
1252
            } elsif ($dir_deg < 165) {
1253
                $dir_eng = "South/Southeast";
1253
                $dir_eng = "South/Southeast";
1254
                                $dir_abb = "SSE";
1254
                                $dir_abb = "SSE";
1255
                $dir_rus = "юго-юго-восточный";
1255
                $dir_rus = "юго-юго-восточный";
1256
            } elsif ($dir_deg < 195) {
1256
            } elsif ($dir_deg < 195) {
1257
                $dir_eng = "South";
1257
                $dir_eng = "South";
1258
                                $dir_abb = "S";
1258
                                $dir_abb = "S";
1259
                $dir_rus = "южный";
1259
                $dir_rus = "южный";
1260
            } elsif ($dir_deg < 210) {
1260
            } elsif ($dir_deg < 210) {
1261
                $dir_eng = "South/Southwest";
1261
                $dir_eng = "South/Southwest";
1262
                                $dir_abb = "SSW";
1262
                                $dir_abb = "SSW";
1263
                $dir_rus = "юго-юго-западный"
1263
                $dir_rus = "юго-юго-западный"
1264
            } elsif ($dir_deg < 240) {
1264
            } elsif ($dir_deg < 240) {
1265
                $dir_eng = "Southwest";
1265
                $dir_eng = "Southwest";
1266
                                $dir_abb = "SW";
1266
                                $dir_abb = "SW";
1267
                $dir_rus = "юго-западный";
1267
                $dir_rus = "юго-западный";
1268
            } elsif ($dir_deg < 265) {
1268
            } elsif ($dir_deg < 265) {
1269
                $dir_eng = "West/Southwest";
1269
                $dir_eng = "West/Southwest";
1270
                                $dir_abb = "WSW";
1270
                                $dir_abb = "WSW";
1271
                $dir_rus = "западно-юго-западный";
1271
                $dir_rus = "западно-юго-западный";
1272
            } elsif ($dir_deg < 285) {
1272
            } elsif ($dir_deg < 285) {
1273
                $dir_eng = "West";
1273
                $dir_eng = "West";
1274
                                $dir_abb = "W";
1274
                                $dir_abb = "W";
1275
                $dir_rus = "западный";
1275
                $dir_rus = "западный";
1276
            } elsif ($dir_deg < 300) {
1276
            } elsif ($dir_deg < 300) {
1277
                $dir_eng = "West/Northwest";
1277
                $dir_eng = "West/Northwest";
1278
                                $dir_abb = "WNW";
1278
                                $dir_abb = "WNW";
1279
                $dir_rus = "западно-северо-западный";
1279
                $dir_rus = "западно-северо-западный";
1280
            } elsif ($dir_deg < 330) {
1280
            } elsif ($dir_deg < 330) {
1281
                $dir_eng = "Northwest";
1281
                $dir_eng = "Northwest";
1282
                                $dir_abb = "NW";
1282
                                $dir_abb = "NW";
1283
                $dir_rus = "северо-западный";
1283
                $dir_rus = "северо-западный";
1284
            } elsif ($dir_deg < 345) {
1284
            } elsif ($dir_deg < 345) {
1285
                $dir_eng = "North/Northwest";
1285
                $dir_eng = "North/Northwest";
1286
                                $dir_abb = "NNW";
1286
                                $dir_abb = "NNW";
1287
                $dir_rus = "северо-северо-западный";
1287
                $dir_rus = "северо-северо-западный";
1288
            } elsif ($dir_deg < 360) {
1288
            } elsif ($dir_deg < 360) {
1289
                $dir_eng = "North";
1289
                $dir_eng = "North";
1290
                                $dir_abb = "N";
1290
                                $dir_abb = "N";
1291
                $dir_rus = "северный";
1291
                $dir_rus = "северный";
1292
            } else {
1292
            } else {
1293
                # Shouldn't happen, but if for some reason the METAR
1293
                # Shouldn't happen, but if for some reason the METAR
1294
                # information doesn't contain a reasonable direction...
1294
                # information doesn't contain a reasonable direction...
1295
                $dir_eng = "undeterminable";
1295
                $dir_eng = "undeterminable";
1296
                $dir_rus = "неопределенный";
1296
                $dir_rus = "неопределенный";
1297
            }
1297
            }
1298
        }
1298
        }
1299
1299
1300
                my $kts_speed = undef;
1300
                my $kts_speed = undef;
1301
                my $mph_speed = undef;
1301
                my $mph_speed = undef;
1302
                my $mps_speed = undef;
1302
                my $mps_speed = undef;
1303
1303
1304
                my $kts_gust = "";
1304
                my $kts_gust = "";
1305
                my $mph_gust = "";
1305
                my $mph_gust = "";
1306
                my $mps_gust = "";
1306
                my $mps_gust = "";
1307
1307
1308
                # parse knots
1308
                # parse knots
1309
1309
1310
                if ($self->{windtype} == $wt_knots){
1310
                if ($self->{windtype} == $wt_knots){
1311
                        $wind =~ /...(\d\d\d?)/o;
1311
                        $wind =~ /...(\d\d\d?)/o;
1312
                        $kts_speed = $1;
1312
                        $kts_speed = $1;
1313
                        $mph_speed = $kts_speed * 1.15077945;
1313
                        $mph_speed = $kts_speed * 1.15077945;
1314
                        $mps_speed = $kts_speed * 0.514444444;
1314
                        $mps_speed = $kts_speed * 0.514444444;
1315
1315
1316
                        if ($wind =~ /.{5,6}G(\d\d\d?)/o) {
1316
                        if ($wind =~ /.{5,6}G(\d\d\d?)/o) {
1317
                                $kts_gust = $1;
1317
                                $kts_gust = $1;
1318
                                $mph_gust = $kts_gust * 1.15077945;
1318
                                $mph_gust = $kts_gust * 1.15077945;
1319
                                $mps_gust = $kts_gust * 0.514444444;
1319
                                $mps_gust = $kts_gust * 0.514444444;
1320
                        }
1320
                        }
1321
                # else: parse meters/second
1321
                # else: parse meters/second
1322
                } elsif ($self->{windtype} == $wt_mps){
1322
                } elsif ($self->{windtype} == $wt_mps){
1323
                        $wind=~ /...(\d\d\d?)/o;
1323
                        $wind=~ /...(\d\d\d?)/o;
1324
                        $mps_speed = $1;
1324
                        $mps_speed = $1;
1325
                        $kts_speed = $mps_speed * 1.9438445; # units
1325
                        $kts_speed = $mps_speed * 1.9438445; # units
1326
                        $mph_speed = $mps_speed * 2.2369363;
1326
                        $mph_speed = $mps_speed * 2.2369363;
1327
                        if ($wind =~ /\d{5,6}G(\d\d\d?)/o) {
1327
                        if ($wind =~ /\d{5,6}G(\d\d\d?)/o) {
1328
                                $mps_gust = $1;
1328
                                $mps_gust = $1;
1329
                                $kts_gust = $mps_gust * 1.9438445;
1329
                                $kts_gust = $mps_gust * 1.9438445;
1330
                                $mph_gust = $mps_gust * 2.2369363;
1330
                                $mph_gust = $mps_gust * 2.2369363;
1331
                        }
1331
                        }
1332
                } else {
1332
                } else {
1333
                        warn "Mod::Geo::METAR Parser error: unknown windtype\n";
1333
                        warn "Geo::ModMETAR Parser error: unknown windtype\n";
1334
                }
1334
                }
1335
1335
1336
        $self->{WIND_KTS} = $kts_speed;
1336
        $self->{WIND_KTS} = $kts_speed;
1337
        $self->{WIND_MPH} = $mph_speed;
1337
        $self->{WIND_MPH} = $mph_speed;
1338
        $self->{WIND_MS}  = $mps_speed;
1338
        $self->{WIND_MS}  = $mps_speed;
1339
1339
1340
        $self->{WIND_GUST_KTS} = $kts_gust;
1340
        $self->{WIND_GUST_KTS} = $kts_gust;
1341
        $self->{WIND_GUST_MPH} = $mph_gust;
1341
        $self->{WIND_GUST_MPH} = $mph_gust;
1342
        $self->{WIND_GUST_MS}  = $mps_gust;
1342
        $self->{WIND_GUST_MS}  = $mps_gust;
1343
1343
1344
        $self->{WIND_DIR_DEG} = $dir_deg;
1344
        $self->{WIND_DIR_DEG} = $dir_deg;
1345
        $self->{WIND_DIR_ENG} = $dir_eng;
1345
        $self->{WIND_DIR_ENG} = $dir_eng;
1346
        $self->{WIND_DIR_ABB} = $dir_abb;
1346
        $self->{WIND_DIR_ABB} = $dir_abb;
1347
        $self->{WIND_DIR_RUS} = $dir_rus;
1347
        $self->{WIND_DIR_RUS} = $dir_rus;
1348
1348
1349
    }
1349
    }
1350
1350
1351
        ##
1351
        ##
1352
        ## wind variation
1352
        ## wind variation
1353
        ##
1353
        ##
1354
1354
1355
        if (defined $self->{windvar})
1355
        if (defined $self->{windvar})
1356
        {
1356
        {
1357
                if ($self->{windvar} =~ /^(\d\d\d)V(\d\d\d)$/){
1357
                if ($self->{windvar} =~ /^(\d\d\d)V(\d\d\d)$/){
1358
                        $self->{WIND_VAR} = "Varying between $1 and $2";
1358
                        $self->{WIND_VAR} = "Varying between $1 and $2";
1359
            $self->{WIND_VAR_1} = $1;
1359
            $self->{WIND_VAR_1} = $1;
1360
            $self->{WIND_VAR_2} = $2;
1360
            $self->{WIND_VAR_2} = $2;
1361
            my @direction = (
1361
            my @direction = (
1362
                15 => "North",
1362
                15 => "North",
1363
                30 => "North/Northeast",
1363
                30 => "North/Northeast",
1364
                60 => "Northeast",
1364
                60 => "Northeast",
1365
                75 => "East/Northeast",
1365
                75 => "East/Northeast",
1366
                105 => "East",
1366
                105 => "East",
1367
                120 => "East/Southeast",
1367
                120 => "East/Southeast",
1368
                150 => "Southeast",
1368
                150 => "Southeast",
1369
                165 => "South/Southeast",
1369
                165 => "South/Southeast",
1370
                195 => "South",
1370
                195 => "South",
1371
                210 => "South/Southwest",
1371
                210 => "South/Southwest",
1372
                240 => "Southwest",
1372
                240 => "Southwest",
1373
                265 => "West/Southwest",
1373
                265 => "West/Southwest",
1374
                285 => "West",
1374
                285 => "West",
1375
                300 => "West/Northwest",
1375
                300 => "West/Northwest",
1376
                330 => "Northwest",
1376
                330 => "Northwest",
1377
                345 => "North/Northwest",
1377
                345 => "North/Northwest",
1378
                360 => "North",
1378
                360 => "North",
1379
                1000 => "undeterminable");
1379
                1000 => "undeterminable");
1380
            for(my $x = 0; $x < $#direction; $x += 2) {
1380
            for(my $x = 0; $x < $#direction; $x += 2) {
1381
                if($self->{WIND_VAR_1} < $direction[$x]) {
1381
                if($self->{WIND_VAR_1} < $direction[$x]) {
1382
                    $self->{WIND_VAR_ENG_1} = $direction[$x+1];
1382
                    $self->{WIND_VAR_ENG_1} = $direction[$x+1];
1383
                    last;
1383
                    last;
1384
                }
1384
                }
1385
            }
1385
            }
1386
            for(my $x = 0; $x < $#direction; $x += 2) {
1386
            for(my $x = 0; $x < $#direction; $x += 2) {
1387
                if($self->{WIND_VAR_2} < $direction[$x]) {
1387
                if($self->{WIND_VAR_2} < $direction[$x]) {
1388
                    $self->{WIND_VAR_ENG_2} = $direction[$x+1];
1388
                    $self->{WIND_VAR_ENG_2} = $direction[$x+1];
1389
                    last;
1389
                    last;
1390
                }
1390
                }
1391
            }
1391
            }
1392
                }
1392
                }
1393
        }
1393
        }
1394
1394
1395
    ##   
1395
    ##   
1396
    ## Calculate relative humidity
1396
    ## Calculate relative humidity
1397
    ##
1397
    ##
1398
1398
1399
    {
1399
    {
1400
        my $esat  = 6.11*(10**((7.5*$self->{TEMP_C})/(237.7+$self->{TEMP_C})));
1400
        my $esat  = 6.11*(10**((7.5*$self->{TEMP_C})/(237.7+$self->{TEMP_C})));
1401
        my $esurf = 6.11*(10**((7.5*$self->{DEW_C})/(237.7+$self->{DEW_C})));
1401
        my $esurf = 6.11*(10**((7.5*$self->{DEW_C})/(237.7+$self->{DEW_C})));
1402
1402
1403
        $self->{RH} = 100.0 * ($esurf/$esat);
1403
        $self->{RH} = 100.0 * ($esurf/$esat);
1404
    }
1404
    }
1405
   
1405
   
1406
    ##
1406
    ##
1407
    ## Calculate windchill temperature
1407
    ## Calculate windchill temperature
1408
    ##
1408
    ##
1409
   
1409
   
1410
    {
1410
    {
1411
        my $windspeed = $self->{WIND_MS}*3.6;
1411
        my $windspeed = $self->{WIND_MS}*3.6;
1412
        $self->{TEMP_WC} = 13.12 + 0.6215*$self->{TEMP_C} - 11.37*($windspeed**0.16) + 0.3965*$self->{TEMP_C}*($windspeed**0.16);
1412
        $self->{TEMP_WC} = 13.12 + 0.6215*$self->{TEMP_C} - 11.37*($windspeed**0.16) + 0.3965*$self->{TEMP_C}*($windspeed**0.16);
1413
    }
1413
    }
1414
1414
1415
    ##
1415
    ##
1416
    ## Visibility.
1416
    ## Visibility.
1417
    ##
1417
    ##
1418
1418
1419
    if($self->{visibility}) {
1419
    if($self->{visibility}) {
1420
        my $vis = $self->{visibility};
1420
        my $vis = $self->{visibility};
1421
                # test for statute miles
1421
                # test for statute miles
1422
                if ($vis =~ /SM$/){
1422
                if ($vis =~ /SM$/){
1423
                        $vis =~ s/SM$//oi;                              # nuke the "SM"
1423
                        $vis =~ s/SM$//oi;                              # nuke the "SM"
1424
                        if ($vis =~ /M(\d\/\d)/o) {
1424
                        if ($vis =~ /M(\d\/\d)/o) {
1425
                                $self->{VISIBILITY} = "Less than $1 statute miles";
1425
                                $self->{VISIBILITY} = "Less than $1 statute miles";
1426
                                $self->{VISIBILITY_RUS} = "Менее чем $1 статутных миль";
1426
                                $self->{VISIBILITY_RUS} = "Менее чем $1 статутных миль";
1427
                        } else {
1427
                        } else {
1428
                                $self->{VISIBILITY} = $vis . " statute miles";
1428
                                $self->{VISIBILITY} = $vis . " statute miles";
1429
                                $self->{VISIBILITY} = $vis . " статутных миль";
1429
                                $self->{VISIBILITY} = $vis . " статутных миль";
1430
                        } # end if
1430
                        } # end if
1431
                # auto metars can have non-directional visibility reports
1431
                # auto metars can have non-directional visibility reports
1432
                } elsif (($self->{MOD} eq 'AUTO') and ($vis =~ /(\d+)NDV$/)){
1432
                } elsif (($self->{MOD} eq 'AUTO') and ($vis =~ /(\d+)NDV$/)){
1433
                        $self->{VISIBILITY} = "$1 meters non-directional visibility";
1433
                        $self->{VISIBILITY} = "$1 meters non-directional visibility";
1434
                        $self->{VISIBILITY_RUS} = "$1 м непрямой видимости";
1434
                        $self->{VISIBILITY_RUS} = "$1 м непрямой видимости";
1435
                } else {
1435
                } else {
1436
                        $self->{VISIBILITY} = $vis . " meters";
1436
                        $self->{VISIBILITY} = $vis . " meters";
-
 
1437
            if ($vis<1000) {
1437
                        $self->{VISIBILITY_RUS} = $vis . " м";
1438
                            $self->{VISIBILITY_RUS} = $vis . " м";
-
 
1439
            } else {
-
 
1440
                $vis = $vis/1000;
-
 
1441
                if (abs($vis-int($vis))>=0.5) {
-
 
1442
                    $vis = int($vis)+1;
-
 
1443
                } else {
-
 
1444
                    $vis = int($vis);
-
 
1445
                }
-
 
1446
                $self->{VISIBILITY_RUS} = $vis . " км";
-
 
1447
            }
1438
                }
1448
                }
1439
    }
1449
    }
1440
1450
1441
    ##
1451
    ##
1442
    ## Calculate F temps for all C temps
1452
    ## Calculate F temps for all C temps
1443
    ##
1453
    ##
1444
1454
1445
    foreach my $key ( keys(%$self) )
1455
    foreach my $key ( keys(%$self) )
1446
    {
1456
    {
1447
        if ( uc($key) eq $key && $key =~ /^(.*)_C$/ )
1457
        if ( uc($key) eq $key && $key =~ /^(.*)_C$/ )
1448
        {
1458
        {
1449
            my $fkey = $1 . "_F";
1459
            my $fkey = $1 . "_F";
1450
1460
1451
            next unless defined $self->{$key} && $self->{$key};
1461
            next unless defined $self->{$key} && $self->{$key};
1452
1462
1453
            $self->{$fkey} = sprintf("%.1f", (($self->{$key} * (9/5)) + 32));
1463
            $self->{$fkey} = sprintf("%.1f", (($self->{$key} * (9/5)) + 32));
1454
        }
1464
        }
1455
    }
1465
    }
1456
1466
1457
        # join the runway group
1467
        # join the runway group
1458
       
1468
       
1459
        $self->{runway} = join(', ' , @{$self->{RUNWAY}});
1469
        $self->{runway} = join(', ' , @{$self->{RUNWAY}});
1460
       
1470
       
1461
}
1471
}
1462
1472
1463
##
1473
##
1464
## Print the tokens--usually when debugging.
1474
## Print the tokens--usually when debugging.
1465
##
1475
##
1466
1476
1467
sub print_tokens
1477
sub print_tokens
1468
{
1478
{
1469
    my $self = shift;
1479
    my $self = shift;
1470
    my $tok;
1480
    my $tok;
1471
    foreach $tok (@{$self->{tokens}}) {
1481
    foreach $tok (@{$self->{tokens}}) {
1472
        print "> $tok\n";
1482
        print "> $tok\n";
1473
    }
1483
    }
1474
}
1484
}
1475
1485
1476
##
1486
##
1477
## Turn debugging on/off.
1487
## Turn debugging on/off.
1478
##
1488
##
1479
1489
1480
sub debug
1490
sub debug
1481
{
1491
{
1482
    my $self = shift;
1492
    my $self = shift;
1483
    my $flag = shift;
1493
    my $flag = shift;
1484
    return $self->{debug} unless defined $flag;
1494
    return $self->{debug} unless defined $flag;
1485
1495
1486
    if (($flag eq "Y") or ($flag eq "y") or ($flag == 1)) {
1496
    if (($flag eq "Y") or ($flag eq "y") or ($flag == 1)) {
1487
        $self->{debug} = 1;
1497
        $self->{debug} = 1;
1488
    } elsif (($flag eq "N") or ($flag eq "n") or ($flag == 0)) {
1498
    } elsif (($flag eq "N") or ($flag eq "n") or ($flag == 0)) {
1489
        $self->{debug} = 0;
1499
        $self->{debug} = 0;
1490
    }
1500
    }
1491
1501
1492
    return $self->{debug};
1502
    return $self->{debug};
1493
}
1503
}
1494
1504
1495
##
1505
##
1496
## Dump internal data structure. Useful for debugging and such.
1506
## Dump internal data structure. Useful for debugging and such.
1497
##
1507
##
1498
1508
1499
sub dump
1509
sub dump
1500
{
1510
{
1501
    my $self = shift;
1511
    my $self = shift;
1502
1512
1503
    print "Modified METAR dump follows.\n\n";
1513
    print "Modified METAR dump follows.\n\n";
1504
1514
1505
    print "type: $self->{type}\n";
1515
    print "type: $self->{type}\n";
1506
    print "site: $self->{site}\n";
1516
    print "site: $self->{site}\n";
1507
    print "date_time: $self->{date_time}\n";
1517
    print "date_time: $self->{date_time}\n";
1508
    print "modifier: $self->{modifier}\n";
1518
    print "modifier: $self->{modifier}\n";
1509
    print "wind: $self->{wind}\n";
1519
    print "wind: $self->{wind}\n";
1510
    print "variable wind: $self->{vrbwind}\n";
1520
    print "variable wind: $self->{vrbwind}\n";
1511
    print "visibility: $self->{visibility}\n";
1521
    print "visibility: $self->{visibility}\n";
1512
    print "runway: $self->{runway}\n";
1522
    print "runway: $self->{runway}\n";
1513
    print "weather: " . join(', ', @{$self->{weather}}) . "\n";
1523
    print "weather: " . join(', ', @{$self->{weather}}) . "\n";
1514
    print "sky: " . join(', ', @{$self->{sky}}) . "\n";
1524
    print "sky: " . join(', ', @{$self->{sky}}) . "\n";
1515
    print "temp_dew: $self->{temp_dew}\n";
1525
    print "temp_dew: $self->{temp_dew}\n";
1516
    print "alt: $self->{alt}\n";
1526
    print "alt: $self->{alt}\n";
1517
    print "pressure: $self->{pressure}\n";
1527
    print "pressure: $self->{pressure}\n";
1518
    print "slp: $self->{slp}\n";
1528
    print "slp: $self->{slp}\n";
1519
    print "remarks: " . join (', ', @{$self->{remarks}}) . "\n";
1529
    print "remarks: " . join (', ', @{$self->{remarks}}) . "\n";
1520
    print "\n";
1530
    print "\n";
1521
1531
1522
    foreach my $var ( sort(keys(%$self)) )
1532
    foreach my $var ( sort(keys(%$self)) )
1523
    {
1533
    {
1524
        next if ( uc($var) ne $var );
1534
        next if ( uc($var) ne $var );
1525
1535
1526
        if ( ref($self->{$var}) eq "ARRAY" )
1536
        if ( ref($self->{$var}) eq "ARRAY" )
1527
        {
1537
        {
1528
            print "$var: ", join(", ", @{$self->{$var}}), "\n";
1538
            print "$var: ", join(", ", @{$self->{$var}}), "\n";
1529
        }
1539
        }
1530
        else
1540
        else
1531
        {
1541
        {
1532
            print "$var: ", $self->{$var}, "\n";
1542
            print "$var: ", $self->{$var}, "\n";
1533
        }
1543
        }
1534
    }
1544
    }
1535
}
1545
}
1536
1546
1537
1;
1547
1;
1538
1548
1539
__END__
1549
__END__
1540
1550
1541
=head1 NAME
1551
=head1 NAME
1542

1552

1543
Mod::Geo::METAR - Process aviation weather reports in the METAR format.
1553
Mod::Geo::METAR - Process aviation weather reports in the METAR format.
1544

1554

1545
=head1 SYNOPSIS
1555
=head1 SYNOPSIS
1546

1556

1547
  use Mod::Geo::METAR;
1557
  use Mod::Geo::METAR;
1548
  use strict;
1558
  use strict;
1549

1559

1550
  my $m = new Mod::Geo::METAR;
1560
  my $m = new Mod::Geo::METAR;
1551
  $m->metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");
1561
  $m->metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");
1552
  print $m->dump;
1562
  print $m->dump;
1553

1563

1554
  exit;
1564
  exit;
1555

1565

1556
=head1 DESCRIPTION
1566
=head1 DESCRIPTION
1557

1567

1558
METAR reports are available on-line, thanks to the National Weather Service.
1568
METAR reports are available on-line, thanks to the National Weather Service.
1559
Since reading the METAR format isn't easy for non-pilots, these reports are
1569
Since reading the METAR format isn't easy for non-pilots, these reports are
1560
relatively useles to the common man who just wants a quick glace at the
1570
relatively useles to the common man who just wants a quick glace at the
1561
weather. This module tries to parse the METAR reports so the data can be
1571
weather. This module tries to parse the METAR reports so the data can be
1562
used to create readable weather reports and/or process the data in
1572
used to create readable weather reports and/or process the data in
1563
applications.
1573
applications.
1564

1574

1565
=head1 USAGE
1575
=head1 USAGE
1566

1576

1567
=head2 How you might use this
1577
=head2 How you might use this
1568

1578

1569
Here is how you I<might> use the Geo::METAR module.
1579
Here is how you I<might> use the Geo::METAR module.
1570

1580

1571
One use that I have had for this module is to query the NWS METAR page
1581
One use that I have had for this module is to query the NWS METAR page
1572
(using the LWP modules) at:
1582
(using the LWP modules) at:
1573

1583

1574
I<http://weather.noaa.gov/cgi-bin/mgetmetar.pl?cccc=EHSB>
1584
I<http://weather.noaa.gov/cgi-bin/mgetmetar.pl?cccc=EHSB>
1575

1585

1576
to get an
1586
to get an
1577
up-to-date METAR. Then, I scan thru the output, looking for what looks
1587
up-to-date METAR. Then, I scan thru the output, looking for what looks
1578
like a METAR string (that's not hard in Perl). Oh, EHSB can be any site
1588
like a METAR string (that's not hard in Perl). Oh, EHSB can be any site
1579
location code where there is a reporting station.
1589
location code where there is a reporting station.
1580

1590

1581
I then pass the METAR into this module and get the info I want. I can
1591
I then pass the METAR into this module and get the info I want. I can
1582
then update my webcam page with the current temperature, sky conditions, or
1592
then update my webcam page with the current temperature, sky conditions, or
1583
whatnot. See for yourself at http://webcam.idefix.net/
1593
whatnot. See for yourself at http://webcam.idefix.net/
1584

1594

1585
See the BUGS section for a remark about multiple passes with the same
1595
See the BUGS section for a remark about multiple passes with the same
1586
Geo::METAR object.
1596
Geo::METAR object.
1587

1597

1588
=head2 Functions
1598
=head2 Functions
1589

1599

1590
The following functions are defined in the METAR module. Most of
1600
The following functions are defined in the METAR module. Most of
1591
them are I<public>, meaning that you're supposed to use
1601
them are I<public>, meaning that you're supposed to use
1592
them. Some are I<private>, meaning that you're not supposed to use
1602
them. Some are I<private>, meaning that you're not supposed to use
1593
them -- but I won't stop you. Assume that functions are I<public>
1603
them -- but I won't stop you. Assume that functions are I<public>
1594
unless otherwise documented.
1604
unless otherwise documented.
1595

1605

1596
=over
1606
=over
1597

1607

1598
=item metar()
1608
=item metar()
1599

1609

1600
metar() is the function to whwich you should pass a METAR string.  It
1610
metar() is the function to whwich you should pass a METAR string.  It
1601
will take care of decomposing it into its component parts converting
1611
will take care of decomposing it into its component parts converting
1602
the units and so on.
1612
the units and so on.
1603

1613

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

1615

1606
=item debug()
1616
=item debug()
1607

1617

1608
debug() toggles debugging messages. By default, debugging is turned
1618
debug() toggles debugging messages. By default, debugging is turned
1609
B<off>. Turn it on if you are developing METAR or having trouble with
1619
B<off>. Turn it on if you are developing METAR or having trouble with
1610
it.
1620
it.
1611

1621

1612
debug() understands all of the folloing:
1622
debug() understands all of the folloing:
1613

1623

1614
        Enable       Disable
1624
        Enable       Disable
1615
        ------       -------
1625
        ------       -------
1616
          1             0
1626
          1             0
1617
        'yes'         'no'
1627
        'yes'         'no'
1618
        'on'          'off'
1628
        'on'          'off'
1619

1629

1620
If you contact me for help, I'll likely ask you for some debugging
1630
If you contact me for help, I'll likely ask you for some debugging
1621
output.
1631
output.
1622

1632

1623
Example: C<$m-E<gt>debug(1);>
1633
Example: C<$m-E<gt>debug(1);>
1624

1634

1625
=item dump()
1635
=item dump()
1626

1636

1627
dump() will dump the internal data structure for the METAR in a
1637
dump() will dump the internal data structure for the METAR in a
1628
semi-human readable format.
1638
semi-human readable format.
1629

1639

1630
Example: C<$m-E<gt>dump;>
1640
Example: C<$m-E<gt>dump;>
1631

1641

1632
=item version()
1642
=item version()
1633

1643

1634
version() will print out the current version.
1644
version() will print out the current version.
1635

1645

1636
Example: C<print $m-E<gt>version;>
1646
Example: C<print $m-E<gt>version;>
1637

1647

1638
=item _tokenize()
1648
=item _tokenize()
1639

1649

1640
B<PRIVATE>
1650
B<PRIVATE>
1641

1651

1642
Called internally to break the METAR into its component tokens.
1652
Called internally to break the METAR into its component tokens.
1643

1653

1644
=item _process()
1654
=item _process()
1645

1655

1646
B<PRIVATE>
1656
B<PRIVATE>
1647

1657

1648
Used to make sense of the tokens found in B<_tokenize()>.
1658
Used to make sense of the tokens found in B<_tokenize()>.
1649

1659

1650
=back
1660
=back
1651

1661

1652
=head2 Variables
1662
=head2 Variables
1653

1663

1654
After you've called B<metar()>, you'd probably like to get at
1664
After you've called B<metar()>, you'd probably like to get at
1655
the individual values for things like temperature, dew point,
1665
the individual values for things like temperature, dew point,
1656
and so on. You do that by accessing individual variables via
1666
and so on. You do that by accessing individual variables via
1657
the METAR object.
1667
the METAR object.
1658

1668

1659
This section lists those variables and what they represent.
1669
This section lists those variables and what they represent.
1660

1670

1661
If you call B<dump()>, you'll find that it spits all of these
1671
If you call B<dump()>, you'll find that it spits all of these
1662
out.
1672
out.
1663

1673

1664
=over
1674
=over
1665

1675

1666
=item VERSION
1676
=item VERSION
1667

1677

1668
The version of METAR.pm that you're using.
1678
The version of METAR.pm that you're using.
1669

1679

1670
=item METAR
1680
=item METAR
1671

1681

1672
The actual, raw METAR.
1682
The actual, raw METAR.
1673

1683

1674
=item TYPE
1684
=item TYPE
1675

1685

1676
Report type in English ("Routine Weather Report" or "Special Weather Report")
1686
Report type in English ("Routine Weather Report" or "Special Weather Report")
1677

1687

1678
=item SITE
1688
=item SITE
1679

1689

1680
4-letter site code.
1690
4-letter site code.
1681

1691

1682
=item DATE
1692
=item DATE
1683

1693

1684
The date (just the day of the month) on which the report was issued.
1694
The date (just the day of the month) on which the report was issued.
1685

1695

1686
=item TIME
1696
=item TIME
1687

1697

1688
The time at which the report was issued.
1698
The time at which the report was issued.
1689

1699

1690
=item MOD
1700
=item MOD
1691

1701

1692
Modifier (AUTO/COR) if any.
1702
Modifier (AUTO/COR) if any.
1693

1703

1694
=item WIND_DIR_ENG
1704
=item WIND_DIR_ENG
1695

1705

1696
The current wind direction in English (Southwest, East, North, etc.)
1706
The current wind direction in English (Southwest, East, North, etc.)
1697

1707

1698
=item WIND_DIR_RUS
1708
=item WIND_DIR_RUS
1699

1709

1700
The current wind direction in Russian
1710
The current wind direction in Russian
1701

1711

1702
=item WIND_DIR_ABB
1712
=item WIND_DIR_ABB
1703

1713

1704
The current wind direction in abbreviated English (S, E, N, etc.)
1714
The current wind direction in abbreviated English (S, E, N, etc.)
1705

1715

1706
=item WIND_DIR_DEG
1716
=item WIND_DIR_DEG
1707

1717

1708
The current wind direction in degrees.
1718
The current wind direction in degrees.
1709

1719

1710
=item WIND_KTS
1720
=item WIND_KTS
1711

1721

1712
The current wind speed in Knots.
1722
The current wind speed in Knots.
1713

1723

1714
=item WIND_MPH
1724
=item WIND_MPH
1715

1725

1716
The current wind speed in Miles Per Hour.
1726
The current wind speed in Miles Per Hour.
1717

1727

1718
=item WIND_MS
1728
=item WIND_MS
1719

1729

1720
The current wind speed in Metres Per Second.
1730
The current wind speed in Metres Per Second.
1721

1731

1722
=item WIND_GUST_KTS
1732
=item WIND_GUST_KTS
1723

1733

1724
The current wind gusting speed in Knots.
1734
The current wind gusting speed in Knots.
1725

1735

1726
=item WIND_GUST_MPH
1736
=item WIND_GUST_MPH
1727

1737

1728
The current wind gusting speed in Miles Per Hour.
1738
The current wind gusting speed in Miles Per Hour.
1729

1739

1730
=item WIND_GUST_MS
1740
=item WIND_GUST_MS
1731

1741

1732
The current wind gusting speed in Metres Per Second.
1742
The current wind gusting speed in Metres Per Second.
1733

1743

1734
=item WIND_VAR
1744
=item WIND_VAR
1735

1745

1736
The wind variation in English
1746
The wind variation in English
1737

1747

1738
=item WIND_VAR_1
1748
=item WIND_VAR_1
1739

1749

1740
The first wind variation direction
1750
The first wind variation direction
1741

1751

1742
=item WIND_VAR_ENG_1
1752
=item WIND_VAR_ENG_1
1743

1753

1744
The first wind variation direction in English
1754
The first wind variation direction in English
1745

1755

1746
=item WIND_VAR_2
1756
=item WIND_VAR_2
1747

1757

1748
The second wind variation direction
1758
The second wind variation direction
1749

1759

1750
=item WIND_VAR_ENG_2
1760
=item WIND_VAR_ENG_2
1751

1761

1752
The second wind variation direction in English
1762
The second wind variation direction in English
1753

1763

1754
=item VISIBILITY
1764
=item VISIBILITY
1755

1765

1756
Visibility information.
1766
Visibility information.
1757

1767

1758
=item VISIBILITY_RUS
1768
=item VISIBILITY_RUS
1759

1769

1760
Visibility information in Russian.
1770
Visibility information in Russian.
1761

1771

1762
=item WIND
1772
=item WIND
1763

1773

1764
Wind information.
1774
Wind information.
1765

1775

1766
=item RUNWAY
1776
=item RUNWAY
1767

1777

1768
Runway information.
1778
Runway information.
1769

1779

1770
=item WEATHER
1780
=item WEATHER
1771

1781

1772
Current weather (array)
1782
Current weather (array)
1773

1783

1774
==item WEATHER_RUS
1784
==item WEATHER_RUS
1775

1785

1776
Current weather in Russian (array)
1786
Current weather in Russian (array)
1777

1787

1778
==item WEATHER_RAW
1788
==item WEATHER_RAW
1779

1789

1780
Current weather in RAW-data (array)
1790
Current weather in RAW-data (array)
1781

1791

1782
=item WEATHER_LOG
1792
=item WEATHER_LOG
1783

1793

1784
Current weather log (array)
1794
Current weather log (array)
1785

1795

1786
=item SKY
1796
=item SKY
1787

1797

1788
Current cloud cover (array)
1798
Current cloud cover (array)
1789

1799

1790
=item SKY_RUS
1800
=item SKY_RUS
1791

1801

1792
Current cloud cover in Russian (array)
1802
Current cloud cover in Russian (array)
1793

1803

1794
=item SKY_RAW
1804
=item SKY_RAW
1795

1805

1796
Current cloud cover in RAW-data (array)
1806
Current cloud cover in RAW-data (array)
1797

1807

1798
=item TEMP_C
1808
=item TEMP_C
1799

1809

1800
Temperature in Celsius.
1810
Temperature in Celsius.
1801

1811

1802
=item TEMP_F
1812
=item TEMP_F
1803

1813

1804
Temperature in Fahrenheit.
1814
Temperature in Fahrenheit.
1805

1815

1806
=item TEMP_WC
1816
=item TEMP_WC
1807

1817

1808
Windchill Temperature in Celsius.
1818
Windchill Temperature in Celsius.
1809

1819

1810
=item DEW_C
1820
=item DEW_C
1811

1821

1812
Dew point in Celsius.
1822
Dew point in Celsius.
1813

1823

1814
=item DEW_F
1824
=item DEW_F
1815

1825

1816
Dew point in Fahrenheit.
1826
Dew point in Fahrenheit.
1817

1827

1818
=item HOURLY_TEMP_F
1828
=item HOURLY_TEMP_F
1819

1829

1820
Hourly current temperature, fahrenheit
1830
Hourly current temperature, fahrenheit
1821

1831

1822
=item HOURLY_TEMP_C
1832
=item HOURLY_TEMP_C
1823

1833

1824
Hourly current temperature, celcius
1834
Hourly current temperature, celcius
1825

1835

1826
=item HOURLY_DEW_F
1836
=item HOURLY_DEW_F
1827

1837

1828
Hourly dewpoint, fahrenheit
1838
Hourly dewpoint, fahrenheit
1829

1839

1830
=item HOURLY_DEW_C
1840
=item HOURLY_DEW_C
1831

1841

1832
Hourly dewpoint, celcius
1842
Hourly dewpoint, celcius
1833

1843

1834
=item ALT
1844
=item ALT
1835

1845

1836
Altimeter setting (barometric pressure).
1846
Altimeter setting (barometric pressure).
1837

1847

1838
=item ALT_HP
1848
=item ALT_HP
1839

1849

1840
Altimeter setting in hectopascals.
1850
Altimeter setting in hectopascals.
1841

1851

1842
=item REMARKS
1852
=item REMARKS
1843

1853

1844
Any remarks in the report.
1854
Any remarks in the report.
1845

1855

1846
=back
1856
=back
1847

1857

1848
=head1 NOTES
1858
=head1 NOTES
1849

1859

1850
Test suite is small and incomplete. Needs work yet.
1860
Test suite is small and incomplete. Needs work yet.
1851

1861

1852
Older versions of this module were installed as "METAR" instaed of
1862
Older versions of this module were installed as "METAR" instaed of
1853
"Geo::METAR"
1863
"Geo::METAR"
1854

1864

1855
=head1 BUGS
1865
=head1 BUGS
1856

1866

1857
The Geo::METAR is only initialized once, which means you'll get left-over
1867
The Geo::METAR is only initialized once, which means you'll get left-over
1858
crud in variables when you call the metar() function twice.
1868
crud in variables when you call the metar() function twice.
1859

1869

1860
What is an invalid METAR in one country is a standard one in the next.
1870
What is an invalid METAR in one country is a standard one in the next.
1861
The standard is interpreted and used by meteorologists all over the world,
1871
The standard is interpreted and used by meteorologists all over the world,
1862
with local variations. This means there will always be METARs that will
1872
with local variations. This means there will always be METARs that will
1863
trip the parser.
1873
trip the parser.
1864

1874

1865
=head1 TODO
1875
=head1 TODO
1866

1876

1867
There is a TODO file included in the Geo::METAR distribution listing
1877
There is a TODO file included in the Geo::METAR distribution listing
1868
the outstanding tasks that I or others have devised. Please check that
1878
the outstanding tasks that I or others have devised. Please check that
1869
list before you submit a bug report or request a new feture. It might
1879
list before you submit a bug report or request a new feture. It might
1870
already be on the TODO list.
1880
already be on the TODO list.
1871

1881

1872
=head1 AUTHORS AND COPYRIGHT
1882
=head1 AUTHORS AND COPYRIGHT
1873

1883

1874
Copyright 1997-2000, Jeremy D. Zawodny <Jeremy [at] Zawodny.com>
1884
Copyright 1997-2000, Jeremy D. Zawodny <Jeremy [at] Zawodny.com>
1875

1885

1876
Copyright 2007, Koos van den Hout <koos@kzdoos.xs4all.nl>
1886
Copyright 2007, Koos van den Hout <koos@kzdoos.xs4all.nl>
1877

1887

1878
Copyright 2010, Alexander Wolf <alex.v.wolf@gmail.com>
1888
Copyright 2010, Alexander Wolf <alex.v.wolf@gmail.com>
1879

1889

1880
Geo::ModMETAR is covered under the GNU Public License (GPL) version 2 or
1890
Geo::ModMETAR is covered under the GNU Public License (GPL) version 2 or
1881
later.
1891
later.
1882

1892

1883
The Geo::ModMETAR Web site is located at:
1893
The Geo::ModMETAR Web site is located at:
1884

1894

1885
  http://astro.uni-altai.ru/~aw/perl/Geo-ModMETAR/
1895
  http://astro.uni-altai.ru/~aw/perl/Geo-ModMETAR/
1886

1896

1887
=head1 CREDITS
1897
=head1 CREDITS
1888

1898

1889
In addition to our work on Geo::METAR, We've received ideas, help, and
1899
In addition to our work on Geo::METAR, We've received ideas, help, and
1890
patches from the following folks:
1900
patches from the following folks:
1891

1901

1892
  * Ethan Dicks <ethan.dicks [at] gmail.com>
1902
  * Ethan Dicks <ethan.dicks [at] gmail.com>
1893

1903

1894
    Testing of Geo::METAR at the South Pole. Corrections and pointers
1904
    Testing of Geo::METAR at the South Pole. Corrections and pointers
1895
        to interesting cases to test.
1905
        to interesting cases to test.
1896

1906

1897
  * Otterboy <jong [at] watchguard.com>
1907
  * Otterboy <jong [at] watchguard.com>
1898

1908

1899
    Random script fixes and initial debugging help
1909
    Random script fixes and initial debugging help
1900

1910

1901
  * Remi Lefebvre <remi [at] solaria.dhis.org>
1911
  * Remi Lefebvre <remi [at] solaria.dhis.org>
1902

1912

1903
    Debian packaging as libgeo-metar-perl.deb.
1913
    Debian packaging as libgeo-metar-perl.deb.
1904

1914

1905
  * Mike Engelhart <mengelhart [at] earthtrip.com>
1915
  * Mike Engelhart <mengelhart [at] earthtrip.com>
1906

1916

1907
    Wind direction naming corrections.
1917
    Wind direction naming corrections.
1908

1918

1909
  * Michael Starling <mstarling [at] logic.bm>
1919
  * Michael Starling <mstarling [at] logic.bm>
1910

1920

1911
    Wind direction naming corrections.
1921
    Wind direction naming corrections.
1912

1922

1913
  * Hans Einar Nielssen <hans.einar [at] nielssen.com>
1923
  * Hans Einar Nielssen <hans.einar [at] nielssen.com>
1914

1924

1915
    Wind direction naming corrections.
1925
    Wind direction naming corrections.
1916

1926

1917
  * Nathan Neulinger <nneul [at] umr.edu>
1927
  * Nathan Neulinger <nneul [at] umr.edu>
1918

1928

1919
    Lots of enhancements and corrections. Too many to list here.
1929
    Lots of enhancements and corrections. Too many to list here.
1920

1930

1921
=head1 RELATED PROJECTS
1931
=head1 RELATED PROJECTS
1922

1932

1923
B<lcdproc> at http://www.lcdproc.org/ uses Geo::METAR in lcdmetar.pl to
1933
B<lcdproc> at http://www.lcdproc.org/ uses Geo::METAR in lcdmetar.pl to
1924
display weather data on an lcd.
1934
display weather data on an lcd.
1925

1935

1926
=cut
1936
=cut
1927
1937
1928
1938
1929
# vim:expandtab:sw=4 ts=4
1939
# vim:expandtab:sw=4 ts=4
1930
 
1940