数据转为音频并返回带有源代码的调制/解调

问题描述

最简单的调制方案是振幅调制(在数字领域中,技术上称为振幅移位键控)。采取固定的频率(假设为10Khz),即“载波”,并使用二进制数据中的位将其打开和关闭。如果您的数据速率为每秒10位,则将以该速率打开和关闭10KHz信号。解调将是一个(可选)10KHz滤波器,然后与阈值进行比较。这是一个相当简单的实现方案。通常,信号频率和可用带宽越高,打开和关闭信号的速度就越快。

这里一个很酷/很有趣的应用程序是将代码编码/解码为莫尔斯电码,并查看您可以走多快。

FSK,在两个频率之间切换在带宽上更有效,并且更不受噪声影响,但是由于需要区分两个频率,解调器将变得更加复杂。

诸如相移键控之类的高级调制方案擅长在给定的带宽和信噪比下获得最高的比特率,但实现起来较为复杂。模拟电话调制解调器需要处理某些带宽(例如,低至3Khz)和噪声限制。如果您需要在给定的带宽和噪声限制下获得尽可能高的比特率,那么这就是要走的路。

对于高级调制方案的实际代码示例,我将研究DSP厂商(例如TIAnalog Devices)的应用笔记,因为它们是DSP的常见应用。

使用TMS320C50实现PI / 4移位D- QPSK基带调制解调器

QPSK调制解开神秘面纱

V.34发送器和接收器在TMS320C50 DSP上的实现

另一种非常简单但效率不高的方法是使用DTMF。这些是电话键盘产生的音调,其中每个符号是两个频率的组合。如果您使用Google,则会发现很多源代码。根据您的应用程序/要求,这可能是一个简单的解决方案。

让我们深入了解一些简单的方案实现细节,例如我之前提到的莫尔斯电码。我们可以将“ dot”用于0,将“ dash”用于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,小于1/2最大幅度的每个值都是0。这假设您已经采样了整个信号。如果这是实时的,则可以选择固定的阈值或执行某种自动增益控制,以在一段时间内跟踪最大信号电平。
  3. 扫描点或破折号的开始。您可能希望在点周期内看到至少一定数量的1,以将样本视为点。然后继续扫描以查看是否是破折号。不要指望一个完美的信号-您会在1的中间看到几个0,在0的中间看到几个1。如果噪声很小,则将“接通”周期与“断开”周期区分开应该很容易。
  4. 然后逆转以上过程。如果看到破折号,则将1推至缓冲区,如果破折号,则将其推至零。

解决方法

我有一个二进制数据流,想要将其转换为原始波形声音数据,然后可以将其发送给扬声器。

这就是老式调制解调器为了通过电话线传输二进制数据(产生典型的现代声音)所做的事情。这称为调制。

然后,我需要一个逆过程-从原始波形样本中,我想获得确切的二进制数据。这称为解调。

  • 任何比特率都可以开始使用。
  • 声音使用计算机扬声器播放,并使用麦克风采样。
  • 带宽会非常低(低质量的麦克风)。
  • 有一些背景噪音,但不多。

我找到了一种执行此操作的特殊方法- 频移键控。问题是我找不到任何源代码。

您能指出我用任何一种语言实现的FSK吗?
还是提供带有可用源代码的任何其他编码二进制<->声音?

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...