diff --git a/src/command/metar/RunwayCommand.ts b/src/command/metar/RunwayCommand.ts index 03ce47a..022539b 100644 --- a/src/command/metar/RunwayCommand.ts +++ b/src/command/metar/RunwayCommand.ts @@ -13,7 +13,7 @@ import { ICommand } from "../metar"; export class RunwayCommand implements ICommand { #genericRegex = /^(R\d{2}\w?\/)/; #runwayMaxRangeRegex = /^R(\d{2}\w?)\/(\d{4})V(\d{3,4})([UDN])?(FT)?/; - #runwayRegex = /^R(\d{2}\w?)\/([MP])?(\d{4})([UDN])?(FT)?$/; + #runwayRegex = /^R(\d{2}\w?)\/([MP])?(\d{4})(?:([UDN])|(FT)(?:\/([UDN]))?)$/; #runwayDepositRegex = /^R(\d{2}\w?)\/([/\d])([/\d])(\/\/|\d{2})(\/\/|\d{2})$/; canParse(input: string): boolean { @@ -42,7 +42,11 @@ export class RunwayCommand implements ICommand { if (!matches) throw new UnexpectedParseError("Should be able to parse"); const indicator = matches[2] ? as(matches[2], ValueIndicator) : undefined; - const trend = matches[4] ? as(matches[4], RunwayInfoTrend) : undefined; + const trend = (() => { + if (matches[6]) return as(matches[6], RunwayInfoTrend); + if (matches[4]) return as(matches[4], RunwayInfoTrend); + })(); + const unit = matches[5] ? as(matches[5], RunwayInfoUnit) : RunwayInfoUnit.Meters; diff --git a/tests/command/metar/RunwayCommand.test.ts b/tests/command/metar/RunwayCommand.test.ts index fffc7c7..ba1a553 100644 --- a/tests/command/metar/RunwayCommand.test.ts +++ b/tests/command/metar/RunwayCommand.test.ts @@ -205,4 +205,27 @@ describe("RunwayCommand", () => { }); }); })(); + + (() => { + const code = "R36/4000FT/D"; // runway info north america style + const metar = { runwaysInfo: [] } as unknown as IMetar; + + describe(code, () => { + test("canParse", () => { + expect(command.canParse(code)).toBe(true); + }); + + test("parse", () => { + command.execute(metar, code); + + expect(metar.runwaysInfo).toHaveLength(1); + expect(metar.runwaysInfo[0]).toEqual({ + name: "36", + minRange: 4000, + unit: RunwayInfoUnit.Feet, + trend: RunwayInfoTrend.Decreasing, + }); + }); + }); + })(); }); diff --git a/tests/parser/parser.test.ts b/tests/parser/parser.test.ts index 348b7ba..3537318 100644 --- a/tests/parser/parser.test.ts +++ b/tests/parser/parser.test.ts @@ -240,6 +240,32 @@ describe("MetarParser", () => { expect((metar.runwaysInfo[0] as IRunwayInfoRange).trend).toBe("N"); }); + test("parses canadian station", () => { + const input = + "CYWG 172000Z 30015G25KT 3/4SM R36/4000FT/D -SN BLSN BKN008 OVC040 M05/M08 A2992 REFZRA WS RWY36 RMK SF5NS3 SLP134"; + + const metar = new MetarParser(en).parse(input); + + expect(metar.station).toBe("CYWG"); + expect(metar.day).toBe(17); + expect(metar.hour).toBe(20); + expect(metar.minute).toBe(0); + expect(metar.wind).toBeDefined(); + expect(metar.wind?.speed).toBe(15); + expect(metar.wind?.gust).toBe(25); + expect(metar.wind?.direction).toBe("WNW"); + expect(metar.wind?.unit).toBe("KT"); + expect(metar.visibility).toBeDefined(); + expect(metar.visibility).toEqual({ + value: 0.75, + unit: DistanceUnit.StatuteMiles, + }); + expect(metar.runwaysInfo).toHaveLength(1); + expect(metar.runwaysInfo[0].name).toBe("36"); + expect((metar.runwaysInfo[0] as IRunwayInfoRange).minRange).toBe(4000); + expect((metar.runwaysInfo[0] as IRunwayInfoRange).trend).toBe("D"); + }); + test("parses 'AUTO' as station if no station identifier", () => { const input = "AUTO 061950Z 10002KT 9999NDV NCD 01/M00 Q1015 RMK=";