Skip to content

File syn_dlt645.c

File List > proto > syn_dlt645.c

Go to the documentation of this file

#include "syn_dlt645.h"

#include "../util/syn_assert.h"

#include <string.h>

/* ── Checksum Calculation ────────────────────────────────────────────────── */

uint8_t syn_dlt645_calc_checksum(const uint8_t *buf, size_t len)
{
    if (buf == NULL || len == 0) {
        return 0;
    }

    uint32_t sum = 0;
    for (size_t i = 0; i < len; i++) {
        sum += buf[i];
    }
    return (uint8_t)(sum & 0xFF);
}

/* ── Encoder ────────────────────────────────────────────────────────────── */

size_t syn_dlt645_encode(const SYN_DLT645_Frame *frame, uint8_t *out_buf, size_t out_capacity)
{
    if (frame == NULL || out_buf == NULL) {
        return 0;
    }

    size_t di_len = (frame->version == SYN_DLT645_VER_1997) ? 2 : 4;
    size_t data_len = di_len + frame->payload_len;
    size_t total_len = 10 + data_len + 2; /* 10 header + data_len + 1 CS + 1 EOF */

    if (out_capacity < total_len) {
        return 0;
    }

    size_t idx = 0;
    out_buf[idx++] = SYN_DLT645_SOF;

    /* 6-byte address */
    memcpy(&out_buf[idx], frame->address, SYN_DLT645_ADDR_LEN);
    idx += SYN_DLT645_ADDR_LEN;

    out_buf[idx++] = SYN_DLT645_SOF;
    out_buf[idx++] = frame->control;
    out_buf[idx++] = (uint8_t)data_len;

    /* Encode Data Identifier (DI) with +0x33 offset (little-endian) */
    if (frame->version == SYN_DLT645_VER_1997) {
        out_buf[idx++] = (uint8_t)((frame->data_id & 0xFF) + SYN_DLT645_OFFSET);
        out_buf[idx++] = (uint8_t)(((frame->data_id >> 8) & 0xFF) + SYN_DLT645_OFFSET);
    } else {
        out_buf[idx++] = (uint8_t)((frame->data_id & 0xFF) + SYN_DLT645_OFFSET);
        out_buf[idx++] = (uint8_t)(((frame->data_id >> 8) & 0xFF) + SYN_DLT645_OFFSET);
        out_buf[idx++] = (uint8_t)(((frame->data_id >> 16) & 0xFF) + SYN_DLT645_OFFSET);
        out_buf[idx++] = (uint8_t)(((frame->data_id >> 24) & 0xFF) + SYN_DLT645_OFFSET);
    }

    /* Encode Payload with +0x33 offset */
    for (uint8_t i = 0; i < frame->payload_len; i++) {
        out_buf[idx++] = (uint8_t)(frame->payload[i] + SYN_DLT645_OFFSET);
    }

    /* Modulo-256 Checksum over bytes 0 to idx-1 */
    out_buf[idx] = syn_dlt645_calc_checksum(out_buf, idx);
    idx++;

    out_buf[idx++] = SYN_DLT645_EOF;

    return idx;
}

/* ── Parser ─────────────────────────────────────────────────────────────── */

