【C#】正弦波と矩形波を作成する

タイトルの通り以下の画像のような正弦波と矩形波を作成するための C# の実装を紹介したと思います。紹介する実装は、電気の実データに似せるために正弦波と矩形波はノイズ入りの波形を作成することができます。

正弦波

正弦波、ノイズありσ=0.5

矩形波

矩形波、ノイズありσ=0.1

確認環境

  • VisualStudio2022
  • .NET 9(C#13.0)
  • Windows11

データを生成後、CSV に出力し Excel でグラフ表示を行います。

実装例と使い方

先に使い方から紹介します。WaveUtility というクラスに正弦波を作成するための CreateSignWave メソッドと、矩形波を作成するための CreateSquareWave というメソッドがあります。

0~1の範囲にどの個数分データを作成するかが samples 変数の数値で、何回振幅させるかの周波数が freq 変数です。

この例ではサンプル数1000、周波数は5Hzです。

// Program.cs

internal class AppMain
{
    static void Main(string[] args)
    {
        Random rand = new();

        int samples = 1000; // サンプル数
        double freq = 5.0;  // 振幅回数

        // 正弦波の生成
        var testData = WaveUtility.CreateSineWave(samples, freq);
        // 矩形波の生成
        var testData2 = WaveUtility.CreateSquareWave(samples, freq);
        // CSVに出力
        string csvPath = (@"d:\data.csv"); // 結果の出力先(サンプル用の固定パス
        using StreamWriter sw = File.CreateText(csvPath);
        for (int i = 0; i < samples; i++)
        {
            sw.WriteLine($"{testData[i]},{testData2[i]}");
        }

        double sigma1 = 0.5; // ノイズの標準偏差
        double sigma2 = 0.1;

        // 正弦波の生成(ノイズあり)
        var testData3 = WaveUtility.CreateSineWaveWithNoise(samples, freq, sigma1, rand);
        // 矩形波の生成(ノイズあり)
        var testData4 = WaveUtility.CreateSquareWaveWithNoise(samples, freq, sigma2, rand);
        // CSVに出力
        string csvPath2 = (@"d:\data2.csv"); // 結果の出力先
        using StreamWriter sw2 = File.CreateText(csvPath2);
        for (int i = 0; i < samples; i++)
        {
            sw2.WriteLine($"{testData3[i]},{testData4[i]}");
        }
    }
}

WaveUtility の中身の実装は次の通りです。

// WaveUtility.cs

public class WaveUtility
{
    // 0~1の範囲にfreqHzのようなサイン波をサンプルの点数で作成する
    public static double[] CreateSineWave(int samples, double freq)
    {
        double dt = 1.0 / samples;
        double[] values = new double[samples];
        for (int i = 0; i < values.Length; i++)
        {
            values[i] = Math.Sin(2 * Math.PI * (dt * i) * freq);
        }
        return values;
    }

    // 0~1の範囲にfHzのようなサイン波にノイズを加えてサンプルの点数で作成する
    public static double[] CreateSineWaveWithNoise(int samples,
        double freq, double sigma, Random rand)
    {
        double[] items = CreateSineWave(samples, freq);
        AddNoise(rand, sigma, items); // 生成したデータにノイズを加えて返す
        return items;
    }

    // 0~1の範囲内にsamples個のデータをプロットしてfreq回の振幅を行う波形を作成する
    public static double[] CreateSquareWave(int samples, double freq)
    {
        double[] values = new double[samples];
        double dt = (1.0 / (samples - 1));
        for (int i = 0; i < values.Length; i++)
        {
            double time = i * dt;
            values[i] = Math.Sign(Math.Sin(2 * Math.PI * freq * time));
            // -1~1の範囲の振幅
        }
        return values;
    }

    // 0~1の範囲内にsamples個のデータをプロットしてfreq回の振幅を行う波形にノイズを加えて作成する
    public static double[] CreateSquareWaveWithNoise(int samples,
        double freq, double sigma, Random rand)
    {
        double[] values = CreateSquareWave(samples, freq);
        AddNoise(rand, sigma, values);
        return values;
    }

    // --- Private Methods ---

    // itemsにノイズを加える
    static void AddNoise(Random rand, double sigma, double[] items)
    {
        for (int i = 0; i < items.Length; i++)
        {
            items[i] += sigma * GetPolar(rand);
        }
    }

    // 正規乱数を取得する
    static double GetPolar(Random rand)
    {
        double u1 = 1.0 - rand.NextDouble();
        double u2 = 1.0 - rand.NextDouble();
        return Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Cos(2.0 * Math.PI * u2);
    }
}