lizhirui 发表于 2019-4-9 08:43

使用电脑声卡来实现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)表示,对于汉字而言,是三个字节表示一个字符),
下载链接:

lizhirui 发表于 2019-4-13 13:30

ayoma 发表于 2019-4-13 01:38
这个其实我们HAM玩的无线电 JT65 PSK JT9... 和现在流行的FT8软件wsjtx和jtdx是一样的原理。希望能有中文传 ...

支持啊 我也是HAM 23333

lizhirui 发表于 2019-4-9 12:15

zwo 发表于 2019-4-9 09:40
如果为了提高传速率,改到4FSK, 8FSK,是不是误码率会很大?

如果频率间隔比较大的话,并且你的滤波器够窄的话 问题也不大
其实这玩意如果不用PC改用嵌入式处理器运行裸机程序的话,传输速度上到几KB/s是一点压力没有
如果提高两个传输频率的话,传输上到几MB/s 几十MB/s的也没啥大问题

chensure 发表于 2019-4-9 09:16

之前听说过,黑一台无网络的计算器,就用这种方式?

goldli 发表于 2019-4-9 09:21

一运行就报错。win10

lizhirui 发表于 2019-4-9 09:23

goldli 发表于 2019-4-9 09:21
一运行就报错。win10

已经编译好的EXE需要在至少.net Framework 4.7.1的环境下启动(版本过低可能会报异常),并且可能需要在插入麦克风和扬声器之后才能成功启动,否则对于某些声卡可能会报异常

zwo 发表于 2019-4-9 09:40

如果为了提高传速率,改到4FSK, 8FSK,是不是误码率会很大?

chengan1998 发表于 2019-4-9 09:58

大佬我来了

苏紫方璇 发表于 2019-4-9 10:59

大佬好厉害,长知识了

lizhirui 发表于 2019-4-9 12:15

zwo 发表于 2019-4-9 09:40
如果为了提高传速率,改到4FSK, 8FSK,是不是误码率会很大?

目前2FSK在PC上现有速率下的测试显示
目前传输还没出过错,所以不知道误码率是多少

goldli 发表于 2019-4-9 14:18

lizhirui 发表于 2019-4-9 09:23
已经编译好的EXE需要在至少.net Framework 4.7.1的环境下启动(版本过低可能会报异常),并且可能需要在 ...

我这有4.7.2了。 没有麦克风
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 使用电脑声卡来实现2FSK数据调制解调传输