commit 8623d7cd86cbf4cc460150b1bc5d78ab4bfbef41 Author: Zack Buhman Date: Sat Jun 22 17:51:55 2024 -0500 initial: incomplete diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..38e1df7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.out +*.o +*.gch +*.d +main \ No newline at end of file diff --git a/DejaVuSansMono.ttf b/DejaVuSansMono.ttf new file mode 100644 index 0000000..f578602 Binary files /dev/null and b/DejaVuSansMono.ttf differ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d017ed9 --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +SDL ?= ../SDL + +DEBUG = -g -gdwarf-4 + +CFLAGS += -Wall -Werror -Wfatal-errors +CFLAGS += -std=c2x +CFLAGS += -I$(SDL)/include -D_REENTRANT +CFLAGS += -I/usr/local/include +CFLAGS += $(shell pkg-config --cflags freetype2) +LDFLAGS += -L$(SDL)/build -lSDL3 -Wl,-rpath=$(SDL)/build +LDFLAGS += -Wl,-rpath,/usr/local/lib -L/usr/local/lib -lserialport +LDFLAGS += $(shell pkg-config --libs freetype2) + +DEPFLAGS = -MMD -MP + +OPT = -O3 -march=native + +OBJS = \ + timer.o + +all: main + +%.o: %.c + $(CC) $(CARCH) $(CFLAGS) $(OPT) $(DEBUG) $(DEPFLAGS) -MF ${<}.d -c $< -o $@ + +main: $(OBJS) + $(CC) $(LDFLAGS) $^ -o $@ + +-include $(shell find -type f -name '*.d') + +.SUFFIXES: +.INTERMEDIATE: +.SECONDARY: +.PHONY: all clean + +%: RCS/%,v +%: RCS/% +%: %,v +%: s.% +%: SCCS/s.% diff --git a/bufsize.h b/bufsize.h new file mode 100644 index 0000000..2a6b512 --- /dev/null +++ b/bufsize.h @@ -0,0 +1,3 @@ +#pragma once + +#define BUFSIZE 512 diff --git a/packet.h b/packet.h new file mode 100644 index 0000000..aaa0158 --- /dev/null +++ b/packet.h @@ -0,0 +1,42 @@ +#include +#include +#include + +typedef _Float64 float64_t; + +enum packet_type { + PACKET_TYPE__TIME_START = 0x2112858517, + PACKET_TYPE__TIME_STOP = 0xdf75d3f8, + PACKET_TYPE__RTT_DELAY = 0xc7065931, + PACKET_TYPE__ACK = 0xfde7959f, +}; + +struct time_stop_data { + uint32_t integer_value; + uint32_t fraction_value; + uint16_t integer_digits; + uint16_t fraction_digits; +} __attribute__((__packed__)); + +static_assert((sizeof (struct time_stop_data)) == 4 * 3); + +struct rtt_delay_data { + float64_t offset; +} __attribute__((__packed__)); + +struct ack_data { + uint64_t seq; +} __attribute__((__packed__)); + +static_assert((sizeof (struct rtt_delay_data)) == 8); + +struct timing_packet { + uint32_t type; + union { + struct time_stop_data time_stop; + struct rtt_delay_data rtt_delay; + struct ack_data ack; + }; +} __attribute__((__packed__)); + +static_assert((sizeof (struct timing_packet)) == 4 * 4); diff --git a/parse_serial.c b/parse_serial.c new file mode 100644 index 0000000..29dca90 --- /dev/null +++ b/parse_serial.c @@ -0,0 +1,128 @@ +#include +#include +#include + +#include "packet.h" +#include "bufsize.h" + +uint8_t const * parse_int(uint8_t const * buf, int * length, int * number, int * digits) +{ + *number = 0; + *digits = 0; + + while (*length > 0) { + *length -= 1; + uint8_t c = *buf++; + if (c < 0x20 || c > 0x7f) + continue; + switch (c) { + case '0': [[fallthrough]]; + case '1': [[fallthrough]]; + case '2': [[fallthrough]]; + case '3': [[fallthrough]]; + case '4': [[fallthrough]]; + case '5': [[fallthrough]]; + case '6': [[fallthrough]]; + case '7': [[fallthrough]]; + case '8': [[fallthrough]]; + case '9': + *digits += 1; + number *= 10; + number += c - '0'; + break; + case ' ': + break; + default: + buf--; + goto exit; + } + } + exit: + return buf; +} + +int parse_time(uint8_t const * const buf, int length, struct stopwatch_time * time) +{ + if (length < 3) + return -1; + + uint8_t const * bufi = buf; + bufi = parse_int(bufi, &length, &time->integer_value, &time->integer_digits); + + if (integer_digits == 0) { + fprintf(stderr, "invalid integer at `%c`\n", *bufi); + return -1; + } + if (*bufi++ != '.') { + fprintf(stderr, "missing decimal point: `%c`\n", *bufi); + return -1; + } + + if (length < 1) + return -1; + + bufi = parse_int(bufi, &length, &time->fraction_value, &time->fraction_digits); + if (integer_digits == 0) { + fprintf(stderr, "invalid fraction at `%c`\n", *bufi); + return -1; + } + + return 0; +} + +int min(int a, int b) { + return a < b ? a : b; +} + +struct parser_state { + uint8_t buf[BUFSIZE]; + int offset; +}; + +bool is_() +{ +} + +void parse_line(uint8_t * buf, int length, + struct timer_state * timer_state) +{ + if (length == 0) { + timer_state->status = COUNTER_RUNNING; + int ret = clock_gettime(CLOCK_MONOTONIC, &timer_state->counter.start); + timer_state->counter.offset.tv_sec = 0; + timer_state->counter.offset.tv_nsec = 0; + assert(ret != -1); + } else if (is_) { + } +} + +void handle_parse(uint8_t * read_buf, int length, + struct parser_state * parser_state, + struct timer_state * timer_state) +{ + while (length > 0) { + int copy_length = min(length, BUFSIZE - state->offset); + length -= copy_length; + memcpy(&state->buf[state->offset], read_buf, copy_length); + state->offset += copy_length; + + while (state->offset > 0) { + int i = 0; + while (i < state->offset) { + if (state->buf[i] == '\r') { + fprintf(stderr, "r at %d %d\n", i, state->offset); + handle_line(state->buf, i); + state->offset -= i + 1; + assert(state->offset >= 0); + memmove(&state->buf[0], &state->buf[i + 1], state->offset); + break; + } + i++; + if (i == state->offset) + goto exit; + } + } + exit: + continue; + } +} diff --git a/serial_forwarder.c b/serial_forwarder.c new file mode 100644 index 0000000..761f9b2 --- /dev/null +++ b/serial_forwarder.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "bufsize.h" + +#define PORT 4321 +#define SERIALPORT "/dev/ttyS0" + +int sp_error(const char * s, enum sp_return result) +{ + char * error_message; + switch (result) { + case SP_ERR_ARG: + printf("%s: error: Invalid argument.\n", s); + case SP_ERR_FAIL: + error_message = sp_last_error_message(); + printf("%s: error: Failed: %s\n", error_message, s); + sp_free_error_message(error_message); + case SP_ERR_SUPP: + printf("%s: error: Not supported.\n", s); + case SP_ERR_MEM: + printf("%s: error: Couldn't allocate memory.\n", s); + case SP_OK: [[fallthrough]]; + default: + return result; + } +} + +int init_serial(struct sp_port * port) +{ + enum sp_return sp_ret; + + sp_ret = sp_get_port_by_name(SERIALPORT, &port); + if (sp_ret != SP_OK) { + sp_error("sp_get_port_by_name", sp_ret); + return -1; + } + sp_ret = sp_open(port, SP_MODE_READ_WRITE); + if (sp_ret != SP_OK) { + sp_error("sp_open", sp_ret); + return -1; + } + sp_ret = sp_set_baudrate(port, 1200); + if (sp_ret != SP_OK) { + sp_error("sp_set_baudrate", sp_ret); + return -1; + } + sp_ret = sp_set_bits(port, 8); + if (sp_ret != SP_OK) { + sp_error("sp_set_bits", sp_ret); + return -1; + } + sp_ret = sp_set_parity(port, SP_PARITY_NONE); + if (sp_ret != SP_OK) { + sp_error("sp_set_parity", sp_ret); + return -1; + } + sp_ret = sp_set_stopbits(port, 1); + if (sp_ret != SP_OK) { + sp_error("sp_set_stopbits", sp_ret); + return -1; + } + sp_ret = sp_set_flowcontrol(port, SP_FLOWCONTROL_NONE); + if (sp_ret != SP_OK) { + sp_error("sp_set_flowcontrol", sp_ret); + return -1; + } +} + +int handle_sock(int sock) +{ + struct sockaddr_in src_addr; + socklen_t addrlen = (sizeof (struct sockaddr_in)); + + char buf[BUFSIZE]; + + while (true) { + ssize_t recv_len = recvfrom(sock, buf, BUFSIZE, 0, (struct sockaddr *)&src_addr, &addrlen); + if (recv_len == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + perror("eagain"); + return 0; + } else { + perror("recvfrom"); + return -1; + } + } + + printf("got %ld data\n", recv_len); + printf("Received packet from %s:%d\n", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port)); + buf[recv_len] = 0; + printf("Data: %s\n", buf); + } +} + +struct receiver_state { + uint64_t seq; + uint64_t ack_seq; +}; + +int main(void) +{ + int ret; + + int sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); + if (sock == -1) { + perror("socket"); + return -1; + } + + struct sockaddr_in sockaddr = {0}; + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(PORT); + sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); + + ret = bind(sock, (struct sockaddr *)&sockaddr, (sizeof (struct sockaddr_in))); + if (ret == -1) { + perror("bind"); + return -1; + } + +#define MAX_EVENTS 2 + struct epoll_event events[MAX_EVENTS]; + + int epollfd = epoll_create1(0); + if (epollfd == -1) { + perror("epoll_create1"); + return -1; + } + + struct epoll_event ev; + ev.events = EPOLLIN | EPOLLET; + ev.data.fd = sock; + ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &ev); + if (ret == -1) { + perror("epoll_ctl: sock"); + return -1; + } + + while (1) { + printf("Wait for datagram\n"); + + int nfds = epoll_wait(epollfd, events, MAX_EVENTS, 1000); + if (nfds == -1) { + perror("epoll_wait"); + return -1; + } + + for (int n = 0; n < nfds; ++n) { + if (events[n].data.fd == sock) { + ret = handle_sock(sock); + if (ret == -1) + return -1; + } + } + + /* + struct sockaddr_in dest_addr; + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(1234); + dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + ret = sendto(sock, buf, 3, 0, (struct sockaddr *)&dest_addr, (sizeof (struct sockaddr_in))); + if (ret == -1) { + perror("sendto()"); + } + */ + + //sleep(2); + } + + close(sock); + + return 0; +} diff --git a/time_display.c b/time_display.c new file mode 100644 index 0000000..4028a3b --- /dev/null +++ b/time_display.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include + +#define BUFLEN 512 +#define PORT 1234 + +int main(void) +{ + int ret; + + int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) { + perror("socket"); + return -1; + } + + struct sockaddr_in sockaddr = {0}; + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(PORT); + sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); + + ret = bind(sock, (struct sockaddr *)&sockaddr, (sizeof (struct sockaddr_in))); + if (ret == -1) { + perror("bind"); + return -1; + } + + char buf[BUFLEN]; + + while (1) { + printf("Wait for datagram\n"); + + struct sockaddr_in src_addr = {0}; + socklen_t addrlen = (sizeof (struct sockaddr_in)); + + ssize_t recv_len = recvfrom(sock, buf, BUFLEN, 0, (struct sockaddr *)&src_addr, &addrlen); + if (recv_len == -1) { + perror("recvfrom"); + return -1; + } + + buf[recv_len] = 0; + printf("Received packet from %s:%d\n", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port)); + printf("Data: %s\n", buf); + + /* + //now reply the client with the same data + if (sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) == -1) { + die("sendto()"); + } + */ + } + + close(sock); + + return 0; +} diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..d43664e --- /dev/null +++ b/timer.c @@ -0,0 +1,270 @@ +#include +#include +#include + +#include +#include FT_FREETYPE_H + +#include + +#include + +struct glyph { + int32_t width; + int32_t height; + + int32_t horiBearingX; + int32_t horiBearingY; + int32_t horiAdvance; + + SDL_Texture * texture; +}; + +static struct glyph glyphs[0x80 - 0x20] = {0}; +int32_t face_height; + +int +load_outline_char(SDL_Renderer * renderer, + const FT_Face face, + const FT_Int32 load_flags, + const FT_Render_Mode render_mode, + const FT_ULong char_code) +{ + FT_Error error; + FT_UInt glyph_index = FT_Get_Char_Index(face, char_code); + + error = FT_Load_Glyph(face, glyph_index, load_flags); + if (error) { + printf("FT_Load_Glyph %s\n", FT_Error_String(error)); + return -1; + } + + error = FT_Render_Glyph(face->glyph, render_mode); + if (error) { + printf("FT_Render_Glyph %s\n", FT_Error_String(error)); + return -1; + } + + struct glyph * glyph = &glyphs[char_code - 0x20]; + + glyph->width = face->glyph->bitmap.width; + glyph->height = face->glyph->bitmap.rows; + glyph->horiBearingX = face->glyph->metrics.horiBearingX; + glyph->horiBearingY = face->glyph->metrics.horiBearingY; + glyph->horiAdvance = face->glyph->metrics.horiAdvance; + + if (face->glyph->bitmap.pitch != 0) { + SDL_Surface * surface = SDL_CreateSurface(face->glyph->bitmap.width, + face->glyph->bitmap.rows, + SDL_PIXELFORMAT_RGBA8888); + + SDL_LockSurface(surface); + + for (int y = 0; y < face->glyph->bitmap.rows; y++) { + for (int x = 0; x < face->glyph->bitmap.width; x++) { + int s_ix = y * face->glyph->bitmap.pitch + x; + int d_ix = y * surface->pitch + x * 4; + uint8_t gray = face->glyph->bitmap.buffer[s_ix]; + ((uint8_t *)surface->pixels)[d_ix + 0] = gray; + ((uint8_t *)surface->pixels)[d_ix + 1] = gray; + ((uint8_t *)surface->pixels)[d_ix + 2] = gray; + ((uint8_t *)surface->pixels)[d_ix + 3] = 255; + } + } + + SDL_UnlockSurface(surface); + + if (glyph->texture != NULL) + SDL_DestroyTexture(glyph->texture); + glyph->texture = SDL_CreateTextureFromSurface(renderer, surface); + if (glyph->texture == NULL) { + printf("%s\n", SDL_GetError()); + } + assert(glyph->texture != NULL); + + SDL_DestroySurface(surface); + } + + return 0; +} + +int load_font(SDL_Renderer * renderer, int font_size) +{ + FT_Library library; + FT_Face face; + FT_Error error; + + error = FT_Init_FreeType(&library); + if (error) { + printf("FT_Init_FreeType\n"); + return -1; + } + + error = FT_New_Face(library, "DejaVuSansMono.ttf", 0, &face); + if (error) { + printf("FT_New_Face\n"); + return -1; + } + + error = FT_Set_Pixel_Sizes(face, 0, font_size); + if (error) { + printf("FT_Set_Pixel_Sizes: %s %d\n", FT_Error_String(error), error); + return -1; + } + + for (int char_code = 0x20; char_code <= 0x7f; char_code++) { + load_outline_char(renderer, face, FT_LOAD_DEFAULT, FT_RENDER_MODE_NORMAL, char_code); + } + + printf("loaded size %ld\n", face->size->metrics.height >> 6); + face_height = face->size->metrics.height; + + return 0; +} + +void render(SDL_Renderer * renderer, int window_width, int window_height, const char * s, int length) +{ + int32_t advance = (window_width - (window_width * 5 / 100)) << 6; + for (int i = (length - 1); i >= 0; i--) { + char c = s[i]; + struct glyph * glyph = &glyphs[c - 0x20]; + advance -= glyph->horiAdvance; + if (glyph->texture != NULL) { + float x = (float)(advance + glyph->horiBearingX) / 64.f; + float y = (window_height / 2) + (face_height >> 6) / 3 + (float)(-glyph->horiBearingY) / 64.f; + SDL_FRect srect = { + .x = 0.f, + .y = 0.f, + .w = glyph->width, + .h = glyph->height + }; + SDL_FRect drect = { + .x = x, + .y = y, + .w = glyph->width, + .h = glyph->height + }; + SDL_RenderTexture(renderer, glyph->texture, &srect, &drect); + } + } +} + +int max(int a, int b) { + return a > b ? a : b; +} + +int main() +{ + int ret; + SDL_Window * window; + SDL_Renderer * renderer; + + ret = SDL_Init(SDL_INIT_VIDEO); + assert(ret == 0); + + window = SDL_CreateWindow("timer", + 512, // w + 512, // h + SDL_WINDOW_RESIZABLE);// | SDL_WINDOW_MAXIMIZED); + + renderer = SDL_CreateRenderer(window, NULL); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + + SDL_PropertiesID props = SDL_GetRendererProperties(renderer); + + const char * name = SDL_GetRendererName(renderer); + assert(name != NULL); + printf("renderer: %s\n", name); + const SDL_PixelFormatEnum * formats = SDL_GetProperty(props, SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, NULL); + assert(formats != NULL); + while (*formats != SDL_PIXELFORMAT_UNKNOWN) { + printf("%s\n", SDL_GetPixelFormatName(*formats++)); + } + + int last_width = -1; + + uint64_t ticks = SDL_GetTicks(); + const int min_length = 4; + int max_length = min_length; + + /* + */ + + uint8_t read_buf[32] = {'7', '1', '.', '0', '1', '\r'}; + + struct state state = {0}; + + int i = 0; + while (1) { + i++; + //enum sp_return sp_ret = sp_nonblocking_read(port, read_buf, (sizeof (read_buf))); + enum sp_return sp_ret = 6; + if (sp_ret < 0) { + printf("sp_nonblocking_read error\n"); + break; + } + handle_state(read_buf, sp_ret, &state); + + char str_buf[16]; + switch (state.counter_state) { + case COUNTER_STOPPED: + snprintf(str_buf, (sizeof (str_buf)) - 1, "%d.%02d", state.time.whole, state.time.fraction); + break; + case COUNTER_RUNNING: + { + uint64_t counter = SDL_GetPerformanceCounter(); + uint64_t frequency = SDL_GetPerformanceFrequency(); + double duration = (((double)counter) - ((double)state.counter)) / ((double)frequency); + + snprintf(str_buf, (sizeof (str_buf)) - 1, "%.02f", duration); + } + break; + default: + str_buf[0] = 0; + break; + } + int length = strlen(str_buf); + + SDL_RenderClear(renderer); + int window_width; + int window_height; + ret = SDL_GetWindowSizeInPixels(window, &window_width, &window_height); + assert(ret == 0); + if ((window_width != last_width) || (length > max_length)) { + max_length = max(min_length, length); + last_width = window_width; + + int divisor = max(1, (max_length * 8 / 10)); + int font_size = (window_width * 95 / 100) / divisor; + printf("fs %d %d %d\n", font_size, window_width, max_length); + load_font(renderer, font_size); + } + + render(renderer, window_width, window_height, str_buf, length); + + while (SDL_GetTicks() - ticks < (1000 / 60)) { SDL_Delay(1); } + SDL_RenderPresent(renderer); + ticks = SDL_GetTicks(); + + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_EVENT_QUIT: + goto exit; + case SDL_EVENT_KEY_DOWN: + if (event.key.keysym.sym == SDLK_ESCAPE) + goto exit; + break; + default: + break; + } + + + } + } + + exit: + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} diff --git a/timer.h b/timer.h new file mode 100644 index 0000000..e3bf079 --- /dev/null +++ b/timer.h @@ -0,0 +1,22 @@ +#include + +enum counter_status { + COUNTER_STOPPED, + COUNTER_RUNNING, +}; + +struct stopwatch_time { + int32_t whole; + int32_t fraction; +}; + +struct running_counter { + struct timespec start; // CLOCK_MONOTONIC (relative) + struct timespec offset; // CLOCK_MONOTONIC (relative) +}; + +struct timer_state { + enum counter_status status; + struct running_counter counter; + struct stopwatch_time time; +};