diff --git a/release_notes.md b/release_notes.md index c12025b4..c088aab6 100644 --- a/release_notes.md +++ b/release_notes.md @@ -38,7 +38,12 @@ - Added ALC power limits by band, if RF power exceeds limit it is folded back to limit + Default is off until a limit is set in hw_settings.ini by adding line max_watts=nn to band data after f_stop + When activated message is given in spectrum between Power and VSWR - + Power reduction is released when RF power drops below bamd limit + + Power reduction is released when RF power drops below bamd limit + - Added swrsweep + + usage select a band, then enter cmd \swrsweep n, n where n is the number of sample frequencies + + uses TNPWR to sample vswr at the n sample points evenly spaced between band limits + + displays results in console + + esc key cancels sweep **Changes:** - GUI diff --git a/src/sbitx.c b/src/sbitx.c index d2818697..6b21b66a 100644 --- a/src/sbitx.c +++ b/src/sbitx.c @@ -178,9 +178,12 @@ FILE *pf_debug = NULL; int sbitx_version = SBITX_V2; // never used int fwdpower = 0; +int fwdpower_calc = 0; +int fwdpower_cnt = 0; int vswr = 10; int cur_band; + float fft_bins[MAX_BINS]; // spectrum ampltiudes float spectrum_window[MAX_BINS]; int spectrum_plot[MAX_BINS]; @@ -1835,12 +1838,17 @@ void read_power() // this calculates the power as 1/10th of a watt, 400 = 40 watts float fwdvoltage = (vfwd * 40.0) / bridge_compensation; fwdpw = (fwdvoltage * fwdvoltage) / 400.0; - // replace report once per 100 ticks (~1 s) with every vfrd update (~100 ms) RLB - if ( fwdpower > 0 ) { // fwdpower is displayed power, expoential smoothing a=.5 - fwdpower = round((5.0*fwdpw + 5*fwdpower)/10.0); - } else { - fwdpower = round(fwdpw); // start history + + if (fwdpw > fwdpower_calc) { + fwdpower_calc = fwdpw; + } + if (!fwdpower_cnt) { + fwdpower = fwdpower_calc; + fwdpower_calc = fwdpw; } + if (!fwdpower) + fwdpower = fwdpw; + fwdpower_cnt = ++fwdpower_cnt % 50; // display new value every 1/2 s float cpower=(float)fwdpw/10.0;; float climit = band_power[cur_band].max_watts; diff --git a/src/sbitx_gtk.c b/src/sbitx_gtk.c index f4d4f94e..6dcbc4f7 100644 --- a/src/sbitx_gtk.c +++ b/src/sbitx_gtk.c @@ -8806,6 +8806,10 @@ static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer use { case MIN_KEY_ESC: // TODO we could do a 2-stage esc: call it with false the first time, true the second + if (swr_sweep_is_running()) + { + swr_sweep_cancel(); + } modem_abort(true); tx_off(); call_wipe(); @@ -11695,6 +11699,24 @@ else if (!strcasecmp(exec, "decode")) } write_console(STYLE_LOG, output); } + + else if (!strcasecmp(exec, "swrsweep")) + { + int steps = 10; + + if (strlen(args) > 0) + steps = atoi(args); + + if (steps < 2) + { + write_console(STYLE_LOG, "Usage: \\swrsweep , steps must be >= 2\n"); + } + else + { + swr_sweep(steps); + } + } + /* else if (!strcasecmp(exec, "PITCH")){ struct field *f = get_field_by_label(exec); field_set("PITCH", args); diff --git a/src/swr_monitor.c b/src/swr_monitor.c index cac16524..78b9749b 100644 --- a/src/swr_monitor.c +++ b/src/swr_monitor.c @@ -6,6 +6,11 @@ #include "swr_monitor.h" #include "sdr_ui.h" #include "sdr.h" +#include +#include +#include + + /* define a default maxv_swr of 3 initialize as enabled but not tripped @@ -29,7 +34,7 @@ Internally vswr_tripped tracks whether max_vswr was exceeded and vswr_on tracks whether enabled or disabled */ - +extern int set_field(const char *id, const char *value); // Maximum VSWR threshold (default 3.0) float max_vswr = 3.0f; @@ -42,11 +47,13 @@ int vswr_on=1; * Check VSWR and handle reduction/recovery * vswr parameter: SWR * 10 (e.g., 30 means 3.0) - project convention */ + int call_count = 0; void check_and_handle_vswr(int vswr) { // Convert from integer representation to float (vswr / 10.0) float swr = vswr / 10.0f; // Check if VSWR exceeds threshold and not already tripped + call_count++; if (swr > max_vswr && vswr_tripped == 0 && vswr_on==1) { // char response[100]; @@ -57,21 +64,14 @@ void check_and_handle_vswr(int vswr) // Set tripped flag vswr_tripped = 1; // printf(" tripped %d\n",vswr_tripped); // - - snprintf(sdr_cmd, sizeof(sdr_cmd), "tx_power=%d", 1); - sdr_request(sdr_cmd, response); - - // Update DRIVE field in GUI to reflect reduced power - char drive_buff[32]; - snprintf(drive_buff, sizeof(drive_buff), "%d", 1); - field_set("DRIVE", drive_buff); - + set_field("tx_power", "1"); // Write warning to console char warning_msg[128]; snprintf(warning_msg, sizeof(warning_msg), "\n *VSWR WARNING: SWR %.1f exceeds threshold %.1f\n", swr, max_vswr, 1); write_console(STYLE_LOG, warning_msg); + printf("on %.1f %d\n", swr, call_count); } // Check if VSWR has fallen below threshold and was previously tripped else if (swr <= max_vswr && vswr_tripped == 1) { @@ -84,7 +84,7 @@ void check_and_handle_vswr(int vswr) "\n *VSWR: SWR %.1f back below threshold %.1f, UI cleared\n", swr, max_vswr); write_console(STYLE_LOG, info_msg); - + printf("off %.1f %d\n", swr, call_count); // Do NOT restore the drive value - leave it reduced for safety } } @@ -96,3 +96,196 @@ void init_vswr_monitor(void) vswr_tripped = 0; vswr_on=1; } + +/* SWR sweep + Power and VSWR update every 100 ms, so each step must be >= 200 ms. + Use 250 ms to leave a little margin. */ +const int settle_ms = 250; +extern int vswr; +extern int in_tx; + +extern void set_rx1(int frequency); +extern void do_control_action(char *cmd); +extern int field_int(char *label); +extern int field_set(const char *label, const char *new_value); + +/* band limits are loaded from hw_settings.ini into band_power[] in sbitx.c */ +struct power_settings +{ + int f_start; + int f_stop; + int max_watts; + double scale; +}; + +extern struct power_settings band_power[]; + +#define SWR_SWEEP_BAND_COUNT 9 +#define SWR_SWEEP_MIN_SETTLE_MS 200 +#define SWR_SWEEP_DEFAULT_SETTLE_MS 250 +#define SWR_SWEEP_MIN_DRIVE 10 + +static const char *swr_sweep_band_names[SWR_SWEEP_BAND_COUNT] = { + "80M", "60M", "40M", "30M", "20M", "17M", "15M", "12M", "10M" +}; + +static int g_swr_sweep_running = 0; +static int g_swr_sweep_cancel = 0; + +static int find_sweep_band_index(int freq) +{ + int i; + + for (i = 0; i < SWR_SWEEP_BAND_COUNT; i++) { + if (freq >= band_power[i].f_start && freq <= band_power[i].f_stop) + return i; + } + + return -1; +} + +int swr_sweep_is_running(void) +{ + return g_swr_sweep_running; +} + +void swr_sweep_cancel(void) +{ + if (g_swr_sweep_running) + g_swr_sweep_cancel = 1; +} + +static void swr_sweep_pump_events(void) +{ + while (gtk_events_pending()) + gtk_main_iteration_do(FALSE); +} + +static int sleep_with_cancel_and_events(int total_ms) +{ + int elapsed = 0; + + while (elapsed < total_ms) { + swr_sweep_pump_events(); + + if (g_swr_sweep_cancel) + return 1; + + usleep(50000); + elapsed += 50; + } + + return 0; +} + + +void swr_sweep(int steps) +{ + char msg[256]; + int saved_freq; + int saved_drive; + int saved_vswr_on; + int was_in_tx; + int settle_ms = SWR_SWEEP_DEFAULT_SETTLE_MS; + int band_idx; + int start; + int stop; + int i; + int freq; + + if (steps < 2) { + write_console(STYLE_LOG, "Usage: \\swrsweep , steps must be >= 2\n"); + return; + } + + if (g_swr_sweep_running) { + write_console(STYLE_LOG, "SWR sweep already running\n"); + return; + } + + g_swr_sweep_running = 1; + g_swr_sweep_cancel = 0; + + if (settle_ms < SWR_SWEEP_MIN_SETTLE_MS) + settle_ms = SWR_SWEEP_MIN_SETTLE_MS; + + saved_freq = get_freq(); + saved_drive = field_int("DRIVE"); + saved_vswr_on = vswr_on; + was_in_tx = in_tx; + + band_idx = find_sweep_band_index(saved_freq); + if (band_idx < 0) { + write_console(STYLE_LOG, "SWR sweep: current frequency is outside configured band limits\n"); + goto cleanup; + } + + start = band_power[band_idx].f_start; + stop = band_power[band_idx].f_stop; + + snprintf(msg, sizeof(msg), + "\nSWR sweep on %s: %d to %d Hz in %d steps, %d ms per step. Press ESC to cancel.\n", + swr_sweep_band_names[band_idx], start, stop, steps, settle_ms); + write_console(STYLE_LOG, msg); + + /* prevent protection from dropping power mid-sweep */ + vswr_on = 0; + + /* ensure enough drive for meaningful SWR readings */ + if (saved_drive < SWR_SWEEP_MIN_DRIVE) { + char drive_buf[16]; + snprintf(drive_buf, sizeof(drive_buf), "%d", SWR_SWEEP_MIN_DRIVE); + field_set("DRIVE", drive_buf); + swr_sweep_pump_events(); + } + + if (!was_in_tx) { + do_control_action("TUNE ON"); + if (sleep_with_cancel_and_events(300)) + goto canceled; + } + + for (i = 0; i < steps; i++) { + if (g_swr_sweep_cancel) + goto canceled; + + freq = start + ((long long)(stop - start) * i) / (steps - 1); + set_rx1(freq); + swr_sweep_pump_events(); + + if (sleep_with_cancel_and_events(settle_ms)) + goto canceled; + + snprintf(msg, sizeof(msg), "%.3f MHz SWR %.1f\n", freq/1000000., vswr / 10.0f); + write_console(STYLE_LOG, msg); + swr_sweep_pump_events(); + } + + write_console(STYLE_LOG, "SWR sweep complete\n"); + goto cleanup_after_tx; + +canceled: + write_console(STYLE_LOG, "SWR sweep canceled\n"); + +cleanup_after_tx: + if (!was_in_tx) { + do_control_action("TUNE OFF"); + swr_sweep_pump_events(); + usleep(100000); + } + + set_rx1(saved_freq); + swr_sweep_pump_events(); + + { + char drive_buf[16]; + snprintf(drive_buf, sizeof(drive_buf), "%d", saved_drive); + field_set("DRIVE", drive_buf); + } + + vswr_on = saved_vswr_on; + +cleanup: + g_swr_sweep_running = 0; + g_swr_sweep_cancel = 0; +} diff --git a/src/swr_monitor.h b/src/swr_monitor.h index fda741c9..dcacf3a3 100644 --- a/src/swr_monitor.h +++ b/src/swr_monitor.h @@ -19,4 +19,8 @@ void check_and_handle_vswr(int vswr); // Reset VSWR tripped state and clear UI void reset_vswr_tripped(void); +void swr_sweep(int steps); // sweep functions +void swr_sweep_cancel(void); +int swr_sweep_is_running(void); + #endif // SWR_MONITOR_H