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

Редакция

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

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

1584

1585
Geo::ModMETAR - Process aviation weather reports in the METAR format.
1585
Geo::ModMETAR - Process aviation weather reports in the METAR format.
1586

1586

1587
=head1 SYNOPSIS
1587
=head1 SYNOPSIS
1588

1588

1589
  use Geo::ModMETAR;
1589
  use Geo::ModMETAR;
1590
  use strict;
1590
  use strict;
1591

1591

1592
  my $m = new Geo::ModMETAR;
1592
  my $m = new Geo::ModMETAR;
1593
  $m->metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");
1593
  $m->metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");
1594
  print $m->dump;
1594
  print $m->dump;
1595

1595

1596
  exit;
1596
  exit;
1597

1597

1598
=head1 DESCRIPTION
1598
=head1 DESCRIPTION
1599

1599

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

1606

1607
=head1 USAGE
1607
=head1 USAGE
1608

1608

1609
=head2 How you might use this
1609
=head2 How you might use this
1610

1610

1611
Here is how you I<might> use the Geo::ModMETAR module.
1611
Here is how you I<might> use the Geo::ModMETAR module.
1612

1612

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

1615

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

1617

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

1622

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

1626

1627
See the BUGS section for a remark about multiple passes with the same
1627
See the BUGS section for a remark about multiple passes with the same
1628
Geo::ModMETAR object.
1628
Geo::ModMETAR object.
1629

1629

1630
=head2 Functions
1630
=head2 Functions
1631

1631

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

1637

1638
=over
1638
=over
1639

1639

1640
=item metar()
1640
=item metar()
1641

1641

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

1645

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

1647

1648
=item debug()
1648
=item debug()
1649

1649

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

1653

1654
debug() understands all of the folloing:
1654
debug() understands all of the folloing:
1655

1655

1656
        Enable       Disable
1656
        Enable       Disable
1657
        ------       -------
1657
        ------       -------
1658
          1             0
1658
          1             0
1659
        'yes'         'no'
1659
        'yes'         'no'
1660
        'on'          'off'
1660
        'on'          'off'
1661

1661

1662
If you contact me for help, I'll likely ask you for some debugging
1662
If you contact me for help, I'll likely ask you for some debugging
1663
output.
1663
output.
1664

1664

1665
Example: C<$m-E<gt>debug(1);>
1665
Example: C<$m-E<gt>debug(1);>
1666

1666

1667
=item dump()
1667
=item dump()
1668

1668

1669
dump() will dump the internal data structure for the METAR in a
1669
dump() will dump the internal data structure for the METAR in a
1670
semi-human readable format.
1670
semi-human readable format.
1671

1671

1672
Example: C<$m-E<gt>dump;>
1672
Example: C<$m-E<gt>dump;>
1673

1673

1674
=item version()
1674
=item version()
1675

1675

1676
version() will print out the current version.
1676
version() will print out the current version.
1677

1677

1678
Example: C<print $m-E<gt>version;>
1678
Example: C<print $m-E<gt>version;>
1679

1679

1680
=item _tokenize()
1680
=item _tokenize()
1681

1681

1682
B<PRIVATE>
1682
B<PRIVATE>
1683

1683

1684
Called internally to break the METAR into its component tokens.
1684
Called internally to break the METAR into its component tokens.
1685

1685

1686
=item _process()
1686
=item _process()
1687

1687

1688
B<PRIVATE>
1688
B<PRIVATE>
1689

1689

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

1691

1692
=back
1692
=back
1693

1693

1694
=head2 Variables
1694
=head2 Variables
1695

1695

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

1700

1701
This section lists those variables and what they represent.
1701
This section lists those variables and what they represent.
1702

1702

1703
If you call B<dump()>, you'll find that it spits all of these
1703
If you call B<dump()>, you'll find that it spits all of these
1704
out.
1704
out.
1705

1705

1706
=over
1706
=over
1707

1707

1708
=item VERSION
1708
=item VERSION
1709

1709

1710
The version of METAR.pm that you're using.
1710
The version of METAR.pm that you're using.
1711

1711

1712
=item METAR
1712
=item METAR
1713

1713

1714
The actual, raw METAR.
1714
The actual, raw METAR.
1715

