@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
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
import { describe, it } from "node:test";
|
|
2
2
|
import { strict as assert } from "node:assert";
|
|
3
|
-
import {
|
|
3
|
+
import { convertDegreeToMatrix, convertFeetToMeter, convertLonLatToVector, convertMatrixToDegree, convertMeterToFeet, convertVectorToLonLat, } from "./Convert.js";
|
|
4
4
|
describe("Convert", () => {
|
|
5
5
|
it("should convert feet to meters correctly", () => {
|
|
6
6
|
const feet = 1000;
|
|
7
|
-
const expectedMeters = 304.
|
|
8
|
-
const actualMeters =
|
|
7
|
+
const expectedMeters = 304.8;
|
|
8
|
+
const actualMeters = convertFeetToMeter(feet);
|
|
9
9
|
assert.strictEqual(actualMeters, expectedMeters, `Expected ${feet} feet to be ${expectedMeters} meters, got ${actualMeters} meters`);
|
|
10
10
|
});
|
|
11
11
|
it("should convert meters to feet correctly", () => {
|
|
12
12
|
const meters = 500;
|
|
13
|
-
const expectedFeet = 1640.
|
|
14
|
-
const actualFeet =
|
|
15
|
-
assert.strictEqual(actualFeet, expectedFeet, `Expected ${meters} meters to be ${expectedFeet} feet, got ${actualFeet} feet`);
|
|
13
|
+
const expectedFeet = 1640.4199;
|
|
14
|
+
const actualFeet = convertMeterToFeet(meters);
|
|
15
|
+
assert.strictEqual(actualFeet.toPrecision(4), expectedFeet.toPrecision(4), `Expected ${meters} meters to be ${expectedFeet} feet, got ${actualFeet} feet`);
|
|
16
16
|
});
|
|
17
17
|
it("should convert longitude / latitude to vector and back correctly", () => {
|
|
18
18
|
const longitude = -122.3088;
|
|
19
19
|
const latitude = 47.4502;
|
|
20
20
|
const expectedLongitude = -122.3088;
|
|
21
21
|
const expectedLatitude = 47.4502;
|
|
22
|
-
const vector =
|
|
23
|
-
const lonLat =
|
|
22
|
+
const vector = convertLonLatToVector(longitude, latitude, 0);
|
|
23
|
+
const lonLat = convertVectorToLonLat(vector);
|
|
24
24
|
assert.strictEqual(lonLat.longitude.toFixed(4), expectedLongitude.toFixed(4), `Expected longitude to be ${expectedLongitude.toFixed(4)}, got ${lonLat.longitude.toFixed(4)}`);
|
|
25
25
|
assert.strictEqual(lonLat.latitude.toFixed(4), expectedLatitude.toFixed(4), `Expected latitude to be ${expectedLatitude.toFixed(4)}, got ${lonLat.latitude.toFixed(4)}`);
|
|
26
26
|
});
|
|
27
27
|
it("should convert heading degree to orientation matrix and back correctly", () => {
|
|
28
28
|
const orientationMatrix = [0, -1, 0, 1, 0, 0, 0, 0, 1];
|
|
29
29
|
const expectedHeadingDegree = 90;
|
|
30
|
-
const actualHeadingDegree =
|
|
30
|
+
const actualHeadingDegree = convertMatrixToDegree(orientationMatrix);
|
|
31
31
|
assert.strictEqual(actualHeadingDegree.toFixed(4), expectedHeadingDegree.toFixed(4), `Expected heading degree to be ${expectedHeadingDegree.toFixed(4)}, got ${actualHeadingDegree.toFixed(4)}`);
|
|
32
32
|
});
|
|
33
33
|
it("should convert heading degree to orientation matrix and back correctly", () => {
|
|
34
34
|
const heading_degree = 90;
|
|
35
35
|
const expectedHeadingDegree = 90;
|
|
36
|
-
const orientationMatrix =
|
|
36
|
+
const orientationMatrix = convertDegreeToMatrix(heading_degree);
|
|
37
37
|
console.log("Orientation Matrix:", orientationMatrix);
|
|
38
|
-
const actualHeadingDegree =
|
|
38
|
+
const actualHeadingDegree = convertMatrixToDegree(orientationMatrix);
|
|
39
39
|
assert.strictEqual(actualHeadingDegree.toFixed(4), expectedHeadingDegree.toFixed(4), `Expected heading degree to be ${expectedHeadingDegree.toFixed(4)}, got ${actualHeadingDegree.toFixed(4)}`);
|
|
40
40
|
});
|
|
41
41
|
it("should show the orientation at Tellruide correctly", () => {
|
|
@@ -44,7 +44,7 @@ describe("Convert", () => {
|
|
|
44
44
|
-0.206738179058507, -0.207947329711721, -0.710293000184874, 0.672489228132418,
|
|
45
45
|
];
|
|
46
46
|
const expectedHeadingDegree = 117.3585;
|
|
47
|
-
const actualHeadingDegree =
|
|
47
|
+
const actualHeadingDegree = convertMatrixToDegree(orientationMatrix);
|
|
48
48
|
assert.strictEqual(actualHeadingDegree.toFixed(4), expectedHeadingDegree.toFixed(4), `Expected heading degree to be ${expectedHeadingDegree.toFixed(4)}, got ${actualHeadingDegree.toFixed(4)}`);
|
|
49
49
|
});
|
|
50
50
|
});
|
package/docs/flight.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"onGround": true,
|
|
18
18
|
"airport": "KTEX",
|
|
19
19
|
"runway": "",
|
|
20
|
-
"altitude_ft": 9072.
|
|
20
|
+
"altitude_ft": 9072.275314580287
|
|
21
21
|
},
|
|
22
22
|
"fuelLoadSetting": {
|
|
23
23
|
"aircraft": "c172",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"longitude": -81.75955555555561,
|
|
57
57
|
"latitude": 24.55611111111108,
|
|
58
58
|
"uid": "5033935394767185920",
|
|
59
|
-
"elevation_ft": 3
|
|
59
|
+
"elevation_ft": 3
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
"type": "departure_runway",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"latitude": 24.556052777777765,
|
|
66
66
|
"uid": "5033564859348430848",
|
|
67
67
|
"direction_degree": null,
|
|
68
|
-
"elevation_ft": 3
|
|
69
|
-
"runwayLength_ft": 4801
|
|
68
|
+
"elevation_ft": 3,
|
|
69
|
+
"runwayLength_ft": 4801
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
72
|
"type": "waypoint",
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"longitude": -122.3088,
|
|
75
75
|
"latitude": 47.4502,
|
|
76
76
|
"uid": null,
|
|
77
|
+
"navaidUid": null,
|
|
77
78
|
"flyOver": false,
|
|
78
79
|
"altitude_ft": null,
|
|
79
80
|
"navaidFrequency_khz": 260
|
|
@@ -84,6 +85,7 @@
|
|
|
84
85
|
"longitude": -122.3088,
|
|
85
86
|
"latitude": 47.4502,
|
|
86
87
|
"uid": null,
|
|
88
|
+
"navaidUid": null,
|
|
87
89
|
"flyOver": false,
|
|
88
90
|
"altitude_ft": null,
|
|
89
91
|
"navaidFrequency_khz": null
|
|
@@ -94,6 +96,7 @@
|
|
|
94
96
|
"longitude": -122.3088,
|
|
95
97
|
"latitude": 47.4502,
|
|
96
98
|
"uid": null,
|
|
99
|
+
"navaidUid": null,
|
|
97
100
|
"flyOver": false,
|
|
98
101
|
"altitude_ft": null,
|
|
99
102
|
"navaidFrequency_khz": 108200
|
|
@@ -107,7 +110,7 @@
|
|
|
107
110
|
"airport": "KMIA",
|
|
108
111
|
"transition": "",
|
|
109
112
|
"transitionUid": null,
|
|
110
|
-
"elevation_ft": 8
|
|
113
|
+
"elevation_ft": 8
|
|
111
114
|
},
|
|
112
115
|
{
|
|
113
116
|
"type": "destination_runway",
|
|
@@ -116,8 +119,8 @@
|
|
|
116
119
|
"latitude": 25.80289722222224,
|
|
117
120
|
"uid": "5108645015094833152",
|
|
118
121
|
"direction_degree": null,
|
|
119
|
-
"elevation_ft": 8
|
|
120
|
-
"runwayLength_ft": 8600
|
|
122
|
+
"elevation_ft": 8,
|
|
123
|
+
"runwayLength_ft": 8600
|
|
121
124
|
},
|
|
122
125
|
{
|
|
123
126
|
"type": "destination",
|
|
@@ -125,10 +128,10 @@
|
|
|
125
128
|
"longitude": -80.29011111111112,
|
|
126
129
|
"latitude": 25.79536111111115,
|
|
127
130
|
"uid": "5109231054766614528",
|
|
128
|
-
"elevation_ft":
|
|
131
|
+
"elevation_ft": 8.999999999999998
|
|
129
132
|
}
|
|
130
133
|
],
|
|
131
|
-
"cruiseAltitude_ft": 32000
|
|
134
|
+
"cruiseAltitude_ft": 32000
|
|
132
135
|
},
|
|
133
136
|
"visibility_meter": 7999.994999999999,
|
|
134
137
|
"_missionTitle": "Mission title",
|
package/docs/flight.mcf
CHANGED
|
@@ -47,41 +47,45 @@
|
|
|
47
47
|
>
|
|
48
48
|
<[tmnavigation_config][navigation][]
|
|
49
49
|
<[tmnav_route][Route][]
|
|
50
|
-
<[float64][CruiseAltitude][9753.6]> //
|
|
50
|
+
<[float64][CruiseAltitude][9753.6]> // 32000 ft
|
|
51
51
|
<[pointer_list_tmnav_route_way][Ways][]
|
|
52
52
|
<[tmnav_route_origin][KEYW][0]
|
|
53
53
|
<[string8u][Identifier][KEYW]>
|
|
54
54
|
<[vector3_float64][Position][831962.2473395835 -5744690.224311131 2634431.8120697998]> // Lon -81.759556, Lat 24.556111
|
|
55
55
|
<[uint64][Uid][5033935394767185920]>
|
|
56
|
-
<[float64][Elevation][0.9144]> // Elevation
|
|
56
|
+
<[float64][Elevation][0.9144]> // Elevation 3 ft
|
|
57
57
|
>
|
|
58
58
|
<[tmnav_route_departure_runway][09][1]
|
|
59
59
|
<[string8u][Identifier][09]>
|
|
60
60
|
<[vector3_float64][Position][831239.6121807017 -5744797.5451743435 2634425.9351001014]> // Lon -81.766767, Lat 24.556053
|
|
61
61
|
<[uint64][Uid][5033564859348430848]>
|
|
62
|
-
<[float64][Elevation][0.9144]> // Elevation
|
|
62
|
+
<[float64][Elevation][0.9144]> // Elevation 3 ft
|
|
63
63
|
<[float64][RunwayLength][1463.3448]> // Runway length 4801 ft
|
|
64
64
|
>
|
|
65
65
|
<[tmnav_route_waypoint][MTH][2]
|
|
66
66
|
<[string8u][Identifier][MTH]>
|
|
67
67
|
<[vector3_float64][Position][-2309470.142824746 -3651978.5471377173 4675755.385390146]> // Lon -122.308800, Lat 47.450200
|
|
68
|
-
|
|
68
|
+
<[uint64][Uid][2956153176169775104]> // Fallback UID, not matching Aerofly FS internal UID
|
|
69
69
|
<[float64][NavaidFrequency][260000]>
|
|
70
|
+
<[uint64][NavaidUid][0]>
|
|
70
71
|
<[vector2_float64][Altitude][-1001 100001]> // unrestricted
|
|
71
72
|
<[bool][FlyOver][false]>
|
|
72
73
|
>
|
|
73
74
|
<[tmnav_route_waypoint][MNATE][3]
|
|
74
75
|
<[string8u][Identifier][MNATE]>
|
|
75
76
|
<[vector3_float64][Position][-2309470.142824746 -3651978.5471377173 4675755.385390146]> // Lon -122.308800, Lat 47.450200
|
|
76
|
-
|
|
77
|
+
<[uint64][Uid][2956153176169775104]> // Fallback UID, not matching Aerofly FS internal UID
|
|
78
|
+
<[float64][NavaidFrequency][0]>
|
|
79
|
+
<[uint64][NavaidUid][0]>
|
|
77
80
|
<[vector2_float64][Altitude][-1001 100001]> // unrestricted
|
|
78
81
|
<[bool][FlyOver][false]>
|
|
79
82
|
>
|
|
80
83
|
<[tmnav_route_waypoint][HST][4]
|
|
81
84
|
<[string8u][Identifier][HST]>
|
|
82
85
|
<[vector3_float64][Position][-2309470.142824746 -3651978.5471377173 4675755.385390146]> // Lon -122.308800, Lat 47.450200
|
|
83
|
-
|
|
86
|
+
<[uint64][Uid][2956153176169775104]> // Fallback UID, not matching Aerofly FS internal UID
|
|
84
87
|
<[float64][NavaidFrequency][108200000]>
|
|
88
|
+
<[uint64][NavaidUid][0]>
|
|
85
89
|
<[vector2_float64][Altitude][-1001 100001]> // unrestricted
|
|
86
90
|
<[bool][FlyOver][false]>
|
|
87
91
|
>
|
|
@@ -91,7 +95,7 @@
|
|
|
91
95
|
<[uint64][Uid][5109231054766956544]>
|
|
92
96
|
<[string8u][Airport][KMIA]>
|
|
93
97
|
<[vector3_float64][Direction][0 0 0]>
|
|
94
|
-
<[float64][Elevation][2.4384]> // Elevation
|
|
98
|
+
<[float64][Elevation][2.4384]> // Elevation 8 ft
|
|
95
99
|
<[string8u][Transition][]>
|
|
96
100
|
<[uint64][TransitionUid][0]>
|
|
97
101
|
>
|
|
@@ -99,14 +103,14 @@
|
|
|
99
103
|
<[string8u][Identifier][08L]>
|
|
100
104
|
<[vector3_float64][Position][967964.1844961813 -5663741.310440378 2759419.1621422493]> // Lon -80.301542, Lat 25.802897
|
|
101
105
|
<[uint64][Uid][5108645015094833152]>
|
|
102
|
-
<[float64][Elevation][2.4384]> // Elevation
|
|
106
|
+
<[float64][Elevation][2.4384]> // Elevation 8 ft
|
|
103
107
|
<[float64][RunwayLength][2621.28]> // Runway length 8600 ft
|
|
104
108
|
>
|
|
105
109
|
<[tmnav_route_destination][KMIA][7]
|
|
106
110
|
<[string8u][Identifier][KMIA]>
|
|
107
111
|
<[vector3_float64][Position][969155.3700254696 -5663906.240326158 2758667.49173681]> // Lon -80.290111, Lat 25.795361
|
|
108
112
|
<[uint64][Uid][5109231054766614528]>
|
|
109
|
-
<[float64][Elevation][2.7432]> // Elevation
|
|
113
|
+
<[float64][Elevation][2.7432]> // Elevation 9 ft
|
|
110
114
|
>
|
|
111
115
|
>
|
|
112
116
|
>
|
package/docs/flight.xml
CHANGED
|
@@ -47,41 +47,45 @@
|
|
|
47
47
|
</clouds>
|
|
48
48
|
<navigation type="tmnavigation_config">
|
|
49
49
|
<Route type="tmnav_route">
|
|
50
|
-
<CruiseAltitude type="float64">9753.6</CruiseAltitude> <!--
|
|
50
|
+
<CruiseAltitude type="float64">9753.6</CruiseAltitude> <!-- 32000 ft -->
|
|
51
51
|
<Ways type="pointer_list_tmnav_route_way">
|
|
52
52
|
<KEYW type="tmnav_route_origin" index="0">
|
|
53
53
|
<Identifier type="string8u">KEYW</Identifier>
|
|
54
54
|
<Position type="vector3_float64">831962.2473395835 -5744690.224311131 2634431.8120697998</Position> <!-- Lon -81.759556, Lat 24.556111 -->
|
|
55
55
|
<Uid type="uint64">5033935394767185920</Uid>
|
|
56
|
-
<Elevation type="float64">0.9144</Elevation> <!-- Elevation
|
|
56
|
+
<Elevation type="float64">0.9144</Elevation> <!-- Elevation 3 ft -->
|
|
57
57
|
</KEYW>
|
|
58
58
|
<09 type="tmnav_route_departure_runway" index="1">
|
|
59
59
|
<Identifier type="string8u">09</Identifier>
|
|
60
60
|
<Position type="vector3_float64">831239.6121807017 -5744797.5451743435 2634425.9351001014</Position> <!-- Lon -81.766767, Lat 24.556053 -->
|
|
61
61
|
<Uid type="uint64">5033564859348430848</Uid>
|
|
62
|
-
<Elevation type="float64">0.9144</Elevation> <!-- Elevation
|
|
62
|
+
<Elevation type="float64">0.9144</Elevation> <!-- Elevation 3 ft -->
|
|
63
63
|
<RunwayLength type="float64">1463.3448</RunwayLength> <!-- Runway length 4801 ft -->
|
|
64
64
|
</09>
|
|
65
65
|
<MTH type="tmnav_route_waypoint" index="2">
|
|
66
66
|
<Identifier type="string8u">MTH</Identifier>
|
|
67
67
|
<Position type="vector3_float64">-2309470.142824746 -3651978.5471377173 4675755.385390146</Position> <!-- Lon -122.308800, Lat 47.450200 -->
|
|
68
|
-
|
|
68
|
+
<Uid type="uint64">2956153176169775104</Uid> <!-- Fallback UID, not matching Aerofly FS internal UID -->
|
|
69
69
|
<NavaidFrequency type="float64">260000</NavaidFrequency>
|
|
70
|
+
<NavaidUid type="uint64">0</NavaidUid>
|
|
70
71
|
<Altitude type="vector2_float64">-1001 100001</Altitude> <!-- unrestricted -->
|
|
71
72
|
<FlyOver type="bool">false</FlyOver>
|
|
72
73
|
</MTH>
|
|
73
74
|
<MNATE type="tmnav_route_waypoint" index="3">
|
|
74
75
|
<Identifier type="string8u">MNATE</Identifier>
|
|
75
76
|
<Position type="vector3_float64">-2309470.142824746 -3651978.5471377173 4675755.385390146</Position> <!-- Lon -122.308800, Lat 47.450200 -->
|
|
76
|
-
|
|
77
|
+
<Uid type="uint64">2956153176169775104</Uid> <!-- Fallback UID, not matching Aerofly FS internal UID -->
|
|
78
|
+
<NavaidFrequency type="float64">0</NavaidFrequency>
|
|
79
|
+
<NavaidUid type="uint64">0</NavaidUid>
|
|
77
80
|
<Altitude type="vector2_float64">-1001 100001</Altitude> <!-- unrestricted -->
|
|
78
81
|
<FlyOver type="bool">false</FlyOver>
|
|
79
82
|
</MNATE>
|
|
80
83
|
<HST type="tmnav_route_waypoint" index="4">
|
|
81
84
|
<Identifier type="string8u">HST</Identifier>
|
|
82
85
|
<Position type="vector3_float64">-2309470.142824746 -3651978.5471377173 4675755.385390146</Position> <!-- Lon -122.308800, Lat 47.450200 -->
|
|
83
|
-
|
|
86
|
+
<Uid type="uint64">2956153176169775104</Uid> <!-- Fallback UID, not matching Aerofly FS internal UID -->
|
|
84
87
|
<NavaidFrequency type="float64">108200000</NavaidFrequency>
|
|
88
|
+
<NavaidUid type="uint64">0</NavaidUid>
|
|
85
89
|
<Altitude type="vector2_float64">-1001 100001</Altitude> <!-- unrestricted -->
|
|
86
90
|
<FlyOver type="bool">false</FlyOver>
|
|
87
91
|
</HST>
|
|
@@ -91,7 +95,7 @@
|
|
|
91
95
|
<Uid type="uint64">5109231054766956544</Uid>
|
|
92
96
|
<Airport type="string8u">KMIA</Airport>
|
|
93
97
|
<Direction type="vector3_float64">0 0 0</Direction>
|
|
94
|
-
<Elevation type="float64">2.4384</Elevation> <!-- Elevation
|
|
98
|
+
<Elevation type="float64">2.4384</Elevation> <!-- Elevation 8 ft -->
|
|
95
99
|
<Transition type="string8u"></Transition>
|
|
96
100
|
<TransitionUid type="uint64">0</TransitionUid>
|
|
97
101
|
</L08L>
|
|
@@ -99,14 +103,14 @@
|
|
|
99
103
|
<Identifier type="string8u">08L</Identifier>
|
|
100
104
|
<Position type="vector3_float64">967964.1844961813 -5663741.310440378 2759419.1621422493</Position> <!-- Lon -80.301542, Lat 25.802897 -->
|
|
101
105
|
<Uid type="uint64">5108645015094833152</Uid>
|
|
102
|
-
<Elevation type="float64">2.4384</Elevation> <!-- Elevation
|
|
106
|
+
<Elevation type="float64">2.4384</Elevation> <!-- Elevation 8 ft -->
|
|
103
107
|
<RunwayLength type="float64">2621.28</RunwayLength> <!-- Runway length 8600 ft -->
|
|
104
108
|
</08L>
|
|
105
109
|
<KMIA type="tmnav_route_destination" index="7">
|
|
106
110
|
<Identifier type="string8u">KMIA</Identifier>
|
|
107
111
|
<Position type="vector3_float64">969155.3700254696 -5663906.240326158 2758667.49173681</Position> <!-- Lon -80.290111, Lat 25.795361 -->
|
|
108
112
|
<Uid type="uint64">5109231054766614528</Uid>
|
|
109
|
-
<Elevation type="float64">2.7432</Elevation> <!-- Elevation
|
|
113
|
+
<Elevation type="float64">2.7432</Elevation> <!-- Elevation 9 ft -->
|
|
110
114
|
</KMIA>
|
|
111
115
|
</Ways>
|
|
112
116
|
</Route>
|
package/package.json
CHANGED
|
@@ -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, type AeroflyNavRouteType } from "./AeroflyNavRouteBase.js";
|
|
4
4
|
|
|
5
5
|
class AeroflyNavRouteAirport extends AeroflyNavRouteBase {
|
|
@@ -36,11 +36,11 @@ class AeroflyNavRouteAirport extends AeroflyNavRouteBase {
|
|
|
36
36
|
* @returns {number | null} elevation in feet, null if not set
|
|
37
37
|
*/
|
|
38
38
|
get elevation_ft(): number | null {
|
|
39
|
-
return this.elevation !== null ?
|
|
39
|
+
return this.elevation !== null ? convertMeterToFeet(this.elevation) : null;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
set elevation_ft(elevation_ft: number | null) {
|
|
43
|
-
this.elevation = elevation_ft !== null ?
|
|
43
|
+
this.elevation = elevation_ft !== null ? convertFeetToMeter(elevation_ft) : null;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
getElement(index: number = 0): AeroflyConfigurationNode {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import { AeroflyNavRouteDestination } from "../index.js";
|
|
3
|
+
|
|
4
|
+
describe("AeroflyNavRouteBase", () => {
|
|
5
|
+
it("should create a valid AeroflyNavRouteBase structure", () => {
|
|
6
|
+
const waypoints = [
|
|
7
|
+
{ wp: new AeroflyNavRouteDestination("MAX-NE", 179.999, 89.999), uid: 0n },
|
|
8
|
+
{ wp: new AeroflyNavRouteDestination("MAX-SW", -179.999, -89.999), uid: 0n },
|
|
9
|
+
{ wp: new AeroflyNavRouteDestination("KEYW", -81.7599558, 24.5561197), uid: 5033914504046249216n },
|
|
10
|
+
{ wp: new AeroflyNavRouteDestination("EGLL", -0.45277777, 51.47138888), uid: 9199731073154483456n },
|
|
11
|
+
{ wp: new AeroflyNavRouteDestination("YSSY", 151.177, -33.4961), uid: 16969837613165511936n },
|
|
12
|
+
{ wp: new AeroflyNavRouteDestination("RJTT", 139.779, 35.709), uid: 16385890514078663936n },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
for (const waypoint of waypoints) {
|
|
16
|
+
console.log(waypoint.wp.getUidFallback(), waypoint.uid);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should test some unpacking of UIDs", () => {
|
|
21
|
+
const unpackPayload = (packed: bigint) => {
|
|
22
|
+
return Number(packed & 0xffffn);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
console.log("KEYW", unpackPayload(5033914504046314752n));
|
|
26
|
+
console.log("EGLL", unpackPayload(9200173076798702848n));
|
|
27
|
+
console.log("YSSY", unpackPayload(16969826619723154688n));
|
|
28
|
+
console.log("RJTT", unpackPayload(16385782762532177152n));
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AeroflyConfigurationNode
|
|
1
|
+
import { convertLonLatToVector, convertVectorToLonLat, type AeroflyVector3Float } from "../node/Convert.js";
|
|
2
|
+
import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
|
|
3
3
|
|
|
4
4
|
export type AeroflyNavRouteType =
|
|
5
5
|
| "origin"
|
|
@@ -33,7 +33,7 @@ export class AeroflyNavRouteBase {
|
|
|
33
33
|
latitude: number;
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* @property {?bigint} uid unique identifier for the waypoint, must match Aerofly FS internal UID if used in an existing mission
|
|
36
|
+
* @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.
|
|
37
37
|
*/
|
|
38
38
|
uid: bigint | null;
|
|
39
39
|
|
|
@@ -43,7 +43,7 @@ export class AeroflyNavRouteBase {
|
|
|
43
43
|
* @param {number} longitude WGS84
|
|
44
44
|
* @param {number} latitude WGS84
|
|
45
45
|
* @param {object} [options] additional options for the waypoint
|
|
46
|
-
* @param {?bigint} [options.uid] unique identifier for the waypoint, must match Aerofly FS internal UID if used in an existing mission
|
|
46
|
+
* @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.
|
|
47
47
|
*/
|
|
48
48
|
constructor(
|
|
49
49
|
type: AeroflyNavRouteType,
|
|
@@ -63,11 +63,11 @@ export class AeroflyNavRouteBase {
|
|
|
63
63
|
* @returns {AeroflyVector3Float} to use in Aerofly FS4's `main.mcf`
|
|
64
64
|
*/
|
|
65
65
|
get position(): AeroflyVector3Float {
|
|
66
|
-
return
|
|
66
|
+
return convertLonLatToVector(this.longitude, this.latitude, 0);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
set position(position: AeroflyVector3Float) {
|
|
70
|
-
const latLonAlt =
|
|
70
|
+
const latLonAlt = convertVectorToLonLat(position);
|
|
71
71
|
this.longitude = latLonAlt.longitude;
|
|
72
72
|
this.latitude = latLonAlt.latitude;
|
|
73
73
|
}
|
|
@@ -91,15 +91,76 @@ export class AeroflyNavRouteBase {
|
|
|
91
91
|
this.position,
|
|
92
92
|
`Lon ${this.longitude.toFixed(6)}, Lat ${this.latitude.toFixed(6)}`,
|
|
93
93
|
)
|
|
94
|
-
.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
.appendChild(
|
|
95
|
+
"uint64",
|
|
96
|
+
"Uid",
|
|
97
|
+
this.uid ?? this.getUidFallback(),
|
|
98
|
+
this.uid ? "" : "Fallback UID, not matching Aerofly FS internal UID",
|
|
98
99
|
);
|
|
99
100
|
|
|
100
101
|
return element;
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
/**
|
|
105
|
+
* @returns {bigint} 24 bit longitude
|
|
106
|
+
* @see https://www.aerofly.com/community/forum/index.php?thread/29490-navdata-coordinates-to-uid/
|
|
107
|
+
*/
|
|
108
|
+
private encodeLongitude(): bigint {
|
|
109
|
+
let scaled = this.longitude / 180;
|
|
110
|
+
scaled = 65536.0 * (0.5 + 0.5 * scaled); // 16 bit
|
|
111
|
+
return BigInt(Math.round(256.0 * scaled + 0.5)); // 24 bit
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @returns {bigint} 24 bit latitude
|
|
116
|
+
* @see https://www.aerofly.com/community/forum/index.php?thread/29490-navdata-coordinates-to-uid/
|
|
117
|
+
*/
|
|
118
|
+
private encodeLatitude(): bigint {
|
|
119
|
+
const worldGridConstantA = 2.3311223704144;
|
|
120
|
+
|
|
121
|
+
let scaled = this.latitude / 180;
|
|
122
|
+
scaled = Math.tan(worldGridConstantA * scaled) / worldGridConstantA;
|
|
123
|
+
scaled = 65536.0 * (0.5 + 0.5 * scaled); // 16 bit
|
|
124
|
+
return BigInt(Math.round(256.0 * scaled + 0.5)); // 24 bit
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @returns {number} 16 bit type
|
|
129
|
+
*/
|
|
130
|
+
private encodeWaypointType(): number {
|
|
131
|
+
// Type code (airport (0500) / runway (0800) / SID (4400) / STAR (4800) / Approach (4C00) / RNAV waypoint (C000) / airways (4000)
|
|
132
|
+
switch (this.type) {
|
|
133
|
+
case "origin":
|
|
134
|
+
case "destination":
|
|
135
|
+
return 0x0500;
|
|
136
|
+
case "departure_runway":
|
|
137
|
+
case "destination_runway":
|
|
138
|
+
return 0x0800;
|
|
139
|
+
case "departure":
|
|
140
|
+
return 0x4400;
|
|
141
|
+
case "arrival":
|
|
142
|
+
return 0x4800;
|
|
143
|
+
case "approach":
|
|
144
|
+
return 0x4c00;
|
|
145
|
+
}
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @returns {bigint} Packs both into a single 64-bit value:
|
|
151
|
+
* - Bits 63..40 → longitude (24 bit)
|
|
152
|
+
* - Bits 39..16 → latitude (24 bit)
|
|
153
|
+
* - Bits 15..0 → payload (16 bit)
|
|
154
|
+
* @see https://www.aerofly.com/community/forum/index.php?thread/29490-navdata-coordinates-to-uid/
|
|
155
|
+
*/
|
|
156
|
+
getUidFallback(): bigint {
|
|
157
|
+
return (
|
|
158
|
+
(this.encodeLongitude() << 40n) |
|
|
159
|
+
(this.encodeLatitude() << 16n) |
|
|
160
|
+
(BigInt(this.encodeWaypointType()) & 0xffffn)
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
103
164
|
toJSON() {
|
|
104
165
|
return {
|
|
105
166
|
...this,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AeroflyVector3Float
|
|
1
|
+
import { convertFeetToMeter, convertMeterToFeet, type AeroflyVector3Float } from "../node/Convert.js";
|
|
2
2
|
import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
|
|
3
3
|
import { AeroflyNavRouteBase, type AeroflyNavRouteType } from "./AeroflyNavRouteBase.js";
|
|
4
4
|
|
|
@@ -44,22 +44,22 @@ class AeroflyNavRouteRunway extends AeroflyNavRouteBase {
|
|
|
44
44
|
* @returns {number | null} elevation in feet, null if not set
|
|
45
45
|
*/
|
|
46
46
|
get elevation_ft(): number | null {
|
|
47
|
-
return this.elevation !== null ?
|
|
47
|
+
return this.elevation !== null ? convertMeterToFeet(this.elevation) : null;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
set elevation_ft(elevation_ft: number | null) {
|
|
51
|
-
this.elevation = elevation_ft !== null ?
|
|
51
|
+
this.elevation = elevation_ft !== null ? convertFeetToMeter(elevation_ft) : null;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* @returns {number | null} runway length in feet, null if not set
|
|
56
56
|
*/
|
|
57
57
|
get runwayLength_ft(): number | null {
|
|
58
|
-
return this.runwayLength !== null ?
|
|
58
|
+
return this.runwayLength !== null ? convertMeterToFeet(this.runwayLength) : null;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
set runwayLength_ft(runwayLength_ft: number | null) {
|
|
62
|
-
this.runwayLength = runwayLength_ft !== null ?
|
|
62
|
+
this.runwayLength = runwayLength_ft !== null ? convertFeetToMeter(runwayLength_ft) : null;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AeroflyVector3Float
|
|
1
|
+
import { convertFeetToMeter, convertMeterToFeet, type AeroflyVector3Float } from "../node/Convert.js";
|
|
2
2
|
import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
|
|
3
3
|
import { AeroflyNavRouteBase, type AeroflyNavRouteType } from "./AeroflyNavRouteBase.js";
|
|
4
4
|
|
|
@@ -41,11 +41,11 @@ class AeroflyNavRouteTransition extends AeroflyNavRouteBase {
|
|
|
41
41
|
* @returns {number | null} elevation in feet, null if not set
|
|
42
42
|
*/
|
|
43
43
|
get elevation_ft(): number | null {
|
|
44
|
-
return this.elevation !== null ?
|
|
44
|
+
return this.elevation !== null ? convertMeterToFeet(this.elevation) : null;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
set elevation_ft(elevation_ft: number | null) {
|
|
48
|
-
this.elevation = elevation_ft !== null ?
|
|
48
|
+
this.elevation = elevation_ft !== null ? convertFeetToMeter(elevation_ft) : null;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
convertFeetToMeter,
|
|
3
|
+
convertLonLatToVector,
|
|
4
|
+
convertMeterToFeet,
|
|
5
|
+
convertVectorToLonLat,
|
|
6
|
+
type AeroflyVector3Float,
|
|
7
|
+
} from "../node/Convert.js";
|
|
2
8
|
import { AeroflyConfigurationNode } from "../node/AeroflyConfigurationNode.js";
|
|
3
9
|
import { AeroflyNavRouteBase } from "./AeroflyNavRouteBase.js";
|
|
4
10
|
|
|
@@ -8,6 +14,11 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
8
14
|
*/
|
|
9
15
|
navaidFrequency: number | null;
|
|
10
16
|
|
|
17
|
+
/**
|
|
18
|
+
* @property {?bigint} navaidUid if the waypoint is a navaid, its unique identifier, must match Aerofly FS internal UID if used in an existing mission
|
|
19
|
+
*/
|
|
20
|
+
navaidUid: bigint | null;
|
|
21
|
+
|
|
11
22
|
/**
|
|
12
23
|
* @property {number | null} altitude in meter, null if not set
|
|
13
24
|
*/
|
|
@@ -24,6 +35,9 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
24
35
|
* @param {number} latitude WGS84
|
|
25
36
|
* @param {object} [options] additional options for the waypoint
|
|
26
37
|
* @param {?number} [options.navaidFrequency] if the waypoint is a navaid, its frequency in Hz
|
|
38
|
+
* @param {?number} [options.navaidFrequency_khz] if the waypoint is a navaid, its frequency in kHz, will override navaidFrequency in Hz if provided
|
|
39
|
+
* @param {?number} [options.navaidFrequency_mhz] if the waypoint is a navaid, its frequency in MHz, will override navaidFrequency in Hz if provided
|
|
40
|
+
* @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
|
|
27
41
|
* @param {number} [options.altitude] in meter
|
|
28
42
|
* @param {?number} [options.altitude_ft] altitude in feet, will override altitude in meter if provided
|
|
29
43
|
* @param {boolean} [options.flyOver] if true, the waypoint is meant to be flown over, otherwise it can be used as a fly-by waypoint
|
|
@@ -37,6 +51,7 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
37
51
|
navaidFrequency = null,
|
|
38
52
|
navaidFrequency_khz = null,
|
|
39
53
|
navaidFrequency_mhz = null,
|
|
54
|
+
navaidUid = null,
|
|
40
55
|
altitude = null,
|
|
41
56
|
altitude_ft = null,
|
|
42
57
|
flyOver = false,
|
|
@@ -45,6 +60,7 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
45
60
|
) {
|
|
46
61
|
super("waypoint", identifier, longitude, latitude, { uid });
|
|
47
62
|
this.navaidFrequency = navaidFrequency;
|
|
63
|
+
this.navaidUid = navaidUid;
|
|
48
64
|
this.altitude = altitude;
|
|
49
65
|
|
|
50
66
|
if (altitude_ft !== null) {
|
|
@@ -64,11 +80,11 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
64
80
|
* @returns {number | null} altitude in feet, null if not set
|
|
65
81
|
*/
|
|
66
82
|
get altitude_ft(): number | null {
|
|
67
|
-
return this.altitude !== null ?
|
|
83
|
+
return this.altitude !== null ? convertMeterToFeet(this.altitude) : null;
|
|
68
84
|
}
|
|
69
85
|
|
|
70
86
|
set altitude_ft(altitude_ft: number | null) {
|
|
71
|
-
this.altitude = altitude_ft !== null ?
|
|
87
|
+
this.altitude = altitude_ft !== null ? convertFeetToMeter(altitude_ft) : null;
|
|
72
88
|
}
|
|
73
89
|
|
|
74
90
|
get navaidFrequency_khz(): number | null {
|
|
@@ -91,11 +107,11 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
91
107
|
* @returns {AeroflyVector3Float} to use in Aerofly FS4's `main.mcf`
|
|
92
108
|
*/
|
|
93
109
|
get position(): AeroflyVector3Float {
|
|
94
|
-
return
|
|
110
|
+
return convertLonLatToVector(this.longitude, this.latitude, this.altitude || 0);
|
|
95
111
|
}
|
|
96
112
|
|
|
97
113
|
set position(position: AeroflyVector3Float) {
|
|
98
|
-
const { longitude, latitude, altitude_meter } =
|
|
114
|
+
const { longitude, latitude, altitude_meter } = convertVectorToLonLat(position);
|
|
99
115
|
this.longitude = longitude;
|
|
100
116
|
this.latitude = latitude;
|
|
101
117
|
this.altitude = altitude_meter;
|
|
@@ -104,11 +120,9 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
104
120
|
getElement(index: number = 0): AeroflyConfigurationNode {
|
|
105
121
|
const element = super.getElement(index);
|
|
106
122
|
|
|
107
|
-
if (this.navaidFrequency) {
|
|
108
|
-
element.appendChild("float64", "NavaidFrequency", this.navaidFrequency);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
123
|
element
|
|
124
|
+
.appendChild("float64", "NavaidFrequency", this.navaidFrequency ?? 0)
|
|
125
|
+
.appendChild("uint64", "NavaidUid", this.navaidUid ?? BigInt(0))
|
|
112
126
|
.appendChild(
|
|
113
127
|
"vector2_float64",
|
|
114
128
|
"Altitude",
|
|
@@ -128,6 +142,7 @@ export class AeroflyNavRouteWaypoint extends AeroflyNavRouteBase {
|
|
|
128
142
|
altitude_ft: this.altitude_ft,
|
|
129
143
|
navaidFrequency: undefined,
|
|
130
144
|
navaidFrequency_khz: this.navaidFrequency_khz,
|
|
145
|
+
navaidUid: this.navaidUid !== null ? this.navaidUid.toString() : null,
|
|
131
146
|
};
|
|
132
147
|
}
|
|
133
148
|
}
|