Compare commits

...

4 Commits

Author SHA1 Message Date
ddbfb9746d main: respect the elevation mask after applying offsets
This prevents offsets from being able to point below the elevation
mask. The way this is done should probably be reworked, but this whole
thing is such a messy hack anyway that, like, whatever, man.
2024-08-22 13:59:43 -07:00
db55a5081d main: treat elevation offset as offset from horizon
Rather than having somewhat confusing flipped logic when the rotator is
operating in the 180-90 degree elevation regime, this internally
performs sign flipping. This means that a 3 degree elevation offset
means "point an additional 3 degrees up from the horizon" regardless
of whether the rotator is operating in the 0-90 or 180-90 elevation
ranges.
2024-08-22 13:06:27 -07:00
b6caab906a main: respect elevation mask
This is done silently, in the sense that the UI doesn't show that it is
clamping the requested pointing target in this way.
2024-08-22 11:49:01 -07:00
511f2ca903 add config
This is very basic, but it beats having everything be hardcoded.
2024-08-22 11:46:54 -07:00
2 changed files with 98 additions and 8 deletions

47
src/Config.zig Normal file
View File

@@ -0,0 +1,47 @@
const std = @import("std");
const Config = @This();
elevation_mask: f64 = 19,
rotctld: AddressPort = .{ .address = "127.0.0.1", .port = 4533 },
listen: AddressPort = .{ .address = "127.0.0.1", .port = 42069 },
const confdir = "rotint";
const conffile = "config.json";
pub fn default() Config {
return .{};
}
pub fn load(allocator: std.mem.Allocator, env: *const std.process.EnvMap) !Config {
if (env.get("XDG_CONFIG_HOME")) |cfg|
if (cfg.len > 0) {
return try loadPath(allocator, try std.fs.path.join(
allocator,
&.{ cfg, confdir, conffile },
));
};
if (env.get("HOME")) |home|
if (home.len > 0) {
return try loadPath(allocator, try std.fs.path.join(
allocator,
&.{ home, ".config", confdir, conffile },
));
};
return error.ConfigHomeMissing;
}
fn loadPath(allocator: std.mem.Allocator, path: []const u8) !Config {
defer allocator.free(path);
const data = try std.fs.cwd().readFileAlloc(allocator, path, 1024);
defer allocator.free(data);
return try std.json.parseFromSliceLeaky(Config, allocator, data, .{});
}
const AddressPort = struct {
address: []const u8,
port: u16,
};

View File

@@ -6,6 +6,7 @@ const xev = @import("xev");
const networking = @import("./networking.zig");
const rotctl = @import("./rotctl.zig");
const Config = @import("./Config.zig");
const log = std.log.scoped(.rotint);
@@ -84,6 +85,7 @@ pub const RotInt = struct {
command_freq: u8 = 4,
pollcount: u9 = 0,
state: State = .initial,
conf: Config,
termbuffer: std.io.BufferedWriter(4096, std.io.AnyWriter),
vx: *vaxis.Vaxis,
@@ -110,8 +112,18 @@ pub const RotInt = struct {
self.rotator.rotint = self;
self.log = try Log.init();
const connect_addr = try std.net.Address.parseIp("127.0.0.1", 4533);
try self.rotator.connect(self.loop, connect_addr);
const rotctld_addr = std.net.Address.parseIp(
self.conf.rotctld.address,
self.conf.rotctld.port,
) catch |err| {
log.err("could not parse rotctld address {s}:{d}: {s}", .{
self.conf.rotctld.address,
self.conf.rotctld.port,
@errorName(err),
});
return err;
};
try self.rotator.connect(self.loop, rotctld_addr);
}
pub fn stateEvent(self: *RotInt, event: State) void {
@@ -126,12 +138,15 @@ pub const RotInt = struct {
},
.rotator_ready => if (self.state == .rotator_connected) {
self.warn("rotator ready", .{});
const listen_addr = std.net.Address.parseIp("127.0.0.1", 42069) catch {
self.warn("bogus listen address", .{});
const listen_addr = std.net.Address.parseIp(
self.conf.listen.address,
self.conf.listen.port,
) catch |err| {
self.showError("bogus listen address: {s}", .{@errorName(err)});
return;
};
self.server.listen(self.loop, listen_addr) catch {
self.warn("listen problem", .{});
self.server.listen(self.loop, listen_addr) catch |err| {
self.showError("listen problem: {s}", .{@errorName(err)});
return;
};
// demangle here to avoid causing initial moves
@@ -165,10 +180,27 @@ pub const RotInt = struct {
self.pollcount = (self.pollcount + 1) % self.command_freq;
if (self.pollcount == 0) {
const mangled: AzEl = .{
var mangled: AzEl = .{
.az = self.requested_posture.az + self.offsets.az,
.el = self.requested_posture.el + self.offsets.el,
.el = self.requested_posture.el,
};
mangled.el = if (mangled.el > 90)
@max(
@min(
@min(mangled.el, 180 - self.conf.elevation_mask) - self.offsets.el,
180 - self.conf.elevation_mask,
),
90,
)
else
@min(
@max(
@max(mangled.el, self.conf.elevation_mask) + self.offsets.el,
self.conf.elevation_mask,
),
90,
);
self.sendRotatorCommand(.{ .set_position = mangled });
} else {
self.sendRotatorCommand(.get_position);
@@ -209,6 +241,10 @@ pub const RotInt = struct {
log.warn(fmt, args);
}
pub fn showError(_: *RotInt, comptime fmt: []const u8, args: anytype) void {
log.err(fmt, args);
}
pub fn handleControlRequest(self: *RotInt, req: []const u8) void {
const command = self.parser.parseCommand(req) catch |err| switch (err) {
error.Incomplete => return,
@@ -387,8 +423,15 @@ pub fn main() !void {
});
defer loop.deinit();
var env = try std.process.getEnvMap(alloc);
defer env.deinit();
var app: RotInt = .{
.allocator = alloc,
.conf = Config.load(alloc, &env) catch cfg: {
log.warn("Could not load config file. Using defaults.", .{});
break :cfg Config.default();
},
.termbuffer = tty.bufferedWriter(),
.vx = &vx,
.loop = &loop,