Редакция 13 | Редакция 15 | К новейшей редакции | Только различия | Не учитывать пробелы | Содержимое файла | Авторство | Последнее изменение | Открыть журнал | RSS
Редакция 13 | Редакция 14 | ||
---|---|---|---|
1 | # AW: create fork Geo::METAR as Geo::ModMETAR
|
1 | # AW: create fork Geo::METAR as Geo::ModMETAR
|
2 | #
|
2 | #
|
3 | # KH: fix the parser
|
3 | # KH: fix the parser
|
4 | # should be a finite state machine
|
4 | # should be a finite state machine
|
5 | # - metar has rules what comes after what. but codes can be missing.
|
5 | # - metar has rules what comes after what. but codes can be missing.
|
6 | # (measurement not done) or //// (measurement broken at the moment)
|
6 | # (measurement not done) or //// (measurement broken at the moment)
|
7 | # so given a state counter, it can stay the same or go up one or more states,
|
7 | # so given a state counter, it can stay the same or go up one or more states,
|
8 | # but it can never go down
|
8 | # but it can never go down
|
9 | #
|
9 | #
|
10 | # info on the last bit which is actually a forecast: (German)
|
10 | # info on the last bit which is actually a forecast: (German)
|
11 | # http://www.wetterklima.de/flug/metar/Metarvorhersage.htm
|
11 | # http://www.wetterklima.de/flug/metar/Metarvorhersage.htm
|
12 | #
|
12 | #
|
13 | # more info here (dutch, and txt 707 is not standard metar)
|
13 | # more info here (dutch, and txt 707 is not standard metar)
|
14 | # http://www.vwkweb.nl/index.html?http://www.vwkweb.nl/weerinfo/weerinfo_teletekst707.html
|
14 | # http://www.vwkweb.nl/index.html?http://www.vwkweb.nl/weerinfo/weerinfo_teletekst707.html
|
15 | # and also (dutch)
|
15 | # and also (dutch)
|
16 | # http://www.gids.nl/weather/eheh/metari.html
|
16 | # http://www.gids.nl/weather/eheh/metari.html
|
17 | #
|
17 | #
|
18 | # 'METAR decoding in Europe'
|
18 | # 'METAR decoding in Europe'
|
19 | # http://users.hol.gr/~chatos/VATSIM/TM/metar.html
|
19 | # http://users.hol.gr/~chatos/VATSIM/TM/metar.html
|
20 | #
|
20 | #
|
21 | # english explanation
|
21 | # english explanation
|
22 | # http://booty.org.uk/booty.weather/metinfo/codes/METAR_decode.htm
|
22 | # http://booty.org.uk/booty.weather/metinfo/codes/METAR_decode.htm
|
23 | #
|
23 | #
|
24 | # canadian explanation
|
24 | # canadian explanation
|
25 | # http://meteocentre.com/doc/metar.html
|
25 | # http://meteocentre.com/doc/metar.html
|
26 | #
|
26 | #
|
27 | # 'METAR decoding, TAF decoding'
|
27 | # 'METAR decoding, TAF decoding'
|
28 | # http://stoivane.kapsi.fi/metar/
|
28 | # http://stoivane.kapsi.fi/metar/
|
29 | #
|
29 | #
|
30 | 30 | ||
31 | # This module is used for decoding NWS METAR code.
|
31 | # This module is used for decoding NWS METAR code.
|
32 | 32 | ||
33 | # Example METARs
|
33 | # Example METARs
|
34 | #
|
34 | #
|
35 | # Findlay, Ohio
|
35 | # Findlay, Ohio
|
36 | # KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK SLP201 57014
|
36 | # KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK SLP201 57014
|
37 | #
|
37 | #
|
38 | # Toledo, Ohio
|
38 | # Toledo, Ohio
|
39 | # KTOL 251451Z 23016G22KT 8SM CLR 04/00 A3006 RMK AO2 SLP185 T00440000 56016
|
39 | # KTOL 251451Z 23016G22KT 8SM CLR 04/00 A3006 RMK AO2 SLP185 T00440000 56016
|
40 | #
|
40 | #
|
41 | # Cleveland, Ohio
|
41 | # Cleveland, Ohio
|
42 | # KCLE 251554Z 20015KT 10SM FEW055 OVC070 03/M02 A3011 RMK AO2 SLP205 T00331017
|
42 | # KCLE 251554Z 20015KT 10SM FEW055 OVC070 03/M02 A3011 RMK AO2 SLP205 T00331017
|
43 | #
|
43 | #
|
44 | # Houston, Texas
|
44 | # Houston, Texas
|
45 | # KHST 251455Z 06017G22KT 7SM FEW040 BKN330 25/18 A3016 RMK SLP213 8/508
|
45 | # KHST 251455Z 06017G22KT 7SM FEW040 BKN330 25/18 A3016 RMK SLP213 8/508
|
46 | # 9/205 51007
|
46 | # 9/205 51007
|
47 | #
|
47 | #
|
48 | # LA
|
48 | # LA
|
49 | #
|
49 | #
|
50 | # KLAX 251450Z 07004KT 7SM SCT100 BKN200 14/11 A3005 RMK AO2 SLP173
|
50 | # KLAX 251450Z 07004KT 7SM SCT100 BKN200 14/11 A3005 RMK AO2 SLP173
|
51 | # T01390111 56005
|
51 | # T01390111 56005
|
52 | #
|
52 | #
|
53 | # Soesterberg
|
53 | # Soesterberg
|
54 | #
|
54 | #
|
55 | # EHSB 181325Z 24009KT 8000 -RA BR FEW011 SCT022 OVC030 07/06 Q1011 WHT WHT TEMPO GRN
|
55 | # EHSB 181325Z 24009KT 8000 -RA BR FEW011 SCT022 OVC030 07/06 Q1011 WHT WHT TEMPO GRN
|
56 | 56 | ||
57 | # For METAR info, please see
|
57 | # For METAR info, please see
|
58 | # http://tgsv5.nws.noaa.gov/oso/oso1/oso12/metar.htm
|
58 | # http://tgsv5.nws.noaa.gov/oso/oso1/oso12/metar.htm
|
59 | # moved
|
59 | # moved
|
60 | # http://metar.noaa.gov/
|
60 | # http://metar.noaa.gov/
|
61 | #
|
61 | #
|
62 | # in scary detail (metar coding)
|
62 | # in scary detail (metar coding)
|
63 | #
|
63 | #
|
64 | # http://metar.noaa.gov/table_master.jsp?sub_menu=yes&show=fmh1ch12.htm&dir=./handbook/&title=title_handbook
|
64 | # http://metar.noaa.gov/table_master.jsp?sub_menu=yes&show=fmh1ch12.htm&dir=./handbook/&title=title_handbook
|
65 | #
|
65 | #
|
66 | 66 | ||
67 | 67 | ||
68 | # The METAR specification is dictated in the Federal Meteorological Handbook
|
68 | # The METAR specification is dictated in the Federal Meteorological Handbook
|
69 | # which is available on-line at:
|
69 | # which is available on-line at:
|
70 | # http://tgsv5.nws.noaa.gov/oso/oso1/oso12/fmh1.htm
|
70 | # http://tgsv5.nws.noaa.gov/oso/oso1/oso12/fmh1.htm
|
71 | 71 | ||
72 | # General Structure is:
|
72 | # General Structure is:
|
73 | # TYPE, SITE, DATE/TIME, WIND, VISIBILITY, CLOUDS, TEMPERATURE, PRESSURE, REMARKS
|
73 | # TYPE, SITE, DATE/TIME, WIND, VISIBILITY, CLOUDS, TEMPERATURE, PRESSURE, REMARKS
|
74 | 74 | ||
75 | # Specifically:
|
75 | # Specifically:
|
76 | 76 | ||
77 | # TYPE (optional)
|
77 | # TYPE (optional)
|
78 | # METAR or SPECI
|
78 | # METAR or SPECI
|
79 | # METAR: regular report
|
79 | # METAR: regular report
|
80 | # SPECI: special report
|
80 | # SPECI: special report
|
81 | 81 | ||
82 | # SITE (required, only once)
|
82 | # SITE (required, only once)
|
83 | #
|
83 | #
|
84 | # 4-Char site identifier (KLAX for LA, KHST for Houston)
|
84 | # 4-Char site identifier (KLAX for LA, KHST for Houston)
|
85 | 85 | ||
86 | # DATE/TIME (required, only once)
|
86 | # DATE/TIME (required, only once)
|
87 | #
|
87 | #
|
88 | # 6-digit time followed by "Z", indicating UTC
|
88 | # 6-digit time followed by "Z", indicating UTC
|
89 | 89 | ||
90 | # REPORT MODIFIER (optional)
|
90 | # REPORT MODIFIER (optional)
|
91 | # AUTO or COR
|
91 | # AUTO or COR
|
92 | # AUTO = Automatic report (no human intervention)
|
92 | # AUTO = Automatic report (no human intervention)
|
93 | # COR = Corrected METAR or SPECI
|
93 | # COR = Corrected METAR or SPECI
|
94 | 94 | ||
95 | # WIND (group)
|
95 | # WIND (group)
|
96 | #
|
96 | #
|
97 | # Wind direction (\d\d\d) and speed (\d?\d\d) and optionaling gusting
|
97 | # Wind direction (\d\d\d) and speed (\d?\d\d) and optionaling gusting
|
98 | # information denoted by "G" and speed (\d?\d\d) followed by "KT", for knots.
|
98 | # information denoted by "G" and speed (\d?\d\d) followed by "KT", for knots.
|
99 | #
|
99 | #
|
100 | # Wind direction MAY be "VRB" (variable) instead of a compass direction.
|
100 | # Wind direction MAY be "VRB" (variable) instead of a compass direction.
|
101 | #
|
101 | #
|
102 | # Variable Wind Direction (Speeds greater than 6 knots). Variable wind
|
102 | # Variable Wind Direction (Speeds greater than 6 knots). Variable wind
|
103 | # direction with wind speed greater than 6 knots shall be coded in the
|
103 | # direction with wind speed greater than 6 knots shall be coded in the
|
104 | # format, dndndnVdxdxdx
|
104 | # format, dndndnVdxdxdx
|
105 | #
|
105 | #
|
106 | # Calm wind is recorded as 00000KT.
|
106 | # Calm wind is recorded as 00000KT.
|
107 | 107 | ||
108 | # VISIBILITY (group)
|
108 | # VISIBILITY (group)
|
109 | #
|
109 | #
|
110 | # Visibility (\d+) followed by "SM" for statute miles or no 'SM' for meters
|
110 | # Visibility (\d+) followed by "SM" for statute miles or no 'SM' for meters
|
111 | # (european)
|
111 | # (european)
|
112 | #
|
112 | #
|
113 | # May be 1/(\d)SM for a fraction.
|
113 | # May be 1/(\d)SM for a fraction.
|
114 | #
|
114 | #
|
115 | # May be M1/\d)SM for less than a given fraction. (M="-")
|
115 | # May be M1/\d)SM for less than a given fraction. (M="-")
|
116 | #
|
116 | #
|
117 | # \d\d\d\d according to KNMI
|
117 | # \d\d\d\d according to KNMI
|
118 | # lowest horizontal visibility (looking around)
|
118 | # lowest horizontal visibility (looking around)
|
119 | # round down
|
119 | # round down
|
120 | # 0000 - 0500m in steps of 0050m
|
120 | # 0000 - 0500m in steps of 0050m
|
121 | # 0500 - 5000m in steps of 0100m
|
121 | # 0500 - 5000m in steps of 0100m
|
122 | # 5000 - 9999m in steps of 1000m
|
122 | # 5000 - 9999m in steps of 1000m
|
123 | # 10km or more is 9999
|
123 | # 10km or more is 9999
|
124 | 124 | ||
125 | # RUNWAY Visual Range (Group)
|
125 | # RUNWAY Visual Range (Group)
|
126 | #
|
126 | #
|
127 | # R(\d\d\d)(L|C|R)?/((M|P)?\d\d\d\d){1,2}FT
|
127 | # R(\d\d\d)(L|C|R)?/((M|P)?\d\d\d\d){1,2}FT
|
128 | #
|
128 | #
|
129 | # Where:
|
129 | # Where:
|
130 | # $1 is the runway number.
|
130 | # $1 is the runway number.
|
131 | # $2 is the runway (Left/Center/Right) for parallel runways.
|
131 | # $2 is the runway (Left/Center/Right) for parallel runways.
|
132 | # $3 is the reported visibility in feet.
|
132 | # $3 is the reported visibility in feet.
|
133 | # $4 is the MAXIMUM reported visibility, making $3 the MINIMUM.
|
133 | # $4 is the MAXIMUM reported visibility, making $3 the MINIMUM.
|
134 | #
|
134 | #
|
135 | # "M" beginning a value means less than the reportable value of \d\d\d\d.
|
135 | # "M" beginning a value means less than the reportable value of \d\d\d\d.
|
136 | # "P" beginning a value means more than the reportable value of \d\d\d\d.
|
136 | # "P" beginning a value means more than the reportable value of \d\d\d\d.
|
137 | #
|
137 | #
|
138 | # new
|
138 | # new
|
139 | #
|
139 | #
|
140 | # R(\d\d\d[LCR]?)/([MP]?\d\d\d\d)(V[MP]?\d\d\d\d)?FT
|
140 | # R(\d\d\d[LCR]?)/([MP]?\d\d\d\d)(V[MP]?\d\d\d\d)?FT
|
141 | #
|
141 | #
|
142 | # $1 runway number + Left/Center/Right
|
142 | # $1 runway number + Left/Center/Right
|
143 | # $2 visibility feet
|
143 | # $2 visibility feet
|
144 | # $3 Varying feet
|
144 | # $3 Varying feet
|
145 | # M = less than
|
145 | # M = less than
|
146 | # P = more than
|
146 | # P = more than
|
147 | 147 | ||
148 | # WEATHER (Present Weather Group)
|
148 | # WEATHER (Present Weather Group)
|
149 | #
|
149 | #
|
150 | # See table in Chapter 12 of FMH-1.
|
150 | # See table in Chapter 12 of FMH-1.
|
151 | 151 | ||
152 | # CLOUDS (Sky Condition Group)
|
152 | # CLOUDS (Sky Condition Group)
|
153 | #
|
153 | #
|
154 | # A space-separated grouping of cloud conditions which will contain at least
|
154 | # A space-separated grouping of cloud conditions which will contain at least
|
155 | # one cloud report. Examples: "CLR", "BKN330", "SCT100", "FEW055", "OVC070"
|
155 | # one cloud report. Examples: "CLR", "BKN330", "SCT100", "FEW055", "OVC070"
|
156 | # The three-letter codes represent the condition (Clear, Broken, Scattered,
|
156 | # The three-letter codes represent the condition (Clear, Broken, Scattered,
|
157 | # Few, Overcast) and the numbers (\d\d\d) represent altitlude/100.
|
157 | # Few, Overcast) and the numbers (\d\d\d) represent altitlude/100.
|
158 | #
|
158 | #
|
159 | # The report may have a trailing CB (cumulonimbus) or TCU (towering
|
159 | # The report may have a trailing CB (cumulonimbus) or TCU (towering
|
160 | # cumulus) appended. ([A-Z]{2,3})?(\d\d\d)(CB|TCU)?
|
160 | # cumulus) appended. ([A-Z]{2,3})?(\d\d\d)(CB|TCU)?
|
161 | 161 | ||
162 | # Vertical visibility (VV)
|
162 | # Vertical visibility (VV)
|
163 | #
|
163 | #
|
164 | # VV
|
164 | # VV
|
165 | # This group is reported when the sky is obscured. VV is the group indicator,
|
165 | # This group is reported when the sky is obscured. VV is the group indicator,
|
166 | # and hshshs is the vertical visibility in units of 30 metres
|
166 | # and hshshs is the vertical visibility in units of 30 metres
|
167 | # (hundreds of feet).
|
167 | # (hundreds of feet).
|
168 | #
|
168 | #
|
169 | # hshshs - Examples of Encoding
|
169 | # hshshs - Examples of Encoding
|
170 | # HEIGHT METAR CODE
|
170 | # HEIGHT METAR CODE
|
171 | # 100 ft (30 metres) 001
|
171 | # 100 ft (30 metres) 001
|
172 | # 450 ft (135 metres) 004
|
172 | # 450 ft (135 metres) 004
|
173 | # 2,700 ft (810 metres) 027
|
173 | # 2,700 ft (810 metres) 027
|
174 | # 12,600 ft (3,780 metres) 1300
|
174 | # 12,600 ft (3,780 metres) 1300
|
175 | #
|
175 | #
|
176 | # source http://meteocentre.com/doc/metar.html
|
176 | # source http://meteocentre.com/doc/metar.html
|
177 | #
|
177 | #
|
178 | # TEMPERATURE and DEW POINT
|
178 | # TEMPERATURE and DEW POINT
|
179 | #
|
179 | #
|
180 | # (M?\d\d)/(M?\d\d) where $1 is the current temperature in degrees celcius,
|
180 | # (M?\d\d)/(M?\d\d) where $1 is the current temperature in degrees celcius,
|
181 | # and $2 is the current dewpoint in degrees celcius.
|
181 | # and $2 is the current dewpoint in degrees celcius.
|
182 | #
|
182 | #
|
183 | # The "M" signifies a negative temperature, so converting the "M" to a
|
183 | # The "M" signifies a negative temperature, so converting the "M" to a
|
184 | # "-" ought to suffice.
|
184 | # "-" ought to suffice.
|
185 | 185 | ||
186 | # PRESSURE
|
186 | # PRESSURE
|
187 | #
|
187 | #
|
188 | # The pressure, or altimeter setting, at the reporting site recorded in whole
|
188 | # The pressure, or altimeter setting, at the reporting site recorded in whole
|
189 | # hectopascals (starts with a Q) or inches of mercury (Hg) minus the decimal
|
189 | # hectopascals (starts with a Q) or inches of mercury (Hg) minus the decimal
|
190 | # point (starts with an A). It should always look like ([AQ]\d\d\d\d).
|
190 | # point (starts with an A). It should always look like ([AQ]\d\d\d\d).
|
191 | #
|
191 | #
|
192 | # KNMI: Q\d\d\d\d pressure in hPa calculated for sea level
|
192 | # KNMI: Q\d\d\d\d pressure in hPa calculated for sea level
|
193 | 193 | ||
194 | # REMARKS
|
194 | # REMARKS
|
195 | #
|
195 | #
|
196 | # Remarks contain additional information. They are optional but often
|
196 | # Remarks contain additional information. They are optional but often
|
197 | # informative of special conditions.
|
197 | # informative of special conditions.
|
198 | #
|
198 | #
|
199 | # Remarks begin with the "RMK" keyword and continue to the end of the line.
|
199 | # Remarks begin with the "RMK" keyword and continue to the end of the line.
|
200 | #
|
200 | #
|
201 | # trend group
|
201 | # trend group
|
202 | #
|
202 | #
|
203 | # color codes BLU WHT GRN YLO AMB RED
|
203 | # color codes BLU WHT GRN YLO AMB RED
|
204 | # BLACK: vliegveld dicht
|
204 | # BLACK: vliegveld dicht
|
205 | # future trend
|
205 | # future trend
|
206 | # NOSIG no significant change
|
206 | # NOSIG no significant change
|
207 | # TEMPO temporary change
|
207 | # TEMPO temporary change
|
208 | # WHT WHT TEMPO GRN = current white, prediction white temporary green
|
208 | # WHT WHT TEMPO GRN = current white, prediction white temporary green
|
209 | # NSW no significant weather
|
209 | # NSW no significant weather
|
210 | # AT at a given time
|
210 | # AT at a given time
|
211 | # PROB30 probability 30%
|
211 | # PROB30 probability 30%
|
212 | # BECMG becoming
|
212 | # BECMG becoming
|
213 | # BECMG (weather) FM \d\d\d\d TL \d\d\d\d = from until utc times
|
213 | # BECMG (weather) FM \d\d\d\d TL \d\d\d\d = from until utc times
|
214 | # BECMG (weather) AT \d\d\d\d = at utc time
|
214 | # BECMG (weather) AT \d\d\d\d = at utc time
|
215 | # BECMG (weather) TL \d\d\d\d = change until utc time
|
215 | # BECMG (weather) TL \d\d\d\d = change until utc time
|
216 | # BECMG 2000 visibility
|
216 | # BECMG 2000 visibility
|
217 | # BECMG NSW weather type
|
217 | # BECMG NSW weather type
|
218 | # etc etc
|
218 | # etc etc
|
219 | # FCST CANCEL (2 tokens!) Forecast cancel: no further forecasts for a while
|
219 | # FCST CANCEL (2 tokens!) Forecast cancel: no further forecasts for a while
|
220 | 220 | ||
221 | ### Package Definition
|
221 | ### Package Definition
|
222 | 222 | ||
223 | package Geo::ModMETAR; # Package based on Debian Geo::METAR |
223 | package Geo::ModMETAR; # Package based on Debian Geo::METAR |
224 | 224 | ||
225 | ## Required Modules
|
225 | ## Required Modules
|
226 | 226 | ||
227 | use 5.005; |
227 | use 5.005; |
228 | use strict; |
228 | use strict; |
229 | use vars qw($AUTOLOAD $VERSION); |
229 | use vars qw($AUTOLOAD $VERSION); |
230 | use Carp 'cluck'; |
230 | use Carp 'cluck'; |
231 | 231 | ||
232 | $VERSION = '1.1'; # Based on Debian Geo::METAR 1.15 |
232 | $VERSION = '1.1'; # Based on Debian Geo::METAR 1.15 |
233 | 233 | ||
234 | ##
|
234 | ##
|
235 | ## Lookup tables
|
235 | ## Lookup tables
|
236 | ##
|
236 | ##
|
237 | 237 | ||
238 | my %_weather_types = ( |
238 | my %_weather_types = ( |
239 | MI => 'shallow', |
239 | MI => 'shallow', |
240 | PI => 'partial', |
240 | PI => 'partial', |
241 | BC => 'patches', |
241 | BC => 'patches', |
242 | DR => 'drizzle', |
242 | DR => 'drizzle', |
243 | BL => 'blowing', |
243 | BL => 'blowing', |
244 | SH => 'shower(s)', |
244 | SH => 'shower(s)', |
245 | TS => 'thunderstorm', |
245 | TS => 'thunderstorm', |
246 | FZ => 'freezing', |
246 | FZ => 'freezing', |
247 | 247 | ||
248 | DZ => 'drizzle', |
248 | DZ => 'drizzle', |
249 | RA => 'rain', |
249 | RA => 'rain', |
250 | SN => 'snow', |
250 | SN => 'snow', |
251 | SG => 'snow grains', |
251 | SG => 'snow grains', |
252 | IC => 'ice crystals', |
252 | IC => 'ice crystals', |
253 | PE => 'ice pellets', |
253 | PE => 'ice pellets', |
254 | GR => 'hail', |
254 | GR => 'hail', |
255 | GS => 'small hail/snow pellets', |
255 | GS => 'small hail/snow pellets', |
256 | UP => 'unknown precip', |
256 | UP => 'unknown precip', |
257 | 257 | ||
258 | BR => 'mist', |
258 | BR => 'mist', |
259 | FG => 'fog', |
259 | FG => 'fog', |
260 | PRFG => 'fog banks', # officially PR is a modifier of FG |
260 | PRFG => 'fog banks', # officially PR is a modifier of FG |
261 | FU => 'smoke', |
261 | FU => 'smoke', |
262 | VA => 'volcanic ash', |
262 | VA => 'volcanic ash', |
263 | DU => 'dust', |
263 | DU => 'dust', |
264 | SA => 'sand', |
264 | SA => 'sand', |
265 | HZ => 'haze', |
265 | HZ => 'haze', |
266 | PY => 'spray', |
266 | PY => 'spray', |
267 | 267 | ||
268 | PO => 'dust/sand whirls', |
268 | PO => 'dust/sand whirls', |
269 | SQ => 'squalls', |
269 | SQ => 'squalls', |
270 | FC => 'funnel cloud(tornado/waterspout)', |
270 | FC => 'funnel cloud(tornado/waterspout)', |
271 | SS => 'sand storm', |
271 | SS => 'sand storm', |
272 | DS => 'dust storm', |
272 | DS => 'dust storm', |
273 | ); |
273 | ); |
274 | 274 | ||
275 | my $_weather_types_pat = join("|", keys(%_weather_types)); |
275 | my $_weather_types_pat = join("|", keys(%_weather_types)); |
276 | 276 | ||
277 | my %_weather_types_ru = ( |
277 | my %_weather_types_ru = ( |
278 | MI => 'мелкий', |
278 | MI => 'мелкий', |
279 | PI => 'частный', |
279 | PI => 'частный', |
280 | BC => 'местами', |
280 | BC => 'местами', |
281 | DR => 'мелкий дождь', |
281 | DR => 'мелкий дождь', |
282 | BL => 'порывистый ветер', |
282 | BL => 'порывистый ветер', |
283 | SH => 'ливневый дождь', |
283 | SH => 'ливневый дождь', |
284 | TS => 'гроза', |
284 | TS => 'гроза', |
285 | FZ => 'заморозки', |
285 | FZ => 'заморозки', |
286 | 286 | ||
287 | DZ => 'мелкий дождь', |
287 | DZ => 'мелкий дождь', |
288 | RA => 'дождь', |
288 | RA => 'дождь', |
289 | SN => 'снег', |
289 | SN => 'снег', |
290 | SG => 'снег гранулами', |
290 | SG => 'снег гранулами', |
291 | IC => 'ледяные кристаллы', |
291 | IC => 'ледяные кристаллы', |
292 | PE => 'ледяные шарики', |
292 | PE => 'ледяные шарики', |
293 | GR => 'град', |
293 | GR => 'град', |
294 | GS => 'небольшой град', |
294 | GS => 'небольшой град', |
295 | UP => 'осадки', |
295 | UP => 'осадки', |
296 | 296 | ||
297 | BR => 'легкий туман', |
297 | BR => 'легкий туман', |
298 | FG => 'туман', |
298 | FG => 'туман', |
299 | PRFG => 'образование тумана', |
299 | PRFG => 'образование тумана', |
300 | FU => 'дым', |
300 | FU => 'дым', |
301 | VA => 'вулканический пепел', |
301 | VA => 'вулканический пепел', |
302 | DU => 'пыль', |
302 | DU => 'пыль', |
303 | SA => 'песок', |
303 | SA => 'песок', |
304 | HZ => 'дымка', |
304 | HZ => 'дымка', |
305 | PY => 'водяная пыль', |
305 | PY => 'водяная пыль', |
306 | PO => 'песчаные или пылевые вихри', |
306 | PO => 'песчаные или пылевые вихри', |
307 | SQ => 'шквалистый ветер', |
307 | SQ => 'шквалистый ветер', |
308 | FC => 'торнадо', |
308 | FC => 'торнадо', |
309 | SS => 'песчанная буря', |
309 | SS => 'песчанная буря', |
310 | DS => 'пылевая буря', |
310 | DS => 'пылевая буря', |
311 | ); |
311 | ); |
312 | 312 | ||
313 | my $_weather_types_ru_pat = join("|", keys(%_weather_types_ru)); |
313 | my $_weather_types_ru_pat = join("|", keys(%_weather_types_ru)); |
314 | 314 | ||
315 | my %_sky_types = ( |
315 | my %_sky_types = ( |
316 | SKC => "Sky Clear", |
316 | SKC => "Sky Clear", |
317 | CLR => "Sky Clear", |
317 | CLR => "Sky Clear", |
318 | SCT => "Scattered", |
318 | SCT => "Scattered", |
319 | BKN => "Broken", |
319 | BKN => "Broken", |
320 | FEW => "Few", |
320 | FEW => "Few", |
321 | OVC => "Solid Overcast", |
321 | OVC => "Solid Overcast", |
322 | NSC => "No significant clouds", |
322 | NSC => "No significant clouds", |
323 | NCD => "No cloud detected", |
323 | NCD => "No cloud detected", |
324 | ); |
324 | ); |
325 | 325 | ||
326 | my %_sky_types_ru = ( |
326 | my %_sky_types_ru = ( |
327 | SKC => "ясно", |
327 | SKC => "ясно", |
328 | CLR => "ясно", |
328 | CLR => "ясно", |
329 | SCT => "переменная облачность", |
329 | SCT => "переменная облачность", |
330 | BKN => "переменная облачность", |
330 | BKN => "облачно с прояснениями", |
331 | FEW => "слабая облачность", |
331 | FEW => "слабая облачность", |
332 | OVC => "сплошная облачность", |
332 | OVC => "сплошная облачность", |
333 | NSC => "нет существенной облачности", |
333 | NSC => "нет существенной облачности", |
334 | NCD => "безоблачно", |
334 | NCD => "безоблачно", |
335 | ); |
335 | ); |
336 | 336 | ||
337 | my %_trend_types = ( |
337 | my %_trend_types = ( |
338 | BLU => "8 km view", |
338 | BLU => "8 km view", |
339 | WHT => "5 km view", |
339 | WHT => "5 km view", |
340 | GRN => "3.7 km view", |
340 | GRN => "3.7 km view", |
341 | YLO => "1.6 km view", |
341 | YLO => "1.6 km view", |
342 | AMB => "0.8 km view", |
342 | AMB => "0.8 km view", |
343 | RED => "< 0.8 km view", |
343 | RED => "< 0.8 km view", |
344 | BLACK => "airport closed", |
344 | BLACK => "airport closed", |
345 | NOSIG => "No significant change", |
345 | NOSIG => "No significant change", |
346 | TEMPO => "Temporary change", |
346 | TEMPO => "Temporary change", |
347 | NSW => "No significant weather", |
347 | NSW => "No significant weather", |
348 | PROB => "Probability", |
348 | PROB => "Probability", |
349 | BECMG => "Becoming", |
349 | BECMG => "Becoming", |
350 | LAST => "Last", |
350 | LAST => "Last", |
351 | ); |
351 | ); |
352 | 352 | ||
353 | my $_trend_types_pat = join("|", keys(%_trend_types)); |
353 | my $_trend_types_pat = join("|", keys(%_trend_types)); |
354 | 354 | ||
- | 355 | my %_trend_types_ru = ( |
|
- | 356 | BLU => "видимость 8 км", |
|
- | 357 | WHT => "видимость 5 км", |
|
- | 358 | GRN => "видимость 3.7 км", |
|
- | 359 | YLO => "видимость 1.6 км", |
|
- | 360 | AMB => "видимость 800 м", |
|
- | 361 | RED => "видимость менее 800 м", |
|
- | 362 | BLACK => "аэропорт закрыт", |
|
- | 363 | NOSIG => "без существенных изменений", |
|
- | 364 | TEMPO => "временные изменения", |
|
- | 365 | NSW => "без существенной погоды", |
|
- | 366 | PROB => "вероятен", |
|
- | 367 | BECMG => "становление", |
|
- | 368 | LAST => "продолжается", |
|
- | 369 | ); |
|
- | 370 | ||
- | 371 | my $_trend_types_ru_pat = join("|", keys(%_trend_types_ru)); |
|
- | 372 | ||
355 | ##
|
373 | ##
|
356 | ## Constructor.
|
374 | ## Constructor.
|
357 | ##
|
375 | ##
|
358 | 376 | ||
359 | sub new |
377 | sub new |
360 | {
|
378 | {
|
361 | my $this = shift; |
379 | my $this = shift; |
362 | my $class = ref($this) || $this; |
380 | my $class = ref($this) || $this; |
363 | my $self = {}; |
381 | my $self = {}; |
364 | 382 | ||
365 | ##
|
383 | ##
|
366 | ## UPPERCASE items have documented accssor functions (methods) or
|
384 | ## UPPERCASE items have documented accssor functions (methods) or
|
367 | ## use AUTOLOAD, while lowercase items are reserved for internal
|
385 | ## use AUTOLOAD, while lowercase items are reserved for internal
|
368 | ## use.
|
386 | ## use.
|
369 | ##
|
387 | ##
|
370 | 388 | ||
371 | $self->{VERSION} = $VERSION; # version number |
389 | $self->{VERSION} = $VERSION; # version number |
372 | $self->{METAR} = undef; # the actual, raw METAR |
390 | $self->{METAR} = undef; # the actual, raw METAR |
373 | $self->{TYPE} = undef; # the type of report |
391 | $self->{TYPE} = undef; # the type of report |
374 | $self->{SITE} = undef; # site code |
392 | $self->{SITE} = undef; # site code |
375 | $self->{DATE} = undef; # when it was issued |
393 | $self->{DATE} = undef; # when it was issued |
376 | $self->{TIME} = undef; # time it was issued |
394 | $self->{TIME} = undef; # time it was issued |
377 | $self->{MOD} = undef; # modifier (AUTO/COR) |
395 | $self->{MOD} = undef; # modifier (AUTO/COR) |
378 | $self->{WIND_DIR_DEG} = undef; # wind dir in degrees |
396 | $self->{WIND_DIR_DEG} = undef; # wind dir in degrees |
379 | $self->{WIND_DIR_ENG} = undef; # wind dir in english (Northwest/Southeast) |
397 | $self->{WIND_DIR_ENG} = undef; # wind dir in english (Northwest/Southeast) |
380 | $self->{WIND_DIR_RUS} = undef; # wind dir in russian (Северо-западный/Юго-восточный) |
398 | $self->{WIND_DIR_RUS} = undef; # wind dir in russian (Северо-западный/Юго-восточный) |
381 | $self->{WIND_DIR_ABB} = undef; # wind dir in abbreviated english (NW/SE) |
399 | $self->{WIND_DIR_ABB} = undef; # wind dir in abbreviated english (NW/SE) |
382 | $self->{WIND_KTS} = undef; # wind speed (knots) |
400 | $self->{WIND_KTS} = undef; # wind speed (knots) |
383 | $self->{WIND_GUST_KTS} = undef; # wind gusts (knots) |
401 | $self->{WIND_GUST_KTS} = undef; # wind gusts (knots) |
384 | $self->{WIND_MPH} = undef; # wind speed (MPH) |
402 | $self->{WIND_MPH} = undef; # wind speed (MPH) |
385 | $self->{WIND_GUST_MPH} = undef; # wind gusts (MPH) |
403 | $self->{WIND_GUST_MPH} = undef; # wind gusts (MPH) |
386 | $self->{WIND_MS} = undef; # wind speed (m/s) |
404 | $self->{WIND_MS} = undef; # wind speed (m/s) |
387 | $self->{WIND_GUST_MS} = undef; # wind gusts (m/s) |
405 | $self->{WIND_GUST_MS} = undef; # wind gusts (m/s) |
388 | $self->{WIND_VAR} = undef; # wind variation (text) |
406 | $self->{WIND_VAR} = undef; # wind variation (text) |
389 | $self->{WIND_VAR_1} = undef; # wind variation (direction 1) |
407 | $self->{WIND_VAR_1} = undef; # wind variation (direction 1) |
390 | $self->{WIND_VAR_2} = undef; # wind variation (direction 2) |
408 | $self->{WIND_VAR_2} = undef; # wind variation (direction 2) |
391 | $self->{WIND_VAR_ENG_1}= undef; # wind variation (text, direction 1) |
409 | $self->{WIND_VAR_ENG_1}= undef; # wind variation (text, direction 1) |
392 | $self->{WIND_VAR_ENG_2}= undef; # wind variation (text, direction 2) |
410 | $self->{WIND_VAR_ENG_2}= undef; # wind variation (text, direction 2) |
393 | $self->{VISIBILITY} = undef; # visibility info |
411 | $self->{VISIBILITY} = undef; # visibility info |
394 | $self->{RUNWAY} = [ ]; # runway vis. |
412 | $self->{RUNWAY} = [ ]; # runway vis. |
395 | $self->{RH} = undef; # relative humidity |
413 | $self->{RH} = undef; # relative humidity |
396 | $self->{WEATHER} = [ ]; # current weather |
414 | $self->{WEATHER} = [ ]; # current weather |
397 | $self->{WEATHER_LOG} = [ ]; # weather log |
415 | $self->{WEATHER_LOG} = [ ]; # weather log |
398 | $self->{SKY} = [ ]; # current sky (cloudcover) |
416 | $self->{SKY} = [ ]; # current sky (cloudcover) |
399 | $self->{TEMP_F} = undef; # current temp, celsius |
417 | $self->{TEMP_F} = undef; # current temp, celsius |
400 | $self->{TEMP_C} = undef; # converted to fahrenheit |
418 | $self->{TEMP_C} = undef; # converted to fahrenheit |
401 | $self->{TEMP_WC} = undef; # current windchill temp, celsius |
419 | $self->{TEMP_WC} = undef; # current windchill temp, celsius |
402 | $self->{DEW_F} = undef; # dew point, celcius |
420 | $self->{DEW_F} = undef; # dew point, celcius |
403 | $self->{DEW_C} = undef; # dew point, fahrenheit |
421 | $self->{DEW_C} = undef; # dew point, fahrenheit |
404 | $self->{HOURLY_TEMP_F} = undef; # hourly current temp, celcius |
422 | $self->{HOURLY_TEMP_F} = undef; # hourly current temp, celcius |
405 | $self->{HOURLY_TEMP_C} = undef; # hourly converted to fahrenheit |
423 | $self->{HOURLY_TEMP_C} = undef; # hourly converted to fahrenheit |
406 | $self->{HOURLY_DEW_F} = undef; # hourly dew point, celcius |
424 | $self->{HOURLY_DEW_F} = undef; # hourly dew point, celcius |
407 | $self->{HOURLY_DEW_C} = undef; # hourly dew point, fahrenheit |
425 | $self->{HOURLY_DEW_C} = undef; # hourly dew point, fahrenheit |
408 | $self->{HOURLY_PRECIP} = undef; # hourly precipitation |
426 | $self->{HOURLY_PRECIP} = undef; # hourly precipitation |
409 | $self->{ALT} = undef; # altimeter setting (Hg) |
427 | $self->{ALT} = undef; # altimeter setting (Hg) |
410 | $self->{ALT_HP} = undef; # altimeter setting (hPa) |
428 | $self->{ALT_HP} = undef; # altimeter setting (hPa) |
411 | $self->{ALT_PL} = undef; # pressure (Hg) |
429 | $self->{ALT_PL} = undef; # pressure (Hg) |
412 | $self->{SLP} = undef; # sea level pressure |
430 | $self->{SLP} = undef; # sea level pressure |
413 | $self->{REMARKS} = undef; # remarks |
431 | $self->{REMARKS} = undef; # remarks |
414 | $self->{WEATHER_RAW} = [ ]; # RAW data for weather |
432 | $self->{WEATHER_RAW} = [ ]; # RAW data for weather |
415 | $self->{WEATHER_RUS} = [ ]; # current weather in Russian |
433 | $self->{WEATHER_RUS} = [ ]; # current weather in Russian |
416 | $self->{SKY_RAW} = [ ]; # RAW data for sky |
434 | $self->{SKY_RAW} = [ ]; # RAW data for sky |
417 | $self->{SKY_RUS} = [ ]; # current sky in Russian |
435 | $self->{SKY_RUS} = [ ]; # current sky in Russian |
418 | $self->{VISIBILITY_RUS}= undef; # visibility info |
436 | $self->{VISIBILITY_RUS}= undef; # visibility info |
- | 437 | $self->{SLP_RUS} = undef; # sea level pressure in Russian |
|
419 | 438 | ||
420 | $self->{tokens} = [ ]; # the "token" list |
439 | $self->{tokens} = [ ]; # the "token" list |
421 | $self->{type} = "METAR"; # the report type (METAR/SPECI) |
440 | $self->{type} = "METAR"; # the report type (METAR/SPECI) |
422 | # default=METAR
|
441 | # default=METAR
|
423 | $self->{site} = undef; # the site code (4 chars) |
442 | $self->{site} = undef; # the site code (4 chars) |
424 | $self->{date_time} = undef; # date/time |
443 | $self->{date_time} = undef; # date/time |
425 | $self->{modifier} = undef; # the AUTO/COR modifier |
444 | $self->{modifier} = undef; # the AUTO/COR modifier |
426 | $self->{wind} = undef; # the wind information |
445 | $self->{wind} = undef; # the wind information |
427 | $self->{windtype} = undef; # the wind speed type (knots/meterpersecond/kilometersperhour) |
446 | $self->{windtype} = undef; # the wind speed type (knots/meterpersecond/kilometersperhour) |
428 | $self->{windvar} = undef; # the wind variation |
447 | $self->{windvar} = undef; # the wind variation |
429 | $self->{visibility} = undef; # visibility information |
448 | $self->{visibility} = undef; # visibility information |
430 | $self->{runway} = undef; # runway visibility |
449 | $self->{runway} = undef; # runway visibility |
431 | $self->{weather} = [ ]; # current weather conditions |
450 | $self->{weather} = [ ]; # current weather conditions |
432 | $self->{sky} = [ ]; # sky conditions (cloud cover) |
451 | $self->{sky} = [ ]; # sky conditions (cloud cover) |
433 | $self->{temp_dew} = undef; # temp and dew pt. |
452 | $self->{temp_dew} = undef; # temp and dew pt. |
434 | $self->{alt} = undef; # altimeter setting |
453 | $self->{alt} = undef; # altimeter setting |
435 | $self->{pressure} = undef; # pressure (HPa) |
454 | $self->{pressure} = undef; # pressure (HPa) |
436 | $self->{slp} = undef; # sea level pressure |
455 | $self->{slp} = undef; # sea level pressure |
437 | $self->{remarks} = [ ]; # remarks |
456 | $self->{remarks} = [ ]; # remarks |
438 | 457 | ||
439 | $self->{debug} = undef; # enable debug trace |
458 | $self->{debug} = undef; # enable debug trace |
440 | 459 | ||
441 | bless $self, $class; |
460 | bless $self, $class; |
442 | return $self; |
461 | return $self; |
443 | }
|
462 | }
|
444 | 463 | ||
445 | ##
|
464 | ##
|
446 | ## Autoload for access methods to stuff in %fields hash. We should
|
465 | ## Autoload for access methods to stuff in %fields hash. We should
|
447 | ## probably disallow access to the lower-case items as stated above,
|
466 | ## probably disallow access to the lower-case items as stated above,
|
448 | ## but I don't feel like being a Nazi about it. Besides, I haven't
|
467 | ## but I don't feel like being a Nazi about it. Besides, I haven't
|
449 | ## checked to see what that might break.
|
468 | ## checked to see what that might break.
|
450 | ##
|
469 | ##
|
451 | 470 | ||
452 | sub AUTOLOAD
|
471 | sub AUTOLOAD
|
453 | {
|
472 | {
|
454 | my $self = shift; |
473 | my $self = shift; |
455 | 474 | ||
456 | if (not ref $self) |
475 | if (not ref $self) |
457 | {
|
476 | {
|
458 | cluck "bad AUTOLOAD for obj [$self]"; |
477 | cluck "bad AUTOLOAD for obj [$self]"; |
459 | }
|
478 | }
|
460 | 479 | ||
461 | if ($AUTOLOAD =~ /.*::(.*)/) |
480 | if ($AUTOLOAD =~ /.*::(.*)/) |
462 | {
|
481 | {
|
463 | my $key = $1; |
482 | my $key = $1; |
464 | 483 | ||
465 | 484 | ||
466 | ## Backward compatible temps...
|
485 | ## Backward compatible temps...
|
467 | 486 | ||
468 | my %compat = ( |
487 | my %compat = ( |
469 | F_TEMP => 'TEMP_F', |
488 | F_TEMP => 'TEMP_F', |
470 | C_TEMP => 'TEMP_C', |
489 | C_TEMP => 'TEMP_C', |
471 | F_DEW => 'DEW_F', |
490 | F_DEW => 'DEW_F', |
472 | C_DEW => 'DEW_C', |
491 | C_DEW => 'DEW_C', |
473 | ); |
492 | ); |
474 | 493 | ||
475 | if ($compat{$key}) |
494 | if ($compat{$key}) |
476 | {
|
495 | {
|
477 | $key = $compat{$key}; |
496 | $key = $compat{$key}; |
478 | }
|
497 | }
|
479 | 498 | ||
480 | ## Check for the items...
|
499 | ## Check for the items...
|
481 | 500 | ||
482 | if (exists $self->{$key}) |
501 | if (exists $self->{$key}) |
483 | {
|
502 | {
|
484 | return $self->{$key}; |
503 | return $self->{$key}; |
485 | }
|
504 | }
|
486 | else
|
505 | else
|
487 | {
|
506 | {
|
488 | return undef; |
507 | return undef; |
489 | }
|
508 | }
|
490 | }
|
509 | }
|
491 | else
|
510 | else
|
492 | {
|
511 | {
|
493 | warn "strange AUTOLOAD problem!"; |
512 | warn "strange AUTOLOAD problem!"; |
494 | return undef; |
513 | return undef; |
495 | }
|
514 | }
|
496 | }
|
515 | }
|
497 | 516 | ||
498 | ##
|
517 | ##
|
499 | ## Get current version number.
|
518 | ## Get current version number.
|
500 | ##
|
519 | ##
|
501 | 520 | ||
502 | sub version
|
521 | sub version
|
503 | {
|
522 | {
|
504 | my $self = shift; |
523 | my $self = shift; |
505 | print "version() called.\n" if $self->{debug}; |
524 | print "version() called.\n" if $self->{debug}; |
506 | return $self->{VERSION}; |
525 | return $self->{VERSION}; |
507 | }
|
526 | }
|
508 | 527 | ||
509 | ##
|
528 | ##
|
510 | ## Take a METAR, tokenize, and process it.
|
529 | ## Take a METAR, tokenize, and process it.
|
511 | ##
|
530 | ##
|
512 | 531 | ||
513 | sub metar
|
532 | sub metar
|
514 | {
|
533 | {
|
515 | my $self = shift; |
534 | my $self = shift; |
516 | 535 | ||
517 | if (@_) |
536 | if (@_) |
518 | {
|
537 | {
|
519 | $self->{METAR} = shift; |
538 | $self->{METAR} = shift; |
520 | $self->{METAR} =~ s/\n//g; ## nuke any newlines |
539 | $self->{METAR} =~ s/\n//g; ## nuke any newlines |
521 | _tokenize($self); |
540 | _tokenize($self); |
522 | _process($self); |
541 | _process($self); |
523 | }
|
542 | }
|
524 | return $self->{METAR}; |
543 | return $self->{METAR}; |
525 | }
|
544 | }
|
526 | 545 | ||
527 | ##
|
546 | ##
|
528 | ## Break {METAR} into parts. Stuff into @tokens.
|
547 | ## Break {METAR} into parts. Stuff into @tokens.
|
529 | ##
|
548 | ##
|
530 | 549 | ||
531 | sub _tokenize
|
550 | sub _tokenize
|
532 | {
|
551 | {
|
533 | my $self = shift; |
552 | my $self = shift; |
534 | my $tok; |
553 | my $tok; |
535 | my @toks; |
554 | my @toks; |
536 | 555 | ||
537 | # Split tokens on whitespace.
|
556 | # Split tokens on whitespace.
|
538 | @toks = split(/\s+/, $self->{METAR}); |
557 | @toks = split(/\s+/, $self->{METAR}); |
539 | $self->{tokens} = \@toks; |
558 | $self->{tokens} = \@toks; |
540 | }
|
559 | }
|
541 | 560 | ||
542 | ## Process @tokens to populate METAR values.
|
561 | ## Process @tokens to populate METAR values.
|
543 | ##
|
562 | ##
|
544 | ## This is a long and involved subroutine. It basically copies the
|
563 | ## This is a long and involved subroutine. It basically copies the
|
545 | ## @tokens array and treats it as a stack, popping off items,
|
564 | ## @tokens array and treats it as a stack, popping off items,
|
546 | ## examining them, and see what they look like. Based on their
|
565 | ## examining them, and see what they look like. Based on their
|
547 | ## "apppearance" it takes care populating the proper fields
|
566 | ## "apppearance" it takes care populating the proper fields
|
548 | ## internally.
|
567 | ## internally.
|
549 | 568 | ||
550 | sub _process
|
569 | sub _process
|
551 | {
|
570 | {
|
552 | my $self = shift; |
571 | my $self = shift; |
553 | 572 | ||
554 | my @toks = @{$self->{tokens}}; # copy tokens array... |
573 | my @toks = @{$self->{tokens}}; # copy tokens array... |
555 | 574 | ||
556 | my $tok; |
575 | my $tok; |
557 | 576 | ||
558 | ## This is a semi-brute-force way of doing things, but the amount
|
577 | ## This is a semi-brute-force way of doing things, but the amount
|
559 | ## of data is relatively small, so it shouldn't be a big deal.
|
578 | ## of data is relatively small, so it shouldn't be a big deal.
|
560 | ##
|
579 | ##
|
561 | ## Ideally, I'd have it skip checks for items which have been
|
580 | ## Ideally, I'd have it skip checks for items which have been
|
562 | ## found, but that would make this more "linear" and I'd remove
|
581 | ## found, but that would make this more "linear" and I'd remove
|
563 | ## the pretty while loop.
|
582 | ## the pretty while loop.
|
564 | #
|
583 | #
|
565 | # KH: modified to maintain state to not get lost in remarks and stuff
|
584 | # KH: modified to maintain state to not get lost in remarks and stuff
|
566 | # and be a lot better at parsing
|
585 | # and be a lot better at parsing
|
567 | 586 | ||
568 | # states
|
587 | # states
|
569 | 588 | ||
570 | my $expect_type = 0; |
589 | my $expect_type = 0; |
571 | my $expect_site = 1; |
590 | my $expect_site = 1; |
572 | my $expect_datetime = 2; |
591 | my $expect_datetime = 2; |
573 | my $expect_modifier = 3; |
592 | my $expect_modifier = 3; |
574 | my $expect_wind = 4; |
593 | my $expect_wind = 4; |
575 | my $expect_visibility = 5; |
594 | my $expect_visibility = 5; |
576 | my $expect_runwayvisual = 6; |
595 | my $expect_runwayvisual = 6; |
577 | my $expect_presentweather = 7; |
596 | my $expect_presentweather = 7; |
578 | my $expect_clouds = 8; |
597 | my $expect_clouds = 8; |
579 | my $expect_temperature = 9; |
598 | my $expect_temperature = 9; |
580 | my $expect_pressure = 10; |
599 | my $expect_pressure = 10; |
581 | my $expect_recentweather = 11; |
600 | my $expect_recentweather = 11; |
582 | my $expect_remarks = 12; |
601 | my $expect_remarks = 12; |
583 | my $expect_usremarks = 13; |
602 | my $expect_usremarks = 13; |
584 | 603 | ||
585 | my $parsestate = $expect_type; |
604 | my $parsestate = $expect_type; |
586 | 605 | ||
587 | # windtypes
|
606 | # windtypes
|
588 | 607 | ||
589 | my $wt_knots = 1; |
608 | my $wt_knots = 1; |
590 | my $wt_mps = 2; |
609 | my $wt_mps = 2; |
591 | my $wt_kph = 3; |
610 | my $wt_kph = 3; |
592 | 611 | ||
593 | ## Assume standard report by default
|
612 | ## Assume standard report by default
|
594 | 613 | ||
595 | $self->{type} = "METAR"; |
614 | $self->{type} = "METAR"; |
596 | $self->{TYPE} = "Routine Weather Report"; |
615 | $self->{TYPE} = "Routine Weather Report"; |
597 | 616 | ||
598 | while (defined($tok = shift(@toks))) ## as long as there are tokens |
617 | while (defined($tok = shift(@toks))) ## as long as there are tokens |
599 | {
|
618 | {
|
600 | print "trying to match [$tok] state is $parsestate\n" if $self->{debug}; |
619 | print "trying to match [$tok] state is $parsestate\n" if $self->{debug}; |
601 | 620 | ||
602 | ##
|
621 | ##
|
603 | ## is it a report type?
|
622 | ## is it a report type?
|
604 | ##
|
623 | ##
|
605 | 624 | ||
606 | if (($parsestate == $expect_type) and ($tok =~ /(METAR|SPECI)/i)) |
625 | if (($parsestate == $expect_type) and ($tok =~ /(METAR|SPECI)/i)) |
607 | {
|
626 | {
|
608 | $self->{type} = $tok; |
627 | $self->{type} = $tok; |
609 | 628 | ||
610 | if ($self->{type} eq "METAR") |
629 | if ($self->{type} eq "METAR") |
611 | {
|
630 | {
|
612 | $self->{TYPE} = "Routine Weather Report"; |
631 | $self->{TYPE} = "Routine Weather Report"; |
613 | }
|
632 | }
|
614 | elsif ($self->{type} eq "SPECI") |
633 | elsif ($self->{type} eq "SPECI") |
615 | {
|
634 | {
|
616 | $self->{TYPE} = "Special Weather Report"; |
635 | $self->{TYPE} = "Special Weather Report"; |
617 | }
|
636 | }
|
618 | print "[$tok] is a report type.\n" if $self->{debug}; |
637 | print "[$tok] is a report type.\n" if $self->{debug}; |
619 | $parsestate = $expect_site; |
638 | $parsestate = $expect_site; |
620 | next; |
639 | next; |
621 | }
|
640 | }
|
622 | 641 | ||
623 | ##
|
642 | ##
|
624 | ## is is a site ID?
|
643 | ## is is a site ID?
|
625 | ##
|
644 | ##
|
626 | 645 | ||
627 | elsif (($parsestate <= $expect_site) and ($tok =~ /([A-Z]{4}|K[A-Z0-9]{3})/)) |
646 | elsif (($parsestate <= $expect_site) and ($tok =~ /([A-Z]{4}|K[A-Z0-9]{3})/)) |
628 | {
|
647 | {
|
629 | $self->{site} = $tok; |
648 | $self->{site} = $tok; |
630 | print "[$tok] is a site ID.\n" if $self->{debug}; |
649 | print "[$tok] is a site ID.\n" if $self->{debug}; |
631 | $parsestate = $expect_datetime; |
650 | $parsestate = $expect_datetime; |
632 | next; |
651 | next; |
633 | }
|
652 | }
|
634 | 653 | ||
635 | ##
|
654 | ##
|
636 | ## is it a date/time?
|
655 | ## is it a date/time?
|
637 | ##
|
656 | ##
|
638 | 657 | ||
639 | elsif (($parsestate == $expect_datetime) and ($tok =~ /\d{6,6}Z/i)) |
658 | elsif (($parsestate == $expect_datetime) and ($tok =~ /\d{6,6}Z/i)) |
640 | {
|
659 | {
|
641 | $self->{date_time} = $tok; |
660 | $self->{date_time} = $tok; |
642 | print "[$tok] is a date/time.\n" if $self->{debug}; |
661 | print "[$tok] is a date/time.\n" if $self->{debug}; |
643 | $parsestate = $expect_modifier; |
662 | $parsestate = $expect_modifier; |
644 | next; |
663 | next; |
645 | 664 | ||
646 | 665 | ||
647 | }
|
666 | }
|
648 | 667 | ||
649 | ##
|
668 | ##
|
650 | ## is it a report modifier?
|
669 | ## is it a report modifier?
|
651 | ##
|
670 | ##
|
652 | 671 | ||
653 | elsif (($parsestate == $expect_modifier) and ($tok =~ /AUTO|COR|CC[A-Z]/i)) |
672 | elsif (($parsestate == $expect_modifier) and ($tok =~ /AUTO|COR|CC[A-Z]/i)) |
654 | {
|
673 | {
|
655 | $self->{modifier} = $tok; |
674 | $self->{modifier} = $tok; |
656 | print "[$tok] is a report modifier.\n" if $self->{debug}; |
675 | print "[$tok] is a report modifier.\n" if $self->{debug}; |
657 | $parsestate = $expect_wind; |
676 | $parsestate = $expect_wind; |
658 | next; |
677 | next; |
659 | }
|
678 | }
|
660 | 679 | ||
661 | ##
|
680 | ##
|
662 | ## is it wind information in knots?
|
681 | ## is it wind information in knots?
|
663 | #
|
682 | #
|
664 | # eew: KT seems to be optional
|
683 | # eew: KT seems to be optional
|
665 | # but making it optional fails on other stuff
|
684 | # but making it optional fails on other stuff
|
666 | # sortafix: wind needs to be \d\d\d\d\d or VRB\d\d
|
685 | # sortafix: wind needs to be \d\d\d\d\d or VRB\d\d
|
667 | # optional \d\d\d\d\dG\d\d\d (gust direction)
|
686 | # optional \d\d\d\d\dG\d\d\d (gust direction)
|
668 | 687 | ||
669 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /(\d{3}|VRB)\d{2}(G\d{1,3})?(KT)?$/i)) |
688 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /(\d{3}|VRB)\d{2}(G\d{1,3})?(KT)?$/i)) |
670 | {
|
689 | {
|
671 | $self->{wind} = $tok; |
690 | $self->{wind} = $tok; |
672 | $self->{windtype} = $wt_knots; |
691 | $self->{windtype} = $wt_knots; |
673 | print "[$tok] is wind information in knots.\n" if $self->{debug}; |
692 | print "[$tok] is wind information in knots.\n" if $self->{debug}; |
674 | $parsestate = $expect_wind; # stay in wind, it can have variation |
693 | $parsestate = $expect_wind; # stay in wind, it can have variation |
675 | next; |
694 | next; |
676 | }
|
695 | }
|
677 | 696 | ||
678 | ##
|
697 | ##
|
679 | ## is it wind information in meters per second?
|
698 | ## is it wind information in meters per second?
|
680 | ##
|
699 | ##
|
681 | ## can be variable too
|
700 | ## can be variable too
|
682 | 701 | ||
683 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /^(\d{3}|VRB)\d{2}(G\d{2,3})?MPS$/)) |
702 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /^(\d{3}|VRB)\d{2}(G\d{2,3})?MPS$/)) |
684 | {
|
703 | {
|
685 | $self->{wind} = $tok; |
704 | $self->{wind} = $tok; |
686 | print "[$tok] is wind information.\n" if $self->{debug}; |
705 | print "[$tok] is wind information.\n" if $self->{debug}; |
687 | $self->{windtype} = $wt_mps; |
706 | $self->{windtype} = $wt_mps; |
688 | $parsestate = $expect_wind; # stay in wind, it can have variation |
707 | $parsestate = $expect_wind; # stay in wind, it can have variation |
689 | next; |
708 | next; |
690 | }
|
709 | }
|
691 | 710 | ||
692 | ##
|
711 | ##
|
693 | ## is it wind variation information?
|
712 | ## is it wind variation information?
|
694 | ##
|
713 | ##
|
695 | 714 | ||
696 | elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\d{3}V\d{3}$/)) |
715 | elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\d{3}V\d{3}$/)) |
697 | {
|
716 | {
|
698 | $self->{windvar} = $tok; |
717 | $self->{windvar} = $tok; |
699 | print "[$tok] is wind variation information.\n" if $self->{debug}; |
718 | print "[$tok] is wind variation information.\n" if $self->{debug}; |
700 | $parsestate = $expect_visibility; |
719 | $parsestate = $expect_visibility; |
701 | next; |
720 | next; |
702 | }
|
721 | }
|
703 | 722 | ||
704 | ##
|
723 | ##
|
705 | ## wind information missing at the moment?
|
724 | ## wind information missing at the moment?
|
706 | ##
|
725 | ##
|
707 | 726 | ||
708 | elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\/\/\/\/\/(KT|MPS)$/)){ |
727 | elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\/\/\/\/\/(KT|MPS)$/)){ |
709 | print "[$tok] is missing wind information.\n" if $self->{debug}; |
728 | print "[$tok] is missing wind information.\n" if $self->{debug}; |
710 | $parsestate = $expect_visibility; |
729 | $parsestate = $expect_visibility; |
711 | next; |
730 | next; |
712 | }
|
731 | }
|
713 | 732 | ||
714 | ##
|
733 | ##
|
715 | ## is it visibility information in meters?
|
734 | ## is it visibility information in meters?
|
716 | ##
|
735 | ##
|
717 | 736 | ||
718 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}$/)) |
737 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}$/)) |
719 | {
|
738 | {
|
720 | $self->{visibility} = $tok; |
739 | $self->{visibility} = $tok; |
721 | print "[$tok] is numerical visibility information.\n" if $self->{debug}; |
740 | print "[$tok] is numerical visibility information.\n" if $self->{debug}; |
722 | $parsestate = $expect_visibility; |
741 | $parsestate = $expect_visibility; |
723 | next; |
742 | next; |
724 | }
|
743 | }
|
725 | 744 | ||
726 | ## auto visibility information in meters?
|
745 | ## auto visibility information in meters?
|
727 | 746 | ||
728 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}NDV$/)) |
747 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}NDV$/)) |
729 | {
|
748 | {
|
730 | $self->{visibility} = $tok; |
749 | $self->{visibility} = $tok; |
731 | print "[$tok] is automatic numerical visibility information.\n" if $self->{debug}; |
750 | print "[$tok] is automatic numerical visibility information.\n" if $self->{debug}; |
732 | $parsestate = $expect_visibility; |
751 | $parsestate = $expect_visibility; |
733 | next; |
752 | next; |
734 | }
|
753 | }
|
735 | 754 | ||
736 | ##
|
755 | ##
|
737 | ## is it visibility information in statute miles?
|
756 | ## is it visibility information in statute miles?
|
738 | ##
|
757 | ##
|
739 | 758 | ||
740 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /.*?SM$/i)) |
759 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /.*?SM$/i)) |
741 | {
|
760 | {
|
742 | $self->{visibility} = $tok; |
761 | $self->{visibility} = $tok; |
743 | print "[$tok] is statute miles visibility information.\n" if $self->{debug}; |
762 | print "[$tok] is statute miles visibility information.\n" if $self->{debug}; |
744 | $parsestate = $expect_visibility; |
763 | $parsestate = $expect_visibility; |
745 | next; |
764 | next; |
746 | }
|
765 | }
|
747 | 766 | ||
748 | ##
|
767 | ##
|
749 | ## is it visibility information with a leading digit?
|
768 | ## is it visibility information with a leading digit?
|
750 | ##
|
769 | ##
|
751 | ## sample:
|
770 | ## sample:
|
752 | ## KERV 132345Z AUTO 07008KT 1 1/4SM HZ 34/11 A3000 RMK AO2
|
771 | ## KERV 132345Z AUTO 07008KT 1 1/4SM HZ 34/11 A3000 RMK AO2
|
753 | ## ^^^^^^^
|
772 | ## ^^^^^^^
|
754 | ##
|
773 | ##
|
755 | 774 | ||
756 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d$/)) |
775 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d$/)) |
757 | {
|
776 | {
|
758 | $tok .= " " . shift(@toks); |
777 | $tok .= " " . shift(@toks); |
759 | $self->{visibility} = $tok; |
778 | $self->{visibility} = $tok; |
760 | print "[$tok] is multi-part visibility information.\n" if $self->{debug}; |
779 | print "[$tok] is multi-part visibility information.\n" if $self->{debug}; |
761 | $parsestate = $expect_visibility; |
780 | $parsestate = $expect_visibility; |
762 | next; |
781 | next; |
763 | }
|
782 | }
|
764 | 783 | ||
765 | ## visibility modifier
|
784 | ## visibility modifier
|
766 | 785 | ||
767 | elsif (($parsestate == $expect_visibility) and ($tok =~ /^\d{4}(N|S|E|W|NE|NW|SE|SW)$/)) |
786 | elsif (($parsestate == $expect_visibility) and ($tok =~ /^\d{4}(N|S|E|W|NE|NW|SE|SW)$/)) |
768 | {
|
787 | {
|
769 | print "[$tok] is a visibility modifier.\n" if $self->{debug}; |
788 | print "[$tok] is a visibility modifier.\n" if $self->{debug}; |
770 | next; |
789 | next; |
771 | }
|
790 | }
|
772 | 791 | ||
773 | ##
|
792 | ##
|
774 | ## is it runway visibility info?
|
793 | ## is it runway visibility info?
|
775 | ##
|
794 | ##
|
776 | # KH: I've seen runway visibility with 'U' units
|
795 | # KH: I've seen runway visibility with 'U' units
|
777 | # EHSB 121425Z 22010KT 1200 R27/1600U -DZ BKN003 OVC007 07/07 Q1016 AMB FCST CANCEL
|
796 | # EHSB 121425Z 22010KT 1200 R27/1600U -DZ BKN003 OVC007 07/07 Q1016 AMB FCST CANCEL
|
778 | # U= going up, D= going down, N= no change
|
797 | # U= going up, D= going down, N= no change
|
779 | # tendency of visual range, http://stoivane.kapsi.fi/metar/
|
798 | # tendency of visual range, http://stoivane.kapsi.fi/metar/
|
780 | 799 | ||
781 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_presentweather) and ($tok =~ /R\d+(L|R|C)?\/P?\d+(VP?\d+)?(FT|D|U|N|\/)?$/i)) |
800 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_presentweather) and ($tok =~ /R\d+(L|R|C)?\/P?\d+(VP?\d+)?(FT|D|U|N|\/)?$/i)) |
782 | {
|
801 | {
|
783 | push (@{$self->{RUNWAY}},$tok); |
802 | push (@{$self->{RUNWAY}},$tok); |
784 | print "[$tok] is runway visual information.\n" if $self->{debug}; |
803 | print "[$tok] is runway visual information.\n" if $self->{debug}; |
785 | $parsestate = $expect_runwayvisual; |
804 | $parsestate = $expect_runwayvisual; |
786 | # there can be multiple runways, so stay at this state
|
805 | # there can be multiple runways, so stay at this state
|
787 | next; |
806 | next; |
788 | }
|
807 | }
|
789 | 808 | ||
790 | ##
|
809 | ##
|
791 | ## is it current weather info?
|
810 | ## is it current weather info?
|
792 | ##
|
811 | ##
|
793 | 812 | ||
794 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_clouds) and ($tok =~ /^(-|\+)?(VC)?($_weather_types_pat)+/i)) |
813 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_clouds) and ($tok =~ /^(-|\+)?(VC)?($_weather_types_pat)+/i)) |
795 | {
|
814 | {
|
796 | my $engl = ""; |
815 | my $engl = ""; |
797 | my $rusl = ""; |
816 | my $rusl = ""; |
798 | my $rawl = ""; |
817 | my $rawl = ""; |
799 | my $qual = $1; |
818 | my $qual = $1; |
800 | my $addlqual = $2; |
819 | my $addlqual = $2; |
801 | 820 | ||
802 | ## qualifier
|
821 | ## qualifier
|
803 | 822 | ||
804 | if (defined $qual) |
823 | if (defined $qual) |
805 | {
|
824 | {
|
806 | if ( $qual eq "-" ) { |
825 | if ( $qual eq "-" ) { |
807 | $engl = "light"; |
826 | $engl = "light"; |
808 | $rusl = "легкий"; |
827 | $rusl = "легкий"; |
809 | } elsif ( $qual eq "+" ) { |
828 | } elsif ( $qual eq "+" ) { |
810 | $engl = "heavy"; |
829 | $engl = "heavy"; |
811 | $rusl = "сильный"; |
830 | $rusl = "сильный"; |
812 | } else { |
831 | } else { |
813 | $engl = ""; ## moderate |
832 | $engl = ""; ## moderate |
814 | $rusl = ""; |
833 | $rusl = ""; |
815 | }
|
834 | }
|
816 | $rawl = $qual; |
835 | $rawl = $qual; |
817 | }
|
836 | }
|
818 | else
|
837 | else
|
819 | {
|
838 | {
|
820 | $engl = ""; ## moderate |
839 | $engl = ""; ## moderate |
821 | $rusl = ""; |
840 | $rusl = ""; |
822 | $rawl = ""; |
841 | $rawl = ""; |
823 | }
|
842 | }
|
824 | 843 | ||
825 | while ( $tok =~ /($_weather_types_pat)/gi ) |
844 | while ( $tok =~ /($_weather_types_pat)/gi ) |
826 | {
|
845 | {
|
827 | $engl .= " " . $_weather_types{$1}; ## figure out weather |
846 | $engl .= " " . $_weather_types{$1}; ## figure out weather |
828 | $rusl .= " " . $_weather_types_ru{$1} . ", "; |
847 | $rusl .= " " . $_weather_types_ru{$1} . ", "; |
829 | $rawl .= " " . $1; |
848 | $rawl .= " " . $1; |
830 | }
|
849 | }
|
831 | $rusl = substr($rusl, 0, length($rusl)-2); |
850 | $rusl = substr($rusl, 0, length($rusl)-2); |
832 | 851 | ||
833 | 852 | ||
834 | ## addl qualifier
|
853 | ## addl qualifier
|
835 | 854 | ||
836 | if (defined $addlqual) |
855 | if (defined $addlqual) |
837 | {
|
856 | {
|
838 | if ( $addlqual eq "VC" ) |
857 | if ( $addlqual eq "VC" ) |
839 | {
|
858 | {
|
840 | $engl .= " in vicinity"; |
859 | $engl .= " in vicinity"; |
841 | $rusl .= " в окрестностях"; |
860 | $rusl .= " в окрестностях"; |
842 | }
|
861 | }
|
843 | }
|
862 | }
|
844 | 863 | ||
845 | $engl =~ s/^\s//gio; |
864 | $engl =~ s/^\s//gio; |
846 | $engl =~ s/\s\s/ /gio; |
865 | $engl =~ s/\s\s/ /gio; |
847 | $rusl =~ s/^\s//gio; |
866 | $rusl =~ s/^\s//gio; |
848 | $rusl =~ s/\s\s/ /gio; |
867 | $rusl =~ s/\s\s/ /gio; |
849 | $rawl =~ s/^\s//gio; |
868 | $rawl =~ s/^\s//gio; |
850 | $rawl =~ s/\s\s/ /gio; |
869 | $rawl =~ s/\s\s/ /gio; |
851 | 870 | ||
852 | push(@{$self->{WEATHER}},$engl); |
871 | push(@{$self->{WEATHER}},$engl); |
853 | push(@{$self->{WEATHER_RUS}},$rusl); |
872 | push(@{$self->{WEATHER_RUS}},$rusl); |
854 | push(@{$self->{WEATHER_RAW}},$rawl); |
873 | push(@{$self->{WEATHER_RAW}},$rawl); |
855 | push(@{$self->{weather}},$tok); |
874 | push(@{$self->{weather}},$tok); |
856 | print "[$tok] is current weather.\n" if $self->{debug}; |
875 | print "[$tok] is current weather.\n" if $self->{debug}; |
857 | $parsestate = $expect_presentweather; |
876 | $parsestate = $expect_presentweather; |
858 | # there can be multiple current weather types, so stay at this state
|
877 | # there can be multiple current weather types, so stay at this state
|
859 | next; |
878 | next; |
860 | }
|
879 | }
|
861 | 880 | ||
862 | ##
|
881 | ##
|
863 | ## special case: CAVOK
|
882 | ## special case: CAVOK
|
864 | ##
|
883 | ##
|
865 | 884 | ||
866 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok eq 'CAVOK' )) |
885 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok eq 'CAVOK' )) |
867 | {
|
886 | {
|
868 | push(@{$self->{sky}},$tok); |
887 | push(@{$self->{sky}},$tok); |
869 | push(@{$self->{SKY}}, "Sky Clear"); |
888 | push(@{$self->{SKY}}, "Sky Clear"); |
870 | push(@{$self->{SKY_RUS}}, "Ясно"); |
889 | push(@{$self->{SKY_RUS}}, "Ясно"); |
871 | push(@{$self->{SKY_RAW}},$tok); |
890 | push(@{$self->{SKY_RAW}},$tok); |
872 | push(@{$self->{weather}},$tok); |
891 | push(@{$self->{weather}},$tok); |
873 | push(@{$self->{WEATHER}},"No significant weather"); |
892 | push(@{$self->{WEATHER}},"No significant weather"); |
874 | $self->{visibility} = '9999'; |
893 | $self->{visibility} = '9999'; |
875 | $parsestate = $expect_temperature; |
894 | $parsestate = $expect_temperature; |
876 | next; |
895 | next; |
877 | }
|
896 | }
|
878 | 897 | ||
879 | ##
|
898 | ##
|
880 | ## is it sky conditions (clear)?
|
899 | ## is it sky conditions (clear)?
|
881 | ##
|
900 | ##
|
882 | 901 | ||
883 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /SKC|CLR/ )) |
902 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /SKC|CLR/ )) |
884 | {
|
903 | {
|
885 | push(@{$self->{sky}},$tok); |
904 | push(@{$self->{sky}},$tok); |
886 | push(@{$self->{SKY}}, "Sky Clear"); |
905 | push(@{$self->{SKY}}, "Sky Clear"); |
887 | push(@{$self->{SKY_RUS}}, "Ясно"); |
906 | push(@{$self->{SKY_RUS}}, "Ясно"); |
888 | push(@{$self->{SKY_RAW}},$tok); |
907 | push(@{$self->{SKY_RAW}},$tok); |
889 | print "[$tok] is a sky condition.\n" if $self->{debug}; |
908 | print "[$tok] is a sky condition.\n" if $self->{debug}; |
890 | $parsestate = $expect_clouds; |
909 | $parsestate = $expect_clouds; |
891 | next; |
910 | next; |
892 | }
|
911 | }
|
893 | 912 | ||
894 | ##
|
913 | ##
|
895 | ## is it sky conditions (clouds)?
|
914 | ## is it sky conditions (clouds)?
|
896 | ##
|
915 | ##
|
897 | ## sky conditions can end with ///
|
916 | ## sky conditions can end with ///
|
898 | 917 | ||
899 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(FEW|SCT|BKN|OVC)(\d\d\d)?(CB|TCU)?\/*$/i)) |
918 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(FEW|SCT|BKN|OVC)(\d\d\d)?(CB|TCU)?\/*$/i)) |
900 | {
|
919 | {
|
901 | push(@{$self->{sky}},$tok); |
920 | push(@{$self->{sky}},$tok); |
902 | my $engl = ""; |
921 | my $engl = ""; |
903 | my $rusl = ""; |
922 | my $rusl = ""; |
904 | my $rawl = ""; |
923 | my $rawl = ""; |
905 | 924 | ||
906 | $engl = $_sky_types{$1}; |
925 | $engl = $_sky_types{$1}; |
907 | $rusl = $_sky_types_ru{$1}; |
926 | $rusl = $_sky_types_ru{$1}; |
908 | $rawl = $1; |
927 | $rawl = $1; |
909 | 928 | ||
910 | if (defined $3) |
929 | if (defined $3) |
911 | {
|
930 | {
|
912 | if ($3 eq "TCU") |
931 | if ($3 eq "TCU") |
913 | {
|
932 | {
|
914 | $engl .= " Towering Cumulus"; |
933 | $engl .= " Towering Cumulus"; |
915 | $rusl .= ", кучевые облака"; |
934 | $rusl .= ", кучевые облака"; |
916 | }
|
935 | }
|
917 | elsif ($3 eq "CB") |
936 | elsif ($3 eq "CB") |
918 | {
|
937 | {
|
919 | $engl .= " Cumulonimbus"; |
938 | $engl .= " Cumulonimbus"; |
920 | $rusl .= ", кучево-дождевые облака"; |
939 | $rusl .= ", кучево-дождевые облака"; |
921 | }
|
940 | }
|
922 | $rawl = $3; |
941 | $rawl = $3; |
923 | }
|
942 | }
|
924 | 943 | ||
925 | if ($2 ne "") |
944 | if ($2 ne "") |
926 | {
|
945 | {
|
927 | my $agl = int($2)*100; |
946 | my $agl = int($2)*100; |
928 | $engl .= " at $agl" . "ft"; |
947 | $engl .= " at $agl" . "ft"; |
929 | $rusl .= " на высоте " . int(($agl*0.3048)/10)*10 . " м"; |
948 | $rusl .= " на высоте " . int(($agl*0.3048)/10)*10 . " м"; |
930 | }
|
949 | }
|
931 | 950 | ||
932 | push(@{$self->{SKY}}, $engl); |
951 | push(@{$self->{SKY}}, $engl); |
933 | push(@{$self->{SKY_RUS}}, $rusl); |
952 | push(@{$self->{SKY_RUS}}, $rusl); |
934 | push(@{$self->{SKY_RAW}}, $rawl); |
953 | push(@{$self->{SKY_RAW}}, $rawl); |
935 | print "[$tok] is a sky condition.\n" if $self->{debug}; |
954 | print "[$tok] is a sky condition.\n" if $self->{debug}; |
936 | $parsestate = $expect_clouds; |
955 | $parsestate = $expect_clouds; |
937 | # clouds DO repeat. a lot ;)
|
956 | # clouds DO repeat. a lot ;)
|
938 | next; |
957 | next; |
939 | }
|
958 | }
|
940 | 959 | ||
941 | ##
|
960 | ##
|
942 | ## auto detected cloud conditions
|
961 | ## auto detected cloud conditions
|
943 | ##
|
962 | ##
|
944 | 963 | ||
945 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(NSC|NCD)$/ )){ |
964 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(NSC|NCD)$/ )){ |
946 | my $engl = ""; |
965 | my $engl = ""; |
947 | my $rusl = ""; |
966 | my $rusl = ""; |
948 | 967 | ||
949 | $engl = $_sky_types{$tok}; |
968 | $engl = $_sky_types{$tok}; |
950 | $rusl = $_sky_types_ru{$tok}; |
969 | $rusl = $_sky_types_ru{$tok}; |
951 | push(@{$self->{SKY}}, $engl); |
970 | push(@{$self->{SKY}}, $engl); |
952 | push(@{$self->{SKY_RUS}}, $rusl); |
971 | push(@{$self->{SKY_RUS}}, $rusl); |
953 | push(@{$self->{SKY_RAW}}, $tok); |
972 | push(@{$self->{SKY_RAW}}, $tok); |
954 | print "[$tok] is an automatic sky condition.\n" if $self->{debug}; |
973 | print "[$tok] is an automatic sky condition.\n" if $self->{debug}; |
955 | $parsestate = $expect_temperature; |
974 | $parsestate = $expect_temperature; |
956 | next; |
975 | next; |
957 | }
|
976 | }
|
958 | 977 | ||
959 | ##
|
978 | ##
|
960 | ## Vertical visibility
|
979 | ## Vertical visibility
|
961 | ##
|
980 | ##
|
962 | 981 | ||
963 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^VV\d+$/ )){ |
982 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^VV\d+$/ )){ |
964 | print "[$tok] is vertical visibility.\n" if $self->{debug}; |
983 | print "[$tok] is vertical visibility.\n" if $self->{debug}; |
965 | $parsestate = $expect_temperature; |
984 | $parsestate = $expect_temperature; |
966 | next; |
985 | next; |
967 | }
|
986 | }
|
968 | 987 | ||
969 | ##
|
988 | ##
|
970 | ## is it temperature and dew point info?
|
989 | ## is it temperature and dew point info?
|
971 | ##
|
990 | ##
|
972 | 991 | ||
973 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_pressure) and ($tok =~ /^(M?\d\d)\/(M?\d{0,2})/i)) |
992 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_pressure) and ($tok =~ /^(M?\d\d)\/(M?\d{0,2})/i)) |
974 | {
|
993 | {
|
975 | next if $self->{temp_dew}; |
994 | next if $self->{temp_dew}; |
976 | $self->{temp_dew} = $tok; |
995 | $self->{temp_dew} = $tok; |
977 | 996 | ||
978 | $self->{TEMP_C} = $1; |
997 | $self->{TEMP_C} = $1; |
979 | $self->{DEW_C} = $2; |
998 | $self->{DEW_C} = $2; |
980 | $self->{TEMP_C} =~ s/^M/-/; |
999 | $self->{TEMP_C} =~ s/^M/-/; |
981 | $self->{DEW_C} =~ s/^M/-/; |
1000 | $self->{DEW_C} =~ s/^M/-/; |
982 | 1001 | ||
983 | print "[$tok] is temperature/dew point information.\n" if $self->{debug}; |
1002 | print "[$tok] is temperature/dew point information.\n" if $self->{debug}; |
984 | $parsestate = $expect_pressure; |
1003 | $parsestate = $expect_pressure; |
985 | next; |
1004 | next; |
986 | }
|
1005 | }
|
987 | 1006 | ||
988 | ##
|
1007 | ##
|
989 | ## is it an altimeter setting? (in.Hg)
|
1008 | ## is it an altimeter setting? (in.Hg)
|
990 | ##
|
1009 | ##
|
991 | 1010 | ||
992 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^A(\d\d)(\d\d)$/i)) |
1011 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^A(\d\d)(\d\d)$/i)) |
993 | {
|
1012 | {
|
994 | $self->{alt} = $tok; |
1013 | $self->{alt} = $tok; |
995 | $self->{ALT} = "$1.$2"+0; |
1014 | $self->{ALT} = "$1.$2"+0; |
996 | $self->{ALT_HP} = "$1.$2" * 33.863886; |
1015 | $self->{ALT_HP} = "$1.$2" * 33.863886; |
997 | 1016 | ||
998 | print "[$tok] is an altimeter setting.\n" if $self->{debug}; |
1017 | print "[$tok] is an altimeter setting.\n" if $self->{debug}; |
999 | $parsestate = $expect_recentweather; |
1018 | $parsestate = $expect_recentweather; |
1000 | next; |
1019 | next; |
1001 | }
|
1020 | }
|
1002 | 1021 | ||
1003 | ##
|
1022 | ##
|
1004 | ## is it a pressure? (hPa)
|
1023 | ## is it a pressure? (hPa)
|
1005 | ##
|
1024 | ##
|
1006 | 1025 | ||
1007 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^Q(\d\d\d\d)$/i)) |
1026 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^Q(\d\d\d\d)$/i)) |
1008 | {
|
1027 | {
|
1009 | $self->{pressure} = $1; |
1028 | $self->{pressure} = $1; |
1010 | $self->{ALT_HP} = $1; |
1029 | $self->{ALT_HP} = $1; |
1011 | $self->{ALT} = 0.029529983 * $self->{pressure}; |
1030 | $self->{ALT} = 0.029529983 * $self->{pressure}; |
1012 | print "[$tok] is an air pressure.\n" if $self->{debug}; |
1031 | print "[$tok] is an air pressure.\n" if $self->{debug}; |
1013 | $parsestate = $expect_recentweather; |
1032 | $parsestate = $expect_recentweather; |
1014 | next; |
1033 | next; |
1015 | }
|
1034 | }
|
1016 | 1035 | ||
1017 | ##
|
1036 | ##
|
1018 | ## recent weather?
|
1037 | ## recent weather?
|
1019 | ##
|
1038 | ##
|
1020 | 1039 | ||
1021 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^RE($_weather_types_pat)$/)){ |
1040 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^RE($_weather_types_pat)$/)){ |
1022 | print "[$tok] is recent significant weather.\n" if $self->{debug}; |
1041 | print "[$tok] is recent significant weather.\n" if $self->{debug}; |
1023 | $parsestate = $expect_remarks; |
1042 | $parsestate = $expect_remarks; |
1024 | next; |
1043 | next; |
1025 | }
|
1044 | }
|
1026 | 1045 | ||
1027 | ##
|
1046 | ##
|
1028 | ## euro type trend?
|
1047 | ## euro type trend?
|
1029 | ##
|
1048 | ##
|
1030 | 1049 | ||
1031 | elsif (($parsestate >= $expect_modifier) and ($tok =~ /^$_trend_types_pat/)){ |
1050 | elsif (($parsestate >= $expect_modifier) and ($tok =~ /^$_trend_types_pat/) and ($tok =~ /^$_trend_types_ru_pat/)){ |
1032 | print "[$tok] is a trend.\n" if $self->{debug}; |
1051 | print "[$tok] is a trend.\n" if $self->{debug}; |
1033 | $parsestate = $expect_remarks; |
1052 | $parsestate = $expect_remarks; |
1034 | next; |
1053 | next; |
1035 | }
|
1054 | }
|
1036 | 1055 | ||
1037 | ##
|
1056 | ##
|
1038 | ## us type remarks? .. can happen quite early in the process already
|
1057 | ## us type remarks? .. can happen quite early in the process already
|
1039 | ##
|
1058 | ##
|
1040 | 1059 | ||
1041 | elsif (($parsestate >= $expect_modifier) and ($tok =~ /^RMK$/i)) |
1060 | elsif (($parsestate >= $expect_modifier) and ($tok =~ /^RMK$/i)) |
1042 | {
|
1061 | {
|
1043 | push(@{$self->{remarks}},$tok); |
1062 | push(@{$self->{remarks}},$tok); |
1044 | print "[$tok] is a (US type) remark.\n" if $self->{debug}; |
1063 | print "[$tok] is a (US type) remark.\n" if $self->{debug}; |
1045 | $parsestate = $expect_usremarks; |
1064 | $parsestate = $expect_usremarks; |
1046 | next; |
1065 | next; |
1047 | }
|
1066 | }
|
1048 | 1067 | ||
1049 | ##
|
1068 | ##
|
1050 | ## automatic station type?
|
1069 | ## automatic station type?
|
1051 | ##
|
1070 | ##
|
1052 | 1071 | ||
1053 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^A(O\d)$/i)) |
1072 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^A(O\d)$/i)) |
1054 | {
|
1073 | {
|
1055 | $self->{autostationtype} = $tok; |
1074 | $self->{autostationtype} = $tok; |
1056 | $self->{AUTO_STATIONTYPE} = $1; |
1075 | $self->{AUTO_STATIONTYPE} = $1; |
1057 | print "[$tok] is an automatic station type remark.\n" if $self->{debug}; |
1076 | print "[$tok] is an automatic station type remark.\n" if $self->{debug}; |
1058 | next; |
1077 | next; |
1059 | }
|
1078 | }
|
1060 | 1079 | ||
1061 | ##
|
1080 | ##
|
1062 | ## sea level pressure
|
1081 | ## sea level pressure
|
1063 | ##
|
1082 | ##
|
1064 | 1083 | ||
1065 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^SLP(\d+)/i)) |
1084 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^SLP(\d+)/i)) |
1066 | {
|
1085 | {
|
1067 | $self->{slp} = $tok; |
1086 | $self->{slp} = $tok; |
1068 | $self->{SLP} = "$1 mb"; |
1087 | $self->{SLP} = "$1 mb"; |
- | 1088 | $self->{SLP_RUS} = "$1 мбар"; |
|
1069 | print "[$tok] is a sea level pressure.\n" if $self->{debug}; |
1089 | print "[$tok] is a sea level pressure.\n" if $self->{debug}; |
1070 | next; |
1090 | next; |
1071 | }
|
1091 | }
|
1072 | 1092 | ||
1073 | ##
|
1093 | ##
|
1074 | ## sea level pressure not available
|
1094 | ## sea level pressure not available
|
1075 | ##
|
1095 | ##
|
1076 | 1096 | ||
1077 | elsif (($parsestate == $expect_usremarks) and ($tok eq "SLPNO")) |
1097 | elsif (($parsestate == $expect_usremarks) and ($tok eq "SLPNO")) |
1078 | {
|
1098 | {
|
1079 | $self->{slp} = "SLPNO"; |
1099 | $self->{slp} = "SLPNO"; |
1080 | $self->{SLP} = "not available"; |
1100 | $self->{SLP} = "not available"; |
- | 1101 | $self->{SLP_RUS} = "нет данных"; |
|
1081 | print "[$tok] is a sea level pressure.\n" if $self->{debug}; |
1102 | print "[$tok] is a sea level pressure.\n" if $self->{debug}; |
1082 | next; |
1103 | next; |
1083 | }
|
1104 | }
|
1084 | 1105 | ||
1085 | ##
|
1106 | ##
|
1086 | ## hourly precipitation
|
1107 | ## hourly precipitation
|
1087 | ##
|
1108 | ##
|
1088 | 1109 | ||
1089 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^P(\d\d\d\d)$/i)) |
1110 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^P(\d\d\d\d)$/i)) |
1090 | {
|
1111 | {
|
1091 | $self->{hourlyprecip} = $tok; |
1112 | $self->{hourlyprecip} = $tok; |
1092 | 1113 | ||
1093 | if ( $1 eq "0000" ) { |
1114 | if ( $1 eq "0000" ) { |
1094 | $self->{HOURLY_PRECIP} = "Trace"; |
1115 | $self->{HOURLY_PRECIP} = "Trace"; |
1095 | } else { |
1116 | } else { |
1096 | $self->{HOURLY_PRECIP} = $1; |
1117 | $self->{HOURLY_PRECIP} = $1; |
1097 | }
|
1118 | }
|
1098 | }
|
1119 | }
|
1099 | 1120 | ||
1100 | ##
|
1121 | ##
|
1101 | ## weather begin/end times
|
1122 | ## weather begin/end times
|
1102 | ##
|
1123 | ##
|
1103 | 1124 | ||
1104 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^($_weather_types_pat)([BE\d]+)$/i)) |
1125 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^($_weather_types_pat)([BE\d]+)$/i)) |
1105 | {
|
1126 | {
|
1106 | my $engl = ""; |
1127 | my $engl = ""; |
1107 | my $times = $2; |
1128 | my $times = $2; |
1108 | 1129 | ||
1109 | $self->{weatherlog} = $tok; |
1130 | $self->{weatherlog} = $tok; |
1110 | 1131 | ||
1111 | $engl = $_weather_types{$1}; |
1132 | $engl = $_weather_types{$1}; |
1112 | 1133 | ||
1113 | while ( $times =~ /(B|E)(\d\d)/g ) |
1134 | while ( $times =~ /(B|E)(\d\d)/g ) |
1114 | {
|
1135 | {
|
1115 | if ( $1 eq "B" ) { |
1136 | if ( $1 eq "B" ) { |
1116 | $engl .= " began :$2"; |
1137 | $engl .= " began :$2"; |
1117 | } else { |
1138 | } else { |
1118 | $engl .= " ended :$2"; |
1139 | $engl .= " ended :$2"; |
1119 | }
|
1140 | }
|
1120 | }
|
1141 | }
|
1121 | 1142 | ||
1122 | push(@{$self->{WEATHER_LOG}}, $engl); |
1143 | push(@{$self->{WEATHER_LOG}}, $engl); |
1123 | print "[$tok] is a weather log.\n" if $self->{debug}; |
1144 | print "[$tok] is a weather log.\n" if $self->{debug}; |
1124 | next; |
1145 | next; |
1125 | }
|
1146 | }
|
1126 | 1147 | ||
1127 | ##
|
1148 | ##
|
1128 | ## remarks for significant cloud types
|
1149 | ## remarks for significant cloud types
|
1129 | ##
|
1150 | ##
|
1130 | 1151 | ||
1131 | elsif (($parsestate >= $expect_recentweather) and ($tok eq "CB" || $tok eq "TCU")) |
1152 | elsif (($parsestate >= $expect_recentweather) and ($tok eq "CB" || $tok eq "TCU")) |
1132 | {
|
1153 | {
|
1133 | push(@{$self->{sigclouds}}, $tok); |
1154 | push(@{$self->{sigclouds}}, $tok); |
1134 | 1155 | ||
1135 | if ( $tok eq "CB" ) { |
1156 | if ( $tok eq "CB" ) { |
1136 | push(@{$self->{SIGCLOUDS}}, "Cumulonimbus"); |
1157 | push(@{$self->{SIGCLOUDS}}, "Cumulonimbus"); |
1137 | } elsif ( $tok eq "TCU" ) { |
1158 | } elsif ( $tok eq "TCU" ) { |
1138 | push(@{$self->{SIGCLOUDS}}, "Towering Cumulus"); |
1159 | push(@{$self->{SIGCLOUDS}}, "Towering Cumulus"); |
1139 | }
|
1160 | }
|
1140 | $parsestate = $expect_usremarks; |
1161 | $parsestate = $expect_usremarks; |
1141 | }
|
1162 | }
|
1142 | 1163 | ||
1143 | ##
|
1164 | ##
|
1144 | ## hourly temp/dewpoint
|
1165 | ## hourly temp/dewpoint
|
1145 | ##
|
1166 | ##
|
1146 | 1167 | ||
1147 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^T(\d)(\d\d)(\d)(\d)(\d\d)(\d)$/i)) |
1168 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^T(\d)(\d\d)(\d)(\d)(\d\d)(\d)$/i)) |
1148 | {
|
1169 | {
|
1149 | $self->{hourlytempdew} = $tok; |
1170 | $self->{hourlytempdew} = $tok; |
1150 | if ( $1 == 1 ) { |
1171 | if ( $1 == 1 ) { |
1151 | $self->{HOURLY_TEMP_C} = "-"; |
1172 | $self->{HOURLY_TEMP_C} = "-"; |
1152 | }
|
1173 | }
|
1153 | $self->{HOURLY_TEMP_C} .= "$2.$3"; |
1174 | $self->{HOURLY_TEMP_C} .= "$2.$3"; |
1154 | 1175 | ||
1155 | $self->{HOURLY_DEW_C} = ""; |
1176 | $self->{HOURLY_DEW_C} = ""; |
1156 | if ( $4 == 1 ) { |
1177 | if ( $4 == 1 ) { |
1157 | $self->{HOURLY_DEW_C} = "-"; |
1178 | $self->{HOURLY_DEW_C} = "-"; |
1158 | }
|
1179 | }
|
1159 | $self->{HOURLY_DEW_C} .= "$5.$6"; |
1180 | $self->{HOURLY_DEW_C} .= "$5.$6"; |
1160 | 1181 | ||
1161 | print "[$tok] is a hourly temp and dewpoint.\n" if $self->{debug}; |
1182 | print "[$tok] is a hourly temp and dewpoint.\n" if $self->{debug}; |
1162 | next; |
1183 | next; |
1163 | }
|
1184 | }
|
1164 | 1185 | ||
1165 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^QFE(\d\d\d)/i)) |
1186 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^QFE(\d\d\d)/i)) |
1166 | {
|
1187 | {
|
1167 | $self->{ALT_PL} = $1; |
1188 | $self->{ALT_PL} = $1; |
1168 | 1189 | ||
1169 | print "[$tok] is a pressure\n" if $self->{debug}; |
1190 | print "[$tok] is a pressure\n" if $self->{debug}; |
1170 | next; |
1191 | next; |
1171 | }
|
1192 | }
|
1172 | 1193 | ||
1173 | ##
|
1194 | ##
|
1174 | ## unknown, not in remarks yet
|
1195 | ## unknown, not in remarks yet
|
1175 | ##
|
1196 | ##
|
1176 | 1197 | ||
1177 | elsif ($parsestate < $expect_remarks) |
1198 | elsif ($parsestate < $expect_remarks) |
1178 | {
|
1199 | {
|
1179 | push(@{$self->{unknown}},$tok); |
1200 | push(@{$self->{unknown}},$tok); |
1180 | push(@{$self->{UNKNOWN}},$tok); |
1201 | push(@{$self->{UNKNOWN}},$tok); |
1181 | print "[$tok] is unexpected at this state.\n" if $self->{debug}; |
1202 | print "[$tok] is unexpected at this state.\n" if $self->{debug}; |
1182 | next; |
1203 | next; |
1183 | }
|
1204 | }
|
1184 | 1205 | ||
1185 | ##
|
1206 | ##
|
1186 | ## unknown. assume remarks
|
1207 | ## unknown. assume remarks
|
1187 | ##
|
1208 | ##
|
1188 | 1209 | ||
1189 | else
|
1210 | else
|
1190 | {
|
1211 | {
|
1191 | push(@{$self->{remarks}},$tok); |
1212 | push(@{$self->{remarks}},$tok); |
1192 | push(@{$self->{REMARKS}},$tok); |
1213 | push(@{$self->{REMARKS}},$tok); |
1193 | print "[$tok] is unknown remark.\n" if $self->{debug}; |
1214 | print "[$tok] is unknown remark.\n" if $self->{debug}; |
1194 | next; |
1215 | next; |
1195 | }
|
1216 | }
|
1196 | 1217 | ||
1197 | }
|
1218 | }
|
1198 | 1219 | ||
1199 | ##
|
1220 | ##
|
1200 | ## Now that the internal stuff is set, let's do the external
|
1221 | ## Now that the internal stuff is set, let's do the external
|
1201 | ## stuff.
|
1222 | ## stuff.
|
1202 | ##
|
1223 | ##
|
1203 | 1224 | ||
1204 | $self->{SITE} = $self->{site}; |
1225 | $self->{SITE} = $self->{site}; |
1205 | $self->{DATE} = substr($self->{date_time},0,2); |
1226 | $self->{DATE} = substr($self->{date_time},0,2); |
1206 | $self->{TIME} = substr($self->{date_time},2,4) . " UTC"; |
1227 | $self->{TIME} = substr($self->{date_time},2,4) . " UTC"; |
1207 | $self->{TIME} =~ s/(\d\d)(\d\d)/$1:$2/o; |
1228 | $self->{TIME} =~ s/(\d\d)(\d\d)/$1:$2/o; |
1208 | $self->{MOD} = $self->{modifier}; |
1229 | $self->{MOD} = $self->{modifier}; |
1209 | 1230 | ||
1210 | ##
|
1231 | ##
|
1211 | ## Okay, wind finally gets interesting.
|
1232 | ## Okay, wind finally gets interesting.
|
1212 | ##
|
1233 | ##
|
1213 | 1234 | ||
1214 | if ( defined $self->{wind} ) |
1235 | if ( defined $self->{wind} ) |
1215 | {
|
1236 | {
|
1216 | my $wind = $self->{wind}; |
1237 | my $wind = $self->{wind}; |
1217 | my $dir_deg = substr($wind,0,3); |
1238 | my $dir_deg = substr($wind,0,3); |
1218 | my $wind_speed; |
1239 | my $wind_speed; |
1219 | my $dir_eng = ""; |
1240 | my $dir_eng = ""; |
1220 | my $dir_rus = ""; |
1241 | my $dir_rus = ""; |
1221 | my $dir_abb = ""; |
1242 | my $dir_abb = ""; |
1222 | 1243 | ||
1223 | $wind_speed = $1 if($wind =~ /...(\d{2,3})/o); |
1244 | $wind_speed = $1 if($wind =~ /...(\d{2,3})/o); |
1224 | # Check for wind direction
|
1245 | # Check for wind direction
|
1225 | if ($dir_deg =~ /VRB/i) { |
1246 | if ($dir_deg =~ /VRB/i) { |
1226 | $dir_deg = $dir_eng = "Variable"; |
1247 | $dir_deg = $dir_eng = "Variable"; |
1227 | $dir_rus = "переменный"; |
1248 | $dir_rus = "переменный"; |
1228 | } else { |
1249 | } else { |
1229 | if ($wind_speed == 0 and $dir_deg == 0) { |
1250 | if ($wind_speed == 0 and $dir_deg == 0) { |
1230 | # Calm wind (00000KT in METAR)
|
1251 | # Calm wind (00000KT in METAR)
|
1231 | $dir_eng = "Calm"; |
1252 | $dir_eng = "Calm"; |
1232 | $dir_rus = "штиль"; |
1253 | $dir_rus = "штиль"; |
1233 | print "wind is calm\n" if $self->{debug}; |
1254 | print "wind is calm\n" if $self->{debug}; |
1234 | } elsif ($dir_deg < 15) { |
1255 | } elsif ($dir_deg < 15) { |
1235 | $dir_eng = "North"; |
1256 | $dir_eng = "North"; |
1236 | $dir_abb = "N"; |
1257 | $dir_abb = "N"; |
1237 | $dir_rus = "северный"; |
1258 | $dir_rus = "северный"; |
1238 | } elsif ($dir_deg < 30) { |
1259 | } elsif ($dir_deg < 30) { |
1239 | $dir_eng = "North/Northeast"; |
1260 | $dir_eng = "North/Northeast"; |
1240 | $dir_abb = "NNE"; |
1261 | $dir_abb = "NNE"; |
1241 | $dir_rus = "северо-северо-восточный"; |
1262 | $dir_rus = "северо-северо-восточный"; |
1242 | } elsif ($dir_deg < 60) { |
1263 | } elsif ($dir_deg < 60) { |
1243 | $dir_eng = "Northeast"; |
1264 | $dir_eng = "Northeast"; |
1244 | $dir_abb = "NE"; |
1265 | $dir_abb = "NE"; |
1245 | $dir_rus = "северо-восточный"; |
1266 | $dir_rus = "северо-восточный"; |
1246 | } elsif ($dir_deg < 75) { |
1267 | } elsif ($dir_deg < 75) { |
1247 | $dir_eng = "East/Northeast"; |
1268 | $dir_eng = "East/Northeast"; |
1248 | $dir_abb = "ENE"; |
1269 | $dir_abb = "ENE"; |
1249 | $dir_rus = "восточный-северо-восточный"; |
1270 | $dir_rus = "восточный-северо-восточный"; |
1250 | } elsif ($dir_deg < 105) { |
1271 | } elsif ($dir_deg < 105) { |
1251 | $dir_eng = "East"; |
1272 | $dir_eng = "East"; |
1252 | $dir_abb = "E"; |
1273 | $dir_abb = "E"; |
1253 | $dir_rus = "восточный"; |
1274 | $dir_rus = "восточный"; |
1254 | } elsif ($dir_deg < 120) { |
1275 | } elsif ($dir_deg < 120) { |
1255 | $dir_eng = "East/Southeast"; |
1276 | $dir_eng = "East/Southeast"; |
1256 | $dir_abb = "ESE"; |
1277 | $dir_abb = "ESE"; |
1257 | $dir_rus = "восточный-юго-восточный"; |
1278 | $dir_rus = "восточный-юго-восточный"; |
1258 | } elsif ($dir_deg < 150) { |
1279 | } elsif ($dir_deg < 150) { |
1259 | $dir_eng = "Southeast"; |
1280 | $dir_eng = "Southeast"; |
1260 | $dir_abb = "SE"; |
1281 | $dir_abb = "SE"; |
1261 | $dir_rus = "юго-восточный"; |
1282 | $dir_rus = "юго-восточный"; |
1262 | } elsif ($dir_deg < 165) { |
1283 | } elsif ($dir_deg < 165) { |
1263 | $dir_eng = "South/Southeast"; |
1284 | $dir_eng = "South/Southeast"; |
1264 | $dir_abb = "SSE"; |
1285 | $dir_abb = "SSE"; |
1265 | $dir_rus = "юго-юго-восточный"; |
1286 | $dir_rus = "юго-юго-восточный"; |
1266 | } elsif ($dir_deg < 195) { |
1287 | } elsif ($dir_deg < 195) { |
1267 | $dir_eng = "South"; |
1288 | $dir_eng = "South"; |
1268 | $dir_abb = "S"; |
1289 | $dir_abb = "S"; |
1269 | $dir_rus = "южный"; |
1290 | $dir_rus = "южный"; |
1270 | } elsif ($dir_deg < 210) { |
1291 | } elsif ($dir_deg < 210) { |
1271 | $dir_eng = "South/Southwest"; |
1292 | $dir_eng = "South/Southwest"; |
1272 | $dir_abb = "SSW"; |
1293 | $dir_abb = "SSW"; |
1273 | $dir_rus = "юго-юго-западный" |
1294 | $dir_rus = "юго-юго-западный" |
1274 | } elsif ($dir_deg < 240) { |
1295 | } elsif ($dir_deg < 240) { |
1275 | $dir_eng = "Southwest"; |
1296 | $dir_eng = "Southwest"; |
1276 | $dir_abb = "SW"; |
1297 | $dir_abb = "SW"; |
1277 | $dir_rus = "юго-западный"; |
1298 | $dir_rus = "юго-западный"; |
1278 | } elsif ($dir_deg < 265) { |
1299 | } elsif ($dir_deg < 265) { |
1279 | $dir_eng = "West/Southwest"; |
1300 | $dir_eng = "West/Southwest"; |
1280 | $dir_abb = "WSW"; |
1301 | $dir_abb = "WSW"; |
1281 | $dir_rus = "западно-юго-западный"; |
1302 | $dir_rus = "западно-юго-западный"; |
1282 | } elsif ($dir_deg < 285) { |
1303 | } elsif ($dir_deg < 285) { |
1283 | $dir_eng = "West"; |
1304 | $dir_eng = "West"; |
1284 | $dir_abb = "W"; |
1305 | $dir_abb = "W"; |
1285 | $dir_rus = "западный"; |
1306 | $dir_rus = "западный"; |
1286 | } elsif ($dir_deg < 300) { |
1307 | } elsif ($dir_deg < 300) { |
1287 | $dir_eng = "West/Northwest"; |
1308 | $dir_eng = "West/Northwest"; |
1288 | $dir_abb = "WNW"; |
1309 | $dir_abb = "WNW"; |
1289 | $dir_rus = "западно-северо-западный"; |
1310 | $dir_rus = "западно-северо-западный"; |
1290 | } elsif ($dir_deg < 330) { |
1311 | } elsif ($dir_deg < 330) { |
1291 | $dir_eng = "Northwest"; |
1312 | $dir_eng = "Northwest"; |
1292 | $dir_abb = "NW"; |
1313 | $dir_abb = "NW"; |
1293 | $dir_rus = "северо-западный"; |
1314 | $dir_rus = "северо-западный"; |
1294 | } elsif ($dir_deg < 345) { |
1315 | } elsif ($dir_deg < 345) { |
1295 | $dir_eng = "North/Northwest"; |
1316 | $dir_eng = "North/Northwest"; |
1296 | $dir_abb = "NNW"; |
1317 | $dir_abb = "NNW"; |
1297 | $dir_rus = "северо-северо-западный"; |
1318 | $dir_rus = "северо-северо-западный"; |
1298 | } elsif ($dir_deg < 360) { |
1319 | } elsif ($dir_deg < 360) { |
1299 | $dir_eng = "North"; |
1320 | $dir_eng = "North"; |
1300 | $dir_abb = "N"; |
1321 | $dir_abb = "N"; |
1301 | $dir_rus = "северный"; |
1322 | $dir_rus = "северный"; |
1302 | } else { |
1323 | } else { |
1303 | # Shouldn't happen, but if for some reason the METAR
|
1324 | # Shouldn't happen, but if for some reason the METAR
|
1304 | # information doesn't contain a reasonable direction...
|
1325 | # information doesn't contain a reasonable direction...
|
1305 | $dir_eng = "undeterminable"; |
1326 | $dir_eng = "undeterminable"; |
1306 | $dir_rus = "неопределенный"; |
1327 | $dir_rus = "неопределенный"; |
1307 | }
|
1328 | }
|
1308 | }
|
1329 | }
|
1309 | 1330 | ||
1310 | my $kts_speed = undef; |
1331 | my $kts_speed = undef; |
1311 | my $mph_speed = undef; |
1332 | my $mph_speed = undef; |
1312 | my $mps_speed = undef; |
1333 | my $mps_speed = undef; |
1313 | 1334 | ||
1314 | my $kts_gust = ""; |
1335 | my $kts_gust = ""; |
1315 | my $mph_gust = ""; |
1336 | my $mph_gust = ""; |
1316 | my $mps_gust = ""; |
1337 | my $mps_gust = ""; |
1317 | 1338 | ||
1318 | # parse knots
|
1339 | # parse knots
|
1319 | 1340 | ||
1320 | if ($self->{windtype} == $wt_knots){ |
1341 | if ($self->{windtype} == $wt_knots){ |
1321 | $wind =~ /...(\d\d\d?)/o; |
1342 | $wind =~ /...(\d\d\d?)/o; |
1322 | $kts_speed = $1; |
1343 | $kts_speed = $1; |
1323 | $mph_speed = $kts_speed * 1.15077945; |
1344 | $mph_speed = $kts_speed * 1.15077945; |
1324 | $mps_speed = $kts_speed * 0.514444444; |
1345 | $mps_speed = $kts_speed * 0.514444444; |
1325 | 1346 | ||
1326 | if ($wind =~ /.{5,6}G(\d\d\d?)/o) { |
1347 | if ($wind =~ /.{5,6}G(\d\d\d?)/o) { |
1327 | $kts_gust = $1; |
1348 | $kts_gust = $1; |
1328 | $mph_gust = $kts_gust * 1.15077945; |
1349 | $mph_gust = $kts_gust * 1.15077945; |
1329 | $mps_gust = $kts_gust * 0.514444444; |
1350 | $mps_gust = $kts_gust * 0.514444444; |
1330 | }
|
1351 | }
|
1331 | # else: parse meters/second
|
1352 | # else: parse meters/second
|
1332 | } elsif ($self->{windtype} == $wt_mps){ |
1353 | } elsif ($self->{windtype} == $wt_mps){ |
1333 | $wind=~ /...(\d\d\d?)/o; |
1354 | $wind=~ /...(\d\d\d?)/o; |
1334 | $mps_speed = $1; |
1355 | $mps_speed = $1; |
1335 | $kts_speed = $mps_speed * 1.9438445; # units |
1356 | $kts_speed = $mps_speed * 1.9438445; # units |
1336 | $mph_speed = $mps_speed * 2.2369363; |
1357 | $mph_speed = $mps_speed * 2.2369363; |
1337 | if ($wind =~ /\d{5,6}G(\d\d\d?)/o) { |
1358 | if ($wind =~ /\d{5,6}G(\d\d\d?)/o) { |
1338 | $mps_gust = $1; |
1359 | $mps_gust = $1; |
1339 | $kts_gust = $mps_gust * 1.9438445; |
1360 | $kts_gust = $mps_gust * 1.9438445; |
1340 | $mph_gust = $mps_gust * 2.2369363; |
1361 | $mph_gust = $mps_gust * 2.2369363; |
1341 | }
|
1362 | }
|
1342 | } else { |
1363 | } else { |
1343 | warn "Geo::ModMETAR Parser error: unknown windtype\n"; |
1364 | warn "Geo::ModMETAR Parser error: unknown windtype\n"; |
1344 | }
|
1365 | }
|
1345 | 1366 | ||
1346 | $self->{WIND_KTS} = $kts_speed; |
1367 | $self->{WIND_KTS} = $kts_speed; |
1347 | $self->{WIND_MPH} = $mph_speed; |
1368 | $self->{WIND_MPH} = $mph_speed; |
1348 | $self->{WIND_MS} = $mps_speed; |
1369 | $self->{WIND_MS} = $mps_speed; |
1349 | 1370 | ||
1350 | $self->{WIND_GUST_KTS} = $kts_gust; |
1371 | $self->{WIND_GUST_KTS} = $kts_gust; |
1351 | $self->{WIND_GUST_MPH} = $mph_gust; |
1372 | $self->{WIND_GUST_MPH} = $mph_gust; |
1352 | $self->{WIND_GUST_MS} = $mps_gust; |
1373 | $self->{WIND_GUST_MS} = $mps_gust; |
1353 | 1374 | ||
1354 | $self->{WIND_DIR_DEG} = $dir_deg; |
1375 | $self->{WIND_DIR_DEG} = $dir_deg; |
1355 | $self->{WIND_DIR_ENG} = $dir_eng; |
1376 | $self->{WIND_DIR_ENG} = $dir_eng; |
1356 | $self->{WIND_DIR_ABB} = $dir_abb; |
1377 | $self->{WIND_DIR_ABB} = $dir_abb; |
1357 | $self->{WIND_DIR_RUS} = $dir_rus; |
1378 | $self->{WIND_DIR_RUS} = $dir_rus; |
1358 | 1379 | ||
1359 | }
|
1380 | }
|
1360 | 1381 | ||
1361 | ##
|
1382 | ##
|
1362 | ## wind variation
|
1383 | ## wind variation
|
1363 | ##
|
1384 | ##
|
1364 | 1385 | ||
1365 | if (defined $self->{windvar}) |
1386 | if (defined $self->{windvar}) |
1366 | {
|
1387 | {
|
1367 | if ($self->{windvar} =~ /^(\d\d\d)V(\d\d\d)$/){ |
1388 | if ($self->{windvar} =~ /^(\d\d\d)V(\d\d\d)$/){ |
1368 | $self->{WIND_VAR} = "Varying between $1 and $2"; |
1389 | $self->{WIND_VAR} = "Varying between $1 and $2"; |
1369 | $self->{WIND_VAR_1} = $1; |
1390 | $self->{WIND_VAR_1} = $1; |
1370 | $self->{WIND_VAR_2} = $2; |
1391 | $self->{WIND_VAR_2} = $2; |
1371 | my @direction = ( |
1392 | my @direction = ( |
1372 | 15 => "North", |
1393 | 15 => "North", |
1373 | 30 => "North/Northeast", |
1394 | 30 => "North/Northeast", |
1374 | 60 => "Northeast", |
1395 | 60 => "Northeast", |
1375 | 75 => "East/Northeast", |
1396 | 75 => "East/Northeast", |
1376 | 105 => "East", |
1397 | 105 => "East", |
1377 | 120 => "East/Southeast", |
1398 | 120 => "East/Southeast", |
1378 | 150 => "Southeast", |
1399 | 150 => "Southeast", |
1379 | 165 => "South/Southeast", |
1400 | 165 => "South/Southeast", |
1380 | 195 => "South", |
1401 | 195 => "South", |
1381 | 210 => "South/Southwest", |
1402 | 210 => "South/Southwest", |
1382 | 240 => "Southwest", |
1403 | 240 => "Southwest", |
1383 | 265 => "West/Southwest", |
1404 | 265 => "West/Southwest", |
1384 | 285 => "West", |
1405 | 285 => "West", |
1385 | 300 => "West/Northwest", |
1406 | 300 => "West/Northwest", |
1386 | 330 => "Northwest", |
1407 | 330 => "Northwest", |
1387 | 345 => "North/Northwest", |
1408 | 345 => "North/Northwest", |
1388 | 360 => "North", |
1409 | 360 => "North", |
1389 | 1000 => "undeterminable"); |
1410 | 1000 => "undeterminable"); |
1390 | for(my $x = 0; $x < $#direction; $x += 2) { |
1411 | for(my $x = 0; $x < $#direction; $x += 2) { |
1391 | if($self->{WIND_VAR_1} < $direction[$x]) { |
1412 | if($self->{WIND_VAR_1} < $direction[$x]) { |
1392 | $self->{WIND_VAR_ENG_1} = $direction[$x+1]; |
1413 | $self->{WIND_VAR_ENG_1} = $direction[$x+1]; |
1393 | last; |
1414 | last; |
1394 | }
|
1415 | }
|
1395 | }
|
1416 | }
|
1396 | for(my $x = 0; $x < $#direction; $x += 2) { |
1417 | for(my $x = 0; $x < $#direction; $x += 2) { |
1397 | if($self->{WIND_VAR_2} < $direction[$x]) { |
1418 | if($self->{WIND_VAR_2} < $direction[$x]) { |
1398 | $self->{WIND_VAR_ENG_2} = $direction[$x+1]; |
1419 | $self->{WIND_VAR_ENG_2} = $direction[$x+1]; |
1399 | last; |
1420 | last; |
1400 | }
|
1421 | }
|
1401 | }
|
1422 | }
|
1402 | }
|
1423 | }
|
1403 | }
|
1424 | }
|
1404 | 1425 | ||
1405 | ##
|
1426 | ##
|
1406 | ## Calculate relative humidity
|
1427 | ## Calculate relative humidity
|
1407 | ##
|
1428 | ##
|
1408 | 1429 | ||
1409 | {
|
1430 | {
|
1410 | my $esat = 6.11*(10**((7.5*$self->{TEMP_C})/(237.7+$self->{TEMP_C}))); |
1431 | my $esat = 6.11*(10**((7.5*$self->{TEMP_C})/(237.7+$self->{TEMP_C}))); |
1411 | my $esurf = 6.11*(10**((7.5*$self->{DEW_C})/(237.7+$self->{DEW_C}))); |
1432 | my $esurf = 6.11*(10**((7.5*$self->{DEW_C})/(237.7+$self->{DEW_C}))); |
1412 | 1433 | ||
1413 | $self->{RH} = 100.0 * ($esurf/$esat); |
1434 | $self->{RH} = 100.0 * ($esurf/$esat); |
1414 | }
|
1435 | }
|
1415 | 1436 | ||
1416 | ##
|
1437 | ##
|
1417 | ## Calculate windchill temperature
|
1438 | ## Calculate windchill temperature
|
1418 | ##
|
1439 | ##
|
1419 | 1440 | ||
1420 | {
|
1441 | {
|
1421 | my $windspeed = $self->{WIND_MS}*3.6; |
1442 | my $windspeed = $self->{WIND_MS}*3.6; |
1422 | $self->{TEMP_WC} = 13.12 + 0.6215*$self->{TEMP_C} - 11.37*($windspeed**0.16) + 0.3965*$self->{TEMP_C}*($windspeed**0.16); |
1443 | $self->{TEMP_WC} = 13.12 + 0.6215*$self->{TEMP_C} - 11.37*($windspeed**0.16) + 0.3965*$self->{TEMP_C}*($windspeed**0.16); |
1423 | }
|
1444 | }
|
1424 | 1445 | ||
1425 | ##
|
1446 | ##
|
1426 | ## Visibility.
|
1447 | ## Visibility.
|
1427 | ##
|
1448 | ##
|
1428 | 1449 | ||
1429 | if($self->{visibility}) { |
1450 | if($self->{visibility}) { |
1430 | my $vis = $self->{visibility}; |
1451 | my $vis = $self->{visibility}; |
1431 | # test for statute miles
|
1452 | # test for statute miles
|
1432 | if ($vis =~ /SM$/){ |
1453 | if ($vis =~ /SM$/){ |
1433 | $vis =~ s/SM$//oi; # nuke the "SM" |
1454 | $vis =~ s/SM$//oi; # nuke the "SM" |
1434 | if ($vis =~ /M(\d\/\d)/o) { |
1455 | if ($vis =~ /M(\d\/\d)/o) { |
1435 | $self->{VISIBILITY} = "Less than $1 statute miles"; |
1456 | $self->{VISIBILITY} = "Less than $1 statute miles"; |
1436 | $self->{VISIBILITY_RUS} = "Менее чем $1 статутных миль"; |
1457 | $self->{VISIBILITY_RUS} = "Менее чем $1 статутных миль"; |
1437 | } else { |
1458 | } else { |
1438 | $self->{VISIBILITY} = $vis . " statute miles"; |
1459 | $self->{VISIBILITY} = $vis . " statute miles"; |
1439 | $self->{VISIBILITY} = $vis . " статутных миль"; |
1460 | $self->{VISIBILITY} = $vis . " статутных миль"; |
1440 | } # end if |
1461 | } # end if |
1441 | # auto metars can have non-directional visibility reports
|
1462 | # auto metars can have non-directional visibility reports
|
1442 | } elsif (($self->{MOD} eq 'AUTO') and ($vis =~ /(\d+)NDV$/)){ |
1463 | } elsif (($self->{MOD} eq 'AUTO') and ($vis =~ /(\d+)NDV$/)){ |
1443 | $self->{VISIBILITY} = "$1 meters non-directional visibility"; |
1464 | $self->{VISIBILITY} = "$1 meters non-directional visibility"; |
1444 | $self->{VISIBILITY_RUS} = "$1 м непрямой видимости"; |
1465 | $self->{VISIBILITY_RUS} = "$1 м непрямой видимости"; |
1445 | } else { |
1466 | } else { |
1446 | $self->{VISIBILITY} = $vis . " meters"; |
1467 | $self->{VISIBILITY} = $vis . " meters"; |
1447 | if ($vis<1000) { |
1468 | if ($vis<1000) { |
1448 | $self->{VISIBILITY_RUS} = $vis . " м"; |
1469 | $self->{VISIBILITY_RUS} = $vis . " м"; |
1449 | } else { |
1470 | } else { |
1450 | $vis = $vis/1000; |
1471 | $vis = $vis/1000; |
1451 | if (abs($vis-int($vis))>=0.5) { |
1472 | if (abs($vis-int($vis))>=0.5) { |
1452 | $vis = int($vis)+1; |
1473 | $vis = int($vis)+1; |
1453 | } else { |
1474 | } else { |
1454 | $vis = int($vis); |
1475 | $vis = int($vis); |
1455 | }
|
1476 | }
|
1456 | $self->{VISIBILITY_RUS} = $vis . " км"; |
1477 | $self->{VISIBILITY_RUS} = $vis . " км"; |
1457 | }
|
1478 | }
|
1458 | }
|
1479 | }
|
1459 | }
|
1480 | }
|
1460 | 1481 | ||
1461 | ##
|
1482 | ##
|
1462 | ## Calculate F temps for all C temps
|
1483 | ## Calculate F temps for all C temps
|
1463 | ##
|
1484 | ##
|
1464 | 1485 | ||
1465 | foreach my $key ( keys(%$self) ) |
1486 | foreach my $key ( keys(%$self) ) |
1466 | {
|
1487 | {
|
1467 | if ( uc($key) eq $key && $key =~ /^(.*)_C$/ ) |
1488 | if ( uc($key) eq $key && $key =~ /^(.*)_C$/ ) |
1468 | {
|
1489 | {
|
1469 | my $fkey = $1 . "_F"; |
1490 | my $fkey = $1 . "_F"; |
1470 | 1491 | ||
1471 | next unless defined $self->{$key} && $self->{$key}; |
1492 | next unless defined $self->{$key} && $self->{$key}; |
1472 | 1493 | ||
1473 | $self->{$fkey} = sprintf("%.1f", (($self->{$key} * (9/5)) + 32)); |
1494 | $self->{$fkey} = sprintf("%.1f", (($self->{$key} * (9/5)) + 32)); |
1474 | }
|
1495 | }
|
1475 | }
|
1496 | }
|
1476 | 1497 | ||
1477 | # join the runway group
|
1498 | # join the runway group
|
1478 | 1499 | ||
1479 | $self->{runway} = join(', ' , @{$self->{RUNWAY}}); |
1500 | $self->{runway} = join(', ' , @{$self->{RUNWAY}}); |
1480 | 1501 | ||
1481 | }
|
1502 | }
|
1482 | 1503 | ||
1483 | ##
|
1504 | ##
|
1484 | ## Print the tokens--usually when debugging.
|
1505 | ## Print the tokens--usually when debugging.
|
1485 | ##
|
1506 | ##
|
1486 | 1507 | ||
1487 | sub print_tokens
|
1508 | sub print_tokens
|
1488 | {
|
1509 | {
|
1489 | my $self = shift; |
1510 | my $self = shift; |
1490 | my $tok; |
1511 | my $tok; |
1491 | foreach $tok (@{$self->{tokens}}) { |
1512 | foreach $tok (@{$self->{tokens}}) { |
1492 | print "> $tok\n"; |
1513 | print "> $tok\n"; |
1493 | }
|
1514 | }
|
1494 | }
|
1515 | }
|
1495 | 1516 | ||
1496 | ##
|
1517 | ##
|
1497 | ## Turn debugging on/off.
|
1518 | ## Turn debugging on/off.
|
1498 | ##
|
1519 | ##
|
1499 | 1520 | ||
1500 | sub debug
|
1521 | sub debug
|
1501 | {
|
1522 | {
|
1502 | my $self = shift; |
1523 | my $self = shift; |
1503 | my $flag = shift; |
1524 | my $flag = shift; |
1504 | return $self->{debug} unless defined $flag; |
1525 | return $self->{debug} unless defined $flag; |
1505 | 1526 | ||
1506 | if (($flag eq "Y") or ($flag eq "y") or ($flag == 1)) { |
1527 | if (($flag eq "Y") or ($flag eq "y") or ($flag == 1)) { |
1507 | $self->{debug} = 1; |
1528 | $self->{debug} = 1; |
1508 | } elsif (($flag eq "N") or ($flag eq "n") or ($flag == 0)) { |
1529 | } elsif (($flag eq "N") or ($flag eq "n") or ($flag == 0)) { |
1509 | $self->{debug} = 0; |
1530 | $self->{debug} = 0; |
1510 | }
|
1531 | }
|
1511 | 1532 | ||
1512 | return $self->{debug}; |
1533 | return $self->{debug}; |
1513 | }
|
1534 | }
|
1514 | 1535 | ||
1515 | ##
|
1536 | ##
|
1516 | ## Dump internal data structure. Useful for debugging and such.
|
1537 | ## Dump internal data structure. Useful for debugging and such.
|
1517 | ##
|
1538 | ##
|
1518 | 1539 | ||
1519 | sub dump |
1540 | sub dump |
1520 | {
|
1541 | {
|
1521 | my $self = shift; |
1542 | my $self = shift; |
1522 | 1543 | ||
1523 | print "Modified METAR dump follows.\n\n"; |
1544 | print "Modified METAR dump follows.\n\n"; |
1524 | 1545 | ||
1525 | print "type: $self->{type}\n"; |
1546 | print "type: $self->{type}\n"; |
1526 | print "site: $self->{site}\n"; |
1547 | print "site: $self->{site}\n"; |
1527 | print "date_time: $self->{date_time}\n"; |
1548 | print "date_time: $self->{date_time}\n"; |
1528 | print "modifier: $self->{modifier}\n"; |
1549 | print "modifier: $self->{modifier}\n"; |
1529 | print "wind: $self->{wind}\n"; |
1550 | print "wind: $self->{wind}\n"; |
1530 | print "variable wind: $self->{vrbwind}\n"; |
1551 | print "variable wind: $self->{vrbwind}\n"; |
1531 | print "visibility: $self->{visibility}\n"; |
1552 | print "visibility: $self->{visibility}\n"; |
1532 | print "runway: $self->{runway}\n"; |
1553 | print "runway: $self->{runway}\n"; |
1533 | print "weather: " . join(', ', @{$self->{weather}}) . "\n"; |
1554 | print "weather: " . join(', ', @{$self->{weather}}) . "\n"; |
1534 | print "sky: " . join(', ', @{$self->{sky}}) . "\n"; |
1555 | print "sky: " . join(', ', @{$self->{sky}}) . "\n"; |
1535 | print "temp_dew: $self->{temp_dew}\n"; |
1556 | print "temp_dew: $self->{temp_dew}\n"; |
1536 | print "alt: $self->{alt}\n"; |
1557 | print "alt: $self->{alt}\n"; |
1537 | print "pressure: $self->{pressure}\n"; |
1558 | print "pressure: $self->{pressure}\n"; |
1538 | print "slp: $self->{slp}\n"; |
1559 | print "slp: $self->{slp}\n"; |
1539 | print "remarks: " . join (', ', @{$self->{remarks}}) . "\n"; |
1560 | print "remarks: " . join (', ', @{$self->{remarks}}) . "\n"; |
1540 | print "\n"; |
1561 | print "\n"; |
1541 | 1562 | ||
1542 | foreach my $var ( sort(keys(%$self)) ) |
1563 | foreach my $var ( sort(keys(%$self)) ) |
1543 | {
|
1564 | {
|
1544 | next if ( uc($var) ne $var ); |
1565 | next if ( uc($var) ne $var ); |
1545 | 1566 | ||
1546 | if ( ref($self->{$var}) eq "ARRAY" ) |
1567 | if ( ref($self->{$var}) eq "ARRAY" ) |
1547 | {
|
1568 | {
|
1548 | print "$var: ", join(", ", @{$self->{$var}}), "\n"; |
1569 | print "$var: ", join(", ", @{$self->{$var}}), "\n"; |
1549 | }
|
1570 | }
|
1550 | else
|
1571 | else
|
1551 | {
|
1572 | {
|
1552 | print "$var: ", $self->{$var}, "\n"; |
1573 | print "$var: ", $self->{$var}, "\n"; |
1553 | }
|
1574 | }
|
1554 | }
|
1575 | }
|
1555 | }
|
1576 | }
|
1556 | 1577 | ||
1557 | 1; |
1578 | 1; |
1558 | 1579 | ||
1559 | __END__
|
1580 | __END__
|
1560 | 1581 | ||
1561 | =head1 NAME
|
1582 | =head1 NAME
|
1562 | 1583 | ||
1563 | Mod::Geo::METAR - Process aviation weather reports in the METAR format.
|
1584 | Geo::ModMETAR - Process aviation weather reports in the METAR format.
|
1564 | 1585 | ||
1565 | =head1 SYNOPSIS
|
1586 | =head1 SYNOPSIS
|
1566 | 1587 | ||
1567 | use Mod::Geo::METAR;
|
1588 | use Geo::ModMETAR;
|
1568 | use strict;
|
1589 | use strict;
|
1569 | 1590 | ||
1570 | my $m = new Mod::Geo::METAR;
|
1591 | my $m = new Geo::ModMETAR;
|
1571 | $m->metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");
|
1592 | $m->metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");
|
1572 | print $m->dump;
|
1593 | print $m->dump;
|
1573 | 1594 | ||
1574 | exit;
|
1595 | exit;
|
1575 | 1596 | ||
1576 | =head1 DESCRIPTION
|
1597 | =head1 DESCRIPTION
|
1577 | 1598 | ||
1578 | METAR reports are available on-line, thanks to the National Weather Service.
|
1599 | METAR reports are available on-line, thanks to the National Weather Service.
|
1579 | Since reading the METAR format isn't easy for non-pilots, these reports are
|
1600 | Since reading the METAR format isn't easy for non-pilots, these reports are
|
1580 | relatively useles to the common man who just wants a quick glace at the
|
1601 | relatively useles to the common man who just wants a quick glace at the
|
1581 | weather. This module tries to parse the METAR reports so the data can be
|
1602 | weather. This module tries to parse the METAR reports so the data can be
|
1582 | used to create readable weather reports and/or process the data in
|
1603 | used to create readable weather reports and/or process the data in
|
1583 | applications.
|
1604 | applications.
|
1584 | 1605 | ||
1585 | =head1 USAGE
|
1606 | =head1 USAGE
|
1586 | 1607 | ||
1587 | =head2 How you might use this
|
1608 | =head2 How you might use this
|
1588 | 1609 | ||
1589 | Here is how you I<might> use the Geo::METAR module.
|
1610 | Here is how you I<might> use the Geo::ModMETAR module.
|
1590 | 1611 | ||
1591 | One use that I have had for this module is to query the NWS METAR page
|
1612 | One use that I have had for this module is to query the NWS METAR page
|
1592 | (using the LWP modules) at:
|
1613 | (using the LWP modules) at:
|
1593 | 1614 | ||
1594 | I<http://weather.noaa.gov/cgi-bin/mgetmetar.pl?cccc=EHSB>
|
1615 | I<http://weather.noaa.gov/cgi-bin/mgetmetar.pl?cccc=EHSB>
|
1595 | 1616 | ||
1596 | to get an
|
1617 | to get an
|
1597 | up-to-date METAR. Then, I scan thru the output, looking for what looks
|
1618 | up-to-date METAR. Then, I scan thru the output, looking for what looks
|
1598 | like a METAR string (that's not hard in Perl). Oh, EHSB can be any site
|
1619 | like a METAR string (that's not hard in Perl). Oh, EHSB can be any site
|
1599 | location code where there is a reporting station.
|
1620 | location code where there is a reporting station.
|
1600 | 1621 | ||
1601 | I then pass the METAR into this module and get the info I want. I can
|
1622 | I then pass the METAR into this module and get the info I want. I can
|
1602 | then update my webcam page with the current temperature, sky conditions, or
|
1623 | then update my webcam page with the current temperature, sky conditions, or
|
1603 | whatnot. See for yourself at http://webcam.idefix.net/
|
1624 | whatnot. See for yourself at http://webcam.idefix.net/
|
1604 | 1625 | ||
1605 | See the BUGS section for a remark about multiple passes with the same
|
1626 | See the BUGS section for a remark about multiple passes with the same
|
1606 | Geo::METAR object.
|
1627 | Geo::ModMETAR object.
|
1607 | 1628 | ||
1608 | =head2 Functions
|
1629 | =head2 Functions
|
1609 | 1630 | ||
1610 | The following functions are defined in the METAR module. Most of
|
1631 | The following functions are defined in the METAR module. Most of
|
1611 | them are I<public>, meaning that you're supposed to use
|
1632 | them are I<public>, meaning that you're supposed to use
|
1612 | them. Some are I<private>, meaning that you're not supposed to use
|
1633 | them. Some are I<private>, meaning that you're not supposed to use
|
1613 | them -- but I won't stop you. Assume that functions are I<public>
|
1634 | them -- but I won't stop you. Assume that functions are I<public>
|
1614 | unless otherwise documented.
|
1635 | unless otherwise documented.
|
1615 | 1636 | ||
1616 | =over
|
1637 | =over
|
1617 | 1638 | ||
1618 | =item metar()
|
1639 | =item metar()
|
1619 | 1640 | ||
1620 | metar() is the function to whwich you should pass a METAR string. It
|
1641 | metar() is the function to whwich you should pass a METAR string. It
|
1621 | will take care of decomposing it into its component parts converting
|
1642 | will take care of decomposing it into its component parts converting
|
1622 | the units and so on.
|
1643 | the units and so on.
|
1623 | 1644 | ||
1624 | Example: C<$m-E<gt>metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");>
|
1645 | Example: C<$m-E<gt>metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");>
|
1625 | 1646 | ||
1626 | =item debug()
|
1647 | =item debug()
|
1627 | 1648 | ||
1628 | debug() toggles debugging messages. By default, debugging is turned
|
1649 | debug() toggles debugging messages. By default, debugging is turned
|
1629 | B<off>. Turn it on if you are developing METAR or having trouble with
|
1650 | B<off>. Turn it on if you are developing METAR or having trouble with
|
1630 | it.
|
1651 | it.
|
1631 | 1652 | ||
1632 | debug() understands all of the folloing:
|
1653 | debug() understands all of the folloing:
|
1633 | 1654 | ||
1634 | Enable Disable
|
1655 | Enable Disable
|
1635 | ------ -------
|
1656 | ------ -------
|
1636 | 1 0
|
1657 | 1 0
|
1637 | 'yes' 'no'
|
1658 | 'yes' 'no'
|
1638 | 'on' 'off'
|
1659 | 'on' 'off'
|
1639 | 1660 | ||
1640 | If you contact me for help, I'll likely ask you for some debugging
|
1661 | If you contact me for help, I'll likely ask you for some debugging
|
1641 | output.
|
1662 | output.
|
1642 | 1663 | ||
1643 | Example: C<$m-E<gt>debug(1);>
|
1664 | Example: C<$m-E<gt>debug(1);>
|
1644 | 1665 | ||
1645 | =item dump()
|
1666 | =item dump()
|
1646 | 1667 | ||
1647 | dump() will dump the internal data structure for the METAR in a
|
1668 | dump() will dump the internal data structure for the METAR in a
|
1648 | semi-human readable format.
|
1669 | semi-human readable format.
|
1649 | 1670 | ||
1650 | Example: C<$m-E<gt>dump;>
|
1671 | Example: C<$m-E<gt>dump;>
|
1651 | 1672 | ||
1652 | =item version()
|
1673 | =item version()
|
1653 | 1674 | ||
1654 | version() will print out the current version.
|
1675 | version() will print out the current version.
|
1655 | 1676 | ||
1656 | Example: C<print $m-E<gt>version;>
|
1677 | Example: C<print $m-E<gt>version;>
|
1657 | 1678 | ||
1658 | =item _tokenize()
|
1679 | =item _tokenize()
|
1659 | 1680 | ||
1660 | B<PRIVATE>
|
1681 | B<PRIVATE>
|
1661 | 1682 | ||
1662 | Called internally to break the METAR into its component tokens.
|
1683 | Called internally to break the METAR into its component tokens.
|
1663 | 1684 | ||
1664 | =item _process()
|
1685 | =item _process()
|
1665 | 1686 | ||
1666 | B<PRIVATE>
|
1687 | B<PRIVATE>
|
1667 | 1688 | ||
1668 | Used to make sense of the tokens found in B<_tokenize()>.
|
1689 | Used to make sense of the tokens found in B<_tokenize()>.
|
1669 | 1690 | ||
1670 | =back
|
1691 | =back
|
1671 | 1692 | ||
1672 | =head2 Variables
|
1693 | =head2 Variables
|
1673 | 1694 | ||
1674 | After you've called B<metar()>, you'd probably like to get at
|
1695 | After you've called B<metar()>, you'd probably like to get at
|
1675 | the individual values for things like temperature, dew point,
|
1696 | the individual values for things like temperature, dew point,
|
1676 | and so on. You do that by accessing individual variables via
|
1697 | and so on. You do that by accessing individual variables via
|
1677 | the METAR object.
|
1698 | the METAR object.
|
1678 | 1699 | ||
1679 | This section lists those variables and what they represent.
|
1700 | This section lists those variables and what they represent.
|
1680 | 1701 | ||
1681 | If you call B<dump()>, you'll find that it spits all of these
|
1702 | If you call B<dump()>, you'll find that it spits all of these
|
1682 | out.
|
1703 | out.
|
1683 | 1704 | ||
1684 | =over
|
1705 | =over
|
1685 | 1706 | ||
1686 | =item VERSION
|
1707 | =item VERSION
|
1687 | 1708 | ||
1688 | The version of METAR.pm that you're using.
|
1709 | The version of METAR.pm that you're using.
|
1689 | 1710 | ||
1690 | =item METAR
|
1711 | =item METAR
|
1691 | 1712 | ||
1692 | The actual, raw METAR.
|
1713 | The actual, raw METAR.
|
1693 | 1714 | ||
1694 | =item TYPE
|
1715 | =item TYPE
|
1695 | 1716 | ||
1696 | Report type in English ("Routine Weather Report" or "Special Weather Report")
|
1717 | Report type in English ("Routine Weather Report" or "Special Weather Report")
|
1697 | 1718 | ||
1698 | =item SITE
|
1719 | =item SITE
|
1699 | 1720 | ||
1700 | 4-letter site code.
|
1721 | 4-letter site code.
|
1701 | 1722 | ||
1702 | =item DATE
|
1723 | =item DATE
|
1703 | 1724 | ||
1704 | The date (just the day of the month) on which the report was issued.
|
1725 | The date (just the day of the month) on which the report was issued.
|
1705 | 1726 | ||
1706 | =item TIME
|
1727 | =item TIME
|
1707 | 1728 | ||
1708 | The time at which the report was issued.
|
1729 | The time at which the report was issued.
|
1709 | 1730 | ||
1710 | =item MOD
|
1731 | =item MOD
|
1711 | 1732 | ||
1712 | Modifier (AUTO/COR) if any.
|
1733 | Modifier (AUTO/COR) if any.
|
1713 | 1734 | ||
1714 | =item WIND_DIR_ENG
|
1735 | =item WIND_DIR_ENG
|
1715 | 1736 | ||
1716 | The current wind direction in English (Southwest, East, North, etc.)
|
1737 | The current wind direction in English (Southwest, East, North, etc.)
|
1717 | 1738 | ||
1718 | =item WIND_DIR_RUS
|
1739 | =item WIND_DIR_RUS
|
1719 | 1740 | ||
1720 | The current wind direction in Russian
|
1741 | The current wind direction in Russian
|
1721 | 1742 | ||
1722 | =item WIND_DIR_ABB
|
1743 | =item WIND_DIR_ABB
|
1723 | 1744 | ||
1724 | The current wind direction in abbreviated English (S, E, N, etc.)
|
1745 | The current wind direction in abbreviated English (S, E, N, etc.)
|
1725 | 1746 | ||
1726 | =item WIND_DIR_DEG
|
1747 | =item WIND_DIR_DEG
|
1727 | 1748 | ||
1728 | The current wind direction in degrees.
|
1749 | The current wind direction in degrees.
|
1729 | 1750 | ||
1730 | =item WIND_KTS
|
1751 | =item WIND_KTS
|
1731 | 1752 | ||
1732 | The current wind speed in Knots.
|
1753 | The current wind speed in Knots.
|
1733 | 1754 | ||
1734 | =item WIND_MPH
|
1755 | =item WIND_MPH
|
1735 | 1756 | ||
1736 | The current wind speed in Miles Per Hour.
|
1757 | The current wind speed in Miles Per Hour.
|
1737 | 1758 | ||
1738 | =item WIND_MS
|
1759 | =item WIND_MS
|
1739 | 1760 | ||
1740 | The current wind speed in Metres Per Second.
|
1761 | The current wind speed in Metres Per Second.
|
1741 | 1762 | ||
1742 | =item WIND_GUST_KTS
|
1763 | =item WIND_GUST_KTS
|
1743 | 1764 | ||
1744 | The current wind gusting speed in Knots.
|
1765 | The current wind gusting speed in Knots.
|
1745 | 1766 | ||
1746 | =item WIND_GUST_MPH
|
1767 | =item WIND_GUST_MPH
|
1747 | 1768 | ||
1748 | The current wind gusting speed in Miles Per Hour.
|
1769 | The current wind gusting speed in Miles Per Hour.
|
1749 | 1770 | ||
1750 | =item WIND_GUST_MS
|
1771 | =item WIND_GUST_MS
|
1751 | 1772 | ||
1752 | The current wind gusting speed in Metres Per Second.
|
1773 | The current wind gusting speed in Metres Per Second.
|
1753 | 1774 | ||
1754 | =item WIND_VAR
|
1775 | =item WIND_VAR
|
1755 | 1776 | ||
1756 | The wind variation in English
|
1777 | The wind variation in English
|
1757 | 1778 | ||
1758 | =item WIND_VAR_1
|
1779 | =item WIND_VAR_1
|
1759 | 1780 | ||
1760 | The first wind variation direction
|
1781 | The first wind variation direction
|
1761 | 1782 | ||
1762 | =item WIND_VAR_ENG_1
|
1783 | =item WIND_VAR_ENG_1
|
1763 | 1784 | ||
1764 | The first wind variation direction in English
|
1785 | The first wind variation direction in English
|
1765 | 1786 | ||
1766 | =item WIND_VAR_2
|
1787 | =item WIND_VAR_2
|
1767 | 1788 | ||
1768 | The second wind variation direction
|
1789 | The second wind variation direction
|
1769 | 1790 | ||
1770 | =item WIND_VAR_ENG_2
|
1791 | =item WIND_VAR_ENG_2
|
1771 | 1792 | ||
1772 | The second wind variation direction in English
|
1793 | The second wind variation direction in English
|
1773 | 1794 | ||
1774 | =item VISIBILITY
|
1795 | =item VISIBILITY
|
1775 | 1796 | ||
1776 | Visibility information.
|
1797 | Visibility information.
|
1777 | 1798 | ||
1778 | =item VISIBILITY_RUS
|
1799 | =item VISIBILITY_RUS
|
1779 | 1800 | ||
1780 | Visibility information in Russian.
|
1801 | Visibility information in Russian.
|
1781 | 1802 | ||
1782 | =item WIND
|
1803 | =item WIND
|
1783 | 1804 | ||
1784 | Wind information.
|
1805 | Wind information.
|
1785 | 1806 | ||
1786 | =item RUNWAY
|
1807 | =item RUNWAY
|
1787 | 1808 | ||
1788 | Runway information.
|
1809 | Runway information.
|
1789 | 1810 | ||
1790 | =item WEATHER
|
1811 | =item WEATHER
|
1791 | 1812 | ||
1792 | Current weather (array)
|
1813 | Current weather (array)
|
1793 | 1814 | ||
1794 | ==item WEATHER_RUS
|
1815 | ==item WEATHER_RUS
|
1795 | 1816 | ||
1796 | Current weather in Russian (array)
|
1817 | Current weather in Russian (array)
|
1797 | 1818 | ||
1798 | ==item WEATHER_RAW
|
1819 | ==item WEATHER_RAW
|
1799 | 1820 | ||
1800 | Current weather in RAW-data (array)
|
1821 | Current weather in RAW-data (array)
|
1801 | 1822 | ||
1802 | =item WEATHER_LOG
|
1823 | =item WEATHER_LOG
|
1803 | 1824 | ||
1804 | Current weather log (array)
|
1825 | Current weather log (array)
|
1805 | 1826 | ||
1806 | =item SKY
|
1827 | =item SKY
|
1807 | 1828 | ||
1808 | Current cloud cover (array)
|
1829 | Current cloud cover (array)
|
1809 | 1830 | ||
1810 | =item SKY_RUS
|
1831 | =item SKY_RUS
|
1811 | 1832 | ||
1812 | Current cloud cover in Russian (array)
|
1833 | Current cloud cover in Russian (array)
|
1813 | 1834 | ||
1814 | =item SKY_RAW
|
1835 | =item SKY_RAW
|
1815 | 1836 | ||
1816 | Current cloud cover in RAW-data (array)
|
1837 | Current cloud cover in RAW-data (array)
|
1817 | 1838 | ||
1818 | =item TEMP_C
|
1839 | =item TEMP_C
|
1819 | 1840 | ||
1820 | Temperature in Celsius.
|
1841 | Temperature in Celsius.
|
1821 | 1842 | ||
1822 | =item TEMP_F
|
1843 | =item TEMP_F
|
1823 | 1844 | ||
1824 | Temperature in Fahrenheit.
|
1845 | Temperature in Fahrenheit.
|
1825 | 1846 | ||
1826 | =item TEMP_WC
|
1847 | =item TEMP_WC
|
1827 | 1848 | ||
1828 | Windchill Temperature in Celsius.
|
1849 | Windchill Temperature in Celsius.
|
1829 | 1850 | ||
1830 | =item DEW_C
|
1851 | =item DEW_C
|
1831 | 1852 | ||
1832 | Dew point in Celsius.
|
1853 | Dew point in Celsius.
|
1833 | 1854 | ||
1834 | =item DEW_F
|
1855 | =item DEW_F
|
1835 | 1856 | ||
1836 | Dew point in Fahrenheit.
|
1857 | Dew point in Fahrenheit.
|
1837 | 1858 | ||
1838 | =item HOURLY_TEMP_F
|
1859 | =item HOURLY_TEMP_F
|
1839 | 1860 | ||
1840 | Hourly current temperature, fahrenheit
|
1861 | Hourly current temperature, fahrenheit
|
1841 | 1862 | ||
1842 | =item HOURLY_TEMP_C
|
1863 | =item HOURLY_TEMP_C
|
1843 | 1864 | ||
1844 | Hourly current temperature, celcius
|
1865 | Hourly current temperature, celcius
|
1845 | 1866 | ||
1846 | =item HOURLY_DEW_F
|
1867 | =item HOURLY_DEW_F
|
1847 | 1868 | ||
1848 | Hourly dewpoint, fahrenheit
|
1869 | Hourly dewpoint, fahrenheit
|
1849 | 1870 | ||
1850 | =item HOURLY_DEW_C
|
1871 | =item HOURLY_DEW_C
|
1851 | 1872 | ||
1852 | Hourly dewpoint, celcius
|
1873 | Hourly dewpoint, celcius
|
1853 | 1874 | ||
1854 | =item ALT
|
1875 | =item ALT
|
1855 | 1876 | ||
1856 | Altimeter setting (barometric pressure).
|
1877 | Altimeter setting (barometric pressure).
|
1857 | 1878 | ||
1858 | =item ALT_HP
|
1879 | =item ALT_HP
|
1859 | 1880 | ||
1860 | Altimeter setting in hectopascals.
|
1881 | Altimeter setting in hectopascals.
|
1861 | 1882 | ||
- | 1883 | =item ALT_PL
|
|
- | 1884 | ||
- | 1885 | QFE pressure in mmHg.
|
|
- | 1886 | ||
1862 | =item REMARKS
|
1887 | =item REMARKS
|
1863 | 1888 | ||
1864 | Any remarks in the report.
|
1889 | Any remarks in the report.
|
1865 | 1890 | ||
1866 | =back
|
1891 | =back
|
1867 | 1892 | ||
1868 | =head1 NOTES
|
1893 | =head1 NOTES
|
1869 | 1894 | ||
1870 | Test suite is small and incomplete. Needs work yet.
|
1895 | Test suite is small and incomplete. Needs work yet.
|
1871 | 1896 | ||
1872 | Older versions of this module were installed as "METAR" instaed of
|
1897 | Older versions of this module were installed as "METAR" instaed of
|
1873 | "Geo::METAR"
|
1898 | "Geo::METAR"
|
1874 | 1899 | ||
1875 | =head1 BUGS
|
1900 | =head1 BUGS
|
1876 | 1901 | ||
1877 | The Geo::METAR is only initialized once, which means you'll get left-over
|
1902 | The Geo::ModMETAR is only initialized once, which means you'll get left-over
|
1878 | crud in variables when you call the metar() function twice.
|
1903 | crud in variables when you call the metar() function twice.
|
1879 | 1904 | ||
1880 | What is an invalid METAR in one country is a standard one in the next.
|
1905 | What is an invalid METAR in one country is a standard one in the next.
|
1881 | The standard is interpreted and used by meteorologists all over the world,
|
1906 | The standard is interpreted and used by meteorologists all over the world,
|
1882 | with local variations. This means there will always be METARs that will
|
1907 | with local variations. This means there will always be METARs that will
|
1883 | trip the parser.
|
1908 | trip the parser.
|
1884 | 1909 | ||
1885 | =head1 TODO
|
1910 | =head1 TODO
|
1886 | 1911 | ||
1887 | There is a TODO file included in the Geo::METAR distribution listing
|
1912 | There is a TODO file included in the Geo::ModMETAR distribution listing
|
1888 | the outstanding tasks that I or others have devised. Please check that
|
1913 | the outstanding tasks that I or others have devised. Please check that
|
1889 | list before you submit a bug report or request a new feture. It might
|
1914 | list before you submit a bug report or request a new feture. It might
|
1890 | already be on the TODO list.
|
1915 | already be on the TODO list.
|
1891 | 1916 | ||
1892 | =head1 AUTHORS AND COPYRIGHT
|
1917 | =head1 AUTHORS AND COPYRIGHT
|
1893 | 1918 | ||
1894 | Copyright 1997-2000, Jeremy D. Zawodny <Jeremy [at] Zawodny.com>
|
1919 | Copyright 1997-2000, Jeremy D. Zawodny <Jeremy [at] Zawodny.com>
|
1895 | 1920 | ||
1896 | Copyright 2007, Koos van den Hout <koos@kzdoos.xs4all.nl>
|
1921 | Copyright 2007, Koos van den Hout <koos@kzdoos.xs4all.nl>
|
1897 | 1922 | ||
1898 | Copyright 2010, Alexander Wolf <alex.v.wolf@gmail.com>
|
1923 | Copyright 2010, Alexander Wolf <alex.v.wolf@gmail.com>
|
1899 | 1924 | ||
1900 | Geo::ModMETAR is covered under the GNU Public License (GPL) version 2 or
|
1925 | Geo::ModMETAR is covered under the GNU Public License (GPL) version 2 or
|
1901 | later.
|
1926 | later.
|
1902 | 1927 | ||
1903 | The Geo::ModMETAR Web site is located at:
|
1928 | The Geo::ModMETAR Web site is located at:
|
1904 | 1929 | ||
1905 | http://astro.uni-altai.ru/~aw/perl/Geo-ModMETAR/
|
1930 | http://astro.uni-altai.ru/~aw/perl/Geo-ModMETAR/
|
1906 | 1931 | ||
1907 | =head1 CREDITS
|
1932 | =head1 CREDITS
|
1908 | 1933 | ||
1909 | In addition to our work on Geo::METAR, We've received ideas, help, and
|
1934 | In addition to our work on Geo::METAR, We've received ideas, help, and
|
1910 | patches from the following folks:
|
1935 | patches from the following folks:
|
1911 | 1936 | ||
1912 | * Ethan Dicks <ethan.dicks [at] gmail.com>
|
1937 | * Ethan Dicks <ethan.dicks [at] gmail.com>
|
1913 | 1938 | ||
1914 | Testing of Geo::METAR at the South Pole. Corrections and pointers
|
1939 | Testing of Geo::METAR at the South Pole. Corrections and pointers
|
1915 | to interesting cases to test.
|
1940 | to interesting cases to test.
|
1916 | 1941 | ||
1917 | * Otterboy <jong [at] watchguard.com>
|
1942 | * Otterboy <jong [at] watchguard.com>
|
1918 | 1943 | ||
1919 | Random script fixes and initial debugging help
|
1944 | Random script fixes and initial debugging help
|
1920 | 1945 | ||
1921 | * Remi Lefebvre <remi [at] solaria.dhis.org>
|
1946 | * Remi Lefebvre <remi [at] solaria.dhis.org>
|
1922 | 1947 | ||
1923 | Debian packaging as libgeo-metar-perl.deb.
|
1948 | Debian packaging as libgeo-metar-perl.deb.
|
1924 | 1949 | ||
1925 | * Mike Engelhart <mengelhart [at] earthtrip.com>
|
1950 | * Mike Engelhart <mengelhart [at] earthtrip.com>
|
1926 | 1951 | ||
1927 | Wind direction naming corrections.
|
1952 | Wind direction naming corrections.
|
1928 | 1953 | ||
1929 | * Michael Starling <mstarling [at] logic.bm>
|
1954 | * Michael Starling <mstarling [at] logic.bm>
|
1930 | 1955 | ||
1931 | Wind direction naming corrections.
|
1956 | Wind direction naming corrections.
|
1932 | 1957 | ||
1933 | * Hans Einar Nielssen <hans.einar [at] nielssen.com>
|
1958 | * Hans Einar Nielssen <hans.einar [at] nielssen.com>
|
1934 | 1959 | ||
1935 | Wind direction naming corrections.
|
1960 | Wind direction naming corrections.
|
1936 | 1961 | ||
1937 | * Nathan Neulinger <nneul [at] umr.edu>
|
1962 | * Nathan Neulinger <nneul [at] umr.edu>
|
1938 | 1963 | ||
1939 | Lots of enhancements and corrections. Too many to list here.
|
1964 | Lots of enhancements and corrections. Too many to list here.
|
1940 | 1965 | ||
1941 | =head1 RELATED PROJECTS
|
1966 | =head1 RELATED PROJECTS
|
1942 | 1967 | ||
1943 | B<lcdproc> at http://www.lcdproc.org/ uses Geo::METAR in lcdmetar.pl to
|
1968 | B<lcdproc> at http://www.lcdproc.org/ uses Geo::METAR in lcdmetar.pl to
|
1944 | display weather data on an lcd.
|
1969 | display weather data on an lcd.
|
1945 | 1970 | ||
1946 | =cut
|
1971 | =cut
|
1947 | 1972 | ||
1948 | 1973 | ||
1949 | # vim:expandtab:sw=4 ts=4
|
1974 | # vim:expandtab:sw=4 ts=4
|
1950 | 1975 |