@aircast-4g/mavlink 1.1.6 → 1.1.8

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.
@@ -5,7 +5,7 @@ interface ParsedMAVLinkMessage$1 {
5
5
  message_id: number;
6
6
  message_name: string;
7
7
  sequence: number;
8
- payload: Record<string, any>;
8
+ payload: Record<string, unknown>;
9
9
  protocol_version: 1 | 2;
10
10
  checksum: number;
11
11
  crc_ok: boolean;
@@ -266,17 +266,17 @@ type MAV_COMPONENT = MAV_COMPONENTEnum;
266
266
  interface MessageHeartbeat {
267
267
  type: MAV_TYPE;
268
268
  autopilot: MAV_AUTOPILOT;
269
- baseMode: MAV_MODE_FLAG;
270
- customMode: number;
271
- systemStatus: MAV_STATE;
272
- mavlinkVersion: number;
269
+ base_mode: MAV_MODE_FLAG;
270
+ custom_mode: number;
271
+ system_status: MAV_STATE;
272
+ mavlink_version: number;
273
273
  }
274
274
  interface MessageProtocolVersion {
275
275
  version: number;
276
- minVersion: number;
277
- maxVersion: number;
278
- specVersionHash: number[];
279
- libraryVersionHash: number[];
276
+ min_version: number;
277
+ max_version: number;
278
+ spec_version_hash: number[];
279
+ library_version_hash: number[];
280
280
  }
281
281
  interface MessageTypeMap {
282
282
  HEARTBEAT: MessageHeartbeat;
@@ -297,7 +297,7 @@ interface ParsedMAVLinkMessage {
297
297
  message_id: number;
298
298
  message_name: string;
299
299
  sequence: number;
300
- payload: Record<string, any>;
300
+ payload: Record<string, unknown>;
301
301
  protocol_version: 1 | 2;
302
302
  checksum: number;
303
303
  crc_ok: boolean;
@@ -316,6 +316,8 @@ interface MAVLinkFrame {
316
316
  payload: Uint8Array;
317
317
  checksum: number;
318
318
  signature?: Uint8Array;
319
+ crc_ok?: boolean;
320
+ protocol_version?: 1 | 2;
319
321
  }
320
322
  interface MessageDefinition {
321
323
  id: number;
@@ -337,10 +339,7 @@ declare abstract class DialectParser {
337
339
  parseBytes(data: Uint8Array): ParsedMAVLinkMessage[];
338
340
  private tryParseFrame;
339
341
  resetBuffer(): void;
340
- decode(frame: MAVLinkFrame & {
341
- crc_ok?: boolean;
342
- protocol_version?: 1 | 2;
343
- }): ParsedMAVLinkMessage;
342
+ decode(frame: MAVLinkFrame): ParsedMAVLinkMessage;
344
343
  private decodePayload;
345
344
  private getDefaultValue;
346
345
  private decodeField;
@@ -349,12 +348,37 @@ declare abstract class DialectParser {
349
348
  getSupportedMessageIds(): number[];
350
349
  getDialectName(): string;
351
350
  supportsMessage(messageId: number): boolean;
351
+ serializeMessage(message: Record<string, unknown> & {
352
+ message_name: string;
353
+ }): Uint8Array;
354
+ private extractMessageFields;
355
+ private completeMessageWithDefaults;
356
+ private getDefaultValueForField;
357
+ private serializePayload;
358
+ private serializeField;
359
+ private serializeSingleValue;
360
+ private getFieldSize;
361
+ private getSingleFieldSize;
362
+ private getDefaultValueForType;
363
+ private createMAVLinkFrame;
352
364
  }
353
365
  declare class MinimalParser extends DialectParser {
354
366
  constructor();
355
367
  loadDefinitions(): Promise<void>;
356
368
  private loadDefinitionsSync;
357
369
  }
370
+ declare class MinimalSerializer {
371
+ private parser;
372
+ constructor();
373
+ serialize(message: Record<string, unknown> & {
374
+ message_name: string;
375
+ }): Uint8Array;
376
+ completeMessage(message: Record<string, unknown> & {
377
+ message_name: string;
378
+ }): Record<string, unknown>;
379
+ getSupportedMessages(): string[];
380
+ supportsMessage(messageName: string): boolean;
381
+ }
358
382
 
359
- export { MAV_AUTOPILOTEnum, MAV_COMPONENTEnum, MAV_MODE_FLAGEnum, MAV_MODE_FLAG_DECODE_POSITIONEnum, MAV_STATEEnum, MAV_TYPEEnum, MinimalParser, isHeartbeat, isProtocolVersion };
383
+ export { MAV_AUTOPILOTEnum, MAV_COMPONENTEnum, MAV_MODE_FLAGEnum, MAV_MODE_FLAG_DECODE_POSITIONEnum, MAV_STATEEnum, MAV_TYPEEnum, MinimalParser, MinimalSerializer, isHeartbeat, isProtocolVersion };
360
384
  export type { AnyMessage, MAV_AUTOPILOT, MAV_COMPONENT, MAV_MODE_FLAG, MAV_MODE_FLAG_DECODE_POSITION, MAV_STATE, MAV_TYPE, MessageHeartbeat, MessageProtocolVersion, MessageTypeMap, ParsedMAVLinkMessage$1 as ParsedMAVLinkMessage };
@@ -505,6 +505,35 @@ function isProtocolVersion(msg) {
505
505
 
506
506
  // Auto-generated decoder and parser for minimal dialect
507
507
  // Generated from MAVLink XML definitions
508
+ // Embedded MAVLink CRC implementation
509
+ const CRC_EXTRA = {
510
+ 0: 50,
511
+ 300: 0
512
+ };
513
+ class MAVLinkCRC {
514
+ static calculate(data, crcExtra) {
515
+ let crc = 0xffff;
516
+ // Process all message bytes using MCRF4XX algorithm
517
+ for (let i = 0; i < data.length; i++) {
518
+ let tmp = data[i] ^ (crc & 0xff);
519
+ tmp = (tmp ^ (tmp << 4)) & 0xff;
520
+ crc = ((crc >> 8) ^ (tmp << 8) ^ (tmp << 3) ^ (tmp >> 4)) & 0xffff;
521
+ }
522
+ // Add CRC_EXTRA byte using the same algorithm
523
+ let tmp = crcExtra ^ (crc & 0xff);
524
+ tmp = (tmp ^ (tmp << 4)) & 0xff;
525
+ crc = ((crc >> 8) ^ (tmp << 8) ^ (tmp << 3) ^ (tmp >> 4)) & 0xffff;
526
+ return crc;
527
+ }
528
+ static validate(data, messageId, receivedChecksum) {
529
+ const crcExtra = CRC_EXTRA[messageId];
530
+ if (crcExtra === undefined) {
531
+ return false;
532
+ }
533
+ const calculatedChecksum = this.calculate(data, crcExtra);
534
+ return calculatedChecksum === receivedChecksum;
535
+ }
536
+ }
508
537
  // Base dialect parser class
509
538
  class DialectParser {
510
539
  constructor(dialectName) {
@@ -592,9 +621,11 @@ class DialectParser {
592
621
  frameOffset += 13;
593
622
  }
594
623
  }
595
- frame.crc_ok = true; // Simplified - not doing CRC validation
624
+ // Validate CRC using proper MAVLink algorithm
625
+ const headerAndPayload = data.slice(offset + 1, offset + frameOffset - offset - 2);
626
+ frame.crc_ok = MAVLinkCRC.validate(headerAndPayload, frame.message_id, frame.checksum);
596
627
  frame.protocol_version = isV2 ? 2 : 1;
597
- return { frame, bytesConsumed: frameOffset - offset };
628
+ return { frame: frame, bytesConsumed: frameOffset - offset };
598
629
  }
599
630
  resetBuffer() {
600
631
  this.buffer = new Uint8Array(0);
@@ -615,7 +646,7 @@ class DialectParser {
615
646
  },
616
647
  protocol_version: protocolVersion,
617
648
  checksum: frame.checksum,
618
- crc_ok: frame.crc_ok !== false,
649
+ crc_ok: frame.crc_ok ?? true,
619
650
  signature: frame.signature,
620
651
  dialect: this.dialectName
621
652
  };
@@ -631,7 +662,7 @@ class DialectParser {
631
662
  payload,
632
663
  protocol_version: protocolVersion,
633
664
  checksum: frame.checksum,
634
- crc_ok: frame.crc_ok !== false,
665
+ crc_ok: frame.crc_ok ?? true,
635
666
  signature: frame.signature,
636
667
  dialect: this.dialectName
637
668
  };
@@ -683,18 +714,37 @@ class DialectParser {
683
714
  const isArray = field.arrayLength !== undefined;
684
715
  const arrayLength = field.arrayLength || 1;
685
716
  if (isArray && arrayLength > 1) {
686
- const values = [];
687
- let totalBytes = 0;
688
717
  // Strip array notation from type to avoid double processing
689
718
  let baseType = field.type;
690
719
  if (baseType.includes('[') && baseType.includes(']')) {
691
720
  baseType = baseType.substring(0, baseType.indexOf('['));
692
721
  }
722
+ // Special handling for char arrays - return as string
723
+ if (baseType === 'char') {
724
+ const chars = [];
725
+ let totalBytes = 0;
726
+ for (let i = 0; i < arrayLength; i++) {
727
+ if (offset + totalBytes >= view.byteLength)
728
+ break;
729
+ const charCode = view.getUint8(offset + totalBytes);
730
+ if (charCode === 0)
731
+ break; // Null terminator
732
+ chars.push(String.fromCharCode(charCode));
733
+ totalBytes += 1;
734
+ }
735
+ // Return string value and total bytes for the array
736
+ return { value: chars.join(''), bytesRead: arrayLength }; // Always consume full array size
737
+ }
738
+ // Handle other array types
739
+ const values = [];
740
+ let totalBytes = 0;
693
741
  for (let i = 0; i < arrayLength; i++) {
694
742
  if (offset + totalBytes >= view.byteLength)
695
743
  break;
696
744
  const { value, bytesRead } = this.decodeSingleValue(view, offset + totalBytes, baseType);
697
- values.push(value);
745
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'bigint' || typeof value === 'boolean') {
746
+ values.push(value);
747
+ }
698
748
  totalBytes += bytesRead;
699
749
  }
700
750
  return { value: values, bytesRead: totalBytes };
@@ -711,7 +761,7 @@ class DialectParser {
711
761
  case 'int8_t':
712
762
  return { value: view.getInt8(offset), bytesRead: 1 };
713
763
  case 'uint16_t':
714
- return { value: view.getUint16(offset, true), bytesRead: 2 };
764
+ return { value: view.getUint16(offset, false), bytesRead: 2 };
715
765
  case 'int16_t':
716
766
  return { value: view.getInt16(offset, true), bytesRead: 2 };
717
767
  case 'uint32_t':
@@ -751,7 +801,9 @@ class DialectParser {
751
801
  if (offset + totalBytes >= view.byteLength)
752
802
  break;
753
803
  const { value, bytesRead } = this.decodeSingleValue(view, offset + totalBytes, baseType);
754
- values.push(value);
804
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'bigint' || typeof value === 'boolean') {
805
+ values.push(value);
806
+ }
755
807
  totalBytes += bytesRead;
756
808
  }
757
809
  return { value: values, bytesRead: totalBytes };
@@ -775,6 +827,347 @@ class DialectParser {
775
827
  supportsMessage(messageId) {
776
828
  return this.messageDefinitions.has(messageId);
777
829
  }
830
+ // Serialization methods for outgoing commands
831
+ serializeMessage(message) {
832
+ const messageDef = Array.from(this.messageDefinitions.values())
833
+ .find(def => def.name === message.message_name);
834
+ if (!messageDef) {
835
+ throw new Error(`Unknown message type: ${message.message_name}`);
836
+ }
837
+ // Extract fields from either flat structure or payload structure
838
+ const messageFields = this.extractMessageFields(message, messageDef.fields);
839
+ // Complete the message with all defined fields (including extension fields with defaults)
840
+ const completeMessage = this.completeMessageWithDefaults(messageFields, messageDef.fields);
841
+ const payload = this.serializePayload(completeMessage, messageDef.fields);
842
+ return this.createMAVLinkFrame(message, messageDef.id, payload);
843
+ }
844
+ // Extract message fields from payload structure (payload format required)
845
+ extractMessageFields(message, fieldDefinitions) {
846
+ // Require payload structure
847
+ if (!message.payload || typeof message.payload !== 'object') {
848
+ throw new Error(`Message must have a 'payload' object containing the message fields. Expected format: { message_name: '...', system_id: 1, component_id: 1, sequence: 0, payload: { ...fields } }`);
849
+ }
850
+ return message.payload;
851
+ }
852
+ // Helper method to complete message with all defined fields
853
+ completeMessageWithDefaults(message, fields) {
854
+ const completeMessage = { ...message };
855
+ for (const field of fields) {
856
+ if (completeMessage[field.name] === undefined) {
857
+ completeMessage[field.name] = this.getDefaultValueForField(field);
858
+ }
859
+ }
860
+ return completeMessage;
861
+ }
862
+ // Get default value for a field based on its definition
863
+ getDefaultValueForField(field) {
864
+ const isArray = field.arrayLength !== undefined && field.arrayLength > 1;
865
+ if (isArray) {
866
+ return [];
867
+ }
868
+ let baseType = field.type;
869
+ if (baseType.includes('[') && baseType.includes(']')) {
870
+ baseType = baseType.substring(0, baseType.indexOf('['));
871
+ }
872
+ switch (baseType) {
873
+ case 'uint8_t':
874
+ case 'int8_t':
875
+ case 'uint16_t':
876
+ case 'int16_t':
877
+ case 'uint32_t':
878
+ case 'int32_t':
879
+ case 'float':
880
+ case 'double':
881
+ return 0;
882
+ case 'uint64_t':
883
+ case 'int64_t':
884
+ return 0n;
885
+ case 'char':
886
+ return field.type.includes('[') ? '' : '\0';
887
+ default:
888
+ return 0;
889
+ }
890
+ }
891
+ serializePayload(message, fields) {
892
+ // Calculate total payload size
893
+ let totalSize = 0;
894
+ for (const field of fields) {
895
+ totalSize += this.getFieldSize(field);
896
+ }
897
+ const buffer = new ArrayBuffer(totalSize);
898
+ const view = new DataView(buffer);
899
+ let offset = 0;
900
+ for (const field of fields) {
901
+ const value = message[field.name];
902
+ const bytesWritten = this.serializeField(view, offset, field, value);
903
+ offset += bytesWritten;
904
+ }
905
+ // Implement MAVLink payload trimming: remove trailing zero bytes from extension fields only
906
+ // This is required for proper handling of extension fields
907
+ const fullPayload = new Uint8Array(buffer);
908
+ // Calculate minimum payload size (core fields only)
909
+ let corePayloadSize = 0;
910
+ let extensionStartOffset = 0;
911
+ let hasExtensions = false;
912
+ message.message_name;
913
+ for (const field of fields) {
914
+ const fieldSize = this.getFieldSize(field);
915
+ // Check if this is an extension field using proper XML-based detection
916
+ const isExtensionField = field.extension === true;
917
+ if (isExtensionField) {
918
+ if (!hasExtensions) {
919
+ extensionStartOffset = corePayloadSize;
920
+ hasExtensions = true;
921
+ }
922
+ // Don't add extension field sizes to core payload size
923
+ }
924
+ else {
925
+ // This is a core field - always add to core payload size
926
+ corePayloadSize += fieldSize;
927
+ }
928
+ }
929
+ // If there are no extension fields, don't trim at all
930
+ if (!hasExtensions) {
931
+ return fullPayload;
932
+ }
933
+ // Find the last non-zero byte in extension fields only
934
+ let trimmedLength = fullPayload.length;
935
+ // If we have extensions, check if they contain any non-zero bytes
936
+ if (hasExtensions && extensionStartOffset < fullPayload.length) {
937
+ // Look for non-zero bytes in the extension section
938
+ let hasNonZeroExtensions = false;
939
+ for (let i = extensionStartOffset; i < fullPayload.length; i++) {
940
+ if (fullPayload[i] !== 0) {
941
+ hasNonZeroExtensions = true;
942
+ break;
943
+ }
944
+ }
945
+ if (!hasNonZeroExtensions) {
946
+ // All extension fields are zero, trim them all
947
+ trimmedLength = corePayloadSize;
948
+ }
949
+ else {
950
+ // Find the last non-zero byte in extension fields
951
+ for (let i = fullPayload.length - 1; i >= extensionStartOffset; i--) {
952
+ if (fullPayload[i] !== 0) {
953
+ trimmedLength = i + 1;
954
+ break;
955
+ }
956
+ }
957
+ }
958
+ }
959
+ // Never trim below the core payload size
960
+ if (trimmedLength < corePayloadSize) {
961
+ trimmedLength = corePayloadSize;
962
+ }
963
+ // Return trimmed payload if it's shorter than the original
964
+ if (trimmedLength < fullPayload.length) {
965
+ return fullPayload.slice(0, trimmedLength);
966
+ }
967
+ return fullPayload;
968
+ }
969
+ serializeField(view, offset, field, value) {
970
+ const isArray = field.arrayLength !== undefined;
971
+ const arrayLength = field.arrayLength || 1;
972
+ if (isArray && arrayLength > 1) {
973
+ let totalBytes = 0;
974
+ let baseType = field.type;
975
+ if (baseType.includes('[') && baseType.includes(']')) {
976
+ baseType = baseType.substring(0, baseType.indexOf('['));
977
+ }
978
+ // Special handling for char arrays - treat string as char array
979
+ if (baseType === 'char' && typeof value === 'string') {
980
+ const str = value;
981
+ for (let i = 0; i < arrayLength; i++) {
982
+ const charCode = i < str.length ? str.charCodeAt(i) : 0;
983
+ view.setUint8(offset + totalBytes, charCode);
984
+ totalBytes += 1;
985
+ }
986
+ return totalBytes;
987
+ }
988
+ // Handle other array types
989
+ const arrayValue = Array.isArray(value) ? value : [value];
990
+ for (let i = 0; i < arrayLength; i++) {
991
+ const itemValue = i < arrayValue.length ? arrayValue[i] : this.getDefaultValueForType(baseType);
992
+ const bytesWritten = this.serializeSingleValue(view, offset + totalBytes, baseType, itemValue);
993
+ totalBytes += bytesWritten;
994
+ }
995
+ return totalBytes;
996
+ }
997
+ else {
998
+ return this.serializeSingleValue(view, offset, field.type, value);
999
+ }
1000
+ }
1001
+ serializeSingleValue(view, offset, type, value) {
1002
+ const actualValue = value ?? this.getDefaultValueForType(type);
1003
+ switch (type) {
1004
+ case 'uint8_t':
1005
+ view.setUint8(offset, Number(actualValue));
1006
+ return 1;
1007
+ case 'int8_t':
1008
+ view.setInt8(offset, Number(actualValue));
1009
+ return 1;
1010
+ case 'uint16_t':
1011
+ view.setUint16(offset, Number(actualValue), false);
1012
+ return 2;
1013
+ case 'int16_t':
1014
+ view.setInt16(offset, Number(actualValue), true);
1015
+ return 2;
1016
+ case 'uint32_t':
1017
+ view.setUint32(offset, Number(actualValue), true);
1018
+ return 4;
1019
+ case 'int32_t':
1020
+ view.setInt32(offset, Number(actualValue), true);
1021
+ return 4;
1022
+ case 'uint64_t':
1023
+ view.setBigUint64(offset, typeof actualValue === 'bigint' ? actualValue : BigInt(Number(actualValue) || 0), true);
1024
+ return 8;
1025
+ case 'int64_t':
1026
+ view.setBigInt64(offset, typeof actualValue === 'bigint' ? actualValue : BigInt(Number(actualValue) || 0), true);
1027
+ return 8;
1028
+ case 'float':
1029
+ view.setFloat32(offset, Number(actualValue), true);
1030
+ return 4;
1031
+ case 'double':
1032
+ view.setFloat64(offset, Number(actualValue), true);
1033
+ return 8;
1034
+ case 'char':
1035
+ view.setUint8(offset, typeof actualValue === 'string' ? actualValue.charCodeAt(0) : Number(actualValue));
1036
+ return 1;
1037
+ default:
1038
+ if (type.startsWith('char[') && type.endsWith(']')) {
1039
+ const length = parseInt(type.slice(5, -1));
1040
+ const str = String(actualValue);
1041
+ for (let i = 0; i < length; i++) {
1042
+ const charCode = i < str.length ? str.charCodeAt(i) : 0;
1043
+ view.setUint8(offset + i, charCode);
1044
+ }
1045
+ return length;
1046
+ }
1047
+ view.setUint8(offset, Number(actualValue));
1048
+ return 1;
1049
+ }
1050
+ }
1051
+ getFieldSize(field) {
1052
+ if (typeof field === 'string') {
1053
+ // Legacy support for type string
1054
+ if (field.includes('[') && field.includes(']')) {
1055
+ const baseType = field.substring(0, field.indexOf('['));
1056
+ const arrayLength = parseInt(field.substring(field.indexOf('[') + 1, field.indexOf(']')));
1057
+ return this.getSingleFieldSize(baseType) * arrayLength;
1058
+ }
1059
+ return this.getSingleFieldSize(field);
1060
+ }
1061
+ // Handle FieldDefinition object
1062
+ const type = field.type;
1063
+ const arrayLength = field.arrayLength;
1064
+ if (arrayLength && arrayLength > 1) {
1065
+ return this.getSingleFieldSize(type) * arrayLength;
1066
+ }
1067
+ if (type.includes('[') && type.includes(']')) {
1068
+ const baseType = type.substring(0, type.indexOf('['));
1069
+ const parsedArrayLength = parseInt(type.substring(type.indexOf('[') + 1, type.indexOf(']')));
1070
+ return this.getSingleFieldSize(baseType) * parsedArrayLength;
1071
+ }
1072
+ return this.getSingleFieldSize(type);
1073
+ }
1074
+ getSingleFieldSize(type) {
1075
+ switch (type) {
1076
+ case 'uint8_t':
1077
+ case 'int8_t':
1078
+ case 'char':
1079
+ return 1;
1080
+ case 'uint16_t':
1081
+ case 'int16_t':
1082
+ return 2;
1083
+ case 'uint32_t':
1084
+ case 'int32_t':
1085
+ case 'float':
1086
+ return 4;
1087
+ case 'uint64_t':
1088
+ case 'int64_t':
1089
+ case 'double':
1090
+ return 8;
1091
+ default:
1092
+ return 1;
1093
+ }
1094
+ }
1095
+ getDefaultValueForType(type) {
1096
+ switch (type) {
1097
+ case 'uint8_t':
1098
+ case 'int8_t':
1099
+ case 'uint16_t':
1100
+ case 'int16_t':
1101
+ case 'uint32_t':
1102
+ case 'int32_t':
1103
+ case 'float':
1104
+ case 'double':
1105
+ return 0;
1106
+ case 'uint64_t':
1107
+ case 'int64_t':
1108
+ return 0n;
1109
+ case 'char':
1110
+ return 0;
1111
+ default:
1112
+ return 0;
1113
+ }
1114
+ }
1115
+ createMAVLinkFrame(message, messageId, payload) {
1116
+ const systemId = typeof message.system_id === 'number' ? message.system_id : 1;
1117
+ const componentId = typeof message.component_id === 'number' ? message.component_id : 1;
1118
+ const sequence = typeof message.sequence === 'number' ? message.sequence : 0;
1119
+ // Automatically determine protocol version based on message requirements
1120
+ const needsV2 = messageId > 255; // v1 only supports 8-bit message IDs (0-255)
1121
+ const userSpecifiedVersion = typeof message.protocol_version === 'number' ? message.protocol_version : null;
1122
+ // Use user-specified version if provided, otherwise auto-detect
1123
+ const protocolVersion = userSpecifiedVersion !== null ? userSpecifiedVersion : (needsV2 ? 2 : 1);
1124
+ const isV2 = protocolVersion === 2;
1125
+ const magic = isV2 ? 0xFD : 0xFE;
1126
+ // Calculate frame size based on protocol version
1127
+ const headerSize = isV2 ? 10 : 6; // v2 has extra fields
1128
+ const frameSize = headerSize + payload.length + 2;
1129
+ const buffer = new ArrayBuffer(frameSize);
1130
+ const view = new DataView(buffer);
1131
+ let offset = 0;
1132
+ // Header
1133
+ view.setUint8(offset++, magic);
1134
+ view.setUint8(offset++, payload.length);
1135
+ if (isV2) {
1136
+ // MAVLink v2: magic(1) + len(1) + incompat_flags(1) + compat_flags(1) + seq(1) + sysid(1) + compid(1) + msgid(3) + payload + checksum(2)
1137
+ view.setUint8(offset++, 0); // incompat_flags
1138
+ view.setUint8(offset++, 0); // compat_flags
1139
+ view.setUint8(offset++, sequence);
1140
+ view.setUint8(offset++, systemId);
1141
+ view.setUint8(offset++, componentId);
1142
+ // 24-bit message ID in v2
1143
+ view.setUint8(offset++, messageId & 0xFF);
1144
+ view.setUint8(offset++, (messageId >> 8) & 0xFF);
1145
+ view.setUint8(offset++, (messageId >> 16) & 0xFF);
1146
+ }
1147
+ else {
1148
+ // MAVLink v1: magic(1) + len(1) + seq(1) + sysid(1) + compid(1) + msgid(1) + payload + checksum(2)
1149
+ view.setUint8(offset++, sequence);
1150
+ view.setUint8(offset++, systemId);
1151
+ view.setUint8(offset++, componentId);
1152
+ view.setUint8(offset++, messageId & 0xFF); // 8-bit message ID in v1
1153
+ }
1154
+ // Payload
1155
+ const payloadView = new Uint8Array(buffer, offset, payload.length);
1156
+ payloadView.set(payload);
1157
+ offset += payload.length;
1158
+ // Calculate proper MAVLink CRC with CRC_EXTRA
1159
+ const crcExtra = CRC_EXTRA[messageId];
1160
+ if (crcExtra === undefined) {
1161
+ throw new Error("No CRC_EXTRA defined for message ID " + messageId);
1162
+ }
1163
+ // Get message data (exclude start byte and checksum)
1164
+ const messageData = new Uint8Array(buffer, 1, offset - 1);
1165
+ const checksum = MAVLinkCRC.calculate(messageData, crcExtra);
1166
+ // Checksum (little endian)
1167
+ view.setUint8(offset++, checksum & 0xFF);
1168
+ view.setUint8(offset++, (checksum >> 8) & 0xFF);
1169
+ return new Uint8Array(buffer);
1170
+ }
778
1171
  }
779
1172
  const MESSAGE_DEFINITIONS = [
780
1173
  {
@@ -790,19 +1183,19 @@ const MESSAGE_DEFINITIONS = [
790
1183
  type: 'uint8_t',
791
1184
  },
792
1185
  {
793
- name: 'baseMode',
1186
+ name: 'base_mode',
794
1187
  type: 'uint8_t',
795
1188
  },
796
1189
  {
797
- name: 'customMode',
1190
+ name: 'custom_mode',
798
1191
  type: 'uint32_t',
799
1192
  },
800
1193
  {
801
- name: 'systemStatus',
1194
+ name: 'system_status',
802
1195
  type: 'uint8_t',
803
1196
  },
804
1197
  {
805
- name: 'mavlinkVersion',
1198
+ name: 'mavlink_version',
806
1199
  type: 'uint8_t_mavlink_version',
807
1200
  },
808
1201
  ]
@@ -816,21 +1209,21 @@ const MESSAGE_DEFINITIONS = [
816
1209
  type: 'uint16_t',
817
1210
  },
818
1211
  {
819
- name: 'minVersion',
1212
+ name: 'min_version',
820
1213
  type: 'uint16_t',
821
1214
  },
822
1215
  {
823
- name: 'maxVersion',
1216
+ name: 'max_version',
824
1217
  type: 'uint16_t',
825
1218
  },
826
1219
  {
827
- name: 'specVersionHash',
828
- type: 'uint8_t[8]',
1220
+ name: 'spec_version_hash',
1221
+ type: 'uint8_t',
829
1222
  arrayLength: 8,
830
1223
  },
831
1224
  {
832
- name: 'libraryVersionHash',
833
- type: 'uint8_t[8]',
1225
+ name: 'library_version_hash',
1226
+ type: 'uint8_t',
834
1227
  arrayLength: 8,
835
1228
  },
836
1229
  ]
@@ -854,6 +1247,43 @@ class MinimalParser extends DialectParser {
854
1247
  }
855
1248
  }
856
1249
  }
1250
+ // Dialect-specific serializer
1251
+ class MinimalSerializer {
1252
+ constructor() {
1253
+ this.parser = new MinimalParser();
1254
+ }
1255
+ // Serialize a message to MAVLink bytes
1256
+ serialize(message) {
1257
+ return this.parser.serializeMessage(message);
1258
+ }
1259
+ // Complete a message with all defined fields (including extension fields with defaults)
1260
+ // This is useful to see what the serializer would send without actually serializing
1261
+ // Requires payload structure format
1262
+ completeMessage(message) {
1263
+ const messageDef = Array.from(this.parser['messageDefinitions'].values())
1264
+ .find(def => def.name === message.message_name);
1265
+ if (!messageDef) {
1266
+ throw new Error(`Unknown message type: ${message.message_name}`);
1267
+ }
1268
+ // Extract fields from payload structure (throws error if not payload format)
1269
+ const messageFields = this.parser['extractMessageFields'](message, messageDef.fields);
1270
+ // Complete the message with defaults
1271
+ const completedFields = this.parser['completeMessageWithDefaults'](messageFields, messageDef.fields);
1272
+ // Return in the payload structure format
1273
+ return {
1274
+ ...message,
1275
+ payload: completedFields
1276
+ };
1277
+ }
1278
+ // Get supported message names for this dialect
1279
+ getSupportedMessages() {
1280
+ return Array.from(this.parser['messageDefinitions'].values()).map(def => def.name);
1281
+ }
1282
+ // Check if a message is supported by this dialect
1283
+ supportsMessage(messageName) {
1284
+ return Array.from(this.parser['messageDefinitions'].values()).some(def => def.name === messageName);
1285
+ }
1286
+ }
857
1287
 
858
- export { MAV_AUTOPILOTEnum, MAV_COMPONENTEnum, MAV_MODE_FLAGEnum, MAV_MODE_FLAG_DECODE_POSITIONEnum, MAV_STATEEnum, MAV_TYPEEnum, MinimalParser, isHeartbeat, isProtocolVersion };
1288
+ export { MAV_AUTOPILOTEnum, MAV_COMPONENTEnum, MAV_MODE_FLAGEnum, MAV_MODE_FLAG_DECODE_POSITIONEnum, MAV_STATEEnum, MAV_TYPEEnum, MinimalParser, MinimalSerializer, isHeartbeat, isProtocolVersion };
859
1289
  //# sourceMappingURL=index.js.map