From 7fbfe1c5f7c8495edfd85d9e5ff7c8be134a5cba Mon Sep 17 00:00:00 2001 From: torque Date: Sat, 6 Jul 2024 12:59:37 -0700 Subject: [PATCH] rotctl: do range validation This is at the interface layer though it should arguably be done at the controller layer. Oh well. --- src/Config.zig | 9 +++++++++ src/RotCtl.zig | 30 +++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Config.zig b/src/Config.zig index 17d686e..64867a6 100644 --- a/src/Config.zig +++ b/src/Config.zig @@ -37,6 +37,15 @@ rotctl: RotControlConfig = .{ labjack: LabjackConfig = .{ .device = .autodetect, .feedback_calibration = .{ + // NOTE: these min and max angles are treated as hardware limits. This serves + // two purposes: first, it means that feedback is always interpolated, + // never extrapolated (though with a two point calibration, that doesn't + // matter much). Second, it prevents having a redundant set of bounds + // values that could potentially desync from these and cause problems. + // + // The functional min and max are these plus the angle offset values. For + // example, given controller.angle_offset.azimuth = -6, the practical minimum + // azimuth would be -6 deg and the practical maximum would be 444 deg. .azimuth = .{ .minimum = .{ .voltage = 0.0, .angle = 0.0 }, .maximum = .{ .voltage = 5.0, .angle = 450.0 }, diff --git a/src/RotCtl.zig b/src/RotCtl.zig index d0db0ea..e348618 100644 --- a/src/RotCtl.zig +++ b/src/RotCtl.zig @@ -1,6 +1,7 @@ const std = @import("std"); -const config = @import("./Config.zig").global; +const Config = @import("./Config.zig"); +const config = Config.global; const LabjackYaesu = @import("./LabjackYaesu.zig"); const RotCtl = @This(); @@ -113,6 +114,27 @@ fn getPosition(self: *RotCtl, _: []const u8, tokens: *TokenIter) CommandError!vo self.printReply("{d:.1}\n{d:.1}", .{ pos.azimuth, pos.elevation }) catch return error.BadOutput; } +fn inRange(request: f64, comptime dof: enum { azimuth, elevation }) bool { + return switch (dof) { + // zig fmt: off + .azimuth => request >= ( + config.labjack.feedback_calibration.azimuth.minimum.angle + + config.controller.angle_offset.azimuth + ) and request <= ( + config.labjack.feedback_calibration.azimuth.maximum.angle + + config.controller.angle_offset.azimuth + ), + .elevation => request >= ( + config.labjack.feedback_calibration.elevation.minimum.angle + + config.controller.angle_offset.elevation + ) and request <= ( + config.labjack.feedback_calibration.elevation.maximum.angle + + config.controller.angle_offset.elevation + ), + // zig fmt: on + }; +} + fn setPosition(self: *RotCtl, _: []const u8, tokens: *TokenIter) CommandError!void { const azimuth = std.fmt.parseFloat(f64, tokens.next() orelse { return self.replyStatus(.invalid_parameter) catch error.BadOutput; @@ -120,12 +142,18 @@ fn setPosition(self: *RotCtl, _: []const u8, tokens: *TokenIter) CommandError!vo return self.replyStatus(.invalid_parameter) catch error.BadOutput; }; + if (!inRange(azimuth, .azimuth)) + return self.replyStatus(.invalid_parameter) catch error.BadOutput; + const elevation = std.fmt.parseFloat(f64, tokens.next() orelse { return self.replyStatus(.invalid_parameter) catch error.BadOutput; }) catch { return self.replyStatus(.invalid_parameter) catch error.BadOutput; }; + if (!inRange(elevation, .elevation)) + return self.replyStatus(.invalid_parameter) catch error.BadOutput; + self.rotator.setTarget(.{ .azimuth = azimuth, .elevation = elevation }); return self.replyStatus(.okay) catch error.BadOutput; }