@fboes/aerofly-custom-missions 1.0.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/.editorconfig +12 -0
- package/.prettierignore +3 -0
- package/CHANGELOG.md +5 -0
- package/LICENSE.txt +9 -0
- package/README.md +144 -0
- package/dist/index.js +418 -0
- package/dist/index.test.js +107 -0
- package/docs/custom_missions_user.tmc +1287 -0
- package/eslint.config.js +31 -0
- package/package.json +35 -0
- package/src/index.test.ts +127 -0
- package/src/index.ts +613 -0
- package/tsconfig.json +19 -0
- package/types/index.d.ts +340 -0
- package/types/index.d.ts.map +1 -0
- package/types/index.test.d.ts +2 -0
- package/types/index.test.d.ts.map +1 -0
package/.editorconfig
ADDED
package/.prettierignore
ADDED
package/CHANGELOG.md
ADDED
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright © 2024 Frank Boës
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Aerofly FS4 Custom Mission
|
|
2
|
+
|
|
3
|
+
> Builder for Aerofly FS4 Custom Missions Files
|
|
4
|
+
|
|
5
|
+
[Aerofly Flight Simulator 4](https://www.aerofly.com/) has a custom mission file `custom_missions_user.tmc` with a very unique format. To help build this format file, this JavaScript / TypeScript library offers Data Transfers Objects (DTOs) to create this flight plan files programatically.
|
|
6
|
+
|
|
7
|
+
This library is intended to work in modern browsers as well as [Node.js](https://nodejs.org/en).
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Either download the [`dist/index.js`](dist/index.js) to a sensible location in your web project, or do a NPM installation:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @fboes/aerofly-custom-missions --save
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Instead of a local installation you may also load the library from https://unpkg.com/. Beware: This makes https://unpkg.com/ a dependency of your project and may pose data protection issues.
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<script type="module" src="https://unpkg.com/@fboes/aerofly-custom-missions@latest/dist/index.js"></script>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Everything required for the functionality of this library is contained in [`dist/index.js`](dist/index.js).
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Loading the library prior to use:
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
// 1. NodeJS - NPM installation
|
|
31
|
+
import {
|
|
32
|
+
AeroflyMissionsList,
|
|
33
|
+
AeroflyMission,
|
|
34
|
+
AeroflyMissionConditions,
|
|
35
|
+
AeroflyMissionConditionsCloud,
|
|
36
|
+
AeroflyMissionCheckpoint,
|
|
37
|
+
} from "@fboes/aerofly-custom-missions";
|
|
38
|
+
|
|
39
|
+
// 2. Local installation and/or browser usage
|
|
40
|
+
import {
|
|
41
|
+
AeroflyMissionsList,
|
|
42
|
+
AeroflyMission,
|
|
43
|
+
AeroflyMissionConditions,
|
|
44
|
+
AeroflyMissionConditionsCloud,
|
|
45
|
+
AeroflyMissionCheckpoint,
|
|
46
|
+
} from "dist/index.js";
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
You might want to enable TypeScript type checking by adding `// @ts-check` as your first line in your scripts.
|
|
50
|
+
|
|
51
|
+
### Basic idea
|
|
52
|
+
|
|
53
|
+
All objects are basic structures needed for the mission list. The constructors tell you which properties are required, and will start with sensible defaults for all other properties.
|
|
54
|
+
|
|
55
|
+
You can alter the properties of the objects afterwards, or (in some cases) by passing an optional configuration object to the constructor.
|
|
56
|
+
|
|
57
|
+
All objects can be exported as JSON or as string via the `toString()` methods. Exporting the `AeroflyMissionsList` via `toString()` gives you the complete source code for a valid `custom_missions_user.tmc`.
|
|
58
|
+
|
|
59
|
+
### Building a missions file
|
|
60
|
+
|
|
61
|
+
A mission file contains multiple missions. Build this file starts with the outer container, wich contains the missions:
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
// Build a missions list
|
|
65
|
+
const missionList = new AeroflyMissionsList();
|
|
66
|
+
|
|
67
|
+
// You can now add missions to this `missionList`:
|
|
68
|
+
missionList.missions.push(new AeroflyMission("Mission 1"));
|
|
69
|
+
missionList.missions.push(new AeroflyMission("Mission 2"));
|
|
70
|
+
|
|
71
|
+
// Show output of actual missions file
|
|
72
|
+
console.log(missionList.toString());
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Building a mission
|
|
76
|
+
|
|
77
|
+
A mission needs multiple properties:
|
|
78
|
+
|
|
79
|
+
- The aircraft, its position and state
|
|
80
|
+
- The time and weather conditions
|
|
81
|
+
- The actual flight plan
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
// Build time and weather
|
|
85
|
+
const conditions = new AeroflyMissionConditions({
|
|
86
|
+
time: new Date(),
|
|
87
|
+
wind: {
|
|
88
|
+
direction: 190,
|
|
89
|
+
speed: 11,
|
|
90
|
+
gusts: 22,
|
|
91
|
+
},
|
|
92
|
+
visibility: 25000,
|
|
93
|
+
clouds: [new AeroflyMissionConditionsCloud(0.1, 1524), new AeroflyMissionConditionsCloud(0.2, 2286)],
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Build checkpoints
|
|
97
|
+
const checkpoints = [
|
|
98
|
+
new AeroflyMissionCheckpoint("KCCR", "origin", -122.057, 37.9897),
|
|
99
|
+
new AeroflyMissionCheckpoint("19L", "departure_runway", -122.055, 37.993),
|
|
100
|
+
new AeroflyMissionCheckpoint("24", "destination_runway", -70.607, 41.399),
|
|
101
|
+
new AeroflyMissionCheckpoint("KMVY", "destination", -70.6139, 41.3934),
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
// Build mission
|
|
105
|
+
const mission = new AeroflyMission("From Concord to Martha's Vineyard", {
|
|
106
|
+
aircraft: {
|
|
107
|
+
name: "c172",
|
|
108
|
+
livery: "",
|
|
109
|
+
icao: "C172",
|
|
110
|
+
},
|
|
111
|
+
checkpoints,
|
|
112
|
+
conditions,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Build mission list
|
|
116
|
+
const missionList = new AeroflyMissionsList([mission]);
|
|
117
|
+
|
|
118
|
+
// Show output of actual missions file
|
|
119
|
+
console.log(missionList.toString());
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
As there are lots of properties for the flight plan as well as explanation for the properties mentioned above, check the type hinting on the various objects to find out which properties you are able to set.
|
|
123
|
+
|
|
124
|
+
### Important notices
|
|
125
|
+
|
|
126
|
+
- Be aware that `mission.origin` and `mission.destination` do not need to match the flight plan. In case of `origin` you may want to set the position to the actual parking position of your aircraft, which may not be the first way point in your flight plan.
|
|
127
|
+
- Flightplan almost always require at least 4 checkpoint:
|
|
128
|
+
- `origin`
|
|
129
|
+
- `departure_runway`
|
|
130
|
+
- `destination_runway`
|
|
131
|
+
- `destination`
|
|
132
|
+
- Be aware that all untis for altitude, elevation or distance are measured in meters! In most cases there will be helper functions for defining these values in feet or statute miles (for visbility).
|
|
133
|
+
|
|
134
|
+
## Status
|
|
135
|
+
|
|
136
|
+
[](https://badge.fury.io/gh/fboes%2Faerofly-custom-missions)
|
|
137
|
+
[](https://badge.fury.io/js/%40fboes%2Faerofly-custom-missions)
|
|
138
|
+

|
|
139
|
+
|
|
140
|
+
## Legal stuff
|
|
141
|
+
|
|
142
|
+
Author: [Frank Boës](https://3960.org)
|
|
143
|
+
|
|
144
|
+
Copyright & license: See [LICENSE.txt](LICENSE.txt)
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
const feetPerMeter = 3.28084;
|
|
2
|
+
const meterPerStatuteMile = 1609.344;
|
|
3
|
+
/**
|
|
4
|
+
* A list of flight plans.
|
|
5
|
+
*
|
|
6
|
+
* The purpose of this class is to collect data needed for Aerofly FS4's
|
|
7
|
+
* `custom_missions_user.tmc` flight plan file format, and export the structure
|
|
8
|
+
* for this file via the `toString()` method.
|
|
9
|
+
*/
|
|
10
|
+
export class AeroflyMissionsList {
|
|
11
|
+
/**
|
|
12
|
+
* @param missions to add to mission list
|
|
13
|
+
*/
|
|
14
|
+
constructor(missions = []) {
|
|
15
|
+
this.missions = missions;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* @returns String to use in Aerofly FS4's `custom_missions_user.tmc`
|
|
19
|
+
*/
|
|
20
|
+
toString() {
|
|
21
|
+
const separator = "\n// -----------------------------------------------------------------------------\n";
|
|
22
|
+
return `<[file][][]
|
|
23
|
+
<[tmmissions_list][][]
|
|
24
|
+
<[list_tmmission_definition][missions][]${separator + this.missions.join(separator) + separator} >
|
|
25
|
+
>
|
|
26
|
+
>`;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A single flighplan, containing aircraft and weather data as well.
|
|
31
|
+
*
|
|
32
|
+
* The purpose of this class is to collect data needed for Aerofly FS4's
|
|
33
|
+
* `custom_missions_user.tmc` flight plan file format, and export the structure
|
|
34
|
+
* for this file via the `toString()` method.
|
|
35
|
+
*/
|
|
36
|
+
export class AeroflyMission {
|
|
37
|
+
/**
|
|
38
|
+
*
|
|
39
|
+
* @param title of this flight plan
|
|
40
|
+
* @param additionalAttributes allows to set additional attributes on creation
|
|
41
|
+
*/
|
|
42
|
+
constructor(title, additionalAttributes = {}) {
|
|
43
|
+
/**
|
|
44
|
+
* Additional description text, mission briefing, etc
|
|
45
|
+
*/
|
|
46
|
+
this.description = "";
|
|
47
|
+
/**
|
|
48
|
+
* Flight settings of aircraft, like "taxi", "cruise"
|
|
49
|
+
*/
|
|
50
|
+
this.flightSetting = "taxi";
|
|
51
|
+
/**
|
|
52
|
+
* Aircraft for this mission
|
|
53
|
+
*/
|
|
54
|
+
this.aircraft = {
|
|
55
|
+
name: "c172",
|
|
56
|
+
livery: "",
|
|
57
|
+
icao: "",
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Uppercase callsign of aircraft
|
|
61
|
+
*/
|
|
62
|
+
this.callsign = "";
|
|
63
|
+
/**
|
|
64
|
+
* Starting position of aircraft, as well as name of starting airport. Position does not have match airport.
|
|
65
|
+
*/
|
|
66
|
+
this.origin = {
|
|
67
|
+
icao: "",
|
|
68
|
+
longitude: 0,
|
|
69
|
+
latitude: 0,
|
|
70
|
+
dir: 0,
|
|
71
|
+
alt: 0,
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Intended end position of aircraft, as well as name of destination airport. Position does not have match airport.
|
|
75
|
+
*/
|
|
76
|
+
this.destination = {
|
|
77
|
+
icao: "",
|
|
78
|
+
longitude: 0,
|
|
79
|
+
latitude: 0,
|
|
80
|
+
dir: 0,
|
|
81
|
+
alt: 0,
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Time and weather for mission
|
|
85
|
+
*/
|
|
86
|
+
this.conditions = new AeroflyMissionConditions();
|
|
87
|
+
/**
|
|
88
|
+
* The actual flight plan
|
|
89
|
+
*/
|
|
90
|
+
this.checkpoints = [];
|
|
91
|
+
this.title = title;
|
|
92
|
+
this.checkpoints = additionalAttributes.checkpoints ?? this.checkpoints;
|
|
93
|
+
this.description = additionalAttributes.description ?? this.description;
|
|
94
|
+
this.flightSetting = additionalAttributes.flightSetting ?? this.flightSetting;
|
|
95
|
+
this.aircraft = additionalAttributes.aircraft ?? this.aircraft;
|
|
96
|
+
this.callsign = additionalAttributes.callsign ?? this.callsign;
|
|
97
|
+
this.origin = additionalAttributes.origin ?? this.origin;
|
|
98
|
+
this.destination = additionalAttributes.destination ?? this.destination;
|
|
99
|
+
this.conditions = additionalAttributes.conditions ?? this.conditions;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* @returns indexed checkpoints
|
|
103
|
+
*/
|
|
104
|
+
getCheckpointsString() {
|
|
105
|
+
return this.checkpoints
|
|
106
|
+
.map((c, index) => {
|
|
107
|
+
return c.toString(index);
|
|
108
|
+
})
|
|
109
|
+
.join("\n");
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* @throws {Error} on missing waypoints
|
|
113
|
+
* @returns String to use in Aerofly FS4's `custom_missions_user.tmc`
|
|
114
|
+
*/
|
|
115
|
+
toString() {
|
|
116
|
+
if (this.checkpoints.length < 2) {
|
|
117
|
+
throw Error("this.checkpoints.length < 2");
|
|
118
|
+
}
|
|
119
|
+
if (!this.origin.icao) {
|
|
120
|
+
const firstCheckpoint = this.checkpoints[0];
|
|
121
|
+
this.origin = {
|
|
122
|
+
icao: firstCheckpoint.name,
|
|
123
|
+
longitude: firstCheckpoint.longitude,
|
|
124
|
+
latitude: firstCheckpoint.latitude,
|
|
125
|
+
dir: this.origin.dir,
|
|
126
|
+
alt: firstCheckpoint.altitude,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
if (!this.destination.icao) {
|
|
130
|
+
const lastCheckpoint = this.checkpoints[this.checkpoints.length - 1];
|
|
131
|
+
this.destination = {
|
|
132
|
+
icao: lastCheckpoint.name,
|
|
133
|
+
longitude: lastCheckpoint.longitude,
|
|
134
|
+
latitude: lastCheckpoint.latitude,
|
|
135
|
+
dir: lastCheckpoint.direction ?? 0,
|
|
136
|
+
alt: lastCheckpoint.altitude,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return ` <[tmmission_definition][mission][]
|
|
140
|
+
<[string8][title][${this.title}]>
|
|
141
|
+
<[string8][description][${this.description}]>
|
|
142
|
+
<[string8] [flight_setting] [${this.flightSetting}]>
|
|
143
|
+
<[string8u] [aircraft_name] [${this.aircraft.name}]>
|
|
144
|
+
//<[string8u][aircraft_livery] [${this.aircraft.livery}]>
|
|
145
|
+
<[stringt8c] [aircraft_icao] [${this.aircraft.icao}]>
|
|
146
|
+
<[stringt8c] [callsign] [${this.callsign}]>
|
|
147
|
+
<[stringt8c] [origin_icao] [${this.origin.icao}]>
|
|
148
|
+
<[tmvector2d][origin_lon_lat] [${this.origin.longitude} ${this.origin.latitude}]>
|
|
149
|
+
<[float64] [origin_dir] [${this.origin.dir}]>
|
|
150
|
+
<[float64] [origin_alt] [${this.origin.alt}]> // ${this.origin.alt * feetPerMeter} ft MSL
|
|
151
|
+
<[stringt8c] [destination_icao] [${this.destination.icao}]>
|
|
152
|
+
<[tmvector2d][destination_lon_lat][${this.destination.longitude} ${this.destination.latitude}]>
|
|
153
|
+
<[float64] [destination_dir] [${this.destination.dir}]>
|
|
154
|
+
${this.conditions}
|
|
155
|
+
<[list_tmmission_checkpoint][checkpoints][]
|
|
156
|
+
${this.getCheckpointsString()}
|
|
157
|
+
>
|
|
158
|
+
>`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Time and weather data for the given flight plan
|
|
163
|
+
*
|
|
164
|
+
* The purpose of this class is to collect data needed for Aerofly FS4's
|
|
165
|
+
* `custom_missions_user.tmc` flight plan file format, and export the structure
|
|
166
|
+
* for this file via the `toString()` method.
|
|
167
|
+
*/
|
|
168
|
+
export class AeroflyMissionConditions {
|
|
169
|
+
/**
|
|
170
|
+
* @param additionalAttributes allows to set additional attributes on creation
|
|
171
|
+
*/
|
|
172
|
+
constructor(additionalAttributes = {}) {
|
|
173
|
+
/**
|
|
174
|
+
* Start time of flight plan. Relevant is the UTC part, so
|
|
175
|
+
* consider setting this date in UTC.
|
|
176
|
+
*/
|
|
177
|
+
this.time = new Date();
|
|
178
|
+
/**
|
|
179
|
+
* Current wind state
|
|
180
|
+
*/
|
|
181
|
+
this.wind = {
|
|
182
|
+
direction: 0,
|
|
183
|
+
speed: 0,
|
|
184
|
+
gusts: 0,
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* 0..1, percentage
|
|
188
|
+
*/
|
|
189
|
+
this.turbulenceStrength = 0;
|
|
190
|
+
/**
|
|
191
|
+
* 0..1, percentage
|
|
192
|
+
*/
|
|
193
|
+
this.thermalStrength = 0;
|
|
194
|
+
/**
|
|
195
|
+
* Visibility in meters
|
|
196
|
+
*/
|
|
197
|
+
this.visibility = 25000;
|
|
198
|
+
this.clouds = [];
|
|
199
|
+
this.time = additionalAttributes.time ?? this.time;
|
|
200
|
+
this.wind.direction = additionalAttributes.wind?.direction ?? this.wind.direction;
|
|
201
|
+
this.wind.speed = additionalAttributes.wind?.speed ?? this.wind.speed;
|
|
202
|
+
this.wind.gusts = additionalAttributes.wind?.gusts ?? this.wind.gusts;
|
|
203
|
+
this.turbulenceStrength = additionalAttributes.turbulenceStrength ?? this.turbulenceStrength;
|
|
204
|
+
this.visibility = additionalAttributes.visibility ?? this.visibility;
|
|
205
|
+
this.clouds = additionalAttributes.clouds ?? this.clouds;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* @returns the Aerofly value for UTC hours + minutes/60 + seconds/3600. Ignores milliseconds ;)
|
|
209
|
+
*/
|
|
210
|
+
get time_hours() {
|
|
211
|
+
return this.time.getUTCHours() + this.time.getUTCMinutes() / 60 + this.time.getUTCSeconds() / 3600;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* @returns Time representation like "20:15:00"
|
|
215
|
+
*/
|
|
216
|
+
get time_presentational() {
|
|
217
|
+
return [this.time.getUTCHours(), this.time.getUTCMinutes(), this.time.getUTCSeconds()]
|
|
218
|
+
.map((t) => {
|
|
219
|
+
return String(t).padStart(2, "0");
|
|
220
|
+
})
|
|
221
|
+
.join(":");
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* @param visibility_sm `this.visibility` in statute miles instead of meters
|
|
225
|
+
*/
|
|
226
|
+
set visibility_sm(visibility_sm) {
|
|
227
|
+
this.visibility = visibility_sm * meterPerStatuteMile;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* @returns
|
|
231
|
+
*/
|
|
232
|
+
getCloudsString() {
|
|
233
|
+
return this.clouds
|
|
234
|
+
.map((c, index) => {
|
|
235
|
+
return c.toString(index);
|
|
236
|
+
})
|
|
237
|
+
.join("\n");
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* @returns String to use in Aerofly FS4's `custom_missions_user.tmc`
|
|
241
|
+
*/
|
|
242
|
+
toString() {
|
|
243
|
+
if (this.clouds.length < 1) {
|
|
244
|
+
this.clouds = [new AeroflyMissionConditionsCloud(0, 0)];
|
|
245
|
+
}
|
|
246
|
+
return ` <[tmmission_conditions][conditions][]
|
|
247
|
+
<[tm_time_utc][time][]
|
|
248
|
+
<[int32][time_year][${this.time.getUTCFullYear()}]>
|
|
249
|
+
<[int32][time_month][${this.time.getUTCMonth() + 1}]>
|
|
250
|
+
<[int32][time_day][${this.time.getUTCDate()}]>
|
|
251
|
+
<[float64][time_hours][${this.time_hours}]> // ${this.time_presentational} UTC
|
|
252
|
+
>
|
|
253
|
+
<[float64][wind_direction][${this.wind.direction}]>
|
|
254
|
+
<[float64][wind_speed][${this.wind.speed}]> // kts
|
|
255
|
+
<[float64][wind_gusts][${this.wind.gusts}]> // kts
|
|
256
|
+
<[float64][turbulence_strength][${this.turbulenceStrength}]>
|
|
257
|
+
<[float64][thermal_strength][${this.thermalStrength}]>
|
|
258
|
+
<[float64][visibility][${this.visibility}]> // ${this.visibility / meterPerStatuteMile} SM
|
|
259
|
+
${this.getCloudsString()}
|
|
260
|
+
>`;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* A cloud layer for the current flight plan's weather data
|
|
265
|
+
*
|
|
266
|
+
* The purpose of this class is to collect data needed for Aerofly FS4's
|
|
267
|
+
* `custom_missions_user.tmc` flight plan file format, and export the structure
|
|
268
|
+
* for this file via the `toString()` method.
|
|
269
|
+
*/
|
|
270
|
+
export class AeroflyMissionConditionsCloud {
|
|
271
|
+
/**
|
|
272
|
+
* @param cover 0..1, percentage
|
|
273
|
+
* @param base in meters AGL
|
|
274
|
+
*/
|
|
275
|
+
constructor(cover, base) {
|
|
276
|
+
this.cover = cover;
|
|
277
|
+
this.base = base;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* @param cover 0..1, percentage
|
|
281
|
+
* @param base_feet base, but in feet AGL instead of meters AGL
|
|
282
|
+
* @returns {AeroflyMissionConditionsCloud}
|
|
283
|
+
*/
|
|
284
|
+
static createInFeet(cover, base_feet) {
|
|
285
|
+
return new AeroflyMissionConditionsCloud(cover, base_feet / feetPerMeter);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* @param base_feet `this.base` in feet instead of meters
|
|
289
|
+
*/
|
|
290
|
+
set base_feet(base_feet) {
|
|
291
|
+
this.base = base_feet / feetPerMeter;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* @returns Cloud coverage as text representation like "OVC" for `this.cover`
|
|
295
|
+
*/
|
|
296
|
+
get cover_code() {
|
|
297
|
+
if (this.cover < 1 / 8) {
|
|
298
|
+
return "CLR";
|
|
299
|
+
}
|
|
300
|
+
else if (this.cover <= 2 / 8) {
|
|
301
|
+
return "FEW";
|
|
302
|
+
}
|
|
303
|
+
else if (this.cover <= 4 / 8) {
|
|
304
|
+
return "SCT";
|
|
305
|
+
}
|
|
306
|
+
else if (this.cover <= 7 / 8) {
|
|
307
|
+
return "BKN";
|
|
308
|
+
}
|
|
309
|
+
return "OVC";
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* @param index if used in an array will se the array index
|
|
313
|
+
* @returns String to use in Aerofly FS4's `custom_missions_user.tmc`
|
|
314
|
+
*/
|
|
315
|
+
toString(index = 0) {
|
|
316
|
+
const indexString = index === 0 ? "" : String(index + 1);
|
|
317
|
+
const comment = index === 0 ? "" : "//";
|
|
318
|
+
return ` ${comment}<[float64][cloud_cover${indexString}][${this.cover ?? 0}]> // ${this.cover_code}
|
|
319
|
+
${comment}<[float64][cloud_base${indexString}][${this.base}]> // ${this.base * feetPerMeter} ft AGL`;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* A single way point for the given flight plan
|
|
324
|
+
*
|
|
325
|
+
* The purpose of this class is to collect data needed for Aerofly FS4's
|
|
326
|
+
* `custom_missions_user.tmc` flight plan file format, and export the structure
|
|
327
|
+
* for this file via the `toString()` method.
|
|
328
|
+
*/
|
|
329
|
+
export class AeroflyMissionCheckpoint {
|
|
330
|
+
/**
|
|
331
|
+
* @param name ICAO code for airport, runway designator, navaid
|
|
332
|
+
* designator, fix name, or custom name
|
|
333
|
+
* @param type Type of checkpoint, like "departure_runway"
|
|
334
|
+
* @param longitude easting, using the World Geodetic
|
|
335
|
+
* System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
|
|
336
|
+
* of decimal degrees; -180..180
|
|
337
|
+
* @param latitude northing, using the World Geodetic
|
|
338
|
+
* System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
|
|
339
|
+
* of decimal degrees; -90..90
|
|
340
|
+
* @param additionalAttributes allows to set additional attributes on creation
|
|
341
|
+
*/
|
|
342
|
+
constructor(name, type, longitude, latitude, additionalAttributes = {}) {
|
|
343
|
+
/**
|
|
344
|
+
* The height in meters above or below the WGS
|
|
345
|
+
* 84 reference ellipsoid
|
|
346
|
+
*/
|
|
347
|
+
this.altitude = 0;
|
|
348
|
+
/**
|
|
349
|
+
* For runways: in degree
|
|
350
|
+
*/
|
|
351
|
+
this.direction = null;
|
|
352
|
+
/**
|
|
353
|
+
* For runways
|
|
354
|
+
*/
|
|
355
|
+
this.slope = null;
|
|
356
|
+
/**
|
|
357
|
+
* For runways: in meters
|
|
358
|
+
*/
|
|
359
|
+
this.length = null;
|
|
360
|
+
/**
|
|
361
|
+
* For runways and navigational aids, in Hz; multiply by 1000 for kHz, 1_000_000 for MHz
|
|
362
|
+
*/
|
|
363
|
+
this.frequency = null;
|
|
364
|
+
this.type = type;
|
|
365
|
+
this.name = name;
|
|
366
|
+
this.longitude = longitude;
|
|
367
|
+
this.latitude = latitude;
|
|
368
|
+
if (additionalAttributes.altitude_feet) {
|
|
369
|
+
additionalAttributes.altitude = additionalAttributes.altitude_feet / feetPerMeter;
|
|
370
|
+
}
|
|
371
|
+
this.altitude = additionalAttributes.altitude ?? this.altitude;
|
|
372
|
+
this.direction = additionalAttributes.direction ?? this.direction;
|
|
373
|
+
this.slope = additionalAttributes.slope ?? this.slope;
|
|
374
|
+
this.length = additionalAttributes.length ?? this.length;
|
|
375
|
+
this.frequency = additionalAttributes.frequency ?? this.frequency;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* @param altitude_feet
|
|
379
|
+
*/
|
|
380
|
+
set altitude_feet(altitude_feet) {
|
|
381
|
+
this.altitude = altitude_feet / feetPerMeter;
|
|
382
|
+
}
|
|
383
|
+
get frequency_string() {
|
|
384
|
+
if (!this.frequency) {
|
|
385
|
+
return "None";
|
|
386
|
+
}
|
|
387
|
+
if (this.frequency > 1000000) {
|
|
388
|
+
return String(this.frequency / 1000000) + " MHz";
|
|
389
|
+
}
|
|
390
|
+
if (this.frequency > 1000) {
|
|
391
|
+
return String(this.frequency / 1000) + " kHz";
|
|
392
|
+
}
|
|
393
|
+
return String(this.frequency) + " Hz";
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* @param index if used in an array will se the array index
|
|
397
|
+
* @returns String to use in Aerofly FS4's `custom_missions_user.tmc`
|
|
398
|
+
*/
|
|
399
|
+
toString(index = 0) {
|
|
400
|
+
return ` <[tmmission_checkpoint][element][${index}]
|
|
401
|
+
<[string8u][type][${this.type}]>
|
|
402
|
+
<[string8u][name][${this.name}]>
|
|
403
|
+
<[vector2_float64][lon_lat][${this.longitude} ${this.latitude}]>
|
|
404
|
+
<[float64][altitude][${this.altitude}]> // ${this.altitude * feetPerMeter} ft
|
|
405
|
+
<[float64][direction][${this.direction ?? (index === 0 ? -1 : 0)}]>
|
|
406
|
+
<[float64][slope][${this.slope ?? 0}]>
|
|
407
|
+
<[float64][length][${this.length ?? 0}]> // ${(this.length ?? 0) * feetPerMeter} ft
|
|
408
|
+
<[float64][frequency][${this.frequency ?? 0}]> // ${this.frequency_string}
|
|
409
|
+
>`;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
export default {
|
|
413
|
+
AeroflyMissionsList,
|
|
414
|
+
AeroflyMission,
|
|
415
|
+
AeroflyMissionConditions,
|
|
416
|
+
AeroflyMissionConditionsCloud,
|
|
417
|
+
AeroflyMissionCheckpoint,
|
|
418
|
+
};
|