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,
|
||||
});
|
||||
|
||||
if (optimize == .Debug) {
|
||||
liblabjackusb.defineCMacro("LJ_DEBUG", "1");
|
||||
}
|
||||
|
||||
liblabjackusb.addCSourceFile(.{ .file = b.path("liblabjackusb/labjackusb.c") });
|
||||
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;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Test if the kernel driver has the U12.
|
||||
if (desc->idProduct == U12_PRODUCT_ID && libusb_kernel_driver_active(devh, 0)) {
|
||||
#if LJ_DEBUG
|
||||
@ -521,6 +522,7 @@ static HANDLE LJUSB_OpenSpecificDevice(libusb_device *dev, const struct libusb_d
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
r = libusb_claim_interface(devh, 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,
|
||||
});
|
||||
|
||||
if (optimize == .Debug) {
|
||||
libljacklm.defineCMacro("LJ_DEBUG", "1");
|
||||
}
|
||||
|
||||
if (target.result.os.tag == .windows) {
|
||||
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 #
|
||||
|
||||
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)
|
||||
{
|
||||
//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) {
|
||||
return TryEnterCriticalSection(mutex) != 0;
|
||||
return TryEnterCriticalSection(mutex) == 0;
|
||||
}
|
||||
|
||||
static inline void pthread_mutex_destroy(pthread_mutex_t *mutex) {
|
||||
|
@ -6,15 +6,34 @@ const lj = @import("./labjack.zig");
|
||||
const Config = @This();
|
||||
|
||||
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 {
|
||||
var jread = std.json.Reader(1024, @TypeOf(reader)).init(allocator, reader);
|
||||
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 = .{
|
||||
.device = .autodetect,
|
||||
.feedback_calibration = .{
|
||||
@ -56,6 +75,11 @@ pub const MinMax = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const RotControlConfig = struct {
|
||||
listen_address: []const u8,
|
||||
listen_port: u16,
|
||||
};
|
||||
|
||||
const LabjackConfig = struct {
|
||||
device: union(enum) {
|
||||
autodetect,
|
||||
|
@ -4,6 +4,8 @@ const lj = @import("./labjack.zig");
|
||||
const Config = @import("./Config.zig");
|
||||
const config = Config.global;
|
||||
|
||||
const log = std.log.scoped(.labjack_yaesu);
|
||||
|
||||
const LabjackYaesu = @This();
|
||||
|
||||
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
|
||||
// (assuming there's no deadband in the feedback). The latter requires
|
||||
// manual input as the human is the feedback hardware in the loop.
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn idle(self: LabjackYaesu) void {
|
||||
self.lock.lock();
|
||||
defer self.lock.unlock();
|
||||
|
||||
const controller = @constCast(self.controller);
|
||||
controller.requested_state = .idle;
|
||||
}
|
||||
|
||||
@ -64,11 +68,17 @@ pub fn stop(self: LabjackYaesu) void {
|
||||
self.lock.lock();
|
||||
defer self.lock.unlock();
|
||||
|
||||
const controller = @constCast(self.controller);
|
||||
controller.requested_state = .stopped;
|
||||
}
|
||||
|
||||
fn runController(controller: *Controller) void {
|
||||
controller.run() catch {};
|
||||
controller.run() catch {
|
||||
log.err(
|
||||
"the labjack control loop has terminated unexpectedly!!!!",
|
||||
.{},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const Controller = struct {
|
||||
@ -93,19 +103,19 @@ const Controller = struct {
|
||||
self.* = .{
|
||||
.target = .{ .azimuth = 0, .elevation = 0 },
|
||||
.position = .{ .azimuth = 0, .elevation = 0 },
|
||||
.state = .stopped,
|
||||
.current_state = .stopped,
|
||||
.requested_state = .idle,
|
||||
.lock = lock,
|
||||
.labjack = switch (config.labjack.device) {
|
||||
.autodetect => try labjack.Labjack.autodetect(),
|
||||
.serial_number => |sn| labjack.Labjack.with_serial_number(sn),
|
||||
.autodetect => lj.Labjack.autodetect(),
|
||||
.serial_number => |sn| lj.Labjack.with_serial_number(sn),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn connectLabjack(self: *Controller) !void {
|
||||
const info = try controller.labjack.connect();
|
||||
controller.labjack.id = info.local_id;
|
||||
const info = try self.labjack.connect();
|
||||
self.labjack.id = info.local_id;
|
||||
}
|
||||
|
||||
fn lerpOne(input: f64, cal_points: Config.MinMax) f64 {
|
||||
@ -158,10 +168,10 @@ const Controller = struct {
|
||||
config.controller.angle_tolerance.elevation,
|
||||
);
|
||||
|
||||
drive_signal[config.azimuth_outputs.increase.io] = azsign == .positive;
|
||||
drive_signal[config.azimuth_outputs.decrease.io] = azsign == .negative;
|
||||
drive_signal[config.elevation_outputs.increase.io] = elsign == .positive;
|
||||
drive_signal[config.elevation_outputs.decrease.io] = elsign == .negative;
|
||||
drive_signal[config.controller.azimuth_outputs.increase.io] = azsign == .positive;
|
||||
drive_signal[config.controller.azimuth_outputs.decrease.io] = azsign == .negative;
|
||||
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);
|
||||
|
||||
@ -169,17 +179,17 @@ const Controller = struct {
|
||||
}
|
||||
|
||||
fn run(self: *Controller) !void {
|
||||
self.state = .initializing;
|
||||
self.current_state = .initializing;
|
||||
|
||||
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 => {
|
||||
const pos = self.updateAzEl() catch {
|
||||
self.lock.lock();
|
||||
defer self.lock.unlock();
|
||||
|
||||
self.state = .stopped;
|
||||
self.current_state = .stopped;
|
||||
continue;
|
||||
};
|
||||
|
||||
@ -187,15 +197,15 @@ const Controller = struct {
|
||||
defer self.lock.unlock();
|
||||
|
||||
self.position = pos;
|
||||
self.state = self.requested_state;
|
||||
self.current_state = self.requested_state;
|
||||
},
|
||||
.calibration => {
|
||||
self.lock.lock();
|
||||
defer self.lock.unlock();
|
||||
|
||||
// run calibration routine. psych, this does nothing. gottem
|
||||
self.state = .idle;
|
||||
self.requested_state = self.state;
|
||||
self.current_state = .idle;
|
||||
self.requested_state = self.current_state;
|
||||
},
|
||||
.running => {
|
||||
const pos_error: AzEl = blk: {
|
||||
@ -212,7 +222,7 @@ const Controller = struct {
|
||||
self.lock.lock();
|
||||
defer self.lock.unlock();
|
||||
|
||||
self.state = .stopped;
|
||||
self.current_state = .stopped;
|
||||
continue;
|
||||
};
|
||||
|
||||
@ -220,7 +230,7 @@ const Controller = struct {
|
||||
defer self.lock.unlock();
|
||||
|
||||
self.position = pos;
|
||||
self.state = self.requested_state;
|
||||
self.current_state = self.requested_state;
|
||||
},
|
||||
.stopped => {
|
||||
// attempt to reset the drive outputs
|
||||
@ -245,6 +255,6 @@ pub const LoopTimer = struct {
|
||||
const now = std.time.nanoTimestamp();
|
||||
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 config = @import("./Config.zig").global;
|
||||
const LabjackYaesu = @import("./LabjackYaesu.zig");
|
||||
|
||||
const RotCtl = @This();
|
||||
@ -10,15 +11,16 @@ writer: std.io.BufferedWriter(512, std.net.Stream.Writer),
|
||||
running: bool,
|
||||
rotator: LabjackYaesu,
|
||||
|
||||
pub fn run() !void {
|
||||
var server = std.net.StreamServer.init(.{ .reuse_address = true });
|
||||
defer server.deinit();
|
||||
pub fn run(allocator: std.mem.Allocator) !void {
|
||||
// var server = std.net.StreamServer.init(.{ .reuse_address = true });
|
||||
// defer server.deinit();
|
||||
|
||||
const listen_addr = try std.net.Address.parseIp(
|
||||
config.gpredict_listen_address,
|
||||
config.gpredict_listen_port,
|
||||
config.rotctl.listen_address,
|
||||
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});
|
||||
return;
|
||||
};
|
||||
@ -27,7 +29,7 @@ pub fn run() !void {
|
||||
var interface: RotCtl = .{
|
||||
.writer = undefined,
|
||||
.running = true,
|
||||
.rotator = LabjackYaesu.init(),
|
||||
.rotator = try LabjackYaesu.init(allocator),
|
||||
};
|
||||
|
||||
while (true) {
|
||||
@ -49,21 +51,22 @@ pub fn run() !void {
|
||||
|
||||
while (interface.running) : (fbs.reset()) {
|
||||
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 {
|
||||
try self.writer.writeAll(buf);
|
||||
fn write(self: *RotCtl, buf: []const u8) !void {
|
||||
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");
|
||||
}
|
||||
|
||||
fn handleHamlibCommand(
|
||||
self: *const Hamlibber,
|
||||
self: *RotCtl,
|
||||
command: []const u8,
|
||||
) !void {
|
||||
var tokens = std.mem.tokenizeScalar(u8, command, ' ');
|
||||
@ -71,43 +74,13 @@ fn handleHamlibCommand(
|
||||
const first = tokens.next().?;
|
||||
if (first.len == 1 or 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' => {
|
||||
self.running = false;
|
||||
self.replyStatus(.okay) catch return;
|
||||
},
|
||||
'\\' => return self.parseLongCommand(first[1..], &tokens),
|
||||
// zig fmt: off
|
||||
'*', '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);
|
||||
'\\' => {
|
||||
return try self.parseLongCommand(first[1..], &tokens);
|
||||
},
|
||||
// zig fmt: on
|
||||
else => |cmd| {
|
||||
log.err("unknown command {}", .{cmd});
|
||||
try self.replyStatus(.not_implemented);
|
||||
@ -116,30 +89,25 @@ fn handleHamlibCommand(
|
||||
} else if (std.mem.eql(u8, first, "AOS")) {
|
||||
// gpredict just kind of shoves this message in on top of the HamLib
|
||||
// protocol.
|
||||
log.info("Received AOS message from gpredict", .{});
|
||||
self.setFrequency(self.base_freq) catch return self.replyStatus(.io_error);
|
||||
try self.replyStatus(.okay);
|
||||
} 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);
|
||||
} else try self.replyStatus(.not_supported);
|
||||
}
|
||||
|
||||
fn parseLongCommand(
|
||||
self: *RadioProxy,
|
||||
self: *RotCtl,
|
||||
command: []const u8,
|
||||
tokens: *std.mem.TokenIterator(u8, .scalar),
|
||||
) !void {
|
||||
_ = tokens;
|
||||
|
||||
for (hamlib_commands) |check| {
|
||||
if (command.len >= check.long.len and std.mem.eql(u8, check.long, command[0..check.long.len])) {
|
||||
log.warn("Unsupported command {s}", .{command});
|
||||
break;
|
||||
for (rotctl_commands) |check| {
|
||||
if (check.long) |long| {
|
||||
if (command.len >= long.len and std.mem.eql(u8, long, command)) {
|
||||
log.warn("Unsupported command {s}", .{command});
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("Unknown command {s}", .{command});
|
||||
@ -147,47 +115,6 @@ fn parseLongCommand(
|
||||
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) {
|
||||
okay = 0,
|
||||
invalid_parameter = 1,
|
||||
@ -222,38 +149,33 @@ const HamlibErrorCode = enum(u8) {
|
||||
const HamlibCommand = struct {
|
||||
short: ?u8 = null,
|
||||
long: ?[]const u8 = null,
|
||||
mode: enum { rotator, radio, both },
|
||||
};
|
||||
|
||||
const hamlib_commands = [_]HamlibCommand{
|
||||
.{ .short = 'F', .long = "set_freq", .mode = .radio },
|
||||
.{ .short = 'f', .long = "get_freq", .mode = .radio },
|
||||
.{ .short = 'T', .long = "set_ptt", .mode = .radio },
|
||||
.{ .short = 't', .long = "get_ptt", .mode = .radio },
|
||||
.{ .short = 'q', .mode = .both }, // quit
|
||||
.{ .short = 'Q', .mode = .both }, // quit
|
||||
.{ .short = 'P', .long = "set_pos", .mode = .rotator }, // azimuth: f64, elevation: f64
|
||||
.{ .short = 'p', .long = "get_pos", .mode = .rotator }, // return az: f64, el: f64
|
||||
.{ .short = 'M', .long = "move", .mode = .rotator }, // direction: enum { up=2, down=4, left=8, right=16 }, speed: i8 (0-100 or -1)
|
||||
.{ .short = 'S', .long = "stop", .mode = .rotator },
|
||||
.{ .short = 'K', .long = "park", .mode = .rotator },
|
||||
.{ .short = 'C', .long = "set_conf", .mode = .rotator }, // token: []const u8, value: []const u8
|
||||
.{ .short = 'R', .long = "reset", .mode = .rotator }, // u1 (1 is reset all)
|
||||
.{ .short = '_', .long = "get_info", .mode = .rotator }, // return Model name
|
||||
.{ .short = 'K', .long = "park", .mode = .rotator },
|
||||
.{ .long = "dump_state", .mode = .rotator }, // ???
|
||||
.{ .short = '1', .long = "dump_caps", .mode = .rotator }, // ???
|
||||
.{ .short = 'w', .long = "send_cmd", .mode = .rotator }, // []const u8, send serial command directly to the rotator
|
||||
.{ .short = 'L', .long = "lonlat2loc", .mode = .rotator }, // return Maidenhead locator for given long: f64 and lat: f64, locator precision: u4 (2-12)
|
||||
.{ .short = 'l', .long = "loc2lonlat", .mode = .rotator }, // the inverse of the above
|
||||
.{ .short = 'D', .long = "dms2dec", .mode = .rotator }, // deg, min, sec, 0 (positive) or 1 (negative)
|
||||
.{ .short = 'd', .long = "dec2dms", .mode = .rotator },
|
||||
.{ .short = 'E', .long = "dmmm2dec", .mode = .rotator },
|
||||
.{ .short = 'e', .long = "dec2dmmm", .mode = .rotator },
|
||||
.{ .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 },
|
||||
const rotctl_commands = [_]HamlibCommand{
|
||||
.{ .short = 'q' }, // quit
|
||||
.{ .short = 'Q' }, // quit
|
||||
.{ .short = 'P', .long = "set_pos" }, // azimuth: f64, elevation: f64
|
||||
.{ .short = 'p', .long = "get_pos" }, // return az: f64, el: f64
|
||||
.{ .short = 'M', .long = "move" }, // direction: enum { up=2, down=4, left=8, right=16 }, speed: i8 (0-100 or -1)
|
||||
.{ .short = 'S', .long = "stop" },
|
||||
.{ .short = 'K', .long = "park" },
|
||||
.{ .short = 'C', .long = "set_conf" }, // token: []const u8, value: []const u8
|
||||
.{ .short = 'R', .long = "reset" }, // u1 (1 is reset all)
|
||||
.{ .short = '_', .long = "get_info" }, // return Model name
|
||||
.{ .short = 'K', .long = "park" },
|
||||
.{ .long = "dump_state" }, // ???
|
||||
.{ .short = '1', .long = "dump_caps" }, // ???
|
||||
.{ .short = 'w', .long = "send_cmd" }, // []const u8, send serial command directly to the rotator
|
||||
.{ .short = 'L', .long = "lonlat2loc" }, // return Maidenhead locator for given long: f64 and lat: f64, locator precision: u4 (2-12)
|
||||
.{ .short = 'l', .long = "loc2lonlat" }, // the inverse of the above
|
||||
.{ .short = 'D', .long = "dms2dec" }, // deg, min, sec, 0 (positive) or 1 (negative)
|
||||
.{ .short = 'd', .long = "dec2dms" },
|
||||
.{ .short = 'E', .long = "dmmm2dec" },
|
||||
.{ .short = 'e', .long = "dec2dmmm" },
|
||||
.{ .short = 'B', .long = "grb" },
|
||||
.{ .short = 'A', .long = "a_sp2a_lp" },
|
||||
.{ .short = 'a', .long = "d_sp2d_lp" },
|
||||
.{ .long = "pause" },
|
||||
};
|
||||
|
||||
// D, dms2dec 'Degrees' 'Minutes' 'Seconds' 'S/W'
|
||||
|
45
src/main.zig
45
src/main.zig
@ -1,25 +1,38 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Config = @import("./Config.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();
|
||||
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 });
|
||||
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 });
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user