Add refresh rle: compressed remote desktop pushes (tinySA ULTRA)

"refresh rle" is a third refresh mode next to off and on (auto_capture 2,
__REFRESH_RLE__). Plain "refresh on" is unchanged, so existing hosts keep
working; firmware without the mode answers "usage: refresh off|on".

- A bulk push of 256 pixels or more is sent as "rle8\r\n" + x, y, w, h,
  u16 palette size + the region's own sorted palette, then the w * h
  palette indices as one PackBits stream without lengths (DiSlord's
  packbits(), 480 indices at a time). Background runs continue over the
  rows. Smaller pushes fit the USB queue and stay "bulk", as do regions
  with more than 256 colours or that might not get smaller.
- The waterfall scrolls the LCD as without remote desktop (4 lines at a
  time) while remote_hold drops the pushes, then sends one "scrl\r\n" +
  x, y, w, h + int16 dy instead of pushing every moved row again.

Tested on HW V0.5.4 (ZS407) with tinySA-App: the screen rebuilt from a
capture and all pushes equals a raw capture (0 pixels differ), every
stream is byte-identical to packbits(). Normal screen 289 -> 34 KB/s,
big waterfall 368 -> 23 KB/s at 2.12 instead of 1.75 sweeps/s.
Costs about 530 bytes of flash and 544 bytes of RAM.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
copilot/add-multi-platform-rf-scan-exports
erikkaashoek 3 weeks ago
parent 2b4aa513b7
commit 4f60bf4906

