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.
This commit is contained in:
torque 2024-08-11 20:31:01 -07:00
parent 8c6d9431c8
commit e1c54fec91
Signed by: torque
SSH Key Fingerprint: SHA256:nCrXefBNo6EbjNSQhv0nXmEg/VuNq3sMF5b8zETw3Tk

View File

@ -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