標準関数を使用せずにCで正弦信号を生成する 質問する

標準関数を使用せずにCで正弦信号を生成する 質問する

LED の明るさを正弦波状に変化させるために、標準関数 sin() を使用せずに C で正弦信号を生成したいと考えています。基本的なアイデアは、40 ポイントのルックアップ テーブルと補間を使用することです。

これが私の最初のアプローチです:

const int sine_table[40] = {0, 5125, 10125, 14876, 19260, 23170, 26509, 29196,
31163, 32364, 32767,  32364, 31163, 29196, 26509, 23170, 19260, 14876, 10125,
5125, 0, -5126, -10126,-14877, -19261, -23171, -26510, -29197, -31164, -32365,
-32768, -32365, -31164, -29197, -26510, -23171, -19261, -14877, -10126, -5126};

int i = 0;
int x1 = 0;
int x2 = 0;
float y = 0;

float sin1(float phase)
{
    x1 = (int) phase % 41;
    x2 = x1 + 1;
    y = (sine_table[x2] - sine_table[x1])*((float) ((int) (40*0.001*i*100) % 4100)/100 - x1) + sine_table[x1];
    return y;
}

int main()
{
    while(1)
    {
    printf("%f      ", sin1(40*0.001*i)/32768);
    i = i + 1;
    }
}

残念ながら、この関数は 1 よりはるかに大きい値を返すことがあります。さらに、補間は適切ではないようです (私はこれを使用して LED の正弦波状の明るさの変化を作成しましたが、これらは非常に不滑らかです)。

C で正弦波ジェネレータを実装するためのより良いアイデアを持っている人はいますか?

ベストアンサー1

OP の主な問題は、テーブル検索用のインデックスを生成することにあります。

OPのコードは配列の外部にアクセスしようsine_table[40]とし、未定義の動作少なくともそれを修正してください。

const int sine_table[40] = {0, 5125, 10125, ...
    ...
    x1 = (int) phase % 41;                     // -40 <= x1 <= 40
    x2 = x1 + 1;                               // -39 <= x2 <= 41  
    y = (sine_table[x2] - sine_table[x1])*...  // bad code, consider x1 = 40 or x2 = 40,41

提案された変更

    x1 = (int) phase % 40;   // mod 40, not 41
    if (x1 < 0) x1 += 40;    // Handle negative values
    x2 = (x1 + 1) % 40;      // Handle wrap-around 
    y = (sine_table[x2] - sine_table[x1])*...  

はるかに優れたアプローチは存在しますが、OP の方法に焦点を当てるには、以下を参照してください。

#include <math.h>
#include <stdio.h>

const int sine_table[40] = { 0, 5125, 10125, 14876, 19260, 23170, 26509, 29196,
31163, 32364, 32767, 32364, 31163, 29196, 26509, 23170, 19260, 14876, 10125,
5125, 0, -5126, -10126, -14877, -19261, -23171, -26510, -29197, -31164, -32365,
-32768, -32365, -31164, -29197, -26510, -23171, -19261, -14877, -10126, -5126 };

int i = 0;
int x1 = 0;
int x2 = 0;
float y = 0;

float sin1(float phase) {
  x1 = (int) phase % 40;
  if (x1 < 0) x1 += 40;
  x2 = (x1 + 1) % 40;
  y = (sine_table[x2] - sine_table[x1])
      * ((float) ((int) (40 * 0.001 * i * 100) % 4100) / 100 - x1)
      + sine_table[x1];
  return y;
}

int main(void) {
  double pi = 3.1415926535897932384626433832795;
  for (int j = 0; j < 1000; j++) {
    float x = 40 * 0.001 * i;
    float radians = x * 2 * pi / 40;
    printf("%f %f %f\n", x, sin1(x) / 32768, sin(radians));
    i = i + 1;
  }
}

出力

         OP's     Reference sin()
0.000000 0.000000 0.000000
0.040000 0.006256 0.006283
0.080000 0.012512 0.012566
...
1.960000 0.301361 0.303035
2.000000 0.308990 0.309017
2.040000 0.314790 0.314987
...
39.880001 -0.020336 -0.018848
39.919998 -0.014079 -0.012567
39.959999 -0.006257 -0.006283

より良いコードでは、値をi, x1, x2, yグローバル変数として渡すのではなく、関数パラメータまたは関数変数として渡します。おそらく、これは OP のデバッグによる結果です。


C で正弦波ジェネレータを実装するためのより良いアイデアを持っている人はいますか?

これはかなり広範囲です。速度、精度、コード スペース、移植性、保守性の点で優れているでしょうか。sine()関数は簡単に作成できます。高品質のものにはより多くの労力が必要です。

曖昧ではありますが、OPの小さなルックアップテーブルの使用は良い出発点です。浮動小数点演算なしでも実行できることはわかります。テスト済みの動作するソリューションを構築して、投稿することをお勧めします。コードレビュー改善のアイデアを得るために。

おすすめ記事