From e1c54fec91d86e5de8cfc0ac38663a2306e314c8 Mon Sep 17 00:00:00 2001 From: torque Date: Sun, 11 Aug 2024 20:31:01 -0700 Subject: [PATCH] main: add configurable poll/command rates These are coupled in a somewhat nonintuitive manner. Previously, we would send a command every other request. Due to an oversight, we were only sending a request once per second, which meant we were reading position once every two seconds and writing position once every two seconds. However, this seemed to work pretty well (at least the radio performance was largely improved and qualitatively it seemed like the controller was doing a better job of keeping up with the pointing target). I wanted the UI to update the position more frequently, so we keep the scheme of sending a command every N requests, but we send significantly more get position requests. These can be adjusted on the fly in order to get an idea of how much the communication pattern actually impacts the pointing performance. --- src/main.zig | 125 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 42 deletions(-) diff --git a/src/main.zig b/src/main.zig index 258c28a..806c219 100644 --- a/src/main.zig +++ b/src/main.zig @@ -24,7 +24,9 @@ pub const RotInt = struct { offsets: AzEl = .{ .az = 0, .el = 0 }, requested_posture: AzEl = .{ .az = 0, .el = 0 }, current_posture: AzEl = .{ .az = 0, .el = 0 }, - flipflop: bool = false, + poll_interval: u64 = 100, + command_freq: u8 = 10, + pollcount: u9 = 0, state: State = .initial, termbuffer: std.io.BufferedWriter(4096, std.io.AnyWriter), @@ -38,8 +40,6 @@ pub const RotInt = struct { poller: xev.Timer, poll_completion: xev.Completion = undefined, - const poll_interval: u64 = 1000; - pub const State = enum { initial, rotator_connected, @@ -99,7 +99,11 @@ pub const RotInt = struct { return .disarm; }; - if (self.flipflop) { + // pre-increment so that this does not try to command when pollcount = 0 on the + // first call + self.pollcount = (self.pollcount + 1) % self.command_freq; + + if (self.pollcount == 0) { const mangled: AzEl = .{ .az = self.requested_posture.az + self.offsets.az, .el = self.requested_posture.el + self.offsets.el, @@ -108,7 +112,6 @@ pub const RotInt = struct { } else { self.sendRotatorCommand(.get_position); } - self.flipflop = !self.flipflop; return .disarm; } @@ -136,7 +139,7 @@ pub const RotInt = struct { .status => |code| if (code != .okay) self.warn("rotctl error {s}", .{@tagName(code)}), } - self.poller.run(self.loop, &self.poll_completion, poll_interval, RotInt, self, poll); + self.poller.run(self.loop, &self.poll_completion, self.poll_interval, RotInt, self, poll); } pub fn warn(_: *RotInt, comptime fmt: []const u8, args: anytype) void { @@ -178,7 +181,7 @@ pub const RotInt = struct { const win = self.vx.window(); win.clear(); - var lines: [3][128]u8 = undefined; + var lines: [4][128]u8 = undefined; const offsets: vaxis.Segment = .{ .text = try std.fmt.bufPrint( lines[0][0..], "Offsets: Az: {d: >6.1}, El: {d: >6.1}", @@ -194,13 +197,28 @@ pub const RotInt = struct { "Current: Az: {d: >6.1}, El: {d: >6.1}", .{ self.current_posture.az, self.current_posture.el }, ) }; + const pollinfo: vaxis.Segment = .{ .text = try std.fmt.bufPrint( + lines[3][0..], + "Poll: {d} ms, Command: {d} ms", + .{ self.poll_interval, self.command_freq * self.poll_interval }, + ) }; const center = vaxis.widgets.alignment.center(win, offsets.text.len, 1); _ = try center.printSegment(offsets, .{}); - const center_up = win.initChild(center.x_off, center.y_off + 1, .{ .limit = requested.text.len }, .{ .limit = 1 }); + const center_up = win.initChild(center.x_off, center.y_off - 1, .{ .limit = requested.text.len }, .{ .limit = 1 }); _ = try center_up.printSegment(requested, .{}); - const center_down = win.initChild(center.x_off, center.y_off - 1, .{ .limit = current.text.len }, .{ .limit = 1 }); + const center_down = win.initChild(center.x_off, center.y_off + 1, .{ .limit = current.text.len }, .{ .limit = 1 }); _ = try center_down.printSegment(current, .{}); + const poll_win = win.initChild(center.x_off, center.y_off + 3, .{ .limit = pollinfo.text.len }, .{ .limit = 1 }); + _ = try poll_win.printSegment(pollinfo, .{}); + + const help: vaxis.Segment = .{ + .text = "Keys: q/^c - quit, ↑/↓ - el +/- 1° (+shift - 0.1°), ←/→ - az -/+ 1° (+shift - 0.1°), ⏎ - reset offsets, ^l - redraw, w/a/s/d - poll rates", + .style = .{ .fg = .{ .index = 245 } }, + }; + + const helpwin = win.child(.{ .x_off = 0, .y_off = 0 }); + _ = try helpwin.printSegment(help, .{ .wrap = .word }); try self.vx.render(self.termbuffer.writer().any()); try self.termbuffer.flush(); @@ -214,44 +232,67 @@ pub const RotInt = struct { ) xev.CallbackAction { const self = self_.?; switch (event) { - .key_press => |key| keyp: { + .key_press => |key| { var mods = key.mods; mods.caps_lock = false; mods.num_lock = false; - const scale: f64 = if (std.meta.eql(mods, .{ .shift = true })) 1 else 10; - const delta: AzEl = switch (key.codepoint) { - vaxis.Key.left, vaxis.Key.kp_left => .{ .az = -0.1 * scale, .el = 0 }, - vaxis.Key.right, vaxis.Key.kp_right => .{ .az = 0.1 * scale, .el = 0 }, - vaxis.Key.up, vaxis.Key.kp_up => .{ .az = 0, .el = 0.1 * scale }, - vaxis.Key.down, vaxis.Key.kp_down => .{ .az = 0 * scale, .el = -0.1 * scale }, - 'l' => { - if (std.meta.eql(mods, .{ .ctrl = true })) - self.vx.queueRefresh(); - break :keyp; - }, - 'c' => { - if (std.meta.eql(mods, .{ .ctrl = true })) { - loop.stop(); - return .disarm; - } - break :keyp; - }, - 'q' => { - if (std.meta.eql(mods, .{})) { - loop.stop(); - return .disarm; - } - break :keyp; - }, - else => break :keyp, - }; + switch (key.codepoint) { + vaxis.Key.left, + vaxis.Key.kp_left, + vaxis.Key.right, + vaxis.Key.kp_right, + vaxis.Key.up, + vaxis.Key.kp_up, + vaxis.Key.down, + vaxis.Key.kp_down, + vaxis.Key.enter, + vaxis.Key.kp_enter, + => |arrow| { + const scale: f64 = if (std.meta.eql(mods, .{ .shift = true })) 1 else 10; + const delta: AzEl = switch (arrow) { + vaxis.Key.left, vaxis.Key.kp_left => .{ .az = -0.1 * scale, .el = 0 }, + vaxis.Key.right, vaxis.Key.kp_right => .{ .az = 0.1 * scale, .el = 0 }, + vaxis.Key.up, vaxis.Key.kp_up => .{ .az = 0, .el = 0.1 * scale }, + vaxis.Key.down, vaxis.Key.kp_down => .{ .az = 0 * scale, .el = -0.1 * scale }, + vaxis.Key.enter, vaxis.Key.kp_enter => .{ .az = -self.offsets.az, .el = -self.offsets.el }, + else => unreachable, + }; - self.offsets.az += delta.az; - self.offsets.el += delta.el; - self.draw() catch { - self.warn("draw failure", .{}); - }; + self.offsets.az += delta.az; + self.offsets.el += delta.el; + self.draw() catch { + self.warn("draw failure", .{}); + }; + }, + 'w', 'a', 's', 'd' => |wasd| { + switch (wasd) { + 'w' => self.poll_interval += 100, + 'a' => if (self.command_freq > 2) { + self.command_freq -= 1; + }, + 's' => if (self.poll_interval > 100) { + self.poll_interval -= 100; + }, + 'd' => self.command_freq += 1, + else => unreachable, + } + self.draw() catch {}; + }, + 'l' => if (std.meta.eql(mods, .{ .ctrl = true })) { + self.vx.queueRefresh(); + self.draw() catch {}; + }, + 'c' => if (std.meta.eql(mods, .{ .ctrl = true })) { + loop.stop(); + return .disarm; + }, + 'q' => if (std.meta.eql(mods, .{})) { + loop.stop(); + return .disarm; + }, + else => {}, + } }, .winsize => |ws| { watcher.vx.resize(self.allocator, watcher.tty.anyWriter(), ws) catch