Compare commits
4 Commits
bd465af30d
...
b0aac111a2
Author | SHA1 | Date | |
---|---|---|---|
b0aac111a2 | |||
7fbfe1c5f7 | |||
7105775426 | |||
0e88022a8d |
@ -32,11 +32,20 @@ pub fn destroy(allocator: std.mem.Allocator) void {
|
||||
|
||||
rotctl: RotControlConfig = .{
|
||||
.listen_address = "127.0.0.1",
|
||||
.listen_port = 5432,
|
||||
.listen_port = 4533,
|
||||
},
|
||||
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 },
|
||||
@ -48,14 +57,17 @@ labjack: LabjackConfig = .{
|
||||
},
|
||||
},
|
||||
controller: ControllerConfig = .{
|
||||
.azimuth_input = .{ .channel = .diff_01, .gain_index = 2 },
|
||||
.elevation_input = .{ .channel = .diff_23, .gain_index = 2 },
|
||||
.azimuth_input = .{ .channel = .diff_01, .range = .@"5 V" },
|
||||
.elevation_input = .{ .channel = .diff_23, .range = .@"5 V" },
|
||||
.azimuth_outputs = .{ .increase = .{ .io = 0 }, .decrease = .{ .io = 1 } },
|
||||
.elevation_outputs = .{ .increase = .{ .io = 2 }, .decrease = .{ .io = 3 } },
|
||||
.loop_interval_ns = 50_000_000,
|
||||
.parking_posture = .{ .azimuth = 180, .elevation = 90 },
|
||||
.angle_tolerance = .{ .azimuth = 1, .elevation = 1 },
|
||||
.angle_offset = .{ .azimuth = 0, .elevation = 0 },
|
||||
// this is a symmetric mask, so the minimum usable elevation is elevation_mask deg
|
||||
// and the maximum usable elevation is 180 - elevation_mask deg
|
||||
.elevation_mask = 0.0,
|
||||
},
|
||||
|
||||
pub const VoltAngle = struct { voltage: f64, angle: f64 };
|
||||
@ -106,6 +118,7 @@ const ControllerConfig = struct {
|
||||
parking_posture: AzEl,
|
||||
angle_tolerance: AzEl,
|
||||
angle_offset: AzEl,
|
||||
elevation_mask: f64,
|
||||
|
||||
const OutPair = struct {
|
||||
increase: lj.DigitalOutputChannel,
|
||||
|
@ -40,7 +40,13 @@ pub fn setTarget(self: LabjackYaesu, target: AzEl) void {
|
||||
defer self.lock.unlock();
|
||||
|
||||
const controller = @constCast(self.controller);
|
||||
controller.target = target;
|
||||
controller.target = .{
|
||||
.azimuth = target.azimuth,
|
||||
.elevation = @min(
|
||||
@max(target.elevation, config.controller.elevation_mask),
|
||||
180.0 - config.controller.elevation_mask,
|
||||
),
|
||||
};
|
||||
controller.requested_state = .running;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ pub const Labjack = struct {
|
||||
&id,
|
||||
@intFromBool(self.demo),
|
||||
input.channelNumber(),
|
||||
input.gain_index,
|
||||
input.gainIndex(),
|
||||
&over_v,
|
||||
&res.voltage,
|
||||
);
|
||||
@ -93,7 +93,7 @@ pub const Labjack = struct {
|
||||
var gains: [incount]c_long = undefined;
|
||||
for (inputs, &in_channels, &gains) |from, *inc, *gain| {
|
||||
inc.* = from.channelNumber();
|
||||
gain.* = from.gain_index;
|
||||
gain.* = from.gainIndex();
|
||||
}
|
||||
var v_out: [4]f32 = .{0} ** 4;
|
||||
var over_v: c_long = 0;
|
||||
@ -133,11 +133,15 @@ pub const Labjack = struct {
|
||||
|
||||
pub const AnalogInput = struct {
|
||||
channel: AnalogInputChannel,
|
||||
gain_index: GainIndex = 0,
|
||||
range: InputRange = .@"20 V",
|
||||
|
||||
pub fn channelNumber(self: AnalogInput) u4 {
|
||||
return @intFromEnum(self.channel);
|
||||
}
|
||||
|
||||
pub fn gainIndex(self: AnalogInput) GainIndex {
|
||||
return @intFromEnum(self.range);
|
||||
}
|
||||
};
|
||||
|
||||
pub const AnalogReadResult = struct {
|
||||
@ -207,6 +211,17 @@ pub const DigitalOutputChannel = union(enum) {
|
||||
// 7 => G=20 ±1 volt
|
||||
pub const GainIndex = u3;
|
||||
|
||||
pub const InputRange = enum(GainIndex) {
|
||||
@"20 V" = 0,
|
||||
@"10 V" = 1,
|
||||
@"5 V" = 2,
|
||||
@"4 V" = 3,
|
||||
@"2.5 V" = 4,
|
||||
@"2 V" = 5,
|
||||
@"1.25 V" = 6,
|
||||
@"1 V" = 7,
|
||||
};
|
||||
|
||||
pub const PackedOutput = packed struct(u4) {
|
||||
io0: bool,
|
||||
io1: bool,
|
||||
|
Loading…
x
Reference in New Issue
Block a user