使用电脑声卡来实现2FSK数据调制解调传输
本帖最后由 lizhirui 于 2019-4-19 11:36 编辑此程序在物理层上使用10KHz的频率表示逻辑“0”,使用15KHz的频率表示逻辑“1”
在物理层之上(假设就叫传输层吧)用一个单位时间的0和一个单位时间的1表示逻辑“0”,用一个单位时间的0和两个单位时间的1表示逻辑“1”,并且每个字节的结尾都有一个偶校验位以及一个保持较长时间的0用于分隔字节
在传输层之上(假设就叫网络层吧),以包为单位传输数据,0x55表示包头,0xAA表示包尾,无校验
程序解调原理是:
首先利用10KHz和15KHz带通滤波器将声卡接收来的波形进行滤波,然后将两通道分别通过两个包络检波器,之后在门限噪声过滤后进行比较,谁幅度大判决结果就是谁
滤波器使用MATLAB生成的,值如下所示:
private const int BandPass_Size = 45;
private static double[] BandPass_10KHz = new double[]{
0.0009781904976227,2.310687083045e-05,-0.001850921386016,-0.0009604556387189,
0.002732177361363, 0.002454026683306,-0.003530807893481,-0.005033359378625,
0.003268338256639, 0.008046002294271,-0.001785884965235, -0.01118839681511,
-0.001425109899399,0.01337602169719, 0.005932600339844, -0.01398968593694,
-0.01124059666706,0.01232758211354, 0.016159778255,-0.008524876132051,
-0.01973020933926, 0.003023046445069,0.02098489087997, 0.003023046445069,
-0.01973020933926,-0.008524876132051, 0.016159778255,0.01232758211354,
-0.01124059666706, -0.01398968593694, 0.005932600339844,0.01337602169719,
-0.001425109899399, -0.01118839681511,-0.001785884965235, 0.008046002294271,
0.003268338256639,-0.005033359378625,-0.003530807893481, 0.002454026683306,
0.002732177361363,-0.0009604556387189,-0.001850921386016,2.310687083045e-05,
0.0009781904976227
};
private static double[] BandPass_15KHz = new double[]{
-0.0009981746791559, 0.000759180810415,0.0006404064214932,-0.002457709613472,
0.00238467384023,0.0007556946095888,-0.004687333695163, 0.004906581539168,
0.0005502824808439,-0.007567247883353,0.00864646206089,-0.0006606208202895,
-0.01018615121518,0.01288307335994,-0.002755953433256, -0.01205731207217,
0.01703290275592,-0.005710913930339, -0.01241906755974,0.01996890238488,
-0.008755521713043, -0.01126587989407,0.02109366603246, -0.01126587989407,
-0.008755521713043,0.01996890238488, -0.01241906755974,-0.005710913930339,
0.01703290275592, -0.01205731207217,-0.002755953433256,0.01288307335994,
-0.01018615121518,-0.0006606208202895,0.00864646206089,-0.007567247883353,
0.0005502824808439, 0.004906581539168,-0.004687333695163,0.0007556946095888,
0.00238467384023,-0.002457709613472,0.0006404064214932, 0.000759180810415,
-0.0009981746791559
};
滤波程序如下(原理是将信号与滤波器进行卷积,卷积使用了一个滚动缓冲区来解决):
private int[] Filter_10KHz(int[] Data)
{
double[] dData = new double;
int i,j,k;
for(i = 0;i < dData.Length;i++)
{
dData = 0;
k = BandPass_Size - 1;
for(j = i + 1;j < BandPass_Size;j++)
{
dData += BandPassBuf_10KHz * BandPass_10KHz;
}
for(j = Math.Max(0,i - BandPass_Size + 1);j <= i;j++)
{
dData += ((double)Data) * BandPass_10KHz;
}
if(k != -1)
{
throw new Exception();
}
}
for(i = Data.Length - 1,j = BandPass_Size - 1;j >= 0;i--,j--)
{
BandPassBuf_10KHz = (double)Data;
}
int[] r = new int;
for(i = 0;i < Data.Length;i++)
{
r = (int)dData;
}
return r;
}
private int[] Filter_15KHz(int[] Data)
{
double[] dData = new double;
int i,j,k;
for(i = 0;i < dData.Length;i++)
{
dData = 0;
k = BandPass_Size - 1;
for(j = i + 1;j < BandPass_Size;j++)
{
dData += BandPassBuf_15KHz * BandPass_15KHz;
}
for(j = Math.Max(0,i - BandPass_Size + 1);j <= i;j++)
{
dData += ((double)Data) * BandPass_15KHz;
}
if(k != -1)
{
throw new Exception();
}
}
for(i = Data.Length - 1,j = BandPass_Size - 1;j >= 0;i--,j--)
{
BandPassBuf_15KHz = (double)Data;
}
int[] r = new int;
for(i = 0;i < Data.Length;i++)
{
r = (int)dData;
}
return r;
}
包络检波器代码如下(同样采用了滚动缓冲区):
private double env_3(double x,double rct)
{
if(rct == 0.0)
{
old_y = 0.0;
}
else
{
x = Math.Abs(x);
if(x > old_y)
{
old_y = x;
}
else
{
old_y *= rct / (rct + 1);
}
}
return old_y;
}
private void env_4(double[] x,out double[] y,int N,double rct)
{
double xx = 0.0;
int i;
old_y = 0.0;
y = new double;
y = Math.Abs(x);
for(i = 1; i < N; i++)
{
xx = Math.Abs(x);
if(xx > y)
{
y = xx;
}
else
{
y = y * rct / (rct + 1);
}
}
}
private int[] Env_10KHz(int[] Data)
{
double[] dData = null;
double[] dr;
int i,j,k;
if(Env10KHzInit)
{
dData = new double;
for(i = 0;i < Data.Length;i++)
{
dData = Data;
}
Env10KHzInit = false;
}
else
{
dData = new double;
for(i = 0;i < Data.Length;i++)
{
dData = Env10KHzBuf;
}
for(i = 0;i < Data.Length;i++)
{
dData = Data;
}
}
env_4(dData,out dr,dData.Length,rc);
int[] r = new int;
if(Data.Length == dr.Length)
{
for(i = 0;i < dr.Length;i++)
{
r = (int)dr;
}
}
else
{
for(i = Data.Length;i < dr.Length;i++)
{
r = (int)dr;
}
}
Env10KHzBuf = Data.ToArray();
return r;
}
private int[] Env_15KHz(int[] Data)
{
double[] dData = null;
double[] dr;
int i,j,k;
if(Env15KHzInit)
{
dData = new double;
for(i = 0;i < Data.Length;i++)
{
dData = Data;
}
Env15KHzInit = false;
}
else
{
dData = new double;
for(i = 0;i < Data.Length;i++)
{
dData = Env15KHzBuf;
}
for(i = 0;i < Data.Length;i++)
{
dData = Data;
}
}
env_4(dData,out dr,dData.Length,rc);
int[] r = new int;
if(Data.Length == dr.Length)
{
for(i = 0;i < dr.Length;i++)
{
r = (int)dr;
}
}
else
{
for(i = Data.Length;i < dr.Length;i++)
{
r = (int)dr;
}
}
Env15KHzBuf = Data.ToArray();
return r;
}
传输层数据接收程序:
private void DataArrived_Audio(int[] audioData)
{
var r10 = Filter_10KHz(audioData);
var re10 = Env_10KHz(r10);
var r15 = Filter_15KHz(audioData);
var re15 = Env_15KHz(r15);
var v = 0;
for(int i = 0;i < re10.Length;i++)
{
statusChart1.Value = v = (((re10 < CurTValue) && (re15 < CurTValue)) ? 0 : (re10 > re15 ? 10000 : 30000));
statusChart2.Value = re10;
statusChart3.Value = re15;
if(v == 30000)
{
if(LastOne)
{
OneCnt++;
}
else
{
LastOne = true;
OneCnt = 1;
}
}
else if(v == 0)
{
//无效
ZeroCnt = 0;
OneCnt = 0;
LastOne = false;
CurBit = 0;
CurValue = 0;
}
else
{
if(LastOne)
{
//一个bit接收完成
if((ZeroCnt >= LowValue) && (OneCnt >= LowValue))
{
if(OneCnt > CenterValue)
{
//判定为1
CurValue |= (1 << CurBit);
CurBit++;
}
else
{
//判定为0
CurBit++;
}
if(CurBit >= 9)
{
var check = 0;
for(int j = 0;j < 8;j++)
{
check ^= (CurValue >> j) & 0x01;
}
if(check == (CurValue >> 8))
{
RevQ.Enqueue((byte)(CurValue & 0xFF));
}
ZeroCnt = 0;
CurBit = 0;
CurValue = 0;
}
}
LastOne = false;
ZeroCnt = 1;
}
else
{
ZeroCnt++;
}
}
}
}
传输层数据发送函数:
private void DataSendThread()
{
while(true)
{
var check = 0;
var list = new List<byte>();
if(SendQ.Count > 0)
{
var x = SendQ.Dequeue();
for(int i = 0;i < 8;i++)
{
if((x & (1 << i)) == 0)
{
check ^= 0;
/*m_Fifo.Write(Signal10KHz,0,Signal10KHz.Length / 2);
m_Fifo.Write(Signal15KHz,0,Signal15KHz.Length / 2);*/
AddToList(list,Signal10KHz,Signal10KHz.Length / ZeroDiv);
AddToList(list,Signal15KHz,Signal15KHz.Length / ZeroDiv);
}
else
{
check ^= 1;
/*m_Fifo.Write(Signal10KHz,0,Signal10KHz.Length / 2);
m_Fifo.Write(Signal15KHz,0,Signal15KHz.Length);*/
AddToList(list,Signal10KHz,Signal10KHz.Length / ZeroDiv);
AddToList(list,Signal15KHz,Signal15KHz.Length / OneDiv);
}
}
if(check == 0)
{
AddToList(list,Signal10KHz,Signal10KHz.Length / ZeroDiv);
AddToList(list,Signal15KHz,Signal15KHz.Length / ZeroDiv);
}
else
{
AddToList(list,Signal10KHz,Signal10KHz.Length / ZeroDiv);
AddToList(list,Signal15KHz,Signal15KHz.Length / OneDiv);
}
AddToList(list,Signal10KHz,Signal10KHz.Length / 2);
}
m_Fifo.Write(list.ToArray(),0,list.Count);
}
}
该代码抗干扰能力很强,大声说话甚至喊叫都不会影响到声音传输,如果你那里一直接收不到数据,可能是你的扬声器发不出10KHz和15KHz的声音或者声音太小,这时可以采用音频线直接连接声卡的输入端和输出端
该程序在Windows 7 x64旗舰版下测试通过,由于受限于Windows的多任务调度机制,程序的实时性很难得到保证,因此传输速度只能达到4bit/s即0.5Byte/s或者说2s/Byte
发送速率可以由“speed”变量控制,该变量位于Form1类成员中
已经编译好的EXE需要在至少.net Framework 4.7.1的环境下启动(版本过低可能会报异常),并且可能需要在插入麦克风和扬声器之后才能成功启动,否则对于某些声卡可能会报异常
代码基于VS2017开发
窗口左下角的发送值只能填0-255之间的一个数,否则会报异常,那里是单字节的发送测试
右上角那个文本框可以输入任意文本,点击发送之后等待接收即可,那里是以包为单位发送的,因此若发送x个字符,实际发送的是(f(x)+2)*9个bit(包头一个字节,包尾一个字节,UTF-8编码表示编码的长度不定,所以x字符实际对应的字符数用f(x)表示,对于汉字而言,是三个字节表示一个字符),
下载链接:
ayoma 发表于 2019-4-13 01:38
这个其实我们HAM玩的无线电 JT65 PSK JT9... 和现在流行的FT8软件wsjtx和jtdx是一样的原理。希望能有中文传 ...
支持啊 我也是HAM 23333 zwo 发表于 2019-4-9 09:40
如果为了提高传速率,改到4FSK, 8FSK,是不是误码率会很大?
如果频率间隔比较大的话,并且你的滤波器够窄的话 问题也不大
其实这玩意如果不用PC改用嵌入式处理器运行裸机程序的话,传输速度上到几KB/s是一点压力没有
如果提高两个传输频率的话,传输上到几MB/s 几十MB/s的也没啥大问题 之前听说过,黑一台无网络的计算器,就用这种方式? 一运行就报错。win10 goldli 发表于 2019-4-9 09:21
一运行就报错。win10
已经编译好的EXE需要在至少.net Framework 4.7.1的环境下启动(版本过低可能会报异常),并且可能需要在插入麦克风和扬声器之后才能成功启动,否则对于某些声卡可能会报异常 如果为了提高传速率,改到4FSK, 8FSK,是不是误码率会很大? 大佬我来了 大佬好厉害,长知识了 zwo 发表于 2019-4-9 09:40
如果为了提高传速率,改到4FSK, 8FSK,是不是误码率会很大?
目前2FSK在PC上现有速率下的测试显示
目前传输还没出过错,所以不知道误码率是多少 lizhirui 发表于 2019-4-9 09:23
已经编译好的EXE需要在至少.net Framework 4.7.1的环境下启动(版本过低可能会报异常),并且可能需要在 ...
我这有4.7.2了。 没有麦克风