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

Редакция

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

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

1583

1563
Mod::Geo::METAR - Process aviation weather reports in the METAR format.
1584
Geo::ModMETAR - Process aviation weather reports in the METAR format.
1564

1585

1565
=head1 SYNOPSIS
1586
=head1 SYNOPSIS
1566

1587

1567
  use Mod::Geo::METAR;
1588
  use Geo::ModMETAR;
1568
  use strict;
1589
  use strict;
1569

1590

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

1594

1574
  exit;
1595
  exit;
1575

1596

1576
=head1 DESCRIPTION
1597
=head1 DESCRIPTION
1577

1598

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

1605

1585
=head1 USAGE
1606
=head1 USAGE
1586

1607

1587
=head2 How you might use this
1608
=head2 How you might use this
1588

1609

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

1611

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

1614

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

1616

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

1621

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

1625

1605
See the BUGS section for a remark about multiple passes with the same
1626
See the BUGS section for a remark about multiple passes with the same
1606
Geo::METAR object.
1627
Geo::ModMETAR object.
1607

1628

1608
=head2 Functions
1629
=head2 Functions
1609

1630

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

1636

1616
=over
1637
=over
1617

1638

1618
=item metar()
1639
=item metar()
1619

1640

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

1644

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

1646

1626
=item debug()
1647
=item debug()
1627

1648

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

1652

1632
debug() understands all of the folloing:
1653
debug() understands all of the folloing:
1633

1654

1634
        Enable       Disable
1655
        Enable       Disable
1635
        ------       -------
1656
        ------       -------
1636
          1             0
1657
          1             0
1637
        'yes'         'no'
1658
        'yes'         'no'
1638
        'on'          'off'
1659
        'on'          'off'
1639

1660

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

1663

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

1665

1645
=item dump()
1666
=item dump()
1646

1667

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

1670

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

1672

1652
=item version()
1673
=item version()
1653

1674

1654
version() will print out the current version.
1675
version() will print out the current version.
1655

1676

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

1678

1658
=item _tokenize()
1679
=item _tokenize()
1659

1680

1660
B<PRIVATE>
1681
B<PRIVATE>
1661

1682

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

1684

1664
=item _process()
1685
=item _process()
1665

1686

1666
B<PRIVATE>
1687
B<PRIVATE>
1667

1688

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

1690

1670
=back
1691
=back
1671

1692

1672
=head2 Variables
1693
=head2 Variables
1673

1694

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

1699

1679
This section lists those variables and what they represent.
1700
This section lists those variables and what they represent.
1680

1701

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

1704

1684
=over
1705
=over
1685

1706

1686
=item VERSION
1707
=item VERSION
1687

1708

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

1710

1690
=item METAR
1711
=item METAR
1691

1712

1692
The actual, raw METAR.
1713
The actual, raw METAR.
1693

1714

1694
=item TYPE
1715
=item TYPE
1695

1716

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

1718

1698
=item SITE
1719
=item SITE
1699

1720

1700
4-letter site code.
1721
4-letter site code.
1701

1722

1702
=item DATE
1723
=item DATE
1703

1724

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

1726

1706
=item TIME
1727
=item TIME
1707

1728

1708
The time at which the report was issued.
1729
The time at which the report was issued.
1709

1730

1710
=item MOD
1731
=item MOD
1711

1732

1712
Modifier (AUTO/COR) if any.
1733
Modifier (AUTO/COR) if any.
1713

1734

1714
=item WIND_DIR_ENG
1735
=item WIND_DIR_ENG
1715

1736

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

1738

1718
=item WIND_DIR_RUS
1739
=item WIND_DIR_RUS
1719

1740

1720
The current wind direction in Russian
1741
The current wind direction in Russian
1721

1742

1722
=item WIND_DIR_ABB
1743
=item WIND_DIR_ABB
1723

1744

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

1746

1726
=item WIND_DIR_DEG
1747
=item WIND_DIR_DEG
1727

1748

1728
The current wind direction in degrees.
1749
The current wind direction in degrees.
1729

1750

1730
=item WIND_KTS
1751
=item WIND_KTS
1731

