diff --git a/docs/claude/probe-readings.md b/docs/claude/probe-readings.md index f962fc3..c7fe8a8 100644 --- a/docs/claude/probe-readings.md +++ b/docs/claude/probe-readings.md @@ -1165,14 +1165,15 @@ through one row type, for the reason in `invariants-gtk-and-chrome.md`. The form ## `trigger panel:` ``` -[udpscope] trigger panel: boxes=13 controls_inside=13/13 search_widgets=4/4 search_inside=4/4 +[udpscope] trigger panel: boxes=15 controls_inside=15/15 search_widgets=4/4 search_inside=4/4 ``` The trigger panel's frame, read from the window: `boxes` is the row boxes the `.ui` holds, `controls_inside` -counts the thirteen controls the code built and then appended into those rows, `search_widgets` is the four +counts the fifteen controls the code built and then appended into those rows -- the last two being the +noise band and the HF reject, whose rows arrived with the noise-rejection work, `search_widgets` is the four search buttons having resolved by id, and `search_inside` is those four being inside the block the expander opens onto. A control built and never placed -- an id that drifted between the file and the code -- reads as -`controls_inside` below 13 while every other line still looks right, which is the panel that has a field +`controls_inside` below 15 while every other line still looks right, which is the panel that has a field missing. The search walk starts at `gtk_expander_get_child()`, not at the panel: an expander's child is not in the diff --git a/src/ui.cpp b/src/ui.cpp index 9594be9..7c57ecd 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -5812,7 +5812,7 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit (app.search_run_button != nullptr) + (app.search_cancel_button != nullptr) + (app.search_prev_button != nullptr) + (app.search_next_button != nullptr); std::printf( - "[udpscope] trigger panel: boxes=%d controls_inside=%d/13 search_widgets=%d/4 " + "[udpscope] trigger panel: boxes=%d controls_inside=%d/15 search_widgets=%d/4 " "search_inside=%d/4\n", boxes, inside(app.trigger_signal_drop, app.trigger_panel) + @@ -5827,6 +5827,8 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit inside(app.trigger_level_spin, app.trigger_panel) + inside(app.trigger_pre_spin, app.trigger_panel) + inside(app.trigger_holdoff_spin, app.trigger_panel) + + inside(app.trigger_band_spin, app.trigger_panel) + + inside(app.trigger_hf_spin, app.trigger_panel) + inside(app.events_record_switch, app.trigger_panel), search_present, inside(app.search_run_button, search_root) + inside(app.search_cancel_button, search_root) + diff --git a/src/ui/trigger-panel.ui b/src/ui/trigger-panel.ui index 9659bb0..8429fc4 100644 --- a/src/ui/trigger-panel.ui +++ b/src/ui/trigger-panel.ui @@ -162,6 +162,30 @@ + + + horizontal + 6 + + + noise band + start + + + + + + + horizontal + 6 + + + HF reject Hz + start + + + + horizontal diff --git a/src/ui_app.hpp b/src/ui_app.hpp index 363d63b..595d81a 100644 --- a/src/ui_app.hpp +++ b/src/ui_app.hpp @@ -170,6 +170,8 @@ struct App { GtkWidget* trigger_level_spin = nullptr; GtkWidget* trigger_pre_spin = nullptr; GtkWidget* trigger_holdoff_spin = nullptr; + GtkWidget* trigger_band_spin = nullptr; ///< EventCondition::noise_band + GtkWidget* trigger_hf_spin = nullptr; ///< EventCondition::hf_fc, 0 is off GtkWidget* signal_list = nullptr; GtkWidget* pause_button = nullptr; /// When the pause began, on the **monotonic wall clock**, or 0 while running. The elapsed time diff --git a/src/ui_trigger.cpp b/src/ui_trigger.cpp index 140e0aa..c8a9b72 100644 --- a/src/ui_trigger.cpp +++ b/src/ui_trigger.cpp @@ -69,6 +69,8 @@ void publish_trigger_fields(App* app) { const double width2 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(app->trigger_width2_spin)); const double level2 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(app->trigger_level2_spin)); const double timeout = gtk_spin_button_get_value(GTK_SPIN_BUTTON(app->trigger_timeout_spin)); + const double band = gtk_spin_button_get_value(GTK_SPIN_BUTTON(app->trigger_band_spin)); + const double hf = gtk_spin_button_get_value(GTK_SPIN_BUTTON(app->trigger_hf_spin)); { std::lock_guard lock(app->model->mutex()); TriggerConfig& tc = app->model->trigger(); @@ -85,6 +87,8 @@ void publish_trigger_fields(App* app) { tc.condition.width2_s = width2; tc.condition.level2 = level2; tc.condition.timeout_s = timeout; + tc.condition.noise_band = band; + tc.condition.hf_fc = hf; } } @@ -110,6 +114,11 @@ void sync_trigger_rows(App& app) { show(app.trigger_width2_row, pulse && (cmp == WidthCmp::Within || cmp == WidthCmp::Outside)); show(app.trigger_level2_row, window); show(app.trigger_timeout_row, type == TriggerType::Dropout); + // A band widens the comparator every type but Dropout compares with; a dropout is an absence and a + // band cannot describe one. The HF reject sits before every comparison, so it applies to all types. + // The rows come from the file and hold their spin, so the spin's parent *is* the row. + show(gtk_widget_get_parent(app.trigger_band_spin), type != TriggerType::Dropout); + show(gtk_widget_get_parent(app.trigger_hf_spin), true); // A band of zero width reduces the condition to a float-equality test on a real-valued // signal, which is unusable: it fires on the exact value and nothing else, which in practice // is never. The panel is where that is refused: the second level is pushed at least one @@ -282,6 +291,13 @@ GtkWidget* build_trigger_panel(App& app) { app.trigger_holdoff_spin = spin(0.0, 3600.0, 0.001, 4, app.model->config().trigger_holdoff_s); row_for(ui, "holdoff_row", app.trigger_holdoff_spin); + // A band in signal units, on the same scale as the level spins. + app.trigger_band_spin = spin(0.0, 1.0e12, 0.01, 4, 0.0); + row_for(ui, "band_row", app.trigger_band_spin); + // 0 is off. The upper bound is generous; `configure()` clamps to just under the stream's Nyquist, + // which the panel cannot know. + app.trigger_hf_spin = spin(0.0, 1.0e7, 1.0, 3, 0.0); + row_for(ui, "hf_row", app.trigger_hf_spin); const char* modes[] = {"normal", "single", nullptr}; app.trigger_mode_drop = gtk_drop_down_new_from_strings(modes); @@ -314,7 +330,8 @@ GtkWidget* build_trigger_panel(App& app) { for (GtkWidget* w : {app.trigger_level_spin, app.trigger_pre_spin, app.trigger_holdoff_spin, app.trigger_width_spin, app.trigger_width2_spin, - app.trigger_level2_spin, app.trigger_timeout_spin}) { + app.trigger_level2_spin, app.trigger_timeout_spin, app.trigger_band_spin, + app.trigger_hf_spin}) { g_signal_connect(w, "value-changed", G_CALLBACK(on_trigger_field_changed), &app); } g_signal_connect(app.trigger_edge_drop, "notify::selected",