1715

1716
=item TYPE
1716
=item TYPE
1717

1717

1718
Report type in English ("Routine Weather Report" or "Special Weather Report")
1718
Report type in English ("Routine Weather Report" or "Special Weather Report")
1719

1719

1720
=item SITE
1720
=item SITE
1721

1721

1722
4-letter site code.
1722
4-letter site code.
1723

1723

1724
=item DATE
1724
=item DATE
1725

1725

1726
The date (just the day of the month) on which the report was issued.
1726
The date (just the day of the month) on which the report was issued.
1727

1727

1728
=item TIME
1728
=item TIME
1729

1729

1730
The time at which the report was issued.
1730
The time at which the report was issued.
1731

1731

1732
=item MOD
1732
=item MOD
1733

1733

1734
Modifier (AUTO/COR) if any.
1734
Modifier (AUTO/COR) if any.
1735

1735

1736
=item WIND_DIR_ENG
1736
=item WIND_DIR_ENG
1737

1737

1738
The current wind direction in English (Southwest, East, North, etc.)
1738
The current wind direction in English (Southwest, East, North, etc.)
1739

1739

1740
=item WIND_DIR_RUS
1740
=item WIND_DIR_RUS
1741

1741

1742
The current wind direction in Russian
1742
The current wind direction in Russian
1743

1743

1744
=item WIND_DIR_ABB
1744
=item WIND_DIR_ABB
1745

1745

1746
The current wind direction in abbreviated English (S, E, N, etc.)
1746
The current wind direction in abbreviated English (S, E, N, etc.)
1747

1747

1748
=item WIND_DIR_DEG
1748
=item WIND_DIR_DEG
1749

1749

1750
The current wind direction in degrees.
1750
The current wind direction in degrees.
1751

1751

1752
=item WIND_KTS
1752
=item WIND_KTS
1753

1753

1754
The current wind speed in Knots.
1754
The current wind speed in Knots.
1755

1755

1756
=item WIND_MPH
1756
=item WIND_MPH
1757

1757

1758
The current wind speed in Miles Per Hour.
1758
The current wind speed in Miles Per Hour.
1759

1759

1760
=item WIND_MS
1760
=item WIND_MS
1761

1761

1762
The current wind speed in Metres Per Second.
1762
The current wind speed in Metres Per Second.
1763

1763

1764
=item WIND_GUST_KTS
1764
=item WIND_GUST_KTS
1765

1765

1766
The current wind gusting speed in Knots.
1766
The current wind gusting speed in Knots.
1767

1767

1768
=item WIND_GUST_MPH
1768
=item WIND_GUST_MPH
1769

1769

1770
The current wind gusting speed in Miles Per Hour.
1770
The current wind gusting speed in Miles Per Hour.
1771

1771

1772
=item WIND_GUST_MS
1772
=item WIND_GUST_MS
1773

1773

1774
The current wind gusting speed in Metres Per Second.
1774
The current wind gusting speed in Metres Per Second.
1775

1775

1776
=item WIND_VAR
1776
=item WIND_VAR
1777

1777

1778
The wind variation in English
1778
The wind variation in English
1779

1779

1780
=item WIND_VAR_1
1780
=item WIND_VAR_1
1781

1781

1782
The first wind variation direction
1782
The first wind variation direction
1783

1783

1784
=item WIND_VAR_ENG_1
1784
=item WIND_VAR_ENG_1
1785

1785

1786
The first wind variation direction in English
1786
The first wind variation direction in English
1787

1787

1788
=item WIND_VAR_2
1788
=item WIND_VAR_2
1789

1789

1790
The second wind variation direction
1790
The second wind variation direction
1791

1791

1792
=item WIND_VAR_ENG_2
1792
=item WIND_VAR_ENG_2
1793

1793

1794
The second wind variation direction in English
1794
The second wind variation direction in English
1795

1795

1796
=item VISIBILITY
1796
=item VISIBILITY
1797

1797

1798
Visibility information.
1798
Visibility information.
1799

1799

1800
=item VISIBILITY_RUS
1800
=item VISIBILITY_RUS
1801

1801

1802
Visibility information in Russian.
1802
Visibility information in Russian.
1803

1803

1804
=item WIND
1804
=item WIND
1805

