@fboes/aerofly-custom-missions 1.12.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -1
- package/dist/dto-flight/AeroflyNavRouteAirports.js +3 -3
- package/dist/dto-flight/AeroflyNavRouteBase.js +60 -9
- package/dist/dto-flight/AeroflyNavRouteBase.test.js +26 -0
- package/dist/dto-flight/AeroflyNavRouteRunway.js +5 -5
- package/dist/dto-flight/AeroflyNavRouteTransition.js +3 -3
- package/dist/dto-flight/AeroflyNavRouteWaypoint.js +17 -9
- package/dist/dto-flight/AeroflyNavigationConfig.js +4 -4
- package/dist/dto-flight/AeroflySettingsFlight.js +8 -8
- package/dist/dto-flight/AeroflySettingsFuelLoad.js +6 -6
- package/dist/node/Convert.js +107 -72
- package/dist/node/Convert.test.js +12 -12
- package/docs/flight.json +12 -9
- package/docs/flight.mcf +13 -9
- package/docs/flight.xml +13 -9
- package/package.json +1 -1
- package/src/dto-flight/AeroflyNavRouteAirports.ts +3 -3
- package/src/dto-flight/AeroflyNavRouteBase.test.ts +30 -0
- package/src/dto-flight/AeroflyNavRouteBase.ts +71 -10
- package/src/dto-flight/AeroflyNavRouteRunway.ts +5 -5
- package/src/dto-flight/AeroflyNavRouteTransition.ts +3 -3
- package/src/dto-flight/AeroflyNavRouteWaypoint.ts +24 -9
- package/src/dto-flight/AeroflyNavigationConfig.ts +4 -4
- package/src/dto-flight/AeroflySettingsFlight.ts +17 -8
- package/src/dto-flight/AeroflySettingsFuelLoad.ts +11 -7
- package/src/node/Convert.test.ts +21 -13
- package/src/node/Convert.ts +120 -81
- package/types/dto-flight/AeroflyNavRouteBase.d.ts +24 -2
- package/types/dto-flight/AeroflyNavRouteBase.d.ts.map +1 -1
- package/types/dto-flight/AeroflyNavRouteBase.test.d.ts +2 -0
- package/types/dto-flight/AeroflyNavRouteBase.test.d.ts.map +1 -0
- package/types/dto-flight/AeroflyNavRouteRunway.d.ts.map +1 -1
- package/types/dto-flight/AeroflyNavRouteTransition.d.ts.map +1 -1
- package/types/dto-flight/AeroflyNavRouteWaypoint.d.ts +8 -0
- package/types/dto-flight/AeroflyNavRouteWaypoint.d.ts.map +1 -1
- package/types/dto-flight/AeroflySettingsFlight.d.ts +1 -1
- package/types/dto-flight/AeroflySettingsFlight.d.ts.map +1 -1
- package/types/dto-flight/AeroflySettingsFuelLoad.d.ts +1 -1
- package/types/dto-flight/AeroflySettingsFuelLoad.d.ts.map +1 -1
- package/types/node/Convert.d.ts +52 -14
- package/types/node/Convert.d.ts.map +1 -1
- package/AGENTS.md +0 -43
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
This changelog documents all notable changes to the Aerofly Custom Missions project. Each version entry includes a list of changes, with the most recent version at the top.
|
|
4
4
|
|
|
5
|
-
## [1.
|
|
5
|
+
## [1.13.0] - 2026-07-23
|
|
6
|
+
|
|
7
|
+
- Made unit conversions more precise
|
|
8
|
+
- Added correct UID generator
|
|
9
|
+
|
|
10
|
+
## [1.12.0] - 2026-07-01
|
|
6
11
|
|
|
7
12
|
- Added unofficial `_cruiseSpeed` property to `AeroflyNavigationConfig`
|
|
8
13
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
|
|
2
|
-
import {
|
|
2
|
+
import { convertFeetToMeter, convertMeterToFeet } from "../node/Convert.js";
|
|
3
3
|
import { AeroflyNavRouteBase } from "./AeroflyNavRouteBase.js";
|
|
4
4
|
class AeroflyNavRouteAirport extends AeroflyNavRouteBase {
|
|
5
5
|
/**
|
|
@@ -27,10 +27,10 @@ class AeroflyNavRouteAirport extends AeroflyNavRouteBase {
|
|
|
27
27
|
* @returns {number | null} elevation in feet, null if not set
|
|
28
28
|
*/
|
|
29
29
|
get elevation_ft() {
|
|
30
|
-
return this.elevation !== null ?
|
|
30
|
+
return this.elevation !== null ? convertMeterToFeet(this.elevation) : null;
|
|
31
31
|
}
|
|
32
32
|
set elevation_ft(elevation_ft) {
|
|
33
|
-
this.elevation = elevation_ft !== null ?
|
|
33
|
+
this.elevation = elevation_ft !== null ? convertFeetToMeter(elevation_ft) : null;
|
|
34
34
|
}
|
|
35
35
|
getElement(index = 0) {
|
|
36
36
|
const element = super.getElement(index);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AeroflyConfigurationNode
|
|
1
|
+
import { convertLonLatToVector, convertVectorToLonLat } from "../node/Convert.js";
|
|
2
|
+
import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
|
|
3
3
|
export class AeroflyNavRouteBase {
|
|
4
4
|
/**
|
|
5
5
|
* @property {AeroflyNavRouteType} type like "origin", "departure_runway", "departure", "waypoint", "arrival", "approach", "destination_runway" or "destination"
|
|
@@ -18,7 +18,7 @@ export class AeroflyNavRouteBase {
|
|
|
18
18
|
*/
|
|
19
19
|
latitude;
|
|
20
20
|
/**
|
|
21
|
-
* @property {?bigint} uid unique identifier for the waypoint, must match Aerofly FS internal UID if used in an existing mission
|
|
21
|
+
* @property {?bigint} uid unique identifier for the waypoint, must match Aerofly FS internal UID if used in an existing mission. Obviously the UID encodes the geographic position as well as the name of the waypoint.
|
|
22
22
|
*/
|
|
23
23
|
uid;
|
|
24
24
|
/**
|
|
@@ -27,7 +27,7 @@ export class AeroflyNavRouteBase {
|
|
|
27
27
|
* @param {number} longitude WGS84
|
|
28
28
|
* @param {number} latitude WGS84
|
|
29
29
|
* @param {object} [options] additional options for the waypoint
|
|
30
|
-
* @param {?bigint} [options.uid] unique identifier for the waypoint, must match Aerofly FS internal UID if used in an existing mission
|
|
30
|
+
* @param {?bigint} [options.uid] unique identifier for the waypoint, must match Aerofly FS internal UID if used in an existing mission. Obviously the UID encodes the geographic position as well as the name of the waypoint.
|
|
31
31
|
*/
|
|
32
32
|
constructor(type, identifier, longitude, latitude, { uid = null } = {}) {
|
|
33
33
|
this.type = type;
|
|
@@ -40,10 +40,10 @@ export class AeroflyNavRouteBase {
|
|
|
40
40
|
* @returns {AeroflyVector3Float} to use in Aerofly FS4's `main.mcf`
|
|
41
41
|
*/
|
|
42
42
|
get position() {
|
|
43
|
-
return
|
|
43
|
+
return convertLonLatToVector(this.longitude, this.latitude, 0);
|
|
44
44
|
}
|
|
45
45
|
set position(position) {
|
|
46
|
-
const latLonAlt =
|
|
46
|
+
const latLonAlt = convertVectorToLonLat(position);
|
|
47
47
|
this.longitude = latLonAlt.longitude;
|
|
48
48
|
this.latitude = latLonAlt.latitude;
|
|
49
49
|
}
|
|
@@ -60,11 +60,62 @@ export class AeroflyNavRouteBase {
|
|
|
60
60
|
const element = new AeroflyConfigurationNode("tmnav_route_" + this.type, this.identifier, String(index))
|
|
61
61
|
.appendChild("string8u", "Identifier", this.identifier)
|
|
62
62
|
.appendChild("vector3_float64", "Position", this.position, `Lon ${this.longitude.toFixed(6)}, Lat ${this.latitude.toFixed(6)}`)
|
|
63
|
-
.
|
|
64
|
-
? new AeroflyConfigurationNode("uint64", "Uid", this.uid)
|
|
65
|
-
: new AeroflyConfigurationNodeComment("uint64", "Uid", ""));
|
|
63
|
+
.appendChild("uint64", "Uid", this.uid ?? this.getUidFallback(), this.uid ? "" : "Fallback UID, not matching Aerofly FS internal UID");
|
|
66
64
|
return element;
|
|
67
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* @returns {bigint} 24 bit longitude
|
|
68
|
+
* @see https://www.aerofly.com/community/forum/index.php?thread/29490-navdata-coordinates-to-uid/
|
|
69
|
+
*/
|
|
70
|
+
encodeLongitude() {
|
|
71
|
+
let scaled = this.longitude / 180;
|
|
72
|
+
scaled = 65536.0 * (0.5 + 0.5 * scaled); // 16 bit
|
|
73
|
+
return BigInt(Math.round(256.0 * scaled + 0.5)); // 24 bit
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* @returns {bigint} 24 bit latitude
|
|
77
|
+
* @see https://www.aerofly.com/community/forum/index.php?thread/29490-navdata-coordinates-to-uid/
|
|
78
|
+
*/
|
|
79
|
+
encodeLatitude() {
|
|
80
|
+
const worldGridConstantA = 2.3311223704144;
|
|
81
|
+
let scaled = this.latitude / 180;
|
|
82
|
+
scaled = Math.tan(worldGridConstantA * scaled) / worldGridConstantA;
|
|
83
|
+
scaled = 65536.0 * (0.5 + 0.5 * scaled); // 16 bit
|
|
84
|
+
return BigInt(Math.round(256.0 * scaled + 0.5)); // 24 bit
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* @returns {number} 16 bit type
|
|
88
|
+
*/
|
|
89
|
+
encodeWaypointType() {
|
|
90
|
+
// Type code (airport (0500) / runway (0800) / SID (4400) / STAR (4800) / Approach (4C00) / RNAV waypoint (C000) / airways (4000)
|
|
91
|
+
switch (this.type) {
|
|
92
|
+
case "origin":
|
|
93
|
+
case "destination":
|
|
94
|
+
return 0x0500;
|
|
95
|
+
case "departure_runway":
|
|
96
|
+
case "destination_runway":
|
|
97
|
+
return 0x0800;
|
|
98
|
+
case "departure":
|
|
99
|
+
return 0x4400;
|
|
100
|
+
case "arrival":
|
|
101
|
+
return 0x4800;
|
|
102
|
+
case "approach":
|
|
103
|
+
return 0x4c00;
|
|
104
|
+
}
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* @returns {bigint} Packs both into a single 64-bit value:
|
|
109
|
+
* - Bits 63..40 → longitude (24 bit)
|
|
110
|
+
* - Bits 39..16 → latitude (24 bit)
|
|
111
|
+
* - Bits 15..0 → payload (16 bit)
|
|
112
|
+
* @see https://www.aerofly.com/community/forum/index.php?thread/29490-navdata-coordinates-to-uid/
|
|
113
|
+
*/
|
|
114
|
+
getUidFallback() {
|
|
115
|
+
return ((this.encodeLongitude() << 40n) |
|
|
116
|
+
(this.encodeLatitude() << 16n) |
|
|
117
|
+
(BigInt(this.encodeWaypointType()) & 0xffffn));
|
|
118
|
+
}
|
|
68
119
|
toJSON() {
|
|
69
120
|
return {
|
|
70
121
|
...this,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import { AeroflyNavRouteDestination } from "../index.js";
|
|
3
|
+
describe("AeroflyNavRouteBase", () => {
|
|
4
|
+
it("should create a valid AeroflyNavRouteBase structure", () => {
|
|
5
|
+
const waypoints = [
|
|
6
|
+
{ wp: new AeroflyNavRouteDestination("MAX-NE", 179.999, 89.999), uid: 0n },
|
|
7
|
+
{ wp: new AeroflyNavRouteDestination("MAX-SW", -179.999, -89.999), uid: 0n },
|
|
8
|
+
{ wp: new AeroflyNavRouteDestination("KEYW", -81.7599558, 24.5561197), uid: 5033914504046249216n },
|
|
9
|
+
{ wp: new AeroflyNavRouteDestination("EGLL", -0.45277777, 51.47138888), uid: 9199731073154483456n },
|
|
10
|
+
{ wp: new AeroflyNavRouteDestination("YSSY", 151.177, -33.4961), uid: 16969837613165511936n },
|
|
11
|
+
{ wp: new AeroflyNavRouteDestination("RJTT", 139.779, 35.709), uid: 16385890514078663936n },
|
|
12
|
+
];
|
|
13
|
+
for (const waypoint of waypoints) {
|
|
14
|
+
console.log(waypoint.wp.getUidFallback(), waypoint.uid);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
it("should test some unpacking of UIDs", () => {
|
|
18
|
+
const unpackPayload = (packed) => {
|
|
19
|
+
return Number(packed & 0xffffn);
|
|
20
|
+
};
|
|
21
|
+
console.log("KEYW", unpackPayload(5033914504046314752n));
|
|
22
|
+
console.log("EGLL", unpackPayload(9200173076798702848n));
|
|
23
|
+
console.log("YSSY", unpackPayload(16969826619723154688n));
|
|
24
|
+
console.log("RJTT", unpackPayload(16385782762532177152n));
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { convertFeetToMeter, convertMeterToFeet } from "../node/Convert.js";
|
|
2
2
|
import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
|
|
3
3
|
import { AeroflyNavRouteBase } from "./AeroflyNavRouteBase.js";
|
|
4
4
|
class AeroflyNavRouteRunway extends AeroflyNavRouteBase {
|
|
@@ -27,19 +27,19 @@ class AeroflyNavRouteRunway extends AeroflyNavRouteBase {
|
|
|
27
27
|
* @returns {number | null} elevation in feet, null if not set
|
|
28
28
|
*/
|
|
29
29
|
get elevation_ft() {
|
|
30
|
-
return this.elevation !== null ?
|
|
30
|
+
return this.elevation !== null ? convertMeterToFeet(this.elevation) : null;
|
|
31
31
|
}
|
|
32
32
|
set elevation_ft(elevation_ft) {
|
|
33
|
-
this.elevation = elevation_ft !== null ?
|
|
33
|
+
this.elevation = elevation_ft !== null ? convertFeetToMeter(elevation_ft) : null;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* @returns {number | null} runway length in feet, null if not set
|
|
37
37
|
*/
|
|
38
38
|
get runwayLength_ft() {
|
|
39
|
-
return this.runwayLength !== null ?
|
|
39
|
+
return this.runwayLength !== null ? convertMeterToFeet(this.runwayLength) : null;
|
|
40
40
|
}
|
|
41
41
|
set runwayLength_ft(runwayLength_ft) {
|
|
42
|
-
this.runwayLength = runwayLength_ft !== null ?
|
|
42
|
+
this.runwayLength = runwayLength_ft !== null ? convertFeetToMeter(runwayLength_ft) : null;
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
45
|
* @returns {AeroflyVector3Float | null} runway direction, null if not set
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { convertFeetToMeter, convertMeterToFeet } from "../node/Convert.js";
|
|
2
2
|
import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
|
|
3
3
|
import { AeroflyNavRouteBase } from "./AeroflyNavRouteBase.js";
|
|
4
4
|
class AeroflyNavRouteTransition extends AeroflyNavRouteBase {
|
|
@@ -29,10 +29,10 @@ class AeroflyNavRouteTransition extends AeroflyNavRouteBase {
|
|
|
29
29
|
* @returns {number | null} elevation in feet, null if not set
|
|
30
30
|
*/
|
|
31
31
|
get elevation_ft() {
|
|
32
|
-
return this.elevation !== null ?
|
|
32
|
+
return this.elevation !== null ? convertMeterToFeet(this.elevation) : null;
|
|
33
33
|
}
|
|
34
34
|
set elevation_ft(elevation_ft) {
|
|
35
|
-
this.elevation = elevation_ft !== null ?
|
|
35
|
+
this.elevation = elevation_ft !== null ? convertFeetToMeter(elevation_ft) : null;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
38
|
* @returns {AeroflyVector3Float} deliberately empty
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { convertFeetToMeter, convertLonLatToVector, convertMeterToFeet, convertVectorToLonLat, } from "../node/Convert.js";
|
|
2
2
|
import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
|
|
3
3
|
import { AeroflyNavRouteBase } from "./AeroflyNavRouteBase.js";
|
|
4
4
|
export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
@@ -9,6 +9,10 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
9
9
|
* @property {?number} navaidFrequency if the waypoint is a navaid, its frequency in Hz
|
|
10
10
|
*/
|
|
11
11
|
navaidFrequency;
|
|
12
|
+
/**
|
|
13
|
+
* @property {?bigint} navaidUid if the waypoint is a navaid, its unique identifier, must match Aerofly FS internal UID if used in an existing mission
|
|
14
|
+
*/
|
|
15
|
+
navaidUid;
|
|
12
16
|
/**
|
|
13
17
|
* @property {number | null} altitude in meter, null if not set
|
|
14
18
|
*/
|
|
@@ -23,17 +27,21 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
23
27
|
* @param {number} latitude WGS84
|
|
24
28
|
* @param {object} [options] additional options for the waypoint
|
|
25
29
|
* @param {?number} [options.navaidFrequency] if the waypoint is a navaid, its frequency in Hz
|
|
30
|
+
* @param {?number} [options.navaidFrequency_khz] if the waypoint is a navaid, its frequency in kHz, will override navaidFrequency in Hz if provided
|
|
31
|
+
* @param {?number} [options.navaidFrequency_mhz] if the waypoint is a navaid, its frequency in MHz, will override navaidFrequency in Hz if provided
|
|
32
|
+
* @param {?bigint} [options.navaidUid] if the waypoint is a navaid, its unique identifier, must match Aerofly FS internal UID if used in an existing mission
|
|
26
33
|
* @param {number} [options.altitude] in meter
|
|
27
34
|
* @param {?number} [options.altitude_ft] altitude in feet, will override altitude in meter if provided
|
|
28
35
|
* @param {boolean} [options.flyOver] if true, the waypoint is meant to be flown over, otherwise it can be used as a fly-by waypoint
|
|
29
36
|
* @param {?bigint} [options.uid] unique identifier for the waypoint, will be generated automatically if not provided
|
|
30
37
|
*/
|
|
31
|
-
constructor(identifier, longitude, latitude, { navaidFrequency = null, navaidFrequency_khz = null, navaidFrequency_mhz = null, altitude = null, altitude_ft = null, flyOver = false, uid = null, } = {}) {
|
|
38
|
+
constructor(identifier, longitude, latitude, { navaidFrequency = null, navaidFrequency_khz = null, navaidFrequency_mhz = null, navaidUid = null, altitude = null, altitude_ft = null, flyOver = false, uid = null, } = {}) {
|
|
32
39
|
super("waypoint", identifier, longitude, latitude, { uid });
|
|
33
40
|
this.identifier = identifier;
|
|
34
41
|
this.longitude = longitude;
|
|
35
42
|
this.latitude = latitude;
|
|
36
43
|
this.navaidFrequency = navaidFrequency;
|
|
44
|
+
this.navaidUid = navaidUid;
|
|
37
45
|
this.altitude = altitude;
|
|
38
46
|
if (altitude_ft !== null) {
|
|
39
47
|
this.altitude_ft = altitude_ft;
|
|
@@ -50,10 +58,10 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
50
58
|
* @returns {number | null} altitude in feet, null if not set
|
|
51
59
|
*/
|
|
52
60
|
get altitude_ft() {
|
|
53
|
-
return this.altitude !== null ?
|
|
61
|
+
return this.altitude !== null ? convertMeterToFeet(this.altitude) : null;
|
|
54
62
|
}
|
|
55
63
|
set altitude_ft(altitude_ft) {
|
|
56
|
-
this.altitude = altitude_ft !== null ?
|
|
64
|
+
this.altitude = altitude_ft !== null ? convertFeetToMeter(altitude_ft) : null;
|
|
57
65
|
}
|
|
58
66
|
get navaidFrequency_khz() {
|
|
59
67
|
return this.navaidFrequency ? this.navaidFrequency / 1000 : null;
|
|
@@ -71,20 +79,19 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
71
79
|
* @returns {AeroflyVector3Float} to use in Aerofly FS4's `main.mcf`
|
|
72
80
|
*/
|
|
73
81
|
get position() {
|
|
74
|
-
return
|
|
82
|
+
return convertLonLatToVector(this.longitude, this.latitude, this.altitude || 0);
|
|
75
83
|
}
|
|
76
84
|
set position(position) {
|
|
77
|
-
const { longitude, latitude, altitude_meter } =
|
|
85
|
+
const { longitude, latitude, altitude_meter } = convertVectorToLonLat(position);
|
|
78
86
|
this.longitude = longitude;
|
|
79
87
|
this.latitude = latitude;
|
|
80
88
|
this.altitude = altitude_meter;
|
|
81
89
|
}
|
|
82
90
|
getElement(index = 0) {
|
|
83
91
|
const element = super.getElement(index);
|
|
84
|
-
if (this.navaidFrequency) {
|
|
85
|
-
element.appendChild("float64", "NavaidFrequency", this.navaidFrequency);
|
|
86
|
-
}
|
|
87
92
|
element
|
|
93
|
+
.appendChild("float64", "NavaidFrequency", this.navaidFrequency ?? 0)
|
|
94
|
+
.appendChild("uint64", "NavaidUid", this.navaidUid ?? BigInt(0))
|
|
88
95
|
.appendChild("vector2_float64", "Altitude", this.altitude !== null && this.altitude > 0 ? [this.altitude, this.altitude] : [-1001, 100001], `${this.altitude_ft !== null && this.altitude_ft > 0 ? Math.ceil(this.altitude_ft) + " ft" : "unrestricted"}`)
|
|
89
96
|
.appendChild("bool", "FlyOver", this.flyOver);
|
|
90
97
|
return element;
|
|
@@ -97,6 +104,7 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
97
104
|
altitude_ft: this.altitude_ft,
|
|
98
105
|
navaidFrequency: undefined,
|
|
99
106
|
navaidFrequency_khz: this.navaidFrequency_khz,
|
|
107
|
+
navaidUid: this.navaidUid !== null ? this.navaidUid.toString() : null,
|
|
100
108
|
};
|
|
101
109
|
}
|
|
102
110
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
|
|
2
|
-
import {
|
|
2
|
+
import { convertFeetToMeter, convertMeterToFeet } from "../node/Convert.js";
|
|
3
3
|
import { AeroflyNavRouteBase } from "./AeroflyNavRouteBase.js";
|
|
4
4
|
export class AeroflyNavigationConfig {
|
|
5
5
|
/**
|
|
@@ -31,16 +31,16 @@ export class AeroflyNavigationConfig {
|
|
|
31
31
|
* @returns {AeroflyNavigationConfig} with cruise altitude converted to meters
|
|
32
32
|
*/
|
|
33
33
|
static createInFeet(cruiseAltitude_ft, waypoints = [], _cruiseSpeed_kts = undefined) {
|
|
34
|
-
return new AeroflyNavigationConfig(
|
|
34
|
+
return new AeroflyNavigationConfig(convertFeetToMeter(cruiseAltitude_ft), waypoints, _cruiseSpeed_kts);
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
37
|
* @returns {number} cruise altitude in feet
|
|
38
38
|
*/
|
|
39
39
|
get cruiseAltitude_ft() {
|
|
40
|
-
return
|
|
40
|
+
return convertMeterToFeet(this.cruiseAltitude);
|
|
41
41
|
}
|
|
42
42
|
set cruiseAltitude_ft(cruiseAltitude_ft) {
|
|
43
|
-
this.cruiseAltitude =
|
|
43
|
+
this.cruiseAltitude = convertFeetToMeter(cruiseAltitude_ft);
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
46
|
* @returns {AeroflyConfigurationNode[]} indexed checkpoints
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Convert } from "../node/Convert.js";
|
|
2
1
|
import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
|
|
2
|
+
import { convertFeetToMeter, convertLonLatToVector, convertVectorToLonLat, convertDegreeToMatrix, convertMatrixToDegree, convertMeterToFeet, } from "../node/Convert.js";
|
|
3
3
|
export class AeroflySettingsFlight {
|
|
4
4
|
longitude;
|
|
5
5
|
latitude;
|
|
@@ -52,7 +52,7 @@ export class AeroflySettingsFlight {
|
|
|
52
52
|
this.runway = runway;
|
|
53
53
|
}
|
|
54
54
|
static createInFeet(longitude, latitude, altitude_ft, heading_degree, speed_kts = 0, additionalAttributes = {}) {
|
|
55
|
-
return new AeroflySettingsFlight(longitude, latitude,
|
|
55
|
+
return new AeroflySettingsFlight(longitude, latitude, convertFeetToMeter(altitude_ft), heading_degree, speed_kts, additionalAttributes);
|
|
56
56
|
}
|
|
57
57
|
static createInCartesian(position, velocity, orientation, additionalAttributes = {}) {
|
|
58
58
|
const flight = new AeroflySettingsFlight(0, 0, 0, 0, 0, additionalAttributes);
|
|
@@ -95,10 +95,10 @@ export class AeroflySettingsFlight {
|
|
|
95
95
|
* @returns {AeroflyVector3Float} position vector to use in Aerofly FS4's `main.mcf`
|
|
96
96
|
*/
|
|
97
97
|
get position() {
|
|
98
|
-
return
|
|
98
|
+
return convertLonLatToVector(this.longitude, this.latitude, this.altitude_meter);
|
|
99
99
|
}
|
|
100
100
|
set position(position) {
|
|
101
|
-
const { longitude, latitude, altitude_meter } =
|
|
101
|
+
const { longitude, latitude, altitude_meter } = convertVectorToLonLat(position);
|
|
102
102
|
this.longitude = longitude;
|
|
103
103
|
this.latitude = latitude;
|
|
104
104
|
this.altitude_meter = altitude_meter;
|
|
@@ -116,19 +116,19 @@ export class AeroflySettingsFlight {
|
|
|
116
116
|
this.speed_ms = 0;
|
|
117
117
|
}
|
|
118
118
|
get orientation() {
|
|
119
|
-
return
|
|
119
|
+
return convertDegreeToMatrix(this.heading_degree);
|
|
120
120
|
}
|
|
121
121
|
set orientation(orientation) {
|
|
122
|
-
this.heading_degree =
|
|
122
|
+
this.heading_degree = convertMatrixToDegree(orientation);
|
|
123
123
|
}
|
|
124
124
|
/**
|
|
125
125
|
* @returns {number} altitude in feet AGL
|
|
126
126
|
*/
|
|
127
127
|
get altitude_ft() {
|
|
128
|
-
return
|
|
128
|
+
return convertMeterToFeet(this.altitude_meter);
|
|
129
129
|
}
|
|
130
130
|
set altitude_ft(altitude_ft) {
|
|
131
|
-
this.altitude_meter =
|
|
131
|
+
this.altitude_meter = convertFeetToMeter(altitude_ft);
|
|
132
132
|
}
|
|
133
133
|
/**
|
|
134
134
|
* @returns {number} speed in meters per seconds
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
|
|
2
|
-
import {
|
|
2
|
+
import { convertKgToLb, convertLbToKg } from "../node/Convert.js";
|
|
3
3
|
export class AeroflySettingsFuelLoad {
|
|
4
4
|
/**
|
|
5
5
|
* @property {string} aircraft aerofly aircraft name (e.g. "c172")
|
|
@@ -34,20 +34,20 @@ export class AeroflySettingsFuelLoad {
|
|
|
34
34
|
this.configuration = configuration;
|
|
35
35
|
}
|
|
36
36
|
get fuelMass_lb() {
|
|
37
|
-
return
|
|
37
|
+
return convertKgToLb(this.fuelMass);
|
|
38
38
|
}
|
|
39
39
|
set fuelMass_lb(fuelMass_lb) {
|
|
40
|
-
this.fuelMass =
|
|
40
|
+
this.fuelMass = convertLbToKg(fuelMass_lb);
|
|
41
41
|
}
|
|
42
42
|
get payloadMass_lb() {
|
|
43
|
-
return
|
|
43
|
+
return convertKgToLb(this.payloadMass);
|
|
44
44
|
}
|
|
45
45
|
set payloadMass_lb(payloadMass_lb) {
|
|
46
|
-
this.payloadMass =
|
|
46
|
+
this.payloadMass = convertLbToKg(payloadMass_lb);
|
|
47
47
|
}
|
|
48
48
|
getElement() {
|
|
49
49
|
return new AeroflyConfigurationNode("tmsettings_fuel_load", "fuel_load_setting")
|
|
50
|
-
.appendChild("fuel_load_configuration", "configuration", this.fuelMass > 0 ? "
|
|
50
|
+
.appendChild("fuel_load_configuration", "configuration", this.fuelMass > 0 && this.configuration === "Invalid" ? "Custom" : this.configuration)
|
|
51
51
|
.appendChild("string8u", "aircraft", this.aircraft)
|
|
52
52
|
.appendChild("float64", "fuel_mass", this.fuelMass)
|
|
53
53
|
.appendChild("float64", "payload_mass", this.payloadMass);
|
package/dist/node/Convert.js
CHANGED
|
@@ -1,80 +1,115 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
lambda = 0.5 * Math.PI;
|
|
1
|
+
/**
|
|
2
|
+
* @param {number} longitude in degrees
|
|
3
|
+
* @param {number} latitude in degrees
|
|
4
|
+
* @param {number} altitude_meter in meters
|
|
5
|
+
* @returns {AeroflyVector3Float} for Aerofly
|
|
6
|
+
*/
|
|
7
|
+
export function convertLonLatToVector(longitude, latitude, altitude_meter) {
|
|
8
|
+
const a = 6378137.0;
|
|
9
|
+
const f = 1.0 / 298.257223563;
|
|
10
|
+
const e2 = f * (2 - f);
|
|
11
|
+
const lon = longitude * (Math.PI / 180); // in radians
|
|
12
|
+
const lat = latitude * (Math.PI / 180); // in radians
|
|
13
|
+
const h = altitude_meter;
|
|
14
|
+
const sinLat = Math.sin(lat);
|
|
15
|
+
const cosLat = Math.cos(lat);
|
|
16
|
+
const cosLon = Math.cos(lon);
|
|
17
|
+
const sinLon = Math.sin(lon);
|
|
18
|
+
const N = a / Math.sqrt(1 - e2 * sinLat * sinLat);
|
|
19
|
+
const x = (N + h) * cosLat * cosLon;
|
|
20
|
+
const y = (N + h) * cosLat * sinLon;
|
|
21
|
+
const z = (N * (1 - e2) + h) * sinLat;
|
|
22
|
+
return [x, y, z];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* @param {AeroflyVector3Float} coordinates to convert
|
|
26
|
+
* @returns {object} as with longitude, latitude, altitude_meter
|
|
27
|
+
*/
|
|
28
|
+
export function convertVectorToLonLat(coordinates) {
|
|
29
|
+
// TODO: This implementation is not correct
|
|
30
|
+
const f = 1.0 / 298.257223563; // WGS84
|
|
31
|
+
const e2 = 2 * f - f * f;
|
|
32
|
+
//const lambda = VectorToAngle( coordinates[0], coordinates[1] );
|
|
33
|
+
let lambda = 0;
|
|
34
|
+
if (coordinates[0] > 0) {
|
|
35
|
+
if (coordinates[1] < 0) {
|
|
36
|
+
lambda = 2 * Math.PI + Math.atan(coordinates[1] / coordinates[0]);
|
|
38
37
|
}
|
|
39
38
|
else {
|
|
40
|
-
lambda = 1
|
|
39
|
+
lambda = Math.atan(coordinates[1] / coordinates[0]);
|
|
41
40
|
}
|
|
42
|
-
const rho = Math.sqrt(coordinates[0] * coordinates[0] + coordinates[1] * coordinates[1]);
|
|
43
|
-
const phi = Math.atan(coordinates[2] / ((1.0 - e2) * rho));
|
|
44
|
-
const longitude = (lambda * 180) / Math.PI;
|
|
45
|
-
const latitude = (phi * 180) / Math.PI;
|
|
46
|
-
const altitude_meter = rho / Math.cos(phi) - 6378137.0 / Math.sqrt(1 - e2 * Math.sin(phi) * Math.sin(phi));
|
|
47
|
-
return {
|
|
48
|
-
longitude: longitude > 180 ? longitude - 360 : longitude,
|
|
49
|
-
latitude: latitude > 90 ? latitude - 180 : latitude < -90 ? latitude + 180 : latitude,
|
|
50
|
-
altitude_meter,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
static convertDegreeToMatrix(heading_degree) {
|
|
54
|
-
// TODO: This implementation is not correct
|
|
55
|
-
const theta = heading_degree * (Math.PI / 180); // heading in radians
|
|
56
|
-
const cosTheta = Math.cos(theta);
|
|
57
|
-
const sinTheta = Math.sin(theta);
|
|
58
|
-
return [cosTheta, -sinTheta, 0, sinTheta, cosTheta, 0, 0, 0, 1];
|
|
59
|
-
}
|
|
60
|
-
static convertMatrixToDegree(orientation) {
|
|
61
|
-
const headingRad = Math.atan2(orientation[3], orientation[0]);
|
|
62
|
-
let headingDeg = headingRad * (180 / Math.PI);
|
|
63
|
-
// Normalize to [0, 360)
|
|
64
|
-
if (headingDeg < 0)
|
|
65
|
-
headingDeg += 360;
|
|
66
|
-
return headingDeg;
|
|
67
|
-
}
|
|
68
|
-
static convertMeterToFeet(meter) {
|
|
69
|
-
return meter * 3.28084;
|
|
70
41
|
}
|
|
71
|
-
|
|
72
|
-
|
|
42
|
+
else if (coordinates[0] < 0) {
|
|
43
|
+
lambda = Math.PI + Math.atan(coordinates[1] / coordinates[0]);
|
|
73
44
|
}
|
|
74
|
-
|
|
75
|
-
|
|
45
|
+
else if (coordinates[1] > 0) {
|
|
46
|
+
lambda = 0.5 * Math.PI;
|
|
76
47
|
}
|
|
77
|
-
|
|
78
|
-
|
|
48
|
+
else {
|
|
49
|
+
lambda = 1.5 * Math.PI;
|
|
79
50
|
}
|
|
51
|
+
const rho = Math.sqrt(coordinates[0] * coordinates[0] + coordinates[1] * coordinates[1]);
|
|
52
|
+
const phi = Math.atan(coordinates[2] / ((1.0 - e2) * rho));
|
|
53
|
+
const longitude = (lambda * 180) / Math.PI;
|
|
54
|
+
const latitude = (phi * 180) / Math.PI;
|
|
55
|
+
const altitude_meter = rho / Math.cos(phi) - 6378137.0 / Math.sqrt(1 - e2 * Math.sin(phi) * Math.sin(phi));
|
|
56
|
+
return {
|
|
57
|
+
longitude: longitude > 180 ? longitude - 360 : longitude,
|
|
58
|
+
latitude: latitude > 90 ? latitude - 180 : latitude < -90 ? latitude + 180 : latitude,
|
|
59
|
+
altitude_meter,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* @param {number} heading_degree in deg
|
|
64
|
+
* @returns {AeroflyMatrix3Float} for Aerofly
|
|
65
|
+
*/
|
|
66
|
+
export function convertDegreeToMatrix(heading_degree) {
|
|
67
|
+
// TODO: This implementation is not correct
|
|
68
|
+
const theta = heading_degree * (Math.PI / 180); // heading in radians
|
|
69
|
+
const cosTheta = Math.cos(theta);
|
|
70
|
+
const sinTheta = Math.sin(theta);
|
|
71
|
+
return [cosTheta, -sinTheta, 0, sinTheta, cosTheta, 0, 0, 0, 1];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* @param {AeroflyMatrix3Float} orientation as Matrxi
|
|
75
|
+
* @returns {number} heading in degrees
|
|
76
|
+
* @see https://www.aerofly.com/community/forum/index.php?thread/28025-custom-missions-file-livery-and-parking-position-property/&postID=184313#post184313
|
|
77
|
+
* @see
|
|
78
|
+
*/
|
|
79
|
+
export function convertMatrixToDegree(orientation) {
|
|
80
|
+
// TODO: https://www.aerofly.com/community/forum/index.php?thread/28025-custom-missions-file-livery-and-parking-position-property/&postID=184313#post184313
|
|
81
|
+
const headingRad = Math.atan2(orientation[3], orientation[0]);
|
|
82
|
+
let headingDeg = headingRad * (180 / Math.PI);
|
|
83
|
+
// Normalize to [0, 360)
|
|
84
|
+
if (headingDeg < 0)
|
|
85
|
+
headingDeg += 360;
|
|
86
|
+
return headingDeg;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* @param {number} meter in meters
|
|
90
|
+
* @returns {number} feet
|
|
91
|
+
*/
|
|
92
|
+
export function convertMeterToFeet(meter) {
|
|
93
|
+
return meter / 0.3048;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* @param {number} feet in feet
|
|
97
|
+
* @returns {number} meters
|
|
98
|
+
*/
|
|
99
|
+
export function convertFeetToMeter(feet) {
|
|
100
|
+
return feet * 0.3048;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* @param {number} kg in kilograms
|
|
104
|
+
* @returns {number} pounds
|
|
105
|
+
*/
|
|
106
|
+
export function convertKgToLb(kg) {
|
|
107
|
+
return kg / 0.45359237;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @param {number} lb in pounds
|
|
111
|
+
* @returns {number} kilograms
|
|
112
|
+
*/
|
|
113
|
+
export function convertLbToKg(lb) {
|
|
114
|
+
return lb * 0.45359237;
|
|
80
115
|
}
|