1752

1732
The current wind speed in Knots.
1753
The current wind speed in Knots.
1733

1754

1734
=item WIND_MPH
1755
=item WIND_MPH
1735

1756

1736
The current wind speed in Miles Per Hour.
1757
The current wind speed in Miles Per Hour.
1737

1758

1738
=item WIND_MS
1759
=item WIND_MS
1739

1760

1740
The current wind speed in Metres Per Second.
1761
The current wind speed in Metres Per Second.
1741

1762

1742
=item WIND_GUST_KTS
1763
=item WIND_GUST_KTS
1743

1764

1744
The current wind gusting speed in Knots.
1765
The current wind gusting speed in Knots.
1745

1766

1746
=item WIND_GUST_MPH
1767
=item WIND_GUST_MPH
1747

1768

1748
The current wind gusting speed in Miles Per Hour.
1769
The current wind gusting speed in Miles Per Hour.
1749

1770

1750
=item WIND_GUST_MS
1771
=item WIND_GUST_MS
1751

1772

1752
The current wind gusting speed in Metres Per Second.
1773
The current wind gusting speed in Metres Per Second.
1753

1774

1754
=item WIND_VAR
1775
=item WIND_VAR
1755

1776

1756
The wind variation in English
1777
The wind variation in English
1757

1778

1758
=item WIND_VAR_1
1779
=item WIND_VAR_1
1759

1780

1760
The first wind variation direction
1781
The first wind variation direction
1761

1782

1762
=item WIND_VAR_ENG_1
1783
=item WIND_VAR_ENG_1
1763

1784

1764
The first wind variation direction in English
1785
The first wind variation direction in English
1765

1786

1766
=item WIND_VAR_2
1787
=item WIND_VAR_2
1767

1788

1768
The second wind variation direction
1789
The second wind variation direction
1769

1790

1770
=item WIND_VAR_ENG_2
1791
=item WIND_VAR_ENG_2
1771

1792

1772
The second wind variation direction in English
1793
The second wind variation direction in English
1773

1794

1774
=item VISIBILITY
1795
=item VISIBILITY
1775

1796

1776
Visibility information.
1797
Visibility information.
1777

1798

1778
=item VISIBILITY_RUS
1799
=item VISIBILITY_RUS
1779

1800

1780
Visibility information in Russian.
1801
Visibility information in Russian.
1781

1802

1782
=item WIND
1803
=item WIND
1783

1804

1784
Wind information.
1805
Wind information.
1785

1806

1786
=item RUNWAY
1807
=item RUNWAY
1787

1808

1788
Runway information.
1809
Runway information.
1789

1810

1790
=item WEATHER
1811
=item WEATHER
1791

1812

1792
Current weather (array)
1813
Current weather (array)
1793

1814

1794
==item WEATHER_RUS
1815
==item WEATHER_RUS
1795

1816

1796
Current weather in Russian (array)
1817
Current weather in Russian (array)
1797

1818

1798
==item WEATHER_RAW
1819
==item WEATHER_RAW
1799

1820

1800
Current weather in RAW-data (array)
1821
Current weather in RAW-data (array)
1801

1822

1802
=item WEATHER_LOG
1823
=item WEATHER_LOG
1803

1824

1804
Current weather log (array)
1825
Current weather log (array)
1805

1826

1806
=item SKY
1827
=item SKY
1807

1828

1808
Current cloud cover (array)
1829
Current cloud cover (array)
1809

1830

1810
=item SKY_RUS
1831
=item SKY_RUS
1811

1832

1812
Current cloud cover in Russian (array)
1833
Current cloud cover in Russian (array)
1813

1834

1814
=item SKY_RAW
1835
=item SKY_RAW
1815

1836

1816
Current cloud cover in RAW-data (array)
1837
Current cloud cover in RAW-data (array)
1817

1838

1818
=item TEMP_C
1839
=item TEMP_C
1819

1840

1820
Temperature in Celsius.
1841
Temperature in Celsius.
1821

1842

1822
=item TEMP_F
1843
=item TEMP_F
1823

1844

