mirror of
https://github.com/JasonYANG170/logicanalyzer.git
synced 2024-11-23 12:06:27 +00:00
Measure tool
This commit is contained in:
parent
397c406085
commit
767cd0314b
|
@ -0,0 +1,30 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="375" d:DesignHeight="260" Width="375" Height="260"
|
||||
x:Class="LogicAnalyzer.Controls.ChannelMeasures" BorderBrush="LightGray" BorderThickness="1">
|
||||
<Grid ColumnDefinitions="3.2*,*" RowDefinitions="20,*">
|
||||
<TextBlock Grid.Row="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" Margin="10,5,10,10" Name="lblName">Channel 0</TextBlock>
|
||||
<StackPanel Grid.Column="0" Grid.Row="1" Spacing="10" Margin="10,10,10,10">
|
||||
<TextBlock>Total positive pulses:</TextBlock>
|
||||
<TextBlock>Average positive pulse duration:</TextBlock>
|
||||
<TextBlock>Predominant positive pulse duration:</TextBlock>
|
||||
<TextBlock>Total negative pulses:</TextBlock>
|
||||
<TextBlock>Average negative pulse duration:</TextBlock>
|
||||
<TextBlock>Predominant negative pulse duration:</TextBlock>
|
||||
<TextBlock>Average frequency:</TextBlock>
|
||||
<TextBlock>Predominant frequency:</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1" Grid.Row="1" Spacing="10" Margin="10,10,10,10">
|
||||
<TextBlock Name="lblPosPulses">0</TextBlock>
|
||||
<TextBlock Name="lblPosPulsesDuration">0ns</TextBlock>
|
||||
<TextBlock Name="lblPosPredPulsesDuration">0ns</TextBlock>
|
||||
<TextBlock Name="lblNegPulses">0</TextBlock>
|
||||
<TextBlock Name="lblNegPulsesDuration">0ns</TextBlock>
|
||||
<TextBlock Name="lblNegPredPulsesDuration">0ns</TextBlock>
|
||||
<TextBlock Name="lblFreq">0Hz</TextBlock>
|
||||
<TextBlock Name="lblPredFreq">0hz</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
|
@ -0,0 +1,122 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
using LogicAnalyzer.Classes;
|
||||
using LogicAnalyzer.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace LogicAnalyzer.Controls
|
||||
{
|
||||
public partial class ChannelMeasures : UserControl
|
||||
{
|
||||
public static readonly StyledProperty<IBrush> ForegroundProperty = AvaloniaProperty.Register<SampleMarker, IBrush>(nameof(Foreground));
|
||||
|
||||
public static readonly StyledProperty<IBrush> BackgroundProperty = AvaloniaProperty.Register<SampleMarker, IBrush>(nameof(Background));
|
||||
|
||||
public new IBrush Foreground
|
||||
{
|
||||
get { return GetValue<IBrush>(ForegroundProperty); }
|
||||
set { SetValue<IBrush>(ForegroundProperty, value); base.Foreground = value; UpdateTextColors(); InvalidateVisual(); }
|
||||
}
|
||||
|
||||
public new IBrush Background
|
||||
{
|
||||
get { return GetValue<IBrush>(BackgroundProperty); }
|
||||
set { SetValue<IBrush>(BackgroundProperty, value); base.Background = value; UpdateTextColors(); InvalidateVisual(); }
|
||||
}
|
||||
|
||||
void UpdateTextColors()
|
||||
{
|
||||
lblFreq.Foreground= Foreground;
|
||||
lblFreq.Background= Background;
|
||||
lblNegPulses.Foreground= Foreground;
|
||||
lblNegPulses.Background= Background;
|
||||
lblNegPulsesDuration.Foreground= Foreground;
|
||||
lblNegPulsesDuration.Background= Background;
|
||||
lblPosPulses.Foreground= Foreground;
|
||||
lblPosPulses.Background = Background;
|
||||
lblPosPulsesDuration.Foreground = Foreground;
|
||||
lblPosPulsesDuration.Background = Background;
|
||||
}
|
||||
|
||||
public ChannelMeasures()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void SetData(string channelName, byte[] channelSamples, int frequency)
|
||||
{
|
||||
int currentPulse = -1;
|
||||
int currentCount = 0;
|
||||
|
||||
List<int> posLengths= new List<int>();
|
||||
List<int> negLengths = new List<int>();
|
||||
|
||||
for (int buc = 0; buc < channelSamples.Length; buc++)
|
||||
{
|
||||
if (channelSamples[buc] != currentPulse)
|
||||
{
|
||||
if (currentPulse == 1)
|
||||
posLengths.Add(currentCount);
|
||||
else if (currentPulse == 0)
|
||||
negLengths.Add(currentCount);
|
||||
|
||||
currentPulse = channelSamples[buc];
|
||||
currentCount = 1;
|
||||
}
|
||||
else
|
||||
currentCount++;
|
||||
}
|
||||
|
||||
if (currentPulse == 1)
|
||||
posLengths.Add(currentCount);
|
||||
else if (currentPulse == 0)
|
||||
negLengths.Add(currentCount);
|
||||
|
||||
var posGrouped = posLengths.GroupBy(p => p).OrderBy(g => g.Count()).ToArray();
|
||||
var posOrderedByCount = posGrouped.SelectMany(g => g.ToArray()).ToArray();
|
||||
|
||||
int fivePercent = (int)(posOrderedByCount.Length * 0.95);
|
||||
|
||||
var finalPosSamples = posOrderedByCount.Skip(fivePercent).ToArray();
|
||||
|
||||
var negGrouped = negLengths.GroupBy(p => p).OrderBy(g => g.Count()).ToArray();
|
||||
var negOrderedByCount = negGrouped.SelectMany(g => g.ToArray()).ToArray();
|
||||
|
||||
fivePercent = (int)(negOrderedByCount.Length * 0.95);
|
||||
|
||||
var finalNegSamples = negOrderedByCount.Skip(fivePercent).ToArray();
|
||||
|
||||
int minPulses = Math.Min(finalPosSamples.Length, finalNegSamples.Length);
|
||||
|
||||
var matchedPos = finalPosSamples.Skip(finalPosSamples.Length - minPulses).ToArray();
|
||||
var matchedNeg = finalNegSamples.Skip(finalNegSamples.Length - minPulses).ToArray();
|
||||
|
||||
int totalSamples = matchedPos.Sum() + matchedNeg.Sum();
|
||||
double period = totalSamples * (1.0 / frequency);
|
||||
|
||||
double predPosPeriod = finalPosSamples.Length == 0 ? 0 : (finalPosSamples.Average() * (1.0 / frequency));
|
||||
double predNegPeriod = finalNegSamples.Length == 0 ? 0 : (finalNegSamples.Average() * (1.0 / frequency));
|
||||
|
||||
double predFreq = period == 0 ? 0 : (1.0 / (predPosPeriod + predNegPeriod));
|
||||
|
||||
double avgPosPeriod = posLengths.Count == 0 ? 0 : (posLengths.Average() * (1.0 / frequency));
|
||||
double avgNegPeriod = negLengths.Count == 0 ? 0 : (negLengths.Average() * (1.0 / frequency));
|
||||
|
||||
double avgFreq = period == 0 ? 0 : (1.0 / (avgPosPeriod + avgNegPeriod));
|
||||
|
||||
lblPosPulses.Text = posLengths.Count.ToString();
|
||||
lblNegPulses.Text = negLengths.Count.ToString();
|
||||
lblPosPredPulsesDuration.Text = predPosPeriod.ToSmallTime();
|
||||
lblPosPulsesDuration.Text = avgPosPeriod.ToSmallTime();
|
||||
lblNegPredPulsesDuration.Text = predNegPeriod.ToSmallTime();
|
||||
lblNegPulsesDuration.Text = avgNegPeriod.ToSmallTime();
|
||||
lblPredFreq.Text = predFreq.ToLargeFrequency();
|
||||
lblFreq.Text = avgFreq.ToLargeFrequency();
|
||||
lblName.Text = $"Channel {channelName}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -11,9 +11,10 @@
|
|||
|
||||
<Panel.ContextMenu>
|
||||
<ContextMenu Name="rgnDeleteMenu">
|
||||
<MenuItem Header="Delete regions" Name="mnuDeleteRegions" />
|
||||
<MenuItem Header="Delete regions and samples" Name="mnuDeleteRegionsSamples" />
|
||||
</ContextMenu>
|
||||
<MenuItem Header="Delete regions" Name="mnuDeleteRegions" />
|
||||
<MenuItem Header="Delete regions and samples" Name="mnuDeleteRegionsSamples" />
|
||||
<MenuItem Header="Measure region" Name="mnuMeasure" />
|
||||
</ContextMenu>
|
||||
</Panel.ContextMenu>
|
||||
</Panel>
|
||||
</UserControl>
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace LogicAnalyzer.Controls
|
|||
|
||||
public event EventHandler<SamplesEventArgs> SamplesDeleted;
|
||||
public event EventHandler<UserMarkerEventArgs> UserMarkerSelected;
|
||||
public event EventHandler<SamplesEventArgs> MeasureSamples;
|
||||
|
||||
List<SelectedSampleRegion> regions = new List<SelectedSampleRegion>();
|
||||
|
||||
|
@ -66,6 +67,13 @@ namespace LogicAnalyzer.Controls
|
|||
InitializeComponent();
|
||||
mnuDeleteRegions.Click += MnuDeleteRegions_Click;
|
||||
mnuDeleteRegionsSamples.Click += MnuDeleteRegionsSamples_Click;
|
||||
mnuMeasure.Click += MnuMeasure_Click;
|
||||
}
|
||||
|
||||
private void MnuMeasure_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
if (MeasureSamples != null)
|
||||
MeasureSamples(this, new SamplesEventArgs { FirstSample = regionsToDelete[0].FirstSample, SampleCount = regionsToDelete[0].SampleCount });
|
||||
}
|
||||
|
||||
private void MnuDeleteRegionsSamples_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
x:Class="LogicAnalyzer.Dialogs.MeasureDialog"
|
||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="450"
|
||||
MaxWidth="400" MinWidth="400"
|
||||
MaxHeight="450" MinHeight="450"
|
||||
Title="Measurements" Icon="/Assets/window.ico"
|
||||
Background="#383838" CanResize="False" WindowStartupLocation="CenterOwner">
|
||||
<StackPanel>
|
||||
<Grid ColumnDefinitions="2.5*,*" RowDefinitions="20,20,20">
|
||||
<TextBlock Grid.Row="0" Margin="10,5,10,5">Total samples:</TextBlock>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Margin="10,5,10,5" Name="lblSamples">0</TextBlock>
|
||||
<TextBlock Grid.Row="1" Margin="10,5,10,5">Total period:</TextBlock>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Margin="10,5,10,5" Name="lblPeriod">0ns</TextBlock>
|
||||
<TextBlock Grid.Row="2" Margin="10,5,10,5">Sample period:</TextBlock>
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Margin="10,5,10,5" Name="lblSamplePeriod">0ns</TextBlock>
|
||||
</Grid>
|
||||
<Grid RowDefinitions="8*,2*">
|
||||
<ScrollViewer Name="scrViewer" Margin="10" VerticalAlignment="Top" Height="300" Background="#282828">
|
||||
<StackPanel Name="pnlControls">
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Row="1" Margin="0,10,0,10">
|
||||
<Button Name="btnAccept" Margin="10,0,10,0">Accept</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Window>
|
|
@ -0,0 +1,42 @@
|
|||
using Avalonia.Controls;
|
||||
using LogicAnalyzer.Controls;
|
||||
using LogicAnalyzer.Extensions;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
|
||||
namespace LogicAnalyzer.Dialogs
|
||||
{
|
||||
public partial class MeasureDialog : Window
|
||||
{
|
||||
public MeasureDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
btnAccept.Click += BtnAccept_Click;
|
||||
}
|
||||
|
||||
private void BtnAccept_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
public void SetData(string[] ChannelNames, IEnumerable<byte[]> Samples, int SamplingFrequency)
|
||||
{
|
||||
int samples = Samples.First().Length;
|
||||
lblSamples.Text = samples.ToString();
|
||||
double period = (1.0 / (double)SamplingFrequency) * samples;
|
||||
lblPeriod.Text = period.ToSmallTime();
|
||||
|
||||
period = 1.0 / (double)SamplingFrequency;
|
||||
lblSamplePeriod.Text = period.ToSmallTime();
|
||||
|
||||
for (int buc = 0; buc < ChannelNames.Length; buc++)
|
||||
{
|
||||
ChannelMeasures cm = new ChannelMeasures();
|
||||
cm.SetData(ChannelNames[buc], Samples.Skip(buc).First(), SamplingFrequency);
|
||||
pnlControls.Children.Add(cm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LogicAnalyzer.Extensions
|
||||
{
|
||||
public static class DoubleExtensions
|
||||
{
|
||||
public static string ToSmallTime(this double Time)
|
||||
{
|
||||
if (Time < 0.000001)
|
||||
return $"{Math.Round(Time * 1000000000, 2)}ns";
|
||||
else if (Time < 0.001)
|
||||
return $"{Math.Round(Time * 1000000, 2)}us";
|
||||
else if (Time < 1)
|
||||
return $"{Math.Round(Time * 1000, 2)}ms";
|
||||
else
|
||||
return $"{Math.Round(Time, 2)}s";
|
||||
}
|
||||
|
||||
public static string ToLargeFrequency(this double Frequency)
|
||||
{
|
||||
if (Frequency > 999999)
|
||||
return $"{Math.Round(Frequency / 1000000, 2)}Mhz";
|
||||
else if (Frequency > 999)
|
||||
return $"{Math.Round(Frequency / 1000, 2)}Khz";
|
||||
else
|
||||
return $"{Math.Round(Frequency, 2)}Hz";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@ namespace LogicAnalyzer
|
|||
sampleMarker.RegionDeleted += sampleMarker_RegionDeleted;
|
||||
sampleMarker.UserMarkerSelected += sampleMarker_UserMarkerSelected;
|
||||
sampleMarker.SamplesDeleted += SampleMarker_SamplesDeleted;
|
||||
sampleMarker.MeasureSamples += SampleMarker_MeasureSamples;
|
||||
tkInScreen.PropertyChanged += tkInScreen_ValueChanged;
|
||||
scrSamplePos.Scroll += scrSamplePos_ValueChanged;
|
||||
mnuOpen.Click += mnuOpen_Click;
|
||||
|
@ -53,6 +54,25 @@ namespace LogicAnalyzer
|
|||
RefreshPorts();
|
||||
}
|
||||
|
||||
private async void SampleMarker_MeasureSamples(object? sender, SamplesEventArgs e)
|
||||
{
|
||||
List<byte[]> samples = new List<byte[]>();
|
||||
|
||||
for (int buc = 0; buc < sampleViewer.ChannelCount; buc++)
|
||||
samples.Add(ExtractSamples(buc, sampleViewer.Samples, e.FirstSample, e.SampleCount));
|
||||
|
||||
var names = channelViewer.ChannelsText.ToArray();
|
||||
|
||||
for (int buc = 0; buc < names.Length; buc++)
|
||||
if (string.IsNullOrWhiteSpace(names[buc]))
|
||||
names[buc] = (buc + 1).ToString();
|
||||
|
||||
MeasureDialog dlg = new MeasureDialog();
|
||||
dlg.SetData(names, samples, settings.Frequency);
|
||||
await dlg.ShowDialog(this);
|
||||
|
||||
}
|
||||
|
||||
private async void MnuNetSettings_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var dlg = new NetworkSettingsDialog();
|
||||
|
@ -370,6 +390,12 @@ namespace LogicAnalyzer
|
|||
channel.Samples = samples.Select(s => (s & mask) != 0 ? (byte)1 : (byte)0).ToArray();
|
||||
}
|
||||
|
||||
private byte[] ExtractSamples(int channel, uint[] samples, int firstSample, int count)
|
||||
{
|
||||
int mask = 1 << channel;
|
||||
return samples.Skip(firstSample).Take(count).Select(s => (s & mask) != 0 ? (byte)1 : (byte)0).ToArray();
|
||||
}
|
||||
|
||||
private async void btnOpenClose_Click(object? sender, EventArgs e)
|
||||
{
|
||||
if (driver == null)
|
||||
|
|
Loading…
Reference in New Issue
Block a user