web-dev-qa-db-ja.com

オーディオへのデータとその逆。ソースコードによる変調/復調

バイナリデータのストリームがあり、それを生の波形サウンドデータに変換してスピーカーに送信したいと考えています。

これは、電話回線を介してバイナリデータを転送するために旧式のモデムが行ったことです(典型的なモデムのような音を生成します)。これは変調と呼ばれます。

次に、逆のプロセスが必要です。生の波形サンプルから、正確なバイナリデータを取得したいと思います。これは復調と呼ばれます。

  • どのビットレートでも最初から機能します。
  • サウンドはコンピューターのスピーカーを使用して再生され、マイクを使用してサンプリングされます。
  • 帯域幅はかなり低くなります(低品質のマイク)。
  • バックグラウンドノイズはありますが、それほど多くはありません。

私はこれを行うための1つの特定の方法を見つけました--- 周波数シフトキーイング 。問題は、ソースコードが見つからないことです。

FSKの任意の言語での実装を教えていただけますか?
または、利用可能なソースコードを使用した代替エンコーディングバイナリ<->サウンドを提供しますか?

22
Martin Konicek

最も単純な変調方式は 振幅変調 です(技術的にはデジタル領域では、これは振幅シフトキーイングと呼ばれます)。固定周波数(たとえば10Khz)、つまり「搬送波」を取得し、バイナリデータのビットを使用してオンとオフを切り替えます。データレートが10ビット/秒の場合、そのレートで10KHz信号のオンとオフを切り替えることになります。復調は(オプションの)10KHzフィルタであり、その後にしきい値と比較されます。これは、実装するのにかなり簡単なスキームです。一般に、信号周波数と使用可能な帯域幅が高いほど、その信号のオンとオフをすばやく切り替えることができます。

ここでの非常にクールで楽しいアプリケーションは、モールス信号としてエンコード/デコードし、どれだけ速く進むことができるかを確認することです。

FSK、2つの周波数間をシフトすると、帯域幅がより効率的になり、ノイズの影響を受けにくくなりますが、2つの周波数を区別する必要があるため、復調器がより複雑になります。

位相偏移変調 などの高度な変調方式は、特定の帯域幅と信号対雑音比で最高のビットレートを取得するのに適していますが、実装がより複雑です。アナログ電話モデムは、特定の帯域幅(たとえば、わずか3Khz)とノイズ制限に対処する必要がありました。帯域幅とノイズの制限を考慮して可能な限り高いビットレートを取得する必要がある場合は、それが最適な方法です。

高度な変調方式の実際のコードサンプルについては、DSPベンダーからのアプリケーションノート( [〜#〜] ti [〜#〜]Analog Devices など)を調査します。 DSPの一般的なアプリケーションでした。

TMS320C50を使用したPI/4シフトD-QPSKベースバンドモデムの実装

QPSK変調の謎を解き明かす

V.34 TMS320C50 DSPでの送信機と受信機の実装

もう1つの非常に単純で効率の悪い方法は、DTMFを使用することです。これらは、各記号が2つの周波数の組み合わせである電話のキーパッドによって生成されるトーンです。あなたがグーグルなら、あなたはたくさんのソースコードを見つけるでしょう。アプリケーション/要件によっては、これは簡単な解決策になる場合があります。

先に述べたモールス信号のような、いくつかの簡単なスキーム実装の詳細に飛び込みましょう。 0には「ドット」、1には「ダッシュ」を使用できます。モールスのようなスキームの利点は、スペースごとにサンプリングを再同期できるため、フレーミングの問題も解決できることです。簡単にするために、「搬送波」周波数を選択しましょう。 11KHzで、波の出力が44Khz、16ビット、モノラルであると仮定します。高調波を生成する方形波も使用しますが、あまり気にしません。11KHzがマイクの周波数応答を超えている場合は、すべての周波数を2で割ります。たとえば、任意のレベル10000を選択するため、「オン」の波形は次のようになります。

{10000, 10000, 0, 0, 10000, 10000, 0, 0, 10000, 0, 0, ...} // 4 samples = 11Khz period

「オフ」の波形はすべてゼロです。この部分のコーディングは、読者の演習として残しておきます。

そして、次のようなものがあります。

const int dot_samples = 400; // ~10ms - speed up later
const int space_samples = 400; // ~10ms
const int dash_samples = 800; // ~20ms

void encode( uint8_t* source, int length, int16_t* target ) // assumes enough room in target
{
  for(int i=0; i<length; i++)
  {
    for(int j=0; j<8; j++)
    {
      if((source[i]>>j) & 1) // If data bit is 1 we'll encode a dot
      {
        generate_on(&target, dash_samples); // Generate ON wave for n samples and update target ptr
      }
      else // otherwise a dash
      {
        generate_on(&target, dot_samples); // Generate ON wave for n samples and update target ptr
      }
      generate_off(&target, space_samples); // Generate zeros
    } 
  }
}

デコーダーはもう少し複雑ですが、概要は次のとおりです。

  1. オプションで、サンプリングされた信号を約11Khzでバンドパスフィルター処理します。これにより、騒がしい環境でのパフォーマンスが向上します。 FIRフィルターは非常にシンプルで、フィルターを生成するオンラインデザインアプレットがいくつかあります。
  2. 信号をしきい値設定します。最大振幅の1/2を超えるすべての値は1であり、以下のすべての値は0です。これは、信号全体をサンプリングしたことを前提としています。これがリアルタイムの場合は、固定のしきい値を選択するか、ある時間にわたって最大信号レベルを追跡する何らかの自動ゲイン制御を実行します。
  3. ドットまたはダッシュの開始をスキャンします。サンプルをドットと見なすために、ドット期間に少なくとも特定の数の1を表示することをお勧めします。次に、スキャンを続けて、これがダッシュかどうかを確認します。完全な信号を期待しないでください。1の真ん中にいくつかの0が表示され、0の真ん中にいくつかの1が表示されます。ノイズが少ない場合は、「オン」期間と「オフ」期間を区別するのはかなり簡単です。
  4. 次に、上記のプロセスを逆にします。ダッシュが表示されている場合はバッファに1ビットをプッシュし、ドットが表示されている場合はゼロをプッシュします。
20
Guy Sirton

変調/復調の目的の1つは、チャネル特性に適応することです。たとえば、チャネルがDCを通過できない場合があります。別の目的は、特定のエラーレートを超えるデータを転送しながら、チャネル内の特定の量とタイプのノイズを克服することです。

FSKの場合、送信側で2つの異なる周波数で正弦波を生成し、受信側で2つの異なる周波数をフィルタリングおよび検出できるルーチンが必要です。正弦波の各セグメントの長さ、周波数の分離、および振幅は、データレートと克服する必要のあるノイズの量によって異なります。

最も単純なケースであるゼロノイズでは、連続する固定時間フレーム内でNまたは2Nの正弦波を生成するだけです。何かのようなもの:

x[i] = amplitude * sin( i * 2 * pi * (data[j] ? 1.0 : 2.0) * freq) / sampleRate )

受信側では、最大周波数の2倍をはるかに超える信号をサンプリングし、ゼロ交差間の距離を測定して、短周期または長周期の波形が多数あるかどうかを確認できます。ゼロ以外のノイズが存在する場合は、デジタル信号処理フィルター(IIR、FIRなど)とさまざまな統計検出器を使用した、より高度な方法を使用できます。

2
hotpaw2