--buffer-samples (k/M/G) replaces --buffer-mb: the request is rounded down to a power of two for every signal and the shared time ring. A separate memory limit (--memory-limit-mb, a Preferences row, default 8 GiB) halves the plan until the real ring bytes fit and says so on stderr and in the status line. The per-source key buffer_mb becomes buffer_samples; an old buffer_mb is ignored. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
115 lines
6.1 KiB
C++
115 lines
6.1 KiB
C++
// SPDX-License-Identifier: MIT
|
|
// SPDX-FileCopyrightText: 2026 Martino Ferrari
|
|
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
|
|
#include "source/source_config.hpp"
|
|
|
|
namespace udpscope {
|
|
|
|
/// Everything the command line can say. Defaults are the ones the spec names
|
|
/// (10 M samples per signal, 10 ms trigger hold-off) unless the user overrides them.
|
|
struct Config {
|
|
std::string server = "127.0.0.1"; ///< IPv4 dotted quad of the stream source.
|
|
std::uint16_t port = 8000; ///< UDP port (unicast) or TCP port (multicast).
|
|
std::string multicast; ///< Empty selects unicast.
|
|
std::string interface_addr; ///< Local IPv4 to join the group on; empty = default route.
|
|
|
|
/// Whether `--server` was given. Absent, the application asks which source to open rather
|
|
/// than silently taking the default above -- so this is "did the user choose", not "is
|
|
/// `server` valid". The default stays a usable value for every other path.
|
|
bool server_specified = false;
|
|
|
|
/// `--source NAME`: which registered kind opens. `udps` is the default, and `--server/--port/
|
|
/// --multicast/--interface` keep meaning UDPS.
|
|
std::string source_kind = "udps";
|
|
/// Whether `--source` was given (the other half of "did the user choose a source").
|
|
bool source_specified = false;
|
|
/// Repeatable `--source-opt k=v`: the kind's own settings. For `udps` they overlay the fields above.
|
|
SourceConfig source_opts;
|
|
|
|
/// A source was named on the command line (`--server` or `--source`), so the chooser is skipped.
|
|
bool has_source() const noexcept { return server_specified || source_specified; }
|
|
/// The settings the kind's `make()` receives: for UDPS the four connection fields plus the receive
|
|
/// buffer, then `source_opts` on top.
|
|
SourceConfig source_config() const;
|
|
/// The (host, port) that name this source's session file; see `session_host()` in the registry.
|
|
std::string session_host() const;
|
|
std::uint16_t session_port() const;
|
|
|
|
/// The sample budget: how many samples to keep for **every acquired signal**, and for the shared
|
|
/// time ring. The rings are powers of two, so the request is rounded *down* to one (`buffer_ticks()`
|
|
/// in model.hpp) -- "up to" this many -- and the UI shows both figures. 10 M asks for 8 388 608.
|
|
std::uint64_t buffer_samples = 10'000'000;
|
|
/// Whether `--buffer-samples` was given. A stored configuration's sample budget fills in only when
|
|
/// the flag was *absent*: an explicit flag beats a saved setting, and "not asked" is not the same
|
|
/// question as "asked for 10 M".
|
|
bool buffer_samples_specified = false;
|
|
|
|
/// The hard ceiling on the ring storage, in MiB, for the whole application rather than per source.
|
|
/// `plan_partition()` halves the rings until they fit under it, so it can clip `buffer_samples`.
|
|
/// Default 8 GiB; the preferences carry it between runs and `--memory-limit-mb` overrides it.
|
|
std::uint64_t memory_limit_mb = 8192;
|
|
/// Whether `--memory-limit-mb` was given, so the stored preference only fills the gap.
|
|
bool memory_limit_specified = false;
|
|
|
|
/// Largest time window the toolbar offers, in seconds. The buffer partition is
|
|
/// sized to hold at least this much time (spec: decimation).
|
|
double max_window_s = 600.0;
|
|
|
|
double trigger_holdoff_s = 0.010;
|
|
int recv_buffer_bytes = 8 * 1024 * 1024;
|
|
|
|
/// Number of stacked plots (rows) to start with, 1..`RowSet::kMaxRows`. Only the
|
|
/// starting point: rows are added and removed from the layout editor.
|
|
int plots = 1;
|
|
|
|
/// Take the plot's time from the packet counter and the declared sample rate rather than
|
|
/// from the stream's own time signal, so arrival and producer-clock jitter cannot move a
|
|
/// sample's x. Absolute stream time is still recorded, for export only (`todo.md` item 2).
|
|
bool relative_time = false;
|
|
|
|
/// Whether relative time was *stated* rather than left at its default -- by `--relative-time`, or
|
|
/// by the preferences, which the source chooser can reach before this run is planned. A source's
|
|
/// stored configuration is a statement from an *older* session, so it only fills in the gap.
|
|
bool relative_time_specified = false;
|
|
|
|
/// `--capture-packets N`: the ring size of the raw-datagram capture, 1..100000.
|
|
/// 0 means the flag was absent: the capture is off, and the popover button then
|
|
/// uses the 1000-slot default when the user toggles it.
|
|
std::uint32_t capture_packets = 0;
|
|
|
|
/// `--open FILE`: a `.npy` an earlier run exported, opened for viewing and analysis instead of a
|
|
/// live source. File mode and source mode are exclusive -- there is no acquisition behind a file,
|
|
/// and the chooser's Waveforms page sets this rather than the connection fields.
|
|
std::string open_waveform;
|
|
|
|
/// `--renderer NAME`: the GSK renderer to force, by exporting `GSK_RENDERER` **before any GTK
|
|
/// call**. Empty leaves GTK's own choice alone. `cairo` is the software preset over SSH/X11
|
|
/// forwarding: it also exports `LIBGL_ALWAYS_SOFTWARE=1`, because the plot's `GtkGLArea` asks
|
|
/// for a GL context whatever GSK draws with. `gl` is the faster alternative when indirect GL
|
|
/// works. The variables are read when the renderer/context is created, so setting them anywhere
|
|
/// after `gtk_init()`/`adw_init()` is a no-op -- which is why this is a `Config` field applied
|
|
/// at the top of `main()` and not a preference.
|
|
std::string renderer;
|
|
|
|
/// The source chooser's "Guided tour" entry. The tour is drawn on the application's own window,
|
|
/// which does not exist while the chooser runs, so the request rides on the one value `main()`
|
|
/// owns and hands to both the chooser and the front end (`open_waveform` and the preferences'
|
|
/// relative time cross the same way) and the window starts the tour once it is up and laid out.
|
|
/// It is *not* a preference: it describes this request and nothing else, so nothing persists it.
|
|
bool tour_pending = false;
|
|
};
|
|
|
|
/// Parses argv. On `--help` prints usage to stdout and exits(0); on a bad
|
|
/// argument prints a diagnostic to stderr and exits(2).
|
|
Config parse_args(int argc, char** argv);
|
|
|
|
/// Writes the usage text to `out`.
|
|
void print_usage(std::FILE* out, const char* argv0);
|
|
|
|
} // namespace udpscope
|