From d252f543a38765d405c1d23d449c1e53d10ec8df Mon Sep 17 00:00:00 2001 From: Troels Jessen Date: Tue, 16 Dec 2025 10:07:27 +0100 Subject: [PATCH 1/6] Include sub-second information in serializer --- meson.build | 4 ++++ meson_options.txt | 1 + src/param/param_serializer.c | 13 +++++++++++++ 3 files changed, 18 insertions(+) diff --git a/meson.build b/meson.build index 8d13bf3f..04e6812b 100644 --- a/meson.build +++ b/meson.build @@ -23,6 +23,10 @@ if get_option('have_float') == false conf.set('MPACK_FLOAT', 0) endif +if get_option('serialize_extended_timestamp') == true + conf.set('EXTENDED_TIMESTAMP', 0) +endif + if get_option('have_fopen') == true if get_option('list_dynamic') == true conf.set('MPACK_STDIO', 1) diff --git a/meson_options.txt b/meson_options.txt index a01bbaa5..58b11227 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,3 +5,4 @@ option('list_dynamic', type: 'boolean', value: false, description: 'Compile supp option('list_pool', type: 'integer', value: 0, description: 'Compile support for pre-allocated param list (requres sys/queue.h)') option('have_float', type: 'boolean', value: true, description: 'Support float/double') option('num_publishqueues', type: 'integer', value: 0, description: 'Number of param publish queues required') +option('serialize_extended_timestamp', type: 'boolean', value: false, description: 'Include ns part of timestamps when serializing parameters (network incompatible with libparam older than June 2025') diff --git a/src/param/param_serializer.c b/src/param/param_serializer.c index dc759f64..e504dfcc 100644 --- a/src/param/param_serializer.c +++ b/src/param/param_serializer.c @@ -69,11 +69,18 @@ void param_serialize_id(mpack_writer_t *writer, param_t *param, int offset, para int array_flag = (offset >= 0) ? 1 : 0; int node_flag = (queue->last_node != node) ? 1 : 0; int timestamp_flag = (queue->last_timestamp.tv_sec != param->timestamp->tv_sec) ? 1 : 0; +#ifdef EXTENDED_TIMESTAMP + int extendedtimestamp_flag = (timestamp_flag || queue->last_timestamp.tv_nsec != param->timestamp->tv_nsec) ? 1 : 0; + if (extendedtimestamp_flag) timestamp_flag = 1; +#else + int extendedtimestamp_flag = 0; +#endif int extendedid_flag = (param->id > 0x3ff) ? 1 : 0; uint16_t header = array_flag << PARAM_HEADER_ARRAY_POS | node_flag << PARAM_HEADER_NODE_POS | timestamp_flag << PARAM_HEADER_TIMESTAMP_POS + | extendedtimestamp_flag << PARAM_HEADER_EXTENDEDTIMESTAMP_POS | extendedid_flag << PARAM_HEADER_EXTENDEDID_POS |(param->id & PARAM_HEADER_ID_MASK); header = htobe16(header); @@ -96,6 +103,12 @@ void param_serialize_id(mpack_writer_t *writer, param_t *param, int offset, para mpack_write_bytes(writer, (char*) &_timestamp, 4); } + if (extendedtimestamp_flag) { + queue->last_timestamp = *param->timestamp; + uint32_t _extendedtimestamp = htobe32(param->timestamp->tv_nsec); + mpack_write_bytes(writer, (char*) &_extendedtimestamp, 4); + } + if (extendedid_flag) { char _extendedid = (param->id&0xfc00) >> 8; mpack_write_bytes(writer, &_extendedid, 1); From 2997628e48d501dd95224eeea20a4f807f2dbeee Mon Sep 17 00:00:00 2001 From: Henrik Davidsen Date: Mon, 26 Jan 2026 10:01:01 +0100 Subject: [PATCH 2/6] Only include sub-second information in serializer if time is UTC --- include/param/param.h | 14 ++++++++++---- src/param/param_serializer.c | 19 ++++++++++--------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/include/param/param.h b/include/param/param.h index 0a9957ad..91762219 100644 --- a/include/param/param.h +++ b/include/param/param.h @@ -122,6 +122,12 @@ typedef struct param_s { } param_t; +#ifdef EXTENDED_TIMESTAMP +#define PARAM_TIMESTAMP_TV_NSEC -1 +#else +#define PARAM_TIMESTAMP_TV_NSEC 0 +#endif + /** * DEFINITION HELPERS: * @@ -137,7 +143,7 @@ typedef struct param_s { */ #define PARAM_DEFINE_STATIC_RAM(_id, _name, _type, _array_count, _array_step, _flags, _callback, _unit, _physaddr, _docstr) \ ; /* Catch const param defines */ \ - csp_timestamp_t _timestamp_##_name = { .tv_sec = 0, .tv_nsec = 0 }; \ + csp_timestamp_t _timestamp_##_name = { .tv_sec = 0, .tv_nsec = PARAM_TIMESTAMP_TV_NSEC }; \ uint16_t _node_##_name = 0; \ __attribute__((section("param"))) \ __attribute__((used, no_reorder)) \ @@ -160,7 +166,7 @@ typedef struct param_s { #define PARAM_DEFINE_STATIC_VMEM(_id, _name, _type, _array_count, _array_step, _flags, _callback, _unit, _vmem_name, _vmem_addr, _docstr) \ ; /* Catch const param defines */ \ - csp_timestamp_t _timestamp_##_name = { .tv_sec = 0, .tv_nsec = 0 }; \ + csp_timestamp_t _timestamp_##_name = { .tv_sec = 0, .tv_nsec = PARAM_TIMESTAMP_TV_NSEC }; \ uint16_t _node_##_name = 0; \ __attribute__((section("param"))) \ __attribute__((used, no_reorder)) \ @@ -185,7 +191,7 @@ typedef struct param_s { #define PARAM_DEFINE_REMOTE(_id, _name, _nodeaddr, _type, _array_count, _array_step, _flags, _physaddr, _docstr) \ ; /* Catch const param defines */ \ - csp_timestamp_t _timestamp_##_name = { .tv_sec = 0, .tv_nsec = 0 }; \ + csp_timestamp_t _timestamp_##_name = { .tv_sec = 0, .tv_nsec = PARAM_TIMESTAMP_TV_NSEC }; \ __attribute__((section("param"))) \ __attribute__((used, no_reorder)) \ param_t _name = { \ @@ -205,7 +211,7 @@ typedef struct param_s { #define PARAM_DEFINE_REMOTE_DYNAMIC(_id, _name, _node, _type, _array_count, _array_step, _flags, _physaddr, _docstr) \ ; /* Catch const param defines */ \ - csp_timestamp_t _timestamp_##_name = { .tv_sec = 0, .tv_nsec = 0 }; \ + csp_timestamp_t _timestamp_##_name = { .tv_sec = 0, .tv_nsec = PARAM_TIMESTAMP_TV_NSEC }; \ uint16_t _node_##_name = _node; \ param_t _name = { \ .node = &_node_##_name, \ diff --git a/src/param/param_serializer.c b/src/param/param_serializer.c index e504dfcc..884e528e 100644 --- a/src/param/param_serializer.c +++ b/src/param/param_serializer.c @@ -69,11 +69,13 @@ void param_serialize_id(mpack_writer_t *writer, param_t *param, int offset, para int array_flag = (offset >= 0) ? 1 : 0; int node_flag = (queue->last_node != node) ? 1 : 0; int timestamp_flag = (queue->last_timestamp.tv_sec != param->timestamp->tv_sec) ? 1 : 0; -#ifdef EXTENDED_TIMESTAMP - int extendedtimestamp_flag = (timestamp_flag || queue->last_timestamp.tv_nsec != param->timestamp->tv_nsec) ? 1 : 0; - if (extendedtimestamp_flag) timestamp_flag = 1; -#else int extendedtimestamp_flag = 0; +#ifdef EXTENDED_TIMESTAMP + if ((timestamp_flag || queue->last_timestamp.tv_nsec != param->timestamp->tv_nsec)) { + extendedtimestamp_flag = param->timestamp->tv_sec > 1577836800 && + (param->timestamp->tv_nsec < 1000000000ULL); + timestamp_flag |= extendedtimestamp_flag; + } #endif int extendedid_flag = (param->id > 0x3ff) ? 1 : 0; @@ -101,12 +103,11 @@ void param_serialize_id(mpack_writer_t *writer, param_t *param, int offset, para queue->last_timestamp = *param->timestamp; uint32_t _timestamp = htobe32(param->timestamp->tv_sec); mpack_write_bytes(writer, (char*) &_timestamp, 4); - } - if (extendedtimestamp_flag) { - queue->last_timestamp = *param->timestamp; - uint32_t _extendedtimestamp = htobe32(param->timestamp->tv_nsec); - mpack_write_bytes(writer, (char*) &_extendedtimestamp, 4); + if (extendedtimestamp_flag) { + _timestamp = htobe32(param->timestamp->tv_nsec); + mpack_write_bytes(writer, (char*) &_timestamp, 4); + } } if (extendedid_flag) { From 8a70e55fd6b88c1dc70b87a743abaff1fe0c9b59 Mon Sep 17 00:00:00 2001 From: Henrik Davidsen Date: Fri, 30 Jan 2026 08:58:33 +0100 Subject: [PATCH 3/6] Set timestamp and extended timestamp in param_serialize_id if param timestamp has changed compared to previous queue timestamp --- src/param/param_serializer.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/param/param_serializer.c b/src/param/param_serializer.c index 884e528e..5621fa40 100644 --- a/src/param/param_serializer.c +++ b/src/param/param_serializer.c @@ -71,11 +71,10 @@ void param_serialize_id(mpack_writer_t *writer, param_t *param, int offset, para int timestamp_flag = (queue->last_timestamp.tv_sec != param->timestamp->tv_sec) ? 1 : 0; int extendedtimestamp_flag = 0; #ifdef EXTENDED_TIMESTAMP - if ((timestamp_flag || queue->last_timestamp.tv_nsec != param->timestamp->tv_nsec)) { - extendedtimestamp_flag = param->timestamp->tv_sec > 1577836800 && - (param->timestamp->tv_nsec < 1000000000ULL); - timestamp_flag |= extendedtimestamp_flag; - } + /* If timestamp is after 1577836800: Jan 1st 2020 then this is a UTC timestamp */ + extendedtimestamp_flag = (timestamp_flag && param->timestamp->tv_sec > 1577836800) || + queue->last_timestamp.tv_nsec != param->timestamp->tv_nsec; + timestamp_flag |= extendedtimestamp_flag; #endif int extendedid_flag = (param->id > 0x3ff) ? 1 : 0; @@ -182,7 +181,7 @@ void param_deserialize_id(mpack_reader_t *reader, int *id, int *node, csp_timest _timestamp_ns = be32toh(_timestamp_ns); queue->last_timestamp.tv_nsec = _timestamp_ns; } else { - queue->last_timestamp.tv_nsec = 0; + queue->last_timestamp.tv_nsec = PARAM_TIMESTAMP_TV_NSEC; } } } else if (extendedtimestamp_flag) { From 3fd37d7b090f8a92488f70e1fc1096657f8f00c4 Mon Sep 17 00:00:00 2001 From: Henrik Davidsen Date: Fri, 30 Jan 2026 09:38:37 +0100 Subject: [PATCH 4/6] Changed how timestamp_flag and extendedtimestamp_flag works. I param (de)serialize timestamp_flag represents changes in sec and extendedtimestamp_flag is changes in nsec, and can be set independently of each other. --- src/param/param_serializer.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/param/param_serializer.c b/src/param/param_serializer.c index 5852c69e..0fe4631b 100644 --- a/src/param/param_serializer.c +++ b/src/param/param_serializer.c @@ -72,10 +72,7 @@ void param_serialize_id(mpack_writer_t *writer, param_t *param, int offset, para int timestamp_flag = (queue->last_timestamp.tv_sec != param->timestamp->tv_sec) ? 1 : 0; int extendedtimestamp_flag = 0; #ifdef EXTENDED_TIMESTAMP - /* If timestamp is after 1577836800: Jan 1st 2020 then this is a UTC timestamp */ - extendedtimestamp_flag = (timestamp_flag && param->timestamp->tv_sec > 1577836800) || - queue->last_timestamp.tv_nsec != param->timestamp->tv_nsec; - timestamp_flag |= extendedtimestamp_flag; + extendedtimestamp_flag = (queue->last_timestamp.tv_nsec != param->timestamp->tv_nsec) ? 1 : 0; #endif /* EXTENDED_TIMESTAMP */ #else int timestamp_flag = 0; @@ -83,7 +80,7 @@ void param_serialize_id(mpack_writer_t *writer, param_t *param, int offset, para #endif int extendedid_flag = (param->id > 0x3ff) ? 1 : 0; - uint16_t header = array_flag << PARAM_HEADER_ARRAY_POS + uint16_t header = array_flag << PARAM_HEADER_ARRAY_POS | node_flag << PARAM_HEADER_NODE_POS | timestamp_flag << PARAM_HEADER_TIMESTAMP_POS | extendedtimestamp_flag << PARAM_HEADER_EXTENDEDTIMESTAMP_POS @@ -108,11 +105,12 @@ void param_serialize_id(mpack_writer_t *writer, param_t *param, int offset, para queue->last_timestamp = *param->timestamp; uint32_t _timestamp = htobe32(param->timestamp->tv_sec); mpack_write_bytes(writer, (char*) &_timestamp, 4); + } - if (extendedtimestamp_flag) { - _timestamp = htobe32(param->timestamp->tv_nsec); - mpack_write_bytes(writer, (char*) &_timestamp, 4); - } + if (extendedtimestamp_flag) { + uint32_t _timestamp_ns = htobe32(param->timestamp->tv_nsec); + mpack_write_bytes(writer, (char*) &_timestamp_ns, 4); + queue->last_timestamp.tv_nsec = _timestamp_ns; } #endif @@ -182,19 +180,14 @@ void param_deserialize_id(mpack_reader_t *reader, int *id, int *node, csp_timest queue->last_timestamp = queue->client_timestamp; } else { queue->last_timestamp.tv_sec = _timestamp; - if (extendedtimestamp_flag) { - uint32_t _timestamp_ns; - mpack_read_bytes(reader, (char*) &_timestamp_ns, 4); - _timestamp_ns = be32toh(_timestamp_ns); - queue->last_timestamp.tv_nsec = _timestamp_ns; - } else { - queue->last_timestamp.tv_nsec = PARAM_TIMESTAMP_TV_NSEC; - } } - } else if (extendedtimestamp_flag) { - /* Invalid header combination, discard header field */ + } + + if (extendedtimestamp_flag) { uint32_t _timestamp_ns; mpack_read_bytes(reader, (char*) &_timestamp_ns, 4); + _timestamp_ns = be32toh(_timestamp_ns); + queue->last_timestamp.tv_nsec = _timestamp_ns; } *timestamp = queue->last_timestamp; From f2f73c31c609cd04490c9f079b45e74231753ef8 Mon Sep 17 00:00:00 2001 From: Henrik Davidsen Date: Fri, 30 Jan 2026 09:46:14 +0100 Subject: [PATCH 5/6] Wrong timestamp set in last_timestamp in param_serialize_id if extendedtimestamp_flag is set --- src/param/param_serializer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/param/param_serializer.c b/src/param/param_serializer.c index 0fe4631b..24aad67c 100644 --- a/src/param/param_serializer.c +++ b/src/param/param_serializer.c @@ -108,9 +108,9 @@ void param_serialize_id(mpack_writer_t *writer, param_t *param, int offset, para } if (extendedtimestamp_flag) { + queue->last_timestamp.tv_nsec = param->timestamp->tv_nsec; uint32_t _timestamp_ns = htobe32(param->timestamp->tv_nsec); mpack_write_bytes(writer, (char*) &_timestamp_ns, 4); - queue->last_timestamp.tv_nsec = _timestamp_ns; } #endif From 7aac0276134f50ec75f52b99e6b5f63f7269689e Mon Sep 17 00:00:00 2001 From: Henrik Davidsen Date: Mon, 2 Feb 2026 09:57:11 +0100 Subject: [PATCH 6/6] Renamed PARAM_TIMESTAMP_TV_NSEC to CSP_TIMESTAMP_INVALID_NSEC --- include/param/param.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/param/param.h b/include/param/param.h index dc01c8d1..4b31782e 100644 --- a/include/param/param.h +++ b/include/param/param.h @@ -86,6 +86,12 @@ typedef enum { #define PM_CSP (5 << 16) //! Known as 5 in elfparse and genparamtable #define PM_KEYCONF (6 << 16) //! Known as 6 in elfparse and genparamtable +/** + * Value to indicate an invalid nsec. + * + * This define should be moved to CSP if CSP will have support for invalid nsec + */ +#define CSP_TIMESTAMP_INVALID_NSEC -1 /** * Parameter description structure @@ -124,12 +130,6 @@ typedef struct param_s { } param_t; -#ifdef EXTENDED_TIMESTAMP -#define PARAM_TIMESTAMP_TV_NSEC -1 -#else -#define PARAM_TIMESTAMP_TV_NSEC 0 -#endif - /** * DEFINITION HELPERS: * @@ -146,7 +146,7 @@ typedef struct param_s { #ifdef PARAM_HAVE_TIMESTAMP #define PARAM_TIMESTAMP_DECL(_name) \ - csp_timestamp_t _timestamp_##_name = { .tv_sec = 0, .tv_nsec = PARAM_TIMESTAMP_TV_NSEC }; + csp_timestamp_t _timestamp_##_name = { .tv_sec = 0, .tv_nsec = CSP_TIMESTAMP_INVALID_NSEC }; #define PARAM_TIMESTAMP_INIT(_name) \ .timestamp = &_timestamp_##_name,