Weather computation in pure MoonBit: humidity, apparent temperature, pressure and altitude, the Beaufort scale, wind barbs, and winds-aloft (FD) flight calculations
///|
let bulletin = @moonwind.parse_bulletin(product_text)
///|
let station = bulletin.station("ABR").unwrap()
///|
let level = station.level_at(24000).unwrap() // wind and temperature| Field | Encoding |
|---|---|
| Wind | ddss: tens of degrees and knots. 9900 is light and variable |
| Wind ≥ 100 kt | 50 is added to the direction and 100 taken off the speed |
| Temperature | +TT / -TT in °C |
| Temperature above 24,000 ft | always negative, so the sign is dropped |
| 3,000 ft | no temperature at all |
FT 3000 6000 9000 12000 18000 24000 30000 34000 39000
ABR 1519 1618+06 1614+03 1514+01 1509-13 1612-25 260739 311344 331151
ACK 0623 1108+05 2805+03 3010+00 2642-10 2648-20 266535 256945 257756///|
let wind = station.vector_at(4500).unwrap()
///|
let temperature = station.temperature_at(7500) // Some(4.5)
///|
let reported = station.vector_at(4500).unwrap().to_wind() // rounded to whole numbers///|
let standard = @moonwind.isa_temperature(24000.0) // -32.5488
///|
let deviation = @moonwind.isa_deviation(-25.0, 24000.0) // 7.5488, warmer than standard///|
let dew = @moonwind.dew_point(20.0, 50.0) // 9.26 °C, the classic Magnus example
///|
let back = @moonwind.relative_humidity(20.0, dew) // 50 %
///|
let frost = @moonwind.frost_point(-10.0, 50.0) // -16.52, above its -18.47 dew point
///|
let wet = @moonwind.wet_bulb_temperature(20.0, 50.0) // Some(13.70)
///|
let grams = @moonwind.absolute_humidity(
@moonwind.saturation_vapor_pressure(20.0),
20.0,
) // 17.25 g/m³///|
let american = @moonwind.wind_chill_fahrenheit(0.0, 15.0) // Some(-19.4), the chart's -19
///|
let canadian = @moonwind.wind_chill_celsius(0.0, 20.0) // Some(-5.2)///|
let heat = @moonwind.heat_index_celsius(30.0, 50.0) // 31.05, 86 F at 50 %
///|
let humid = @moonwind.humidex(30.0, 15.0) // 33.9, Environment Canada's example
///|
let feels = @moonwind.apparent_temperature(30.0, 80.0, 10.0 / 3.6) // 35.24, the Bureau's///|
let sea_level = @moonwind.mean_sea_level_pressure(955.0, 500.0, 20.0) // 1011.98 hPa
///|
let station = @moonwind.station_pressure(1012.0, 500.0, 20.0) // 955.02 hPa, its inverse
///|
let setting = @moonwind.altimeter_setting_hpa(954.61, 500.0) // 1013.25 hPa, a standard station at 500 m
///|
let inches = @moonwind.altimeter_setting_inhg(954.61, 500.0) // 29.92, the same setting///|
let pressure_altitude = @moonwind.pressure_altitude(850.0) // 4781.17 ft, the 850 hPa level
///|
let hot_day = @moonwind.density_altitude(1013.25, 25.0) // 1200.0 ft, 120 ft per degree
///|
let damp_day = @moonwind.density_altitude_with_dew_point(1013.25, 25.0, 15.0) // 1377.0 ft, the vapour counted too///|
let breeze = @moonwind.Beaufort::from_knots(14.0) // force 4, "Moderate breeze"
///|
let force = breeze.description() // "Moderate breeze"
///|
let band = breeze.upper_bound_knots() // Some(17.0), where force 5 begins
///|
let reported = @moonwind.Beaufort::from_wind(wind) // 150 at 14 kt is the same breeze///|
let barb = @moonwind.WindBarb::from_wind(wind) // 270 at 25 kt
///|
let feathers = (barb.pennants(), barb.full_barbs(), barb.half_barbs()) // (0, 2, 1)
///|
let drawn = barb.to_unicode() // "──╌│"
///|
let plain = barb.to_ascii() // "==-|", for a font with no pennant glyph///|
let triangle = @moonwind.WindTriangle::solve(90.0, 120.0, wind)
///|
let heading = triangle.heading() // 097.99°, course plus the correction
///|
let ground_speed = triangle.ground_speed() // 111.01 kt///|
let runway = @moonwind.Runway::parse("27").unwrap()
///|
let crosswind = runway.crosswind(wind) // positive from the right
///|
let headwind = runway.headwind(wind) // negative for a tailwind
///|
let other_end = runway.reciprocal().unwrap() // runway 09///|
let route = @moonwind.Route::new([
@moonwind.Coordinate::new(45.0, -98.0),
@moonwind.Coordinate::new(44.0, -96.0),
@moonwind.Coordinate::new(43.0, -94.0),
])
///|
let distance = route.distance() // 210.38 nm along the waypoints
///|
let segments = route.segments() // a distance and a course for every hop
///|
let shorter = route.subdivided(8) // every hop flown as eight shorter ones
///|
let middle = route.points()[0].midpoint(route.points()[1])///|
let plan = route.plan(station, 12000, 120.0)
///|
let legs = plan.legs() // each with its wind, heading, ground speed and time
///|
let hours = plan.total_time_hours()
///|
let distance = plan.total_distance()moon run cmd/main
moon run cmd/main -- --station ABR --altitude 12000 --airspeed 120 \
--legs 4 45,-98 44,-96 43,-94moonwind — a flight plan from a winds and temperatures aloft product
product fd1us1 FD1US1 valid 221800Z
station ABR at 12000 ft: 150 at 14 kt, 1.0 C
route 3 waypoints, 2 legs, 210.4 nm, at 120 kt
leg 1 45.00N 98.00W -> 44.00N 96.00W
104.6 nm on course 124.3, wind 150 at 14 kt, heading 127.2 at 107.2 kt, 0.98 h
6.1 kt across, 12.6 kt along
leg 2 44.00N 96.00W -> 43.00N 94.00W
105.8 nm on course 123.9, wind 150 at 14 kt, heading 126.8 at 107.3 kt, 0.99 h
6.2 kt across, 12.6 kt along
total 210.4 nm in 1 h 58 minmoon testpython scripts/gen_fixtures.py > fd_fixtures_wbtest.mbt
python scripts/gen_fixtures.py --cli > cmd/main/sample.mbt
moon fmtmoon bench --release benchmarkspub suberror ApparentTemperatureError {
BadRelativeHumidity(Double)
BadWindSpeed(Double)
}pub suberror BeaufortError {
BadWindSpeed(Double)
}pub suberror FdError {
MissingLevelHeader
BadLevelHeader(String)
BadStationLine(String)
BadWindGroup(String)
BadTemperature(String)
}pub suberror FlightError {
BadAirspeed(Double)
CrosswindExceedsAirspeed(Double, Double)
NoForecast(Int)
NoGroundSpeed(Double)
}pub suberror HumidityError {
BadRelativeHumidity(Double)
DewPointAboveTemperature(Double, Double)
}pub suberror PressureError {
ElevationTooHigh(Double)
}pub suberror WindBarbError {
BadWindSpeed(Double)
}pub struct Beaufort {
force : Int
}pub struct Bulletin {
header : Array[String]
level_header : String
altitudes : Array[Int]
wmo_header : String?
designator : String?
based_on : String?
valid : String?
use_window : String?
temps_negative_above : Int?
stations : Array[StationWinds]
index : HashMap[String, StationWinds]
trailing : Array[String]
}pub struct Coordinate {
latitude : Double
longitude : Double
}fn Route::plan(self : Route, station : StationWinds, altitude : Int, airspeed : Double) -> RoutePlan raise FlightErrorfn Route::plan_with(self : Route, wind_at : (Coordinate, Int) -> WindVector?, altitude : Int, airspeed : Double) -> RoutePlan raise FlightErrorpub struct Runway {
designator : String
heading : Int
}pub struct Wind {
direction : Int?
speed : Int
}pub struct WindBarb {
direction_tens : Int
pennants : Int
full_barbs : Int
half_barbs : Int
}pub struct WindTriangle {
course : Double
airspeed : Double
wind : WindVector
heading : Double
wind_correction_angle : Double
ground_speed : Double
headwind : Double
crosswind : Double
}fn WindTriangle::solve(course : Double, airspeed : Double, wind : WindVector) -> WindTriangle raise FlightErrorpub struct WindVector {
u : Double
v : Double
}fn absolute_humidity(vapor_pressure_hpa : Double, celsius : Double) -> Doublefn altimeter_setting_hpa(station_pressure_hpa : Double, elevation_meters : Double) -> Double raise PressureErrorfn altimeter_setting_inhg(station_pressure_hpa : Double, elevation_meters : Double) -> Double raise PressureErrorfn apparent_temperature(celsius : Double, relative_humidity : Double, wind_speed_meters_per_second : Double) -> Double raise ApparentTemperatureErrorfn celsius_to_fahrenheit(celsius : Double) -> Doublefn density_altitude(pressure_hpa : Double, celsius : Double) -> Doublefn density_altitude_with_dew_point(pressure_hpa : Double, celsius : Double, dew_point_celsius : Double) -> Double raise HumidityErrorfn fahrenheit_to_celsius(fahrenheit : Double) -> Doublefn heat_index_celsius(celsius : Double, relative_humidity : Double) -> Double? raise ApparentTemperatureErrorfn heat_index_fahrenheit(fahrenheit : Double, relative_humidity : Double) -> Double? raise ApparentTemperatureErrorfn hectopascals_to_inches_of_mercury(hectopascals : Double) -> Doublefn hectopascals_to_millimeters_of_mercury(hectopascals : Double) -> Doublefn inches_of_mercury_to_hectopascals(inches_of_mercury : Double) -> Doublefn isa_deviation(celsius : Double, feet : Double) -> Doublefn isa_temperature(feet : Double) -> Doublefn kilometers_per_hour_to_knots(kilometers_per_hour : Double) -> Doublefn knots_to_kilometers_per_hour(knots : Double) -> Doublefn knots_to_meters_per_second(knots : Double) -> Doublefn knots_to_miles_per_hour(knots : Double) -> Doublefn mean_sea_level_pressure(station_pressure_hpa : Double, elevation_meters : Double, celsius : Double) -> Doublefn meters_per_second_to_knots(meters_per_second : Double) -> Doublefn miles_per_hour_to_knots(miles_per_hour : Double) -> Doublefn millimeters_of_mercury_to_hectopascals(millimeters_of_mercury : Double) -> Doublefn pressure_altitude(pressure_hpa : Double) -> Doublefn saturation_vapor_pressure(celsius : Double) -> Doublefn saturation_vapor_pressure_over_ice(celsius : Double) -> Doublefn station_pressure(sea_level_pressure_hpa : Double, elevation_meters : Double, celsius : Double) -> Doublefn wet_bulb_temperature(celsius : Double, relative_humidity : Double) -> Double?fn wind_chill_celsius(celsius : Double, kilometers_per_hour : Double) -> Double? raise ApparentTemperatureErrorfn wind_chill_fahrenheit(fahrenheit : Double, miles_per_hour : Double) -> Double? raise ApparentTemperatureErrorInstall
Download zipWeather computation in pure MoonBit: humidity, apparent temperature, pressure and altitude, the Beaufort scale, wind barbs, and winds-aloft (FD) flight calculations