1805

1806
Wind information.
1806
Wind information.
1807

1807

1808
=item RUNWAY
1808
=item RUNWAY
1809

1809

1810
Runway information.
1810
Runway information.
1811

1811

1812
=item WEATHER
1812
=item WEATHER
1813

1813

1814
Current weather (array)
1814
Current weather (array)
1815

1815

1816
==item WEATHER_RUS
1816
==item WEATHER_RUS
1817

1817

1818
Current weather in Russian (array)
1818
Current weather in Russian (array)
1819

1819

1820
==item WEATHER_RAW
1820
==item WEATHER_RAW
1821

1821

1822
Current weather in RAW-data (array)
1822
Current weather in RAW-data (array)
1823

1823

1824
=item WEATHER_LOG
1824
=item WEATHER_LOG
1825

1825

1826
Current weather log (array)
1826
Current weather log (array)
1827

1827

1828
=item SKY
1828
=item SKY
1829

1829

1830
Current cloud cover (array)
1830
Current cloud cover (array)
1831

1831

1832
=item SKY_RUS
1832
=item SKY_RUS
1833

1833

1834
Current cloud cover in Russian (array)
1834
Current cloud cover in Russian (array)
1835

1835

1836
=item SKY_RAW
1836
=item SKY_RAW
1837

1837

1838
Current cloud cover in RAW-data (array)
1838
Current cloud cover in RAW-data (array)
1839

1839

1840
=item TEMP_C
1840
=item TEMP_C
1841

1841

1842
Temperature in Celsius.
1842
Temperature in Celsius.
1843

1843

1844
=item TEMP_F
1844
=item TEMP_F
1845

1845

1846
Temperature in Fahrenheit.
1846
Temperature in Fahrenheit.
1847

1847

1848
=item TEMP_WC
1848
=item TEMP_WC
1849

1849

1850
Windchill Temperature in Celsius.
1850
Windchill Temperature in Celsius.
1851

1851

1852
=item DEW_C
1852
=item DEW_C
1853

1853

1854
Dew point in Celsius.
1854
Dew point in Celsius.
1855

1855

1856
=item DEW_F
1856
=item DEW_F
1857

1857

1858
Dew point in Fahrenheit.
1858
Dew point in Fahrenheit.
1859

1859

1860
=item HOURLY_TEMP_F
1860
=item HOURLY_TEMP_F
1861

1861

1862
Hourly current temperature, fahrenheit
1862
Hourly current temperature, fahrenheit
1863

1863

1864
=item HOURLY_TEMP_C
1864
=item HOURLY_TEMP_C
1865

1865

1866
Hourly current temperature, celcius
1866
Hourly current temperature, celcius
1867

1867

1868
=item HOURLY_DEW_F
1868
=item HOURLY_DEW_F
1869

1869

1870
Hourly dewpoint, fahrenheit
1870
Hourly dewpoint, fahrenheit
1871

1871

1872
=item HOURLY_DEW_C
1872
=item HOURLY_DEW_C
1873

1873

1874
Hourly dewpoint, celcius
1874
Hourly dewpoint, celcius
1875

1875

1876
=item ALT
1876
=item ALT
1877

1877

1878
Altimeter setting (barometric pressure).
1878
Altimeter setting (barometric pressure).
1879

1879

1880
=item ALT_HP
1880
=item ALT_HP
1881

1881

1882
Altimeter setting in hectopascals.
1882
Altimeter setting in hectopascals.
1883

1883

1884
=item ALT_PL
1884
=item ALT_PL
1885

1885

1886
QFE pressure in mmHg.
1886
QFE pressure in mmHg.
1887

1887

1888
=item REMARKS
1888
=item REMARKS
1889

1889

1890
Any remarks in the report.
1890
Any remarks in the report.
1891

1891

1892
=back
1892
=back
1893

1893

1894
=head1 NOTES
1894
=head1 NOTES
1895

1895

1896
Test suite is small and incomplete. Needs work yet.
1896
Test suite is small and incomplete. Needs work yet.
1897

1897

1898
Older versions of original module were installed as "METAR" instaed of
1898
Older versions of original module were installed as "METAR" instaed of
1899
"Geo::METAR"
1899
"Geo::METAR"
1900

