Compare commits

..

No commits in common. "8d9fee7796f3015f1b5f18ef479075b3a0200a76" and "011f300f0a6de897bf01fe38bc8ced25950ec002" have entirely different histories.

8 changed files with 58 additions and 210 deletions

View File

@ -4,24 +4,12 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const libusb_use_udev = b.option( const use_udev = b.option(
bool, bool,
"use_udev", "use_udev",
"link and use udev (Linux only. Default: false)", "link and use udev (Linux only. Default: false)",
) orelse false; ) orelse false;
const libusb_enable_logging = b.option(
bool,
"libusb_enable_logging",
"enable libusb's built-in logging (Default: false)",
) orelse false;
const libusb_enable_debug_logging = b.option(
bool,
"libusb_enable_debug_logging",
"enable libusb's debug logging (Default: false)",
) orelse false;
const exe = b.addExecutable(.{ const exe = b.addExecutable(.{
.name = "yaes", .name = "yaes",
.root_source_file = b.path("src/main.zig"), .root_source_file = b.path("src/main.zig"),
@ -50,13 +38,7 @@ pub fn build(b: *std.Build) void {
} else { } else {
const ljacklm_dep = b.dependency( const ljacklm_dep = b.dependency(
"ljacklm", "ljacklm",
.{ .{ .target = target, .optimize = optimize, .use_udev = use_udev },
.target = target,
.optimize = optimize,
.libusb_use_udev = libusb_use_udev,
.libusb_enable_logging = libusb_enable_logging,
.libusb_enable_debug_logging = libusb_enable_debug_logging,
},
); );
exe.linkLibrary(ljacklm_dep.artifact("ljacklm")); exe.linkLibrary(ljacklm_dep.artifact("ljacklm"));
} }

View File

@ -4,22 +4,10 @@ pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const libusb_use_udev = b.option( const use_udev = b.option(
bool, bool,
"libusb_use_udev", "use_udev",
"libusb: link and use udev (Linux only. Default: false)", "link and use udev (Linux only. Default: false)",
) orelse false;
const libusb_enable_logging = b.option(
bool,
"libusb_enable_logging",
"enable libusb's built-in logging (Default: false)",
) orelse false;
const libusb_enable_debug_logging = b.option(
bool,
"libusb_enable_debug_logging",
"enable libusb's debug logging (Default: false)",
) orelse false; ) orelse false;
const liblabjackusb = b.addStaticLibrary(.{ const liblabjackusb = b.addStaticLibrary(.{
@ -43,13 +31,7 @@ pub fn build(b: *std.Build) !void {
const usb_dep = b.dependency( const usb_dep = b.dependency(
"usb", "usb",
.{ .{ .target = target, .optimize = optimize, .use_udev = use_udev },
.target = target,
.optimize = optimize,
.use_udev = libusb_use_udev,
.enable_logging = libusb_enable_logging,
.enable_debug_logging = libusb_enable_debug_logging,
},
); );
liblabjackusb.linkLibrary(usb_dep.artifact("usb")); liblabjackusb.linkLibrary(usb_dep.artifact("usb"));

View File

@ -4,22 +4,10 @@ pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const libusb_use_udev = b.option( const use_udev = b.option(
bool, bool,
"libusb_use_udev", "use_udev",
"libusb: link and use udev (Linux only. Default: false)", "link and use udev (Linux only. Default: false)",
) orelse false;
const libusb_enable_logging = b.option(
bool,
"libusb_enable_logging",
"enable libusb's built-in logging (Default: false)",
) orelse false;
const libusb_enable_debug_logging = b.option(
bool,
"libusb_enable_debug_logging",
"enable libusb's debug logging (Default: false)",
) orelse false; ) orelse false;
const libljacklm = b.addStaticLibrary(.{ const libljacklm = b.addStaticLibrary(.{
@ -42,13 +30,7 @@ pub fn build(b: *std.Build) !void {
const usb_dep = b.dependency( const usb_dep = b.dependency(
"labjackusb", "labjackusb",
.{ .{ .target = target, .optimize = optimize, .use_udev = use_udev },
.target = target,
.optimize = optimize,
.libusb_use_udev = libusb_use_udev,
.libusb_enable_logging = libusb_enable_logging,
.libusb_enable_debug_logging = libusb_enable_debug_logging,
},
); );
libljacklm.linkLibrary(usb_dep.artifact("labjackusb")); libljacklm.linkLibrary(usb_dep.artifact("labjackusb"));

16
deps/libusb/build.zig vendored
View File

@ -10,18 +10,6 @@ pub fn build(b: *std.Build) !void {
"link and use udev (Linux only. Default: false)", "link and use udev (Linux only. Default: false)",
) orelse false; ) orelse false;
const enable_logging = b.option(
bool,
"enable_logging",
"enable libusb's built-in logging (Default: false)",
) orelse false;
const enable_debug_logging = b.option(
bool,
"enable_debug_logging",
"enable libusb's debug logging (Default: false)",
) orelse false;
const libusb = b.addStaticLibrary(.{ const libusb = b.addStaticLibrary(.{
.name = "usb", .name = "usb",
.target = target, .target = target,
@ -71,8 +59,8 @@ pub fn build(b: *std.Build) !void {
.{ .style = .{ .autoconf = b.path("config.h.in") } }, .{ .style = .{ .autoconf = b.path("config.h.in") } },
.{ .{
.DEFAULT_VISIBILITY = .@"__attribute__ ((visibility (\"default\")))", .DEFAULT_VISIBILITY = .@"__attribute__ ((visibility (\"default\")))",
.ENABLE_DEBUG_LOGGING = oneOrNull(enable_debug_logging), .ENABLE_DEBUG_LOGGING = oneOrNull(optimize == .Debug),
.ENABLE_LOGGING = oneOrNull(enable_logging), .ENABLE_LOGGING = oneOrNull(optimize == .Debug),
.HAVE_ASM_TYPES_H = null, .HAVE_ASM_TYPES_H = null,
.HAVE_CLOCK_GETTIME = oneOrNull(linux_target), .HAVE_CLOCK_GETTIME = oneOrNull(linux_target),
.HAVE_DECL_EFD_CLOEXEC = oneOrNull(linux_target), .HAVE_DECL_EFD_CLOEXEC = oneOrNull(linux_target),

View File

@ -1,41 +1,35 @@
const std = @import("std"); const std = @import("std");
const AzEl = @import("./YaesuController.zig").AzEl; const AzEl = @import("./LabjackYaesu.zig").AzEl;
const lj = @import("./labjack.zig"); const lj = @import("./labjack.zig");
const Config = @This(); const Config = @This();
var global_internal: std.json.Parsed(Config) = undefined; var global_internal: Config = undefined;
pub const global: *const Config = &global_internal.value; pub const global: *const Config = &global_internal;
pub fn load(allocator: std.mem.Allocator, reader: anytype, err_writer: anytype) !void { pub fn load(allocator: std.mem.Allocator, reader: anytype, err_writer: anytype) !void {
var jread = std.json.Reader(1024, @TypeOf(reader)).init(allocator, reader); var jread = std.json.Reader(1024, @TypeOf(reader)).init(allocator, reader);
defer jread.deinit(); defer jread.deinit();
global_internal = try std.json.parseFromTokenSource( global_internal = try std.json.parseFromTokenSourceLeaky(
Config, Config,
allocator, allocator,
&jread, &jread,
.{}, .{},
); );
try global.validate(err_writer); try global_internal.validate(err_writer);
} }
pub fn loadDefault(allocator: std.mem.Allocator) void { pub fn loadDefault(allocator: std.mem.Allocator) void {
const arena = allocator.create(std.heap.ArenaAllocator) catch unreachable; _ = allocator;
arena.* = std.heap.ArenaAllocator.init(allocator); global_internal = .{};
global_internal = .{
.arena = arena,
.value = .{},
};
} }
pub fn deinit() void { pub fn destroy(allocator: std.mem.Allocator) void {
// TODO: implement this probably // TODO: implement this probably
const allocator = global_internal.arena.child_allocator; _ = allocator;
global_internal.arena.deinit();
allocator.destroy(global_internal.arena);
} }
pub fn validate(self: Config, err_writer: anytype) !void { pub fn validate(self: Config, err_writer: anytype) !void {

View File

@ -4,9 +4,9 @@ const lj = @import("./labjack.zig");
const Config = @import("./Config.zig"); const Config = @import("./Config.zig");
const config = Config.global; const config = Config.global;
const log = std.log.scoped(.yaesu_controller); const log = std.log.scoped(.labjack_yaesu);
const YaesuController = @This(); const LabjackYaesu = @This();
control_thread: std.Thread, control_thread: std.Thread,
lock: *std.Thread.Mutex, lock: *std.Thread.Mutex,
@ -17,25 +17,7 @@ pub const AzEl = struct {
elevation: f64, elevation: f64,
}; };
pub const CalibrationRoutine = enum { pub fn init(allocator: std.mem.Allocator) !LabjackYaesu {
feedback,
orientation,
};
pub fn calibrate(allocator: std.mem.Allocator, routine: CalibrationRoutine) !void {
const controller = try YaesuController.init(allocator);
defer {
controller.quit();
controller.control_thread.join();
}
switch (routine) {
.feedback => try controller.calibrate_feedback(),
.orientation => try controller.calibrate_orientation(),
}
}
pub fn init(allocator: std.mem.Allocator) !YaesuController {
const lock = try allocator.create(std.Thread.Mutex); const lock = try allocator.create(std.Thread.Mutex);
errdefer allocator.destroy(lock); errdefer allocator.destroy(lock);
lock.* = .{}; lock.* = .{};
@ -74,7 +56,7 @@ fn inRange(request: f64, comptime dof: enum { azimuth, elevation }) bool {
}; };
} }
pub fn setTarget(self: YaesuController, target: AzEl) error{OutOfRange}!void { pub fn setTarget(self: LabjackYaesu, target: AzEl) error{OutOfRange}!void {
self.lock.lock(); self.lock.lock();
defer self.lock.unlock(); defer self.lock.unlock();
@ -94,14 +76,14 @@ pub fn setTarget(self: YaesuController, target: AzEl) error{OutOfRange}!void {
controller.requested_state = .running; controller.requested_state = .running;
} }
pub fn currentPosition(self: YaesuController) AzEl { pub fn currentPosition(self: LabjackYaesu) AzEl {
self.lock.lock(); self.lock.lock();
defer self.lock.unlock(); defer self.lock.unlock();
return self.controller.position; return self.controller.position;
} }
pub fn startCalibration(self: YaesuController) void { pub fn startCalibration(self: LabjackYaesu) void {
// there are two different types of calibration: // there are two different types of calibration:
// 1. feedback calibration, running to the extents of the rotator // 1. feedback calibration, running to the extents of the rotator
// 2. sun calibration, which determines the azimuth and elevation angle // 2. sun calibration, which determines the azimuth and elevation angle
@ -113,7 +95,7 @@ pub fn startCalibration(self: YaesuController) void {
_ = self; _ = self;
} }
pub fn quit(self: YaesuController) void { pub fn quit(self: LabjackYaesu) void {
self.lock.lock(); self.lock.lock();
defer self.lock.unlock(); defer self.lock.unlock();
@ -121,7 +103,7 @@ pub fn quit(self: YaesuController) void {
controller.requested_state = .stopped; controller.requested_state = .stopped;
} }
pub fn stop(self: YaesuController) void { pub fn stop(self: LabjackYaesu) void {
self.lock.lock(); self.lock.lock();
defer self.lock.unlock(); defer self.lock.unlock();
@ -130,26 +112,14 @@ pub fn stop(self: YaesuController) void {
controller.requested_state = .idle; controller.requested_state = .idle;
} }
pub fn startPark(self: YaesuController) void { pub fn startPark(self: LabjackYaesu) void {
self.setTarget(config.controller.parking_posture) catch unreachable; self.setTarget(config.controller.parking_posture) catch unreachable;
} }
fn calibrate_feedback(self: YaesuController) !void {
_ = self;
log.err("this isn't implemented yet, sorry.", .{});
return error.NotImplemented;
}
fn calibrate_orientation(self: YaesuController) !void {
_ = self;
log.err("this isn't implemented yet, sorry.", .{});
return error.NotImplemented;
}
fn runController(controller: *Controller) void { fn runController(controller: *Controller) void {
controller.run() catch { controller.run() catch {
log.err( log.err(
"the rotator control loop has terminated unexpectedly!!!!", "the labjack control loop has terminated unexpectedly!!!!",
.{}, .{},
); );
}; };
@ -210,21 +180,7 @@ const Controller = struct {
}; };
} }
const Sign = enum { fn signDeadzone(offset: f64, deadzone: f64) enum { negative, zero, positive } {
negative,
zero,
positive,
pub fn symbol(self: Sign) u8 {
return switch (self) {
.negative => '-',
.zero => '=',
.positive => '+',
};
}
};
fn signDeadzone(offset: f64, deadzone: f64) Sign {
return if (@abs(offset) < deadzone) return if (@abs(offset) < deadzone)
.zero .zero
else if (offset < 0) else if (offset < 0)
@ -268,23 +224,11 @@ const Controller = struct {
drive_signal[config.controller.elevation_outputs.increase.io] = elsign == .positive; drive_signal[config.controller.elevation_outputs.increase.io] = elsign == .positive;
drive_signal[config.controller.elevation_outputs.decrease.io] = elsign == .negative; drive_signal[config.controller.elevation_outputs.decrease.io] = elsign == .negative;
const raw = try self.labjack.readAnalogWriteDigital(2, inputs, drive_signal, true); log.info("drive: az = {s}, el = {s}. outputs: {any}", .{ @tagName(azsign), @tagName(elsign), drive_signal });
const angles = lerpAndOffsetAngles(raw);
log.info( const raw = try self.labjack.readAnalogWriteDigital(2, inputs, drive_signal, true);
"az: {d:.1}° ({d:.2} V) {d:.1}° => {c}, el: {d:.1}° ({d:.2} V) {d:.1}° => {c}",
.{ return lerpAndOffsetAngles(raw);
angles.azimuth,
raw[0].voltage,
pos_error.azimuth,
azsign.symbol(),
angles.elevation,
raw[1].voltage,
pos_error.elevation,
elsign.symbol(),
},
);
return angles;
} }
fn run(self: *Controller) !void { fn run(self: *Controller) !void {

View File

@ -2,7 +2,7 @@ const std = @import("std");
const Config = @import("./Config.zig"); const Config = @import("./Config.zig");
const config = Config.global; const config = Config.global;
const YaesuController = @import("./YaesuController.zig"); const LabjackYaesu = @import("./LabjackYaesu.zig");
const RotCtl = @This(); const RotCtl = @This();
@ -10,7 +10,7 @@ const log = std.log.scoped(.RotCtl);
writer: std.io.BufferedWriter(512, std.net.Stream.Writer), writer: std.io.BufferedWriter(512, std.net.Stream.Writer),
running: bool, running: bool,
rotator: YaesuController, rotator: LabjackYaesu,
pub fn run(allocator: std.mem.Allocator) !void { pub fn run(allocator: std.mem.Allocator) !void {
// var server = std.net.StreamServer.init(.{ .reuse_address = true }); // var server = std.net.StreamServer.init(.{ .reuse_address = true });
@ -30,7 +30,7 @@ pub fn run(allocator: std.mem.Allocator) !void {
var interface: RotCtl = .{ var interface: RotCtl = .{
.writer = undefined, .writer = undefined,
.running = true, .running = true,
.rotator = try YaesuController.init(allocator), .rotator = try LabjackYaesu.init(allocator),
}; };
while (true) { while (true) {

View File

@ -3,7 +3,6 @@ const std = @import("std");
const Config = @import("./Config.zig"); const Config = @import("./Config.zig");
const lj = @import("./labjack.zig"); const lj = @import("./labjack.zig");
const RotCtl = @import("./RotCtl.zig"); const RotCtl = @import("./RotCtl.zig");
const YaesuController = @import("./YaesuController.zig");
const udev = @import("udev_rules"); const udev = @import("udev_rules");
@ -48,42 +47,37 @@ pub fn main() !u8 {
} }
Config.loadDefault(allocator); Config.loadDefault(allocator);
defer Config.deinit();
return writeDefaultConfig(if (args.len == 3) args[2] else null); return writeDefaultConfig(if (args.len == 3) args[2] else null);
} else if (std.mem.eql(u8, args[1], commands.run)) { } else if (std.mem.eql(u8, args[1], commands.run)) {
if (args.len > 3) { if (args.len > 3) {
printHelp(exename, .run); printHelp(exename, .run);
return 1; return 1;
} }
loadConfigOrDefault(allocator, if (args.len == 3) args[2] else null) catch blk: {
return 1; const confpath = if (args.len == 3) args[2] else "yaes.json";
const conf_file = std.fs.cwd().openFile(confpath, .{}) catch {
log.warn("Could not load config file '{s}'. Using default config.", .{confpath});
Config.loadDefault(allocator);
break :blk;
};
defer conf_file.close();
defer Config.deinit(); Config.load(allocator, conf_file.reader(), std.io.getStdErr().writer()) catch |err| {
log.err("Could not parse config file '{s}': {s}.", .{ confpath, @errorName(err) });
return 1;
};
}
defer Config.destroy(allocator);
const ver = lj.getDriverVersion();
std.debug.print("Driver version: {d}\n", .{ver});
RotCtl.run(allocator) catch |err| { RotCtl.run(allocator) catch |err| {
log.err("rotator controller ceased unexpectedly! {s}", .{@errorName(err)}); log.err("rotator controller ceased unexpectedly! {s}", .{@errorName(err)});
return 1; return 1;
}; };
} else if (std.mem.eql(u8, args[1], commands.calibrate)) {
if (args.len < 3 or args.len > 4) {
printHelp(exename, .calibrate);
return 1;
}
loadConfigOrDefault(allocator, if (args.len == 4) args[3] else null) catch return 0;
return 1;
defer Config.deinit();
const routine = std.meta.stringToEnum(YaesuController.CalibrationRoutine, args[2]) orelse {
log.err("{s} is not a known calibration routine.", .{args[2]});
printHelp(exename, .calibrate);
return 1;
};
YaesuController.calibrate(allocator, routine) catch |err| {
log.err("Calibration failed: {s}", .{@errorName(err)});
return 1;
};
} else if (std.mem.eql(u8, args[1], commands.help)) { } else if (std.mem.eql(u8, args[1], commands.help)) {
if (args.len != 3) { if (args.len != 3) {
printHelp(exename, .help); printHelp(exename, .help);
@ -103,24 +97,6 @@ pub fn main() !u8 {
printHelp(exename, .main); printHelp(exename, .main);
return 1; return 1;
} }
return 0;
}
fn loadConfigOrDefault(allocator: std.mem.Allocator, path: ?[]const u8) !void {
const confpath = path orelse "yaes.json";
const conf_file = std.fs.cwd().openFile(confpath, .{}) catch {
log.warn("Could not load config file '{s}'. Using default config.", .{confpath});
Config.loadDefault(allocator);
return;
};
defer conf_file.close();
Config.load(allocator, conf_file.reader(), std.io.getStdErr().writer()) catch |err| {
log.err("Could not parse config file '{s}': {s}.", .{ confpath, @errorName(err) });
return error.InvalidConfig;
};
log.info("Loaded config from '{s}'.", .{confpath});
} }
fn installUdevRules(outpath: ?[]const u8) u8 { fn installUdevRules(outpath: ?[]const u8) u8 {
@ -285,7 +261,7 @@ const command_help = .{
\\ Perform a calibration routine and write an updated configuration with its results. \\ Perform a calibration routine and write an updated configuration with its results.
\\ \\
\\Arguments: \\Arguments:
\\ routine Must be either `feedback` or `orientation`. The different calibration routines have \\ routine Must be one of `feedback` or `orientation`. The different calibration routines have
\\ different requirements. `orientation` calibration is a sun-pointing-based routine and \\ different requirements. `orientation` calibration is a sun-pointing-based routine and
\\ should be performed after `feedback` calibration is complete. \\ should be performed after `feedback` calibration is complete.
\\ config_file [Optional] the path of a config file to load. This file will be updated with the \\ config_file [Optional] the path of a config file to load. This file will be updated with the