From 945dccfa0657b0ce9b9de0f88058368e34aae01a Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Wed, 13 Aug 2014 13:59:30 +0200 Subject: [PATCH] Generate: square, triangle and sawtooth waves --- src/Numerics/Generate.cs | 117 +++++++++++++++++++++++++++++---- src/UnitTests/GenerateTests.cs | 32 +++++++-- 2 files changed, 131 insertions(+), 18 deletions(-) diff --git a/src/Numerics/Generate.cs b/src/Numerics/Generate.cs index 4a70e8ab..005eccef 100644 --- a/src/Numerics/Generate.cs +++ b/src/Numerics/Generate.cs @@ -4,7 +4,7 @@ // http://github.com/mathnet/mathnet-numerics // http://mathnetnumerics.codeplex.com // -// Copyright (c) 2009-2013 Math.NET +// Copyright (c) 2009-2014 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -259,12 +259,12 @@ namespace MathNet.Numerics } /// - /// Create a periodic sample vector. + /// Create a periodic wave. /// /// The number of samples to generate. /// Samples per time unit (Hz). Must be larger than twice the frequency to satisfy the Nyquist criterion. /// Frequency in periods per time unit (Hz). - /// The lenght of the period when sampled at one sample per time unit. This is the interval of the periodic domain, a typical value is 1.0, or 2*Pi for angular functions. + /// The length of the period when sampled at one sample per time unit. This is the interval of the periodic domain, a typical value is 1.0, or 2*Pi for angular functions. /// Optional phase offset. /// Optional delay, relative to the phase. public static double[] Periodic(int length, double samplingRate, double frequency, double amplitude = 1.0, double phase = 0.0, int delay = 0) @@ -289,13 +289,13 @@ namespace MathNet.Numerics } /// - /// Create a periodic sample vector. + /// Create a periodic wave. /// /// The number of samples to generate. /// The function to apply to each of the values and evaluate the resulting sample. /// Samples per time unit (Hz). Must be larger than twice the frequency to satisfy the Nyquist criterion. /// Frequency in periods per time unit (Hz). - /// The lenght of the period when sampled at one sample per time unit. This is the interval of the periodic domain, a typical value is 1.0, or 2*Pi for angular functions. + /// The length of the period when sampled at one sample per time unit. This is the interval of the periodic domain, a typical value is 1.0, or 2*Pi for angular functions. /// Optional phase offset. /// Optional delay, relative to the phase. public static T[] PeriodicMap(int length, Func map, double samplingRate, double frequency, double amplitude = 1.0, double phase = 0.0, int delay = 0) @@ -320,11 +320,11 @@ namespace MathNet.Numerics } /// - /// Create an infinite periodic sample sequence. + /// Create an infinite periodic wave sequence. /// /// Samples per time unit (Hz). Must be larger than twice the frequency to satisfy the Nyquist criterion. /// Frequency in periods per time unit (Hz). - /// The lenght of the period when sampled at one sample per time unit. This is the interval of the periodic domain, a typical value is 1.0, or 2*Pi for angular functions. + /// The length of the period when sampled at one sample per time unit. This is the interval of the periodic domain, a typical value is 1.0, or 2*Pi for angular functions. /// Optional phase offset. /// Optional delay, relative to the phase. public static IEnumerable PeriodicSequence(double samplingRate, double frequency, double amplitude = 1.0, double phase = 0.0, int delay = 0) @@ -348,12 +348,12 @@ namespace MathNet.Numerics } /// - /// Create an infinite periodic sample sequence. + /// Create an infinite periodic wave sequence. /// /// The function to apply to each of the values and evaluate the resulting sample. /// Samples per time unit (Hz). Must be larger than twice the frequency to satisfy the Nyquist criterion. /// Frequency in periods per time unit (Hz). - /// The lenght of the period when sampled at one sample per time unit. This is the interval of the periodic domain, a typical value is 1.0, or 2*Pi for angular functions. + /// The length of the period when sampled at one sample per time unit. This is the interval of the periodic domain, a typical value is 1.0, or 2*Pi for angular functions. /// Optional phase offset. /// Optional delay, relative to the phase. public static IEnumerable PeriodicMapSequence(Func map, double samplingRate, double frequency, double amplitude = 1.0, double phase = 0.0, int delay = 0) @@ -377,13 +377,13 @@ namespace MathNet.Numerics } /// - /// Create a Sine sample vector. + /// Create a Sine wave. /// /// The number of samples to generate. /// Samples per time unit (Hz). Must be larger than twice the frequency to satisfy the Nyquist criterion. /// Frequency in periods per time unit (Hz). /// The maximal reached peak. - /// The mean, or dc part, of the signal. + /// The mean, or DC part, of the signal. /// Optional phase offset. /// Optional delay, relative to the phase. public static double[] Sinusoidal(int length, double samplingRate, double frequency, double amplitude, double mean = 0.0, double phase = 0.0, int delay = 0) @@ -400,12 +400,12 @@ namespace MathNet.Numerics } /// - /// Create an infinite Sine sample sequence. + /// Create an infinite Sine wave sequence. /// /// Samples per unit. /// Frequency in samples per unit. /// The maximal reached peak. - /// The mean, or dc part, of the signal. + /// The mean, or DC part, of the signal. /// Optional phase offset. /// Optional delay, relative to the phase. public static IEnumerable SinusoidalSequence(double samplingRate, double frequency, double amplitude, double mean = 0.0, double phase = 0.0, int delay = 0) @@ -423,6 +423,97 @@ namespace MathNet.Numerics } } + /// + /// Create a periodic square wave, starting with the high phase. + /// + /// The number of samples to generate. + /// Number of samples of the high phase. + /// Number of samples of the low phase. + /// Sample value to be emitted during the low phase. + /// Sample value to be emitted during the high phase. + /// Optional delay. + public static double[] Square(int length, int highDuration, int lowDuration, double lowValue, double highValue, int delay = 0) + { + var duration = highDuration + lowDuration; + return PeriodicMap(length, x => x < highDuration ? highValue : lowValue, 1.0, 1.0/duration, duration, 0.0, delay); + } + + /// + /// Create an infinite periodic square wave sequence, starting with the high phase. + /// + /// Number of samples of the high phase. + /// Number of samples of the low phase. + /// Sample value to be emitted during the low phase. + /// Sample value to be emitted during the high phase. + /// Optional delay. + public static IEnumerable SquareSequence(int highDuration, int lowDuration, double lowValue, double highValue, int delay = 0) + { + var duration = highDuration + lowDuration; + return PeriodicMapSequence(x => x < highDuration ? highValue : lowValue, 1.0, 1.0/duration, duration, 0.0, delay); + } + + /// + /// Create a periodic triangle wave, starting with the raise phase from the lowest sample. + /// + /// The number of samples to generate. + /// Number of samples of the raise phase. + /// Number of samples of the fall phase. + /// Lowest sample value. + /// Highest sample value. + /// Optional delay. + public static double[] Triangle(int length, int raiseDuration, int fallDuration, double lowValue, double highValue, int delay = 0) + { + var duration = raiseDuration + fallDuration; + var height = highValue - lowValue; + var raise = height / raiseDuration; + var fall = height / fallDuration; + return PeriodicMap(length, x => x < raiseDuration ? lowValue + x*raise : highValue - (x-raiseDuration)*fall, 1.0, 1.0/duration, duration, 0.0, delay); + } + + /// + /// Create an infinite periodic triangle wave sequence, starting with the raise phase from the lowest sample. + /// + /// Number of samples of the raise phase. + /// Number of samples of the fall phase. + /// Lowest sample value. + /// Highest sample value. + /// Optional delay. + public static IEnumerable TriangleSequence(int raiseDuration, int fallDuration, double lowValue, double highValue, int delay = 0) + { + var duration = raiseDuration + fallDuration; + var height = highValue - lowValue; + var raise = height / raiseDuration; + var fall = height / fallDuration; + return PeriodicMapSequence(x => x < raiseDuration ? lowValue + x*raise : highValue - (x-raiseDuration)*fall, 1.0, 1.0/duration, duration, 0.0, delay); + } + + /// + /// Create a periodic sawtooth wave, starting with the lowest sample. + /// + /// The number of samples to generate. + /// Number of samples a full sawtooth period. + /// Lowest sample value. + /// Highest sample value. + /// Optional delay. + public static double[] Sawtooth(int length, int period, double lowValue, double highValue, int delay = 0) + { + var height = highValue - lowValue; + return PeriodicMap(length, x => x + lowValue, 1.0, 1.0/period, height*period/(period-1), 0.0, delay); + } + + /// + /// Create an infinite periodic sawtooth wave sequence, starting with the lowest sample. + /// + /// Number of samples a full sawtooth period. + /// Lowest sample value. + /// Highest sample value. + /// Optional delay. + public static IEnumerable SawtoothSequence(int period, double lowValue, double highValue, int delay = 0) + { + var height = highValue - lowValue; + return PeriodicMapSequence(x => x + lowValue, 1.0, 1.0/period, height*period/(period-1), 0.0, delay); + } + /// /// Create an array with each field set to the same value. /// diff --git a/src/UnitTests/GenerateTests.cs b/src/UnitTests/GenerateTests.cs index 8eb13ae0..522a43ef 100644 --- a/src/UnitTests/GenerateTests.cs +++ b/src/UnitTests/GenerateTests.cs @@ -3,9 +3,9 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // http://mathnetnumerics.codeplex.com -// -// Copyright (c) 2009-2013 Math.NET -// +// +// Copyright (c) 2009-2014 Math.NET +// // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without @@ -14,10 +14,10 @@ // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -148,6 +148,28 @@ namespace MathNet.Numerics.UnitTests Assert.That(Generate.Periodic(8, 2.0, 0.25, Constants.Pi2).Select(Math.Sin), Is.EqualTo(new[] { 0.0, isq2, 1.0, isq2, 0.0, -isq2, -1.0, -isq2 }).Within(1e-12).AsCollection); } + [Test] + public void StandardWaves() + { + Assert.That(Generate.Square(12, 3, 7, -1.0, 1.0, delay: 1), Is.EqualTo(new[] { -1.0, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1 }).Within(1e-12).AsCollection); + Assert.That(Generate.Triangle(12, 4, 7, -1.0, 1.0, delay: 1), Is.EqualTo(new[] { -0.714, -1, -0.5, 0, 0.5, 1, 0.714, 0.429, 0.143, -0.143, -0.429, -0.714 }).Within(1e-3).AsCollection); + Assert.That(Generate.Sawtooth(12, 5, -1.0, 1.0, delay: 1), Is.EqualTo(new[] { 1.0, -1, -0.5, 0, 0.5, 1, -1, -0.5, 0, 0.5, 1, -1 }).Within(1e-12).AsCollection); + } + + [Test] + public void StandardWavesConsistentWithSequence() + { + Assert.That( + Generate.SquareSequence(3, 7, -1.0, 1.0, delay: -2).Take(1000).ToArray(), + Is.EqualTo(Generate.Square(1000, 3, 7, -1.0, 1.0, delay: -2)).Within(1e-12).AsCollection); + Assert.That( + Generate.TriangleSequence(4, 7, -1.0, 1.0, delay: -2).Take(1000).ToArray(), + Is.EqualTo(Generate.Triangle(1000, 4, 7, -1.0, 1.0, delay: -2)).Within(1e-12).AsCollection); + Assert.That( + Generate.SawtoothSequence(5, -1.0, 1.0, delay: -2).Take(1000).ToArray(), + Is.EqualTo(Generate.Sawtooth(1000, 5, -1.0, 1.0, delay: -2)).Within(1e-12).AsCollection); + } + [Test] public void PeriodicConsistentWithSinusoidal() {