147 lines
4.3 KiB
C#
147 lines
4.3 KiB
C#
using System.Windows;
|
|
using System.Windows.Media;
|
|
using System.Windows.Threading;
|
|
using Pcie8586Probe.Models;
|
|
using Pcie8586Probe.ViewModels;
|
|
|
|
namespace Pcie8586Probe;
|
|
|
|
public partial class MainWindow : Window
|
|
{
|
|
private const double TableRowHeight = 20.0;
|
|
private const double TableHeaderHeight = 24.0;
|
|
private readonly MainViewModel _viewModel = new();
|
|
private readonly DispatcherTimer _renderTimer;
|
|
|
|
public MainWindow()
|
|
{
|
|
InitializeComponent();
|
|
DataContext = _viewModel;
|
|
|
|
_renderTimer = new DispatcherTimer
|
|
{
|
|
Interval = TimeSpan.FromMilliseconds(50)
|
|
};
|
|
_renderTimer.Tick += RenderTimer_Tick;
|
|
_renderTimer.Start();
|
|
ConfigurePlot();
|
|
}
|
|
|
|
private void ConfigurePlot()
|
|
{
|
|
var fontName = GetChineseFontName();
|
|
WavePlot.Plot.Font.Set(fontName);
|
|
WavePlot.Plot.Title("PCIe8586M \u5B9E\u65F6\u6CE2\u5F62");
|
|
WavePlot.Plot.XLabel("\u6837\u672C\u5E8F\u53F7");
|
|
WavePlot.Plot.YLabel("\u7535\u538B (V)");
|
|
WavePlot.Plot.Axes.SetLimitsY(-5.2, 5.2);
|
|
WavePlot.Refresh();
|
|
}
|
|
|
|
private void RenderTimer_Tick(object? sender, EventArgs e)
|
|
{
|
|
if (_viewModel.ConsumePlotClearRequest())
|
|
{
|
|
ClearPlot();
|
|
}
|
|
|
|
var frame = _viewModel.ConsumeLatestFrame();
|
|
if (frame is not null)
|
|
{
|
|
_viewModel.AppendTableRows(frame, GetVisibleTableRowCount());
|
|
}
|
|
|
|
var plotBlock = _viewModel.ConsumePendingPlotBlock();
|
|
if (plotBlock is not null)
|
|
{
|
|
DrawFrame(plotBlock, _viewModel.ConfiguredRange);
|
|
}
|
|
}
|
|
|
|
private void ClearPlot()
|
|
{
|
|
WavePlot.Plot.Clear();
|
|
ConfigurePlot();
|
|
}
|
|
|
|
private void DrawFrame(SampleBlock frame, InputRange range)
|
|
{
|
|
WavePlot.Plot.Clear();
|
|
var xLimits = GetDisplayX(frame);
|
|
for (var channel = 0; channel < frame.ChannelCount; channel++)
|
|
{
|
|
var (xs, ys) = Downsample(frame.Channels[channel], frame.StartSampleIndex, 6_000);
|
|
var scatter = WavePlot.Plot.Add.Scatter(xs, ys);
|
|
scatter.LegendText = $"CH{channel}";
|
|
}
|
|
|
|
WavePlot.Plot.Axes.SetLimitsX(xLimits.Min, xLimits.Max);
|
|
|
|
WavePlot.Plot.Axes.SetLimitsY(
|
|
range == InputRange.PlusMinus5V ? -5.2 : -1.2,
|
|
range == InputRange.PlusMinus5V ? 5.2 : 1.2);
|
|
WavePlot.Plot.ShowLegend();
|
|
WavePlot.Refresh();
|
|
}
|
|
|
|
private static (double[] Xs, double[] Ys) Downsample(double[] source, long startSampleIndex, int maxPoints)
|
|
{
|
|
if (source.Length <= maxPoints)
|
|
{
|
|
var xs = new double[source.Length];
|
|
for (var index = 0; index < source.Length; index++)
|
|
{
|
|
xs[index] = startSampleIndex + index;
|
|
}
|
|
|
|
return (xs, source);
|
|
}
|
|
|
|
var xsDownsampled = new double[maxPoints];
|
|
var ys = new double[maxPoints];
|
|
var step = source.Length / (double)maxPoints;
|
|
for (var index = 0; index < maxPoints; index++)
|
|
{
|
|
var sourceIndex = (int)(index * step);
|
|
xsDownsampled[index] = startSampleIndex + sourceIndex;
|
|
ys[index] = source[sourceIndex];
|
|
}
|
|
|
|
return (xsDownsampled, ys);
|
|
}
|
|
|
|
private static (double Min, double Max) GetDisplayX(SampleBlock frame)
|
|
{
|
|
var min = frame.StartSampleIndex;
|
|
var max = frame.StartSampleIndex + Math.Max(1, frame.SamplesPerChannel - 1);
|
|
return (min, max);
|
|
}
|
|
|
|
private int GetVisibleTableRowCount()
|
|
{
|
|
var usableHeight = Math.Max(0, SampleTable.ActualHeight - TableHeaderHeight - 2);
|
|
return Math.Max(1, (int)Math.Ceiling(usableHeight / TableRowHeight));
|
|
}
|
|
|
|
private static string GetChineseFontName()
|
|
{
|
|
string[] candidates =
|
|
[
|
|
"Microsoft YaHei UI",
|
|
"Microsoft YaHei",
|
|
"SimSun",
|
|
"Noto Sans CJK SC",
|
|
"Arial Unicode MS"
|
|
];
|
|
|
|
var installed = Fonts.SystemFontFamilies.Select(static family => family.Source).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
return candidates.FirstOrDefault(installed.Contains) ?? "Segoe UI";
|
|
}
|
|
|
|
private void Window_Closing(object? sender, System.ComponentModel.CancelEventArgs e)
|
|
{
|
|
_renderTimer.Stop();
|
|
_viewModel.Dispose();
|
|
}
|
|
}
|