msfs-simconnect-api-wrapper
Advanced tools
Comparing version 4.2.1 to 4.3.0
@@ -0,1 +1,15 @@ | ||
# How this library is versioned | ||
This library _strictly_ adheres to [semver](https://semver.org)'s major.minor.patch versioning: | ||
- patch version changes indicate bug fixes and/or internal-only code changes, | ||
- minor version changes indicate new functionality that does not break backward compatibility, | ||
- major version changes indicate backward-incompatible external API changes, no matter how small. | ||
# Version history | ||
v4.3.0 (19 December 2023) | ||
- Updated runway objects with their in-game start and end point, as well as the in-game runway bounding box, so that you don't have to do that work if you just want to visualize airports, or do runway related calculations. | ||
v4.2.0 | ||
@@ -2,0 +16,0 @@ |
{ | ||
"name": "msfs-simconnect-api-wrapper", | ||
"version": "4.2.1", | ||
"version": "4.3.0", | ||
"description": "A convenient SimConnect API for playing with Microsoft Flight Simulator 2020", | ||
@@ -5,0 +5,0 @@ "main": "msfs-api.js", |
@@ -7,2 +7,6 @@ # msfs-simconnect-api-wrapper | ||
## Versioning | ||
This library uses strict [semver](http://server.org) versioning. The current version of this library is **4.3.0**. See the [changelog](./CHANGELOG.md) for the full version history. | ||
## Installation and use | ||
@@ -134,3 +138,3 @@ | ||
altitude: number in feet | ||
declination: number in degree | ||
declination: when the magnetic compass says 360, what true heading are we on, in degrees | ||
name: airport name as a string with at most 32 characters | ||
@@ -149,13 +153,16 @@ name64: airport name as a string with at most 64 characters | ||
{ | ||
latitude: number in degrees, marking the center of the runway | ||
longitude: number in degrees, marking the center of the runway | ||
altitude: number in feet | ||
approach: array of runway approaches | ||
bbox: array with the runway's corner points encoded as [lat,long] | ||
end: [lat, long] coordinate for the runway end | ||
heading: number in degrees | ||
latitude: number in degrees, marking the center of the runway | ||
length: number in meters | ||
width: number in meters | ||
longitude: number in degrees, marking the center of the runway | ||
patternAltitude: number in meters | ||
slope: number in degrees | ||
slopeTrue: number in degrees | ||
start: [lat, long] coordinate for the runway start | ||
surface: surface material, as string | ||
approach: array of runway approaches | ||
width: number in meters | ||
} | ||
@@ -170,3 +177,3 @@ ``` | ||
marking: runway marking, as string (can be a number, or cardinal direction) | ||
heading: approach heading in degrees | ||
heading: true approach heading, in degrees | ||
ILS: { | ||
@@ -173,0 +180,0 @@ type: ILS type as string |
@@ -44,4 +44,5 @@ /** | ||
const FEET_PER_METERS = 3.28084; | ||
const degrees = (rad) => (rad / Math.PI) * 180; | ||
const radians = (deg) => (deg / 180) * Math.PI; | ||
const { abs, asin, atan2, sin, cos, PI } = Math; | ||
const degrees = (rad) => (rad / PI) * 180; | ||
const radians = (deg) => (deg / 180) * PI; | ||
@@ -198,2 +199,3 @@ const NEARBY_AIRPORTS = `NEARBY AIRPORTS`; | ||
handle.off("facilityDataEnd", processDataEnd); | ||
postProcessAirport(airportData); | ||
resolve(airportData); | ||
@@ -407,3 +409,3 @@ }; | ||
marking: primary_number, | ||
...getHeadingFromMarking(primary_number, airportData.declination), | ||
heading: 0, | ||
ILS: { | ||
@@ -418,3 +420,3 @@ type: primary_ils_type, | ||
marking: secondary_number, | ||
...getHeadingFromMarking(secondary_number, airportData.declination), | ||
heading: 0, | ||
ILS: { | ||
@@ -443,31 +445,106 @@ type: secondary_ils_type, | ||
function getHeadingFromMarking(marking, declination) { | ||
const headingData = { | ||
heading: undefined, | ||
trueHeading: undefined, | ||
}; | ||
// Improve the airport information | ||
function postProcessAirport(airport) { | ||
// abstract our runways | ||
airport.runways.forEach((runway) => setRunwayBounds(runway)); | ||
// set the approach headings in true degrees, rather than using the runway markings | ||
airport.runways.forEach((runway) => setRunwayApproachHeadings(runway)); | ||
} | ||
const num = parseFloat(marking); | ||
function setRunwayApproachHeadings(runway) { | ||
const { start, end } = runway; | ||
runway.approach[0].heading = getHeadingFromTo(...end, ...start); | ||
runway.approach[1].heading = getHeadingFromTo(...start, ...end); | ||
} | ||
if (isNaN(num)) { | ||
const mapping = { | ||
north: 360, | ||
northeast: 45, | ||
east: 90, | ||
southeast: 135, | ||
south: 180, | ||
southwest: 225, | ||
west: 270, | ||
northwest: 315, | ||
}; | ||
headingData.heading = mapping[marking]; | ||
if (headingData.heading === 0) headingData.heading = 360; | ||
} else if (1 <= num && num <= 36) headingData.heading = num * 10; | ||
// by including runway start, end, and bounding box information | ||
function setRunwayBounds(runway) { | ||
const { | ||
latitude: lat, | ||
longitude: long, | ||
length, | ||
width, | ||
heading, | ||
} = runway; | ||
let args; | ||
if (headingData.heading) { | ||
headingData.trueHeading = (headingData.heading + declination) % 360; | ||
if (headingData.trueHeading === 0) headingData.trueHeading = 360; | ||
// Find the runway endpoints. length is in meters, getPointAtDistance | ||
// needs kilometers, so we divide by 1000, and then each end is half | ||
// the length from the center. | ||
args = [lat, long, length / 2000, heading]; | ||
const { lat: latS, long: longS } = getPointAtDistance(...args); | ||
args = [lat, long, length / 2000, heading + 180]; | ||
const { lat: latE, long: longE } = getPointAtDistance(...args); | ||
// Runway start/end coordinates | ||
const start = (runway.start = [latS, longS]); | ||
const end = (runway.end = [latE, longE]); | ||
// Do we need to swap these? The runway heading indicates | ||
// the heading for approaches, so the heading from start | ||
// to end should be the opposite heading. | ||
const lineHeading = getHeadingFromTo(...start, ...end); | ||
const diff = getCompassDiff(lineHeading, heading); | ||
if (abs(diff) < 90) { | ||
runway.start = end; | ||
runway.end = start; | ||
} | ||
return headingData; | ||
// Runway bbox | ||
args = [latS, longS, width / 2000, heading + 90]; | ||
const { lat: lat1, long: long1 } = getPointAtDistance(...args); | ||
args = [latS, longS, width / 2000, heading - 90]; | ||
const { lat: lat2, long: long2 } = getPointAtDistance(...args); | ||
args = [latE, longE, width / 2000, heading - 90]; | ||
const { lat: lat3, long: long3 } = getPointAtDistance(...args); | ||
args = [latE, longE, width / 2000, heading + 90]; | ||
const { lat: lat4, long: long4 } = getPointAtDistance(...args); | ||
runway.bbox = [ | ||
[lat1, long1], | ||
[lat2, long2], | ||
[lat3, long3], | ||
[lat4, long4], | ||
]; | ||
} | ||
function getPointAtDistance(lat1, long1, d, heading, R = 6371) { | ||
` | ||
lat: initial latitude, in degrees | ||
lon: initial longitude, in degrees | ||
d: target distance from initial in kilometers | ||
heading: (true) heading in degrees | ||
R: optional radius of sphere, defaults to mean radius of earth | ||
Returns new lat/lon coordinate {d}km from initial, in degrees | ||
`; | ||
lat1 = radians(lat1); | ||
long1 = radians(long1); | ||
const a = radians(heading); | ||
const lat2 = asin(sin(lat1) * cos(d / R) + cos(lat1) * sin(d / R) * cos(a)); | ||
const dx = cos(d / R) - sin(lat1) * sin(lat2); | ||
const dy = sin(a) * sin(d / R) * cos(lat1); | ||
const long2 = long1 + atan2(dy, dx); | ||
return { lat: degrees(lat2), long: degrees(long2) }; | ||
} | ||
export function getHeadingFromTo(lat1, long1, lat2, long2, declination = 0) { | ||
lat1 = radians(parseFloat(lat1)); | ||
long1 = radians(parseFloat(long1)); | ||
lat2 = radians(parseFloat(lat2)); | ||
long2 = radians(parseFloat(long2)); | ||
const dLon = long2 - long1; | ||
const x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon); | ||
const y = cos(lat2) * sin(dLon); | ||
return (degrees(atan2(y, x)) - declination + 360) % 360; | ||
} | ||
function getCompassDiff(current, target, direction = 1) { | ||
const diff = current > 180 ? current - 360 : current; | ||
target = target - diff; | ||
const result = target < 180 ? target : target - 360; | ||
if (direction > 0) return result; | ||
return target < 180 ? 360 - target : target - 360; | ||
} |
Sorry, the diff of this file is not supported yet
13601521
7379
243