From 6c84324fc4ded3094f20e39bd04ed55ffe7eb16a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Sat, 23 Sep 2023 21:49:18 +0200 Subject: [PATCH] Can read and decode TIC data (badly tested) --- TIC.raw.sample | 195 ++++++++++++++++++++++++++++++++++++ main/CMakeLists.txt | 2 +- main/Kconfig.projbuild | 44 ++++++++ main/main.c | 10 +- main/tic.c | 174 ++++++++++++++++++++++++++++++++ main/tic.h | 75 ++++++++++++++ main/uart.c | 102 +++++++++++++++++++ main/uart.h | 6 ++ test_cpu/Makefile | 18 ++++ test_cpu/README.md | 2 + test_cpu/cpu_test_harness.c | 31 ++++++ test_cpu/cpu_test_harness.h | 11 ++ test_cpu/cpu_test_harness.o | Bin 0 -> 7504 bytes test_cpu/test_tic.bin | Bin 0 -> 32096 bytes test_cpu/test_tic.c | 36 +++++++ test_cpu/tic.o | Bin 0 -> 18096 bytes tester/feed_tic.py | 54 ++++++++++ 17 files changed, 758 insertions(+), 2 deletions(-) create mode 100644 TIC.raw.sample create mode 100644 main/Kconfig.projbuild create mode 100644 main/tic.c create mode 100644 main/tic.h create mode 100644 main/uart.c create mode 100644 main/uart.h create mode 100644 test_cpu/Makefile create mode 100644 test_cpu/README.md create mode 100644 test_cpu/cpu_test_harness.c create mode 100644 test_cpu/cpu_test_harness.h create mode 100644 test_cpu/cpu_test_harness.o create mode 100755 test_cpu/test_tic.bin create mode 100644 test_cpu/test_tic.c create mode 100644 test_cpu/tic.o create mode 100755 tester/feed_tic.py diff --git a/TIC.raw.sample b/TIC.raw.sample new file mode 100644 index 0000000..45308c3 --- /dev/null +++ b/TIC.raw.sample @@ -0,0 +1,195 @@ +SE 000051519 + +PTEC TH.. $ + +IINS +ADCO 042375023610 8 + +OPTARIF BASE 0 + +ISOUSC 30 9 + +BASE 000051521 Y + +PTEC TH.. $ + +IINST 001 X + +IMAX 090 H + +PAPP 00250 ( + +HHPHC A , + +MOTDETAT 000000 B + +ADCO 042375023610 8 + +OPTARIF BASE 0 + +ISOUSC 30 9 + +BASE 000051521 Y + +PTEC TH.. $ + +IINST 001 X + +IMAX 090 H + +PAPP 00250 ( + +HHPHC A , + +MOTDETAT 000000 B + +ADCO 042375023610 8 + +OPTARIF BASE 0 + +ISOUSC 30 9 + +BASE 000051521 Y + +PTEC TH.. $ + +IINST 001 X + +IMAX 090 H + +PAPP 00250 ( + +HHPHC A , + +MOTDETAT 000000 B + +ADCO 042375023610 8 + +OPTARIF BASE 0 + +ISOUSC 30 9 + +BASE 000051521 Y + +PTEC TH.. $ + +IINST 005 \ + +IMAX 090 H + +PAPP 01250 ) + +HHPHC A , + +MOTDETAT 000000 B + +ADCO 042375023610 8 + +OPTARIF BASE 0 + +ISOUSC 30 9 + +BASE 000051521 Y + +PTEC TH.. $ + +IINST 005 \ + +IMAX 090 H + +PAPP 01160 ) + +HHPHC A , + +MOTDETAT 000000 B + +ADCO 042375023610 8 + +OPTARIF BASE 0 + +ISOUSC 30 9 + +BASE 000051521 Y + +PTEC TH.. $ + +IINST 004 [ + +IMAX 090 H + +PAPP 00980 2 + +HHPHC A , + +MOTDETAT 000000 B + +ADCO 042375023610 8 + +OPTARIF BASE 0 + +ISOUSC 30 9 + +BASE 000051522 Z + +PTEC TH.. $ + +IINST 004 [ + +IMAX 090 H + +PAPP 01020 $ + +HHPHC A , + +MOTDETAT 000000 B + +ADCO 042375023610 8 + +OPTARIF BASE 0 + +ISOUSC 30 9 + +BASE 000051522 Z + +PTEC TH.. $ + +IINST 004 [ + +IMAX 090 H + +PAPP 01020 $ + +HHPHC A , + +MOTDETAT 000000 B + +ADCO 042375023610 8 + +OPTARIF BASE 0 + +ISOUSC 30 9 + +BASE 000051522 Z + +PTEC TH.. $ + +IINST 004 [ + +IMAX 090 H + +PAPP 01010 # + +HHPHC A , + +MOTDETAT 000000 B + +ADCO 042375023610 8 + +OPTARIF BASE 0 + +ISOUSC 30 9 + +BASE 000051522 Z + +PTEC TH.. $ + diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index cf2c455..0498f3d 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,2 +1,2 @@ -idf_component_register(SRCS "main.c" +idf_component_register(SRCS "tic.c" "uart.c" "main.c" INCLUDE_DIRS ".") diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild new file mode 100644 index 0000000..fa9b66a --- /dev/null +++ b/main/Kconfig.projbuild @@ -0,0 +1,44 @@ +menu "Enedis TIC configuration" + + orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps" + + config TIC_UART_PORT_NUM + int "UART port number" + range 0 2 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3 + default 1 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3 + range 0 1 + default 1 + help + UART communication port number towards Enedis' TIC + + config TIC_UART_BAUD_RATE + int "UART communication speed" + range 1200 115200 + default 1200 + help + UART communication speed for TIC. 1200 for historique, 9600 for + standard + + config TIC_UART_RXD + int "UART RXD pin number" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 5 + help + GPIO number for UART RX pin. See UART documentation for more information + about available pin numbers for UART. + +# config TIC_UART_TXD +# int "UART TXD pin number" +# range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX +# default 4 +# help +# GPIO number for UART TX pin. See UART documentation for more information +# about available pin numbers for UART. + + config TIC_UART_TASK_STACK_SIZE + int "UART task stack size" + range 1024 16384 + default 2048 + help + Defines stack size for UART echo example. Insufficient stack size can cause crash. +endmenu diff --git a/main/main.c b/main/main.c index 7b66f33..8076a44 100644 --- a/main/main.c +++ b/main/main.c @@ -1,6 +1,14 @@ #include +#include "driver/uart.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "uart.h" +#include "esp_log.h" + +static const char *TAG = "MAIN"; void app_main(void) { - + ESP_LOGI(TAG, "Arriving in main\n"); + xTaskCreate(uart_listen_task, "uart_listen_task", TIC_TASK_STACK_SIZE, NULL, 10, NULL); } diff --git a/main/tic.c b/main/tic.c new file mode 100644 index 0000000..5f963e2 --- /dev/null +++ b/main/tic.c @@ -0,0 +1,174 @@ +#include +#include +#include "tic.h" + +static const char FRAME_START = 0x02; +static const char GROUP_START = 0x0a; +static const char FIELD_SEP = 0x20; + +DecodeState* create_decode_state(size_t queue_size) { + DecodeState* out = malloc(sizeof(DecodeState)); + out->cur_frame = NULL; + out->frame_queue = xQueueCreate(queue_size, sizeof(InfoFrame*)); + out->checksum = 0; + out->fill_step = GFS_NotStarted; + out->postpone_buffer[0] = '\0'; + out->postpone_buffer_size = 0; + return out; +} + +void free_decode_state(DecodeState* st) { + vQueueDelete(st->frame_queue); + if(st->cur_frame != NULL) { + tic_delete_frame(st->cur_frame); + free(st->cur_frame); + } + free(st); +} + +void tic_delete_group(InfoGroup* group) { + free(group->time_str); + if(group->data_type == GROUP_DATA_STR) + free(group->data.str); +} +void tic_delete_frame(InfoFrame* frame) { + for(size_t g_id=0; g_id < frame->group_count; ++g_id) + tic_delete_group(frame->groups + g_id); +} + +char* tic_discard_until_frame(char* buffer, size_t buf_size) { + size_t pos=0; + for(pos=0; pos < buf_size; ++pos) { + if(buffer[pos] == FRAME_START) { + break; + } + } + if(pos == buf_size) + return NULL; + return buffer + pos; +} + +const uint32_t BAD_STR_TO_INT = ~0U; +uint32_t str_to_int(char* buffer, const char* buffer_end) { + uint64_t res = 0; + for(char* chr=buffer; chr != buffer_end; ++chr) { + if('0' <= *chr && *chr <= '9') { + res *= 10; + res += (*chr - '0'); + } else { + return BAD_STR_TO_INT; + } + } + if(res > BAD_STR_TO_INT) + return BAD_STR_TO_INT; + return res; +} + +static void get_data_pointers(char** start, char** end, const char* buffer, char* pos, DecodeState* state) { + if(*start == NULL) { // Postponed data + *start = state->postpone_buffer; + size_t copy_size = pos - buffer; + if(copy_size + state->postpone_buffer_size > POSTPONE_BUFFER_MAXSIZE) + copy_size = POSTPONE_BUFFER_MAXSIZE - state->postpone_buffer_size; + char* postpone_end = state->postpone_buffer + + state->postpone_buffer_size; + memcpy( + postpone_end, + buffer, + copy_size); + *end = postpone_end + copy_size; + } else { // No postponed data + *end = pos; + } +} + +int tic_decode(char* buffer, size_t buf_size, DecodeState* state) { + int frames_decoded = 0; + char* start = NULL; + char* data_end = NULL; + + for(char* pos = buffer; pos != buffer + buf_size; ++pos) { + if(state->fill_step == GFS_Checksum) { // End of frame -- verify checksum + if(((state->checksum & 0x3F) + 0x20) != *pos) { + state->fill_step = GFS_Invalid; + continue; + } + state->fill_step = GFS_Done; + continue; + } + state->checksum += *pos; // Update checksum + + if(*pos == FRAME_START) { + if(state->cur_frame != NULL) { + frames_decoded += 1; + if(uxQueueSpacesAvailable(state->frame_queue) == 0) { + tic_delete_frame(state->cur_frame); + free(state->cur_frame); + } + else + xQueueSend(state->frame_queue, state->cur_frame, 0); + } + state->cur_frame = malloc(sizeof(InfoFrame)); + memset(state->cur_frame, 0, sizeof(InfoFrame)); + state->cur_frame->group_count = 0; + } + else if(*pos == GROUP_START) { + // Reset checksum + state->checksum = 0; + + if(state->fill_step == GFS_Done) { + state->cur_frame->group_count++; + state->fill_step = GFS_NotStarted; + } else if(state->fill_step == GFS_Invalid) { + memset(state->cur_frame->groups + state->cur_frame->group_count, + '0', sizeof(InfoGroup)); + state->fill_step = GFS_NotStarted; + } else if(state->fill_step == GFS_NotStarted) { + state->fill_step = GFS_Tag; + start = pos + 1; + } else { + state->fill_step = GFS_Invalid; + } + } + else if(*pos == FIELD_SEP) { + InfoGroup* cur_group = + state->cur_frame->groups + state->cur_frame->group_count; + if(state->fill_step == GFS_Tag) { + get_data_pointers(&start, &data_end, buffer, pos, state); + size_t label_size = data_end - start + 1; + if(label_size > INFO_GROUP_LABEL_MAX_SIZE) + label_size = INFO_GROUP_LABEL_MAX_SIZE; + memcpy(cur_group->label, start, label_size-1); + cur_group->label[label_size-1] = '\0'; + state->fill_step = GFS_Data; + start = pos + 1; + } + else if(state->fill_step == GFS_Data) { + get_data_pointers(&start, &data_end, buffer, pos, state); + uint32_t int_val = str_to_int(start, data_end); + if(int_val == BAD_STR_TO_INT) { + size_t len = data_end - start + 1; + cur_group->data_type = GROUP_DATA_STR; + cur_group->data.str = malloc(sizeof(char) * len); + memcpy(cur_group->data.str, start, len-1); + cur_group->data.str[len-1] = '\0'; + } else { + cur_group->data_type = GROUP_DATA_NUM; + cur_group->data.num = int_val; + } + state->fill_step = GFS_Checksum; + state->checksum -= *pos; + } + } + } + + if((state->fill_step == GFS_Tag || state->fill_step == GFS_Data) && start != NULL) { + size_t postpone_size = (buffer + buf_size) - start; + if(postpone_size > POSTPONE_BUFFER_MAXSIZE) + postpone_size = POSTPONE_BUFFER_MAXSIZE; + memcpy(state->postpone_buffer, start, postpone_size); + state->postpone_buffer_size = postpone_size; + } + + return frames_decoded; +} diff --git a/main/tic.h b/main/tic.h new file mode 100644 index 0000000..c964d5c --- /dev/null +++ b/main/tic.h @@ -0,0 +1,75 @@ +#pragma once +#include + +#ifndef CPU_TEST +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#else +#include "cpu_test_harness.h" +#endif + +#define INFO_GROUP_LABEL_MAX_SIZE 12lu +typedef struct { + char label[INFO_GROUP_LABEL_MAX_SIZE]; + char* time_str; + union { + uint32_t num; + char* str; + } data; + enum { + GROUP_DATA_NUM, + GROUP_DATA_STR + } data_type; +} InfoGroup; + +#define INFO_FRAME_MAX_GROUPS 12lu +typedef struct { + InfoGroup groups[INFO_FRAME_MAX_GROUPS]; + uint8_t group_count; +} InfoFrame; + +typedef QueueHandle_t InfoFrameQueue; + +typedef enum { + GFS_NotStarted, + GFS_Tag, + GFS_Data, + GFS_Checksum, + GFS_Done, + GFS_Invalid +} GroupFillStep; + +#define POSTPONE_BUFFER_MAXSIZE 32lu +typedef struct { + InfoFrameQueue frame_queue; + InfoFrame* cur_frame; + char checksum; + GroupFillStep fill_step; + char postpone_buffer[POSTPONE_BUFFER_MAXSIZE]; + size_t postpone_buffer_size; +} DecodeState; + +/// Initialize a decode state +DecodeState* create_decode_state(size_t queue_size); +/// Frees a DecodeState +void free_decode_state(DecodeState*); + +/// Deletes the contents of an InfoGroup +void tic_delete_group(InfoGroup* group); +/** Deletes the contents of an InfoFrame. The call to free() itself must be + * done by the user */ +void tic_delete_frame(InfoFrame* frame); + +/** + * Discard characters until just before a frame is started, and return a + * pointer to the beginning of the next frame (including the start of frame + * marker), or NULL if no frame was started. + */ +char* tic_discard_until_frame(char* buffer, size_t buf_size); + +/** + * Decode the buffer, updating the decode state and emitting frames as they are + * finished. + * Returns how many groups were decoded. + */ +int tic_decode(char* buffer, size_t buf_size, DecodeState* state); diff --git a/main/uart.c b/main/uart.c new file mode 100644 index 0000000..c17f740 --- /dev/null +++ b/main/uart.c @@ -0,0 +1,102 @@ +/* UART reception file -- reads data from Linky's TIC */ +#include +#include "driver/uart.h" +#include "driver/gpio.h" +#include "esp_sleep.h" +#include "esp_log.h" + +#include "tic.h" + +/** + * This is an example which echos any data it receives on configured UART back to the sender, + * with hardware flow control turned off. It does not use UART driver event queue. + * + * - Port: configured UART + * - Receive (Rx) buffer: on + * - Transmit (Tx) buffer: off + * - Flow control: off + * - Event queue: off + * - Pin assignment: see defines below (See Kconfig) + */ + +#define TIC_RXD (CONFIG_TIC_UART_RXD) +#define TIC_RTS (UART_PIN_NO_CHANGE) +#define TIC_CTS (UART_PIN_NO_CHANGE) + +#define TIC_UART_PORT_NUM (CONFIG_TIC_UART_PORT_NUM) +#define TIC_UART_BAUD_RATE (CONFIG_TIC_UART_BAUD_RATE) +static const char *TAG = "TIC UART"; + +#define BUF_SIZE (1024) +#define TIC_QUEUE_SIZE 64 + +static void uart_light_sleep() { + // TODO + /* + ESP_LOGI(TAG, "Entering sleep.\n"); + vTaskDelay(10/portTICK_PERIOD_MS); + ESP_ERROR_CHECK(esp_light_sleep_start()); + esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause(); + ESP_LOGI(TAG, "Woken up: %d\n", wakeup_cause); + */ +} + +void uart_listen_task(void *arg) +{ + /* Configure parameters of an UART driver, + * communication pins and install the driver */ + uart_config_t uart_config = { + .baud_rate = TIC_UART_BAUD_RATE, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_DEFAULT, + }; + int intr_alloc_flags = 0; + +#if CONFIG_UART_ISR_IN_IRAM + intr_alloc_flags = ESP_INTR_FLAG_IRAM; +#endif + + ESP_ERROR_CHECK(uart_param_config(TIC_UART_PORT_NUM, &uart_config)); + ESP_ERROR_CHECK(uart_set_pin(TIC_UART_PORT_NUM, UART_PIN_NO_CHANGE, TIC_RXD, TIC_RTS, TIC_CTS)); + ESP_ERROR_CHECK(uart_driver_install(TIC_UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags)); + //ESP_ERROR_CHECK(uart_set_wakeup_threshold(TIC_UART_PORT_NUM, 3)); + //ESP_ERROR_CHECK(esp_sleep_enable_uart_wakeup(TIC_UART_PORT_NUM)); + + // Configure a temporary buffer for the incoming data + char *data = (char *) malloc(BUF_SIZE); + + // Setup the TIC decoding + DecodeState* tic_state = create_decode_state(TIC_QUEUE_SIZE); + int initialized = 0; + + // Discard any data already buffered + ESP_ERROR_CHECK(uart_flush(TIC_UART_PORT_NUM)); + + ESP_LOGI(TAG, "Start receiving.\n"); + while (1) { + int len = uart_read_bytes(TIC_UART_PORT_NUM, data, (BUF_SIZE - 1), 30 / portTICK_PERIOD_MS); + if (len) { + if(!initialized) { + char* init_begin = tic_discard_until_frame(data, len); + if(init_begin != NULL) { + size_t discarded = init_begin - data; + initialized = 1; + tic_decode(init_begin, len - discarded, tic_state); + } + } else { + tic_decode(data, len, tic_state); + } + + data[len] = '\0'; + ESP_LOGI(TAG, "Recv str: %s", (char *) data); + } + else { + uart_light_sleep(); + } + } + + free_decode_state(tic_state); +} diff --git a/main/uart.h b/main/uart.h new file mode 100644 index 0000000..889629b --- /dev/null +++ b/main/uart.h @@ -0,0 +1,6 @@ +#pragma once + +#include "sdkconfig.h" +#define TIC_TASK_STACK_SIZE (CONFIG_TIC_UART_TASK_STACK_SIZE) + +void uart_listen_task(void* arg); diff --git a/test_cpu/Makefile b/test_cpu/Makefile new file mode 100644 index 0000000..adfa5a1 --- /dev/null +++ b/test_cpu/Makefile @@ -0,0 +1,18 @@ +ESP_DIR=../main/ +COMMON_OBJS=cpu_test_harness.o +ESP_OBJS=tic.o +CFLAGS=-Wextra -O2 -g -I$(ESP_DIR) -I. -DCPU_TEST +CC=gcc + +TARGETS=test_tic.bin + +all: $(TARGETS) + +test_%.bin: test_%.o $(COMMON_OBJS) $(ESP_OBJS) + $(CC) $(CFLAGS) $^ -o $@ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.o: $(ESP_DIR)/%.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/test_cpu/README.md b/test_cpu/README.md new file mode 100644 index 0000000..552e6a2 --- /dev/null +++ b/test_cpu/README.md @@ -0,0 +1,2 @@ +Harness to test relevant parts of the code on a standard CPU instead of the +ESP32. diff --git a/test_cpu/cpu_test_harness.c b/test_cpu/cpu_test_harness.c new file mode 100644 index 0000000..f693071 --- /dev/null +++ b/test_cpu/cpu_test_harness.c @@ -0,0 +1,31 @@ +#include +#include +#include "cpu_test_harness.h" +#include "tic.h" + +QueueHandle_t xQueueCreate(int a, int b) { + return NULL; +} + +void vQueueDelete(QueueHandle_t _) {} + +int uxQueueSpacesAvailable(QueueHandle_t _) { + return 1; +} + +void xQueueSend(QueueHandle_t _, void* elt, int _b) { + InfoFrame* frame = elt; + printf(">> SENT: Frame with %u groups.\n", frame->group_count); + for(size_t grp_id=0; grp_id < frame->group_count; ++grp_id) { + InfoGroup* grp = frame->groups + grp_id; + printf("Group %02lu: %s %s ", grp_id, grp->label, + (grp->data_type == GROUP_DATA_NUM) ? "num" : "str"); + if(grp->data_type == GROUP_DATA_NUM) + printf("%u\n", grp->data.num); + else + printf("%s\n", grp->data.str); + } + + tic_delete_frame(frame); + free(frame); +} diff --git a/test_cpu/cpu_test_harness.h b/test_cpu/cpu_test_harness.h new file mode 100644 index 0000000..0280c43 --- /dev/null +++ b/test_cpu/cpu_test_harness.h @@ -0,0 +1,11 @@ +#pragma once +#ifndef CPU_TEST +#define CPU_TEST +#endif + +// ========== FreeRtos queues ========== +typedef void* QueueHandle_t; +QueueHandle_t xQueueCreate(int, int); +int uxQueueSpacesAvailable(QueueHandle_t); +void xQueueSend(QueueHandle_t, void* elt, int); +void vQueueDelete(QueueHandle_t); diff --git a/test_cpu/cpu_test_harness.o b/test_cpu/cpu_test_harness.o new file mode 100644 index 0000000000000000000000000000000000000000..e8a034e51607de3d7243b8484d53ee5975147dfb GIT binary patch literal 7504 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 h?|l$~GBnwttM;f5>{{!EX&@2D| literal 0 HcmV?d00001 diff --git a/test_cpu/test_tic.bin b/test_cpu/test_tic.bin new file mode 100755 index 0000000000000000000000000000000000000000..253a28ea5e41db643c6b093597f2316f065d750b GIT binary patch literal 32096 zcmeHwdwf*Ywg28T=bSk+lgvyWkl`g03>Z-I;29)fLI7tVAdf^teGDO)Kq@3@GC^&% zNeTFCn#KmTReQ0}+FILsE4THcrCKQ{SW$nJ+v}sY7QI-La#cjy;*0#gYdN{UQ6e<7fT)CAMZfsmPYewP5`l%Zmo7!)ldUMIFsk3Gj zv^5t@6DE*dMMxA=S1zw+j!w3ZXj+~#E^s$!bT|~Q%ZWD|iQG=|Z_d2qC!f6V`>Vcr z*TZT2re}}OeKy)bJk$=7As#x2MoAxJzUC4Bkrjwm5q-|79zBG#$gsQ=SmrQ_L~_nU ze*pB5WEpmz4eVTct~w8$T5~S`Dd(X#oQFR7JoKkP4Uelf;2)o$F_Tn8%RuP)jauWgM- zH`O*av4)n`#-?}!YiMqXHL;fVcpGbIjm217ygt_2$~MI|)wSG0q}qB&L$L0~Xx)Yz zqYbr<8(E#yN;)ZoXR#|QBMT~`(+Y}bB)}nP`Vcs+U?z)3s+L7jNo;*%TRhfUwX9-e zb5pFUcHPDpYFfXkxk=d>m8|Dvq!HjmBs=iW5NC`Cj2^-zRr-aabQ3`84?6DN{w;*H z$!-EnwCu*lbQ%F$6#l3vce1Z5ynS>dhMa@l1Dp;$?`nVVQ=R;ZnpZWwMbT@@gjiyJ z)cC$DB%Q{X7+ex%&nD5;6e>!NB+)&Jm$Fxq=)NTSu_U@biLT>?<{9l1QCudOT*xTq z^kJ7ti0H}lL?DSSu7i?|1(WDv{!sK#5}kDEBfm^SMAN<_N>Le|86&!$^9U~~qqC%| zORT`l%0wpk(ky9 z@ShP*OQV6^0{=1Lv@{vmCGhVNPD_%3K7oIma9WBCbPD`V!f9DC&?4}!5l%~wfwcnP zLO3ls237zszvihafGd0F&Glo|eRX+tdDWug|{8kiF9e?k?nM~|~#r5G!rTy(0=%JG0eb-)J zeqH(XIpI#mAn)f*Z(Gl@$f+-$J-@tnCD*KA}C37_NC1b?@IQcwDU->l%gWh@b(si&@hr9nu z!D9RO>^TPJ{w1Fd4t73CAoFj%4d+Prd&RF+_Rf4C>(rj4p&Vjbe%X_X2Rd&Bo+5M3SiN9~#7x6E80Rn&g1R>mW zO=#QE_`gPaI)eQ>KsfQlP@5@2@`bvmZNNhTFt84s4Sg{G)2GT{rD#L{3m|sC_bVDM z;X`GV!Pf`+UqUDxtOx*hhPz)mR6#ot>|jL@Ocf=92@}Az@Oxi*b)@^#@+vWYFiBj| z{}YHV>xtRRwjGb(P~Q1Op72b3X{7hMyg>0gC&tSDF8A-*DvH8g&v3LI?S7_hLbzvc z|K5)W2fMx$;O&11_k79j{|V3&zlD&DcT!#1{dWJ|Ak!>VsoOZP1(b%q)#2`s!#y2= z{x+c|#N+0*h%JndF9ok1xPf?voCSL4Q_{Yx%Bv#XpHvsWvpU=x4V<`0*LO}kN0XhI zqJo5-PM{}#+eZ$k`gA;pxBV@CY4On$0X;st_Ve(Zm*P2{r8$oF_X$i3_Zm5xKQ%tX zbN(K$EZ#>3b&}(|{!#??&T!Wu9kd! z?xBTwh+V{Tz8JUsWcgPY2mE^~21_0B)>4mus|V3n8uD*lMNv5CkMWuQJ<}uIe}W?y z2Pnw9K61AIrt8=hUHd|1B`5t`Gr?DWXmK`8@q6h6q4vXFe*q)E$s6t}&13#;Pl5?E zTpGGOe<-FAf03>)+bkZB^vv%sM=WpK*IvD>=gW4v>R12PK}as^Z4O|PKQRXJ>fiGi z^?0P~X$-e#@*{Ij`@3EyLeFiifBuIUUR}4LQ(p#d_ZI*^@jT57k)F*qDk(nN{}<|a zn8L-Z|5QI@`>!Uso~yGX-HYu37d)Pb*Ybml1FY*K!@u=wVC?$H^lu%&biM7Ue;e^p z!$D_z=@jlBn>X+qj32}g6~O&$FP#P+1yPQhNY7%-JiEn=(K};LWv_7(e)4av6xtpB zZQWp%PTT6Gt4oiOS2^d5e`~wA)a)<*8--FC>*_}w&DayBaJ&~#OXZx8{9FGCrul#6 z=)L(!S?8d8^Y1Hr^76_Lm4(QFwbC-#jxoScwh^Vv4Hl{xhy&k?yi! zf0HIhddh+e4jLtFU?Zud@n_^kdan+KCqMIe2-+~*!~QG#yZgI7wf$R`qk?cx8aTcj z3U|GICfvi}%3ybYxF^ofguCO8GvUb}^)4KNE6Q)Xj-82gztDBYZp{H$e6;(euKo71 zo`o}EqA`2U{?>OR-AyF}FJNwM=%Zk$>>li||NG$Jz#`$>PE?QnTx=H~?d&8+a&k5i z2P#QMUT;O*ZG8t+GJP+_d}Q*WNbi*xzDHpC{%}`6CayDfTj@iFe;b7XT@f>>8U8&P z9OkI) zO=@+x>m$4Mr-X#NUn)Mjtfz7WTAh}Ieze1LPPM*!V!O1z>~GJ&szz9~ZJ&SZ2?`2f zSaJDRHwR(a)>p_EkHz`f26*?Z3cE5^*;ADlfOAU3!Y0zY96c=h{pIlF{o&qH9`2@Y zn&Ap}oz1Q6eyP%5fdKfZKmIor5D|W8;JM*`Ztypod04T9e^I0k|DHY>!P$3W3OiDK zG~68y`7@Y5qb!i2YxA(C_Dy)y)XJ`G zZEkM~O)Q$WvHkMU#5VfRCboOn{Q05PiW1k734*y2L}VV4zEQ{&vo}9UrX0zO!RT0BmXMlIK;XDIsf7+#%~VrF=;Nl z4?2iW4^DT(mPyFjLYH{_LM!DNNd50hTbVDg*cV*l_ugc8vMWZ-?{9&XJd_q?~I*7IpX)I_kSsX%g}Ue0LBZ z>g;ta@&&){EcbvF92`0Rz=MbLX0${$5} zKFZgS-le|4*BuLe!P}h`zEGFZAuNF379KeGJc`L5$H89%{^`VzgJ@?!+X`BhphzF3 zAtsK1b{pNL2p^#rlKV^eXp!{MTE}YxEu~9gi!5rXN#=XuIitlMemvnyw%FilRLz60Fz<6nLMK$8D{^wV!dj zC~)xv8$x(+AaHFr)$#x0(C?3@);)`EhH&61hB&TPd4tL~tNeDAe@o>Ls(g>i52{>h z!Tna5gs!ZpxIC1Px0fe}il-Ogh11ZqqG{8MN{Wibt(@#W*$GFK3URxv^(5tV>*ruT z^q}HbuWbHf{-cO6#h)dW_39W%(oLWTbdzzxoc#bS8%Ip>c6n0Tp|8RNj^oHBAA zmDuK^$nZ!3q`VeFQVjLPCG%yb)&W-+r@uGhF&ksd5B*@Y&W>6BX{#nR1)<}0rEIMNF{59 zZTonfO4ixkQ1CP}#vn6YqxOJr6m29Jm-^Ca)5=;2#3HhXx{IvxlDQy80H=RSP&+|i zAjS@y1mk@G1Y;Jn{slZ~M6jrn(CyPdBf05v?1-w(D=`w)=u{j%y<14&q) z>vLmFxmKc#x!fq^uC0I_uIB+eU1cB}uJ>^^neitS*!j*^k!6-$dL!icgQV`V>=#I! zze&i{02SDLDkye=@OpsLOko#hQB~R8CN)K#Logx8=>>MXIE~_{gjr;@5v1~I&Jkd~ z=p#}&+qwg=gHFt9!P(af@=}wA^UtV+9OxhwlE-%vsu#M5`uwnB=BMYybv%tE`>E(5 za5}z&(|G2;AK4!re$ta}g1nd%3a#Qqtju4Gvx|%P$|ok06({C<40g}-k(KkPgoIe= zFo@hGOii80`3{37ZF*K}XOb9A+_Vs}=~qf_FU9Z-pan#l4#T}}vioyLE0A(CNOhX< zqjP&-hy5y8(gn*`QM?6d0%ApUIt*;(X~A0j<~bn)laPuWz)vWYK*I!6Y{^rw)1 ziInvv#c&rabn=ysFB5T{B9i-^yxOrD=PC!8=QdRlx#vpgq+)vtbB|GU@@<=>yPd8O zL~uab?KDCHhK`wvm-=Xj(jltg#bkmVa`IJ<+rYR~F;YK2Dj2IA9iV?p(aGr7lNH!) zXk?xpnxvZIen@fN3eLYNPBby^MMbARViW%azfckjIJz3r&TU$xg($&G}onj2xJCLbv`f21`L|%GF`s|95jA_^GFxf z6k=&)X$=XHbC`psIV;CWH3a*)HQL<`$e?L%jGsU@ie@=$Z2A*`G0UZqEL~!iJWX=e zI43>$Fo`_693AR<#F0*~6WY{#Hl_$9Yo7HYQNE888@r6aMxIs<tzGD)|?kF3-8`%3u?;yEj8{;gWDzHf};w$4q_9MwNBdxE5|5BKE_Dy_Z83M&^84l}l#ZB%EKthgsHsM7@ z{}Je*IUnFv=mF}SKqi`#W|l+xq#7F-U*h09YM{auWoc8$mAsnyOfY+nDLzOmzD~*K z$yI!TGay)g!t{UxK`~QHT(!&!QJuWnwT@Yr2(XG>yaM_cBjHZw>zEd43{#bti7`xr zL5yM57`+yz)v8|AQkc5r7G3GsT%umQq zy9qQ?j(7r@DMvhs1xz{OU7!a|IpRen()9lUvM^n(Y%t`hf?_e`9|vU0Ax|KiriT1c zz{fZnNy5(}X$5M`-vyFb*jVFpJ_f5WJl7XM=Xu1!#^VMD_lSj!M=WeSW#BVBG=|l} z#>#i13O1qa(v6TaX<>6&_U~|JCar6x28e8%w5%!cP$vXTTGbS0Q8n2nEozE9G&coJ zTGJG#(Ks$)Ub0s#X{>3^v0x3}g5EGGWE{bl09pikg5yjC0)e6FKLnLe5Bvp)+qofx z3`b@$F`Oqcu(SNQ$GKV7&(^iLYAOQz~l68I44J4k7J zP~p{Xs=NY)mmsA@;gM^+r0Wz=ng!FgfvZN49zjL_gfyChd@?j;w=}o0&xwhJE)x!< z(f^Dx8`uA|EU3CY7zW#E&}mgNnkM_DAg9sF?Q21@?-HOFBjqgv+Jagk39KTWlg3xU`7(); z=C=X7K?rHKG1(eOqJ}X5MiD|aOaYLO%~BLyx~2Hb`@ z)4{8Z?YlcHhrEE0A!82Vly(*_xO+6sAIl)K6lvC3wZpW4xNuObvNy(>*tf=#tF8r4 zHB!bHn%EzhyM)D<0MKRJ`LbqLP+xnghmFE@NhnWNraGx zX9JjtBs}~kbO{eH24x|V@bC%%%aH=EF`_nhZV%@@0(JXTYaL?2ad#d+4B`zCu0i7a zfZPfojx>f=B-Try`~=ueB>x*g#NG6p0R95XACSZ`q7)ay$3`kF0j*So#t}!s$rpoZ z5=18;O;F%7qN^fihMglIUAKD)SRAvj;Q9v zEbzPvIMOJB5Y>#CM^npvi0%gi6txYIx&aB(Ani(c>3TGKJ&HME<;N&Ur#Yu$5`mY* zn(A4_3_+z?tzywtkh_FtnTpjkDW%gaRuQ9FZ0xutkXtP37-s>Dh%5qp`Mb*BTSgYTda zm`wU6QaP`lOw;WQYAml3Q)0fjkllf5zlOx7(BwFQx-Fd+wG$@LWpV7d7|gg>*bkOx zP%y5YSdI`hf#yv%hi>J0wiU_-g5wUE!Gv#n7`wbJj>3uLj0cgvgEW^+o3FT6Y3>|% z6zsMLN(#nQ1`&*hAbtQz1Y?s@!I_38C}k=bUjZkL7!i!ODsBuZ1!=-K>nf^Q1miYE zl)<<;8ACA6R5)EaGj=2uZiNDhty}R^fuAPPDH0#RBAAV$Jp{Lp+;BuKMVITA@5sUm zHS0Qf)J;D%V*OyEA)Vda9%r6RlHqX3$H+7OLkZKY`k|`SE@Od@6rC>OWXNWmt9S|S zN2o`4E0eT;_>Oi|DF*Bq6lWk!B)(@AA7e{k$2e;}xP)Kk9ZhN!b3d7kT}0h^Oi^fi zk`BIOG6tiVVwRzJ5t5&J6|P{SC)3(g)S(UWSrG*@=6fXTcy7C<)#?zV8ce!`2caArbRRU)^Ocp^;IU`sjei`$U zF*1n(Ypy)z_0IP?eNgUa-dvlQUHRY{OFc1zm~**Q=g4_%0p>k-nBT@bvN#w$oJR!U z%|$JRkVu;?s!Pv;U@4Xvz5;&5DAHk*j!Q;GvPwshq+iqBbIoLW0QTF8n@q_t`21X# zH`j<{DJn^4W^u1q;<;`jWJ#M4!5Ms_FU#v3KW2P&P6iLa-VtaQEE}m>3J>N?5=I8I zxYGwc!ds+e)R1qmDidv2p1fidHD*i>RhwWR3oFbablfo4qgO6cZ4?5RKm*k=o~{j{ z;U$r*44&yK3w}{>BLL?VG zk%$RWo@-q)D!KLkp^(cBB-96VxCnbFjFM$ChB}?d)ajW+y`3fdKU;SCh~a%W67l8r zq8&s1m?MHK(aVUEM20c06@AVg!z` zrBw+x^;cqjshoua^G#kym#rq0}tO&LidYt8p*n16wQ<~5`mI57H1A8 z>MPK!p%FK`02czO>5@?)FEykjA*4c1lIIL3Cr?6`i+NF*qk?fR`Ci1im%^MBmnMze zc{G^RD4s9IEKLQY5kDp#sDGYDGUp%r(kTNG_x}WqchMLxLE3=yMWj2C?nc^y)v_7T)zZB}00X4y;NsS#bmG&5rIc?G8Vl^S^G zd)Qr|NNRxb=k%3DzA8XNbu>{V4Mh?8S~#4OzZ?`rB6ggK*x|FmVv^HL>93jOFN8TA zqscuptqe1`)SP(@g6DH)cAFXe3I$LWMUa`b#vDmaibxW-Mw;0rfZNS+C^fC6=6I2d zw$+5p$<}<*ITU$V5cr(Q-+$o~Y6wWlwSPg1KyYxNH2xUXaH0LdW5u}7~2tm=`aiX>)Z{q@1xw8N)>df7sG|mQGtH_oLjWDt z!;*wHt`X5;TAvrznbzmbELe#V&5O*r)~iAsmW`JheAYDx%SSNAv&@XS0GFBtLZROr zz0AxgRoIo9AL>b6u+;RHn%URYm^_Hy>V+lQrzDSaGrVItJq4g~v%+$Yc$VtS%1#;+ z6$t)I$$)Q~6^m+Q;9qZgA2Ty<$NVuH{dwFR1!=dnL1^(VF@2WPw*;Nc3Ci3eAUTel z)m!vfgLN~}kemi{oHMV+^g6Fyf+np|la6!JadQF`D6yJnl^7WUN5bgCSU^n#$6_ME z=T7%2$U7hJz(A7|SG%0?#K_!Yj)1!?=Px=CH_6={pg}HsIzGIow+zMNZ*(kfSFzYC zVzB{AoiD%+%!OZk3AL}KxzqGHXB{^$BD0)Js7*_=elJE!I{KF^%eL+^M~eJzk!M(4 zqOHh5&8eYgtleu~1SH++a!x!2t}L^3i8&>11|y(V3N@MNxIQtbXIR_Kbm#6448|-n z@L|l_&LgnP757K6PEp~*&K#m--%cG|gD4!imwcZOEmXx4vjWjo90z3Wg0cylUqWqb zXwt;&yhV%`fZIE2L|mmkhk^oOtMlLCVj8_8tb5EUBJaY~M9rww!D8kD4>=#~Kpp=z zqu~|j<-CuK8_U1*teNJ#r-K&|716!K5x{jR8f|W9h{o|{%d4XFZOv#4-ipG9HN_`6 z8#ivmr!QkItPP*;1XJCHSlx|nc!Mh1wxPK-zOKE2UAcI5bRj;Mi7#cg<5j;Br3yvS zb+v6VhEHj(4~bKIQ(NQurdWN5-U!6*X}kquQ7|^dTBB3}TiG6KkA-WS>NiqljZKa5 z=sJ9qvx(K$#%rVTTUwIUkrsSb6i*_Whm5DK+GxYZ+VyRMMpg(?U3+V^u6bkorY2!< zq-it0MhaFmx~{oCR<}7W=vB4rS+sd$J^7?92K}N<)2FFMZK`d(G1e*!YZPBIrONP) zQta1KI})ETjW=$J!Ef**^xxFl7>^0B3ZI3BxgpZj&@6sXDw@vXjdl1CDOn^$+iEw% zLFB~-`q63h3*;ZRv36Z-Ba7lIsyC{(PGiwcBtv~sp}UCMF1$)@7xGEwkWmTf6au0jMI1(3YU}aA+4iRPwpjf% z{cT$@hR7Sj#fV54CIr{P^!64R0*Of5h_BSLI#h_TlEI?7PkcsqDCDFy^!wz+7{{w= z7~d?yY&Dr8!3v>)uF)GJV7{M>= z+8Z~<8=Ioy!@cSwzam~`5U!8KMTbXQnh^vPIEz=6FI$AzC|^}2$2VCeVp~ciIz$BV zg7SqFN71UQqLJlQay+!D<_|MUxKM?aR4xax=(Hg->*NQ6VM`QK!O(O>M$mwfY8FQp zRifTSE0P+hK17V+9gR0fDWXB4$)c{gV zD~vazi{gc?h@$3Ag|W7l>C@zSDt@Y7h+n10qjfFqtgyYUmA=+pxW2Bg5YZi-HDhW^ zo%(F|`lj|m`QCY9V^iJ6cFY2TUm=C-8u1Zl>*;>pfk$3AlDQm3I}KLJuQMvIcNFh5V&CV0g~WAQ=fZ@}*?hHe#g}l} z#=pkJ3*=B1OBCCv(jI}wL8yvZ`ZY}hJBuxBI~#j(%T5D}o^}PZ2f>xxK%;0buQukM z zo=orcr?fz@R%zC%oszXmv#NH;<_gWfGSH%58NW2q!M`+LG@)08aT%`w-NpMN5Ex==&6mjGld|BhiF}@h4sT zZ5}m#?s>|C?2WYJX;usCU=wy4$I}MVKpLw4jdY+-_@44VkA?&cB0>4p8 z)S_xVS2h%o?;6nYF8(>&I(8Z#7+XvX*)8S+Cddz%51GP|FPd)?{}l;{-kcHv>zymVxC5;heZP9w8+3I|IM%W9*jR<-C4vPFOR zOdkp%!EWt3Opn4KGmxg>GKQBh zT{+_|>WV0QuKMjU*>8`1CR}BEiCsJ6D z(1Sxd<4zi?*CGU-KnO@@kOo~F>XK-}8M{coaKJ?L+IQKQ`R{_f^9vZeLYk&}NUuztjif_p0HXo#x=*{&Ofl6mjOw!{*>jc( zuakw>Q5f83VYuJcl5~~)tKH?cH_PF()97-)>&A8VP4A90WUu*7`S{i15@VcneCQyR zA3E;Cd)O+F(DW5+5kG5RrG>_;{Eu{X{v&@q=@M=j3S>cMq| zW`y0&!=(E#*q!Z&f}>8MIuAPaD#WZxthz2SX3S0!SlJ1is=r>FTtNuVOSYXg%XGKJ|2ajXtl||1_>ExI9zM z58;9Ub55o)oF7ZW{COPU8T+K$==bhQ6H!gwbIwCdSM2@4)Ixd%$2bR_uF>bBll-~p z<3ZZaxX%FUoX=1duq*1;kkn)4z|_1vCOP@s+LAA1G`Pbj>xC-*O)Mw?GdK zu_d7Oa72g&*^06RNdWX%h5{`0H~<3Kb4(RNTpULPT_H)?n}Y89%&S`;fF3}-Ys-?D z0e*x;`t{>6Inm+mAyg#lRWCc~*RnGeJ@q9h`T_;D}MbrX}Kz(M^5Kz zZ`*n3tIk7@fllpOQKoB@dFy%jZwEazoK;}o1U&;!pKHp77k=+NdLAWy)U1{1rRxDj z-w={|H2vs#^t=Un0DteOC8gdQ1wZ;l%}0}zS$|Rdsr~pj#s8U~DB;p8`WL3saaLxG z-Zdq9>f4t=L03#k8T|-@_)}jPT>v`yZEaZ+Gr;AbhlY~`Mqj)aah3Ao-z}g|I0ygT z#P7f?q3qWz-v<4heJ!M zwb7O(HpY@RB;_9g`^=uS_;X;xln57{V+S`H#b54x$*6|}VCFPgQ+Z%HE2 zu2=n3?50Yt@&%EpaXfku>>Fy^Hn4*FTbfXX%<&*Wlx)VfX=8KKFeHky*4V~c5>QYJ ze#KZIEH8-1ZU!uZr=Yc2L|Z{@gWR}{Zm5T>#-v2FwzahuhLVJap}2NaV;yR3hAx~6 zLsUpHe~7RNp%uXA0$ z*n0s-s{OT~(R(afUVryS=l3dyk&Ogu`5<6A8j+|HH4uD^Su^A$=y)qX{0y#UCkTK<@-NawF8MqPhu`|nZm z3lyLJj*reyCduajPL=;IXyg;EKXJWP*Io6SB&9{#nm_>1`$oy~Atk5t-O0>=RdFK! zVT!!I@6vg1(%W#PO3M>W2mLZHxqiJ4)p@$$8ak!M@6VOI_CE_LUX^z$RA;I6{}KgM zzn1R|Nlu;LD$&F_wf^6rBw3#2Dpr-#FKv?#-G7?)ZvbddObxrl-*eP|&oz}_<2wHn zWa(E{T3&y*>XoS=;?R5&8^SgH1PUm2w7k9#OZ$|B_Ty>Lh(*iqyMnkF z+gB*_e3>QAT3+WLq9j>f-`Djk`3(t431~T;J0P4aukR;T6iI$;8tVJUP#cf8V(Du2a!N(p&>s9EO!oT5Cne!5LPmwa`rl&Aad +#include +#include "tic.h" + +#define BUF_SIZE 32 + +int main(void) { + DecodeState* state = create_decode_state(42); + FILE* tic_handle = fopen("../TIC.raw.sample", "rb"); + + //int initialized = 0; + char buffer[BUF_SIZE]; + int buf_size = 0; + while(1) { + buf_size = fread(buffer, sizeof(char), BUF_SIZE, tic_handle); + fprintf(stderr, "Read %d bytes\n", buf_size); + if(buf_size == 0) + break; + /* + if(!initialized) { + char* init_begin = tic_discard_until_frame(buffer, buf_size); + if(init_begin != NULL) { + size_t discarded = (init_begin - buffer); + initialized = 1; + tic_decode(init_begin, buf_size - discarded, state); + } + } else { + */ + tic_decode(buffer, buf_size, state); + //} + } + + free_decode_state(state); + + return 0; +} diff --git a/test_cpu/tic.o b/test_cpu/tic.o new file mode 100644 index 0000000000000000000000000000000000000000..554bead553f8420970871fd8ac6ad8d3dd0404b6 GIT binary patch literal 18096 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 t.Iterator[bytes]: + FRAME_START = 0x02 + next_frame = b"" + # Discard first bytes + raw_data = raw_data[raw_data.find(FRAME_START) :] + + for ch in raw_data: + # Discard first bytes + if ch == FRAME_START and next_frame: + yield next_frame + next_frame = b"" + next_frame += bytes([ch]) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--port", default="/dev/ttyUSB1") + parser.add_argument("-b", "--baud", type=int, default=1200) + parser.add_argument( + "--frame-gap", type=int, default=1500, help="Time between two frames (in ms)" + ) + parser.add_argument( + "recorded_data", + help="File containing real recorded data to replay", + type=Path, + ) + args = parser.parse_args() + + data: bytes = b"" + with args.recorded_data.open("rb") as h: + data = h.read() + + with serial.Serial(port=args.port, baudrate=args.baud) as ser: + for frame in iter_frames(data): + print(f"Feeding {len(frame)}b frame") + start_time = time.monotonic() + ser.write(frame) + elapsed_time = time.monotonic() - start_time + sleep_time = args.frame_gap / 1000 - elapsed_time + if sleep_time > 0: + time.sleep(sleep_time) + + +if __name__ == "__main__": + main()