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 optimize = b.standardOptimizeOption(.{});
const libusb_use_udev = b.option(
const use_udev = b.option(
bool,
"use_udev",
"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;
const exe = b.addExecutable(.{
.name = "yaes",
.root_source_file = b.path("src/main.zig"),
@ -50,13 +38,7 @@ pub fn build(b: *std.Build) void {
} else {
const ljacklm_dep = b.dependency(
"ljacklm",
.{
.target = target,
.optimize = optimize,
.libusb_use_udev = libusb_use_udev,
.libusb_enable_logging = libusb_enable_logging,
.libusb_enable_debug_logging = libusb_enable_debug_logging,
},
.{ .target = target, .optimize = optimize, .use_udev = use_udev },
);
exe.linkLibrary(ljacklm_dep.artifact("ljacklm"));
}

View File

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

View File

@ -4,22 +4,10 @@ pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const libusb_use_udev = b.option(
const use_udev = b.option(
bool,
"libusb_use_udev",
"libusb: 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)",
"use_udev",
"link and use udev (Linux only. Default: false)",
) orelse false;
const libljacklm = b.addStaticLibrary(.{
@ -42,13 +30,7 @@ pub fn build(b: *std.Build) !void {
const usb_dep = b.dependency(
"labjackusb",
.{
.target = target,
.optimize = optimize,
.libusb_use_udev = libusb_use_udev,
.libusb_enable_logging = libusb_enable_logging,
.libusb_enable_debug_logging = libusb_enable_debug_logging,
},
.{ .target = target, .optimize = optimize, .use_udev = use_udev },
);
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)",
) 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(.{
.name = "usb",
.target = target,
@ -71,8 +59,8 @@ pub fn build(b: *std.Build) !void {
.{ .style = .{ .autoconf = b.path("config.h.in") } },
.{
.DEFAULT_VISIBILITY = .@"__attribute__ ((visibility (\"default\")))",
.ENABLE_DEBUG_LOGGING = oneOrNull(enable_debug_logging),
.ENABLE_LOGGING = oneOrNull(enable_logging),
.ENABLE_DEBUG_LOGGING = oneOrNull(optimize == .Debug),
.ENABLE_LOGGING = oneOrNull(optimize == .Debug),
.HAVE_ASM_TYPES_H = null,
.HAVE_CLOCK_GETTIME = oneOrNull(linux_target),
.HAVE_DECL_EFD_CLOEXEC = oneOrNull(linux_target),

View File

@ -1,41 +1,35 @@
const std = @import("std");
const AzEl = @import("./YaesuController.zig").AzEl;
const AzEl = @import("./LabjackYaesu.zig").AzEl;
const lj = @import("./labjack.zig");
const Config = @This();
var global_internal: std.json.Parsed(Config) = undefined;
pub const global: *const Config = &global_internal.value;
var global_internal: Config = undefined;
pub const global: *const Config = &global_internal;
pub fn load(allocator: std.mem.Allocator, reader: anytype, err_writer: anytype) !void {
var jread = std.json.Reader(1024, @TypeOf(reader)).init(allocator, reader);
defer jread.deinit();
global_internal = try std.json.parseFromTokenSource(
global_internal = try std.json.parseFromTokenSourceLeaky(
Config,
allocator,
&jread,
.{},
);
try global.validate(err_writer);
try global_internal.validate(err_writer);
}
pub fn loadDefault(allocator: std.mem.Allocator) void {
const arena = allocator.create(std.heap.ArenaAllocator) catch unreachable;
arena.* = std.heap.ArenaAllocator.init(allocator);
global_internal = .{
.arena = arena,
.value = .{},
};
_ = allocator;
global_internal = .{};
}
pub fn deinit() void {
pub fn destroy(allocator: std.mem.Allocator) void {
// TODO: implement this probably
const allocator = global_internal.arena.child_allocator;
global_internal.arena.deinit();
allocator.destroy(global_internal.arena);
_ = allocator;
}
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 = 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,
lock: *std.Thread.Mutex,
@ -17,25 +17,7 @@ pub const AzEl = struct {
elevation: f64,
};
pub const CalibrationRoutine = enum {
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 {
pub fn init(allocator: std.mem.Allocator) !LabjackYaesu {
const lock = try allocator.create(std.Thread.Mutex);
errdefer allocator.destroy(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();
defer self.lock.unlock();
@ -94,14 +76,14 @@ pub fn setTarget(self: YaesuController, target: AzEl) error{OutOfRange}!void {
controller.requested_state = .running;
}
pub fn currentPosition(self: YaesuController) AzEl {
pub fn currentPosition(self: LabjackYaesu) AzEl {
self.lock.lock();
defer self.lock.unlock();
return self.controller.position;
}
pub fn startCalibration(self: YaesuController) void {
pub fn startCalibration(self: LabjackYaesu) void {
// there are two different types of calibration:
// 1. feedback calibration, running to the extents of the rotator
// 2. sun calibration, which determines the azimuth and elevation angle
@ -113,7 +95,7 @@ pub fn startCalibration(self: YaesuController) void {
_ = self;
}
pub fn quit(self: YaesuController) void {
pub fn quit(self: LabjackYaesu) void {
self.lock.lock();
defer self.lock.unlock();
@ -121,7 +103,7 @@ pub fn quit(self: YaesuController) void {
controller.requested_state = .stopped;
}
pub fn stop(self: YaesuController) void {
pub fn stop(self: LabjackYaesu) void {
self.lock.lock();
defer self.lock.unlock();
@ -130,26 +112,14 @@ pub fn stop(self: YaesuController) void {
controller.requested_state = .idle;
}
pub fn startPark(self: YaesuController) void {
pub fn startPark(self: LabjackYaesu) void {
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 {
controller.run() catch {
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 {
negative,
zero,
positive,
pub fn symbol(self: Sign) u8 {
return switch (self) {
.negative => '-',
.zero => '=',
.positive => '+',
};
}
};
fn signDeadzone(offset: f64, deadzone: f64) Sign {
fn signDeadzone(offset: f64, deadzone: f64) enum { negative, zero, positive } {
return if (@abs(offset) < deadzone)
.zero
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.decrease.io] = elsign == .negative;
const raw = try self.labjack.readAnalogWriteDigital(2, inputs, drive_signal, true);
const angles = lerpAndOffsetAngles(raw);
log.info("drive: az = {s}, el = {s}. outputs: {any}", .{ @tagName(azsign), @tagName(elsign), drive_signal });
log.info(
"az: {d:.1}° ({d:.2} V) {d:.1}° => {c}, el: {d:.1}° ({d:.2} V) {d:.1}° => {c}",
.{
angles.azimuth,
raw[0].voltage,
pos_error.azimuth,
azsign.symbol(),
angles.elevation,
raw[1].voltage,
pos_error.elevation,
elsign.symbol(),
},
);
return angles;
const raw = try self.labjack.readAnalogWriteDigital(2, inputs, drive_signal, true);
return lerpAndOffsetAngles(raw);
}
fn run(self: *Controller) !void {

View File

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

View File

@ -3,7 +3,6 @@ const std = @import("std");
const Config = @import("./Config.zig");
const lj = @import("./labjack.zig");
const RotCtl = @import("./RotCtl.zig");
const YaesuController = @import("./YaesuController.zig");
const udev = @import("udev_rules");
@ -48,42 +47,37 @@ pub fn main() !u8 {
}
Config.loadDefault(allocator);
defer Config.deinit();
return writeDefaultConfig(if (args.len == 3) args[2] else null);
} else if (std.mem.eql(u8, args[1], commands.run)) {
if (args.len > 3) {
printHelp(exename, .run);
return 1;
}
loadConfigOrDefault(allocator, if (args.len == 3) args[2] else null) catch
return 1;
blk: {
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| {
log.err("rotator controller ceased unexpectedly! {s}", .{@errorName(err)});
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 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;
};
return 0;
} else if (std.mem.eql(u8, args[1], commands.help)) {
if (args.len != 3) {
printHelp(exename, .help);
@ -103,24 +97,6 @@ pub fn main() !u8 {
printHelp(exename, .main);
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 {
@ -285,7 +261,7 @@ const command_help = .{
\\ Perform a calibration routine and write an updated configuration with its results.
\\
\\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
\\ 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