// Compile and run:
// gcc -o make_nov_b_rawdmi make_nov_b_rawdmi.c && ./make_nov_b_rawdmi

#include <stdio.h>
#include <stdint.h>
#include <string.h>

// Use same CRC as Novatel messages, see https://docs.novatel.com/OEM7/Content/Messages/32_Bit_CRC.htm
static uint32_t crc32(const uint8_t *data, const int size)
{
    uint32_t crc = 0;
    for (int i = 0; i < size; i++)
    {
        crc ^= data[i];
        for (int j = 0; j < 8; j++)
        {
            if (crc & 1)
            {
                 crc= (crc >> 1) ^ 0xedb88320u;
            }
            else
            {
                crc >>= 1;
            }
        }
    }
    return crc;
}

typedef struct RAWDMI_s
{
    uint8_t  head1;
    uint8_t  head2;
    uint8_t  head3;
    uint8_t  payloadLen;
    uint16_t msgId;
    uint16_t wno;
    int32_t  tow;
    int32_t  dmi1;
    int32_t  dmi2;
    int32_t  dmi3;
    int32_t  dmi4;
    int32_t  mask;
} RAWDMI_t;

int main(int argc, char **argv)
{
    // Check endianness, we require a little endian machine
    {
        volatile uint32_t i = 0x01234567;
        if ((*((uint8_t *)(&i))) != 0x67)
        {
            printf("expecting little endian machine!\n");
            return 1;
        }
    }

    // Check struct size
    if (sizeof(RAWDMI_t) != 32)
    {
        printf("unexpected struct size!\n");
        return 1;
    }

    // Message payload
    const RAWDMI_t rawdmi =
    {
        .head1      = 0xaa,
        .head2      = 0x44,
        .head3      = 0x13,
        .payloadLen = 20,
        .msgId      = 2269,
        .wno        = 0,
        .tow        = 0,
        .dmi1       = 111,
        .dmi2       = -22222,
        .dmi3       = 333333,
        .dmi4       = -44,
        .mask       = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3),
    };

    // Calculate CRC
    const uint32_t checksum = crc32((const uint8_t *)&rawdmi, sizeof(rawdmi));

    // Compose entire message
    uint8_t message[sizeof(rawdmi) + sizeof(checksum)];
    memcpy(&message[0], &rawdmi, sizeof(rawdmi));
    memcpy(&message[sizeof(rawdmi)], &checksum, sizeof(checksum));

    // Hexdump message
    for (int ix = 0; ix < (int)sizeof(message); ix++)
    {
        printf("%02x", message[ix]);
        if ((ix % 16) == 15)
        {
            printf("\n");
        }
        else if ((ix % 4) == 3)
        {
            printf("  ");
        }
        else
        {
            printf(" ");
        }
    }
    printf("\n");

    // Expected output

    // aa 44 13 14  dd 08 00 00  00 00 00 00  6f 00 00 00
    // 32 a9 ff ff  15 16 05 00  d4 ff ff ff  0f 00 00 00
    // 69 9d 53 7b

    return 0;
}