Compare commits
3 Commits
49fce9c584
...
e639a17424
Author | SHA1 | Date | |
---|---|---|---|
e639a17424 | |||
10c40d7d50 | |||
c32390f7c1 |
4
deps/labjack/exodriver/build.zig
vendored
4
deps/labjack/exodriver/build.zig
vendored
@ -17,6 +17,10 @@ pub fn build(b: *std.Build) !void {
|
|||||||
.link_libc = true,
|
.link_libc = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (optimize == .Debug) {
|
||||||
|
liblabjackusb.defineCMacro("LJ_DEBUG", "1");
|
||||||
|
}
|
||||||
|
|
||||||
liblabjackusb.addCSourceFile(.{ .file = b.path("liblabjackusb/labjackusb.c") });
|
liblabjackusb.addCSourceFile(.{ .file = b.path("liblabjackusb/labjackusb.c") });
|
||||||
liblabjackusb.installHeader(b.path("liblabjackusb/labjackusb.h"), "labjackusb.h");
|
liblabjackusb.installHeader(b.path("liblabjackusb/labjackusb.h"), "labjackusb.h");
|
||||||
|
|
||||||
|
@ -505,6 +505,7 @@ static HANDLE LJUSB_OpenSpecificDevice(libusb_device *dev, const struct libusb_d
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
// Test if the kernel driver has the U12.
|
// Test if the kernel driver has the U12.
|
||||||
if (desc->idProduct == U12_PRODUCT_ID && libusb_kernel_driver_active(devh, 0)) {
|
if (desc->idProduct == U12_PRODUCT_ID && libusb_kernel_driver_active(devh, 0)) {
|
||||||
#if LJ_DEBUG
|
#if LJ_DEBUG
|
||||||
@ -521,6 +522,7 @@ static HANDLE LJUSB_OpenSpecificDevice(libusb_device *dev, const struct libusb_d
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
r = libusb_claim_interface(devh, 0);
|
r = libusb_claim_interface(devh, 0);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
|
4
deps/labjack/ljacklm/build.zig
vendored
4
deps/labjack/ljacklm/build.zig
vendored
@ -17,6 +17,10 @@ pub fn build(b: *std.Build) !void {
|
|||||||
.link_libc = true,
|
.link_libc = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (optimize == .Debug) {
|
||||||
|
libljacklm.defineCMacro("LJ_DEBUG", "1");
|
||||||
|
}
|
||||||
|
|
||||||
if (target.result.os.tag == .windows) {
|
if (target.result.os.tag == .windows) {
|
||||||
libljacklm.defineCMacro("LJACKLM_USE_WINDOWS_MUTEX_SHIM", "1");
|
libljacklm.defineCMacro("LJACKLM_USE_WINDOWS_MUTEX_SHIM", "1");
|
||||||
}
|
}
|
||||||
|
11
deps/labjack/ljacklm/libljacklm/ljacklm.c
vendored
11
deps/labjack/ljacklm/libljacklm/ljacklm.c
vendored
@ -7220,6 +7220,17 @@ long GetU12Information( HANDLE hDevice,
|
|||||||
temp = (unsigned long)LJUSB_GetDeviceDescriptorReleaseNumber(hDevice) * 65536; //upper two bytes of serial #
|
temp = (unsigned long)LJUSB_GetDeviceDescriptorReleaseNumber(hDevice) * 65536; //upper two bytes of serial #
|
||||||
|
|
||||||
result = LJUSB_GetHIDReportDescriptor(hDevice, repDesc, 75);
|
result = LJUSB_GetHIDReportDescriptor(hDevice, repDesc, 75);
|
||||||
|
|
||||||
|
#if defined(LJ_DEBUG)
|
||||||
|
|
||||||
|
fprintf(stderr, "U12 HID ReportDescriptor (hex, %lu B): ", result);
|
||||||
|
for (int idx = 0; idx < result; idx++) {
|
||||||
|
fprintf(stderr, "%02hhX", repDesc[idx]);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
#endif // LJ_DEBUG
|
||||||
|
|
||||||
if(result < 75)
|
if(result < 75)
|
||||||
{
|
{
|
||||||
//Failed getting descriptor. First capability would of been input, so
|
//Failed getting descriptor. First capability would of been input, so
|
||||||
|
@ -18,7 +18,7 @@ static inline int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline int pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
static inline int pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
||||||
return TryEnterCriticalSection(mutex) != 0;
|
return TryEnterCriticalSection(mutex) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void pthread_mutex_destroy(pthread_mutex_t *mutex) {
|
static inline void pthread_mutex_destroy(pthread_mutex_t *mutex) {
|
||||||
|
@ -6,15 +6,34 @@ const lj = @import("./labjack.zig");
|
|||||||
const Config = @This();
|
const Config = @This();
|
||||||
|
|
||||||
var global_internal: Config = undefined;
|
var global_internal: Config = undefined;
|
||||||
pub const global: *const Config;
|
pub const global: *const Config = &global_internal;
|
||||||
|
|
||||||
pub fn load(allocator: std.mem.Allocator, reader: anytype) !void {
|
pub fn load(allocator: std.mem.Allocator, reader: 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.parseFromTokenSourceLeaky(allocator, &jread, .{});
|
global_internal = try std.json.parseFromTokenSourceLeaky(
|
||||||
|
Config,
|
||||||
|
allocator,
|
||||||
|
&jread,
|
||||||
|
.{},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn loadDefault(allocator: std.mem.Allocator) void {
|
||||||
|
_ = allocator;
|
||||||
|
global_internal = .{};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(allocator: std.mem.Allocator) void {
|
||||||
|
// TODO: implement this probably
|
||||||
|
_ = allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
rotctl: RotControlConfig = .{
|
||||||
|
.listen_address = "127.0.0.1",
|
||||||
|
.listen_port = 5432,
|
||||||
|
},
|
||||||
labjack: LabjackConfig = .{
|
labjack: LabjackConfig = .{
|
||||||
.device = .autodetect,
|
.device = .autodetect,
|
||||||
.feedback_calibration = .{
|
.feedback_calibration = .{
|
||||||
@ -56,6 +75,11 @@ pub const MinMax = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const RotControlConfig = struct {
|
||||||
|
listen_address: []const u8,
|
||||||
|
listen_port: u16,
|
||||||
|
};
|
||||||
|
|
||||||
const LabjackConfig = struct {
|
const LabjackConfig = struct {
|
||||||
device: union(enum) {
|
device: union(enum) {
|
||||||
autodetect,
|
autodetect,
|
||||||
|
@ -4,6 +4,8 @@ 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(.labjack_yaesu);
|
||||||
|
|
||||||
const LabjackYaesu = @This();
|
const LabjackYaesu = @This();
|
||||||
|
|
||||||
control_thread: std.Thread,
|
control_thread: std.Thread,
|
||||||
@ -51,12 +53,14 @@ pub fn startCalibration(self: LabjackYaesu) void {
|
|||||||
// The former is (fairly) trivial to automate, just run until stall
|
// The former is (fairly) trivial to automate, just run until stall
|
||||||
// (assuming there's no deadband in the feedback). The latter requires
|
// (assuming there's no deadband in the feedback). The latter requires
|
||||||
// manual input as the human is the feedback hardware in the loop.
|
// manual input as the human is the feedback hardware in the loop.
|
||||||
|
_ = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn idle(self: LabjackYaesu) void {
|
pub fn idle(self: LabjackYaesu) void {
|
||||||
self.lock.lock();
|
self.lock.lock();
|
||||||
defer self.lock.unlock();
|
defer self.lock.unlock();
|
||||||
|
|
||||||
|
const controller = @constCast(self.controller);
|
||||||
controller.requested_state = .idle;
|
controller.requested_state = .idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,11 +68,17 @@ pub fn stop(self: LabjackYaesu) void {
|
|||||||
self.lock.lock();
|
self.lock.lock();
|
||||||
defer self.lock.unlock();
|
defer self.lock.unlock();
|
||||||
|
|
||||||
|
const controller = @constCast(self.controller);
|
||||||
controller.requested_state = .stopped;
|
controller.requested_state = .stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runController(controller: *Controller) void {
|
fn runController(controller: *Controller) void {
|
||||||
controller.run() catch {};
|
controller.run() catch {
|
||||||
|
log.err(
|
||||||
|
"the labjack control loop has terminated unexpectedly!!!!",
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const Controller = struct {
|
const Controller = struct {
|
||||||
@ -93,19 +103,19 @@ const Controller = struct {
|
|||||||
self.* = .{
|
self.* = .{
|
||||||
.target = .{ .azimuth = 0, .elevation = 0 },
|
.target = .{ .azimuth = 0, .elevation = 0 },
|
||||||
.position = .{ .azimuth = 0, .elevation = 0 },
|
.position = .{ .azimuth = 0, .elevation = 0 },
|
||||||
.state = .stopped,
|
.current_state = .stopped,
|
||||||
.requested_state = .idle,
|
.requested_state = .idle,
|
||||||
.lock = lock,
|
.lock = lock,
|
||||||
.labjack = switch (config.labjack.device) {
|
.labjack = switch (config.labjack.device) {
|
||||||
.autodetect => try labjack.Labjack.autodetect(),
|
.autodetect => lj.Labjack.autodetect(),
|
||||||
.serial_number => |sn| labjack.Labjack.with_serial_number(sn),
|
.serial_number => |sn| lj.Labjack.with_serial_number(sn),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connectLabjack(self: *Controller) !void {
|
fn connectLabjack(self: *Controller) !void {
|
||||||
const info = try controller.labjack.connect();
|
const info = try self.labjack.connect();
|
||||||
controller.labjack.id = info.local_id;
|
self.labjack.id = info.local_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lerpOne(input: f64, cal_points: Config.MinMax) f64 {
|
fn lerpOne(input: f64, cal_points: Config.MinMax) f64 {
|
||||||
@ -158,10 +168,10 @@ const Controller = struct {
|
|||||||
config.controller.angle_tolerance.elevation,
|
config.controller.angle_tolerance.elevation,
|
||||||
);
|
);
|
||||||
|
|
||||||
drive_signal[config.azimuth_outputs.increase.io] = azsign == .positive;
|
drive_signal[config.controller.azimuth_outputs.increase.io] = azsign == .positive;
|
||||||
drive_signal[config.azimuth_outputs.decrease.io] = azsign == .negative;
|
drive_signal[config.controller.azimuth_outputs.decrease.io] = azsign == .negative;
|
||||||
drive_signal[config.elevation_outputs.increase.io] = elsign == .positive;
|
drive_signal[config.controller.elevation_outputs.increase.io] = elsign == .positive;
|
||||||
drive_signal[config.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);
|
const raw = try self.labjack.readAnalogWriteDigital(2, inputs, drive_signal, true);
|
||||||
|
|
||||||
@ -169,17 +179,17 @@ const Controller = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(self: *Controller) !void {
|
fn run(self: *Controller) !void {
|
||||||
self.state = .initializing;
|
self.current_state = .initializing;
|
||||||
|
|
||||||
var timer: LoopTimer = .{ .interval_ns = config.controller.loop_interval_ns };
|
var timer: LoopTimer = .{ .interval_ns = config.controller.loop_interval_ns };
|
||||||
|
|
||||||
while (timer.mark()) : (timer.sleep()) switch (self.state) {
|
while (timer.mark()) : (timer.sleep()) switch (self.current_state) {
|
||||||
.initializing, .idle => {
|
.initializing, .idle => {
|
||||||
const pos = self.updateAzEl() catch {
|
const pos = self.updateAzEl() catch {
|
||||||
self.lock.lock();
|
self.lock.lock();
|
||||||
defer self.lock.unlock();
|
defer self.lock.unlock();
|
||||||
|
|
||||||
self.state = .stopped;
|
self.current_state = .stopped;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -187,15 +197,15 @@ const Controller = struct {
|
|||||||
defer self.lock.unlock();
|
defer self.lock.unlock();
|
||||||
|
|
||||||
self.position = pos;
|
self.position = pos;
|
||||||
self.state = self.requested_state;
|
self.current_state = self.requested_state;
|
||||||
},
|
},
|
||||||
.calibration => {
|
.calibration => {
|
||||||
self.lock.lock();
|
self.lock.lock();
|
||||||
defer self.lock.unlock();
|
defer self.lock.unlock();
|
||||||
|
|
||||||
// run calibration routine. psych, this does nothing. gottem
|
// run calibration routine. psych, this does nothing. gottem
|
||||||
self.state = .idle;
|
self.current_state = .idle;
|
||||||
self.requested_state = self.state;
|
self.requested_state = self.current_state;
|
||||||
},
|
},
|
||||||
.running => {
|
.running => {
|
||||||
const pos_error: AzEl = blk: {
|
const pos_error: AzEl = blk: {
|
||||||
@ -212,7 +222,7 @@ const Controller = struct {
|
|||||||
self.lock.lock();
|
self.lock.lock();
|
||||||
defer self.lock.unlock();
|
defer self.lock.unlock();
|
||||||
|
|
||||||
self.state = .stopped;
|
self.current_state = .stopped;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -220,7 +230,7 @@ const Controller = struct {
|
|||||||
defer self.lock.unlock();
|
defer self.lock.unlock();
|
||||||
|
|
||||||
self.position = pos;
|
self.position = pos;
|
||||||
self.state = self.requested_state;
|
self.current_state = self.requested_state;
|
||||||
},
|
},
|
||||||
.stopped => {
|
.stopped => {
|
||||||
// attempt to reset the drive outputs
|
// attempt to reset the drive outputs
|
||||||
@ -245,6 +255,6 @@ pub const LoopTimer = struct {
|
|||||||
const now = std.time.nanoTimestamp();
|
const now = std.time.nanoTimestamp();
|
||||||
const elapsed: u64 = @intCast(now - self.start);
|
const elapsed: u64 = @intCast(now - self.start);
|
||||||
|
|
||||||
std.time.sleep(interval_ns - elapsed);
|
std.time.sleep(self.interval_ns - elapsed);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
176
src/RotCtl.zig
176
src/RotCtl.zig
@ -1,5 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const config = @import("./Config.zig").global;
|
||||||
const LabjackYaesu = @import("./LabjackYaesu.zig");
|
const LabjackYaesu = @import("./LabjackYaesu.zig");
|
||||||
|
|
||||||
const RotCtl = @This();
|
const RotCtl = @This();
|
||||||
@ -10,15 +11,16 @@ writer: std.io.BufferedWriter(512, std.net.Stream.Writer),
|
|||||||
running: bool,
|
running: bool,
|
||||||
rotator: LabjackYaesu,
|
rotator: LabjackYaesu,
|
||||||
|
|
||||||
pub fn run() !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 });
|
||||||
defer server.deinit();
|
// defer server.deinit();
|
||||||
|
|
||||||
const listen_addr = try std.net.Address.parseIp(
|
const listen_addr = try std.net.Address.parseIp(
|
||||||
config.gpredict_listen_address,
|
config.rotctl.listen_address,
|
||||||
config.gpredict_listen_port,
|
config.rotctl.listen_port,
|
||||||
);
|
);
|
||||||
server.listen(listen_addr) catch {
|
|
||||||
|
var server = listen_addr.listen(.{ .reuse_address = true }) catch {
|
||||||
log.err("Could not listen on {}. Is it already in use?", .{listen_addr});
|
log.err("Could not listen on {}. Is it already in use?", .{listen_addr});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -27,7 +29,7 @@ pub fn run() !void {
|
|||||||
var interface: RotCtl = .{
|
var interface: RotCtl = .{
|
||||||
.writer = undefined,
|
.writer = undefined,
|
||||||
.running = true,
|
.running = true,
|
||||||
.rotator = LabjackYaesu.init(),
|
.rotator = try LabjackYaesu.init(allocator),
|
||||||
};
|
};
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -49,21 +51,22 @@ pub fn run() !void {
|
|||||||
|
|
||||||
while (interface.running) : (fbs.reset()) {
|
while (interface.running) : (fbs.reset()) {
|
||||||
reader.streamUntilDelimiter(fbs.writer(), '\n', readbuffer.len) catch break;
|
reader.streamUntilDelimiter(fbs.writer(), '\n', readbuffer.len) catch break;
|
||||||
try radio.handleHamlibCommand(fbs.getWritten());
|
try interface.handleHamlibCommand(fbs.getWritten());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(self: *const Hamlibber, buf: []const u8) !void {
|
fn write(self: *RotCtl, buf: []const u8) !void {
|
||||||
try self.writer.writeAll(buf);
|
try self.writer.writer().writeAll(buf);
|
||||||
|
try self.writer.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replyStatus(self: *RadioProxy, comptime status: HamlibErrorCode) !void {
|
fn replyStatus(self: *RotCtl, comptime status: HamlibErrorCode) !void {
|
||||||
try self.write(comptime status.replyFrame() ++ "\n");
|
try self.write(comptime status.replyFrame() ++ "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleHamlibCommand(
|
fn handleHamlibCommand(
|
||||||
self: *const Hamlibber,
|
self: *RotCtl,
|
||||||
command: []const u8,
|
command: []const u8,
|
||||||
) !void {
|
) !void {
|
||||||
var tokens = std.mem.tokenizeScalar(u8, command, ' ');
|
var tokens = std.mem.tokenizeScalar(u8, command, ' ');
|
||||||
@ -71,43 +74,13 @@ fn handleHamlibCommand(
|
|||||||
const first = tokens.next().?;
|
const first = tokens.next().?;
|
||||||
if (first.len == 1 or first[0] == '\\') {
|
if (first.len == 1 or first[0] == '\\') {
|
||||||
switch (first[0]) {
|
switch (first[0]) {
|
||||||
'F' => {
|
|
||||||
const freqt = tokens.next() orelse
|
|
||||||
return self.replyStatus(.invalid_parameter);
|
|
||||||
|
|
||||||
const new = std.fmt.parseInt(i32, freqt, 10) catch
|
|
||||||
return self.replyStatus(.invalid_parameter);
|
|
||||||
|
|
||||||
self.setFrequency(new) catch
|
|
||||||
return self.replyStatus(.io_error);
|
|
||||||
|
|
||||||
try self.replyStatus(.okay);
|
|
||||||
},
|
|
||||||
'f' => {
|
|
||||||
const freq = self.getFrequency() catch
|
|
||||||
return self.replyStatus(.io_error);
|
|
||||||
|
|
||||||
try self.print("{d}", .{freq});
|
|
||||||
},
|
|
||||||
't' => {
|
|
||||||
try self.print("{d}", .{@intFromBool(self.ptt_state)});
|
|
||||||
},
|
|
||||||
'q' => {
|
'q' => {
|
||||||
self.running = false;
|
self.running = false;
|
||||||
self.replyStatus(.okay) catch return;
|
self.replyStatus(.okay) catch return;
|
||||||
},
|
},
|
||||||
'\\' => return self.parseLongCommand(first[1..], &tokens),
|
'\\' => {
|
||||||
// zig fmt: off
|
return try self.parseLongCommand(first[1..], &tokens);
|
||||||
'*', '1', '2', '4', '_', 'A', 'a', 'B', 'b', 'C', 'c', 'D',
|
|
||||||
'd', 'E', 'e', 'G', 'g', 'H', 'h', 'I', 'i', 'J', 'j', 'L',
|
|
||||||
'l', 'M', 'm', 'N', 'n', 'O', 'o', 'P', 'p', 'R', 'r', 'S',
|
|
||||||
's', 'T', 'U', 'u', 'V', 'v', 'X', 'x', 'Y', 'y', 'Z', 'z',
|
|
||||||
0x87, 0x88, 0x89, 0x8A, 0x8B, 0x90, 0x91, 0x92, 0x93, 0xF3, 0xF5
|
|
||||||
=> |cmd| {
|
|
||||||
log.warn("Unsupported command {c}", .{cmd});
|
|
||||||
try self.replyStatus(.not_supported);
|
|
||||||
},
|
},
|
||||||
// zig fmt: on
|
|
||||||
else => |cmd| {
|
else => |cmd| {
|
||||||
log.err("unknown command {}", .{cmd});
|
log.err("unknown command {}", .{cmd});
|
||||||
try self.replyStatus(.not_implemented);
|
try self.replyStatus(.not_implemented);
|
||||||
@ -116,30 +89,25 @@ fn handleHamlibCommand(
|
|||||||
} else if (std.mem.eql(u8, first, "AOS")) {
|
} else if (std.mem.eql(u8, first, "AOS")) {
|
||||||
// gpredict just kind of shoves this message in on top of the HamLib
|
// gpredict just kind of shoves this message in on top of the HamLib
|
||||||
// protocol.
|
// protocol.
|
||||||
log.info("Received AOS message from gpredict", .{});
|
|
||||||
self.setFrequency(self.base_freq) catch return self.replyStatus(.io_error);
|
|
||||||
try self.replyStatus(.okay);
|
try self.replyStatus(.okay);
|
||||||
} else if (std.mem.eql(u8, first, "LOS")) {
|
} else if (std.mem.eql(u8, first, "LOS")) {
|
||||||
log.info("Received LOS message from gpredict", .{});
|
|
||||||
if (self.stop_on_los) {
|
|
||||||
self.running = false;
|
|
||||||
self.setFrequency(self.base_freq) catch return self.replyStatus(.io_error);
|
|
||||||
}
|
|
||||||
try self.replyStatus(.okay);
|
try self.replyStatus(.okay);
|
||||||
} else try self.replyStatus(.not_supported);
|
} else try self.replyStatus(.not_supported);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseLongCommand(
|
fn parseLongCommand(
|
||||||
self: *RadioProxy,
|
self: *RotCtl,
|
||||||
command: []const u8,
|
command: []const u8,
|
||||||
tokens: *std.mem.TokenIterator(u8, .scalar),
|
tokens: *std.mem.TokenIterator(u8, .scalar),
|
||||||
) !void {
|
) !void {
|
||||||
_ = tokens;
|
_ = tokens;
|
||||||
|
|
||||||
for (hamlib_commands) |check| {
|
for (rotctl_commands) |check| {
|
||||||
if (command.len >= check.long.len and std.mem.eql(u8, check.long, command[0..check.long.len])) {
|
if (check.long) |long| {
|
||||||
log.warn("Unsupported command {s}", .{command});
|
if (command.len >= long.len and std.mem.eql(u8, long, command)) {
|
||||||
break;
|
log.warn("Unsupported command {s}", .{command});
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.warn("Unknown command {s}", .{command});
|
log.warn("Unknown command {s}", .{command});
|
||||||
@ -147,47 +115,6 @@ fn parseLongCommand(
|
|||||||
return self.replyStatus(.not_supported);
|
return self.replyStatus(.not_supported);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resetWatchdog(self: *RadioProxy) !void {
|
|
||||||
const wd = spacecraft.shared.commands.watchdog;
|
|
||||||
|
|
||||||
const reqheader = csp.Header{
|
|
||||||
.priority = 1,
|
|
||||||
.source = Config.global.doppler_shift.gs_source_address,
|
|
||||||
.destination = Config.global.doppler_shift.gs_radio_address,
|
|
||||||
.destination_port = @intFromEnum(wd.Reset.port),
|
|
||||||
.source_port = self.reqPort(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const request = wd.Reset{};
|
|
||||||
const packet = reqheader.packBig() ++ request.packLittle();
|
|
||||||
|
|
||||||
try self.nats.publish(self.nats_up, &packet);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const res = self.nats_down.nextMessage(reply_timeout) catch |err| {
|
|
||||||
log.err("Could not reset ground UHF radio watchdog. Is the radio on and bridged to NATS?", .{});
|
|
||||||
return err;
|
|
||||||
};
|
|
||||||
const data = res.getData() orelse continue;
|
|
||||||
if (data.len != csp.Header.pack_size + wd.Reset.Response.pack_size)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const resheader = csp.Header.unpackBig(data[0..csp.Header.pack_size].*);
|
|
||||||
if (!resheader.isReply(reqheader)) continue;
|
|
||||||
|
|
||||||
const reply = wd.Reset.Response.unpackLittle(
|
|
||||||
data[csp.Header.pack_size..][0..wd.Reset.Response.pack_size].*,
|
|
||||||
);
|
|
||||||
if (reply.err != .okay) {
|
|
||||||
log.err("Got error response for ground UHF watchdog reset: {}", .{reply.err.code()});
|
|
||||||
return error.Failure;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("Successfully reset the ground UHF radio watchdog timer.", .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
const HamlibErrorCode = enum(u8) {
|
const HamlibErrorCode = enum(u8) {
|
||||||
okay = 0,
|
okay = 0,
|
||||||
invalid_parameter = 1,
|
invalid_parameter = 1,
|
||||||
@ -222,38 +149,33 @@ const HamlibErrorCode = enum(u8) {
|
|||||||
const HamlibCommand = struct {
|
const HamlibCommand = struct {
|
||||||
short: ?u8 = null,
|
short: ?u8 = null,
|
||||||
long: ?[]const u8 = null,
|
long: ?[]const u8 = null,
|
||||||
mode: enum { rotator, radio, both },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const hamlib_commands = [_]HamlibCommand{
|
const rotctl_commands = [_]HamlibCommand{
|
||||||
.{ .short = 'F', .long = "set_freq", .mode = .radio },
|
.{ .short = 'q' }, // quit
|
||||||
.{ .short = 'f', .long = "get_freq", .mode = .radio },
|
.{ .short = 'Q' }, // quit
|
||||||
.{ .short = 'T', .long = "set_ptt", .mode = .radio },
|
.{ .short = 'P', .long = "set_pos" }, // azimuth: f64, elevation: f64
|
||||||
.{ .short = 't', .long = "get_ptt", .mode = .radio },
|
.{ .short = 'p', .long = "get_pos" }, // return az: f64, el: f64
|
||||||
.{ .short = 'q', .mode = .both }, // quit
|
.{ .short = 'M', .long = "move" }, // direction: enum { up=2, down=4, left=8, right=16 }, speed: i8 (0-100 or -1)
|
||||||
.{ .short = 'Q', .mode = .both }, // quit
|
.{ .short = 'S', .long = "stop" },
|
||||||
.{ .short = 'P', .long = "set_pos", .mode = .rotator }, // azimuth: f64, elevation: f64
|
.{ .short = 'K', .long = "park" },
|
||||||
.{ .short = 'p', .long = "get_pos", .mode = .rotator }, // return az: f64, el: f64
|
.{ .short = 'C', .long = "set_conf" }, // token: []const u8, value: []const u8
|
||||||
.{ .short = 'M', .long = "move", .mode = .rotator }, // direction: enum { up=2, down=4, left=8, right=16 }, speed: i8 (0-100 or -1)
|
.{ .short = 'R', .long = "reset" }, // u1 (1 is reset all)
|
||||||
.{ .short = 'S', .long = "stop", .mode = .rotator },
|
.{ .short = '_', .long = "get_info" }, // return Model name
|
||||||
.{ .short = 'K', .long = "park", .mode = .rotator },
|
.{ .short = 'K', .long = "park" },
|
||||||
.{ .short = 'C', .long = "set_conf", .mode = .rotator }, // token: []const u8, value: []const u8
|
.{ .long = "dump_state" }, // ???
|
||||||
.{ .short = 'R', .long = "reset", .mode = .rotator }, // u1 (1 is reset all)
|
.{ .short = '1', .long = "dump_caps" }, // ???
|
||||||
.{ .short = '_', .long = "get_info", .mode = .rotator }, // return Model name
|
.{ .short = 'w', .long = "send_cmd" }, // []const u8, send serial command directly to the rotator
|
||||||
.{ .short = 'K', .long = "park", .mode = .rotator },
|
.{ .short = 'L', .long = "lonlat2loc" }, // return Maidenhead locator for given long: f64 and lat: f64, locator precision: u4 (2-12)
|
||||||
.{ .long = "dump_state", .mode = .rotator }, // ???
|
.{ .short = 'l', .long = "loc2lonlat" }, // the inverse of the above
|
||||||
.{ .short = '1', .long = "dump_caps", .mode = .rotator }, // ???
|
.{ .short = 'D', .long = "dms2dec" }, // deg, min, sec, 0 (positive) or 1 (negative)
|
||||||
.{ .short = 'w', .long = "send_cmd", .mode = .rotator }, // []const u8, send serial command directly to the rotator
|
.{ .short = 'd', .long = "dec2dms" },
|
||||||
.{ .short = 'L', .long = "lonlat2loc", .mode = .rotator }, // return Maidenhead locator for given long: f64 and lat: f64, locator precision: u4 (2-12)
|
.{ .short = 'E', .long = "dmmm2dec" },
|
||||||
.{ .short = 'l', .long = "loc2lonlat", .mode = .rotator }, // the inverse of the above
|
.{ .short = 'e', .long = "dec2dmmm" },
|
||||||
.{ .short = 'D', .long = "dms2dec", .mode = .rotator }, // deg, min, sec, 0 (positive) or 1 (negative)
|
.{ .short = 'B', .long = "grb" },
|
||||||
.{ .short = 'd', .long = "dec2dms", .mode = .rotator },
|
.{ .short = 'A', .long = "a_sp2a_lp" },
|
||||||
.{ .short = 'E', .long = "dmmm2dec", .mode = .rotator },
|
.{ .short = 'a', .long = "d_sp2d_lp" },
|
||||||
.{ .short = 'e', .long = "dec2dmmm", .mode = .rotator },
|
.{ .long = "pause" },
|
||||||
.{ .short = 'B', .long = "grb", .mode = .rotator },
|
|
||||||
.{ .short = 'A', .long = "a_sp2a_lp", .mode = .rotator },
|
|
||||||
.{ .short = 'a', .long = "d_sp2d_lp", .mode = .rotator },
|
|
||||||
.{ .long = "pause", .mode = .rotator },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// D, dms2dec 'Degrees' 'Minutes' 'Seconds' 'S/W'
|
// D, dms2dec 'Degrees' 'Minutes' 'Seconds' 'S/W'
|
||||||
|
45
src/main.zig
45
src/main.zig
@ -1,25 +1,38 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Config = @import("./Config.zig");
|
||||||
const lj = @import("./labjack.zig");
|
const lj = @import("./labjack.zig");
|
||||||
|
const RotCtl = @import("./RotCtl.zig");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.main);
|
||||||
|
|
||||||
|
pub fn main() !u8 {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer _ = gpa.deinit();
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
blk: {
|
||||||
|
const conf_file = std.fs.cwd().openFile("yaes.json", .{}) catch {
|
||||||
|
log.warn("Could not load config file yaes.json. Using default config.", .{});
|
||||||
|
Config.loadDefault(allocator);
|
||||||
|
break :blk;
|
||||||
|
};
|
||||||
|
defer conf_file.close();
|
||||||
|
|
||||||
|
Config.load(allocator, conf_file.reader()) catch {
|
||||||
|
log.err("Could not parse config file yaes.json. Good luck figuring out why.", .{});
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
defer Config.destroy(allocator);
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const ver = lj.getDriverVersion();
|
const ver = lj.getDriverVersion();
|
||||||
std.debug.print("Driver version: {d}\n", .{ver});
|
std.debug.print("Driver version: {d}\n", .{ver});
|
||||||
|
|
||||||
const labjack = lj.Labjack.autodetect();
|
RotCtl.run(allocator) catch |err| {
|
||||||
|
log.err("rotator controller ceased unexpectedly! {s}", .{@errorName(err)});
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
const in = try labjack.analogReadOne(.{ .channel = .diff_01, .gain_index = 2 });
|
return 0;
|
||||||
std.debug.print("Read voltage: {d}. Overvolt: {}\n", .{ in.voltage, in.over_voltage });
|
|
||||||
try labjack.digitalWriteOne(.{ .channel = .{ .io = 0 }, .level = true });
|
|
||||||
|
|
||||||
const sample = try labjack.readAnalogWriteDigital(
|
|
||||||
2,
|
|
||||||
.{ .{ .channel = .diff_01, .gain_index = 2 }, .{ .channel = .diff_23, .gain_index = 2 } },
|
|
||||||
.{false} ** 4,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
for (sample, 0..) |input, idx| {
|
|
||||||
std.debug.print(" channel {d}: {d} V\n", .{ idx, input.voltage });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user