-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathVEDTP.cpp
More file actions
3611 lines (3242 loc) · 145 KB
/
Copy pathVEDTP.cpp
File metadata and controls
3611 lines (3242 loc) · 145 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* vedtp.c
*
* Created on: Jun 16, 2025
* Author: Sooraj R
*/
#include "vedtp.h"
uint8_t inputBuffer[MAX_BUFFER_SIZE];
uint8_t bufferIndex = 0;
/**
* @brief Incrementally feed incoming bytes into the VEDTP frame parser.
*
* This function processes a single incoming byte at a time and assembles a
* complete VEDTP frame using an internal buffer. It performs early validation
* of frame start bytes and protocol version before attempting full frame
* parsing and checksum verification.
*
* The function is designed to be called repeatedly from a byte-oriented
* receive context (e.g., UART ISR, DMA callback, socket receive loop).
* It maintains internal parser state across calls and signals parsing progress
* or errors via explicit return codes.
*
* Parsing sequence:
* 1. Validate first start byte (0xFF)
* 2. Validate second start byte (0xEE)
* 3. Validate protocol version
* 4. Accumulate bytes until VEDTP_HEADER_SIZE is reached
* 5. Parse frame and verify checksum
*
* On successful parsing, the decoded frame is copied into the provided
* @ref VEDTP_Main storage structure.
*
* @param[in] rxByte Incoming byte to process.
* @param[out] storage Pointer to a @ref VEDTP_Main structure where the fully
* parsed frame will be stored upon successful completion.
*
* @return uint8_t
* - VEDTP_INCOMPLETE : Frame reception in progress
* - VEDTP_COMPLETE : Valid frame received and parsed
* - VEDTP_INVALID_START_BYTES : First start byte mismatch
* - VEDTP_INVALID_SECOND_BYTES : Second start byte mismatch
* - VEDTP_PROTOCOL_VERSION_MISMATCH: Protocol version mismatch
* - VEDTP_CRC_FAIL : Frame checksum validation failed
*
* @note This function assumes a fixed-size VEDTP frame and relies on
* VEDTP_HEADER_SIZE to determine when parsing should occur.
*
* @note The internal buffer index is reset automatically on error or after
* successful frame completion.
*
* @warning This function is stateful and not re-entrant. It must not be called
* concurrently from multiple contexts without external protection.
*
* @warning Any modification to frame structure, header size, or protocol
* version handling must be reflected here to maintain parser
* correctness.
*/
uint8_t feed_Me_Bytes(uint8_t rxByte, VEDTP_Main *storage) {
if (bufferIndex == 0 && rxByte != 0xFF) {
return VEDTP_INVALID_START_BYTES;
}
inputBuffer[bufferIndex++] = rxByte;
if (bufferIndex == 2 && rxByte != 0xEE) {
bufferIndex = 0;
return VEDTP_INVALID_SECOND_BYTES;
}
if(bufferIndex == 3 && rxByte != PROTOCOL_VERSION) {
bufferIndex = 0;
return VEDTP_PROTOCOL_VERSION_MISMATCH;
}
if (bufferIndex >= VEDTP_HEADER_SIZE) {
if (parseData(inputBuffer,storage)) {
bufferIndex = 0;
return VEDTP_COMPLETE;
}
else {
bufferIndex = 0;
return VEDTP_CRC_FAIL;
}
}
return VEDTP_INCOMPLETE;
}
/**
* @brief Finalize a VEDTP frame by computing and inserting its checksum.
*
* This function calculates the checksum for a populated @ref VEDTP_Main
* structure and writes the result into the frame's checksum field.
*
* The checksum is computed over the fixed-size VEDTP header and payload fields,
* excluding the checksum byte itself. The resulting checksum is used by the
* receiver to verify frame integrity during parsing.
*
* This function must be called only after all header fields, payload contents,
* and payload length have been fully populated.
*
* @param[in,out] data Pointer to the VEDTP frame to finalize.
*
* @note The checksum is calculated using the protocol-defined checksum
* algorithm and assumes a fixed header size defined by
* VEDTP_HEADER_SIZE.
*
* @note This function performs no memory allocation and does not modify any
* fields other than the checksum.
*
* @warning Calling this function before the payload or header is fully
* populated will result in an invalid checksum and a frame that
* will be rejected by the receiver.
*/
void vedtp_pack_data(VEDTP_Main *data)
{
data->checksum = calculateChecksum((const uint8_t *)data, VEDTP_HEADER_SIZE-1);
}
/**
* @brief Parse and validate a complete VEDTP frame from a raw byte buffer.
*
* This function validates and decodes a fully received VEDTP frame from a
* raw byte buffer into a @ref VEDTP_Main structure. It performs basic framing
* validation and checksum verification to ensure data integrity.
*
* The function assumes that the input buffer contains a complete VEDTP frame
* starting at index 0. On successful validation, the frame is copied into the
* provided storage structure.
*
* Validation steps:
* 1. Verify VEDTP start bytes (0xFF, 0xEE)
* 2. Copy raw frame into structured storage
* 3. Recalculate checksum over header and payload (excluding checksum byte)
* 4. Compare calculated checksum with received checksum
*
* @param[in] incoming Pointer to the raw byte buffer containing the VEDTP
* frame data.
* @param[out] storage Pointer to a @ref VEDTP_Main structure where the parsed
* frame will be stored on success.
*
* @return uint8_t
* - 1 : Frame is valid and checksum matches.
* - 0 : Invalid start bytes or checksum mismatch.
*
* @note This function assumes a fixed-size VEDTP frame as defined by
* @ref VEDTP_HEADER_SIZE and the @ref VEDTP_Main structure.
*
* @note The checksum is calculated directly from the raw incoming buffer to
* avoid any side effects from structure padding or alignment.
*
* @warning This function does not perform incremental parsing and must only
* be called once the complete frame has been received.
*
* @warning Any change to frame layout, checksum coverage, or header size must
* be reflected here to maintain protocol correctness.
*/
uint8_t parseData(uint8_t *incoming, VEDTP_Main *storage) {
if (incoming[0] == 0xFF && incoming[1] == 0xEE) {
memcpy(storage, incoming, sizeof(VEDTP_Main));
uint8_t checksum = calculateChecksum((const uint8_t *)incoming, VEDTP_HEADER_SIZE-1);
if (storage->checksum == checksum) {
return 1;
} else {
return 0;
}
}
return 0; // Invalid data
}
/**
* @brief Compute the VEDTP checksum using an 8-bit XOR accumulator.
*
* This function calculates a simple 8-bit checksum by XOR-ing all bytes in
* the provided data buffer. The resulting checksum is used to detect
* transmission errors and data corruption in VEDTP frames.
*
* The checksum algorithm is intentionally lightweight and deterministic,
* making it suitable for real-time embedded systems with constrained
* processing resources.
*
* @param[in] data Pointer to the byte buffer over which the checksum
* will be calculated.
* @param[in] length Number of bytes to include in the checksum calculation.
*
* @return uint8_t
* The computed XOR checksum value.
*
* @note The checksum is order-dependent and sensitive to single-bit errors,
* but it is not cryptographically secure and does not protect against
* intentional tampering.
*
* @note This function performs no memory allocation and has constant time
* complexity relative to the input length.
*
* @warning This checksum provides basic error detection only. For stronger
* integrity guarantees (e.g., burst error detection or security),
* consider replacing or augmenting this algorithm with CRC or
* cryptographic authentication.
*/
uint8_t calculateChecksum(const uint8_t *data, uint8_t length) {
uint8_t checksum = 0;
for (uint8_t i = 0; i < length; i++) {
checksum ^= data[i];
}
return checksum;
}
////////////////////////////////////////////////////////////
//little-endian utility functions
/**
* @brief Write an unsigned 8-bit value to a buffer.
*
* @param b Pointer to destination byte buffer.
* @param o Byte offset into buffer.
* @param v Value to write.
*/
static inline void put_u8(uint8_t *b, uint16_t o, uint8_t v)
{
b[o] = v;
}
/**
* @brief Write an unsigned 16-bit value to a buffer (little-endian).
*
* @param b Pointer to destination byte buffer.
* @param o Byte offset into buffer.
* @param v 16-bit unsigned value to write.
*/
static inline void put_u16_le(uint8_t *b, uint16_t o, uint16_t v)
{
b[o+0] = (uint8_t)(v);
b[o+1] = (uint8_t)(v >> 8);
}
/**
* @brief Write a signed 16-bit value to a buffer (little-endian).
*
* @param b Pointer to destination byte buffer.
* @param o Byte offset into buffer.
* @param v 16-bit signed value to write.
*/
static inline void put_i16_le(uint8_t *b, uint16_t o, int16_t v)
{
put_u16_le(b, o, (uint16_t)v);
}
/**
* @brief Write an unsigned 32-bit value to a buffer (little-endian).
*
* @param b Pointer to destination byte buffer.
* @param o Byte offset into buffer.
* @param v 32-bit unsigned value to write.
*/
static inline void put_u32_le(uint8_t *b, uint16_t o, uint32_t v)
{
b[o+0] = (uint8_t)(v);
b[o+1] = (uint8_t)(v >> 8);
b[o+2] = (uint8_t)(v >> 16);
b[o+3] = (uint8_t)(v >> 24);
}
/**
* @brief Write a signed 32-bit value to a buffer (little-endian).
*
* @param b Pointer to destination byte buffer.
* @param o Byte offset into buffer.
* @param v 32-bit signed value to write.
*/
static inline void put_i32_le(uint8_t *b, uint16_t o, int32_t v)
{
put_u32_le(b, o, (uint32_t)v);
}
/**
* @brief Read an unsigned 8-bit value from a buffer.
*
* @param b Pointer to source byte buffer.
* @param o Byte offset into buffer.
* @return Unsigned 8-bit value.
*/
static inline uint8_t get_u8(const uint8_t *b, uint16_t o)
{
return b[o];
}
/**
* @brief Read an unsigned 16-bit value from a buffer (little-endian).
*
* @param b Pointer to source byte buffer.
* @param o Byte offset into buffer.
* @return Unsigned 16-bit value.
*/
static inline uint16_t get_u16_le(const uint8_t *b, uint16_t o)
{
return (uint16_t)(b[o] | (b[o+1] << 8));
}
/**
* @brief Read a signed 16-bit value from a buffer (little-endian).
*
* @param b Pointer to source byte buffer.
* @param o Byte offset into buffer.
* @return Signed 16-bit value.
*/
static inline int16_t get_i16_le(const uint8_t *b, uint16_t o)
{
return (int16_t)get_u16_le(b, o);
}
/**
* @brief Read an unsigned 32-bit value from a buffer (little-endian).
*
* @param b Pointer to source byte buffer.
* @param o Byte offset into buffer.
* @return Unsigned 32-bit value.
*/
static inline uint32_t get_u32_le(const uint8_t *b, uint16_t o)
{
return (uint32_t)(
((uint32_t)b[o+0]) |
((uint32_t)b[o+1] << 8) |
((uint32_t)b[o+2] << 16) |
((uint32_t)b[o+3] << 24)
);
}
/**
* @brief Read a signed 32-bit value from a buffer (little-endian).
*
* @param b Pointer to source byte buffer.
* @param o Byte offset into buffer.
* @return Signed 32-bit value.
*/
static inline int32_t get_i32_le(const uint8_t *b, uint16_t o)
{
return (int32_t)get_u32_le(b, o);
}
//////////////////////////////////////////////////////////// encode_ data
/**
* @brief Encode HEARTBEAT message into a VEDTP transport frame.
*
* This function serializes a @ref HEARTBEAT structure into the payload
* of a @ref VEDTP_Main packet using a fixed, little-endian wire format.
* It initializes the protocol header fields, clears the payload buffer,
* encodes all heartbeat fields at predefined offsets, and finalizes the
* packet by invoking the VEDTP packing routine.
*
* The encoded HEARTBEAT payload layout is strictly defined and verified
* at compile time to guarantee binary compatibility across firmware,
* ground control software, and logging tools.
*
* Payload format (byte offsets):
* - [0] : Logged state (uint8_t, 0 = not logged, 1 = logged)
* - [1] : Vehicle ID (enum VehicleID)
* - [2] : Armed state (uint8_t, 0 = disarmed, 1 = armed)
* - [3] : System mode (enum SystemMode)
* - [4] : GPS fix status (enum GPSFixStatus)
* - [5] : Failsafe flags (bitmask)
* - [6] : System health (enum SystemHealth)
* - [7] : Sensors validity flags (uint16_t, little-endian)
* - [9] : System uptime (uint32_t, little-endian, ms)
*
* @param[out] out Pointer to the VEDTP packet to be populated.
* @param[in] data Pointer to the HEARTBEAT data source.
*
* @return uint8_t
* - 1 : Encoding successful and packet is ready for transmission.
* - 0 : Invalid arguments (NULL pointer).
*
* @note This function performs no dynamic memory allocation and is safe
* for use in real-time contexts when called outside of ISR scope.
*
* @note Endianness of multi-byte fields is explicitly defined as
* little-endian using helper functions to ensure cross-platform
* compatibility.
*
* @warning The payload size is fixed. Any change to the HEARTBEAT structure
* or payload layout must be accompanied by an update to
* HEARTBEAT_SIZE and the corresponding static assertion.
*/
uint8_t encode_HEARTBEAT(VEDTP_Main *out, const HEARTBEAT_Packet *data)
{
if (!out || !data) {
return 0;
}
out->startByte1 = 0xFF;
out->startByte2 = 0xEE;
out->protocol_version = PROTOCOL_VERSION;
out->vehicle = VEHICLE_ROV6;
out->device = DEVICE_HEARTBEAT;
memset(out->payload, 0, sizeof(out->payload));
// payload
out->payload[0] = data->loggedState ? 1 : 0;
out->payload[1] = data->vehicleID;
out->payload[2] = data->armedState ? 1 : 0;
out->payload[3] = data->systemMode;
out->payload[4] = data->gps_fix;
out->payload[5] = data->failsafe_flags;
out->payload[6] = data->system_health;
put_u16_le(out->payload, 7, data->sensors_validity);
put_u32_le(out->payload, 9, data->uptime_ms);
out->payload_length = HEARTBEAT_SIZE;
////_Static_assert(HEARTBEAT_SIZE == 13, "HEARTBEAT_SIZE mismatch");
vedtp_pack_data(out);
return 1;
}
/**
* @brief Encode AHRS attitude data into a VEDTP transport frame.
*
* This function serializes attitude and timing data from an @ref AHRS_Packet
* into the payload of a @ref VEDTP_Main packet using a fixed, little-endian
* wire format. The resulting packet is fully initialized with protocol
* header fields and finalized with a checksum for transmission.
*
* Attitude angles are scaled and clamped to ensure deterministic encoding:
* - Yaw is normalized to the range [0, 360) degrees and encoded as an
* unsigned 16-bit value with 0.01° resolution.
* - Pitch and Roll are encoded as signed 16-bit values with 0.01° resolution
* and are clamped to the representable range of int16_t.
*
* Payload layout (byte offsets):
* - [0..1] : Yaw angle (uint16_t, degrees × 100, little-endian)
* - [2..3] : Pitch angle (int16_t, degrees × 100, little-endian)
* - [4..5] : Roll angle (int16_t, degrees × 100, little-endian)
* - [6..9] : System uptime (uint32_t, milliseconds, little-endian)
*
* The payload size is fixed and verified at compile time to maintain
* binary compatibility across firmware and ground control software.
*
* @param[out] out Pointer to the VEDTP packet to be populated.
* @param[in] data Pointer to the AHRS data source (angles in degrees).
*
* @return uint8_t
* - 1 : Encoding successful and packet is ready for transmission.
* - 0 : Invalid arguments (NULL pointer).
*
* @note This function performs no dynamic memory allocation and is suitable
* for use in real-time systems when called outside interrupt context.
*
* @note All multi-byte fields are explicitly encoded in little-endian order
* to ensure cross-platform interoperability.
*
* @warning Any modification to the AHRS payload layout or scaling factors
* must be reflected in AHRS_SIZE and validated with the static
* assertion to prevent protocol incompatibility.
*/
uint8_t encode_AHRS(VEDTP_Main *out, const AHRS_Packet *data)
{
if (!out || !data) {
return 0;
}
out->startByte1 = 0xFF;
out->startByte2 = 0xEE;
out->protocol_version = PROTOCOL_VERSION;
out->vehicle = VEHICLE_ROV6;
out->device = DEVICE_AHRS;
memset(out->payload, 0, sizeof(out->payload));
// YAW
float yaw = data->yaw_deg;
while (yaw < 0.0f) yaw += 360.0f;
while (yaw >= 360.0f) yaw -= 360.0f;
float yaw_scaled = yaw * f_e2;
if (yaw_scaled > 36000.0f) yaw_scaled = 36000.0f;
if (yaw_scaled < 0.0f) yaw_scaled = 0.0f;
put_u16_le(out->payload, 0, (uint16_t)yaw_scaled);
// PITCH
float pitch_scaled = data->pitch_deg * f_e2;
if (pitch_scaled > INT16_MAX) pitch_scaled = INT16_MAX;
if (pitch_scaled < INT16_MIN) pitch_scaled = INT16_MIN;
put_i16_le(out->payload, 2, (int16_t)pitch_scaled);
//ROLL
float roll_scaled = data->roll_deg * f_e2;
if (roll_scaled > INT16_MAX) roll_scaled = INT16_MAX;
if (roll_scaled < INT16_MIN) roll_scaled = INT16_MIN;
put_i16_le(out->payload, 4, (int16_t)roll_scaled);
//UPTIME
put_u32_le(out->payload, 6, data->uptime_ms);
out->payload_length = AHRS_SIZE;
//_Static_assert(AHRS_SIZE == 10, "AHRS_SIZE mismatch");
vedtp_pack_data(out);
return 1;
}
/**
* @brief Encode GPS navigation data into a VEDTP transport frame.
*
* This function serializes position, velocity, accuracy, and timing data
* from a @ref GPS_Packet into the payload of a @ref VEDTP_Main packet using
* a fixed, little-endian wire format. The packet header is initialized,
* the payload is populated at predefined offsets, and the frame is finalized
* with a checksum for transmission.
*
* All floating-point inputs are converted to scaled integer representations
* to ensure deterministic, platform-independent encoding:
* - Latitude and Longitude are encoded as signed int32 values in degrees × 1e7.
* - Altitude is encoded as signed int32 in millimeters.
* - Ground speed is encoded as unsigned uint16 in m/s × 100.
* - Heading is normalized to [0, 360) degrees and encoded as uint16 × 100.
* - Horizontal and Vertical accuracies are encoded as uint32 in millimeters × 100.
*
* Payload layout (byte offsets):
* - [0..3] : Time of week (uint32_t, milliseconds)
* - [4..7] : Latitude (int32_t, deg × 1e7)
* - [8..11] : Longitude (int32_t, deg × 1e7)
* - [12..15] : Altitude (int32_t, millimeters)
* - [16..17] : Ground speed (uint16_t, m/s × 100)
* - [18..19] : Heading (uint16_t, degrees × 100)
* - [20..23] : Horizontal accuracy (uint32_t, mm × 100)
* - [24..27] : Vertical accuracy (uint32_t, mm × 100)
* - [28..31] : Data age (uint32_t, milliseconds)
* - [32] : Fix type (enum GPSFixStatus)
* - [33] : Satellites in view (uint8_t)
* - [34] : GPS flags (bitmask)
* - [35..38] : System uptime (uint32_t, milliseconds)
*
* The payload size is fixed and verified at compile time to preserve binary
* compatibility between firmware, ground control software, and log decoders.
*
* @param[out] out Pointer to the VEDTP packet to be populated.
* @param[in] data Pointer to the GPS data source (values in SI units).
*
* @return uint8_t
* - 1 : Encoding successful and packet is ready for transmission.
* - 0 : Invalid arguments (NULL pointer).
*
* @note This function performs no dynamic memory allocation and is suitable
* for real-time systems when called outside interrupt context.
*
* @note All multi-byte fields are explicitly encoded in little-endian order
* to ensure cross-platform interoperability.
*
* @warning Any modification to payload layout, scaling factors, or field
* ordering must be reflected in GPS_SIZE and validated via the
* static assertion to avoid protocol incompatibility.
*/
uint8_t encode_GPS(VEDTP_Main *out, const GPS_Packet *data)
{
if (!out || !data) return 0;
out->startByte1 = 0xFF;
out->startByte2 = 0xEE;
out->protocol_version = PROTOCOL_VERSION;
out->vehicle = VEHICLE_ROV6;
out->device = DEVICE_GPS;
memset(out->payload, 0, sizeof(out->payload));
/* ---- time of week ---- */
put_u32_le(out->payload, 0, data->tow_ms);
/* ---- latitude / longitude ---- */
double lat_e7 = data->lat_deg * 1e7;
double lon_e7 = data->lon_deg * 1e7;
if (lat_e7 > 2147483647.0) lat_e7 = 2147483647.0;
if (lat_e7 < -2147483648.0) lat_e7 = -2147483648.0;
if (lon_e7 > 2147483647.0) lon_e7 = 2147483647.0;
if (lon_e7 < -2147483648.0) lon_e7 = -2147483648.0;
put_i32_le(out->payload, 4, (int32_t)lat_e7);
put_i32_le(out->payload, 8, (int32_t)lon_e7);
/* ---- altitude (meters → mm) ---- */
float alt_mm = data->alt_m * 1000.0f;
put_i32_le(out->payload, 12, (int32_t)alt_mm);
/* ---- ground speed (m/s * 1e2) ---- */
float speed_e2 = data->ground_speed_mps * 100.0f;
if (speed_e2 < 0.0f) speed_e2 = 0.0f;
if (speed_e2 > 65535.0f) speed_e2 = 65535.0f;
put_u16_le(out->payload, 16, (uint16_t)speed_e2);
/* ---- heading (deg * 1e2) ---- */
float heading = data->heading_deg;
while (heading < 0.0f) heading += 360.0f;
while (heading >= 360.0f) heading -= 360.0f;
put_u16_le(out->payload, 18, (uint16_t)(heading * 100.0f));
/* ---- accuracies (mm * 1e2) ---- */
put_u32_le(out->payload, 20, (uint32_t)(data->hAcc_mm * 100.0f));
put_u32_le(out->payload, 24, (uint32_t)(data->vAcc_mm * 100.0f));
/* ---- age ---- */
put_u32_le(out->payload, 28, data->age_ms);
/* ---- status ---- */
out->payload[32] = data->fix_type;
out->payload[33] = data->sats;
out->payload[34] = data->flags;
/* ---- uptime ---- */
put_u32_le(out->payload, 35, data->uptime_ms);
out->payload_length = GPS_SIZE;
//_Static_assert(GPS_SIZE == 39, "GPS_SIZE mismatch");
vedtp_pack_data(out);
return 1;
}
/**
* @brief Encode raw IMU sensor data into a VEDTP transport frame.
*
* This function serializes inertial, magnetic, and environmental sensor data
* from an @ref IMU_Packet into the payload of a @ref VEDTP_Main packet using a
* fixed, little-endian wire format. The packet header is initialized, the IMU
* payload is populated sequentially, and the frame is finalized with a checksum
* for transmission.
*
* All vector quantities are encoded as signed 16-bit integers with a fixed
* scaling factor (×1e3) to ensure deterministic, platform-independent encoding.
* Values are assumed to be provided in SI units and are clamped implicitly by
* the conversion macro where applicable.
*
* Payload layout (byte offsets, little-endian):
* - [0..5] : Acceleration vector (X, Y, Z) int16_t × 1e3
* - [6..11] : Angular rate (gyro) (X, Y, Z) int16_t × 1e3
* - [12..17] : Magnetic field (X, Y, Z) int16_t × 1e3
* - [18..23] : Rotation vector (X, Y, Z) int16_t × 1e3
* - [24..29] : Linear acceleration (X, Y, Z) int16_t × 1e3
* - [30..35] : Gravity vector (X, Y, Z) int16_t × 1e3
* - [36..37] : Temperature (int16_t, °C × 100)
* - [38] : System calibration status (uint8_t)
* - [39] : Gyroscope calibration (uint8_t)
* - [40] : Accelerometer calibration (uint8_t)
* - [41] : Magnetometer calibration (uint8_t)
* - [42..45] : System uptime (uint32_t, milliseconds)
*
* The payload size is fixed and verified at compile time to maintain binary
* compatibility between firmware, ground control software, and log decoders.
*
* @param[out] out Pointer to the VEDTP packet to be populated.
* @param[in] data Pointer to the IMU data source structure.
*
* @return uint8_t
* - 1 : Encoding successful and packet is ready for transmission.
* - 0 : Invalid arguments (NULL pointer).
*
* @note This function performs no dynamic memory allocation and is suitable
* for use in real-time systems when called outside interrupt context.
*
* @note All multi-byte fields are explicitly encoded in little-endian order
* to ensure cross-platform interoperability.
*
* @warning Any modification to the IMU payload layout, scaling factors, or
* ordering must be reflected in IMU_SIZE and validated using the
* static assertion to avoid protocol incompatibility.
*/
uint8_t encode_IMU(VEDTP_Main *out, const IMU_Packet *data)
{
if (!out || !data) return 0;
out->startByte1 = 0xFF;
out->startByte2 = 0xEE;
out->protocol_version = PROTOCOL_VERSION;
out->vehicle = VEHICLE_ROV6;
out->device = DEVICE_IMU;
memset(out->payload, 0, sizeof(out->payload));
uint16_t o = 0;
// accel
for (int i = 0; i < 3; i++) {
put_i16_le(out->payload, o, F_TO_I16_E3(data->acceleration[i]));
o += 2;
}
// gyro
for (int i = 0; i < 3; i++) {
put_i16_le(out->payload, o, F_TO_I16_E3(data->gyro[i]));
o += 2;
}
// magnetic
for (int i = 0; i < 3; i++) {
put_i16_le(out->payload, o, F_TO_I16_E3(data->magnetic[i]));
o += 2;
}
// rotation vector
for (int i = 0; i < 3; i++) {
put_i16_le(out->payload, o, F_TO_I16_E3(data->rotationvector[i]));
o += 2;
}
// linear acceleration
for (int i = 0; i < 3; i++) {
put_i16_le(out->payload, o, F_TO_I16_E3(data->linearacceleration[i]));
o += 2;
}
// gravity
for (int i = 0; i < 3; i++) {
put_i16_le(out->payload, o, F_TO_I16_E3(data->gravity[i]));
o += 2;
}
// temperature (C)
float temp_e2 = data->temperature_C * 100.0f;
if (temp_e2 > INT16_MAX) temp_e2 = INT16_MAX;
if (temp_e2 < INT16_MIN) temp_e2 = INT16_MIN;
put_i16_le(out->payload, o, (int16_t)temp_e2);
o += 2;
// calibration flags
out->payload[o++] = data->systemcalibration;
out->payload[o++] = data->gyrocalibration;
out->payload[o++] = data->accelerometercalibration;
out->payload[o++] = data->magnetometercalibration;
// uptime
put_u32_le(out->payload, o, data->uptime_ms);
o += 4;
out->payload_length = IMU_SIZE;
//_Static_assert(IMU_SIZE == 46, "IMU_SIZE mismatch");
vedtp_pack_data(out);
return 1;
}
/**
* @brief Encode internal atmosphere sensor data into a VEDTP transport frame.
*
* This function serializes environmental data measured inside the vehicle hull
* from an @ref INTERNAL_ATMOSPHERE_Packet into the payload of a
* @ref VEDTP_Main packet using a fixed, little-endian wire format. The packet
* header is initialized, the payload fields are populated at predefined offsets,
* and the frame is finalized with a checksum for transmission.
*
* All floating-point inputs are converted to scaled integer representations
* to ensure deterministic, platform-independent encoding:
* - Temperature is encoded as signed int16 in °C × 100.
* - Relative humidity is encoded as unsigned uint16 in %RH × 100.
* - Pressure is encoded as unsigned uint32 in mbar × 100.
* - TVOC concentration is encoded as unsigned uint16 in ppb.
* - eCO₂ concentration is encoded as unsigned uint16 in ppm.
*
* Boolean and status fields are encoded explicitly to avoid ambiguity on the
* wire. All values are range-clamped prior to encoding to prevent overflow.
*
* Payload layout (byte offsets, little-endian):
* - [0..1] : Internal temperature (int16_t, °C × 100)
* - [2..3] : Internal humidity (uint16_t, %RH × 100)
* - [4..7] : Internal pressure (uint32_t, mbar × 100)
* - [8..9] : TVOC concentration (uint16_t, ppb)
* - [10..11] : eCO₂ concentration (uint16_t, ppm)
* - [12] : Leak status (uint8_t, 0 = no leak, 1 = leak)
* - [13..16] : System uptime (uint32_t, milliseconds)
*
* The payload size is fixed and verified at compile time to preserve binary
* compatibility across firmware versions, ground control software, and log
* decoders.
*
* @param[out] out Pointer to the VEDTP packet to be populated.
* @param[in] data Pointer to the internal atmosphere data source structure.
*
* @return uint8_t
* - 1 : Encoding successful and packet is ready for transmission.
* - 0 : Invalid arguments (NULL pointer).
*
* @note This function performs no dynamic memory allocation and is suitable
* for real-time systems when called outside interrupt context.
*
* @note All multi-byte fields are explicitly encoded in little-endian order
* to ensure cross-platform interoperability.
*
* @warning Any modification to the internal atmosphere payload layout, scaling
* factors, or field ordering must be reflected in
* INTERNAL_ATMOSPHERE_SIZE and validated using the static assertion
* to avoid protocol incompatibility.
*/
uint8_t encode_INTERNAL_ATMOSPHERE( VEDTP_Main *out, const INTERNAL_ATMOSPHERE_Packet *data)
{
if (!out || !data) return 0;
out->startByte1 = 0xFF;
out->startByte2 = 0xEE;
out->protocol_version = PROTOCOL_VERSION;
out->vehicle = VEHICLE_ROV6;
out->device = DEVICE_INTERNAL_ATMOSPHERE;
memset(out->payload, 0, sizeof(out->payload));
/* ---- temperature (°C * 1e2) ---- */
float temp_e2 = data->internal_temp_C * 100.0f;
if (temp_e2 > INT16_MAX) temp_e2 = INT16_MAX;
if (temp_e2 < INT16_MIN) temp_e2 = INT16_MIN;
put_i16_le(out->payload, 0, (int16_t)temp_e2);
/* ---- humidity (%RH * 1e2) ---- */
float hum_e2 = data->internal_humidity_RH * 100.0f;
if (hum_e2 < 0.0f) hum_e2 = 0.0f;
if (hum_e2 > 65535.0f) hum_e2 = 65535.0f;
put_u16_le(out->payload, 2, (uint16_t)hum_e2);
/* ---- pressure (mbar * 1e2) ---- */
float pres_e2 = data->internal_pressure_mbar * 100.0f;
if (pres_e2 < 0.0f) pres_e2 = 0.0f;
put_u32_le(out->payload, 4, (uint32_t)pres_e2);
/* ---- TVOC (ppb, no scale) ---- */
float tvoc = data->internal_tvoc_ppb;
if (tvoc < 0.0f) tvoc = 0.0f;
if (tvoc > 65535.0f) tvoc = 65535.0f;
put_u16_le(out->payload, 8, (uint16_t)tvoc);
/* ---- eCO2 (ppm, no scale) ---- */
float eco2 = data->internal_eco2_ppm;
if (eco2 < 0.0f) eco2 = 0.0f;
if (eco2 > 65535.0f) eco2 = 65535.0f;
put_u16_le(out->payload, 10, (uint16_t)eco2);
/* ---- leak status ---- */
out->payload[12] = data->leak_status ? 1 : 0;
/* ---- uptime ---- */
put_u32_le(out->payload, 13, data->uptime_ms);
out->payload_length = INTERNAL_ATMOSPHERE_SIZE;
static_assert(INTERNAL_ATMOSPHERE_SIZE == 17,
"INTERNAL_ATMOSPHERE_SIZE mismatch");
vedtp_pack_data(out);
return 1;
}
/**
* @brief Encode external atmosphere and marine environment data into a VEDTP transport frame.
*
* This function serializes environmental data measured outside the vehicle
* (atmospheric and marine parameters) from an @ref EXTERNAL_ATMOSPHERE_Packet
* into the payload of a @ref VEDTP_Main packet using a fixed, little-endian
* wire format. The packet header is initialized, payload fields are written
* at predefined offsets, and the frame is finalized with a checksum.
*
* All floating-point inputs are converted to scaled integer representations
* to ensure deterministic, platform-independent encoding suitable for
* real-time telemetry and logging.
*
* Payload layout (byte offsets, little-endian):
* - [0..1] : External temperature (int16_t, °C × 100)
* - [2..5] : External pressure (uint32_t, mbar × 10)
* - [6..7] : External humidity (uint16_t, %RH × 100)
* - [8..11] : Altitude (int32_t, meters × 100)
* - [12..13] : TVOC concentration (uint16_t, ppb)
* - [14..15] : eCO₂ concentration (uint16_t, ppm)
* - [16..17] : Air quality index (uint16_t)
* - [18..21] : Water depth (int32_t, meters × 100)
* - [22..23] : Salinity (uint16_t, ppt × 100)
* - [24..27] : Water conductivity (uint32_t, µS/cm)
* - [28..29] : Dissolved oxygen (uint16_t, mg/L × 100)
* - [30..31] : Turbidity (uint16_t, NTU × 100)
* - [32] : Sensor status flags (uint8_t, bitmask)
* - [33] : Medium type (uint8_t, air / water)
* - [34..37] : System uptime (uint32_t, milliseconds)
*
* The payload size is fixed and verified at compile time to guarantee binary
* compatibility between firmware, ground control software, and log decoders.
*
* @param[out] out Pointer to the VEDTP packet to be populated.
* @param[in] data Pointer to the external atmosphere data source structure.
*
* @return uint8_t
* - 1 : Encoding successful and packet is ready for transmission.
* - 0 : Invalid arguments (NULL pointer).
*
* @note This function performs no dynamic memory allocation and is suitable
* for real-time systems when called outside interrupt context.
*
* @note All multi-byte fields are explicitly encoded in little-endian order
* to ensure cross-platform interoperability.
*
* @warning Any modification to the external atmosphere payload layout,
* scaling factors, or field ordering must be reflected in
* EXTERNAL_ATMOSPHERE_SIZE and validated using the static assertion
* to avoid protocol incompatibility.
*/
uint8_t encode_EXTERNAL_ATMOSPHERE( VEDTP_Main *out, const EXTERNAL_ATMOSPHERE_Packet *data)
{
if (!out || !data) return 0;
out->startByte1 = 0xFF;
out->startByte2 = 0xEE;
out->protocol_version = PROTOCOL_VERSION;
out->vehicle = VEHICLE_ROV6;
out->device = DEVICE_EXTERNAL_ATMOSPHERE;
memset(out->payload, 0, sizeof(out->payload));
// temperature °C * e2
put_i16_le(out->payload, 0, (int16_t)(data->temperature_C * 100.0f));
// pressure mbar * e1
put_u32_le(out->payload, 2, (uint32_t)(data->pressure_mbar * 10.0f));
// humidity % * e2
put_u16_le(out->payload, 6, (uint16_t)(data->humidity_RH * 100.0f));
// altitude m * e2
put_i32_le(out->payload, 8, (int32_t)(data->altitude_m * 100.0f));
// air quality
put_u16_le(out->payload, 12, (uint16_t)data->tvoc_ppb);
put_u16_le(out->payload, 14, (uint16_t)data->eco2_ppm);
put_u16_le(out->payload, 16, data->air_quality_index);
// marine
put_i32_le(out->payload, 18, (int32_t)(data->depth_m * 100.0f));
put_u16_le(out->payload, 22, (uint16_t)(data->salinity_ppt * 100.0f));
put_u32_le(out->payload, 24, (uint32_t)data->conductivity_uS);
put_u16_le(out->payload, 28, (uint16_t)(data->water_dissolved_oxygen_mgL * 100.0f));
put_u16_le(out->payload, 30, (uint16_t)(data->turbidity_NTU * 100.0f));
// status
out->payload[32] = data->sensor_status_flags;
out->payload[33] = data->medium_type;
// uptime
put_u32_le(out->payload, 34, data->uptime_ms);
out->payload_length = EXTERNAL_ATMOSPHERE_SIZE;
static_assert(EXTERNAL_ATMOSPHERE_SIZE == 38, "EXTERNAL_ATMOSPHERE_SIZE mismatch");
vedtp_pack_data(out);
return 1;
}
/**
* @brief Encode sonar ranging data into a VEDTP transport frame.
*
* This function serializes acoustic ranging and quality metrics from a
* @ref SONAR_Packet into the payload of a @ref VEDTP_Main packet using a
* fixed, little-endian wire format. The packet header is initialized,
* payload fields are populated at predefined offsets, and the frame is
* finalized with a checksum for transmission.
*
* Floating-point inputs are converted to scaled integer representations
* to ensure deterministic, platform-independent encoding suitable for
* real-time telemetry and logging.
*
* Payload layout (byte offsets, little-endian):
* - [0] : Sonar type (uint8_t)
* - [1] : Sonar identifier (uint8_t)
* - [2..3] : Range (uint16_t, meters × 100)
* - [4..5] : Signal strength (uint16_t, normalized × 1000)
* - [6..7] : Measurement confidence (uint16_t, normalized × 1000)
* - [8..11] : System uptime (uint32_t, milliseconds)
*
* All scalar values are range-clamped prior to encoding to prevent overflow
* and to guarantee a bounded wire representation.
*
* The payload size is fixed and verified at compile time to preserve binary
* compatibility across firmware versions, ground control software, and
* log decoders.
*
* @param[out] out Pointer to the VEDTP packet to be populated.
* @param[in] data Pointer to the sonar data source structure.
*
* @return uint8_t
* - 1 : Encoding successful and packet is ready for transmission.
* - 0 : Invalid arguments (NULL pointer).
*
* @note This function performs no dynamic memory allocation and is suitable
* for real-time systems when called outside interrupt context.
*
* @note All multi-byte fields are explicitly encoded in little-endian order
* to ensure cross-platform interoperability.
*