1900

1901
=head1 BUGS
1901
=head1 BUGS
1902

1902

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

1905

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

1910

1911
=head1 TODO
1911
=head1 TODO
1912

1912

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

1917

1918
=head1 AUTHORS AND COPYRIGHT
1918
=head1 AUTHORS AND COPYRIGHT
1919

1919

1920
Copyright 1997-2000, Jeremy D. Zawodny <Jeremy [at] Zawodny.com>
1920
Copyright 1997-2000, Jeremy D. Zawodny <Jeremy [at] Zawodny.com>
1921

1921

1922
Copyright 2007, Koos van den Hout <koos@kzdoos.xs4all.nl>
1922
Copyright 2007, Koos van den Hout <koos@kzdoos.xs4all.nl>
1923

1923

1924
Copyright 2010, Alexander Wolf <alex.v.wolf@gmail.com>
1924
Copyright 2010, Alexander Wolf <alex.v.wolf@gmail.com>
1925

1925

1926
Geo::ModMETAR is covered under the GNU Public License (GPL) version 2 or
1926
Geo::ModMETAR is covered under the GNU Public License (GPL) version 2 or
1927
later.
1927
later.
1928

1928

1929
The Geo::ModMETAR Web site is located at:
1929
The Geo::ModMETAR Web site is located at:
1930

1930

1931
  http://astro.uni-altai.ru/~aw/perl/Geo-ModMETAR/
1931
  http://astro.uni-altai.ru/~aw/perl/Geo-ModMETAR/
1932

1932

1933
=head1 CREDITS
1933
=head1 CREDITS
1934

1934

1935
In addition to our work on Geo::METAR, We've received ideas, help, and
1935
In addition to our work on Geo::METAR, We've received ideas, help, and
1936
patches from the following folks:
1936
patches from the following folks:
1937

1937

1938
  * Ethan Dicks <ethan.dicks [at] gmail.com>
1938
  * Ethan Dicks <ethan.dicks [at] gmail.com>
1939

1939

1940
    Testing of Geo::METAR at the South Pole. Corrections and pointers
1940
    Testing of Geo::METAR at the South Pole. Corrections and pointers
1941
to interesting cases to test.
1941
to interesting cases to test.
1942

1942

1943
  * Otterboy <jong [at] watchguard.com>
1943
  * Otterboy <jong [at] watchguard.com>
1944

1944

1945
    Random script fixes and initial debugging help
1945
    Random script fixes and initial debugging help
1946

1946

1947
  * Remi Lefebvre <remi [at] solaria.dhis.org>
1947
  * Remi Lefebvre <remi [at] solaria.dhis.org>
1948

1948

1949
    Debian packaging as libgeo-metar-perl.deb.
1949
    Debian packaging as libgeo-metar-perl.deb.
1950

1950

1951
  * Mike Engelhart <mengelhart [at] earthtrip.com>
1951
  * Mike Engelhart <mengelhart [at] earthtrip.com>
1952

1952

1953
    Wind direction naming corrections.
1953
    Wind direction naming corrections.
1954

1954

1955
  * Michael Starling <mstarling [at] logic.bm>
1955
  * Michael Starling <mstarling [at] logic.bm>
1956

1956

1957
    Wind direction naming corrections.
1957
    Wind direction naming corrections.
1958

1958

1959
  * Hans Einar Nielssen <hans.einar [at] nielssen.com>
1959
  * Hans Einar Nielssen <hans.einar [at] nielssen.com>
1960

1960

1961
    Wind direction naming corrections.
1961
    Wind direction naming corrections.
1962

1962

1963
  * Nathan Neulinger <nneul [at] umr.edu>
1963
  * Nathan Neulinger <nneul [at] umr.edu>
1964

1964

1965
    Lots of enhancements and corrections. Too many to list here.
1965
    Lots of enhancements and corrections. Too many to list here.
1966

1966

1967
=head1 RELATED PROJECTS
1967
=head1 RELATED PROJECTS
1968

1968

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

1971

1972
=cut
1972
=cut
1973
1973
1974
1974
1975
# vim:expandtab:sw=4 ts=4
1975
# vim:expandtab:sw=4 ts=4
1976
 
1976