1824
Temperature in Fahrenheit.
1845
Temperature in Fahrenheit.
1825

1846

1826
=item TEMP_WC
1847
=item TEMP_WC
1827

1848

1828
Windchill Temperature in Celsius.
1849
Windchill Temperature in Celsius.
1829

1850

1830
=item DEW_C
1851
=item DEW_C
1831

1852

1832
Dew point in Celsius.
1853
Dew point in Celsius.
1833

1854

1834
=item DEW_F
1855
=item DEW_F
1835

1856

1836
Dew point in Fahrenheit.
1857
Dew point in Fahrenheit.
1837

1858

1838
=item HOURLY_TEMP_F
1859
=item HOURLY_TEMP_F
1839

1860

1840
Hourly current temperature, fahrenheit
1861
Hourly current temperature, fahrenheit
1841

1862

1842
=item HOURLY_TEMP_C
1863
=item HOURLY_TEMP_C
1843

1864

1844
Hourly current temperature, celcius
1865
Hourly current temperature, celcius
1845

1866

1846
=item HOURLY_DEW_F
1867
=item HOURLY_DEW_F
1847

1868

1848
Hourly dewpoint, fahrenheit
1869
Hourly dewpoint, fahrenheit
1849

1870

1850
=item HOURLY_DEW_C
1871
=item HOURLY_DEW_C
1851

1872

1852
Hourly dewpoint, celcius
1873
Hourly dewpoint, celcius
1853

1874

1854
=item ALT
1875
=item ALT
1855

1876

1856
Altimeter setting (barometric pressure).
1877
Altimeter setting (barometric pressure).
1857

1878

1858
=item ALT_HP
1879
=item ALT_HP
1859

1880

1860
Altimeter setting in hectopascals.
1881
Altimeter setting in hectopascals.
1861

1882

-
 
1883
=item ALT_PL
-
 
1884

-
 
1885
QFE pressure in mmHg.
-
 
1886

1862
=item REMARKS
1887
=item REMARKS
1863

1888

1864
Any remarks in the report.
1889
Any remarks in the report.
1865

1890

1866
=back
1891
=back
1867

1892

1868
=head1 NOTES
1893
=head1 NOTES
1869

1894

1870
Test suite is small and incomplete. Needs work yet.
1895
Test suite is small and incomplete. Needs work yet.
1871

1896

1872
Older versions of this module were installed as "METAR" instaed of
1897
Older versions of this module were installed as "METAR" instaed of
1873
"Geo::METAR"
1898
"Geo::METAR"
1874

1899

1875
=head1 BUGS
1900
=head1 BUGS
1876

1901

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

1904

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

1909

1885
=head1 TODO
1910
=head1 TODO
1886

1911

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

1916

1892
=head1 AUTHORS AND COPYRIGHT
1917
=head1 AUTHORS AND COPYRIGHT
1893

1918

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

1920

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

1922

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

1924

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

1927

1903
The Geo::ModMETAR Web site is located at:
1928
The Geo::ModMETAR Web site is located at:
1904

1929

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

1931

1907
=head1 CREDITS
1932
=head1 CREDITS
1908

1933

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

1936

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

1938

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

1941

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

1943

1919
    Random script fixes and initial debugging help
1944
    Random script fixes and initial debugging help
1920

1945

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

1947

1923
    Debian packaging as libgeo-metar-perl.deb.
1948
    Debian packaging as libgeo-metar-perl.deb.
1924

1949

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

1951

1927
    Wind direction naming corrections.
1952
    Wind direction naming corrections.
1928

1953

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

1955

1931
    Wind direction naming corrections.
1956
    Wind direction naming corrections.
1932

1957

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

1959

1935
    Wind direction naming corrections.
1960
    Wind direction naming corrections.
1936

1961

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

1963

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

1965

1941
=head1 RELATED PROJECTS
1966
=head1 RELATED PROJECTS
1942

1967

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

1970

1946
=cut
1971
=cut
1947
1972
1948
1973
1949
# vim:expandtab:sw=4 ts=4
1974
# vim:expandtab:sw=4 ts=4
1950
 
1975