@ -983,10 +983,21 @@ VNA_SHELL_FUNCTION(cmd_remark)
#ifdef __REMOTE_DESKTOP__
uint8_t remote_mouse_down = false;
uint8_t auto_capture = false;
#ifdef __REFRESH_RLE__
uint8_t remote_hold = false;
static bool send_region_rle8(remote_region_t *rd, const pixel_t *px);
#endif
void send_region(remote_region_t *rd, uint8_t * buf, uint16_t size)
{
#ifdef __REFRESH_RLE__
if (remote_hold) return;
#endif
if (SDU1.config->usbp->state == USB_ACTIVE) {
#ifdef __REFRESH_RLE__
if (auto_capture == 2 && rd->new_str[0] == 'b' && send_region_rle8(rd, (const pixel_t *)buf))
return;
#endif
streamWrite(shell_stream, (void*) rd, sizeof(remote_region_t));
streamWrite(shell_stream, (void*) buf, size);
streamWrite(shell_stream, (void*)"ch> ", 4);
@ -998,7 +1009,11 @@ void send_region(remote_region_t *rd, uint8_t * buf, uint16_t size)
VNA_SHELL_FUNCTION(cmd_refresh)
{
// read pixel count at one time (PART*2 bytes required for read buffer)
#ifdef __REFRESH_RLE__
int m = generic_option_cmd("refresh", "off|on|rle", argc, argv[0]);
#else
int m = generic_option_cmd("refresh", "off|on", argc, argv[0]);
#endif
if (m>=0) {
auto_capture = m;
}
@ -1160,6 +1175,47 @@ static bool capture_rle8(void)
}
return true;
}
#ifdef __REFRESH_RLE__
/*
* "refresh rle": a bulk push of 256 pixels or more is sent RLE8 packed with its own palette:
* "rle8\r\n", x, y, w, h, u16 palette size in bytes, palette, then the palette indices of all
* w * h pixels as one PackBits stream without lengths (the receiver unpacks until it has w * h),
* so runs of background continue over the rows. Smaller pushes fit the USB queue and stay "bulk",
* as do regions with too many colours to get smaller.
*/
static uint16_t rle8_buf[32 + LCD_WIDTH / 2]; // packed part, its indices from byte 64
static bool send_region_rle8(remote_region_t *rd, const pixel_t *px)
{
int n = rd->w * rd->h, i = 0, k, m, x;
pixel_t c = ~px[0];
if (n < 256)
return false;
capture_colors = 0;
for (x = 0; x < n; x++) // the palette of the region
if (px[x] != c && !capture_palette_add(c = px[x]))
return false;
if (2 * capture_colors + n / 64 + 2 > n) // packed it could be larger than raw
return false;
uint16_t size = capture_colors * sizeof(pixel_t);
memcpy(rd->new_str, "rle8\r\n", sizeof(rd->new_str));
streamWrite(shell_stream, (void *)rd, sizeof(remote_region_t));
streamWrite(shell_stream, (void *)&size, sizeof(size));
streamWrite(shell_stream, (void *)capture_palette, size);
uint8_t *index = (uint8_t *)&rle8_buf[32];
for (k = 0, c = ~px[0]; k < n; k += m) { // LCD_WIDTH indices at a time, packed in place
m = n - k < LCD_WIDTH ? n - k : LCD_WIDTH;
for (x = 0; x < m; x++, px++) {
if (*px != c)
i = capture_palette_find(c = *px);
index[x] = i;
}
streamWrite(shell_stream, (void *)rle8_buf, packbits(index, (uint8_t *)rle8_buf, m));
}
streamWrite(shell_stream, (void *)"ch> ", 4);
return true;
}
#endif
#endif
VNA_SHELL_FUNCTION(cmd_capture)

@ -112,6 +112,7 @@
#define __USE_USB_MSC__ // USB DISK mode: SD card as USB mass storage next to the USB serial port
#define __SD_FIRMWARE_UPDATE__ // Install a firmware .bin from the SD card (fw_update.c)
#define __CAPTURE_RLE8__ // capture with an argument sends the screen RLE8 compressed (NanoVNA-D format)
#define __REFRESH_RLE__ // refresh rle: remote desktop pushes RLE8 compressed, the waterfall as a scroll
#define __LCD_BRIGHTNESS__ // LCD or hardware allow change brightness, add menu item for this
#define __HARMONIC__
#define __NOISE_FIGURE__
@ -298,7 +299,10 @@ const char *get_hw_version_text(void);
#ifdef __REMOTE_DESKTOP__
extern uint8_t remote_mouse_down;
extern uint8_t auto_capture;
extern uint8_t auto_capture; // 0 = refresh off, 1 = refresh on, 2 = refresh rle
#ifdef __REFRESH_RLE__
extern uint8_t remote_hold; // drop pushes, e.g. while the waterfall scrolls (pushed as one "scrl")
#endif
typedef struct {
char new_str[6];
int16_t x;
@ -1803,6 +1807,9 @@ void clock_at_48MHz(void);
#if defined(__SD_FIRMWARE_UPDATE__) && (!defined(TINYSA4) || !defined(__USE_USB_MSC__) || !defined(__SD_FILE_BROWSER__))
#error "__SD_FIRMWARE_UPDATE__ needs TINYSA4, __USE_USB_MSC__ and __SD_FILE_BROWSER__"
#endif
#if defined(__REFRESH_RLE__) && (!defined(__REMOTE_DESKTOP__) || !defined(__CAPTURE_RLE8__))
#error "__REFRESH_RLE__ needs __REMOTE_DESKTOP__ and __CAPTURE_RLE8__"
#endif
#ifdef __SD_FIRMWARE_UPDATE__
#include "fw_update.h"
#endif

@ -2205,7 +2205,12 @@ static void update_waterfall(void){
// START_PROFILE;
#ifdef TINYSA4
#define WATERFALL_MULTI (SPI_BUFFER_SIZE / WIDTH)
#ifdef __REFRESH_RLE__
remote_hold = (auto_capture == 2); // refresh rle: scroll as without remote, then push one "scrl"
if (!auto_capture || remote_hold) {
#else
if (!auto_capture) {
#endif
for (i = CHART_BOTTOM-WATERFALL_MULTI; i>=graph_bottom; i-=WATERFALL_MULTI) { // Scroll down WATERFALL_MULTI lines at once
ili9341_read_memory(OFFSETX, i, w_width, WATERFALL_MULTI, spi_buffer);
ili9341_bulk(OFFSETX, i+1, w_width, WATERFALL_MULTI);
@ -2217,6 +2222,14 @@ static void update_waterfall(void){
ili9341_read_memory(OFFSETX, i, w_width, 1, spi_buffer);
ili9341_bulk(OFFSETX, i+1, w_width, 1);
}
#ifdef __REFRESH_RLE__
if (remote_hold) { // rows graph_bottom .. CHART_BOTTOM-1 moved down by dy
remote_region_t rd = {"scrl\r\n", OFFSETX, graph_bottom, w_width, CHART_BOTTOM - graph_bottom};
int16_t dy = 1;
remote_hold = false;
send_region(&rd, (uint8_t *)&dy, sizeof(dy));
}
#endif
index_y_t *index = NULL;
for (int t=0;t<TRACES_MAX;t++) { // Find trace with active measurement
if (IS_TRACE_ENABLE(t) && setting.average[t] == AV_OFF) {

Loading…
Cancel
Save

Powered by TurnKey Linux.