From 58f5ca9546838150be254b18d62d887a550bb040 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Tue, 2 Jul 2024 02:41:13 +0100 Subject: [PATCH 1/4] msgproto: Support multi-byte command and response ids Update the msgproto.py code so that it can support message ids that are larger than a single byte. (The host C code in klippy/chelper/msgblock.c already supports multi-byte ids.) Signed-off-by: Kevin O'Connor --- klippy/clocksync.py | 8 ++++-- klippy/mcu.py | 2 +- klippy/msgproto.py | 64 ++++++++++++++++++++++----------------------- 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/klippy/clocksync.py b/klippy/clocksync.py index 291481005..33bd1b196 100644 --- a/klippy/clocksync.py +++ b/klippy/clocksync.py @@ -238,7 +238,9 @@ def connect_file(self, serial, pace=False): # clock frequency conversions def print_time_to_clock(self, print_time): if self.clock_adj[1] == 1.0: - logging.warning("Clock not yet synchronized, clock is untrustworthy") + logging.warning( + "Clock not yet synchronized, clock is untrustworthy" + ) for line in traceback.format_stack(): logging.warning(line.strip()) adjusted_offset, adjusted_freq = self.clock_adj @@ -246,7 +248,9 @@ def print_time_to_clock(self, print_time): def clock_to_print_time(self, clock): if self.clock_adj[1] == 1.0: - logging.warning("Clock not yet synchronized, print time is untrustworthy") + logging.warning( + "Clock not yet synchronized, print time is untrustworthy" + ) for line in traceback.format_stack(): logging.warning(line.strip()) adjusted_offset, adjusted_freq = self.clock_adj diff --git a/klippy/mcu.py b/klippy/mcu.py index 9d90b254a..b69262e62 100644 --- a/klippy/mcu.py +++ b/klippy/mcu.py @@ -111,7 +111,7 @@ def __init__(self, serial, msgformat, cmd_queue=None): if cmd_queue is None: cmd_queue = serial.get_default_command_queue() self._cmd_queue = cmd_queue - self._msgtag = msgparser.lookup_msgtag(msgformat) & 0xFFFFFFFF + self._msgtag = msgparser.lookup_msgid(msgformat) & 0xFFFFFFFF def send(self, data=(), minclock=0, reqclock=0): cmd = self._cmd.encode(data) diff --git a/klippy/msgproto.py b/klippy/msgproto.py index 40ddfd878..d14c83e60 100644 --- a/klippy/msgproto.py +++ b/klippy/msgproto.py @@ -1,6 +1,6 @@ # Protocol definitions for firmware communication # -# Copyright (C) 2016-2021 Kevin O'Connor +# Copyright (C) 2016-2024 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. import json, zlib, logging @@ -199,8 +199,8 @@ def convert_msg_format(msgformat): class MessageFormat: - def __init__(self, msgid, msgformat, enumerations={}): - self.msgid = msgid + def __init__(self, msgid_bytes, msgformat, enumerations={}): + self.msgid_bytes = msgid_bytes self.msgformat = msgformat self.debugformat = convert_msg_format(msgformat) self.name = msgformat.split()[0] @@ -209,21 +209,19 @@ def __init__(self, msgid, msgformat, enumerations={}): self.name_to_type = dict(self.param_names) def encode(self, params): - out = [] - out.append(self.msgid) + out = list(self.msgid_bytes) for i, t in enumerate(self.param_types): t.encode(out, params[i]) return out def encode_by_name(self, **params): - out = [] - out.append(self.msgid) + out = list(self.msgid_bytes) for name, t in self.param_names: t.encode(out, params[name]) return out def parse(self, s, pos): - pos += 1 + pos += len(self.msgid_bytes) out = {} for name, t in self.param_names: v, pos = t.parse(s, pos) @@ -243,14 +241,14 @@ def format_params(self, params): class OutputFormat: name = "#output" - def __init__(self, msgid, msgformat): - self.msgid = msgid + def __init__(self, msgid_bytes, msgformat): + self.msgid_bytes = msgid_bytes self.msgformat = msgformat self.debugformat = convert_msg_format(msgformat) self.param_types = lookup_output_params(msgformat) def parse(self, s, pos): - pos += 1 + pos += len(self.msgid_bytes) out = [] for t in self.param_types: v, pos = t.parse(s, pos) @@ -268,7 +266,7 @@ class UnknownFormat: name = "#unknown" def parse(self, s, pos): - msgid = s[pos] + msgid, param_pos = PT_int32().parse(s, pos) msg = bytes(bytearray(s)) return {"#msgid": msgid, "#msg": msg}, len(s) - MESSAGE_TRAILER_SIZE @@ -286,7 +284,8 @@ def __init__(self, warn_prefix=""): self.messages = [] self.messages_by_id = {} self.messages_by_name = {} - self.msgtag_by_format = {} + self.msgid_by_format = {} + self.msgid_parser = PT_int32() self.config = {} self.version = self.build_versions = "" self.raw_identify_data = "" @@ -322,8 +321,8 @@ def dump(self, s): msgseq = s[MESSAGE_POS_SEQ] out = ["seq: %02x" % (msgseq,)] pos = MESSAGE_HEADER_SIZE - while True: - msgid = s[pos] + while 1: + msgid, param_pos = self.msgid_parser.parse(s, pos) mid = self.messages_by_id.get(msgid, self.unknown) params, pos = mid.parse(s, pos) out.append(mid.format_params(params)) @@ -342,7 +341,7 @@ def format_params(self, params): return str(params) def parse(self, s): - msgid = s[MESSAGE_HEADER_SIZE] + msgid, param_pos = self.msgid_parser.parse(s, MESSAGE_HEADER_SIZE) mid = self.messages_by_id.get(msgid, self.unknown) params, pos = mid.parse(s, MESSAGE_HEADER_SIZE) if pos != len(s) - MESSAGE_TRAILER_SIZE: @@ -350,7 +349,7 @@ def parse(self, s): params["#name"] = mid.name return params - def encode(self, seq, cmd): + def encode_msgblock(self, seq, cmd): msglen = MESSAGE_MIN + len(cmd) seq = (seq & MESSAGE_SEQ_MASK) | MESSAGE_DEST out = [msglen, seq] + cmd @@ -381,11 +380,11 @@ def lookup_command(self, msgformat): ) return mp - def lookup_msgtag(self, msgformat): - msgtag = self.msgtag_by_format.get(msgformat) - if msgtag is None: + def lookup_msgid(self, msgformat): + msgid = self.msgid_by_format.get(msgformat) + if msgid is None: self._error("Unknown command: %s", msgformat) - return msgtag + return msgid def create_command(self, msg): parts = msg.strip().split() @@ -439,22 +438,23 @@ def fill_enumerations(self, enumerations): for i in range(count): enums[enum_root + str(start_enum + i)] = start_value + i - def _init_messages(self, messages, command_tags=[], output_tags=[]): - for msgformat, msgtag in messages.items(): + def _init_messages(self, messages, command_ids=[], output_ids=[]): + for msgformat, msgid in messages.items(): msgtype = "response" - if msgtag in command_tags: + if msgid in command_ids: msgtype = "command" - elif msgtag in output_tags: + elif msgid in output_ids: msgtype = "output" - self.messages.append((msgtag, msgtype, msgformat)) - if msgtag < -32 or msgtag > 95: - self._error("Multi-byte msgtag not supported") - self.msgtag_by_format[msgformat] = msgtag - msgid = msgtag & 0x7F + self.messages.append((msgid, msgtype, msgformat)) + self.msgid_by_format[msgformat] = msgid + msgid_bytes = [] + self.msgid_parser.encode(msgid_bytes, msgid) if msgtype == "output": - self.messages_by_id[msgid] = OutputFormat(msgid, msgformat) + self.messages_by_id[msgid] = OutputFormat( + msgid_bytes, msgformat + ) else: - msg = MessageFormat(msgid, msgformat, self.enumerations) + msg = MessageFormat(msgid_bytes, msgformat, self.enumerations) self.messages_by_id[msgid] = msg self.messages_by_name[msg.name] = msg From 6505cd7ff61a11ce26e0dfb6e25d9229fc78b68e Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Tue, 2 Jul 2024 02:41:14 +0100 Subject: [PATCH 2/4] sensor_bulk: Change maximum data size from 52 to 51 bytes Reduce the maximum data size from 52 bytes to 51 bytes. This will enable support for 2-byte response ids. This change would alter the behavior of the ldc1612 sensor support. Force an ldc1612 command name change so that users are alerted that they must rebuild the micro-controller code upon update of the host code. Signed-off-by: Kevin O'Connor --- klippy/extras/bulk_sensor.py | 2 +- klippy/extras/ldc1612.py | 2 +- src/sensor_bulk.h | 2 +- src/sensor_ldc1612.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/klippy/extras/bulk_sensor.py b/klippy/extras/bulk_sensor.py index fba14cb91..a29f6c7ad 100644 --- a/klippy/extras/bulk_sensor.py +++ b/klippy/extras/bulk_sensor.py @@ -235,7 +235,7 @@ def get_time_translation(self): return base_time, base_chip, inv_freq -MAX_BULK_MSG_SIZE = 52 +MAX_BULK_MSG_SIZE = 51 # Read sensor_bulk_data and calculate timestamps for devices that take diff --git a/klippy/extras/ldc1612.py b/klippy/extras/ldc1612.py index 05aea3db6..e0a936e39 100644 --- a/klippy/extras/ldc1612.py +++ b/klippy/extras/ldc1612.py @@ -140,7 +140,7 @@ def _build_config(self): "query_ldc1612 oid=%c rest_ticks=%u", cq=cmdqueue ) self.ffreader.setup_query_command( - "query_ldc1612_status oid=%c", oid=self.oid, cq=cmdqueue + "query_status_ldc1612 oid=%c", oid=self.oid, cq=cmdqueue ) self.ldc1612_setup_home_cmd = self.mcu.lookup_command( "ldc1612_setup_home oid=%c clock=%u threshold=%u" diff --git a/src/sensor_bulk.h b/src/sensor_bulk.h index 9c130bea3..c750dbdae 100644 --- a/src/sensor_bulk.h +++ b/src/sensor_bulk.h @@ -4,7 +4,7 @@ struct sensor_bulk { uint16_t sequence, possible_overflows; uint8_t data_count; - uint8_t data[52]; + uint8_t data[51]; }; void sensor_bulk_reset(struct sensor_bulk *sb); diff --git a/src/sensor_ldc1612.c b/src/sensor_ldc1612.c index 6e52c177c..01cf3ee04 100644 --- a/src/sensor_ldc1612.c +++ b/src/sensor_ldc1612.c @@ -210,7 +210,7 @@ command_query_ldc1612(uint32_t *args) DECL_COMMAND(command_query_ldc1612, "query_ldc1612 oid=%c rest_ticks=%u"); void -command_query_ldc1612_status(uint32_t *args) +command_query_status_ldc1612(uint32_t *args) { struct ldc1612 *ld = oid_lookup(args[0], command_config_ldc1612); @@ -232,7 +232,7 @@ command_query_ldc1612_status(uint32_t *args) uint32_t fifo = status & 0x08 ? BYTES_PER_SAMPLE : 0; sensor_bulk_status(&ld->sb, args[0], time1, time2-time1, fifo); } -DECL_COMMAND(command_query_ldc1612_status, "query_ldc1612_status oid=%c"); +DECL_COMMAND(command_query_status_ldc1612, "query_status_ldc1612 oid=%c"); void ldc1612_task(void) From c37fab4cebaedc3ee1ff400b67a0de05aeb6b9d1 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Tue, 2 Jul 2024 02:41:15 +0100 Subject: [PATCH 3/4] command: Support 2-byte message ids Allow command ids, response ids, and output ids to be either 1 or 2 bytes long. This increases the total number of message types from 128 to 16384. Signed-off-by: Kevin O'Connor --- scripts/buildcommands.py | 104 ++++++++++++++++++++++----------------- src/command.c | 30 +++++++++-- src/command.h | 9 ++-- src/pru/pru0.c | 2 +- 4 files changed, 93 insertions(+), 52 deletions(-) diff --git a/scripts/buildcommands.py b/scripts/buildcommands.py index 9d3d0b41b..4f8cee29d 100644 --- a/scripts/buildcommands.py +++ b/scripts/buildcommands.py @@ -292,8 +292,9 @@ class HandleCommandGeneration: def __init__(self): self.commands = {} self.encoders = [] - self.msg_to_id = dict(msgproto.DefaultMessages) - self.messages_by_name = {m.split()[0]: m for m in self.msg_to_id} + self.msg_to_encid = dict(msgproto.DefaultMessages) + self.encid_to_msgid = {} + self.messages_by_name = {m.split()[0]: m for m in self.msg_to_encid} self.all_param_types = {} self.ctr_dispatch = { "DECL_COMMAND_FLAGS": self.decl_command, @@ -325,54 +326,65 @@ def decl_output(self, req): msg = req.split(None, 1)[1] self.encoders.append((None, msg)) + def convert_encoded_msgid(self, encoded_msgid): + if encoded_msgid >= 0x80: + data = [(encoded_msgid >> 7) | 0x80, encoded_msgid & 0x7F] + else: + data = [encoded_msgid] + return msgproto.PT_int32().parse(data, 0)[0] + def create_message_ids(self): # Create unique ids for each message type - msgid = max(self.msg_to_id.values()) + encoded_msgid = max(self.msg_to_encid.values()) mlist = list(self.commands.keys()) + [m for n, m in self.encoders] for msgname in mlist: msg = self.messages_by_name.get(msgname, msgname) - if msg not in self.msg_to_id: - msgid += 1 - self.msg_to_id[msg] = msgid - if msgid >= 128: - # The mcu currently assumes all message ids encode to one byte + if msg not in self.msg_to_encid: + encoded_msgid += 1 + self.msg_to_encid[msg] = encoded_msgid + if encoded_msgid >= 1 << 14: + # The mcu currently assumes all message ids encode to 1 or 2 bytes error("Too many message ids") + self.encid_to_msgid = { + encoded_msgid: self.convert_encoded_msgid(encoded_msgid) + for encoded_msgid in self.msg_to_encid.values() + } def update_data_dictionary(self, data): - # Handle message ids over 96 (they are decoded as negative numbers) - msg_to_tag = { - msg: msgid if msgid < 96 else msgid - 128 - for msg, msgid in self.msg_to_id.items() + # Convert ids to standard form (use both positive and negative numbers) + msg_to_msgid = { + msg: self.encid_to_msgid[encoded_msgid] + for msg, encoded_msgid in self.msg_to_encid.items() } - command_tags = [ - msg_to_tag[msg] + command_ids = [ + msg_to_msgid[msg] for msgname, msg in self.messages_by_name.items() if msgname in self.commands ] - response_tags = [ - msg_to_tag[msg] + response_ids = [ + msg_to_msgid[msg] for msgname, msg in self.messages_by_name.items() if msgname not in self.commands ] data["commands"] = { - msg: msgtag - for msg, msgtag in msg_to_tag.items() - if msgtag in command_tags + msg: msgid + for msg, msgid in msg_to_msgid.items() + if msgid in command_ids } data["responses"] = { - msg: msgtag - for msg, msgtag in msg_to_tag.items() - if msgtag in response_tags + msg: msgid + for msg, msgid in msg_to_msgid.items() + if msgid in response_ids } output = { - msg: msgtag - for msg, msgtag in msg_to_tag.items() - if msgtag not in command_tags and msgtag not in response_tags + msg: msgid + for msg, msgid in msg_to_msgid.items() + if msgid not in command_ids and msgid not in response_ids } if output: data["output"] = output - def build_parser(self, msgid, msgformat, msgtype): + def build_parser(self, encoded_msgid, msgformat, msgtype): if msgtype == "output": param_types = msgproto.lookup_output_params(msgformat) comment = "Output: " + msgformat @@ -389,12 +401,13 @@ def build_parser(self, msgid, msgformat, msgtype): params = "command_parameters%d" % (paramid,) out = """ // %s - .msg_id=%d, + .encoded_msgid=%d, // msgid=%d .num_params=%d, .param_types = %s, """ % ( comment, - msgid, + encoded_msgid, + self.encid_to_msgid[encoded_msgid], len(types), params, ) @@ -406,11 +419,14 @@ def build_parser(self, msgid, msgformat, msgtype): ) out += " .num_args=%d," % (num_args,) else: + msgid_size = 1 + if encoded_msgid >= 0x80: + msgid_size = 2 max_size = min( msgproto.MESSAGE_MAX, ( msgproto.MESSAGE_MIN - + 1 + + msgid_size + sum([t.max_length for t in param_types]) ), ) @@ -423,23 +439,23 @@ def generate_responses_code(self): encoder_code = [] did_output = {} for msgname, msg in self.encoders: - msgid = self.msg_to_id[msg] - if msgid in did_output: + encoded_msgid = self.msg_to_encid[msg] + if encoded_msgid in did_output: continue - did_output[msgid] = True + did_output[encoded_msgid] = True code = ( ' if (__builtin_strcmp(str, "%s") == 0)\n' - " return &command_encoder_%s;\n" % (msg, msgid) + " return &command_encoder_%s;\n" % (msg, encoded_msgid) ) if msgname is None: - parsercode = self.build_parser(msgid, msg, "output") + parsercode = self.build_parser(encoded_msgid, msg, "output") output_code.append(code) else: - parsercode = self.build_parser(msgid, msg, "command") + parsercode = self.build_parser(encoded_msgid, msg, "command") encoder_code.append(code) encoder_defs.append( "const struct command_encoder command_encoder_%s PROGMEM = {" - " %s\n};\n" % (msgid, parsercode) + " %s\n};\n" % (encoded_msgid, parsercode) ) fmt = """ %s @@ -465,21 +481,21 @@ def generate_responses_code(self): ) def generate_commands_code(self): - cmd_by_id = { - self.msg_to_id[self.messages_by_name.get(msgname, msgname)]: cmd + cmd_by_encid = { + self.msg_to_encid[self.messages_by_name.get(msgname, msgname)]: cmd for msgname, cmd in self.commands.items() } - max_cmd_msgid = max(cmd_by_id.keys()) + max_cmd_encid = max(cmd_by_encid.keys()) index = [] externs = {} - for msgid in range(max_cmd_msgid + 1): - if msgid not in cmd_by_id: + for encoded_msgid in range(max_cmd_encid + 1): + if encoded_msgid not in cmd_by_encid: index.append(" {\n},") continue - funcname, flags, msgname = cmd_by_id[msgid] + funcname, flags, msgname = cmd_by_encid[encoded_msgid] msg = self.messages_by_name[msgname] externs[funcname] = 1 - parsercode = self.build_parser(msgid, msg, "response") + parsercode = self.build_parser(encoded_msgid, msg, "response") index.append( " {%s\n .flags=%s,\n .func=%s\n}," % (parsercode, flags, funcname) @@ -498,7 +514,7 @@ def generate_commands_code(self): %s }; -const uint8_t command_index_size PROGMEM = ARRAY_SIZE(command_index); +const uint16_t command_index_size PROGMEM = ARRAY_SIZE(command_index); """ return fmt % (externs, index) diff --git a/src/command.c b/src/command.c index 39c09458b..d2d05aff9 100644 --- a/src/command.c +++ b/src/command.c @@ -1,6 +1,6 @@ // Code for parsing incoming commands and encoding outgoing messages // -// Copyright (C) 2016,2017 Kevin O'Connor +// Copyright (C) 2016-2024 Kevin O'Connor // // This file may be distributed under the terms of the GNU GPLv3 license. @@ -69,6 +69,28 @@ parse_int(uint8_t **pp) return v; } +// Write an encoded msgid (optimized 2-byte VLQ encoder) +static uint8_t * +encode_msgid(uint8_t *p, uint_fast16_t encoded_msgid) +{ + if (encoded_msgid >= 0x80) + *p++ = (encoded_msgid >> 7) | 0x80; + *p++ = encoded_msgid & 0x7f; + return p; +} + +// Parse an encoded msgid (optimized 2-byte parser, return as positive number) +uint_fast16_t +command_parse_msgid(uint8_t **pp) +{ + uint8_t *p = *pp; + uint_fast16_t encoded_msgid = *p++; + if (encoded_msgid & 0x80) + encoded_msgid = ((encoded_msgid & 0x7f) << 7) | (*p++); + *pp = p; + return encoded_msgid; +} + // Parse an incoming command into 'args' uint8_t * command_parsef(uint8_t *p, uint8_t *maxend @@ -119,7 +141,7 @@ command_encodef(uint8_t *buf, const struct command_encoder *ce, va_list args) uint8_t *maxend = &p[max_size - MESSAGE_MIN]; uint_fast8_t num_params = READP(ce->num_params); const uint8_t *param_types = READP(ce->param_types); - *p++ = READP(ce->msg_id); + p = encode_msgid(p, READP(ce->encoded_msgid)); while (num_params--) { if (p > maxend) goto error; @@ -227,7 +249,7 @@ DECL_SHUTDOWN(sendf_shutdown); // Find the command handler associated with a command static const struct command_parser * -command_lookup_parser(uint_fast8_t cmdid) +command_lookup_parser(uint_fast16_t cmdid) { if (!cmdid || cmdid >= READP(command_index_size)) shutdown("Invalid command"); @@ -309,7 +331,7 @@ command_dispatch(uint8_t *buf, uint_fast8_t msglen) uint8_t *p = &buf[MESSAGE_HEADER_SIZE]; uint8_t *msgend = &buf[msglen-MESSAGE_TRAILER_SIZE]; while (p < msgend) { - uint_fast8_t cmdid = *p++; + uint_fast16_t cmdid = command_parse_msgid(&p); const struct command_parser *cp = command_lookup_parser(cmdid); uint32_t args[READP(cp->num_args)]; p = command_parsef(p, msgend, cp, args); diff --git a/src/command.h b/src/command.h index 894114d71..21b3f79b8 100644 --- a/src/command.h +++ b/src/command.h @@ -57,11 +57,13 @@ #define MESSAGE_SYNC 0x7E struct command_encoder { - uint8_t msg_id, max_size, num_params; + uint16_t encoded_msgid; + uint8_t max_size, num_params; const uint8_t *param_types; }; struct command_parser { - uint8_t msg_id, num_args, flags, num_params; + uint16_t encoded_msgid; + uint8_t num_args, flags, num_params; const uint8_t *param_types; void (*func)(uint32_t *args); }; @@ -72,6 +74,7 @@ enum { // command.c void *command_decode_ptr(uint32_t v); +uint_fast16_t command_parse_msgid(uint8_t **pp); uint8_t *command_parsef(uint8_t *p, uint8_t *maxend , const struct command_parser *cp, uint32_t *args); uint_fast8_t command_encode_and_frame( @@ -86,7 +89,7 @@ int_fast8_t command_find_and_dispatch(uint8_t *buf, uint_fast8_t buf_len // out/compile_time_request.c (auto generated file) extern const struct command_parser command_index[]; -extern const uint8_t command_index_size; +extern const uint16_t command_index_size; extern const uint8_t command_identify_data[]; extern const uint32_t command_identify_size; const struct command_encoder *ctr_lookup_encoder(const char *str); diff --git a/src/pru/pru0.c b/src/pru/pru0.c index 57d55d279..8a11e1402 100644 --- a/src/pru/pru0.c +++ b/src/pru/pru0.c @@ -141,7 +141,7 @@ do_dispatch(uint8_t *buf, uint32_t msglen) uint8_t *msgend = &buf[msglen-MESSAGE_TRAILER_SIZE]; while (p < msgend) { // Parse command - uint_fast8_t cmdid = *p++; + uint_fast16_t cmdid = command_parse_msgid(&p); const struct command_parser *cp = &SHARED_MEM->command_index[cmdid]; if (!cmdid || cmdid >= SHARED_MEM->command_index_size || cp->num_args > ARRAY_SIZE(SHARED_MEM->next_command_args)) { From 82364d47f2d414548d0619ddeaf57f7e49e7d199 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 1 Jul 2024 23:42:33 +0100 Subject: [PATCH 4/4] motan: Fix logic error resulting in incorrect stepper phase graphing The mcu_phase_offset should be added not subtracted. Signed-off-by: Kevin O'Connor --- scripts/motan/readlog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/motan/readlog.py b/scripts/motan/readlog.py index a2f0b9e11..aa8960b18 100644 --- a/scripts/motan/readlog.py +++ b/scripts/motan/readlog.py @@ -338,7 +338,7 @@ def pull_data(self, req_time): self._pull_block(req_time) continue step_pos = step_data[data_pos][1] - return (step_pos - self.mcu_phase_offset) % self.phases + return (step_pos + self.mcu_phase_offset) % self.phases def _pull_block(self, req_time): step_data = self.step_data