taf/taf_test.go

704 lines
15 KiB
Go

package taf
import (
"strings"
"testing"
"time"
"github.com/go-test/deep"
"go.elara.ws/taf/airports"
"go.elara.ws/taf/units"
)
func TestKLAX(t *testing.T) {
const data = `KLAX 212011Z 2120/2224 26012KT P6SM FEW035 SCT050 SCT060
FM212200 25010KT P6SM SCT040
FM220300 VRB03KT P6SM BKN025
FM221000 VRB03KT P6SM OVC025
FM221700 26006KT P6SM BKN025
FM222000 26012KT P6SM SCT030`
expected := &Forecast{
Identifier: "KLAX",
Airport: airports.Airport{
ICAO: "KLAX",
IATA: "LAX",
Name: "Los Angeles International Airport",
City: "Los Angeles",
State: "California",
Country: "US",
Elevation: 125,
Latitude: 33.94250107,
Longitude: -118.4079971,
Timezone: "America/Los_Angeles",
},
PublishTime: time.Date(2023, time.August, 21, 20, 11, 0, 0, time.UTC),
Valid: ValidPair{
From: time.Date(2023, time.August, 21, 20, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 23, 0, 0, 0, 0, time.UTC),
Duration: time.Duration(100800000000000),
},
Visibility: Visibility{
Plus: true,
Value: 6,
Unit: units.Miles,
},
Wind: Wind{
Direction: Direction{
Value: 260,
},
Speed: 12,
Unit: units.Knots,
},
SkyCondition: []SkyCondition{
{
Type: Few,
Altitude: 3500,
},
{
Type: Scattered,
Altitude: 5000,
},
{
Type: Scattered,
Altitude: 6000,
},
},
Changes: []*Change{
{
Type: From,
Valid: ValidPair{
From: time.Date(2023, time.August, 21, 22, 0, 0, 0, time.UTC),
},
Visibility: Visibility{
Plus: true,
Value: 6,
Unit: units.Miles,
},
Wind: Wind{
Direction: Direction{
Value: 250,
},
Speed: 10,
Unit: units.Knots,
},
SkyCondition: []SkyCondition{
{
Type: Scattered,
Altitude: 4000,
},
},
},
{
Type: From,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 3, 0, 0, 0, time.UTC),
},
Visibility: Visibility{
Plus: true,
Value: 6,
Unit: units.Miles,
},
Wind: Wind{
Direction: Direction{
Variable: true,
},
Speed: 3,
Unit: units.Knots,
},
SkyCondition: []SkyCondition{
{
Type: Broken,
Altitude: 2500,
},
},
},
{
Type: From,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 10, 0, 0, 0, time.UTC),
},
Visibility: Visibility{
Plus: true,
Value: 6,
Unit: units.Miles,
},
Wind: Wind{
Direction: Direction{
Variable: true,
},
Speed: 3,
Unit: units.Knots,
},
SkyCondition: []SkyCondition{
{
Type: Overcast,
Altitude: 2500,
},
},
},
{
Type: From,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 17, 0, 0, 0, time.UTC),
},
Visibility: Visibility{
Plus: true,
Value: 6,
Unit: units.Miles,
},
Wind: Wind{
Direction: Direction{
Value: 260,
},
Speed: 6,
Unit: units.Knots,
},
SkyCondition: []SkyCondition{
{
Type: Broken,
Altitude: 2500,
},
},
},
{
Type: From,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 20, 0, 0, 0, time.UTC),
},
Visibility: Visibility{
Plus: true,
Value: 6,
Unit: units.Miles,
},
Wind: Wind{
Direction: Direction{
Value: 260,
},
Speed: 12,
Unit: units.Knots,
},
SkyCondition: []SkyCondition{
{
Type: Scattered,
Altitude: 3000,
},
},
},
},
}
fc, err := DecodeWithOptions(strings.NewReader(data), Options{
Month: time.August,
Year: 2023,
})
if err != nil {
t.Fatalf("Error during parsing: %s", err)
}
if diff := deep.Equal(fc, expected); diff != nil {
t.Error(diff)
}
}
func TestZGSZ(t *testing.T) {
const data = `TAF AMD ZGSZ 211907Z 2118/2218 18004MPS 8000 SCT020 TX32/2206Z TN28/2122Z
TEMPO 2120/2202 SHRA SCT020 FEW023CB
TEMPO 2204/2208 TSRA SCT020 FEW023CB`
expected := &Forecast{
ReportType: Amended,
Identifier: "ZGSZ",
Airport: airports.Airport{
ICAO: "ZGSZ",
IATA: "SZX",
Name: "Shenzhen Bao'an International Airport",
City: "Shenzhen",
State: "Guangdong",
Country: "CN",
Elevation: 13,
Latitude: 22.6392993927,
Longitude: 113.8109970093,
Timezone: "Asia/Shanghai",
},
PublishTime: time.Date(2023, time.August, 21, 19, 7, 0, 0, time.UTC),
Valid: ValidPair{
From: time.Date(2023, time.August, 21, 18, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 18, 0, 0, 0, time.UTC),
Duration: time.Duration(86400000000000),
},
Visibility: Visibility{
Value: 8000,
Unit: units.Meters,
},
Wind: Wind{
Direction: Direction{
Value: 180,
},
Speed: 4,
Unit: units.MetersPerSecond,
},
SkyCondition: []SkyCondition{
{
Type: Scattered,
Altitude: 2000,
},
},
Temperature: []Temperature{
{
Type: High,
Value: 32,
Time: time.Date(2023, time.August, 22, 6, 0, 0, 0, time.UTC),
},
{
Type: Low,
Value: 28,
Time: time.Date(2023, time.August, 21, 22, 0, 0, 0, time.UTC),
},
},
Changes: []*Change{
{
Type: Temporary,
Valid: ValidPair{
From: time.Date(2023, time.August, 21, 20, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 2, 0, 0, 0, time.UTC),
Duration: time.Duration(21600000000000),
},
SkyCondition: []SkyCondition{
{
Type: Scattered,
Altitude: 2000,
},
{
Type: Few,
Altitude: 2300,
CloudType: CumuloNimbus,
},
},
Weather: []Weather{
{
Descriptor: Showers,
Precipitation: Rain,
},
},
},
{
Type: Temporary,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 4, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 8, 0, 0, 0, time.UTC),
Duration: time.Duration(14400000000000),
},
SkyCondition: []SkyCondition{
{
Type: Scattered,
Altitude: 2000,
},
{
Type: Few,
Altitude: 2300,
CloudType: CumuloNimbus,
},
},
Weather: []Weather{
{
Descriptor: Thunderstorm,
Precipitation: Rain,
},
},
},
},
}
fc, err := DecodeWithOptions(strings.NewReader(data), Options{
Month: time.August,
Year: 2023,
})
if err != nil {
t.Fatalf("Error during parsing: %s", err)
}
if diff := deep.Equal(fc, expected); diff != nil {
t.Error(diff)
}
}
func TestLFBD(t *testing.T) {
const data = `TAF LFBD 211700Z 2118/2224 31010KT CAVOK TX37/2214Z TN22/2205Z
BECMG 2118/2120 32004KT
BECMG 2200/2202 26005KT
BECMG 2213/2215 32010KT
BECMG 2222/2224 24004KT`
expected := &Forecast{
Identifier: "LFBD",
Airport: airports.Airport{
ICAO: "LFBD",
IATA: "BOD",
Name: "Bordeaux-Merignac (BA 106) Airport",
City: "Bordeaux/Merignac",
State: "Nouvelle-Aquitaine",
Country: "FR",
Elevation: 162,
Latitude: 44.8283004761,
Longitude: -0.7155560255,
Timezone: "Europe/Paris",
},
PublishTime: time.Date(2023, time.August, 21, 17, 0, 0, 0, time.UTC),
Valid: ValidPair{
From: time.Date(2023, time.August, 21, 18, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 23, 0, 0, 0, 0, time.UTC),
Duration: time.Duration(108000000000000),
},
Wind: Wind{
Direction: Direction{
Value: 310,
},
Speed: 10,
Unit: units.Knots,
},
Temperature: []Temperature{
{
Type: High,
Value: 37,
Time: time.Date(2023, time.August, 22, 14, 0, 0, 0, time.UTC),
},
{
Type: Low,
Value: 22,
Time: time.Date(2023, time.August, 22, 5, 0, 0, 0, time.UTC),
},
},
Changes: []*Change{
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.August, 21, 18, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 21, 20, 0, 0, 0, time.UTC),
Duration: time.Duration(7200000000000),
},
Wind: Wind{
Direction: Direction{
Value: 320,
},
Speed: 4,
Unit: units.Knots,
},
},
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 0, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 2, 0, 0, 0, time.UTC),
Duration: time.Duration(7200000000000),
},
Wind: Wind{
Direction: Direction{
Value: 260,
},
Speed: 5,
Unit: units.Knots,
},
},
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 13, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 15, 0, 0, 0, time.UTC),
Duration: time.Duration(7200000000000),
},
Wind: Wind{
Direction: Direction{
Value: 320,
},
Speed: 10,
Unit: units.Knots,
},
},
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 22, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 23, 0, 0, 0, 0, time.UTC),
Duration: time.Duration(7200000000000),
},
Wind: Wind{
Direction: Direction{
Value: 240,
},
Speed: 4,
Unit: units.Knots,
},
},
},
Flags: []Flag{
CeilingAndVisibilityOK,
},
}
fc, err := DecodeWithOptions(strings.NewReader(data), Options{
Month: time.August,
Year: 2023,
})
if err != nil {
t.Fatalf("Error during parsing: %s", err)
}
if diff := deep.Equal(fc, expected); diff != nil {
t.Error(diff)
}
}
func TestUUEE(t *testing.T) {
const data = `TAF UUEE 211958Z 2121/2221 VRB01MPS 9999 SCT030 TX20/2212Z TN12/2202Z
TEMPO 2121/2204 BKN004
PROB40
TEMPO 2121/2204 0300 FG
BECMG 2204/2206 24006MPS
PROB40
TEMPO 2209/2218 -TSRA BKN020CB`
expected := &Forecast{
Identifier: "UUEE",
Airport: airports.Airport{
ICAO: "UUEE",
IATA: "SVO",
Name: "Sheremetyevo International Airport",
City: "Moscow",
State: "Moscow-Oblast",
Country: "RU",
Elevation: 622,
Latitude: 55.9725990295,
Longitude: 37.4146003723,
Timezone: "Europe/Moscow",
},
PublishTime: time.Date(2023, time.August, 21, 19, 58, 0, 0, time.UTC),
Valid: ValidPair{
From: time.Date(2023, time.August, 21, 21, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 21, 0, 0, 0, time.UTC),
Duration: time.Duration(86400000000000),
},
Visibility: Visibility{
Value: 9999,
Unit: units.Meters,
},
Wind: Wind{
Direction: Direction{
Variable: true,
},
Speed: 1,
Unit: units.MetersPerSecond,
},
SkyCondition: []SkyCondition{
{
Type: Scattered,
Altitude: 3000,
},
},
Temperature: []Temperature{
{
Type: High,
Value: 20,
Time: time.Date(2023, time.August, 22, 12, 0, 0, 0, time.UTC),
},
{
Type: Low,
Value: 12,
Time: time.Date(2023, time.August, 22, 2, 0, 0, 0, time.UTC),
},
},
Changes: []*Change{
{
Type: Temporary,
Valid: ValidPair{
From: time.Date(2023, time.August, 21, 21, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 4, 0, 0, 0, time.UTC),
Duration: time.Duration(25200000000000),
},
SkyCondition: []SkyCondition{
{
Type: Broken,
Altitude: 400,
},
},
},
{
Type: Temporary,
Valid: ValidPair{
From: time.Date(2023, time.August, 21, 21, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 4, 0, 0, 0, time.UTC),
Duration: time.Duration(25200000000000),
},
Visibility: Visibility{
Value: 300,
Unit: units.Meters,
},
Weather: []Weather{
{
Obscuration: Fog,
},
},
Probability: 40,
},
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 4, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 6, 0, 0, 0, time.UTC),
Duration: time.Duration(7200000000000),
},
Wind: Wind{
Direction: Direction{
Value: 240,
},
Speed: 6,
Unit: units.MetersPerSecond,
},
},
{
Type: Temporary,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 9, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 18, 0, 0, 0, time.UTC),
Duration: time.Duration(32400000000000),
},
SkyCondition: []SkyCondition{
{
Type: Broken,
Altitude: 2000,
CloudType: CumuloNimbus,
},
},
Weather: []Weather{
{
Modifier: Light,
Descriptor: Thunderstorm,
Precipitation: Rain,
},
},
Probability: 40,
},
},
}
fc, err := DecodeWithOptions(strings.NewReader(data), Options{
Month: time.August,
Year: 2023,
})
if err != nil {
t.Fatalf("Error during parsing: %s", err)
}
if diff := deep.Equal(fc, expected); diff != nil {
t.Error(diff)
}
}
func TestEGLL(t *testing.T) {
const data = `TAF EGLL 211658Z 2118/2224 22008KT 9999 FEW040
BECMG 2201/2204 BKN007
PROB30
TEMPO 2202/2206 8000 BKN004
BECMG 2207/2210 SCT025`
expected := &Forecast{
Identifier: "EGLL",
Airport: airports.Airport{
ICAO: "EGLL",
IATA: "LHR",
Name: "London Heathrow Airport",
City: "London",
State: "England",
Country: "GB",
Elevation: 83,
Latitude: 51.4706001282,
Longitude: -0.4619410038,
Timezone: "Europe/London",
},
PublishTime: time.Date(2023, time.August, 21, 16, 58, 0, 0, time.UTC),
Valid: ValidPair{
From: time.Date(2023, time.August, 21, 18, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 23, 0, 0, 0, 0, time.UTC),
Duration: time.Duration(108000000000000),
},
Visibility: Visibility{
Value: 9999,
Unit: units.Meters,
},
Wind: Wind{
Direction: Direction{
Value: 220,
},
Speed: 8,
Unit: units.Knots,
},
SkyCondition: []SkyCondition{
{
Type: Few,
Altitude: 4000,
},
},
Changes: []*Change{
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 1, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 4, 0, 0, 0, time.UTC),
Duration: time.Duration(10800000000000),
},
SkyCondition: []SkyCondition{
{
Type: Broken,
Altitude: 700,
},
},
},
{
Type: Temporary,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 2, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 6, 0, 0, 0, time.UTC),
Duration: time.Duration(14400000000000),
},
Visibility: Visibility{
Value: 8000,
Unit: units.Meters,
},
SkyCondition: []SkyCondition{
{
Type: Broken,
Altitude: 400,
},
},
Probability: 30,
},
{
Type: Becoming,
Valid: ValidPair{
From: time.Date(2023, time.August, 22, 7, 0, 0, 0, time.UTC),
To: time.Date(2023, time.August, 22, 10, 0, 0, 0, time.UTC),
Duration: time.Duration(10800000000000),
},
SkyCondition: []SkyCondition{
{
Type: Scattered,
Altitude: 2500,
},
},
},
},
}
fc, err := DecodeWithOptions(strings.NewReader(data), Options{
Month: time.August,
Year: 2023,
})
if err != nil {
t.Fatalf("Error during parsing: %s", err)
}
if diff := deep.Equal(fc, expected); diff != nil {
t.Error(diff)
}
}