From 745ffdd05c777fcc83c956c557ca8086883a5735 Mon Sep 17 00:00:00 2001 From: Ramakrishnan Muthukrishnan Date: Tue, 4 Feb 2025 22:51:31 +0530 Subject: [PATCH] working waterfall on browser via websocket --- Makefile | 7 +- main.c | 29 ++++--- waterfall.c | 173 ++++++++++++++++++++++++++++++++++++++ waterfall.h | 3 +- www/index.html | 219 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 416 insertions(+), 15 deletions(-) create mode 100644 www/index.html diff --git a/Makefile b/Makefile index 9c74803..8b9b546 100644 --- a/Makefile +++ b/Makefile @@ -173,8 +173,11 @@ ifeq ($(UNAME_S), Linux) RT_OPTION=-lrt endif -LIBS=$(RT_OPTION) -lfftw3 -lm -lwdsp -lpthread $(AUDIO_LIBS) $(USBOZY_LIBS) $(GTKLIBS) $(GPIO_LIBS) $(MIDI_LIBS) -INCLUDES=$(GTKINCLUDES) +WEBSOCKETINCLUDES=`pkg-config --cflags libwebsockets` +WEBSOCKETLIB=`pkg-config --libs libwebsockets` + +LIBS=$(RT_OPTION) -lfftw3 -lm -lwdsp -lpthread $(AUDIO_LIBS) $(USBOZY_LIBS) $(GTKLIBS) $(GPIO_LIBS) $(MIDI_LIBS) $(WEBSOCKETLIB) +INCLUDES=$(GTKINCLUDES) $(WEBSOCKETINCLUDES) COMPILE=$(CC) $(CFLAGS) $(OPTIONS) $(INCLUDES) diff --git a/main.c b/main.c index 68ebbbd..2f865c9 100644 --- a/main.c +++ b/main.c @@ -60,6 +60,7 @@ #include "css.h" #include "ext.h" #include "vfo.h" +#include "waterfall.h" #include "log.h" @@ -77,6 +78,7 @@ GtkWidget *grid; static GtkWidget *status; + void status_text(char *text) { gtk_label_set_text(GTK_LABEL(status), text); usleep(100000); @@ -132,24 +134,25 @@ bool keypress_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) { gboolean main_delete(GtkWidget *widget) { if (radio != NULL) { + cleanup_websocket_server(); // Add this line #ifdef GPIO - gpio_close(); + gpio_close(); #endif #ifdef CLIENT_SERVER - if (!radio_is_remote) { + if (!radio_is_remote) { #endif - switch (protocol) { - case ORIGINAL_PROTOCOL: - old_protocol_stop(); - break; - case NEW_PROTOCOL: - new_protocol_stop(); - break; - } + switch (protocol) { + case ORIGINAL_PROTOCOL: + old_protocol_stop(); + break; + case NEW_PROTOCOL: + new_protocol_stop(); + break; + } #ifdef CLIENT_SERVER - } + } #endif - radioSaveState(); + radioSaveState(); } _exit(0); } @@ -159,6 +162,7 @@ static int init(void *data) { audio_get_cards(); + init_websocket_server(); // wait for get_cards to complete // g_mutex_lock(&audio_mutex); // g_mutex_unlock(&audio_mutex); @@ -194,6 +198,7 @@ static int init(void *data) { } g_idle_add(ext_discovery, NULL); + return 0; } diff --git a/waterfall.c b/waterfall.c index a117f4b..b835098 100644 --- a/waterfall.c +++ b/waterfall.c @@ -25,6 +25,8 @@ #include #include +#include + #include "radio.h" #include "vfo.h" #include "waterfall.h" @@ -55,6 +57,12 @@ static gfloat hz_per_pixel; static int display_width; static int display_height; +// Add these global variables near the top +struct lws_context *ws_context = NULL; +static pthread_t websocket_thread; +static int websocket_running = 0; +static struct lws *ws_client = NULL; // Store most recent client + /* Create a new surface of the appropriate size to store our scribbles */ static gboolean waterfall_configure_event_cb(GtkWidget *widget, GdkEventConfigure *event, @@ -110,6 +118,126 @@ static gboolean waterfall_scroll_event_cb(GtkWidget *widget, return receiver_scroll_event(widget, event, data); } +static struct timespec last_write_time = {0, 0}; + +// WebSocket protocol handler +static int callback_waterfall(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) { + switch (reason) { + case LWS_CALLBACK_ESTABLISHED: + printf("WebSocket connection established\n"); + ws_client = wsi; + clock_gettime(CLOCK_MONOTONIC, &last_write_time); + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + double elapsed = (now.tv_sec - last_write_time.tv_sec) * 1000.0 + + (now.tv_nsec - last_write_time.tv_nsec) / 1000000.0; + if (elapsed < 50.0) { // Limit to 20 updates per second + return 0; + } + last_write_time = now; + } + break; + + case LWS_CALLBACK_PROTOCOL_INIT: + printf("WebSocket protocol initialized\n"); + break; + + case LWS_CALLBACK_RECEIVE: + printf("Received data from browser, len: %zu\n", len); + break; + + + case LWS_CALLBACK_CLOSED: + printf("WebSocket connection closed\n"); + if (ws_client == wsi) { + ws_client = NULL; + } + break; + + default: + // printf("Callback reason: %d\n", reason); + break; + } + + return 0; +} + + +static void* websocket_service_thread(void* arg) { + printf("WebSocket service thread started\n"); + while (websocket_running) { + if (ws_context) { + lws_service(ws_context, 10); // 10ms timeout + } + usleep(1000); // Sleep for 1ms between service calls + } + printf("WebSocket service thread ending\n"); + return NULL; +} + +// Define protocols +static struct lws_protocols protocols[] = { + { + .name = "waterfall", + .callback = callback_waterfall, + .per_session_data_size = 0, + .rx_buffer_size = 1024, + }, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +void init_websocket_server() { + struct lws_context_creation_info info; + memset(&info, 0, sizeof info); + + info.port = 8080; + info.protocols = protocols; + info.gid = -1; + info.uid = -1; + info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; + + printf("Creating WebSocket context on port 8080\n"); + + ws_context = lws_create_context(&info); + if (!ws_context) { + fprintf(stderr, "Failed to create WebSocket context\n"); + return; + } + + printf("WebSocket context created, starting service thread\n"); + + // Start the service thread + websocket_running = 1; + if (pthread_create(&websocket_thread, NULL, websocket_service_thread, NULL) != 0) { + fprintf(stderr, "Failed to create WebSocket service thread\n"); + lws_context_destroy(ws_context); + ws_context = NULL; + return; + } + + printf("WebSocket server started successfully on port %d\n", info.port); +} + + +void cleanup_websocket_server() { + if (websocket_running) { + printf("Stopping WebSocket service thread\n"); + websocket_running = 0; + pthread_join(websocket_thread, NULL); + } + + if (ws_context) { + printf("Destroying WebSocket context\n"); + lws_context_destroy(ws_context); + ws_context = NULL; + } +} + void waterfall_update(RECEIVER *rx) { int i; @@ -267,6 +395,51 @@ void waterfall_update(RECEIVER *rx) { } gtk_widget_queue_draw(rx->waterfall); + + + if (ws_client) { + // Calculate min and max for normalization + float min_val = rx->pixel_samples[0]; + float max_val = rx->pixel_samples[0]; + for (int i = 1; i < display_width; i++) { + if (rx->pixel_samples[i] < min_val) min_val = rx->pixel_samples[i]; + if (rx->pixel_samples[i] > max_val) max_val = rx->pixel_samples[i]; + } + + float range = max_val - min_val; + + // Allocate 8-bit buffer (1/4 the size of float buffer) + size_t payload_len = display_width; + unsigned char *buf = malloc(LWS_PRE + payload_len + 8); // Extra 8 bytes for min/max + if (buf) { + // Store min and max as floats at start of buffer for reconstruction + memcpy(&buf[LWS_PRE], &min_val, sizeof(float)); + memcpy(&buf[LWS_PRE + sizeof(float)], &max_val, sizeof(float)); + + // Convert floats to normalized 8-bit integers + for (int i = 0; i < display_width; i++) { + float normalized = (rx->pixel_samples[i] - min_val) / range; + buf[LWS_PRE + 8 + i] = (unsigned char)(normalized * 255); + } + + if (lws_callback_on_writable(ws_client) < 0) { + printf("Failed to request writable callback\n"); + free(buf); + return; + } + + int written = lws_write(ws_client, &buf[LWS_PRE], payload_len + 8, LWS_WRITE_BINARY); + if (written < 0) { + printf("WebSocket write failed\n"); + } else if (written < payload_len + 8) { + printf("Partial write: %d of %zu bytes\n", written, payload_len + 8); + } else { + // printf("Successfully wrote %d bytes\n", written); + } + + free(buf); + } + } } } diff --git a/waterfall.h b/waterfall.h index 3d41f52..85cf941 100644 --- a/waterfall.h +++ b/waterfall.h @@ -22,5 +22,6 @@ extern void waterfall_update(RECEIVER *rx); extern void waterfall_init(RECEIVER *rx,int width,int height); - +extern void init_websocket_server(void); +extern void cleanup_websocket_server(void); #endif diff --git a/www/index.html b/www/index.html new file mode 100644 index 0000000..38b1648 --- /dev/null +++ b/www/index.html @@ -0,0 +1,219 @@ + + + + SDR Waterfall Debug + + + +
Disconnected
+ + + + + -- 2.45.2