@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.
Files changed (42) hide show
  1. package/CHANGELOG.md +6 -1
  2. package/dist/dto-flight/AeroflyNavRouteAirports.js +3 -3
  3. package/dist/dto-flight/AeroflyNavRouteBase.js +60 -9
  4. package/dist/dto-flight/AeroflyNavRouteBase.test.js +26 -0
  5. package/dist/dto-flight/AeroflyNavRouteRunway.js +5 -5
  6. package/dist/dto-flight/AeroflyNavRouteTransition.js +3 -3
  7. package/dist/dto-flight/AeroflyNavRouteWaypoint.js +17 -9
  8. package/dist/dto-flight/AeroflyNavigationConfig.js +4 -4
  9. package/dist/dto-flight/AeroflySettingsFlight.js +8 -8
  10. package/dist/dto-flight/AeroflySettingsFuelLoad.js +6 -6
  11. package/dist/node/Convert.js +107 -72
  12. package/dist/node/Convert.test.js +12 -12
  13. package/docs/flight.json +12 -9
  14. package/docs/flight.mcf +13 -9
  15. package/docs/flight.xml +13 -9
  16. package/package.json +1 -1
  17. package/src/dto-flight/AeroflyNavRouteAirports.ts +3 -3
  18. package/src/dto-flight/AeroflyNavRouteBase.test.ts +30 -0
  19. package/src/dto-flight/AeroflyNavRouteBase.ts +71 -10
  20. package/src/dto-flight/AeroflyNavRouteRunway.ts +5 -5
  21. package/src/dto-flight/AeroflyNavRouteTransition.ts +3 -3
  22. package/src/dto-flight/AeroflyNavRouteWaypoint.ts +24 -9
  23. package/src/dto-flight/AeroflyNavigationConfig.ts +4 -4
  24. package/src/dto-flight/AeroflySettingsFlight.ts +17 -8
  25. package/src/dto-flight/AeroflySettingsFuelLoad.ts +11 -7
  26. package/src/node/Convert.test.ts +21 -13
  27. package/src/node/Convert.ts +120 -81
  28. package/types/dto-flight/AeroflyNavRouteBase.d.ts +24 -2
  29. package/types/dto-flight/AeroflyNavRouteBase.d.ts.map +1 -1
  30. package/types/dto-flight/AeroflyNavRouteBase.test.d.ts +2 -0
  31. package/types/dto-flight/AeroflyNavRouteBase.test.d.ts.map +1 -0
  32. package/types/dto-flight/AeroflyNavRouteRunway.d.ts.map +1 -1
  33. package/types/dto-flight/AeroflyNavRouteTransition.d.ts.map +1 -1
  34. package/types/dto-flight/AeroflyNavRouteWaypoint.d.ts +8 -0
  35. package/types/dto-flight/AeroflyNavRouteWaypoint.d.ts.map +1 -1
  36. package/types/dto-flight/AeroflySettingsFlight.d.ts +1 -1
  37. package/types/dto-flight/AeroflySettingsFlight.d.ts.map +1 -1
  38. package/types/dto-flight/AeroflySettingsFuelLoad.d.ts +1 -1
  39. package/types/dto-flight/AeroflySettingsFuelLoad.d.ts.map +1 -1
  40. package/types/node/Convert.d.ts +52 -14
  41. package/types/node/Convert.d.ts.map +1 -1
  42. 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.12.0] - 2026-07-08
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 { Convert } from "../node/Convert.js";
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 ? Convert.convertMeterToFeet(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 ? Convert.convertFeetToMeter(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 { Convert } from "../node/Convert.js";
2
- import { AeroflyConfigurationNode, AeroflyConfigurationNodeComment } from "../node/AeroflyConfigurationNode.js";
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, can be null for new waypoints
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, can be null for new waypoints
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 Convert.convertLonLatToVector(this.longitude, this.latitude, 0);
43
+ return convertLonLatToVector(this.longitude, this.latitude, 0);
44
44
  }
45
45
  set position(position) {
46
- const latLonAlt = Convert.convertVectorToLonLat(position);
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
- .append(this.uid
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 { Convert } from "../node/Convert.js";
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 ? Convert.convertMeterToFeet(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 ? Convert.convertFeetToMeter(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 ? Convert.convertMeterToFeet(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 ? Convert.convertFeetToMeter(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 { Convert } from "../node/Convert.js";
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 ? Convert.convertMeterToFeet(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 ? Convert.convertFeetToMeter(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 { Convert } from "../node/Convert.js";
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 ? Convert.convertMeterToFeet(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 ? Convert.convertFeetToMeter(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 Convert.convertLonLatToVector(this.longitude, this.latitude, this.altitude || 0);
82
+ return convertLonLatToVector(this.longitude, this.latitude, this.altitude || 0);
75
83
  }
76
84
  set position(position) {
77
- const { longitude, latitude, altitude_meter } = Convert.convertVectorToLonLat(position);
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 { Convert } from "../node/Convert.js";
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(Convert.convertFeetToMeter(cruiseAltitude_ft), waypoints, _cruiseSpeed_kts);
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 Convert.convertMeterToFeet(this.cruiseAltitude);
40
+ return convertMeterToFeet(this.cruiseAltitude);
41
41
  }
42
42
  set cruiseAltitude_ft(cruiseAltitude_ft) {
43
- this.cruiseAltitude = Convert.convertFeetToMeter(cruiseAltitude_ft);
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, Convert.convertFeetToMeter(altitude_ft), heading_degree, speed_kts, additionalAttributes);
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 Convert.convertLonLatToVector(this.longitude, this.latitude, this.altitude_meter);
98
+ return convertLonLatToVector(this.longitude, this.latitude, this.altitude_meter);
99
99
  }
100
100
  set position(position) {
101
- const { longitude, latitude, altitude_meter } = Convert.convertVectorToLonLat(position);
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 Convert.convertDegreeToMatrix(this.heading_degree);
119
+ return convertDegreeToMatrix(this.heading_degree);
120
120
  }
121
121
  set orientation(orientation) {
122
- this.heading_degree = Convert.convertMatrixToDegree(orientation);
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 Convert.convertMeterToFeet(this.altitude_meter);
128
+ return convertMeterToFeet(this.altitude_meter);
129
129
  }
130
130
  set altitude_ft(altitude_ft) {
131
- this.altitude_meter = Convert.convertFeetToMeter(altitude_ft);
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 { Convert } from "../node/Convert.js";
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 Convert.convertKgToLb(this.fuelMass);
37
+ return convertKgToLb(this.fuelMass);
38
38
  }
39
39
  set fuelMass_lb(fuelMass_lb) {
40
- this.fuelMass = Convert.convertLbToKg(fuelMass_lb);
40
+ this.fuelMass = convertLbToKg(fuelMass_lb);
41
41
  }
42
42
  get payloadMass_lb() {
43
- return Convert.convertKgToLb(this.payloadMass);
43
+ return convertKgToLb(this.payloadMass);
44
44
  }
45
45
  set payloadMass_lb(payloadMass_lb) {
46
- this.payloadMass = Convert.convertLbToKg(payloadMass_lb);
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 ? "Keep" : this.configuration)
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);
@@ -1,80 +1,115 @@
1
- export class Convert {
2
- static convertLonLatToVector(longitude, latitude, altitude_meter) {
3
- const a = 6378137.0;
4
- const f = 1.0 / 298.257223563;
5
- const e2 = f * (2 - f);
6
- const lon = longitude * (Math.PI / 180); // in radians
7
- const lat = latitude * (Math.PI / 180); // in radians
8
- const h = altitude_meter;
9
- const sinLat = Math.sin(lat);
10
- const cosLat = Math.cos(lat);
11
- const cosLon = Math.cos(lon);
12
- const sinLon = Math.sin(lon);
13
- const N = a / Math.sqrt(1 - e2 * sinLat * sinLat);
14
- const x = (N + h) * cosLat * cosLon;
15
- const y = (N + h) * cosLat * sinLon;
16
- const z = (N * (1 - e2) + h) * sinLat;
17
- return [x, y, z];
18
- }
19
- static convertVectorToLonLat(coordinates) {
20
- // TODO: This implementation is not correct
21
- const f = 1.0 / 298.257223563; // WGS84
22
- const e2 = 2 * f - f * f;
23
- //const lambda = VectorToAngle( coordinates[0], coordinates[1] );
24
- let lambda = 0;
25
- if (coordinates[0] > 0) {
26
- if (coordinates[1] < 0) {
27
- lambda = 2 * Math.PI + Math.atan(coordinates[1] / coordinates[0]);
28
- }
29
- else {
30
- lambda = Math.atan(coordinates[1] / coordinates[0]);
31
- }
32
- }
33
- else if (coordinates[0] < 0) {
34
- lambda = Math.PI + Math.atan(coordinates[1] / coordinates[0]);
35
- }
36
- else if (coordinates[1] > 0) {
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.5 * Math.PI;
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
- static convertFeetToMeter(feet) {
72
- return feet / 3.28084;
42
+ else if (coordinates[0] < 0) {
43
+ lambda = Math.PI + Math.atan(coordinates[1] / coordinates[0]);
73
44
  }
74
- static convertKgToLb(kg) {
75
- return kg;
45
+ else if (coordinates[1] > 0) {
46
+ lambda = 0.5 * Math.PI;
76
47
  }
77
- static convertLbToKg(lb) {
78
- return lb;
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
  }