protected override void OnRender(DrawingContext DC) { DC.DrawRectangle(BorderBrush, null, new Rect(0, 0, ActualWidth, ActualHeight)); Rect bounds = new Rect( BorderThickness.Left, BorderThickness.Top, ActualWidth - (BorderThickness.Right + BorderThickness.Left), ActualHeight - (BorderThickness.Top + BorderThickness.Bottom)); DC.PushClip(new RectangleGeometry(bounds)); DC.DrawRectangle(Background, null, bounds); DrawTimeAxis(DC, bounds); if (Signals.Empty()) { DC.Pop(); return; } Signal stats = SelectedSignal; Signal stabilize = stats; if (stabilize == null) { stabilize = Signals.First(); } double sampleRate = Signals.SampleRate; double f0 = 0.0; // Remembe the clock for when we analyzed the signal to keep the signals in sync even if new data gets added in the background. long sync = stabilize.Clock; lock (stabilize.Lock) { int Decimate = 1 << (int)Math.Floor(Math.Log(sampleRate / 22000, 2)); int BlockSize = 8192; if (stabilize.Count >= BlockSize) { double[] data = stabilize.Skip(stabilize.Count - BlockSize).ToArray(); // Estimate the fundamental frequency of the signal. double phase; double f = Frequency.Estimate(data, Decimate, out phase); // Convert phase from (-pi, pi] to (0, 1] phase = ((phase + Math.PI) / (2 * Math.PI)); // Shift all the signals by the phase in samples to align the signal between frames. if (f > 1.0) { sync -= (int)Math.Round(phase * BlockSize / f); } // Compute fundamental frequency in Hz. f0 = sampleRate * f / BlockSize; } } double mean = 0.0; double peak = 0.0; double rms = 0.0; if (stats != null) { lock (stats.Lock) { // Compute statistics of the clock signal. mean = stats.Sum() / stats.Count; peak = stats.Max(i => Math.Abs(i - mean), 0.0); rms = Math.Sqrt(stats.Sum(i => (i - mean) * (i - mean)) / stats.Count); } } else { foreach (Signal i in signals) { lock (i.Lock) peak = Math.Max(peak, i.Max(j => Math.Abs(j), 0.0)); } } // Compute the target min/max double bound = peak; double gamma = 0.1; double window = Math.Max(Math.Pow(2.0, Math.Ceiling(Math.Log(bound + 1e-9, 2.0))), 5e-3); Vmax = Math.Max(TimeFilter(Vmax, window, gamma), Math.Abs(bound + (Vmean - mean))); Vmean = TimeFilter(Vmean, mean, gamma); if (Math.Abs(mean) * 1e2 < Vmax) { Vmean = 0.0; } DrawSignalAxis(DC, bounds); foreach (Signal i in signals.Except(stabilize).Append(stabilize)) { lock (i.Lock) DrawSignal(DC, bounds, i, (int)(sync - i.Clock)); } if (stats != null) { DrawStatistics(DC, bounds, stats.Pen.Brush, peak, mean, rms, f0); } if (tracePoint.HasValue) { DrawTrace(DC, bounds, tracePoint.Value); } DC.Pop(); }
/// <summary> /// Update the signal level of this channel. /// </summary> /// <param name="level"></param> /// <param name="time"></param> public void SampleSignalLevel(double level, double time) { double a = Frequency.DecayRate(time, 0.25); signalLevel = Math.Max(level, level * a + signalLevel * (1 - a)); }