add visualization of voltage channels
This commit is contained in:
parent
d0e7445421
commit
3838293c9d
13
build.zig
13
build.zig
@ -1,13 +1,10 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Module = std.Build.Module;
|
const Module = std.Build.Module;
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) !void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
const libdaq_dep = b.dependency("libdaq", .{});
|
|
||||||
_ = libdaq_dep; // TODO: Build libdaq
|
|
||||||
|
|
||||||
var lib: *Module = undefined;
|
var lib: *Module = undefined;
|
||||||
{
|
{
|
||||||
lib = b.createModule(.{
|
lib = b.createModule(.{
|
||||||
@ -42,6 +39,14 @@ pub fn build(b: *std.Build) void {
|
|||||||
exe.linkLibrary(raylib_dep.artifact("raylib"));
|
exe.linkLibrary(raylib_dep.artifact("raylib"));
|
||||||
exe.root_module.addImport("raylib", raylib_dep.module("raylib"));
|
exe.root_module.addImport("raylib", raylib_dep.module("raylib"));
|
||||||
|
|
||||||
|
const external_compiler_support_dir = try std.process.getEnvVarOwned(b.allocator, "NIEXTCCOMPILERSUPP");
|
||||||
|
exe.addSystemIncludePath(.{ .cwd_relative = try std.fs.path.join(b.allocator, &.{ external_compiler_support_dir, "include" }) });
|
||||||
|
exe.addLibraryPath(.{ .cwd_relative = try std.fs.path.join(b.allocator, &.{ external_compiler_support_dir, "lib64", "msvc" }) });
|
||||||
|
|
||||||
|
exe.linkSystemLibrary("nidaqmx");
|
||||||
|
exe.linkSystemLibrary("odbccp32");
|
||||||
|
exe.linkSystemLibrary("odbc32");
|
||||||
|
|
||||||
b.installArtifact(exe);
|
b.installArtifact(exe);
|
||||||
const run_cmd = b.addRunArtifact(exe);
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
run_cmd.step.dependOn(b.getInstallStep());
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
@ -3,10 +3,6 @@
|
|||||||
.version = "0.1.0",
|
.version = "0.1.0",
|
||||||
|
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.libdaq = .{
|
|
||||||
.url = "https://github.com/snort3/libdaq/archive/refs/tags/v3.0.17.tar.gz",
|
|
||||||
.hash = "1220338b42823b08d2f848549d03958b19836b4794c12f9a7875bca37a8f9477a5c0",
|
|
||||||
},
|
|
||||||
.@"raylib-zig" = .{
|
.@"raylib-zig" = .{
|
||||||
.url = "https://github.com/Not-Nik/raylib-zig/archive/43d15b05c2b97cab30103fa2b46cff26e91619ec.tar.gz",
|
.url = "https://github.com/Not-Nik/raylib-zig/archive/43d15b05c2b97cab30103fa2b46cff26e91619ec.tar.gz",
|
||||||
.hash = "12204a223b19043e17b79300413d02f60fc8004c0d9629b8d8072831e352a78bf212"
|
.hash = "12204a223b19043e17b79300413d02f60fc8004c0d9629b8d8072831e352a78bf212"
|
||||||
|
140
src/font-face.zig
Normal file
140
src/font-face.zig
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const rl = @import("raylib");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
font: rl.Font,
|
||||||
|
spacing: ?f32 = null,
|
||||||
|
line_height: f32 = 1.4,
|
||||||
|
|
||||||
|
pub fn getSpacing(self: @This()) f32 {
|
||||||
|
if (self.spacing) |spacing| {
|
||||||
|
return spacing;
|
||||||
|
} else {
|
||||||
|
return self.getSize() / 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getSize(self: @This()) f32 {
|
||||||
|
return @floatFromInt(self.font.baseSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawTextLines(self: @This(), lines: []const []const u8, position: rl.Vector2, tint: rl.Color) void {
|
||||||
|
var offset_y: f32 = 0;
|
||||||
|
|
||||||
|
const font_size = self.getSize();
|
||||||
|
|
||||||
|
for (lines) |line| {
|
||||||
|
self.drawText(line, position.add(.{ .x = 0, .y = offset_y }), tint);
|
||||||
|
|
||||||
|
const line_size = self.measureText(line);
|
||||||
|
offset_y += line_size.y + font_size * (self.line_height - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn measureTextLines(self: @This(), lines: []const []const u8) rl.Vector2 {
|
||||||
|
var text_size = rl.Vector2.zero();
|
||||||
|
|
||||||
|
const font_size = self.getSize();
|
||||||
|
|
||||||
|
for (lines) |line| {
|
||||||
|
const line_size = self.measureText(line);
|
||||||
|
|
||||||
|
text_size.x = @max(text_size.x, line_size.x);
|
||||||
|
text_size.y += line_size.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
text_size.y += (self.line_height - 1) * font_size * @as(f32, @floatFromInt(@max(lines.len - 1, 0)));
|
||||||
|
|
||||||
|
return text_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawTextEx(self: @This(), text: []const u8, position: rl.Vector2, tint: rl.Color, offset: *rl.Vector2) void {
|
||||||
|
if (self.font.texture.id == 0) return;
|
||||||
|
|
||||||
|
const font_size = self.getSize();
|
||||||
|
const spacing = self.getSpacing();
|
||||||
|
|
||||||
|
var iter = std.unicode.Utf8Iterator{ .bytes = text, .i = 0 };
|
||||||
|
while (iter.nextCodepoint()) |codepoint| {
|
||||||
|
|
||||||
|
if (codepoint == '\n') {
|
||||||
|
offset.x = 0;
|
||||||
|
offset.y += font_size * self.line_height;
|
||||||
|
} else {
|
||||||
|
if (!(codepoint <= 255 and std.ascii.isWhitespace(@intCast(codepoint)))) {
|
||||||
|
var codepoint_position = position.add(offset.*);
|
||||||
|
codepoint_position.x = @round(codepoint_position.x);
|
||||||
|
codepoint_position.y = @round(codepoint_position.y);
|
||||||
|
rl.drawTextCodepoint(self.font, codepoint, codepoint_position, font_size, tint);
|
||||||
|
}
|
||||||
|
|
||||||
|
const index: u32 = @intCast(rl.getGlyphIndex(self.font, codepoint));
|
||||||
|
if (self.font.glyphs[index].advanceX != 0) {
|
||||||
|
offset.x += @floatFromInt(self.font.glyphs[index].advanceX);
|
||||||
|
} else {
|
||||||
|
offset.x += self.font.recs[index].width;
|
||||||
|
offset.x += @floatFromInt(self.font.glyphs[index].offsetX);
|
||||||
|
}
|
||||||
|
offset.x += spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawText(self: @This(), text: []const u8, position: rl.Vector2, tint: rl.Color) void {
|
||||||
|
var offset = rl.Vector2.init(0, 0);
|
||||||
|
self.drawTextEx(text, position, tint, &offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawTextAlloc(self: @This(), allocator: Allocator, comptime fmt: []const u8, args: anytype, position: rl.Vector2, tint: rl.Color) !void {
|
||||||
|
const text = try std.fmt.allocPrint(allocator, fmt, args);
|
||||||
|
defer allocator.free(text);
|
||||||
|
|
||||||
|
self.drawText(text, position, tint);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn measureText(self: @This(), text: []const u8) rl.Vector2 {
|
||||||
|
var text_size = rl.Vector2.zero();
|
||||||
|
|
||||||
|
if (self.font.texture.id == 0) return text_size; // Security check
|
||||||
|
if (text.len == 0) return text_size;
|
||||||
|
|
||||||
|
const font_size = self.getSize();
|
||||||
|
const spacing = self.getSpacing();
|
||||||
|
|
||||||
|
var line_width: f32 = 0;
|
||||||
|
text_size.y = font_size;
|
||||||
|
|
||||||
|
var iter = std.unicode.Utf8Iterator{ .bytes = text, .i = 0 };
|
||||||
|
while (iter.nextCodepoint()) |codepoint| {
|
||||||
|
if (codepoint == '\n') {
|
||||||
|
text_size.y += font_size * self.line_height;
|
||||||
|
|
||||||
|
line_width = 0;
|
||||||
|
} else {
|
||||||
|
if (line_width > 0) {
|
||||||
|
line_width += spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index: u32 = @intCast(rl.getGlyphIndex(self.font, codepoint));
|
||||||
|
if (self.font.glyphs[index].advanceX != 0) {
|
||||||
|
line_width += @floatFromInt(self.font.glyphs[index].advanceX);
|
||||||
|
} else {
|
||||||
|
line_width += self.font.recs[index].width;
|
||||||
|
line_width += @floatFromInt(self.font.glyphs[index].offsetX);
|
||||||
|
}
|
||||||
|
|
||||||
|
text_size.x = @max(text_size.x, line_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return text_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawTextCenter(self: @This(), text: []const u8, position: rl.Vector2, tint: rl.Color) void {
|
||||||
|
const text_size = self.measureText(text);
|
||||||
|
const adjusted_position = rl.Vector2{
|
||||||
|
.x = position.x - text_size.x/2,
|
||||||
|
.y = position.y - text_size.y/2,
|
||||||
|
};
|
||||||
|
self.drawText(text, adjusted_position, tint);
|
||||||
|
}
|
279
src/main.zig
279
src/main.zig
@ -1,5 +1,15 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const rl = @import("raylib");
|
const rl = @import("raylib");
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("stdint.h");
|
||||||
|
@cDefine("__int64", "long long");
|
||||||
|
@cInclude("NIDAQmx.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const FontFace = @import("font-face.zig");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const Vec2 = rl.Vector2;
|
||||||
|
|
||||||
fn toTraceLogLevel(log_level: std.log.Level) rl.TraceLogLevel {
|
fn toTraceLogLevel(log_level: std.log.Level) rl.TraceLogLevel {
|
||||||
return switch (log_level) {
|
return switch (log_level) {
|
||||||
@ -10,6 +20,100 @@ fn toTraceLogLevel(log_level: std.log.Level) rl.TraceLogLevel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn checkDAQmxError(error_code: i32) !void {
|
||||||
|
if (error_code != 0) {
|
||||||
|
var error_msg: [512:0]u8 = .{ 0 } ** 512;
|
||||||
|
if (c.DAQmxGetErrorString(error_code, &error_msg, error_msg.len) == 0) {
|
||||||
|
std.debug.print("DAQmx error ({}): {s}\n", .{error_code, error_msg});
|
||||||
|
} else {
|
||||||
|
std.debug.print("DAQmx error ({}): Unknown (Buffer too small for error message)\n", .{error_code});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error_code < 0) {
|
||||||
|
return error.DAQError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn splitCommaDelimitedList(allocator: Allocator, list: []u8) ![][:0]u8 {
|
||||||
|
const name_count = std.mem.count(u8, list, ",") + 1;
|
||||||
|
var names = try std.ArrayList([:0]u8).initCapacity(allocator, name_count);
|
||||||
|
errdefer names.deinit();
|
||||||
|
|
||||||
|
var name_iter = std.mem.tokenizeSequence(u8, list, ", ");
|
||||||
|
while (name_iter.next()) |name| {
|
||||||
|
names.appendAssumeCapacity(try allocator.dupeZ(u8, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return try names.toOwnedSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn listDeviceNames(allocator: Allocator) ![][:0]u8 {
|
||||||
|
const required_size = c.DAQmxGetSysDevNames(null, 0);
|
||||||
|
if (required_size == 0) {
|
||||||
|
return try allocator.alloc([:0]u8, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const device_names_list = try allocator.alloc(u8, @intCast(required_size));
|
||||||
|
defer allocator.free(device_names_list);
|
||||||
|
|
||||||
|
try checkDAQmxError( c.DAQmxGetSysDevNames(device_names_list.ptr, @intCast(device_names_list.len)) );
|
||||||
|
|
||||||
|
const nullbyte = std.mem.indexOfScalar(u8, device_names_list, 0) orelse unreachable;
|
||||||
|
assert(nullbyte == required_size - 1);
|
||||||
|
|
||||||
|
return try splitCommaDelimitedList(allocator, device_names_list[0..nullbyte]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn listDeviceAIPhysicalChannels(allocator: Allocator, device: [:0]u8) ![][:0]u8 {
|
||||||
|
const required_size = c.DAQmxGetDevAIPhysicalChans(device, null, 0);
|
||||||
|
if (required_size == 0) {
|
||||||
|
return try allocator.alloc([:0]u8, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const device_names_list = try allocator.alloc(u8, @intCast(required_size));
|
||||||
|
defer allocator.free(device_names_list);
|
||||||
|
|
||||||
|
try checkDAQmxError( c.DAQmxGetDevAIPhysicalChans(device, device_names_list.ptr, @intCast(device_names_list.len)) );
|
||||||
|
|
||||||
|
const nullbyte = std.mem.indexOfScalar(u8, device_names_list, 0) orelse unreachable;
|
||||||
|
assert(nullbyte == required_size - 1);
|
||||||
|
|
||||||
|
return try splitCommaDelimitedList(allocator, device_names_list[0..nullbyte]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn freeNameList(allocator: Allocator, names: [][:0]u8) void {
|
||||||
|
for (names) |name| {
|
||||||
|
allocator.free(name);
|
||||||
|
}
|
||||||
|
allocator.free(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remap(from_min: f32, from_max: f32, to_min: f32, to_max: f32, value: f32) f32 {
|
||||||
|
const t = (value - from_min) / (from_max - from_min);
|
||||||
|
return std.math.lerp(to_min, to_max, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Channel = struct {
|
||||||
|
color: rl.Color,
|
||||||
|
min_sample: f64,
|
||||||
|
max_sample: f64,
|
||||||
|
samples: std.ArrayList(f64),
|
||||||
|
|
||||||
|
fn init(allocator: Allocator) Channel {
|
||||||
|
return Channel{
|
||||||
|
.color = rl.Color.red,
|
||||||
|
.min_sample = 0,
|
||||||
|
.max_sample = 0,
|
||||||
|
.samples = std.ArrayList(f64).init(allocator)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: Channel) void {
|
||||||
|
self.samples.deinit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
rl.setTraceLogLevel(toTraceLogLevel(std.log.default_level));
|
rl.setTraceLogLevel(toTraceLogLevel(std.log.default_level));
|
||||||
|
|
||||||
@ -17,7 +121,68 @@ pub fn main() !void {
|
|||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
|
|
||||||
_ = allocator;
|
|
||||||
|
|
||||||
|
const devices = try listDeviceNames(allocator);
|
||||||
|
defer freeNameList(allocator, devices);
|
||||||
|
|
||||||
|
std.debug.print("Devices ({}):\n", .{devices.len});
|
||||||
|
for (devices) |device| {
|
||||||
|
std.debug.print(" * '{s}'\n", .{device});
|
||||||
|
|
||||||
|
const channel_names = try listDeviceAIPhysicalChannels(allocator, device);
|
||||||
|
defer freeNameList(allocator, channel_names);
|
||||||
|
for (channel_names) |channel_name| {
|
||||||
|
std.debug.print(" * '{s}'\n", .{channel_name});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sample_rate: f64 = 5000;
|
||||||
|
|
||||||
|
var task_handle: c.TaskHandle = null;
|
||||||
|
try checkDAQmxError(c.DAQmxCreateTask("", &task_handle));
|
||||||
|
defer checkDAQmxError(c.DAQmxClearTask(task_handle)) catch unreachable;
|
||||||
|
|
||||||
|
var channels = std.ArrayList(Channel).init(allocator);
|
||||||
|
defer channels.deinit();
|
||||||
|
defer {
|
||||||
|
for (channels.items) |channel| {
|
||||||
|
channel.deinit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var channel1 = Channel.init(allocator);
|
||||||
|
channel1.color = rl.Color.red;
|
||||||
|
channel1.min_sample = -10.0;
|
||||||
|
channel1.max_sample = 10.0;
|
||||||
|
try checkDAQmxError(c.DAQmxCreateAIVoltageChan(task_handle, "Dev1/ai1", "", c.DAQmx_Val_Cfg_Default, channel1.min_sample, channel1.max_sample, c.DAQmx_Val_Volts, null));
|
||||||
|
try channels.append(channel1);
|
||||||
|
|
||||||
|
var channel2 = Channel.init(allocator);
|
||||||
|
channel2.color = rl.Color.green;
|
||||||
|
channel2.min_sample = -10.0;
|
||||||
|
channel2.max_sample = 10.0;
|
||||||
|
try checkDAQmxError(c.DAQmxCreateAIVoltageChan(task_handle, "Dev1/ai2", "", c.DAQmx_Val_Cfg_Default, channel2.min_sample, channel2.max_sample, c.DAQmx_Val_Volts, null));
|
||||||
|
try channels.append(channel2);
|
||||||
|
|
||||||
|
var channel3 = Channel.init(allocator);
|
||||||
|
channel3.color = rl.Color.blue;
|
||||||
|
channel3.min_sample = -10.0;
|
||||||
|
channel3.max_sample = 10.0;
|
||||||
|
try checkDAQmxError(c.DAQmxCreateAIVoltageChan(task_handle, "Dev1/ai3", "", c.DAQmx_Val_Cfg_Default, channel3.min_sample, channel3.max_sample, c.DAQmx_Val_Volts, null));
|
||||||
|
try channels.append(channel3);
|
||||||
|
|
||||||
|
var channel4 = Channel.init(allocator);
|
||||||
|
channel4.color = rl.Color.yellow;
|
||||||
|
channel4.min_sample = -10.0;
|
||||||
|
channel4.max_sample = 10.0;
|
||||||
|
try checkDAQmxError(c.DAQmxCreateAIVoltageChan(task_handle, "Dev1/ai4", "", c.DAQmx_Val_Cfg_Default, channel4.min_sample, channel4.max_sample, c.DAQmx_Val_Volts, null));
|
||||||
|
try channels.append(channel4);
|
||||||
|
|
||||||
|
try checkDAQmxError(c.DAQmxCfgSampClkTiming(task_handle, null, sample_rate, c.DAQmx_Val_Rising, c.DAQmx_Val_ContSamps, 0));
|
||||||
|
|
||||||
|
try checkDAQmxError(c.DAQmxStartTask(task_handle));
|
||||||
|
defer checkDAQmxError(c.DAQmxStopTask(task_handle)) catch unreachable;
|
||||||
|
|
||||||
rl.initWindow(800, 450, "DAQ view");
|
rl.initWindow(800, 450, "DAQ view");
|
||||||
defer rl.closeWindow();
|
defer rl.closeWindow();
|
||||||
@ -25,12 +190,122 @@ pub fn main() !void {
|
|||||||
|
|
||||||
rl.setTargetFPS(60);
|
rl.setTargetFPS(60);
|
||||||
|
|
||||||
|
var font_face = FontFace{
|
||||||
|
.font = rl.getFontDefault()
|
||||||
|
};
|
||||||
|
|
||||||
|
var last_read_size: u32 = 0;
|
||||||
|
var last_read_at: f64 = 0;
|
||||||
|
|
||||||
while (!rl.windowShouldClose()) {
|
while (!rl.windowShouldClose()) {
|
||||||
rl.beginDrawing();
|
rl.beginDrawing();
|
||||||
defer rl.endDrawing();
|
defer rl.endDrawing();
|
||||||
|
|
||||||
rl.clearBackground(rl.Color.white);
|
rl.clearBackground(rl.Color.white);
|
||||||
|
|
||||||
rl.drawText("Congrats! You created your first window!", 190, 200, 20, rl.Color.light_gray);
|
const window_width: f32 = @floatFromInt(rl.getScreenWidth());
|
||||||
|
const window_height: f32 = @floatFromInt(rl.getScreenHeight());
|
||||||
|
|
||||||
|
rl.drawLineV(
|
||||||
|
Vec2.init(0, window_height/2),
|
||||||
|
Vec2.init(window_width, window_height/2),
|
||||||
|
rl.Color.gray
|
||||||
|
);
|
||||||
|
|
||||||
|
for (channels.items) |channel| {
|
||||||
|
const samples = channel.samples;
|
||||||
|
const min_sample: f32 = @floatCast(channel.min_sample);
|
||||||
|
const max_sample: f32 = @floatCast(channel.max_sample);
|
||||||
|
|
||||||
|
const max_visible_samples: u32 = @intFromFloat(sample_rate * 5);
|
||||||
|
var shown_samples = samples.items;
|
||||||
|
if (shown_samples.len > max_visible_samples) {
|
||||||
|
shown_samples = samples.items[(samples.items.len-max_visible_samples-1)..(samples.items.len-1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shown_samples.len >= 2) {
|
||||||
|
const color = channel.color; // rl.Color.alpha(channel.color, 1.5 / @as(f32, @floatFromInt(channels.items.len)));
|
||||||
|
|
||||||
|
const samples_per_pixel = max_visible_samples / window_width;
|
||||||
|
|
||||||
|
var i: f32 = 0;
|
||||||
|
while (i < @as(f32, @floatFromInt(shown_samples.len)) - samples_per_pixel) : (i += samples_per_pixel) {
|
||||||
|
const next_i = i + samples_per_pixel;
|
||||||
|
|
||||||
|
var min_slice_sample = shown_samples[@intFromFloat(i)];
|
||||||
|
var max_slice_sample = shown_samples[@intFromFloat(i)];
|
||||||
|
|
||||||
|
for (@intFromFloat(i)..@intFromFloat(next_i)) |sub_i| {
|
||||||
|
min_slice_sample = @min(min_slice_sample, shown_samples[sub_i]);
|
||||||
|
max_slice_sample = @max(max_slice_sample, shown_samples[sub_i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const offset_i: f32 = @floatFromInt(max_visible_samples - shown_samples.len);
|
||||||
|
|
||||||
|
const start_pos = Vec2.init(
|
||||||
|
(offset_i + i) / max_visible_samples * window_width,
|
||||||
|
remap(min_sample, max_sample, 0, window_height, @floatCast(min_slice_sample))
|
||||||
|
);
|
||||||
|
|
||||||
|
const end_pos = Vec2.init(
|
||||||
|
(offset_i + i) / max_visible_samples * window_width,
|
||||||
|
remap(min_sample, max_sample, 0, window_height, @floatCast(max_slice_sample))
|
||||||
|
);
|
||||||
|
rl.drawLineV(start_pos, end_pos, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = rl.getTime();
|
||||||
|
|
||||||
|
{
|
||||||
|
var y: f32 = 10;
|
||||||
|
try font_face.drawTextAlloc(allocator, "Time: {d:.03}", .{now}, Vec2.init(10, y), rl.Color.black);
|
||||||
|
y += 10;
|
||||||
|
|
||||||
|
try font_face.drawTextAlloc(allocator, "Last read size (bytes): {d}", .{last_read_size * @as(u32, @intCast(channels.items.len)) * @sizeOf(f64)}, Vec2.init(20, y), rl.Color.black);
|
||||||
|
y += 10;
|
||||||
|
|
||||||
|
try font_face.drawTextAlloc(allocator, "Last read count per channel: {d}", .{last_read_size}, Vec2.init(20, y), rl.Color.black);
|
||||||
|
y += 10;
|
||||||
|
|
||||||
|
try font_face.drawTextAlloc(allocator, "Time since last read: {d:.05}", .{now - last_read_at}, Vec2.init(20, y), rl.Color.black);
|
||||||
|
y += 10;
|
||||||
|
|
||||||
|
for (1.., channels.items) |i, channel| {
|
||||||
|
const sample_count = channel.samples.items.len;
|
||||||
|
y += 10;
|
||||||
|
|
||||||
|
try font_face.drawTextAlloc(allocator, "Channel {}:", .{i}, Vec2.init(10, y), rl.Color.black);
|
||||||
|
y += 10;
|
||||||
|
|
||||||
|
try font_face.drawTextAlloc(allocator, "Sample count: {}", .{sample_count}, Vec2.init(20, y), rl.Color.black);
|
||||||
|
y += 10;
|
||||||
|
|
||||||
|
try font_face.drawTextAlloc(allocator, "Sample rate: {d:.03}", .{@as(f64, @floatFromInt(sample_count)) / now}, Vec2.init(20, y), rl.Color.black);
|
||||||
|
y += 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.drawFPS(@as(i32, @intFromFloat(window_width)) - 100, 10);
|
||||||
|
|
||||||
|
var read_buffer: [1024]f64 = undefined;
|
||||||
|
var read: i32 = 0;
|
||||||
|
const err = c.DAQmxReadAnalogF64(task_handle, -1, 0, c.DAQmx_Val_GroupByChannel, &read_buffer, @intCast(read_buffer.len), &read, null);
|
||||||
|
if (err == 0 and read > 0) {
|
||||||
|
const read_u32 = @as(u32, @intCast(read));
|
||||||
|
|
||||||
|
for (0.., channels.items) |i, *channel| {
|
||||||
|
const channel_samples = read_buffer[(i*read_u32)..((i+1)*read_u32)];
|
||||||
|
try channel.samples.appendSlice(channel_samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
last_read_size = read_u32;
|
||||||
|
last_read_at = now;
|
||||||
|
} else if (err != 0 and err != c.DAQmxErrorSamplesNotYetAvailable) {
|
||||||
|
// TODO: Handle error c.DAQmxErrorSamplesNoLongerAvailable
|
||||||
|
// This error occurs, when application is not reading data fast enough and the DAQmx internal buffer fills up.
|
||||||
|
try checkDAQmxError(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user