Редакция 12 | Редакция 14 | К новейшей редакции | Только различия | Не учитывать пробелы | Содержимое файла | Авторство | Последнее изменение | Открыть журнал | RSS
Редакция 12 | Редакция 13 | ||
---|---|---|---|
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 | ##
|
355 | ##
|
356 | ## Constructor.
|
356 | ## Constructor.
|
357 | ##
|
357 | ##
|
358 | 358 | ||
359 | sub new |
359 | sub new |
360 | {
|
360 | {
|
361 | my $this = shift; |
361 | my $this = shift; |
362 | my $class = ref($this) || $this; |
362 | my $class = ref($this) || $this; |
363 | my $self = {}; |
363 | my $self = {}; |
364 | 364 | ||
365 | ##
|
365 | ##
|
366 | ## UPPERCASE items have documented accssor functions (methods) or
|
366 | ## UPPERCASE items have documented accssor functions (methods) or
|
367 | ## use AUTOLOAD, while lowercase items are reserved for internal
|
367 | ## use AUTOLOAD, while lowercase items are reserved for internal
|
368 | ## use.
|
368 | ## use.
|
369 | ##
|
369 | ##
|
370 | 370 | ||
371 | $self->{VERSION} = $VERSION; # version number |
371 | $self->{VERSION} = $VERSION; # version number |
372 | $self->{METAR} = undef; # the actual, raw METAR |
372 | $self->{METAR} = undef; # the actual, raw METAR |
373 | $self->{TYPE} = undef; # the type of report |
373 | $self->{TYPE} = undef; # the type of report |
374 | $self->{SITE} = undef; # site code |
374 | $self->{SITE} = undef; # site code |
375 | $self->{DATE} = undef; # when it was issued |
375 | $self->{DATE} = undef; # when it was issued |
376 | $self->{TIME} = undef; # time it was issued |
376 | $self->{TIME} = undef; # time it was issued |
377 | $self->{MOD} = undef; # modifier (AUTO/COR) |
377 | $self->{MOD} = undef; # modifier (AUTO/COR) |
378 | $self->{WIND_DIR_DEG} = undef; # wind dir in degrees |
378 | $self->{WIND_DIR_DEG} = undef; # wind dir in degrees |
379 | $self->{WIND_DIR_ENG} = undef; # wind dir in english (Northwest/Southeast) |
379 | $self->{WIND_DIR_ENG} = undef; # wind dir in english (Northwest/Southeast) |
380 | $self->{WIND_DIR_RUS} = undef; # wind dir in russian (Северо-западный/Юго-восточный) |
380 | $self->{WIND_DIR_RUS} = undef; # wind dir in russian (Северо-западный/Юго-восточный) |
381 | $self->{WIND_DIR_ABB} = undef; # wind dir in abbreviated english (NW/SE) |
381 | $self->{WIND_DIR_ABB} = undef; # wind dir in abbreviated english (NW/SE) |
382 | $self->{WIND_KTS} = undef; # wind speed (knots) |
382 | $self->{WIND_KTS} = undef; # wind speed (knots) |
383 | $self->{WIND_GUST_KTS} = undef; # wind gusts (knots) |
383 | $self->{WIND_GUST_KTS} = undef; # wind gusts (knots) |
384 | $self->{WIND_MPH} = undef; # wind speed (MPH) |
384 | $self->{WIND_MPH} = undef; # wind speed (MPH) |
385 | $self->{WIND_GUST_MPH} = undef; # wind gusts (MPH) |
385 | $self->{WIND_GUST_MPH} = undef; # wind gusts (MPH) |
386 | $self->{WIND_MS} = undef; # wind speed (m/s) |
386 | $self->{WIND_MS} = undef; # wind speed (m/s) |
387 | $self->{WIND_GUST_MS} = undef; # wind gusts (m/s) |
387 | $self->{WIND_GUST_MS} = undef; # wind gusts (m/s) |
388 | $self->{WIND_VAR} = undef; # wind variation (text) |
388 | $self->{WIND_VAR} = undef; # wind variation (text) |
389 | $self->{WIND_VAR_1} = undef; # wind variation (direction 1) |
389 | $self->{WIND_VAR_1} = undef; # wind variation (direction 1) |
390 | $self->{WIND_VAR_2} = undef; # wind variation (direction 2) |
390 | $self->{WIND_VAR_2} = undef; # wind variation (direction 2) |
391 | $self->{WIND_VAR_ENG_1}= undef; # wind variation (text, direction 1) |
391 | $self->{WIND_VAR_ENG_1}= undef; # wind variation (text, direction 1) |
392 | $self->{WIND_VAR_ENG_2}= undef; # wind variation (text, direction 2) |
392 | $self->{WIND_VAR_ENG_2}= undef; # wind variation (text, direction 2) |
393 | $self->{VISIBILITY} = undef; # visibility info |
393 | $self->{VISIBILITY} = undef; # visibility info |
394 | $self->{RUNWAY} = [ ]; # runway vis. |
394 | $self->{RUNWAY} = [ ]; # runway vis. |
395 | $self->{RH} = undef; # relative humidity |
395 | $self->{RH} = undef; # relative humidity |
396 | $self->{WEATHER} = [ ]; # current weather |
396 | $self->{WEATHER} = [ ]; # current weather |
397 | $self->{WEATHER_LOG} = [ ]; # weather log |
397 | $self->{WEATHER_LOG} = [ ]; # weather log |
398 | $self->{SKY} = [ ]; # current sky (cloudcover) |
398 | $self->{SKY} = [ ]; # current sky (cloudcover) |
399 | $self->{TEMP_F} = undef; # current temp, celsius |
399 | $self->{TEMP_F} = undef; # current temp, celsius |
400 | $self->{TEMP_C} = undef; # converted to fahrenheit |
400 | $self->{TEMP_C} = undef; # converted to fahrenheit |
401 | $self->{TEMP_WC} = undef; # current windchill temp, celsius |
401 | $self->{TEMP_WC} = undef; # current windchill temp, celsius |
402 | $self->{DEW_F} = undef; # dew point, celcius |
402 | $self->{DEW_F} = undef; # dew point, celcius |
403 | $self->{DEW_C} = undef; # dew point, fahrenheit |
403 | $self->{DEW_C} = undef; # dew point, fahrenheit |
404 | $self->{HOURLY_TEMP_F} = undef; # hourly current temp, celcius |
404 | $self->{HOURLY_TEMP_F} = undef; # hourly current temp, celcius |
405 | $self->{HOURLY_TEMP_C} = undef; # hourly converted to fahrenheit |
405 | $self->{HOURLY_TEMP_C} = undef; # hourly converted to fahrenheit |
406 | $self->{HOURLY_DEW_F} = undef; # hourly dew point, celcius |
406 | $self->{HOURLY_DEW_F} = undef; # hourly dew point, celcius |
407 | $self->{HOURLY_DEW_C} = undef; # hourly dew point, fahrenheit |
407 | $self->{HOURLY_DEW_C} = undef; # hourly dew point, fahrenheit |
408 | $self->{HOURLY_PRECIP} = undef; # hourly precipitation |
408 | $self->{HOURLY_PRECIP} = undef; # hourly precipitation |
409 | $self->{ALT} = undef; # altimeter setting (Hg) |
409 | $self->{ALT} = undef; # altimeter setting (Hg) |
410 | $self->{ALT_HP} = undef; # altimeter setting (hPa) |
410 | $self->{ALT_HP} = undef; # altimeter setting (hPa) |
411 | $self->{ALT_PL} = undef; # pressure (Hg) |
411 | $self->{ALT_PL} = undef; # pressure (Hg) |
412 | $self->{SLP} = undef; # sea level pressure |
412 | $self->{SLP} = undef; # sea level pressure |
413 | $self->{REMARKS} = undef; # remarks |
413 | $self->{REMARKS} = undef; # remarks |
414 | $self->{WEATHER_RAW} = [ ]; # RAW data for weather |
414 | $self->{WEATHER_RAW} = [ ]; # RAW data for weather |
415 | $self->{WEATHER_RUS} = [ ]; # current weather in Russian |
415 | $self->{WEATHER_RUS} = [ ]; # current weather in Russian |
416 | $self->{SKY_RAW} = [ ]; # RAW data for sky |
416 | $self->{SKY_RAW} = [ ]; # RAW data for sky |
417 | $self->{SKY_RUS} = [ ]; # current sky in Russian |
417 | $self->{SKY_RUS} = [ ]; # current sky in Russian |
418 | $self->{VISIBILITY_RUS}= undef; # visibility info |
418 | $self->{VISIBILITY_RUS}= undef; # visibility info |
419 | 419 | ||
420 | $self->{tokens} = [ ]; # the "token" list |
420 | $self->{tokens} = [ ]; # the "token" list |
421 | $self->{type} = "METAR"; # the report type (METAR/SPECI) |
421 | $self->{type} = "METAR"; # the report type (METAR/SPECI) |
422 | # default=METAR
|
422 | # default=METAR
|
423 | $self->{site} = undef; # the site code (4 chars) |
423 | $self->{site} = undef; # the site code (4 chars) |
424 | $self->{date_time} = undef; # date/time |
424 | $self->{date_time} = undef; # date/time |
425 | $self->{modifier} = undef; # the AUTO/COR modifier |
425 | $self->{modifier} = undef; # the AUTO/COR modifier |
426 | $self->{wind} = undef; # the wind information |
426 | $self->{wind} = undef; # the wind information |
427 | $self->{windtype} = undef; # the wind speed type (knots/meterpersecond/kilometersperhour) |
427 | $self->{windtype} = undef; # the wind speed type (knots/meterpersecond/kilometersperhour) |
428 | $self->{windvar} = undef; # the wind variation |
428 | $self->{windvar} = undef; # the wind variation |
429 | $self->{visibility} = undef; # visibility information |
429 | $self->{visibility} = undef; # visibility information |
430 | $self->{runway} = undef; # runway visibility |
430 | $self->{runway} = undef; # runway visibility |
431 | $self->{weather} = [ ]; # current weather conditions |
431 | $self->{weather} = [ ]; # current weather conditions |
432 | $self->{sky} = [ ]; # sky conditions (cloud cover) |
432 | $self->{sky} = [ ]; # sky conditions (cloud cover) |
433 | $self->{temp_dew} = undef; # temp and dew pt. |
433 | $self->{temp_dew} = undef; # temp and dew pt. |
434 | $self->{alt} = undef; # altimeter setting |
434 | $self->{alt} = undef; # altimeter setting |
435 | $self->{pressure} = undef; # pressure (HPa) |
435 | $self->{pressure} = undef; # pressure (HPa) |
436 | $self->{slp} = undef; # sea level pressure |
436 | $self->{slp} = undef; # sea level pressure |
437 | $self->{remarks} = [ ]; # remarks |
437 | $self->{remarks} = [ ]; # remarks |
438 | 438 | ||
439 | $self->{debug} = undef; # enable debug trace |
439 | $self->{debug} = undef; # enable debug trace |
440 | 440 | ||
441 | bless $self, $class; |
441 | bless $self, $class; |
442 | return $self; |
442 | return $self; |
443 | }
|
443 | }
|
444 | 444 | ||
445 | ##
|
445 | ##
|
446 | ## Autoload for access methods to stuff in %fields hash. We should
|
446 | ## Autoload for access methods to stuff in %fields hash. We should
|
447 | ## probably disallow access to the lower-case items as stated above,
|
447 | ## probably disallow access to the lower-case items as stated above,
|
448 | ## but I don't feel like being a Nazi about it. Besides, I haven't
|
448 | ## but I don't feel like being a Nazi about it. Besides, I haven't
|
449 | ## checked to see what that might break.
|
449 | ## checked to see what that might break.
|
450 | ##
|
450 | ##
|
451 | 451 | ||
452 | sub AUTOLOAD
|
452 | sub AUTOLOAD
|
453 | {
|
453 | {
|
454 | my $self = shift; |
454 | my $self = shift; |
455 | 455 | ||
456 | if (not ref $self) |
456 | if (not ref $self) |
457 | {
|
457 | {
|
458 | cluck "bad AUTOLOAD for obj [$self]"; |
458 | cluck "bad AUTOLOAD for obj [$self]"; |
459 | }
|
459 | }
|
460 | 460 | ||
461 | if ($AUTOLOAD =~ /.*::(.*)/) |
461 | if ($AUTOLOAD =~ /.*::(.*)/) |
462 | {
|
462 | {
|
463 | my $key = $1; |
463 | my $key = $1; |
464 | 464 | ||
465 | 465 | ||
466 | ## Backward compatible temps...
|
466 | ## Backward compatible temps...
|
467 | 467 | ||
468 | my %compat = ( |
468 | my %compat = ( |
469 | F_TEMP => 'TEMP_F', |
469 | F_TEMP => 'TEMP_F', |
470 | C_TEMP => 'TEMP_C', |
470 | C_TEMP => 'TEMP_C', |
471 | F_DEW => 'DEW_F', |
471 | F_DEW => 'DEW_F', |
472 | C_DEW => 'DEW_C', |
472 | C_DEW => 'DEW_C', |
473 | ); |
473 | ); |
474 | 474 | ||
475 | if ($compat{$key}) |
475 | if ($compat{$key}) |
476 | {
|
476 | {
|
477 | $key = $compat{$key}; |
477 | $key = $compat{$key}; |
478 | }
|
478 | }
|
479 | 479 | ||
480 | ## Check for the items...
|
480 | ## Check for the items...
|
481 | 481 | ||
482 | if (exists $self->{$key}) |
482 | if (exists $self->{$key}) |
483 | {
|
483 | {
|
484 | return $self->{$key}; |
484 | return $self->{$key}; |
485 | }
|
485 | }
|
486 | else
|
486 | else
|
487 | {
|
487 | {
|
488 | return undef; |
488 | return undef; |
489 | }
|
489 | }
|
490 | }
|
490 | }
|
491 | else
|
491 | else
|
492 | {
|
492 | {
|
493 | warn "strange AUTOLOAD problem!"; |
493 | warn "strange AUTOLOAD problem!"; |
494 | return undef; |
494 | return undef; |
495 | }
|
495 | }
|
496 | }
|
496 | }
|
497 | 497 | ||
498 | ##
|
498 | ##
|
499 | ## Get current version number.
|
499 | ## Get current version number.
|
500 | ##
|
500 | ##
|
501 | 501 | ||
502 | sub version
|
502 | sub version
|
503 | {
|
503 | {
|
504 | my $self = shift; |
504 | my $self = shift; |
505 | print "version() called.\n" if $self->{debug}; |
505 | print "version() called.\n" if $self->{debug}; |
506 | return $self->{VERSION}; |
506 | return $self->{VERSION}; |
507 | }
|
507 | }
|
508 | 508 | ||
509 | ##
|
509 | ##
|
510 | ## Take a METAR, tokenize, and process it.
|
510 | ## Take a METAR, tokenize, and process it.
|
511 | ##
|
511 | ##
|
512 | 512 | ||
513 | sub metar
|
513 | sub metar
|
514 | {
|
514 | {
|
515 | my $self = shift; |
515 | my $self = shift; |
516 | 516 | ||
517 | if (@_) |
517 | if (@_) |
518 | {
|
518 | {
|
519 | $self->{METAR} = shift; |
519 | $self->{METAR} = shift; |
520 | $self->{METAR} =~ s/\n//g; ## nuke any newlines |
520 | $self->{METAR} =~ s/\n//g; ## nuke any newlines |
521 | _tokenize($self); |
521 | _tokenize($self); |
522 | _process($self); |
522 | _process($self); |
523 | }
|
523 | }
|
524 | return $self->{METAR}; |
524 | return $self->{METAR}; |
525 | }
|
525 | }
|
526 | 526 | ||
527 | ##
|
527 | ##
|
528 | ## Break {METAR} into parts. Stuff into @tokens.
|
528 | ## Break {METAR} into parts. Stuff into @tokens.
|
529 | ##
|
529 | ##
|
530 | 530 | ||
531 | sub _tokenize
|
531 | sub _tokenize
|
532 | {
|
532 | {
|
533 | my $self = shift; |
533 | my $self = shift; |
534 | my $tok; |
534 | my $tok; |
535 | my @toks; |
535 | my @toks; |
536 | 536 | ||
537 | # Split tokens on whitespace.
|
537 | # Split tokens on whitespace.
|
538 | @toks = split(/\s+/, $self->{METAR}); |
538 | @toks = split(/\s+/, $self->{METAR}); |
539 | $self->{tokens} = \@toks; |
539 | $self->{tokens} = \@toks; |
540 | }
|
540 | }
|
541 | 541 | ||
542 | ## Process @tokens to populate METAR values.
|
542 | ## Process @tokens to populate METAR values.
|
543 | ##
|
543 | ##
|
544 | ## This is a long and involved subroutine. It basically copies the
|
544 | ## This is a long and involved subroutine. It basically copies the
|
545 | ## @tokens array and treats it as a stack, popping off items,
|
545 | ## @tokens array and treats it as a stack, popping off items,
|
546 | ## examining them, and see what they look like. Based on their
|
546 | ## examining them, and see what they look like. Based on their
|
547 | ## "apppearance" it takes care populating the proper fields
|
547 | ## "apppearance" it takes care populating the proper fields
|
548 | ## internally.
|
548 | ## internally.
|
549 | 549 | ||
550 | sub _process
|
550 | sub _process
|
551 | {
|
551 | {
|
552 | my $self = shift; |
552 | my $self = shift; |
553 | 553 | ||
554 | my @toks = @{$self->{tokens}}; # copy tokens array... |
554 | my @toks = @{$self->{tokens}}; # copy tokens array... |
555 | 555 | ||
556 | my $tok; |
556 | my $tok; |
557 | 557 | ||
558 | ## This is a semi-brute-force way of doing things, but the amount
|
558 | ## This is a semi-brute-force way of doing things, but the amount
|
559 | ## of data is relatively small, so it shouldn't be a big deal.
|
559 | ## of data is relatively small, so it shouldn't be a big deal.
|
560 | ##
|
560 | ##
|
561 | ## Ideally, I'd have it skip checks for items which have been
|
561 | ## Ideally, I'd have it skip checks for items which have been
|
562 | ## found, but that would make this more "linear" and I'd remove
|
562 | ## found, but that would make this more "linear" and I'd remove
|
563 | ## the pretty while loop.
|
563 | ## the pretty while loop.
|
564 | #
|
564 | #
|
565 | # KH: modified to maintain state to not get lost in remarks and stuff
|
565 | # KH: modified to maintain state to not get lost in remarks and stuff
|
566 | # and be a lot better at parsing
|
566 | # and be a lot better at parsing
|
567 | 567 | ||
568 | # states
|
568 | # states
|
569 | 569 | ||
570 | my $expect_type = 0; |
570 | my $expect_type = 0; |
571 | my $expect_site = 1; |
571 | my $expect_site = 1; |
572 | my $expect_datetime = 2; |
572 | my $expect_datetime = 2; |
573 | my $expect_modifier = 3; |
573 | my $expect_modifier = 3; |
574 | my $expect_wind = 4; |
574 | my $expect_wind = 4; |
575 | my $expect_visibility = 5; |
575 | my $expect_visibility = 5; |
576 | my $expect_runwayvisual = 6; |
576 | my $expect_runwayvisual = 6; |
577 | my $expect_presentweather = 7; |
577 | my $expect_presentweather = 7; |
578 | my $expect_clouds = 8; |
578 | my $expect_clouds = 8; |
579 | my $expect_temperature = 9; |
579 | my $expect_temperature = 9; |
580 | my $expect_pressure = 10; |
580 | my $expect_pressure = 10; |
581 | my $expect_recentweather = 11; |
581 | my $expect_recentweather = 11; |
582 | my $expect_remarks = 12; |
582 | my $expect_remarks = 12; |
583 | my $expect_usremarks = 13; |
583 | my $expect_usremarks = 13; |
584 | 584 | ||
585 | my $parsestate = $expect_type; |
585 | my $parsestate = $expect_type; |
586 | 586 | ||
587 | # windtypes
|
587 | # windtypes
|
588 | 588 | ||
589 | my $wt_knots = 1; |
589 | my $wt_knots = 1; |
590 | my $wt_mps = 2; |
590 | my $wt_mps = 2; |
591 | my $wt_kph = 3; |
591 | my $wt_kph = 3; |
592 | 592 | ||
593 | ## Assume standard report by default
|
593 | ## Assume standard report by default
|
594 | 594 | ||
595 | $self->{type} = "METAR"; |
595 | $self->{type} = "METAR"; |
596 | $self->{TYPE} = "Routine Weather Report"; |
596 | $self->{TYPE} = "Routine Weather Report"; |
597 | 597 | ||
598 | while (defined($tok = shift(@toks))) ## as long as there are tokens |
598 | while (defined($tok = shift(@toks))) ## as long as there are tokens |
599 | {
|
599 | {
|
600 | print "trying to match [$tok] state is $parsestate\n" if $self->{debug}; |
600 | print "trying to match [$tok] state is $parsestate\n" if $self->{debug}; |
601 | 601 | ||
602 | ##
|
602 | ##
|
603 | ## is it a report type?
|
603 | ## is it a report type?
|
604 | ##
|
604 | ##
|
605 | 605 | ||
606 | if (($parsestate == $expect_type) and ($tok =~ /(METAR|SPECI)/i)) |
606 | if (($parsestate == $expect_type) and ($tok =~ /(METAR|SPECI)/i)) |
607 | {
|
607 | {
|
608 | $self->{type} = $tok; |
608 | $self->{type} = $tok; |
609 | 609 | ||
610 | if ($self->{type} eq "METAR") |
610 | if ($self->{type} eq "METAR") |
611 | {
|
611 | {
|
612 | $self->{TYPE} = "Routine Weather Report"; |
612 | $self->{TYPE} = "Routine Weather Report"; |
613 | }
|
613 | }
|
614 | elsif ($self->{type} eq "SPECI") |
614 | elsif ($self->{type} eq "SPECI") |
615 | {
|
615 | {
|
616 | $self->{TYPE} = "Special Weather Report"; |
616 | $self->{TYPE} = "Special Weather Report"; |
617 | }
|
617 | }
|
618 | print "[$tok] is a report type.\n" if $self->{debug}; |
618 | print "[$tok] is a report type.\n" if $self->{debug}; |
619 | $parsestate = $expect_site; |
619 | $parsestate = $expect_site; |
620 | next; |
620 | next; |
621 | }
|
621 | }
|
622 | 622 | ||
623 | ##
|
623 | ##
|
624 | ## is is a site ID?
|
624 | ## is is a site ID?
|
625 | ##
|
625 | ##
|
626 | 626 | ||
627 | elsif (($parsestate <= $expect_site) and ($tok =~ /([A-Z]{4}|K[A-Z0-9]{3})/)) |
627 | elsif (($parsestate <= $expect_site) and ($tok =~ /([A-Z]{4}|K[A-Z0-9]{3})/)) |
628 | {
|
628 | {
|
629 | $self->{site} = $tok; |
629 | $self->{site} = $tok; |
630 | print "[$tok] is a site ID.\n" if $self->{debug}; |
630 | print "[$tok] is a site ID.\n" if $self->{debug}; |
631 | $parsestate = $expect_datetime; |
631 | $parsestate = $expect_datetime; |
632 | next; |
632 | next; |
633 | }
|
633 | }
|
634 | 634 | ||
635 | ##
|
635 | ##
|
636 | ## is it a date/time?
|
636 | ## is it a date/time?
|
637 | ##
|
637 | ##
|
638 | 638 | ||
639 | elsif (($parsestate == $expect_datetime) and ($tok =~ /\d{6,6}Z/i)) |
639 | elsif (($parsestate == $expect_datetime) and ($tok =~ /\d{6,6}Z/i)) |
640 | {
|
640 | {
|
641 | $self->{date_time} = $tok; |
641 | $self->{date_time} = $tok; |
642 | print "[$tok] is a date/time.\n" if $self->{debug}; |
642 | print "[$tok] is a date/time.\n" if $self->{debug}; |
643 | $parsestate = $expect_modifier; |
643 | $parsestate = $expect_modifier; |
644 | next; |
644 | next; |
645 | 645 | ||
646 | 646 | ||
647 | }
|
647 | }
|
648 | 648 | ||
649 | ##
|
649 | ##
|
650 | ## is it a report modifier?
|
650 | ## is it a report modifier?
|
651 | ##
|
651 | ##
|
652 | 652 | ||
653 | elsif (($parsestate == $expect_modifier) and ($tok =~ /AUTO|COR|CC[A-Z]/i)) |
653 | elsif (($parsestate == $expect_modifier) and ($tok =~ /AUTO|COR|CC[A-Z]/i)) |
654 | {
|
654 | {
|
655 | $self->{modifier} = $tok; |
655 | $self->{modifier} = $tok; |
656 | print "[$tok] is a report modifier.\n" if $self->{debug}; |
656 | print "[$tok] is a report modifier.\n" if $self->{debug}; |
657 | $parsestate = $expect_wind; |
657 | $parsestate = $expect_wind; |
658 | next; |
658 | next; |
659 | }
|
659 | }
|
660 | 660 | ||
661 | ##
|
661 | ##
|
662 | ## is it wind information in knots?
|
662 | ## is it wind information in knots?
|
663 | #
|
663 | #
|
664 | # eew: KT seems to be optional
|
664 | # eew: KT seems to be optional
|
665 | # but making it optional fails on other stuff
|
665 | # but making it optional fails on other stuff
|
666 | # sortafix: wind needs to be \d\d\d\d\d or VRB\d\d
|
666 | # sortafix: wind needs to be \d\d\d\d\d or VRB\d\d
|
667 | # optional \d\d\d\d\dG\d\d\d (gust direction)
|
667 | # optional \d\d\d\d\dG\d\d\d (gust direction)
|
668 | 668 | ||
669 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /(\d{3}|VRB)\d{2}(G\d{1,3})?(KT)?$/i)) |
669 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /(\d{3}|VRB)\d{2}(G\d{1,3})?(KT)?$/i)) |
670 | {
|
670 | {
|
671 | $self->{wind} = $tok; |
671 | $self->{wind} = $tok; |
672 | $self->{windtype} = $wt_knots; |
672 | $self->{windtype} = $wt_knots; |
673 | print "[$tok] is wind information in knots.\n" if $self->{debug}; |
673 | print "[$tok] is wind information in knots.\n" if $self->{debug}; |
674 | $parsestate = $expect_wind; # stay in wind, it can have variation |
674 | $parsestate = $expect_wind; # stay in wind, it can have variation |
675 | next; |
675 | next; |
676 | }
|
676 | }
|
677 | 677 | ||
678 | ##
|
678 | ##
|
679 | ## is it wind information in meters per second?
|
679 | ## is it wind information in meters per second?
|
680 | ##
|
680 | ##
|
681 | ## can be variable too
|
681 | ## can be variable too
|
682 | 682 | ||
683 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /^(\d{3}|VRB)\d{2}(G\d{2,3})?MPS$/)) |
683 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_visibility) and ($tok =~ /^(\d{3}|VRB)\d{2}(G\d{2,3})?MPS$/)) |
684 | {
|
684 | {
|
685 | $self->{wind} = $tok; |
685 | $self->{wind} = $tok; |
686 | print "[$tok] is wind information.\n" if $self->{debug}; |
686 | print "[$tok] is wind information.\n" if $self->{debug}; |
687 | $self->{windtype} = $wt_mps; |
687 | $self->{windtype} = $wt_mps; |
688 | $parsestate = $expect_wind; # stay in wind, it can have variation |
688 | $parsestate = $expect_wind; # stay in wind, it can have variation |
689 | next; |
689 | next; |
690 | }
|
690 | }
|
691 | 691 | ||
692 | ##
|
692 | ##
|
693 | ## is it wind variation information?
|
693 | ## is it wind variation information?
|
694 | ##
|
694 | ##
|
695 | 695 | ||
696 | elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\d{3}V\d{3}$/)) |
696 | elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\d{3}V\d{3}$/)) |
697 | {
|
697 | {
|
698 | $self->{windvar} = $tok; |
698 | $self->{windvar} = $tok; |
699 | print "[$tok] is wind variation information.\n" if $self->{debug}; |
699 | print "[$tok] is wind variation information.\n" if $self->{debug}; |
700 | $parsestate = $expect_visibility; |
700 | $parsestate = $expect_visibility; |
701 | next; |
701 | next; |
702 | }
|
702 | }
|
703 | 703 | ||
704 | ##
|
704 | ##
|
705 | ## wind information missing at the moment?
|
705 | ## wind information missing at the moment?
|
706 | ##
|
706 | ##
|
707 | 707 | ||
708 | elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\/\/\/\/\/(KT|MPS)$/)){ |
708 | elsif (($parsestate >= $expect_wind) and ($parsestate < $expect_visibility) and ($tok =~ /^\/\/\/\/\/(KT|MPS)$/)){ |
709 | print "[$tok] is missing wind information.\n" if $self->{debug}; |
709 | print "[$tok] is missing wind information.\n" if $self->{debug}; |
710 | $parsestate = $expect_visibility; |
710 | $parsestate = $expect_visibility; |
711 | next; |
711 | next; |
712 | }
|
712 | }
|
713 | 713 | ||
714 | ##
|
714 | ##
|
715 | ## is it visibility information in meters?
|
715 | ## is it visibility information in meters?
|
716 | ##
|
716 | ##
|
717 | 717 | ||
718 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}$/)) |
718 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}$/)) |
719 | {
|
719 | {
|
720 | $self->{visibility} = $tok; |
720 | $self->{visibility} = $tok; |
721 | print "[$tok] is numerical visibility information.\n" if $self->{debug}; |
721 | print "[$tok] is numerical visibility information.\n" if $self->{debug}; |
722 | $parsestate = $expect_visibility; |
722 | $parsestate = $expect_visibility; |
723 | next; |
723 | next; |
724 | }
|
724 | }
|
725 | 725 | ||
726 | ## auto visibility information in meters?
|
726 | ## auto visibility information in meters?
|
727 | 727 | ||
728 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}NDV$/)) |
728 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d{4}NDV$/)) |
729 | {
|
729 | {
|
730 | $self->{visibility} = $tok; |
730 | $self->{visibility} = $tok; |
731 | print "[$tok] is automatic numerical visibility information.\n" if $self->{debug}; |
731 | print "[$tok] is automatic numerical visibility information.\n" if $self->{debug}; |
732 | $parsestate = $expect_visibility; |
732 | $parsestate = $expect_visibility; |
733 | next; |
733 | next; |
734 | }
|
734 | }
|
735 | 735 | ||
736 | ##
|
736 | ##
|
737 | ## is it visibility information in statute miles?
|
737 | ## is it visibility information in statute miles?
|
738 | ##
|
738 | ##
|
739 | 739 | ||
740 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /.*?SM$/i)) |
740 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /.*?SM$/i)) |
741 | {
|
741 | {
|
742 | $self->{visibility} = $tok; |
742 | $self->{visibility} = $tok; |
743 | print "[$tok] is statute miles visibility information.\n" if $self->{debug}; |
743 | print "[$tok] is statute miles visibility information.\n" if $self->{debug}; |
744 | $parsestate = $expect_visibility; |
744 | $parsestate = $expect_visibility; |
745 | next; |
745 | next; |
746 | }
|
746 | }
|
747 | 747 | ||
748 | ##
|
748 | ##
|
749 | ## is it visibility information with a leading digit?
|
749 | ## is it visibility information with a leading digit?
|
750 | ##
|
750 | ##
|
751 | ## sample:
|
751 | ## sample:
|
752 | ## KERV 132345Z AUTO 07008KT 1 1/4SM HZ 34/11 A3000 RMK AO2
|
752 | ## KERV 132345Z AUTO 07008KT 1 1/4SM HZ 34/11 A3000 RMK AO2
|
753 | ## ^^^^^^^
|
753 | ## ^^^^^^^
|
754 | ##
|
754 | ##
|
755 | 755 | ||
756 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d$/)) |
756 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_runwayvisual) and ($tok =~ /^\d$/)) |
757 | {
|
757 | {
|
758 | $tok .= " " . shift(@toks); |
758 | $tok .= " " . shift(@toks); |
759 | $self->{visibility} = $tok; |
759 | $self->{visibility} = $tok; |
760 | print "[$tok] is multi-part visibility information.\n" if $self->{debug}; |
760 | print "[$tok] is multi-part visibility information.\n" if $self->{debug}; |
761 | $parsestate = $expect_visibility; |
761 | $parsestate = $expect_visibility; |
762 | next; |
762 | next; |
763 | }
|
763 | }
|
764 | 764 | ||
765 | ## visibility modifier
|
765 | ## visibility modifier
|
766 | 766 | ||
767 | elsif (($parsestate == $expect_visibility) and ($tok =~ /^\d{4}(N|S|E|W|NE|NW|SE|SW)$/)) |
767 | elsif (($parsestate == $expect_visibility) and ($tok =~ /^\d{4}(N|S|E|W|NE|NW|SE|SW)$/)) |
768 | {
|
768 | {
|
769 | print "[$tok] is a visibility modifier.\n" if $self->{debug}; |
769 | print "[$tok] is a visibility modifier.\n" if $self->{debug}; |
770 | next; |
770 | next; |
771 | }
|
771 | }
|
772 | 772 | ||
773 | ##
|
773 | ##
|
774 | ## is it runway visibility info?
|
774 | ## is it runway visibility info?
|
775 | ##
|
775 | ##
|
776 | # KH: I've seen runway visibility with 'U' units
|
776 | # KH: I've seen runway visibility with 'U' units
|
777 | # EHSB 121425Z 22010KT 1200 R27/1600U -DZ BKN003 OVC007 07/07 Q1016 AMB FCST CANCEL
|
777 | # EHSB 121425Z 22010KT 1200 R27/1600U -DZ BKN003 OVC007 07/07 Q1016 AMB FCST CANCEL
|
778 | # U= going up, D= going down, N= no change
|
778 | # U= going up, D= going down, N= no change
|
779 | # tendency of visual range, http://stoivane.kapsi.fi/metar/
|
779 | # tendency of visual range, http://stoivane.kapsi.fi/metar/
|
780 | 780 | ||
781 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_presentweather) and ($tok =~ /R\d+(L|R|C)?\/P?\d+(VP?\d+)?(FT|D|U|N|\/)?$/i)) |
781 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_presentweather) and ($tok =~ /R\d+(L|R|C)?\/P?\d+(VP?\d+)?(FT|D|U|N|\/)?$/i)) |
782 | {
|
782 | {
|
783 | push (@{$self->{RUNWAY}},$tok); |
783 | push (@{$self->{RUNWAY}},$tok); |
784 | print "[$tok] is runway visual information.\n" if $self->{debug}; |
784 | print "[$tok] is runway visual information.\n" if $self->{debug}; |
785 | $parsestate = $expect_runwayvisual; |
785 | $parsestate = $expect_runwayvisual; |
786 | # there can be multiple runways, so stay at this state
|
786 | # there can be multiple runways, so stay at this state
|
787 | next; |
787 | next; |
788 | }
|
788 | }
|
789 | 789 | ||
790 | ##
|
790 | ##
|
791 | ## is it current weather info?
|
791 | ## is it current weather info?
|
792 | ##
|
792 | ##
|
793 | 793 | ||
794 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_clouds) and ($tok =~ /^(-|\+)?(VC)?($_weather_types_pat)+/i)) |
794 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_clouds) and ($tok =~ /^(-|\+)?(VC)?($_weather_types_pat)+/i)) |
795 | {
|
795 | {
|
796 | my $engl = ""; |
796 | my $engl = ""; |
797 | my $rusl = ""; |
797 | my $rusl = ""; |
798 | my $rawl = ""; |
798 | my $rawl = ""; |
799 | my $qual = $1; |
799 | my $qual = $1; |
800 | my $addlqual = $2; |
800 | my $addlqual = $2; |
801 | 801 | ||
802 | ## qualifier
|
802 | ## qualifier
|
803 | 803 | ||
804 | if (defined $qual) |
804 | if (defined $qual) |
805 | {
|
805 | {
|
806 | if ( $qual eq "-" ) { |
806 | if ( $qual eq "-" ) { |
807 | $engl = "light"; |
807 | $engl = "light"; |
808 | $rusl = "легкий"; |
808 | $rusl = "легкий"; |
809 | } elsif ( $qual eq "+" ) { |
809 | } elsif ( $qual eq "+" ) { |
810 | $engl = "heavy"; |
810 | $engl = "heavy"; |
811 | $rusl = "сильный"; |
811 | $rusl = "сильный"; |
812 | } else { |
812 | } else { |
813 | $engl = ""; ## moderate |
813 | $engl = ""; ## moderate |
814 | $rusl = ""; |
814 | $rusl = ""; |
815 | }
|
815 | }
|
816 | $rawl = $qual; |
816 | $rawl = $qual; |
817 | }
|
817 | }
|
818 | else
|
818 | else
|
819 | {
|
819 | {
|
820 | $engl = ""; ## moderate |
820 | $engl = ""; ## moderate |
821 | $rusl = ""; |
821 | $rusl = ""; |
822 | $rawl = ""; |
822 | $rawl = ""; |
823 | }
|
823 | }
|
824 | 824 | ||
825 | while ( $tok =~ /($_weather_types_pat)/gi ) |
825 | while ( $tok =~ /($_weather_types_pat)/gi ) |
826 | {
|
826 | {
|
827 | $engl .= " " . $_weather_types{$1}; ## figure out weather |
827 | $engl .= " " . $_weather_types{$1}; ## figure out weather |
828 | $rusl .= " " . $_weather_types_ru{$1}; |
828 | $rusl .= " " . $_weather_types_ru{$1} . ", "; |
829 | $rawl .= " " . $1; |
829 | $rawl .= " " . $1; |
830 | }
|
830 | }
|
- | 831 | $rusl = substr($rusl, 0, length($rusl)-2); |
|
831 | 832 | ||
832 | 833 | ||
833 | ## addl qualifier
|
834 | ## addl qualifier
|
834 | 835 | ||
835 | if (defined $addlqual) |
836 | if (defined $addlqual) |
836 | {
|
837 | {
|
837 | if ( $addlqual eq "VC" ) |
838 | if ( $addlqual eq "VC" ) |
838 | {
|
839 | {
|
839 | $engl .= " in vicinity"; |
840 | $engl .= " in vicinity"; |
840 | $rusl .= " в окрестностях"; |
841 | $rusl .= " в окрестностях"; |
841 | }
|
842 | }
|
842 | }
|
843 | }
|
843 | 844 | ||
844 | $engl =~ s/^\s//gio; |
845 | $engl =~ s/^\s//gio; |
845 | $engl =~ s/\s\s/ /gio; |
846 | $engl =~ s/\s\s/ /gio; |
846 | $rusl =~ s/^\s//gio; |
847 | $rusl =~ s/^\s//gio; |
847 | $rusl =~ s/\s\s/ /gio; |
848 | $rusl =~ s/\s\s/ /gio; |
848 | $rawl =~ s/^\s//gio; |
849 | $rawl =~ s/^\s//gio; |
849 | $rawl =~ s/\s\s/ /gio; |
850 | $rawl =~ s/\s\s/ /gio; |
850 | 851 | ||
851 | push(@{$self->{WEATHER}},$engl); |
852 | push(@{$self->{WEATHER}},$engl); |
852 | push(@{$self->{WEATHER_RUS}},$rusl); |
853 | push(@{$self->{WEATHER_RUS}},$rusl); |
853 | push(@{$self->{WEATHER_RAW}},$rawl); |
854 | push(@{$self->{WEATHER_RAW}},$rawl); |
854 | push(@{$self->{weather}},$tok); |
855 | push(@{$self->{weather}},$tok); |
855 | print "[$tok] is current weather.\n" if $self->{debug}; |
856 | print "[$tok] is current weather.\n" if $self->{debug}; |
856 | $parsestate = $expect_presentweather; |
857 | $parsestate = $expect_presentweather; |
857 | # there can be multiple current weather types, so stay at this state
|
858 | # there can be multiple current weather types, so stay at this state
|
858 | next; |
859 | next; |
859 | }
|
860 | }
|
860 | 861 | ||
861 | ##
|
862 | ##
|
862 | ## special case: CAVOK
|
863 | ## special case: CAVOK
|
863 | ##
|
864 | ##
|
864 | 865 | ||
865 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok eq 'CAVOK' )) |
866 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok eq 'CAVOK' )) |
866 | {
|
867 | {
|
867 | push(@{$self->{sky}},$tok); |
868 | push(@{$self->{sky}},$tok); |
868 | push(@{$self->{SKY}}, "Sky Clear"); |
869 | push(@{$self->{SKY}}, "Sky Clear"); |
869 | push(@{$self->{SKY_RUS}}, "Ясно"); |
870 | push(@{$self->{SKY_RUS}}, "Ясно"); |
870 | push(@{$self->{SKY_RAW}},$tok); |
871 | push(@{$self->{SKY_RAW}},$tok); |
871 | push(@{$self->{weather}},$tok); |
872 | push(@{$self->{weather}},$tok); |
872 | push(@{$self->{WEATHER}},"No significant weather"); |
873 | push(@{$self->{WEATHER}},"No significant weather"); |
873 | $self->{visibility} = '9999'; |
874 | $self->{visibility} = '9999'; |
874 | $parsestate = $expect_temperature; |
875 | $parsestate = $expect_temperature; |
875 | next; |
876 | next; |
876 | }
|
877 | }
|
877 | 878 | ||
878 | ##
|
879 | ##
|
879 | ## is it sky conditions (clear)?
|
880 | ## is it sky conditions (clear)?
|
880 | ##
|
881 | ##
|
881 | 882 | ||
882 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /SKC|CLR/ )) |
883 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /SKC|CLR/ )) |
883 | {
|
884 | {
|
884 | push(@{$self->{sky}},$tok); |
885 | push(@{$self->{sky}},$tok); |
885 | push(@{$self->{SKY}}, "Sky Clear"); |
886 | push(@{$self->{SKY}}, "Sky Clear"); |
886 | push(@{$self->{SKY_RUS}}, "Ясно"); |
887 | push(@{$self->{SKY_RUS}}, "Ясно"); |
887 | push(@{$self->{SKY_RAW}},$tok); |
888 | push(@{$self->{SKY_RAW}},$tok); |
888 | print "[$tok] is a sky condition.\n" if $self->{debug}; |
889 | print "[$tok] is a sky condition.\n" if $self->{debug}; |
889 | $parsestate = $expect_clouds; |
890 | $parsestate = $expect_clouds; |
890 | next; |
891 | next; |
891 | }
|
892 | }
|
892 | 893 | ||
893 | ##
|
894 | ##
|
894 | ## is it sky conditions (clouds)?
|
895 | ## is it sky conditions (clouds)?
|
895 | ##
|
896 | ##
|
896 | ## sky conditions can end with ///
|
897 | ## sky conditions can end with ///
|
897 | 898 | ||
898 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(FEW|SCT|BKN|OVC)(\d\d\d)?(CB|TCU)?\/*$/i)) |
899 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(FEW|SCT|BKN|OVC)(\d\d\d)?(CB|TCU)?\/*$/i)) |
899 | {
|
900 | {
|
900 | push(@{$self->{sky}},$tok); |
901 | push(@{$self->{sky}},$tok); |
901 | my $engl = ""; |
902 | my $engl = ""; |
902 | my $rusl = ""; |
903 | my $rusl = ""; |
903 | my $rawl = ""; |
904 | my $rawl = ""; |
904 | 905 | ||
905 | $engl = $_sky_types{$1}; |
906 | $engl = $_sky_types{$1}; |
906 | $rusl = $_sky_types_ru{$1}; |
907 | $rusl = $_sky_types_ru{$1}; |
907 | $rawl = $1; |
908 | $rawl = $1; |
908 | 909 | ||
909 | if (defined $3) |
910 | if (defined $3) |
910 | {
|
911 | {
|
911 | if ($3 eq "TCU") |
912 | if ($3 eq "TCU") |
912 | {
|
913 | {
|
913 | $engl .= " Towering Cumulus"; |
914 | $engl .= " Towering Cumulus"; |
914 | $rusl .= ", кучевые облака"; |
915 | $rusl .= ", кучевые облака"; |
915 | }
|
916 | }
|
916 | elsif ($3 eq "CB") |
917 | elsif ($3 eq "CB") |
917 | {
|
918 | {
|
918 | $engl .= " Cumulonimbus"; |
919 | $engl .= " Cumulonimbus"; |
919 | $rusl .= ", кучево-дождевые облака"; |
920 | $rusl .= ", кучево-дождевые облака"; |
920 | }
|
921 | }
|
921 | $rawl = $3; |
922 | $rawl = $3; |
922 | }
|
923 | }
|
923 | 924 | ||
924 | if ($2 ne "") |
925 | if ($2 ne "") |
925 | {
|
926 | {
|
926 | my $agl = int($2)*100; |
927 | my $agl = int($2)*100; |
927 | $engl .= " at $agl" . "ft"; |
928 | $engl .= " at $agl" . "ft"; |
928 | $rusl .= " на высоте " . int(($agl*0.3048)/10)*10 . " м"; |
929 | $rusl .= " на высоте " . int(($agl*0.3048)/10)*10 . " м"; |
929 | }
|
930 | }
|
930 | 931 | ||
931 | push(@{$self->{SKY}}, $engl); |
932 | push(@{$self->{SKY}}, $engl); |
932 | push(@{$self->{SKY_RUS}}, $rusl); |
933 | push(@{$self->{SKY_RUS}}, $rusl); |
933 | push(@{$self->{SKY_RAW}}, $rawl); |
934 | push(@{$self->{SKY_RAW}}, $rawl); |
934 | print "[$tok] is a sky condition.\n" if $self->{debug}; |
935 | print "[$tok] is a sky condition.\n" if $self->{debug}; |
935 | $parsestate = $expect_clouds; |
936 | $parsestate = $expect_clouds; |
936 | # clouds DO repeat. a lot ;)
|
937 | # clouds DO repeat. a lot ;)
|
937 | next; |
938 | next; |
938 | }
|
939 | }
|
939 | 940 | ||
940 | ##
|
941 | ##
|
941 | ## auto detected cloud conditions
|
942 | ## auto detected cloud conditions
|
942 | ##
|
943 | ##
|
943 | 944 | ||
944 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(NSC|NCD)$/ )){ |
945 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^(NSC|NCD)$/ )){ |
945 | my $engl = ""; |
946 | my $engl = ""; |
946 | my $rusl = ""; |
947 | my $rusl = ""; |
947 | 948 | ||
948 | $engl = $_sky_types{$tok}; |
949 | $engl = $_sky_types{$tok}; |
949 | $rusl = $_sky_types_ru{$tok}; |
950 | $rusl = $_sky_types_ru{$tok}; |
950 | push(@{$self->{SKY}}, $engl); |
951 | push(@{$self->{SKY}}, $engl); |
951 | push(@{$self->{SKY_RUS}}, $rusl); |
952 | push(@{$self->{SKY_RUS}}, $rusl); |
952 | push(@{$self->{SKY_RAW}}, $tok); |
953 | push(@{$self->{SKY_RAW}}, $tok); |
953 | print "[$tok] is an automatic sky condition.\n" if $self->{debug}; |
954 | print "[$tok] is an automatic sky condition.\n" if $self->{debug}; |
954 | $parsestate = $expect_temperature; |
955 | $parsestate = $expect_temperature; |
955 | next; |
956 | next; |
956 | }
|
957 | }
|
957 | 958 | ||
958 | ##
|
959 | ##
|
959 | ## Vertical visibility
|
960 | ## Vertical visibility
|
960 | ##
|
961 | ##
|
961 | 962 | ||
962 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^VV\d+$/ )){ |
963 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_temperature) and ( $tok =~ /^VV\d+$/ )){ |
963 | print "[$tok] is vertical visibility.\n" if $self->{debug}; |
964 | print "[$tok] is vertical visibility.\n" if $self->{debug}; |
964 | $parsestate = $expect_temperature; |
965 | $parsestate = $expect_temperature; |
965 | next; |
966 | next; |
966 | }
|
967 | }
|
967 | 968 | ||
968 | ##
|
969 | ##
|
969 | ## is it temperature and dew point info?
|
970 | ## is it temperature and dew point info?
|
970 | ##
|
971 | ##
|
971 | 972 | ||
972 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_pressure) and ($tok =~ /^(M?\d\d)\/(M?\d{0,2})/i)) |
973 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_pressure) and ($tok =~ /^(M?\d\d)\/(M?\d{0,2})/i)) |
973 | {
|
974 | {
|
974 | next if $self->{temp_dew}; |
975 | next if $self->{temp_dew}; |
975 | $self->{temp_dew} = $tok; |
976 | $self->{temp_dew} = $tok; |
976 | 977 | ||
977 | $self->{TEMP_C} = $1; |
978 | $self->{TEMP_C} = $1; |
978 | $self->{DEW_C} = $2; |
979 | $self->{DEW_C} = $2; |
979 | $self->{TEMP_C} =~ s/^M/-/; |
980 | $self->{TEMP_C} =~ s/^M/-/; |
980 | $self->{DEW_C} =~ s/^M/-/; |
981 | $self->{DEW_C} =~ s/^M/-/; |
981 | 982 | ||
982 | print "[$tok] is temperature/dew point information.\n" if $self->{debug}; |
983 | print "[$tok] is temperature/dew point information.\n" if $self->{debug}; |
983 | $parsestate = $expect_pressure; |
984 | $parsestate = $expect_pressure; |
984 | next; |
985 | next; |
985 | }
|
986 | }
|
986 | 987 | ||
987 | ##
|
988 | ##
|
988 | ## is it an altimeter setting? (in.Hg)
|
989 | ## is it an altimeter setting? (in.Hg)
|
989 | ##
|
990 | ##
|
990 | 991 | ||
991 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^A(\d\d)(\d\d)$/i)) |
992 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^A(\d\d)(\d\d)$/i)) |
992 | {
|
993 | {
|
993 | $self->{alt} = $tok; |
994 | $self->{alt} = $tok; |
994 | $self->{ALT} = "$1.$2"+0; |
995 | $self->{ALT} = "$1.$2"+0; |
995 | $self->{ALT_HP} = "$1.$2" * 33.863886; |
996 | $self->{ALT_HP} = "$1.$2" * 33.863886; |
996 | 997 | ||
997 | print "[$tok] is an altimeter setting.\n" if $self->{debug}; |
998 | print "[$tok] is an altimeter setting.\n" if $self->{debug}; |
998 | $parsestate = $expect_recentweather; |
999 | $parsestate = $expect_recentweather; |
999 | next; |
1000 | next; |
1000 | }
|
1001 | }
|
1001 | 1002 | ||
1002 | ##
|
1003 | ##
|
1003 | ## is it a pressure? (hPa)
|
1004 | ## is it a pressure? (hPa)
|
1004 | ##
|
1005 | ##
|
1005 | 1006 | ||
1006 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^Q(\d\d\d\d)$/i)) |
1007 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^Q(\d\d\d\d)$/i)) |
1007 | {
|
1008 | {
|
1008 | $self->{pressure} = $1; |
1009 | $self->{pressure} = $1; |
1009 | $self->{ALT_HP} = $1; |
1010 | $self->{ALT_HP} = $1; |
1010 | $self->{ALT} = 0.029529983 * $self->{pressure}; |
1011 | $self->{ALT} = 0.029529983 * $self->{pressure}; |
1011 | print "[$tok] is an air pressure.\n" if $self->{debug}; |
1012 | print "[$tok] is an air pressure.\n" if $self->{debug}; |
1012 | $parsestate = $expect_recentweather; |
1013 | $parsestate = $expect_recentweather; |
1013 | next; |
1014 | next; |
1014 | }
|
1015 | }
|
1015 | 1016 | ||
1016 | ##
|
1017 | ##
|
1017 | ## recent weather?
|
1018 | ## recent weather?
|
1018 | ##
|
1019 | ##
|
1019 | 1020 | ||
1020 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^RE($_weather_types_pat)$/)){ |
1021 | elsif (($parsestate >= $expect_modifier) and ($parsestate < $expect_remarks) and ($tok =~ /^RE($_weather_types_pat)$/)){ |
1021 | print "[$tok] is recent significant weather.\n" if $self->{debug}; |
1022 | print "[$tok] is recent significant weather.\n" if $self->{debug}; |
1022 | $parsestate = $expect_remarks; |
1023 | $parsestate = $expect_remarks; |
1023 | next; |
1024 | next; |
1024 | }
|
1025 | }
|
1025 | 1026 | ||
1026 | ##
|
1027 | ##
|
1027 | ## euro type trend?
|
1028 | ## euro type trend?
|
1028 | ##
|
1029 | ##
|
1029 | 1030 | ||
1030 | elsif (($parsestate >= $expect_modifier) and ($tok =~ /^$_trend_types_pat/)){ |
1031 | elsif (($parsestate >= $expect_modifier) and ($tok =~ /^$_trend_types_pat/)){ |
1031 | print "[$tok] is a trend.\n" if $self->{debug}; |
1032 | print "[$tok] is a trend.\n" if $self->{debug}; |
1032 | $parsestate = $expect_remarks; |
1033 | $parsestate = $expect_remarks; |
1033 | next; |
1034 | next; |
1034 | }
|
1035 | }
|
1035 | 1036 | ||
1036 | ##
|
1037 | ##
|
1037 | ## us type remarks? .. can happen quite early in the process already
|
1038 | ## us type remarks? .. can happen quite early in the process already
|
1038 | ##
|
1039 | ##
|
1039 | 1040 | ||
1040 | elsif (($parsestate >= $expect_modifier) and ($tok =~ /^RMK$/i)) |
1041 | elsif (($parsestate >= $expect_modifier) and ($tok =~ /^RMK$/i)) |
1041 | {
|
1042 | {
|
1042 | push(@{$self->{remarks}},$tok); |
1043 | push(@{$self->{remarks}},$tok); |
1043 | print "[$tok] is a (US type) remark.\n" if $self->{debug}; |
1044 | print "[$tok] is a (US type) remark.\n" if $self->{debug}; |
1044 | $parsestate = $expect_usremarks; |
1045 | $parsestate = $expect_usremarks; |
1045 | next; |
1046 | next; |
1046 | }
|
1047 | }
|
1047 | 1048 | ||
1048 | ##
|
1049 | ##
|
1049 | ## automatic station type?
|
1050 | ## automatic station type?
|
1050 | ##
|
1051 | ##
|
1051 | 1052 | ||
1052 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^A(O\d)$/i)) |
1053 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^A(O\d)$/i)) |
1053 | {
|
1054 | {
|
1054 | $self->{autostationtype} = $tok; |
1055 | $self->{autostationtype} = $tok; |
1055 | $self->{AUTO_STATIONTYPE} = $1; |
1056 | $self->{AUTO_STATIONTYPE} = $1; |
1056 | print "[$tok] is an automatic station type remark.\n" if $self->{debug}; |
1057 | print "[$tok] is an automatic station type remark.\n" if $self->{debug}; |
1057 | next; |
1058 | next; |
1058 | }
|
1059 | }
|
1059 | 1060 | ||
1060 | ##
|
1061 | ##
|
1061 | ## sea level pressure
|
1062 | ## sea level pressure
|
1062 | ##
|
1063 | ##
|
1063 | 1064 | ||
1064 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^SLP(\d+)/i)) |
1065 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^SLP(\d+)/i)) |
1065 | {
|
1066 | {
|
1066 | $self->{slp} = $tok; |
1067 | $self->{slp} = $tok; |
1067 | $self->{SLP} = "$1 mb"; |
1068 | $self->{SLP} = "$1 mb"; |
1068 | print "[$tok] is a sea level pressure.\n" if $self->{debug}; |
1069 | print "[$tok] is a sea level pressure.\n" if $self->{debug}; |
1069 | next; |
1070 | next; |
1070 | }
|
1071 | }
|
1071 | 1072 | ||
1072 | ##
|
1073 | ##
|
1073 | ## sea level pressure not available
|
1074 | ## sea level pressure not available
|
1074 | ##
|
1075 | ##
|
1075 | 1076 | ||
1076 | elsif (($parsestate == $expect_usremarks) and ($tok eq "SLPNO")) |
1077 | elsif (($parsestate == $expect_usremarks) and ($tok eq "SLPNO")) |
1077 | {
|
1078 | {
|
1078 | $self->{slp} = "SLPNO"; |
1079 | $self->{slp} = "SLPNO"; |
1079 | $self->{SLP} = "not available"; |
1080 | $self->{SLP} = "not available"; |
1080 | print "[$tok] is a sea level pressure.\n" if $self->{debug}; |
1081 | print "[$tok] is a sea level pressure.\n" if $self->{debug}; |
1081 | next; |
1082 | next; |
1082 | }
|
1083 | }
|
1083 | 1084 | ||
1084 | ##
|
1085 | ##
|
1085 | ## hourly precipitation
|
1086 | ## hourly precipitation
|
1086 | ##
|
1087 | ##
|
1087 | 1088 | ||
1088 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^P(\d\d\d\d)$/i)) |
1089 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^P(\d\d\d\d)$/i)) |
1089 | {
|
1090 | {
|
1090 | $self->{hourlyprecip} = $tok; |
1091 | $self->{hourlyprecip} = $tok; |
1091 | 1092 | ||
1092 | if ( $1 eq "0000" ) { |
1093 | if ( $1 eq "0000" ) { |
1093 | $self->{HOURLY_PRECIP} = "Trace"; |
1094 | $self->{HOURLY_PRECIP} = "Trace"; |
1094 | } else { |
1095 | } else { |
1095 | $self->{HOURLY_PRECIP} = $1; |
1096 | $self->{HOURLY_PRECIP} = $1; |
1096 | }
|
1097 | }
|
1097 | }
|
1098 | }
|
1098 | 1099 | ||
1099 | ##
|
1100 | ##
|
1100 | ## weather begin/end times
|
1101 | ## weather begin/end times
|
1101 | ##
|
1102 | ##
|
1102 | 1103 | ||
1103 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^($_weather_types_pat)([BE\d]+)$/i)) |
1104 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^($_weather_types_pat)([BE\d]+)$/i)) |
1104 | {
|
1105 | {
|
1105 | my $engl = ""; |
1106 | my $engl = ""; |
1106 | my $times = $2; |
1107 | my $times = $2; |
1107 | 1108 | ||
1108 | $self->{weatherlog} = $tok; |
1109 | $self->{weatherlog} = $tok; |
1109 | 1110 | ||
1110 | $engl = $_weather_types{$1}; |
1111 | $engl = $_weather_types{$1}; |
1111 | 1112 | ||
1112 | while ( $times =~ /(B|E)(\d\d)/g ) |
1113 | while ( $times =~ /(B|E)(\d\d)/g ) |
1113 | {
|
1114 | {
|
1114 | if ( $1 eq "B" ) { |
1115 | if ( $1 eq "B" ) { |
1115 | $engl .= " began :$2"; |
1116 | $engl .= " began :$2"; |
1116 | } else { |
1117 | } else { |
1117 | $engl .= " ended :$2"; |
1118 | $engl .= " ended :$2"; |
1118 | }
|
1119 | }
|
1119 | }
|
1120 | }
|
1120 | 1121 | ||
1121 | push(@{$self->{WEATHER_LOG}}, $engl); |
1122 | push(@{$self->{WEATHER_LOG}}, $engl); |
1122 | print "[$tok] is a weather log.\n" if $self->{debug}; |
1123 | print "[$tok] is a weather log.\n" if $self->{debug}; |
1123 | next; |
1124 | next; |
1124 | }
|
1125 | }
|
1125 | 1126 | ||
1126 | ##
|
1127 | ##
|
1127 | ## remarks for significant cloud types
|
1128 | ## remarks for significant cloud types
|
1128 | ##
|
1129 | ##
|
1129 | 1130 | ||
1130 | elsif (($parsestate >= $expect_recentweather) and ($tok eq "CB" || $tok eq "TCU")) |
1131 | elsif (($parsestate >= $expect_recentweather) and ($tok eq "CB" || $tok eq "TCU")) |
1131 | {
|
1132 | {
|
1132 | push(@{$self->{sigclouds}}, $tok); |
1133 | push(@{$self->{sigclouds}}, $tok); |
1133 | 1134 | ||
1134 | if ( $tok eq "CB" ) { |
1135 | if ( $tok eq "CB" ) { |
1135 | push(@{$self->{SIGCLOUDS}}, "Cumulonimbus"); |
1136 | push(@{$self->{SIGCLOUDS}}, "Cumulonimbus"); |
1136 | } elsif ( $tok eq "TCU" ) { |
1137 | } elsif ( $tok eq "TCU" ) { |
1137 | push(@{$self->{SIGCLOUDS}}, "Towering Cumulus"); |
1138 | push(@{$self->{SIGCLOUDS}}, "Towering Cumulus"); |
1138 | }
|
1139 | }
|
1139 | $parsestate = $expect_usremarks; |
1140 | $parsestate = $expect_usremarks; |
1140 | }
|
1141 | }
|
1141 | 1142 | ||
1142 | ##
|
1143 | ##
|
1143 | ## hourly temp/dewpoint
|
1144 | ## hourly temp/dewpoint
|
1144 | ##
|
1145 | ##
|
1145 | 1146 | ||
1146 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^T(\d)(\d\d)(\d)(\d)(\d\d)(\d)$/i)) |
1147 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^T(\d)(\d\d)(\d)(\d)(\d\d)(\d)$/i)) |
1147 | {
|
1148 | {
|
1148 | $self->{hourlytempdew} = $tok; |
1149 | $self->{hourlytempdew} = $tok; |
1149 | if ( $1 == 1 ) { |
1150 | if ( $1 == 1 ) { |
1150 | $self->{HOURLY_TEMP_C} = "-"; |
1151 | $self->{HOURLY_TEMP_C} = "-"; |
1151 | }
|
1152 | }
|
1152 | $self->{HOURLY_TEMP_C} .= "$2.$3"; |
1153 | $self->{HOURLY_TEMP_C} .= "$2.$3"; |
1153 | 1154 | ||
1154 | $self->{HOURLY_DEW_C} = ""; |
1155 | $self->{HOURLY_DEW_C} = ""; |
1155 | if ( $4 == 1 ) { |
1156 | if ( $4 == 1 ) { |
1156 | $self->{HOURLY_DEW_C} = "-"; |
1157 | $self->{HOURLY_DEW_C} = "-"; |
1157 | }
|
1158 | }
|
1158 | $self->{HOURLY_DEW_C} .= "$5.$6"; |
1159 | $self->{HOURLY_DEW_C} .= "$5.$6"; |
1159 | 1160 | ||
1160 | print "[$tok] is a hourly temp and dewpoint.\n" if $self->{debug}; |
1161 | print "[$tok] is a hourly temp and dewpoint.\n" if $self->{debug}; |
1161 | next; |
1162 | next; |
1162 | }
|
1163 | }
|
1163 | 1164 | ||
1164 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^QFE(\d\d\d)/i)) |
1165 | elsif (($parsestate == $expect_usremarks) and ($tok =~ /^QFE(\d\d\d)/i)) |
1165 | {
|
1166 | {
|
1166 | $self->{ALT_PL} = $1; |
1167 | $self->{ALT_PL} = $1; |
1167 | 1168 | ||
1168 | print "[$tok] is a pressure\n" if $self->{debug}; |
1169 | print "[$tok] is a pressure\n" if $self->{debug}; |
1169 | next; |
1170 | next; |
1170 | }
|
1171 | }
|
1171 | 1172 | ||
1172 | ##
|
1173 | ##
|
1173 | ## unknown, not in remarks yet
|
1174 | ## unknown, not in remarks yet
|
1174 | ##
|
1175 | ##
|
1175 | 1176 | ||
1176 | elsif ($parsestate < $expect_remarks) |
1177 | elsif ($parsestate < $expect_remarks) |
1177 | {
|
1178 | {
|
1178 | push(@{$self->{unknown}},$tok); |
1179 | push(@{$self->{unknown}},$tok); |
1179 | push(@{$self->{UNKNOWN}},$tok); |
1180 | push(@{$self->{UNKNOWN}},$tok); |
1180 | print "[$tok] is unexpected at this state.\n" if $self->{debug}; |
1181 | print "[$tok] is unexpected at this state.\n" if $self->{debug}; |
1181 | next; |
1182 | next; |
1182 | }
|
1183 | }
|
1183 | 1184 | ||
1184 | ##
|
1185 | ##
|
1185 | ## unknown. assume remarks
|
1186 | ## unknown. assume remarks
|
1186 | ##
|
1187 | ##
|
1187 | 1188 | ||
1188 | else
|
1189 | else
|
1189 | {
|
1190 | {
|
1190 | push(@{$self->{remarks}},$tok); |
1191 | push(@{$self->{remarks}},$tok); |
1191 | push(@{$self->{REMARKS}},$tok); |
1192 | push(@{$self->{REMARKS}},$tok); |
1192 | print "[$tok] is unknown remark.\n" if $self->{debug}; |
1193 | print "[$tok] is unknown remark.\n" if $self->{debug}; |
1193 | next; |
1194 | next; |
1194 | }
|
1195 | }
|
1195 | 1196 | ||
1196 | }
|
1197 | }
|
1197 | 1198 | ||
1198 | ##
|
1199 | ##
|
1199 | ## Now that the internal stuff is set, let's do the external
|
1200 | ## Now that the internal stuff is set, let's do the external
|
1200 | ## stuff.
|
1201 | ## stuff.
|
1201 | ##
|
1202 | ##
|
1202 | 1203 | ||
1203 | $self->{SITE} = $self->{site}; |
1204 | $self->{SITE} = $self->{site}; |
1204 | $self->{DATE} = substr($self->{date_time},0,2); |
1205 | $self->{DATE} = substr($self->{date_time},0,2); |
1205 | $self->{TIME} = substr($self->{date_time},2,4) . " UTC"; |
1206 | $self->{TIME} = substr($self->{date_time},2,4) . " UTC"; |
1206 | $self->{TIME} =~ s/(\d\d)(\d\d)/$1:$2/o; |
1207 | $self->{TIME} =~ s/(\d\d)(\d\d)/$1:$2/o; |
1207 | $self->{MOD} = $self->{modifier}; |
1208 | $self->{MOD} = $self->{modifier}; |
1208 | 1209 | ||
1209 | ##
|
1210 | ##
|
1210 | ## Okay, wind finally gets interesting.
|
1211 | ## Okay, wind finally gets interesting.
|
1211 | ##
|
1212 | ##
|
1212 | 1213 | ||
1213 | if ( defined $self->{wind} ) |
1214 | if ( defined $self->{wind} ) |
1214 | {
|
1215 | {
|
1215 | my $wind = $self->{wind}; |
1216 | my $wind = $self->{wind}; |
1216 | my $dir_deg = substr($wind,0,3); |
1217 | my $dir_deg = substr($wind,0,3); |
1217 | my $wind_speed; |
1218 | my $wind_speed; |
1218 | my $dir_eng = ""; |
1219 | my $dir_eng = ""; |
1219 | my $dir_rus = ""; |
1220 | my $dir_rus = ""; |
1220 | my $dir_abb = ""; |
1221 | my $dir_abb = ""; |
1221 | 1222 | ||
1222 | $wind_speed = $1 if($wind =~ /...(\d{2,3})/o); |
1223 | $wind_speed = $1 if($wind =~ /...(\d{2,3})/o); |
1223 | # Check for wind direction
|
1224 | # Check for wind direction
|
1224 | if ($dir_deg =~ /VRB/i) { |
1225 | if ($dir_deg =~ /VRB/i) { |
1225 | $dir_deg = $dir_eng = "Variable"; |
1226 | $dir_deg = $dir_eng = "Variable"; |
1226 | $dir_rus = "переменный"; |
1227 | $dir_rus = "переменный"; |
1227 | } else { |
1228 | } else { |
1228 | if ($wind_speed == 0 and $dir_deg == 0) { |
1229 | if ($wind_speed == 0 and $dir_deg == 0) { |
1229 | # Calm wind (00000KT in METAR)
|
1230 | # Calm wind (00000KT in METAR)
|
1230 | $dir_eng = "Calm"; |
1231 | $dir_eng = "Calm"; |
1231 | $dir_rus = "штиль"; |
1232 | $dir_rus = "штиль"; |
1232 | print "wind is calm\n" if $self->{debug}; |
1233 | print "wind is calm\n" if $self->{debug}; |
1233 | } elsif ($dir_deg < 15) { |
1234 | } elsif ($dir_deg < 15) { |
1234 | $dir_eng = "North"; |
1235 | $dir_eng = "North"; |
1235 | $dir_abb = "N"; |
1236 | $dir_abb = "N"; |
1236 | $dir_rus = "северный"; |
1237 | $dir_rus = "северный"; |
1237 | } elsif ($dir_deg < 30) { |
1238 | } elsif ($dir_deg < 30) { |
1238 | $dir_eng = "North/Northeast"; |
1239 | $dir_eng = "North/Northeast"; |
1239 | $dir_abb = "NNE"; |
1240 | $dir_abb = "NNE"; |
1240 | $dir_rus = "северо-северо-восточный"; |
1241 | $dir_rus = "северо-северо-восточный"; |
1241 | } elsif ($dir_deg < 60) { |
1242 | } elsif ($dir_deg < 60) { |
1242 | $dir_eng = "Northeast"; |
1243 | $dir_eng = "Northeast"; |
1243 | $dir_abb = "NE"; |
1244 | $dir_abb = "NE"; |
1244 | $dir_rus = "северо-восточный"; |
1245 | $dir_rus = "северо-восточный"; |
1245 | } elsif ($dir_deg < 75) { |
1246 | } elsif ($dir_deg < 75) { |
1246 | $dir_eng = "East/Northeast"; |
1247 | $dir_eng = "East/Northeast"; |
1247 | $dir_abb = "ENE"; |
1248 | $dir_abb = "ENE"; |
1248 | $dir_rus = "восточный-северо-восточный"; |
1249 | $dir_rus = "восточный-северо-восточный"; |
1249 | } elsif ($dir_deg < 105) { |
1250 | } elsif ($dir_deg < 105) { |
1250 | $dir_eng = "East"; |
1251 | $dir_eng = "East"; |
1251 | $dir_abb = "E"; |
1252 | $dir_abb = "E"; |
1252 | $dir_rus = "восточный"; |
1253 | $dir_rus = "восточный"; |
1253 | } elsif ($dir_deg < 120) { |
1254 | } elsif ($dir_deg < 120) { |
1254 | $dir_eng = "East/Southeast"; |
1255 | $dir_eng = "East/Southeast"; |
1255 | $dir_abb = "ESE"; |
1256 | $dir_abb = "ESE"; |
1256 | $dir_rus = "восточный-юго-восточный"; |
1257 | $dir_rus = "восточный-юго-восточный"; |
1257 | } elsif ($dir_deg < 150) { |
1258 | } elsif ($dir_deg < 150) { |
1258 | $dir_eng = "Southeast"; |
1259 | $dir_eng = "Southeast"; |
1259 | $dir_abb = "SE"; |
1260 | $dir_abb = "SE"; |
1260 | $dir_rus = "юго-восточный"; |
1261 | $dir_rus = "юго-восточный"; |
1261 | } elsif ($dir_deg < 165) { |
1262 | } elsif ($dir_deg < 165) { |
1262 | $dir_eng = "South/Southeast"; |
1263 | $dir_eng = "South/Southeast"; |
1263 | $dir_abb = "SSE"; |
1264 | $dir_abb = "SSE"; |
1264 | $dir_rus = "юго-юго-восточный"; |
1265 | $dir_rus = "юго-юго-восточный"; |
1265 | } elsif ($dir_deg < 195) { |
1266 | } elsif ($dir_deg < 195) { |
1266 | $dir_eng = "South"; |
1267 | $dir_eng = "South"; |
1267 | $dir_abb = "S"; |
1268 | $dir_abb = "S"; |
1268 | $dir_rus = "южный"; |
1269 | $dir_rus = "южный"; |
1269 | } elsif ($dir_deg < 210) { |
1270 | } elsif ($dir_deg < 210) { |
1270 | $dir_eng = "South/Southwest"; |
1271 | $dir_eng = "South/Southwest"; |
1271 | $dir_abb = "SSW"; |
1272 | $dir_abb = "SSW"; |
1272 | $dir_rus = "юго-юго-западный" |
1273 | $dir_rus = "юго-юго-западный" |
1273 | } elsif ($dir_deg < 240) { |
1274 | } elsif ($dir_deg < 240) { |
1274 | $dir_eng = "Southwest"; |
1275 | $dir_eng = "Southwest"; |
1275 | $dir_abb = "SW"; |
1276 | $dir_abb = "SW"; |
1276 | $dir_rus = "юго-западный"; |
1277 | $dir_rus = "юго-западный"; |
1277 | } elsif ($dir_deg < 265) { |
1278 | } elsif ($dir_deg < 265) { |
1278 | $dir_eng = "West/Southwest"; |
1279 | $dir_eng = "West/Southwest"; |
1279 | $dir_abb = "WSW"; |
1280 | $dir_abb = "WSW"; |
1280 | $dir_rus = "западно-юго-западный"; |
1281 | $dir_rus = "западно-юго-западный"; |
1281 | } elsif ($dir_deg < 285) { |
1282 | } elsif ($dir_deg < 285) { |
1282 | $dir_eng = "West"; |
1283 | $dir_eng = "West"; |
1283 | $dir_abb = "W"; |
1284 | $dir_abb = "W"; |
1284 | $dir_rus = "западный"; |
1285 | $dir_rus = "западный"; |
1285 | } elsif ($dir_deg < 300) { |
1286 | } elsif ($dir_deg < 300) { |
1286 | $dir_eng = "West/Northwest"; |
1287 | $dir_eng = "West/Northwest"; |
1287 | $dir_abb = "WNW"; |
1288 | $dir_abb = "WNW"; |
1288 | $dir_rus = "западно-северо-западный"; |
1289 | $dir_rus = "западно-северо-западный"; |
1289 | } elsif ($dir_deg < 330) { |
1290 | } elsif ($dir_deg < 330) { |
1290 | $dir_eng = "Northwest"; |
1291 | $dir_eng = "Northwest"; |
1291 | $dir_abb = "NW"; |
1292 | $dir_abb = "NW"; |
1292 | $dir_rus = "северо-западный"; |
1293 | $dir_rus = "северо-западный"; |
1293 | } elsif ($dir_deg < 345) { |
1294 | } elsif ($dir_deg < 345) { |
1294 | $dir_eng = "North/Northwest"; |
1295 | $dir_eng = "North/Northwest"; |
1295 | $dir_abb = "NNW"; |
1296 | $dir_abb = "NNW"; |
1296 | $dir_rus = "северо-северо-западный"; |
1297 | $dir_rus = "северо-северо-западный"; |
1297 | } elsif ($dir_deg < 360) { |
1298 | } elsif ($dir_deg < 360) { |
1298 | $dir_eng = "North"; |
1299 | $dir_eng = "North"; |
1299 | $dir_abb = "N"; |
1300 | $dir_abb = "N"; |
1300 | $dir_rus = "северный"; |
1301 | $dir_rus = "северный"; |
1301 | } else { |
1302 | } else { |
1302 | # Shouldn't happen, but if for some reason the METAR
|
1303 | # Shouldn't happen, but if for some reason the METAR
|
1303 | # information doesn't contain a reasonable direction...
|
1304 | # information doesn't contain a reasonable direction...
|
1304 | $dir_eng = "undeterminable"; |
1305 | $dir_eng = "undeterminable"; |
1305 | $dir_rus = "неопределенный"; |
1306 | $dir_rus = "неопределенный"; |
1306 | }
|
1307 | }
|
1307 | }
|
1308 | }
|
1308 | 1309 | ||
1309 | my $kts_speed = undef; |
1310 | my $kts_speed = undef; |
1310 | my $mph_speed = undef; |
1311 | my $mph_speed = undef; |
1311 | my $mps_speed = undef; |
1312 | my $mps_speed = undef; |
1312 | 1313 | ||
1313 | my $kts_gust = ""; |
1314 | my $kts_gust = ""; |
1314 | my $mph_gust = ""; |
1315 | my $mph_gust = ""; |
1315 | my $mps_gust = ""; |
1316 | my $mps_gust = ""; |
1316 | 1317 | ||
1317 | # parse knots
|
1318 | # parse knots
|
1318 | 1319 | ||
1319 | if ($self->{windtype} == $wt_knots){ |
1320 | if ($self->{windtype} == $wt_knots){ |
1320 | $wind =~ /...(\d\d\d?)/o; |
1321 | $wind =~ /...(\d\d\d?)/o; |
1321 | $kts_speed = $1; |
1322 | $kts_speed = $1; |
1322 | $mph_speed = $kts_speed * 1.15077945; |
1323 | $mph_speed = $kts_speed * 1.15077945; |
1323 | $mps_speed = $kts_speed * 0.514444444; |
1324 | $mps_speed = $kts_speed * 0.514444444; |
1324 | 1325 | ||
1325 | if ($wind =~ /.{5,6}G(\d\d\d?)/o) { |
1326 | if ($wind =~ /.{5,6}G(\d\d\d?)/o) { |
1326 | $kts_gust = $1; |
1327 | $kts_gust = $1; |
1327 | $mph_gust = $kts_gust * 1.15077945; |
1328 | $mph_gust = $kts_gust * 1.15077945; |
1328 | $mps_gust = $kts_gust * 0.514444444; |
1329 | $mps_gust = $kts_gust * 0.514444444; |
1329 | }
|
1330 | }
|
1330 | # else: parse meters/second
|
1331 | # else: parse meters/second
|
1331 | } elsif ($self->{windtype} == $wt_mps){ |
1332 | } elsif ($self->{windtype} == $wt_mps){ |
1332 | $wind=~ /...(\d\d\d?)/o; |
1333 | $wind=~ /...(\d\d\d?)/o; |
1333 | $mps_speed = $1; |
1334 | $mps_speed = $1; |
1334 | $kts_speed = $mps_speed * 1.9438445; # units |
1335 | $kts_speed = $mps_speed * 1.9438445; # units |
1335 | $mph_speed = $mps_speed * 2.2369363; |
1336 | $mph_speed = $mps_speed * 2.2369363; |
1336 | if ($wind =~ /\d{5,6}G(\d\d\d?)/o) { |
1337 | if ($wind =~ /\d{5,6}G(\d\d\d?)/o) { |
1337 | $mps_gust = $1; |
1338 | $mps_gust = $1; |
1338 | $kts_gust = $mps_gust * 1.9438445; |
1339 | $kts_gust = $mps_gust * 1.9438445; |
1339 | $mph_gust = $mps_gust * 2.2369363; |
1340 | $mph_gust = $mps_gust * 2.2369363; |
1340 | }
|
1341 | }
|
1341 | } else { |
1342 | } else { |
1342 | warn "Geo::ModMETAR Parser error: unknown windtype\n"; |
1343 | warn "Geo::ModMETAR Parser error: unknown windtype\n"; |
1343 | }
|
1344 | }
|
1344 | 1345 | ||
1345 | $self->{WIND_KTS} = $kts_speed; |
1346 | $self->{WIND_KTS} = $kts_speed; |
1346 | $self->{WIND_MPH} = $mph_speed; |
1347 | $self->{WIND_MPH} = $mph_speed; |
1347 | $self->{WIND_MS} = $mps_speed; |
1348 | $self->{WIND_MS} = $mps_speed; |
1348 | 1349 | ||
1349 | $self->{WIND_GUST_KTS} = $kts_gust; |
1350 | $self->{WIND_GUST_KTS} = $kts_gust; |
1350 | $self->{WIND_GUST_MPH} = $mph_gust; |
1351 | $self->{WIND_GUST_MPH} = $mph_gust; |
1351 | $self->{WIND_GUST_MS} = $mps_gust; |
1352 | $self->{WIND_GUST_MS} = $mps_gust; |
1352 | 1353 | ||
1353 | $self->{WIND_DIR_DEG} = $dir_deg; |
1354 | $self->{WIND_DIR_DEG} = $dir_deg; |
1354 | $self->{WIND_DIR_ENG} = $dir_eng; |
1355 | $self->{WIND_DIR_ENG} = $dir_eng; |
1355 | $self->{WIND_DIR_ABB} = $dir_abb; |
1356 | $self->{WIND_DIR_ABB} = $dir_abb; |
1356 | $self->{WIND_DIR_RUS} = $dir_rus; |
1357 | $self->{WIND_DIR_RUS} = $dir_rus; |
1357 | 1358 | ||
1358 | }
|
1359 | }
|
1359 | 1360 | ||
1360 | ##
|
1361 | ##
|
1361 | ## wind variation
|
1362 | ## wind variation
|
1362 | ##
|
1363 | ##
|
1363 | 1364 | ||
1364 | if (defined $self->{windvar}) |
1365 | if (defined $self->{windvar}) |
1365 | {
|
1366 | {
|
1366 | if ($self->{windvar} =~ /^(\d\d\d)V(\d\d\d)$/){ |
1367 | if ($self->{windvar} =~ /^(\d\d\d)V(\d\d\d)$/){ |
1367 | $self->{WIND_VAR} = "Varying between $1 and $2"; |
1368 | $self->{WIND_VAR} = "Varying between $1 and $2"; |
1368 | $self->{WIND_VAR_1} = $1; |
1369 | $self->{WIND_VAR_1} = $1; |
1369 | $self->{WIND_VAR_2} = $2; |
1370 | $self->{WIND_VAR_2} = $2; |
1370 | my @direction = ( |
1371 | my @direction = ( |
1371 | 15 => "North", |
1372 | 15 => "North", |
1372 | 30 => "North/Northeast", |
1373 | 30 => "North/Northeast", |
1373 | 60 => "Northeast", |
1374 | 60 => "Northeast", |
1374 | 75 => "East/Northeast", |
1375 | 75 => "East/Northeast", |
1375 | 105 => "East", |
1376 | 105 => "East", |
1376 | 120 => "East/Southeast", |
1377 | 120 => "East/Southeast", |
1377 | 150 => "Southeast", |
1378 | 150 => "Southeast", |
1378 | 165 => "South/Southeast", |
1379 | 165 => "South/Southeast", |
1379 | 195 => "South", |
1380 | 195 => "South", |
1380 | 210 => "South/Southwest", |
1381 | 210 => "South/Southwest", |
1381 | 240 => "Southwest", |
1382 | 240 => "Southwest", |
1382 | 265 => "West/Southwest", |
1383 | 265 => "West/Southwest", |
1383 | 285 => "West", |
1384 | 285 => "West", |
1384 | 300 => "West/Northwest", |
1385 | 300 => "West/Northwest", |
1385 | 330 => "Northwest", |
1386 | 330 => "Northwest", |
1386 | 345 => "North/Northwest", |
1387 | 345 => "North/Northwest", |
1387 | 360 => "North", |
1388 | 360 => "North", |
1388 | 1000 => "undeterminable"); |
1389 | 1000 => "undeterminable"); |
1389 | for(my $x = 0; $x < $#direction; $x += 2) { |
1390 | for(my $x = 0; $x < $#direction; $x += 2) { |
1390 | if($self->{WIND_VAR_1} < $direction[$x]) { |
1391 | if($self->{WIND_VAR_1} < $direction[$x]) { |
1391 | $self->{WIND_VAR_ENG_1} = $direction[$x+1]; |
1392 | $self->{WIND_VAR_ENG_1} = $direction[$x+1]; |
1392 | last; |
1393 | last; |
1393 | }
|
1394 | }
|
1394 | }
|
1395 | }
|
1395 | for(my $x = 0; $x < $#direction; $x += 2) { |
1396 | for(my $x = 0; $x < $#direction; $x += 2) { |
1396 | if($self->{WIND_VAR_2} < $direction[$x]) { |
1397 | if($self->{WIND_VAR_2} < $direction[$x]) { |
1397 | $self->{WIND_VAR_ENG_2} = $direction[$x+1]; |
1398 | $self->{WIND_VAR_ENG_2} = $direction[$x+1]; |
1398 | last; |
1399 | last; |
1399 | }
|
1400 | }
|
1400 | }
|
1401 | }
|
1401 | }
|
1402 | }
|
1402 | }
|
1403 | }
|
1403 | 1404 | ||
1404 | ##
|
1405 | ##
|
1405 | ## Calculate relative humidity
|
1406 | ## Calculate relative humidity
|
1406 | ##
|
1407 | ##
|
1407 | 1408 | ||
1408 | {
|
1409 | {
|
1409 | my $esat = 6.11*(10**((7.5*$self->{TEMP_C})/(237.7+$self->{TEMP_C}))); |
1410 | my $esat = 6.11*(10**((7.5*$self->{TEMP_C})/(237.7+$self->{TEMP_C}))); |
1410 | my $esurf = 6.11*(10**((7.5*$self->{DEW_C})/(237.7+$self->{DEW_C}))); |
1411 | my $esurf = 6.11*(10**((7.5*$self->{DEW_C})/(237.7+$self->{DEW_C}))); |
1411 | 1412 | ||
1412 | $self->{RH} = 100.0 * ($esurf/$esat); |
1413 | $self->{RH} = 100.0 * ($esurf/$esat); |
1413 | }
|
1414 | }
|
1414 | 1415 | ||
1415 | ##
|
1416 | ##
|
1416 | ## Calculate windchill temperature
|
1417 | ## Calculate windchill temperature
|
1417 | ##
|
1418 | ##
|
1418 | 1419 | ||
1419 | {
|
1420 | {
|
1420 | my $windspeed = $self->{WIND_MS}*3.6; |
1421 | my $windspeed = $self->{WIND_MS}*3.6; |
1421 | $self->{TEMP_WC} = 13.12 + 0.6215*$self->{TEMP_C} - 11.37*($windspeed**0.16) + 0.3965*$self->{TEMP_C}*($windspeed**0.16); |
1422 | $self->{TEMP_WC} = 13.12 + 0.6215*$self->{TEMP_C} - 11.37*($windspeed**0.16) + 0.3965*$self->{TEMP_C}*($windspeed**0.16); |
1422 | }
|
1423 | }
|
1423 | 1424 | ||
1424 | ##
|
1425 | ##
|
1425 | ## Visibility.
|
1426 | ## Visibility.
|
1426 | ##
|
1427 | ##
|
1427 | 1428 | ||
1428 | if($self->{visibility}) { |
1429 | if($self->{visibility}) { |
1429 | my $vis = $self->{visibility}; |
1430 | my $vis = $self->{visibility}; |
1430 | # test for statute miles
|
1431 | # test for statute miles
|
1431 | if ($vis =~ /SM$/){ |
1432 | if ($vis =~ /SM$/){ |
1432 | $vis =~ s/SM$//oi; # nuke the "SM" |
1433 | $vis =~ s/SM$//oi; # nuke the "SM" |
1433 | if ($vis =~ /M(\d\/\d)/o) { |
1434 | if ($vis =~ /M(\d\/\d)/o) { |
1434 | $self->{VISIBILITY} = "Less than $1 statute miles"; |
1435 | $self->{VISIBILITY} = "Less than $1 statute miles"; |
1435 | $self->{VISIBILITY_RUS} = "Менее чем $1 статутных миль"; |
1436 | $self->{VISIBILITY_RUS} = "Менее чем $1 статутных миль"; |
1436 | } else { |
1437 | } else { |
1437 | $self->{VISIBILITY} = $vis . " statute miles"; |
1438 | $self->{VISIBILITY} = $vis . " statute miles"; |
1438 | $self->{VISIBILITY} = $vis . " статутных миль"; |
1439 | $self->{VISIBILITY} = $vis . " статутных миль"; |
1439 | } # end if |
1440 | } # end if |
1440 | # auto metars can have non-directional visibility reports
|
1441 | # auto metars can have non-directional visibility reports
|
1441 | } elsif (($self->{MOD} eq 'AUTO') and ($vis =~ /(\d+)NDV$/)){ |
1442 | } elsif (($self->{MOD} eq 'AUTO') and ($vis =~ /(\d+)NDV$/)){ |
1442 | $self->{VISIBILITY} = "$1 meters non-directional visibility"; |
1443 | $self->{VISIBILITY} = "$1 meters non-directional visibility"; |
1443 | $self->{VISIBILITY_RUS} = "$1 м непрямой видимости"; |
1444 | $self->{VISIBILITY_RUS} = "$1 м непрямой видимости"; |
1444 | } else { |
1445 | } else { |
1445 | $self->{VISIBILITY} = $vis . " meters"; |
1446 | $self->{VISIBILITY} = $vis . " meters"; |
1446 | if ($vis<1000) { |
1447 | if ($vis<1000) { |
1447 | $self->{VISIBILITY_RUS} = $vis . " м"; |
1448 | $self->{VISIBILITY_RUS} = $vis . " м"; |
1448 | } else { |
1449 | } else { |
1449 | $vis = $vis/1000; |
1450 | $vis = $vis/1000; |
1450 | if (abs($vis-int($vis))>=0.5) { |
1451 | if (abs($vis-int($vis))>=0.5) { |
1451 | $vis = int($vis)+1; |
1452 | $vis = int($vis)+1; |
1452 | } else { |
1453 | } else { |
1453 | $vis = int($vis); |
1454 | $vis = int($vis); |
1454 | }
|
1455 | }
|
1455 | $self->{VISIBILITY_RUS} = $vis . " км"; |
1456 | $self->{VISIBILITY_RUS} = $vis . " км"; |
1456 | }
|
1457 | }
|
1457 | }
|
1458 | }
|
1458 | }
|
1459 | }
|
1459 | 1460 | ||
1460 | ##
|
1461 | ##
|
1461 | ## Calculate F temps for all C temps
|
1462 | ## Calculate F temps for all C temps
|
1462 | ##
|
1463 | ##
|
1463 | 1464 | ||
1464 | foreach my $key ( keys(%$self) ) |
1465 | foreach my $key ( keys(%$self) ) |
1465 | {
|
1466 | {
|
1466 | if ( uc($key) eq $key && $key =~ /^(.*)_C$/ ) |
1467 | if ( uc($key) eq $key && $key =~ /^(.*)_C$/ ) |
1467 | {
|
1468 | {
|
1468 | my $fkey = $1 . "_F"; |
1469 | my $fkey = $1 . "_F"; |
1469 | 1470 | ||
1470 | next unless defined $self->{$key} && $self->{$key}; |
1471 | next unless defined $self->{$key} && $self->{$key}; |
1471 | 1472 | ||
1472 | $self->{$fkey} = sprintf("%.1f", (($self->{$key} * (9/5)) + 32)); |
1473 | $self->{$fkey} = sprintf("%.1f", (($self->{$key} * (9/5)) + 32)); |
1473 | }
|
1474 | }
|
1474 | }
|
1475 | }
|
1475 | 1476 | ||
1476 | # join the runway group
|
1477 | # join the runway group
|
1477 | 1478 | ||
1478 | $self->{runway} = join(', ' , @{$self->{RUNWAY}}); |
1479 | $self->{runway} = join(', ' , @{$self->{RUNWAY}}); |
1479 | 1480 | ||
1480 | }
|
1481 | }
|
1481 | 1482 | ||
1482 | ##
|
1483 | ##
|
1483 | ## Print the tokens--usually when debugging.
|
1484 | ## Print the tokens--usually when debugging.
|
1484 | ##
|
1485 | ##
|
1485 | 1486 | ||
1486 | sub print_tokens
|
1487 | sub print_tokens
|
1487 | {
|
1488 | {
|
1488 | my $self = shift; |
1489 | my $self = shift; |
1489 | my $tok; |
1490 | my $tok; |
1490 | foreach $tok (@{$self->{tokens}}) { |
1491 | foreach $tok (@{$self->{tokens}}) { |
1491 | print "> $tok\n"; |
1492 | print "> $tok\n"; |
1492 | }
|
1493 | }
|
1493 | }
|
1494 | }
|
1494 | 1495 | ||
1495 | ##
|
1496 | ##
|
1496 | ## Turn debugging on/off.
|
1497 | ## Turn debugging on/off.
|
1497 | ##
|
1498 | ##
|
1498 | 1499 | ||
1499 | sub debug
|
1500 | sub debug
|
1500 | {
|
1501 | {
|
1501 | my $self = shift; |
1502 | my $self = shift; |
1502 | my $flag = shift; |
1503 | my $flag = shift; |
1503 | return $self->{debug} unless defined $flag; |
1504 | return $self->{debug} unless defined $flag; |
1504 | 1505 | ||
1505 | if (($flag eq "Y") or ($flag eq "y") or ($flag == 1)) { |
1506 | if (($flag eq "Y") or ($flag eq "y") or ($flag == 1)) { |
1506 | $self->{debug} = 1; |
1507 | $self->{debug} = 1; |
1507 | } elsif (($flag eq "N") or ($flag eq "n") or ($flag == 0)) { |
1508 | } elsif (($flag eq "N") or ($flag eq "n") or ($flag == 0)) { |
1508 | $self->{debug} = 0; |
1509 | $self->{debug} = 0; |
1509 | }
|
1510 | }
|
1510 | 1511 | ||
1511 | return $self->{debug}; |
1512 | return $self->{debug}; |
1512 | }
|
1513 | }
|
1513 | 1514 | ||
1514 | ##
|
1515 | ##
|
1515 | ## Dump internal data structure. Useful for debugging and such.
|
1516 | ## Dump internal data structure. Useful for debugging and such.
|
1516 | ##
|
1517 | ##
|
1517 | 1518 | ||
1518 | sub dump |
1519 | sub dump |
1519 | {
|
1520 | {
|
1520 | my $self = shift; |
1521 | my $self = shift; |
1521 | 1522 | ||
1522 | print "Modified METAR dump follows.\n\n"; |
1523 | print "Modified METAR dump follows.\n\n"; |
1523 | 1524 | ||
1524 | print "type: $self->{type}\n"; |
1525 | print "type: $self->{type}\n"; |
1525 | print "site: $self->{site}\n"; |
1526 | print "site: $self->{site}\n"; |
1526 | print "date_time: $self->{date_time}\n"; |
1527 | print "date_time: $self->{date_time}\n"; |
1527 | print "modifier: $self->{modifier}\n"; |
1528 | print "modifier: $self->{modifier}\n"; |
1528 | print "wind: $self->{wind}\n"; |
1529 | print "wind: $self->{wind}\n"; |
1529 | print "variable wind: $self->{vrbwind}\n"; |
1530 | print "variable wind: $self->{vrbwind}\n"; |
1530 | print "visibility: $self->{visibility}\n"; |
1531 | print "visibility: $self->{visibility}\n"; |
1531 | print "runway: $self->{runway}\n"; |
1532 | print "runway: $self->{runway}\n"; |
1532 | print "weather: " . join(', ', @{$self->{weather}}) . "\n"; |
1533 | print "weather: " . join(', ', @{$self->{weather}}) . "\n"; |
1533 | print "sky: " . join(', ', @{$self->{sky}}) . "\n"; |
1534 | print "sky: " . join(', ', @{$self->{sky}}) . "\n"; |
1534 | print "temp_dew: $self->{temp_dew}\n"; |
1535 | print "temp_dew: $self->{temp_dew}\n"; |
1535 | print "alt: $self->{alt}\n"; |
1536 | print "alt: $self->{alt}\n"; |
1536 | print "pressure: $self->{pressure}\n"; |
1537 | print "pressure: $self->{pressure}\n"; |
1537 | print "slp: $self->{slp}\n"; |
1538 | print "slp: $self->{slp}\n"; |
1538 | print "remarks: " . join (', ', @{$self->{remarks}}) . "\n"; |
1539 | print "remarks: " . join (', ', @{$self->{remarks}}) . "\n"; |
1539 | print "\n"; |
1540 | print "\n"; |
1540 | 1541 | ||
1541 | foreach my $var ( sort(keys(%$self)) ) |
1542 | foreach my $var ( sort(keys(%$self)) ) |
1542 | {
|
1543 | {
|
1543 | next if ( uc($var) ne $var ); |
1544 | next if ( uc($var) ne $var ); |
1544 | 1545 | ||
1545 | if ( ref($self->{$var}) eq "ARRAY" ) |
1546 | if ( ref($self->{$var}) eq "ARRAY" ) |
1546 | {
|
1547 | {
|
1547 | print "$var: ", join(", ", @{$self->{$var}}), "\n"; |
1548 | print "$var: ", join(", ", @{$self->{$var}}), "\n"; |
1548 | }
|
1549 | }
|
1549 | else
|
1550 | else
|
1550 | {
|
1551 | {
|
1551 | print "$var: ", $self->{$var}, "\n"; |
1552 | print "$var: ", $self->{$var}, "\n"; |
1552 | }
|
1553 | }
|
1553 | }
|
1554 | }
|
1554 | }
|
1555 | }
|
1555 | 1556 | ||
1556 | 1; |
1557 | 1; |
1557 | 1558 | ||
1558 | __END__
|
1559 | __END__
|
1559 | 1560 | ||
1560 | =head1 NAME
|
1561 | =head1 NAME
|
1561 | 1562 | ||
1562 | Mod::Geo::METAR - Process aviation weather reports in the METAR format.
|
1563 | Mod::Geo::METAR - Process aviation weather reports in the METAR format.
|
1563 | 1564 | ||
1564 | =head1 SYNOPSIS
|
1565 | =head1 SYNOPSIS
|
1565 | 1566 | ||
1566 | use Mod::Geo::METAR;
|
1567 | use Mod::Geo::METAR;
|
1567 | use strict;
|
1568 | use strict;
|
1568 | 1569 | ||
1569 | my $m = new Mod::Geo::METAR;
|
1570 | my $m = new Mod::Geo::METAR;
|
1570 | $m->metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");
|
1571 | $m->metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");
|
1571 | print $m->dump;
|
1572 | print $m->dump;
|
1572 | 1573 | ||
1573 | exit;
|
1574 | exit;
|
1574 | 1575 | ||
1575 | =head1 DESCRIPTION
|
1576 | =head1 DESCRIPTION
|
1576 | 1577 | ||
1577 | METAR reports are available on-line, thanks to the National Weather Service.
|
1578 | METAR reports are available on-line, thanks to the National Weather Service.
|
1578 | Since reading the METAR format isn't easy for non-pilots, these reports are
|
1579 | Since reading the METAR format isn't easy for non-pilots, these reports are
|
1579 | relatively useles to the common man who just wants a quick glace at the
|
1580 | relatively useles to the common man who just wants a quick glace at the
|
1580 | weather. This module tries to parse the METAR reports so the data can be
|
1581 | weather. This module tries to parse the METAR reports so the data can be
|
1581 | used to create readable weather reports and/or process the data in
|
1582 | used to create readable weather reports and/or process the data in
|
1582 | applications.
|
1583 | applications.
|
1583 | 1584 | ||
1584 | =head1 USAGE
|
1585 | =head1 USAGE
|
1585 | 1586 | ||
1586 | =head2 How you might use this
|
1587 | =head2 How you might use this
|
1587 | 1588 | ||
1588 | Here is how you I<might> use the Geo::METAR module.
|
1589 | Here is how you I<might> use the Geo::METAR module.
|
1589 | 1590 | ||
1590 | One use that I have had for this module is to query the NWS METAR page
|
1591 | One use that I have had for this module is to query the NWS METAR page
|
1591 | (using the LWP modules) at:
|
1592 | (using the LWP modules) at:
|
1592 | 1593 | ||
1593 | I<http://weather.noaa.gov/cgi-bin/mgetmetar.pl?cccc=EHSB>
|
1594 | I<http://weather.noaa.gov/cgi-bin/mgetmetar.pl?cccc=EHSB>
|
1594 | 1595 | ||
1595 | to get an
|
1596 | to get an
|
1596 | up-to-date METAR. Then, I scan thru the output, looking for what looks
|
1597 | up-to-date METAR. Then, I scan thru the output, looking for what looks
|
1597 | like a METAR string (that's not hard in Perl). Oh, EHSB can be any site
|
1598 | like a METAR string (that's not hard in Perl). Oh, EHSB can be any site
|
1598 | location code where there is a reporting station.
|
1599 | location code where there is a reporting station.
|
1599 | 1600 | ||
1600 | I then pass the METAR into this module and get the info I want. I can
|
1601 | I then pass the METAR into this module and get the info I want. I can
|
1601 | then update my webcam page with the current temperature, sky conditions, or
|
1602 | then update my webcam page with the current temperature, sky conditions, or
|
1602 | whatnot. See for yourself at http://webcam.idefix.net/
|
1603 | whatnot. See for yourself at http://webcam.idefix.net/
|
1603 | 1604 | ||
1604 | See the BUGS section for a remark about multiple passes with the same
|
1605 | See the BUGS section for a remark about multiple passes with the same
|
1605 | Geo::METAR object.
|
1606 | Geo::METAR object.
|
1606 | 1607 | ||
1607 | =head2 Functions
|
1608 | =head2 Functions
|
1608 | 1609 | ||
1609 | The following functions are defined in the METAR module. Most of
|
1610 | The following functions are defined in the METAR module. Most of
|
1610 | them are I<public>, meaning that you're supposed to use
|
1611 | them are I<public>, meaning that you're supposed to use
|
1611 | them. Some are I<private>, meaning that you're not supposed to use
|
1612 | them. Some are I<private>, meaning that you're not supposed to use
|
1612 | them -- but I won't stop you. Assume that functions are I<public>
|
1613 | them -- but I won't stop you. Assume that functions are I<public>
|
1613 | unless otherwise documented.
|
1614 | unless otherwise documented.
|
1614 | 1615 | ||
1615 | =over
|
1616 | =over
|
1616 | 1617 | ||
1617 | =item metar()
|
1618 | =item metar()
|
1618 | 1619 | ||
1619 | metar() is the function to whwich you should pass a METAR string. It
|
1620 | metar() is the function to whwich you should pass a METAR string. It
|
1620 | will take care of decomposing it into its component parts converting
|
1621 | will take care of decomposing it into its component parts converting
|
1621 | the units and so on.
|
1622 | the units and so on.
|
1622 | 1623 | ||
1623 | Example: C<$m-E<gt>metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");>
|
1624 | Example: C<$m-E<gt>metar("KFDY 251450Z 21012G21KT 8SM OVC065 04/M01 A3010 RMK 57014");>
|
1624 | 1625 | ||
1625 | =item debug()
|
1626 | =item debug()
|
1626 | 1627 | ||
1627 | debug() toggles debugging messages. By default, debugging is turned
|
1628 | debug() toggles debugging messages. By default, debugging is turned
|
1628 | B<off>. Turn it on if you are developing METAR or having trouble with
|
1629 | B<off>. Turn it on if you are developing METAR or having trouble with
|
1629 | it.
|
1630 | it.
|
1630 | 1631 | ||
1631 | debug() understands all of the folloing:
|
1632 | debug() understands all of the folloing:
|
1632 | 1633 | ||
1633 | Enable Disable
|
1634 | Enable Disable
|
1634 | ------ -------
|
1635 | ------ -------
|
1635 | 1 0
|
1636 | 1 0
|
1636 | 'yes' 'no'
|
1637 | 'yes' 'no'
|
1637 | 'on' 'off'
|
1638 | 'on' 'off'
|
1638 | 1639 | ||
1639 | If you contact me for help, I'll likely ask you for some debugging
|
1640 | If you contact me for help, I'll likely ask you for some debugging
|
1640 | output.
|
1641 | output.
|
1641 | 1642 | ||
1642 | Example: C<$m-E<gt>debug(1);>
|
1643 | Example: C<$m-E<gt>debug(1);>
|
1643 | 1644 | ||
1644 | =item dump()
|
1645 | =item dump()
|
1645 | 1646 | ||
1646 | dump() will dump the internal data structure for the METAR in a
|
1647 | dump() will dump the internal data structure for the METAR in a
|
1647 | semi-human readable format.
|
1648 | semi-human readable format.
|
1648 | 1649 | ||
1649 | Example: C<$m-E<gt>dump;>
|
1650 | Example: C<$m-E<gt>dump;>
|
1650 | 1651 | ||
1651 | =item version()
|
1652 | =item version()
|
1652 | 1653 | ||
1653 | version() will print out the current version.
|
1654 | version() will print out the current version.
|
1654 | 1655 | ||
1655 | Example: C<print $m-E<gt>version;>
|
1656 | Example: C<print $m-E<gt>version;>
|
1656 | 1657 | ||
1657 | =item _tokenize()
|
1658 | =item _tokenize()
|
1658 | 1659 | ||
1659 | B<PRIVATE>
|
1660 | B<PRIVATE>
|
1660 | 1661 | ||
1661 | Called internally to break the METAR into its component tokens.
|
1662 | Called internally to break the METAR into its component tokens.
|
1662 | 1663 | ||
1663 | =item _process()
|
1664 | =item _process()
|
1664 | 1665 | ||
1665 | B<PRIVATE>
|
1666 | B<PRIVATE>
|
1666 | 1667 | ||
1667 | Used to make sense of the tokens found in B<_tokenize()>.
|
1668 | Used to make sense of the tokens found in B<_tokenize()>.
|
1668 | 1669 | ||
1669 | =back
|
1670 | =back
|
1670 | 1671 | ||
1671 | =head2 Variables
|
1672 | =head2 Variables
|
1672 | 1673 | ||
1673 | After you've called B<metar()>, you'd probably like to get at
|
1674 | After you've called B<metar()>, you'd probably like to get at
|
1674 | the individual values for things like temperature, dew point,
|
1675 | the individual values for things like temperature, dew point,
|
1675 | and so on. You do that by accessing individual variables via
|
1676 | and so on. You do that by accessing individual variables via
|
1676 | the METAR object.
|
1677 | the METAR object.
|
1677 | 1678 | ||
1678 | This section lists those variables and what they represent.
|
1679 | This section lists those variables and what they represent.
|
1679 | 1680 | ||
1680 | If you call B<dump()>, you'll find that it spits all of these
|
1681 | If you call B<dump()>, you'll find that it spits all of these
|
1681 | out.
|
1682 | out.
|
1682 | 1683 | ||
1683 | =over
|
1684 | =over
|
1684 | 1685 | ||
1685 | =item VERSION
|
1686 | =item VERSION
|
1686 | 1687 | ||
1687 | The version of METAR.pm that you're using.
|
1688 | The version of METAR.pm that you're using.
|
1688 | 1689 | ||
1689 | =item METAR
|
1690 | =item METAR
|
1690 | 1691 | ||
1691 | The actual, raw METAR.
|
1692 | The actual, raw METAR.
|
1692 | 1693 | ||
1693 | =item TYPE
|
1694 | =item TYPE
|
1694 | 1695 | ||
1695 | Report type in English ("Routine Weather Report" or "Special Weather Report")
|
1696 | Report type in English ("Routine Weather Report" or "Special Weather Report")
|
1696 | 1697 | ||
1697 | =item SITE
|
1698 | =item SITE
|
1698 | 1699 | ||
1699 | 4-letter site code.
|
1700 | 4-letter site code.
|
1700 | 1701 | ||
1701 | =item DATE
|
1702 | =item DATE
|
1702 | 1703 | ||
1703 | The date (just the day of the month) on which the report was issued.
|
1704 | The date (just the day of the month) on which the report was issued.
|
1704 | 1705 | ||
1705 | =item TIME
|
1706 | =item TIME
|
1706 | 1707 | ||
1707 | The time at which the report was issued.
|
1708 | The time at which the report was issued.
|
1708 | 1709 | ||
1709 | =item MOD
|
1710 | =item MOD
|
1710 | 1711 | ||
1711 | Modifier (AUTO/COR) if any.
|
1712 | Modifier (AUTO/COR) if any.
|
1712 | 1713 | ||
1713 | =item WIND_DIR_ENG
|
1714 | =item WIND_DIR_ENG
|
1714 | 1715 | ||
1715 | The current wind direction in English (Southwest, East, North, etc.)
|
1716 | The current wind direction in English (Southwest, East, North, etc.)
|
1716 | 1717 | ||
1717 | =item WIND_DIR_RUS
|
1718 | =item WIND_DIR_RUS
|
1718 | 1719 | ||
1719 | The current wind direction in Russian
|
1720 | The current wind direction in Russian
|
1720 | 1721 | ||
1721 | =item WIND_DIR_ABB
|
1722 | =item WIND_DIR_ABB
|
1722 | 1723 | ||
1723 | The current wind direction in abbreviated English (S, E, N, etc.)
|
1724 | The current wind direction in abbreviated English (S, E, N, etc.)
|
1724 | 1725 | ||
1725 | =item WIND_DIR_DEG
|
1726 | =item WIND_DIR_DEG
|
1726 | 1727 | ||
1727 | The current wind direction in degrees.
|
1728 | The current wind direction in degrees.
|
1728 | 1729 | ||
1729 | =item WIND_KTS
|
1730 | =item WIND_KTS
|
1730 | 1731 | ||
1731 | The current wind speed in Knots.
|
1732 | The current wind speed in Knots.
|
1732 | 1733 | ||
1733 | =item WIND_MPH
|
1734 | =item WIND_MPH
|
1734 | 1735 | ||
1735 | The current wind speed in Miles Per Hour.
|
1736 | The current wind speed in Miles Per Hour.
|
1736 | 1737 | ||
1737 | =item WIND_MS
|
1738 | =item WIND_MS
|
1738 | 1739 | ||
1739 | The current wind speed in Metres Per Second.
|
1740 | The current wind speed in Metres Per Second.
|
1740 | 1741 | ||
1741 | =item WIND_GUST_KTS
|
1742 | =item WIND_GUST_KTS
|
1742 | 1743 | ||
1743 | The current wind gusting speed in Knots.
|
1744 | The current wind gusting speed in Knots.
|
1744 | 1745 | ||
1745 | =item WIND_GUST_MPH
|
1746 | =item WIND_GUST_MPH
|
1746 | 1747 | ||
1747 | The current wind gusting speed in Miles Per Hour.
|
1748 | The current wind gusting speed in Miles Per Hour.
|
1748 | 1749 | ||
1749 | =item WIND_GUST_MS
|
1750 | =item WIND_GUST_MS
|
1750 | 1751 | ||
1751 | The current wind gusting speed in Metres Per Second.
|
1752 | The current wind gusting speed in Metres Per Second.
|
1752 | 1753 | ||
1753 | =item WIND_VAR
|
1754 | =item WIND_VAR
|
1754 | 1755 | ||
1755 | The wind variation in English
|
1756 | The wind variation in English
|
1756 | 1757 | ||
1757 | =item WIND_VAR_1
|
1758 | =item WIND_VAR_1
|
1758 | 1759 | ||
1759 | The first wind variation direction
|
1760 | The first wind variation direction
|
1760 | 1761 | ||
1761 | =item WIND_VAR_ENG_1
|
1762 | =item WIND_VAR_ENG_1
|
1762 | 1763 | ||
1763 | The first wind variation direction in English
|
1764 | The first wind variation direction in English
|
1764 | 1765 | ||
1765 | =item WIND_VAR_2
|
1766 | =item WIND_VAR_2
|
1766 | 1767 | ||
1767 | The second wind variation direction
|
1768 | The second wind variation direction
|
1768 | 1769 | ||
1769 | =item WIND_VAR_ENG_2
|
1770 | =item WIND_VAR_ENG_2
|
1770 | 1771 | ||
1771 | The second wind variation direction in English
|
1772 | The second wind variation direction in English
|
1772 | 1773 | ||
1773 | =item VISIBILITY
|
1774 | =item VISIBILITY
|
1774 | 1775 | ||
1775 | Visibility information.
|
1776 | Visibility information.
|
1776 | 1777 | ||
1777 | =item VISIBILITY_RUS
|
1778 | =item VISIBILITY_RUS
|
1778 | 1779 | ||
1779 | Visibility information in Russian.
|
1780 | Visibility information in Russian.
|
1780 | 1781 | ||
1781 | =item WIND
|
1782 | =item WIND
|
1782 | 1783 | ||
1783 | Wind information.
|
1784 | Wind information.
|
1784 | 1785 | ||
1785 | =item RUNWAY
|
1786 | =item RUNWAY
|
1786 | 1787 | ||
1787 | Runway information.
|
1788 | Runway information.
|
1788 | 1789 | ||
1789 | =item WEATHER
|
1790 | =item WEATHER
|
1790 | 1791 | ||
1791 | Current weather (array)
|
1792 | Current weather (array)
|
1792 | 1793 | ||
1793 | ==item WEATHER_RUS
|
1794 | ==item WEATHER_RUS
|
1794 | 1795 | ||
1795 | Current weather in Russian (array)
|
1796 | Current weather in Russian (array)
|
1796 | 1797 | ||
1797 | ==item WEATHER_RAW
|
1798 | ==item WEATHER_RAW
|
1798 | 1799 | ||
1799 | Current weather in RAW-data (array)
|
1800 | Current weather in RAW-data (array)
|
1800 | 1801 | ||
1801 | =item WEATHER_LOG
|
1802 | =item WEATHER_LOG
|
1802 | 1803 | ||
1803 | Current weather log (array)
|
1804 | Current weather log (array)
|
1804 | 1805 | ||
1805 | =item SKY
|
1806 | =item SKY
|
1806 | 1807 | ||
1807 | Current cloud cover (array)
|
1808 | Current cloud cover (array)
|
1808 | 1809 | ||
1809 | =item SKY_RUS
|
1810 | =item SKY_RUS
|
1810 | 1811 | ||
1811 | Current cloud cover in Russian (array)
|
1812 | Current cloud cover in Russian (array)
|
1812 | 1813 | ||
1813 | =item SKY_RAW
|
1814 | =item SKY_RAW
|
1814 | 1815 | ||
1815 | Current cloud cover in RAW-data (array)
|
1816 | Current cloud cover in RAW-data (array)
|
1816 | 1817 | ||
1817 | =item TEMP_C
|
1818 | =item TEMP_C
|
1818 | 1819 | ||
1819 | Temperature in Celsius.
|
1820 | Temperature in Celsius.
|
1820 | 1821 | ||
1821 | =item TEMP_F
|
1822 | =item TEMP_F
|
1822 | 1823 | ||
1823 | Temperature in Fahrenheit.
|
1824 | Temperature in Fahrenheit.
|
1824 | 1825 | ||
1825 | =item TEMP_WC
|
1826 | =item TEMP_WC
|
1826 | 1827 | ||
1827 | Windchill Temperature in Celsius.
|
1828 | Windchill Temperature in Celsius.
|
1828 | 1829 | ||
1829 | =item DEW_C
|
1830 | =item DEW_C
|
1830 | 1831 | ||
1831 | Dew point in Celsius.
|
1832 | Dew point in Celsius.
|
1832 | 1833 | ||
1833 | =item DEW_F
|
1834 | =item DEW_F
|
1834 | 1835 | ||
1835 | Dew point in Fahrenheit.
|
1836 | Dew point in Fahrenheit.
|
1836 | 1837 | ||
1837 | =item HOURLY_TEMP_F
|
1838 | =item HOURLY_TEMP_F
|
1838 | 1839 | ||
1839 | Hourly current temperature, fahrenheit
|
1840 | Hourly current temperature, fahrenheit
|
1840 | 1841 | ||
1841 | =item HOURLY_TEMP_C
|
1842 | =item HOURLY_TEMP_C
|
1842 | 1843 | ||
1843 | Hourly current temperature, celcius
|
1844 | Hourly current temperature, celcius
|
1844 | 1845 | ||
1845 | =item HOURLY_DEW_F
|
1846 | =item HOURLY_DEW_F
|
1846 | 1847 | ||
1847 | Hourly dewpoint, fahrenheit
|
1848 | Hourly dewpoint, fahrenheit
|
1848 | 1849 | ||
1849 | =item HOURLY_DEW_C
|
1850 | =item HOURLY_DEW_C
|
1850 | 1851 | ||
1851 | Hourly dewpoint, celcius
|
1852 | Hourly dewpoint, celcius
|
1852 | 1853 | ||
1853 | =item ALT
|
1854 | =item ALT
|
1854 | 1855 | ||
1855 | Altimeter setting (barometric pressure).
|
1856 | Altimeter setting (barometric pressure).
|
1856 | 1857 | ||
1857 | =item ALT_HP
|
1858 | =item ALT_HP
|
1858 | 1859 | ||
1859 | Altimeter setting in hectopascals.
|
1860 | Altimeter setting in hectopascals.
|
1860 | 1861 | ||
1861 | =item REMARKS
|
1862 | =item REMARKS
|
1862 | 1863 | ||
1863 | Any remarks in the report.
|
1864 | Any remarks in the report.
|
1864 | 1865 | ||
1865 | =back
|
1866 | =back
|
1866 | 1867 | ||
1867 | =head1 NOTES
|
1868 | =head1 NOTES
|
1868 | 1869 | ||
1869 | Test suite is small and incomplete. Needs work yet.
|
1870 | Test suite is small and incomplete. Needs work yet.
|
1870 | 1871 | ||
1871 | Older versions of this module were installed as "METAR" instaed of
|
1872 | Older versions of this module were installed as "METAR" instaed of
|
1872 | "Geo::METAR"
|
1873 | "Geo::METAR"
|
1873 | 1874 | ||
1874 | =head1 BUGS
|
1875 | =head1 BUGS
|
1875 | 1876 | ||
1876 | The Geo::METAR is only initialized once, which means you'll get left-over
|
1877 | The Geo::METAR is only initialized once, which means you'll get left-over
|
1877 | crud in variables when you call the metar() function twice.
|
1878 | crud in variables when you call the metar() function twice.
|
1878 | 1879 | ||
1879 | What is an invalid METAR in one country is a standard one in the next.
|
1880 | What is an invalid METAR in one country is a standard one in the next.
|
1880 | The standard is interpreted and used by meteorologists all over the world,
|
1881 | The standard is interpreted and used by meteorologists all over the world,
|
1881 | with local variations. This means there will always be METARs that will
|
1882 | with local variations. This means there will always be METARs that will
|
1882 | trip the parser.
|
1883 | trip the parser.
|
1883 | 1884 | ||
1884 | =head1 TODO
|
1885 | =head1 TODO
|
1885 | 1886 | ||
1886 | There is a TODO file included in the Geo::METAR distribution listing
|
1887 | There is a TODO file included in the Geo::METAR distribution listing
|
1887 | the outstanding tasks that I or others have devised. Please check that
|
1888 | the outstanding tasks that I or others have devised. Please check that
|
1888 | list before you submit a bug report or request a new feture. It might
|
1889 | list before you submit a bug report or request a new feture. It might
|
1889 | already be on the TODO list.
|
1890 | already be on the TODO list.
|
1890 | 1891 | ||
1891 | =head1 AUTHORS AND COPYRIGHT
|
1892 | =head1 AUTHORS AND COPYRIGHT
|
1892 | 1893 | ||
1893 | Copyright 1997-2000, Jeremy D. Zawodny <Jeremy [at] Zawodny.com>
|
1894 | Copyright 1997-2000, Jeremy D. Zawodny <Jeremy [at] Zawodny.com>
|
1894 | 1895 | ||
1895 | Copyright 2007, Koos van den Hout <koos@kzdoos.xs4all.nl>
|
1896 | Copyright 2007, Koos van den Hout <koos@kzdoos.xs4all.nl>
|
1896 | 1897 | ||
1897 | Copyright 2010, Alexander Wolf <alex.v.wolf@gmail.com>
|
1898 | Copyright 2010, Alexander Wolf <alex.v.wolf@gmail.com>
|
1898 | 1899 | ||
1899 | Geo::ModMETAR is covered under the GNU Public License (GPL) version 2 or
|
1900 | Geo::ModMETAR is covered under the GNU Public License (GPL) version 2 or
|
1900 | later.
|
1901 | later.
|
1901 | 1902 | ||
1902 | The Geo::ModMETAR Web site is located at:
|
1903 | The Geo::ModMETAR Web site is located at:
|
1903 | 1904 | ||
1904 | http://astro.uni-altai.ru/~aw/perl/Geo-ModMETAR/
|
1905 | http://astro.uni-altai.ru/~aw/perl/Geo-ModMETAR/
|
1905 | 1906 | ||
1906 | =head1 CREDITS
|
1907 | =head1 CREDITS
|
1907 | 1908 | ||
1908 | In addition to our work on Geo::METAR, We've received ideas, help, and
|
1909 | In addition to our work on Geo::METAR, We've received ideas, help, and
|
1909 | patches from the following folks:
|
1910 | patches from the following folks:
|
1910 | 1911 | ||
1911 | * Ethan Dicks <ethan.dicks [at] gmail.com>
|
1912 | * Ethan Dicks <ethan.dicks [at] gmail.com>
|
1912 | 1913 | ||
1913 | Testing of Geo::METAR at the South Pole. Corrections and pointers
|
1914 | Testing of Geo::METAR at the South Pole. Corrections and pointers
|
1914 | to interesting cases to test.
|
1915 | to interesting cases to test.
|
1915 | 1916 | ||
1916 | * Otterboy <jong [at] watchguard.com>
|
1917 | * Otterboy <jong [at] watchguard.com>
|
1917 | 1918 | ||
1918 | Random script fixes and initial debugging help
|
1919 | Random script fixes and initial debugging help
|
1919 | 1920 | ||
1920 | * Remi Lefebvre <remi [at] solaria.dhis.org>
|
1921 | * Remi Lefebvre <remi [at] solaria.dhis.org>
|
1921 | 1922 | ||
1922 | Debian packaging as libgeo-metar-perl.deb.
|
1923 | Debian packaging as libgeo-metar-perl.deb.
|
1923 | 1924 | ||
1924 | * Mike Engelhart <mengelhart [at] earthtrip.com>
|
1925 | * Mike Engelhart <mengelhart [at] earthtrip.com>
|
1925 | 1926 | ||
1926 | Wind direction naming corrections.
|
1927 | Wind direction naming corrections.
|
1927 | 1928 | ||
1928 | * Michael Starling <mstarling [at] logic.bm>
|
1929 | * Michael Starling <mstarling [at] logic.bm>
|
1929 | 1930 | ||
1930 | Wind direction naming corrections.
|
1931 | Wind direction naming corrections.
|
1931 | 1932 | ||
1932 | * Hans Einar Nielssen <hans.einar [at] nielssen.com>
|
1933 | * Hans Einar Nielssen <hans.einar [at] nielssen.com>
|
1933 | 1934 | ||
1934 | Wind direction naming corrections.
|
1935 | Wind direction naming corrections.
|
1935 | 1936 | ||
1936 | * Nathan Neulinger <nneul [at] umr.edu>
|
1937 | * Nathan Neulinger <nneul [at] umr.edu>
|
1937 | 1938 | ||
1938 | Lots of enhancements and corrections. Too many to list here.
|
1939 | Lots of enhancements and corrections. Too many to list here.
|
1939 | 1940 | ||
1940 | =head1 RELATED PROJECTS
|
1941 | =head1 RELATED PROJECTS
|
1941 | 1942 | ||
1942 | B<lcdproc> at http://www.lcdproc.org/ uses Geo::METAR in lcdmetar.pl to
|
1943 | B<lcdproc> at http://www.lcdproc.org/ uses Geo::METAR in lcdmetar.pl to
|
1943 | display weather data on an lcd.
|
1944 | display weather data on an lcd.
|
1944 | 1945 | ||
1945 | =cut
|
1946 | =cut
|
1946 | 1947 | ||
1947 | 1948 | ||
1948 | # vim:expandtab:sw=4 ts=4
|
1949 | # vim:expandtab:sw=4 ts=4
|
1949 | 1950 |