@lumikmz/kmz 0.2.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/LICENSE +21 -0
- package/README.md +540 -0
- package/dist/index.d.mts +789 -0
- package/dist/index.mjs +779 -0
- package/package.json +24 -0
- package/src/index.ts +3 -0
- package/src/plan/geometry.ts +109 -0
- package/src/plan/index.ts +31 -0
- package/src/plan/plan-mapping-strip.ts +84 -0
- package/src/plan/plan-mapping2d.ts +128 -0
- package/src/plan/plan-mapping3d.ts +188 -0
- package/src/plan/plan-waypoint.ts +80 -0
- package/src/plan/plan.test.ts +96 -0
- package/src/types/action.ts +253 -0
- package/src/types/branded.ts +20 -0
- package/src/types/drone-payload.ts +59 -0
- package/src/types/enums.ts +97 -0
- package/src/types/errors.ts +19 -0
- package/src/types/index.ts +10 -0
- package/src/types/mission-config.ts +58 -0
- package/src/types/placemark.ts +96 -0
- package/src/types/template.ts +170 -0
- package/src/types/waylines.ts +32 -0
- package/src/types/waypoint-params.ts +78 -0
- package/src/validate/index.ts +25 -0
- package/src/validate/validate-mission-config.ts +48 -0
- package/src/validate/validate-template.ts +101 -0
- package/src/validate/validate-waylines.ts +59 -0
- package/tsconfig.json +4 -0
- package/vite.config.ts +9 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { degrees, meters, metersPerSecond } from "../types/branded.js";
|
|
3
|
+
import { DroneEnum, PayloadEnum } from "../types/drone-payload.js";
|
|
4
|
+
import type { Mapping2dTemplate, WaypointTemplate } from "../types/template.js";
|
|
5
|
+
import { plan } from "./index.js";
|
|
6
|
+
|
|
7
|
+
const missionConfig = {
|
|
8
|
+
flyToWaylineMode: "safely" as const,
|
|
9
|
+
finishAction: "goHome" as const,
|
|
10
|
+
exitOnRCLost: "executeLostAction" as const,
|
|
11
|
+
executeRCLostAction: "goBack" as const,
|
|
12
|
+
takeOffSecurityHeight: meters(20),
|
|
13
|
+
globalTransitionalSpeed: metersPerSecond(10),
|
|
14
|
+
globalRTHHeight: meters(100),
|
|
15
|
+
droneInfo: { ...DroneEnum.M3TD },
|
|
16
|
+
payloadInfo: [{ ...PayloadEnum.M3TDCamera }],
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
describe("plan", () => {
|
|
20
|
+
it("compiles a waypoint template into a single-folder wayline", () => {
|
|
21
|
+
const template: WaypointTemplate = {
|
|
22
|
+
missionConfig,
|
|
23
|
+
Folder: {
|
|
24
|
+
templateType: "waypoint",
|
|
25
|
+
templateId: 0,
|
|
26
|
+
waylineCoordinateSysParam: { coordinateMode: "WGS84", heightMode: "relativeToStartPoint" },
|
|
27
|
+
autoFlightSpeed: metersPerSecond(10),
|
|
28
|
+
globalWaypointTurnMode: "toPointAndStopWithDiscontinuityCurvature",
|
|
29
|
+
gimbalPitchMode: "manual",
|
|
30
|
+
globalHeight: meters(50),
|
|
31
|
+
globalWaypointHeadingParam: {
|
|
32
|
+
waypointHeadingMode: "followWayline",
|
|
33
|
+
waypointHeadingPathMode: "followBadArc",
|
|
34
|
+
},
|
|
35
|
+
Placemark: [
|
|
36
|
+
{
|
|
37
|
+
Point: { coordinates: { lng: 113.938, lat: 22.5397 } },
|
|
38
|
+
index: 0,
|
|
39
|
+
useGlobalHeight: true,
|
|
40
|
+
useGlobalSpeed: true,
|
|
41
|
+
useGlobalHeadingParam: true,
|
|
42
|
+
useGlobalTurnParam: true,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
Point: { coordinates: { lng: 113.939, lat: 22.5407 } },
|
|
46
|
+
index: 1,
|
|
47
|
+
useGlobalHeight: true,
|
|
48
|
+
useGlobalSpeed: true,
|
|
49
|
+
useGlobalHeadingParam: true,
|
|
50
|
+
useGlobalTurnParam: true,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const waylines = plan(template);
|
|
57
|
+
expect(waylines.Folder).toHaveLength(1);
|
|
58
|
+
expect(waylines.Folder[0]!.Placemark).toHaveLength(2);
|
|
59
|
+
expect(waylines.Folder[0]!.executeHeightMode).toBe("relativeToStartPoint");
|
|
60
|
+
expect(waylines.Folder[0]!.Placemark[0]!.executeHeight).toBe(50);
|
|
61
|
+
expect(waylines.missionConfig).toBe(template.missionConfig);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("compiles a mapping2d template into a serpentine scan", () => {
|
|
65
|
+
const template: Mapping2dTemplate = {
|
|
66
|
+
missionConfig,
|
|
67
|
+
Folder: {
|
|
68
|
+
templateType: "mapping2d",
|
|
69
|
+
templateId: 1,
|
|
70
|
+
waylineCoordinateSysParam: { coordinateMode: "WGS84", heightMode: "relativeToStartPoint" },
|
|
71
|
+
autoFlightSpeed: metersPerSecond(10),
|
|
72
|
+
elevationOptimizeEnable: false,
|
|
73
|
+
shootType: "distance",
|
|
74
|
+
direction: degrees(0),
|
|
75
|
+
margin: 0,
|
|
76
|
+
overlap: { orthoCameraOverlapH: 80, orthoCameraOverlapW: 70 },
|
|
77
|
+
ellipsoidHeight: meters(150),
|
|
78
|
+
height: meters(100),
|
|
79
|
+
Placemark: {
|
|
80
|
+
Polygon: {
|
|
81
|
+
outerBoundaryIs: {
|
|
82
|
+
LinearRing: {
|
|
83
|
+
coordinates:
|
|
84
|
+
"113.938,22.5397,0 113.940,22.5397,0 113.940,22.5407,0 113.938,22.5407,0 113.938,22.5397,0",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const waylines = plan(template);
|
|
93
|
+
expect(waylines.Folder).toHaveLength(1);
|
|
94
|
+
expect(waylines.Folder[0]!.Placemark.length).toBeGreaterThan(0);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import type { Degrees } from "./branded.js";
|
|
2
|
+
import type {
|
|
3
|
+
ActionGroupMode,
|
|
4
|
+
ActionTriggerType,
|
|
5
|
+
AircraftPathMode,
|
|
6
|
+
GimbalHeadingYawBase,
|
|
7
|
+
GimbalRotateMode,
|
|
8
|
+
LensType,
|
|
9
|
+
OrientedPhotoMode,
|
|
10
|
+
PanoShotSubMode,
|
|
11
|
+
RecordPointCloudOperate,
|
|
12
|
+
} from "./enums.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `wpml:actionGroup` — see `docs/kmz/common-element.html` §actionGroup.
|
|
16
|
+
*
|
|
17
|
+
* - `actionGroupId` is unique within the entire KMZ (monotonically from 0).
|
|
18
|
+
* - `actionId` is scoped within its group.
|
|
19
|
+
*/
|
|
20
|
+
export interface ActionGroup {
|
|
21
|
+
actionGroupId: number;
|
|
22
|
+
actionGroupStartIndex: number;
|
|
23
|
+
actionGroupEndIndex: number;
|
|
24
|
+
actionGroupMode: ActionGroupMode;
|
|
25
|
+
actionTrigger: ActionTrigger;
|
|
26
|
+
action: Action[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ActionTrigger {
|
|
30
|
+
actionTriggerType: ActionTriggerType;
|
|
31
|
+
/** Seconds for `multipleTiming`, meters for `multipleDistance`. */
|
|
32
|
+
actionTriggerParam?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Discriminated by `actionActuatorFunc`. */
|
|
36
|
+
export type Action =
|
|
37
|
+
| TakePhotoAction
|
|
38
|
+
| StartRecordAction
|
|
39
|
+
| StopRecordAction
|
|
40
|
+
| FocusAction
|
|
41
|
+
| ZoomAction
|
|
42
|
+
| CustomDirNameAction
|
|
43
|
+
| GimbalRotateAction
|
|
44
|
+
| RotateYawAction
|
|
45
|
+
| HoverAction
|
|
46
|
+
| GimbalEvenlyRotateAction
|
|
47
|
+
| AccurateShootAction
|
|
48
|
+
| OrientedShootAction
|
|
49
|
+
| PanoShotAction
|
|
50
|
+
| RecordPointCloudAction
|
|
51
|
+
| MegaphoneAction
|
|
52
|
+
| SearchlightAction;
|
|
53
|
+
|
|
54
|
+
interface ActionBase<F extends string, P> {
|
|
55
|
+
actionId: number;
|
|
56
|
+
actionActuatorFunc: F;
|
|
57
|
+
actionActuatorFuncParam: P;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ─── Action params (one type per actionActuatorFunc) ─────────────────────────
|
|
61
|
+
|
|
62
|
+
export interface TakePhotoParam {
|
|
63
|
+
payloadPositionIndex: number;
|
|
64
|
+
fileSuffix?: string;
|
|
65
|
+
payloadLensIndex?: LensType[];
|
|
66
|
+
useGlobalPayloadLensIndex: boolean;
|
|
67
|
+
}
|
|
68
|
+
export type TakePhotoAction = ActionBase<"takePhoto", TakePhotoParam>;
|
|
69
|
+
|
|
70
|
+
export interface StartRecordParam {
|
|
71
|
+
payloadPositionIndex: number;
|
|
72
|
+
fileSuffix?: string;
|
|
73
|
+
payloadLensIndex?: LensType[];
|
|
74
|
+
useGlobalPayloadLensIndex: boolean;
|
|
75
|
+
}
|
|
76
|
+
export type StartRecordAction = ActionBase<"startRecord", StartRecordParam>;
|
|
77
|
+
|
|
78
|
+
export interface StopRecordParam {
|
|
79
|
+
payloadPositionIndex: number;
|
|
80
|
+
payloadLensIndex?: LensType[];
|
|
81
|
+
}
|
|
82
|
+
export type StopRecordAction = ActionBase<"stopRecord", StopRecordParam>;
|
|
83
|
+
|
|
84
|
+
export interface FocusParam {
|
|
85
|
+
payloadPositionIndex: number;
|
|
86
|
+
isPointFocus: boolean;
|
|
87
|
+
focusX: number;
|
|
88
|
+
focusY: number;
|
|
89
|
+
/** Required iff `isPointFocus === false`. */
|
|
90
|
+
focusRegionWidth?: number;
|
|
91
|
+
/** Required iff `isPointFocus === false`. */
|
|
92
|
+
focusRegionHeight?: number;
|
|
93
|
+
/** Required on M3E/M3T/M3M, M3D/M3TD, M4D/M4TD, M4E/M4T. */
|
|
94
|
+
isInfiniteFocus?: boolean;
|
|
95
|
+
}
|
|
96
|
+
export type FocusAction = ActionBase<"focus", FocusParam>;
|
|
97
|
+
|
|
98
|
+
export interface ZoomParam {
|
|
99
|
+
payloadPositionIndex: number;
|
|
100
|
+
/** mm, > 0. */
|
|
101
|
+
focalLength: number;
|
|
102
|
+
}
|
|
103
|
+
export type ZoomAction = ActionBase<"zoom", ZoomParam>;
|
|
104
|
+
|
|
105
|
+
export interface CustomDirNameParam {
|
|
106
|
+
payloadPositionIndex: number;
|
|
107
|
+
directoryName: string;
|
|
108
|
+
}
|
|
109
|
+
export type CustomDirNameAction = ActionBase<"customDirName", CustomDirNameParam>;
|
|
110
|
+
|
|
111
|
+
export interface GimbalRotateParam {
|
|
112
|
+
payloadPositionIndex: number;
|
|
113
|
+
gimbalHeadingYawBase: GimbalHeadingYawBase;
|
|
114
|
+
gimbalRotateMode: GimbalRotateMode;
|
|
115
|
+
gimbalPitchRotateEnable: boolean;
|
|
116
|
+
gimbalPitchRotateAngle: Degrees;
|
|
117
|
+
gimbalRollRotateEnable: boolean;
|
|
118
|
+
gimbalRollRotateAngle: Degrees;
|
|
119
|
+
gimbalYawRotateEnable: boolean;
|
|
120
|
+
gimbalYawRotateAngle: Degrees;
|
|
121
|
+
gimbalRotateTimeEnable: boolean;
|
|
122
|
+
/** seconds */
|
|
123
|
+
gimbalRotateTime: number;
|
|
124
|
+
}
|
|
125
|
+
export type GimbalRotateAction = ActionBase<"gimbalRotate", GimbalRotateParam>;
|
|
126
|
+
|
|
127
|
+
export interface RotateYawParam {
|
|
128
|
+
/** `[-180, 180]`, 0=N, 90=E, -90=W, ±180=S. */
|
|
129
|
+
aircraftHeading: Degrees;
|
|
130
|
+
/** Required on M300/M350. */
|
|
131
|
+
aircraftPathMode?: AircraftPathMode;
|
|
132
|
+
}
|
|
133
|
+
export type RotateYawAction = ActionBase<"rotateYaw", RotateYawParam>;
|
|
134
|
+
|
|
135
|
+
export interface HoverParam {
|
|
136
|
+
/** seconds, > 0. */
|
|
137
|
+
hoverTime: number;
|
|
138
|
+
}
|
|
139
|
+
export type HoverAction = ActionBase<"hover", HoverParam>;
|
|
140
|
+
|
|
141
|
+
export interface GimbalEvenlyRotateParam {
|
|
142
|
+
payloadPositionIndex: number;
|
|
143
|
+
gimbalPitchRotateAngle: Degrees;
|
|
144
|
+
}
|
|
145
|
+
/** Must be paired with `actionTriggerType === "betweenAdjacentPoints"`. */
|
|
146
|
+
export type GimbalEvenlyRotateAction = ActionBase<"gimbalEvenlyRotate", GimbalEvenlyRotateParam>;
|
|
147
|
+
|
|
148
|
+
/** Deprecated for M30/M30T after firmware V06.01.10.02. Prefer `orientedShoot`. */
|
|
149
|
+
export interface AccurateShootParam {
|
|
150
|
+
gimbalPitchRotateAngle: Degrees;
|
|
151
|
+
gimbalYawRotateAngle: Degrees;
|
|
152
|
+
focusX: number;
|
|
153
|
+
focusY: number;
|
|
154
|
+
focusRegionWidth: number;
|
|
155
|
+
focusRegionHeight: number;
|
|
156
|
+
focalLength: number;
|
|
157
|
+
aircraftHeading: Degrees;
|
|
158
|
+
accurateFrameValid: boolean;
|
|
159
|
+
payloadPositionIndex: number;
|
|
160
|
+
payloadLensIndex: LensType[];
|
|
161
|
+
useGlobalPayloadLensIndex: boolean;
|
|
162
|
+
targetAngle: Degrees;
|
|
163
|
+
imageWidth: number;
|
|
164
|
+
imageHeight: number;
|
|
165
|
+
AFPos: number;
|
|
166
|
+
gimbalPort: number;
|
|
167
|
+
accurateCameraType: number;
|
|
168
|
+
accurateFilePath: string;
|
|
169
|
+
accurateFileMD5: string;
|
|
170
|
+
accurateFileSize: number;
|
|
171
|
+
accurateFileSuffix: string;
|
|
172
|
+
accurateCameraApertue?: number;
|
|
173
|
+
accurateCameraLuminance?: number;
|
|
174
|
+
accurateCameraShutterTime?: number;
|
|
175
|
+
accurateCameraISO?: number;
|
|
176
|
+
}
|
|
177
|
+
export type AccurateShootAction = ActionBase<"accurateShoot", AccurateShootParam>;
|
|
178
|
+
|
|
179
|
+
export interface OrientedShootParam {
|
|
180
|
+
gimbalPitchRotateAngle: Degrees;
|
|
181
|
+
gimbalYawRotateAngle: Degrees;
|
|
182
|
+
focusX: number;
|
|
183
|
+
focusY: number;
|
|
184
|
+
focusRegionWidth: number;
|
|
185
|
+
focusRegionHeight: number;
|
|
186
|
+
focalLength: number;
|
|
187
|
+
aircraftHeading: Degrees;
|
|
188
|
+
accurateFrameValid: boolean;
|
|
189
|
+
payloadPositionIndex: number;
|
|
190
|
+
payloadLensIndex: LensType[];
|
|
191
|
+
useGlobalPayloadLensIndex: boolean;
|
|
192
|
+
targetAngle: Degrees;
|
|
193
|
+
actionUUID: string;
|
|
194
|
+
imageWidth: number;
|
|
195
|
+
imageHeight: number;
|
|
196
|
+
AFPos: number;
|
|
197
|
+
gimbalPort: number;
|
|
198
|
+
orientedCameraType: number;
|
|
199
|
+
orientedFilePath: string;
|
|
200
|
+
orientedFileMD5: string;
|
|
201
|
+
orientedFileSize: number;
|
|
202
|
+
orientedFileSuffix: string;
|
|
203
|
+
orientedCameraApertue: number;
|
|
204
|
+
orientedCameraLuminance: number;
|
|
205
|
+
orientedCameraShutterTime: number;
|
|
206
|
+
orientedCameraISO: number;
|
|
207
|
+
orientedPhotoMode: OrientedPhotoMode;
|
|
208
|
+
}
|
|
209
|
+
export type OrientedShootAction = ActionBase<"orientedShoot", OrientedShootParam>;
|
|
210
|
+
|
|
211
|
+
export interface PanoShotParam {
|
|
212
|
+
payloadPositionIndex?: number;
|
|
213
|
+
payloadLensIndex?: LensType[];
|
|
214
|
+
useGlobalPayloadLensIndex?: boolean;
|
|
215
|
+
panoShotSubMode: PanoShotSubMode;
|
|
216
|
+
}
|
|
217
|
+
export type PanoShotAction = ActionBase<"panoShot", PanoShotParam>;
|
|
218
|
+
|
|
219
|
+
export interface RecordPointCloudParam {
|
|
220
|
+
payloadPositionIndex: number;
|
|
221
|
+
recordPointCloudOperate: RecordPointCloudOperate;
|
|
222
|
+
}
|
|
223
|
+
export type RecordPointCloudAction = ActionBase<"recordPointCloud", RecordPointCloudParam>;
|
|
224
|
+
|
|
225
|
+
/** M4D/M4TD only. */
|
|
226
|
+
export interface MegaphoneParam {
|
|
227
|
+
payloadPositionIndex: number;
|
|
228
|
+
actionUUID: string;
|
|
229
|
+
/** 0 = start, 1 = stop. */
|
|
230
|
+
megaphoneOperateType: 0 | 1;
|
|
231
|
+
/** 0–100. */
|
|
232
|
+
megaphoneOperateVolume: number;
|
|
233
|
+
megaphoneOperateLoop: boolean;
|
|
234
|
+
/** Path inside KMZ, e.g. `/wpmz/res/audio/<hash>.opus`. */
|
|
235
|
+
megaphoneOperateFilePath: string;
|
|
236
|
+
megaphoneFileName: string;
|
|
237
|
+
megaphoneFileOriginalName: string;
|
|
238
|
+
megaphoneFileMd5: string;
|
|
239
|
+
/** 1=8000, 2=16000, 3=24000, 4=32000, 5=48000, 6=64000 (only 4 supported today). */
|
|
240
|
+
megaphoneFileBitrate: 1 | 2 | 3 | 4 | 5 | 6;
|
|
241
|
+
}
|
|
242
|
+
export type MegaphoneAction = ActionBase<"megaphone", MegaphoneParam>;
|
|
243
|
+
|
|
244
|
+
/** M4D/M4TD only. */
|
|
245
|
+
export interface SearchlightParam {
|
|
246
|
+
payloadPositionIndex: number;
|
|
247
|
+
actionUUID?: string;
|
|
248
|
+
/** 0 = off, 1 = illuminate, 2 = flash. */
|
|
249
|
+
searchlightOperateType: 0 | 1 | 2;
|
|
250
|
+
/** 0–100. */
|
|
251
|
+
searchlightBrightness: number;
|
|
252
|
+
}
|
|
253
|
+
export type SearchlightAction = ActionBase<"searchlight", SearchlightParam>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type Brand<T, B> = T & { readonly __brand: B };
|
|
2
|
+
|
|
3
|
+
export type Meters = Brand<number, "Meters">;
|
|
4
|
+
export type MetersPerSecond = Brand<number, "MetersPerSecond">;
|
|
5
|
+
export type Degrees = Brand<number, "Degrees">;
|
|
6
|
+
|
|
7
|
+
export const meters = (n: number) => n as Meters;
|
|
8
|
+
export const metersPerSecond = (n: number) => n as MetersPerSecond;
|
|
9
|
+
export const degrees = (n: number) => n as Degrees;
|
|
10
|
+
|
|
11
|
+
export interface LngLat {
|
|
12
|
+
readonly lng: number;
|
|
13
|
+
readonly lat: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface LngLatHeight {
|
|
17
|
+
readonly lng: number;
|
|
18
|
+
readonly lat: number;
|
|
19
|
+
readonly height: Meters;
|
|
20
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Drone & payload enum tables from docs/kmz/product-support.html.
|
|
2
|
+
// `droneEnumValue` (type) + `droneSubEnumValue` (sub_type) together identify the model.
|
|
3
|
+
|
|
4
|
+
export interface DroneInfo {
|
|
5
|
+
droneEnumValue: number;
|
|
6
|
+
droneSubEnumValue?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface PayloadInfo {
|
|
10
|
+
payloadEnumValue: number;
|
|
11
|
+
payloadPositionIndex: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Aircraft `{ droneEnumValue, droneSubEnumValue }` tuples. */
|
|
15
|
+
export const DroneEnum = {
|
|
16
|
+
M400: { droneEnumValue: 103, droneSubEnumValue: 0 },
|
|
17
|
+
M350RTK: { droneEnumValue: 89, droneSubEnumValue: 0 },
|
|
18
|
+
M300RTK: { droneEnumValue: 60, droneSubEnumValue: 0 },
|
|
19
|
+
M30: { droneEnumValue: 67, droneSubEnumValue: 0 },
|
|
20
|
+
M30T: { droneEnumValue: 67, droneSubEnumValue: 1 },
|
|
21
|
+
M3E: { droneEnumValue: 77, droneSubEnumValue: 0 },
|
|
22
|
+
M3T: { droneEnumValue: 77, droneSubEnumValue: 1 },
|
|
23
|
+
M3TA: { droneEnumValue: 77, droneSubEnumValue: 3 },
|
|
24
|
+
M3D: { droneEnumValue: 91, droneSubEnumValue: 0 },
|
|
25
|
+
M3TD: { droneEnumValue: 91, droneSubEnumValue: 1 },
|
|
26
|
+
M4D: { droneEnumValue: 100, droneSubEnumValue: 0 },
|
|
27
|
+
M4TD: { droneEnumValue: 100, droneSubEnumValue: 1 },
|
|
28
|
+
M4E: { droneEnumValue: 99, droneSubEnumValue: 0 },
|
|
29
|
+
M4T: { droneEnumValue: 99, droneSubEnumValue: 1 },
|
|
30
|
+
} as const satisfies Record<string, DroneInfo>;
|
|
31
|
+
|
|
32
|
+
/** Payload `{ payloadEnumValue, payloadPositionIndex }` tuples for common cameras. */
|
|
33
|
+
export const PayloadEnum = {
|
|
34
|
+
// Main gimbal cameras
|
|
35
|
+
M30Camera: { payloadEnumValue: 52, payloadPositionIndex: 0 },
|
|
36
|
+
M30TCamera: { payloadEnumValue: 53, payloadPositionIndex: 0 },
|
|
37
|
+
M3ECamera: { payloadEnumValue: 66, payloadPositionIndex: 0 },
|
|
38
|
+
M3TCamera: { payloadEnumValue: 67, payloadPositionIndex: 0 },
|
|
39
|
+
M3TACamera: { payloadEnumValue: 129, payloadPositionIndex: 0 },
|
|
40
|
+
M3DCamera: { payloadEnumValue: 80, payloadPositionIndex: 0 },
|
|
41
|
+
M3TDCamera: { payloadEnumValue: 81, payloadPositionIndex: 0 },
|
|
42
|
+
M4DCamera: { payloadEnumValue: 98, payloadPositionIndex: 0 },
|
|
43
|
+
M4TDCamera: { payloadEnumValue: 99, payloadPositionIndex: 0 },
|
|
44
|
+
M4ECamera: { payloadEnumValue: 88, payloadPositionIndex: 0 },
|
|
45
|
+
M4TCamera: { payloadEnumValue: 89, payloadPositionIndex: 0 },
|
|
46
|
+
// Zenmuse mounts: 0=front-left/main, 1=front-right, 2=top (M300 RTK), 0=main for others
|
|
47
|
+
ZenmuseZ30: { payloadEnumValue: 20, payloadPositionIndex: 0 },
|
|
48
|
+
ZenmuseXT2: { payloadEnumValue: 26, payloadPositionIndex: 0 },
|
|
49
|
+
ZenmuseXTS: { payloadEnumValue: 41, payloadPositionIndex: 0 },
|
|
50
|
+
ZenmuseH20: { payloadEnumValue: 42, payloadPositionIndex: 0 },
|
|
51
|
+
ZenmuseH20T: { payloadEnumValue: 43, payloadPositionIndex: 0 },
|
|
52
|
+
ZenmuseH20N: { payloadEnumValue: 61, payloadPositionIndex: 0 },
|
|
53
|
+
ZenmuseH30: { payloadEnumValue: 82, payloadPositionIndex: 0 },
|
|
54
|
+
ZenmuseH30T: { payloadEnumValue: 83, payloadPositionIndex: 0 },
|
|
55
|
+
// FPV / aux
|
|
56
|
+
FPVMatriceSeries: { payloadEnumValue: 39, payloadPositionIndex: 7 },
|
|
57
|
+
AuxiliaryM3xM4x: { payloadEnumValue: 176, payloadPositionIndex: 0 },
|
|
58
|
+
DockCamera: { payloadEnumValue: 165, payloadPositionIndex: 7 },
|
|
59
|
+
} as const satisfies Record<string, PayloadInfo>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Enum string literals mirror DJI WPML spec verbatim, including typos (`visable`, `followBadArc`).
|
|
2
|
+
|
|
3
|
+
export type FlyToWaylineMode = "safely" | "pointToPoint";
|
|
4
|
+
|
|
5
|
+
export type FinishAction = "goHome" | "noAction" | "autoLand" | "gotoFirstWaypoint";
|
|
6
|
+
|
|
7
|
+
export type ExitOnRCLost = "goContinue" | "executeLostAction";
|
|
8
|
+
|
|
9
|
+
export type ExecuteRCLostAction = "goBack" | "landing" | "hover";
|
|
10
|
+
|
|
11
|
+
export type TemplateType = "waypoint" | "mapping2d" | "mapping3d" | "mappingStrip";
|
|
12
|
+
|
|
13
|
+
export type CoordinateMode = "WGS84";
|
|
14
|
+
|
|
15
|
+
/** Used in template.kml `waylineCoordinateSysParam.heightMode`. */
|
|
16
|
+
export type TemplateHeightMode =
|
|
17
|
+
| "EGM96"
|
|
18
|
+
| "relativeToStartPoint"
|
|
19
|
+
| "aboveGroundLevel"
|
|
20
|
+
| "realTimeFollowSurface";
|
|
21
|
+
|
|
22
|
+
/** Used in waylines.wpml `Folder.executeHeightMode`. */
|
|
23
|
+
export type ExecuteHeightMode = "WGS84" | "relativeToStartPoint" | "realTimeFollowSurface";
|
|
24
|
+
|
|
25
|
+
export type PositioningType = "GPS" | "RTKBaseStation" | "QianXun" | "Custom";
|
|
26
|
+
|
|
27
|
+
export type WaypointTurnMode =
|
|
28
|
+
| "coordinateTurn"
|
|
29
|
+
| "toPointAndStopWithDiscontinuityCurvature"
|
|
30
|
+
| "toPointAndStopWithContinuityCurvature"
|
|
31
|
+
| "toPointAndPassWithContinuityCurvature";
|
|
32
|
+
|
|
33
|
+
export type WaypointHeadingMode =
|
|
34
|
+
| "followWayline"
|
|
35
|
+
| "manually"
|
|
36
|
+
| "fixed"
|
|
37
|
+
| "smoothTransition"
|
|
38
|
+
| "towardPOI";
|
|
39
|
+
|
|
40
|
+
/** Spec typo preserved: `followBadArc` (intended "shortest path / smaller arc"). */
|
|
41
|
+
export type WaypointHeadingPathMode = "clockwise" | "counterClockwise" | "followBadArc";
|
|
42
|
+
|
|
43
|
+
/** Template waypoint folder allows manual + per-point; mapping templates allow manual + fixed. */
|
|
44
|
+
export type GimbalPitchMode = "manual" | "usePointSetting" | "fixed";
|
|
45
|
+
|
|
46
|
+
export type ShootType = "time" | "distance";
|
|
47
|
+
|
|
48
|
+
export type MappingHeadingMode = "fixed" | "followWayline";
|
|
49
|
+
|
|
50
|
+
/** Spec typo preserved: `visable`. */
|
|
51
|
+
export type LensType = "wide" | "zoom" | "ir" | "narrow_band" | "visable";
|
|
52
|
+
|
|
53
|
+
export type FocusMode = "firstPoint" | "custom";
|
|
54
|
+
|
|
55
|
+
export type MeteringMode = "average" | "spot";
|
|
56
|
+
|
|
57
|
+
export type LidarReturnMode = "singleReturnStrongest" | "dualReturn" | "tripleReturn";
|
|
58
|
+
|
|
59
|
+
export type LidarScanningMode = "repetitive" | "nonRepetitive";
|
|
60
|
+
|
|
61
|
+
export type ActionGroupMode = "sequence";
|
|
62
|
+
|
|
63
|
+
export type ActionTriggerType =
|
|
64
|
+
| "reachPoint"
|
|
65
|
+
| "betweenAdjacentPoints"
|
|
66
|
+
| "multipleTiming"
|
|
67
|
+
| "multipleDistance";
|
|
68
|
+
|
|
69
|
+
export type ActionActuatorFunc =
|
|
70
|
+
| "takePhoto"
|
|
71
|
+
| "startRecord"
|
|
72
|
+
| "stopRecord"
|
|
73
|
+
| "focus"
|
|
74
|
+
| "zoom"
|
|
75
|
+
| "customDirName"
|
|
76
|
+
| "gimbalRotate"
|
|
77
|
+
| "rotateYaw"
|
|
78
|
+
| "hover"
|
|
79
|
+
| "gimbalEvenlyRotate"
|
|
80
|
+
| "accurateShoot"
|
|
81
|
+
| "orientedShoot"
|
|
82
|
+
| "panoShot"
|
|
83
|
+
| "recordPointCloud"
|
|
84
|
+
| "megaphone"
|
|
85
|
+
| "searchlight";
|
|
86
|
+
|
|
87
|
+
export type GimbalHeadingYawBase = "north";
|
|
88
|
+
|
|
89
|
+
export type GimbalRotateMode = "absoluteAngle";
|
|
90
|
+
|
|
91
|
+
export type AircraftPathMode = "clockwise" | "counterClockwise";
|
|
92
|
+
|
|
93
|
+
export type RecordPointCloudOperate = "startRecord" | "pauseRecord" | "resumeRecord" | "stopRecord";
|
|
94
|
+
|
|
95
|
+
export type PanoShotSubMode = "panoShot_360";
|
|
96
|
+
|
|
97
|
+
export type OrientedPhotoMode = "normalPhoto" | "lowLightSmartShooting";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class KmzError extends Error {
|
|
2
|
+
constructor(message: string) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "KmzError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class PlannerError extends KmzError {
|
|
9
|
+
constructor(message: string) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "PlannerError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Issue {
|
|
16
|
+
path: string;
|
|
17
|
+
message: string;
|
|
18
|
+
severity: "error" | "warning";
|
|
19
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./branded.js";
|
|
2
|
+
export * from "./errors.js";
|
|
3
|
+
export * from "./enums.js";
|
|
4
|
+
export * from "./drone-payload.js";
|
|
5
|
+
export * from "./mission-config.js";
|
|
6
|
+
export * from "./waypoint-params.js";
|
|
7
|
+
export * from "./action.js";
|
|
8
|
+
export * from "./placemark.js";
|
|
9
|
+
export * from "./template.js";
|
|
10
|
+
export * from "./waylines.js";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { LngLatHeight, Meters, MetersPerSecond } from "./branded.js";
|
|
2
|
+
import type { DroneInfo, PayloadInfo } from "./drone-payload.js";
|
|
3
|
+
import type {
|
|
4
|
+
ExecuteRCLostAction,
|
|
5
|
+
ExitOnRCLost,
|
|
6
|
+
FinishAction,
|
|
7
|
+
FlyToWaylineMode,
|
|
8
|
+
FocusMode,
|
|
9
|
+
LensType,
|
|
10
|
+
LidarReturnMode,
|
|
11
|
+
LidarScanningMode,
|
|
12
|
+
MeteringMode,
|
|
13
|
+
} from "./enums.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `wpml:missionConfig` — shared between template.kml and waylines.wpml.
|
|
17
|
+
* See `docs/kmz/common-element.html` §missionConfig.
|
|
18
|
+
*/
|
|
19
|
+
export interface MissionConfig {
|
|
20
|
+
flyToWaylineMode: FlyToWaylineMode;
|
|
21
|
+
finishAction: FinishAction;
|
|
22
|
+
exitOnRCLost: ExitOnRCLost;
|
|
23
|
+
/** Required iff `exitOnRCLost === "executeLostAction"`. */
|
|
24
|
+
executeRCLostAction?: ExecuteRCLostAction;
|
|
25
|
+
takeOffSecurityHeight: Meters;
|
|
26
|
+
/** lat/lng with WGS84 ellipsoid height. Optional; only used for planning. */
|
|
27
|
+
takeOffRefPoint?: LngLatHeight;
|
|
28
|
+
/** AGL altitude of `takeOffRefPoint`. Paired with the ellipsoid height inside `takeOffRefPoint.height`. */
|
|
29
|
+
takeOffRefPointAGLHeight?: Meters;
|
|
30
|
+
globalTransitionalSpeed: MetersPerSecond;
|
|
31
|
+
globalRTHHeight: Meters;
|
|
32
|
+
droneInfo: DroneInfo;
|
|
33
|
+
payloadInfo: PayloadInfo[];
|
|
34
|
+
/** Only M3D/M3TD, M4D/M4TD, M4E/M4T. */
|
|
35
|
+
autoRerouteInfo?: AutoRerouteInfo;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface AutoRerouteInfo {
|
|
39
|
+
missionAutoRerouteMode: boolean;
|
|
40
|
+
transitionalAutoRerouteMode: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* `wpml:payloadParam` — per-payload imaging settings.
|
|
45
|
+
* Lives under template `Folder`.
|
|
46
|
+
*/
|
|
47
|
+
export interface PayloadParam {
|
|
48
|
+
payloadPositionIndex: number;
|
|
49
|
+
focusMode?: FocusMode;
|
|
50
|
+
meteringMode?: MeteringMode;
|
|
51
|
+
dewarpingEnable?: boolean;
|
|
52
|
+
returnMode?: LidarReturnMode;
|
|
53
|
+
samplingRate?: 60000 | 80000 | 120000 | 160000 | 180000 | 240000;
|
|
54
|
+
scanningMode?: LidarScanningMode;
|
|
55
|
+
modelColoringEnable?: boolean;
|
|
56
|
+
/** Comma-joined `LensType` tokens, e.g. `["wide","ir"]`. */
|
|
57
|
+
imageFormat: LensType[];
|
|
58
|
+
}
|