diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/ChannelMeasures.axaml b/Software/LogicAnalyzer/LogicAnalyzer/Controls/ChannelMeasures.axaml
new file mode 100644
index 0000000..36e93fa
--- /dev/null
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/ChannelMeasures.axaml
@@ -0,0 +1,30 @@
+
+
+ Channel 0
+
+ Total positive pulses:
+ Average positive pulse duration:
+ Predominant positive pulse duration:
+ Total negative pulses:
+ Average negative pulse duration:
+ Predominant negative pulse duration:
+ Average frequency:
+ Predominant frequency:
+
+
+ 0
+ 0ns
+ 0ns
+ 0
+ 0ns
+ 0ns
+ 0Hz
+ 0hz
+
+
+
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/ChannelMeasures.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Controls/ChannelMeasures.axaml.cs
new file mode 100644
index 0000000..1dc1efb
--- /dev/null
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/ChannelMeasures.axaml.cs
@@ -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 ForegroundProperty = AvaloniaProperty.Register(nameof(Foreground));
+
+ public static readonly StyledProperty BackgroundProperty = AvaloniaProperty.Register(nameof(Background));
+
+ public new IBrush Foreground
+ {
+ get { return GetValue(ForegroundProperty); }
+ set { SetValue(ForegroundProperty, value); base.Foreground = value; UpdateTextColors(); InvalidateVisual(); }
+ }
+
+ public new IBrush Background
+ {
+ get { return GetValue(BackgroundProperty); }
+ set { SetValue(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 posLengths= new List();
+ List negLengths = new List();
+
+ 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}";
+ }
+
+ }
+}
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleMarker.axaml b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleMarker.axaml
index d8f389b..271d83a 100644
--- a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleMarker.axaml
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleMarker.axaml
@@ -11,9 +11,10 @@
-
-
-
+
+
+
+
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleMarker.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleMarker.axaml.cs
index ba6d78b..e1d8740 100644
--- a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleMarker.axaml.cs
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleMarker.axaml.cs
@@ -52,6 +52,7 @@ namespace LogicAnalyzer.Controls
public event EventHandler SamplesDeleted;
public event EventHandler UserMarkerSelected;
+ public event EventHandler MeasureSamples;
List regions = new List();
@@ -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)
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MeasureDialog.axaml b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MeasureDialog.axaml
new file mode 100644
index 0000000..13a891a
--- /dev/null
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MeasureDialog.axaml
@@ -0,0 +1,31 @@
+
+
+
+ Total samples:
+ 0
+ Total period:
+ 0ns
+ Sample period:
+ 0ns
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MeasureDialog.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MeasureDialog.axaml.cs
new file mode 100644
index 0000000..b8c93d3
--- /dev/null
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MeasureDialog.axaml.cs
@@ -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 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);
+ }
+ }
+ }
+}
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Extensions/DoubleExtensions.cs b/Software/LogicAnalyzer/LogicAnalyzer/Extensions/DoubleExtensions.cs
new file mode 100644
index 0000000..cb1627d
--- /dev/null
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Extensions/DoubleExtensions.cs
@@ -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";
+ }
+ }
+}
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs
index 4065091..2b26f02 100644
--- a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs
+++ b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs
@@ -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 samples = new List();
+
+ 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)