SSPC-Tester/Driver/PCIE8586/Pcie8586Probe/MainWindow.xaml.cs

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();
}
}