SYN_Status syn_dlt645_parse(const uint8_t *in_buf, size_t len, SYN_DLT645_Ver version,
                            SYN_DLT645_Frame *out_frame)
{
    if (in_buf == NULL || out_frame == NULL || len < 12) {
        return SYN_INVALID_PARAM;
    }

    /* Strip leading preambles (0xFE) */
    size_t start = 0;
    while (start < len && in_buf[start] == SYN_DLT645_PREAMBLE) {
        start++;
    }

    size_t rem = len - start;
    if (rem < 12) {
        return SYN_INVALID_PARAM;
    }

    const uint8_t *buf = &in_buf[start];

    /* Validate SOF1 and SOF2 */
    if (buf[0] != SYN_DLT645_SOF || buf[7] != SYN_DLT645_SOF) {
        return SYN_ERROR;
    }

    uint8_t data_len = buf[9];
    size_t di_len = (version == SYN_DLT645_VER_1997) ? 2 : 4;

    if (data_len < di_len) {
        return SYN_ERROR;
    }

    size_t total_frame_len = 10 + data_len + 2;
    if (rem < total_frame_len) {
        return SYN_INVALID_PARAM;
    }

    /* Validate EOF */
    if (buf[total_frame_len - 1] != SYN_DLT645_EOF) {
        return SYN_ERROR;
    }

    /* Validate Checksum */
    uint8_t calc_cs = syn_dlt645_calc_checksum(buf, total_frame_len - 2);
    uint8_t frame_cs = buf[total_frame_len - 2];
    if (calc_cs != frame_cs) {
        return SYN_ERROR;
    }

    /* Populate output frame struct */
    memset(out_frame, 0, sizeof(*out_frame));
    out_frame->version = version;
    memcpy(out_frame->address, &buf[1], SYN_DLT645_ADDR_LEN);
    out_frame->control = buf[8];

    /* Decode Data Identifier (subtract 0x33) */
    if (version == SYN_DLT645_VER_1997) {
        uint8_t di0 = (uint8_t)(buf[10] - SYN_DLT645_OFFSET);
        uint8_t di1 = (uint8_t)(buf[11] - SYN_DLT645_OFFSET);
        out_frame->data_id = (uint32_t)di0 | ((uint32_t)di1 << 8);
    } else {
        uint8_t di0 = (uint8_t)(buf[10] - SYN_DLT645_OFFSET);
        uint8_t di1 = (uint8_t)(buf[11] - SYN_DLT645_OFFSET);
        uint8_t di2 = (uint8_t)(buf[12] - SYN_DLT645_OFFSET);
        uint8_t di3 = (uint8_t)(buf[13] - SYN_DLT645_OFFSET);
        out_frame->data_id =
            (uint32_t)di0 | ((uint32_t)di1 << 8) | ((uint32_t)di2 << 16) | ((uint32_t)di3 << 24);
    }

    /* Decode Payload (subtract 0x33) */
    size_t payload_bytes = data_len - di_len;
    if (payload_bytes > sizeof(out_frame->payload)) {
        return SYN_ERROR;
    }
    out_frame->payload_len = (uint8_t)payload_bytes;
    size_t payload_offset = 10 + di_len;

    for (uint8_t i = 0; i < out_frame->payload_len; i++) {
        out_frame->payload[i] = (uint8_t)(buf[payload_offset + i] - SYN_DLT645_OFFSET);
    }

    return SYN_OK;
}

/* ── Streaming Decoder ──────────────────────────────────────────────────── */

void syn_dlt645_decoder_init(SYN_DLT645_Decoder *dec, SYN_DLT645_Ver version,
                             SYN_DLT645_FrameCallback cb, void *ctx)
{
    SYN_ASSERT(dec != NULL);

    memset(dec, 0, sizeof(*dec));
    dec->version = version;
    dec->cb = cb;
    dec->cb_ctx = ctx;
}

void syn_dlt645_decoder_feed(SYN_DLT645_Decoder *dec, uint8_t rx_byte)
{
    if (dec == NULL) {
        return;
    }

    /* Ignore preambles if buffer is empty */
    if (dec->rx_len == 0 && rx_byte == SYN_DLT645_PREAMBLE) {
        return;
    }

    /* Must start with SOF (0x68) */
    if (dec->rx_len == 0 && rx_byte != SYN_DLT645_SOF) {
        return;
    }

    /* Prevent overflow */
    if (dec->rx_len >= sizeof(dec->rx_buf)) {
        dec->rx_len = 0;
        return;
    }

    dec->rx_buf[dec->rx_len++] = rx_byte;

    /* Check potential frame completion on EOF byte */
    if (rx_byte == SYN_DLT645_EOF && dec->rx_len >= 12) {
        SYN_DLT645_Frame frame;
        if (syn_dlt645_parse(dec->rx_buf, dec->rx_len, dec->version, &frame) == SYN_OK) {
            if (dec->cb != NULL) {
                dec->cb(&frame, dec->cb_ctx);
            }
            dec->rx_len = 0; /* Reset for next frame */
        }
    }
}