diff --git a/SoundReceiver/Detector.cs b/SoundReceiver/Detector.cs index 03d25a1..0ad69d0 100644 --- a/SoundReceiver/Detector.cs +++ b/SoundReceiver/Detector.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Numerics; using System.Collections.Generic; using System.Threading.Tasks; @@ -32,56 +33,146 @@ public int CompareTo(BitInfo other) class Detector { + public const int sampleRate = 44100; + public double bitrate = 100; - public double sineFreq1 = 800; - public double sineFreq2 = 1200; + public double carrierFreq = 1000; + + double maxFreqDeviation = 0.005; + + int recvRrcBitCount = 4; + double rrcBeta = 0.8; + + + Diagram bitLevelsDiagram; + + + public Detector(Diagram bitLevelsDiagram) + { + this.bitLevelsDiagram = bitLevelsDiagram; + } + + + Complex[] Fourier(double[] signal, int size, int ofs, int stride) + { + var result = new Complex[size]; + double step = 2 * Math.PI / size; + + for (int k = 0; k < size; k++) + { + double si = 0.0; + double sq = 0.0; + double stepk = step * k; + for (int n = 0; n < size; n++) + { + double stepkn = stepk * n; + double sv = signal[stride * n + ofs]; + si += sv * Math.Cos(stepkn); + sq -= sv * Math.Sin(stepkn); + } + result[k] = new Complex(si, sq); + } + return result; + } - public const int m_SampleRate = 44100; + Complex[] DitFft2(double[] signal, int size, int ofs, int s) + { + var result = new Complex[size]; + if (size <= 4) + result = Fourier(signal, size, ofs, s); + else + { + var step = -2.0 * Math.PI * Complex.ImaginaryOne / size; + var p1 = DitFft2(signal, size / 2, ofs, s * 2); + var p2 = DitFft2(signal, size / 2, ofs + s, s * 2); + for (int k = 0; k < size / 2; k++) + { + var e = Complex.Exp(step * k); + result[k] = p1[k] + e * p2[k]; + result[k + size / 2] = p1[k] - e * p2[k]; + } + } + return result; + } - Diagram m_DG1; - Diagram m_DG2; + Complex[] DitFft2(double[] signal) + { + return DitFft2(signal, signal.Length, 0, 1); + } - public Detector(Diagram DG1, Diagram DG2) + double EstimateFrequency(double[] signal, double freq1, double freq2) { - m_DG1 = DG1; - m_DG2 = DG2; + double[] signalp2 = signal.Take( + 1 << (int)Math.Log(signal.Length, 2)).ToArray(); + + var n = signalp2.Length; + var signalp2c = DitFft2(PadLeft(n * 3, signalp2)).ToArray(); + + double[] signalp2a = new double[signalp2c.Length]; + for (int k = 0; k < signalp2a.Length; k++) + { + signalp2a[k] = signalp2c[k].Real * signalp2c[k].Real + + signalp2c[k].Imaginary * signalp2c[k].Imaginary; + } + + var maxPos = 0; + var maxVal = double.MinValue; + var x1 = (int)(freq1 / sampleRate * signalp2a.Length); + var x2 = (int)(freq2 / sampleRate * signalp2a.Length); + for (int x = x1; x < x2; x++) + { + if (signalp2a[x] > maxVal) + { + maxVal = signalp2a[x]; + maxPos = x; + } + } + + var vm1 = signalp2a[maxPos - 1]; + var vz = signalp2a[maxPos]; + var vp1 = signalp2a[maxPos + 1]; + + var u = (64 * n) / (Math.Pow(Math.PI, 5) + 32 * Math.PI); + var v = u * Math.Pow(Math.PI, 2) / 4; + var wa = (vp1 - vm1) / (u * (vp1 + vm1) + v * vz); + + return ((double)maxPos / n + wa) * sampleRate / 4; } - public byte[] Detect(short[] signal, ref string snr) + public byte[] Detect(short[] signal, out double snr) { if (signal.Length == 0) throw new SignalException("No data"); bool debug = false; - double sineFreqC = (sineFreq1 + sineFreq2) / 2; double[] D1 = SignalStoD(signal); - SaveWav(string.Format("RS_{0}_{1}_{2}_N.wav", - sineFreq1, sineFreq2, bitrate), SignalDtoS(Normalize(D1))); - if (debug) SaveWav("e:\\_Research\\Sound\\RawSignal_D1.wav", SignalDtoS(Normalize(D1))); - - int F1size = 127; - double[] F1 = ApplyGaussianWindow(MakeBandPassFilter( - sineFreq1 - bitrate * 0.5, - sineFreq2 + bitrate * 0.5, F1size)); + if (debug) SaveWav("d1.wav", SignalDtoS(Normalize(D1))); + + double bandwidth = (1 + rrcBeta) * bitrate; + double freq1 = (carrierFreq - bandwidth / 2.0) * (1.0 - maxFreqDeviation); + double freq2 = (carrierFreq + bandwidth / 2.0) * (1.0 + maxFreqDeviation); + int F1size = (int)(4.0 * sampleRate / (freq2 - freq1)) | 1; + double[] F1 = ApplyGaussianWindow(MakeBandPassFilter(freq1, freq2, F1size)); double[] D2 = Convolution(F1, D1); - if (debug) SaveWav("e:\\_Research\\Sound\\RawSignal_D2.wav", SignalDtoS(Normalize(D2))); + if (debug) SaveWav("d2.wav", SignalDtoS(Normalize(D2))); double[] D21 = Abs(D2); - //if (debug) SaveWav("e:\\_Research\\Sound\\RawSignal_D21.wav", SignalDtoS(Normalize(D21))); - int bitLen = (int)(m_SampleRate / bitrate); + int bitLen = (int)(sampleRate / bitrate); double[] D22 = Integrate(D21, bitLen); - if (debug) SaveWav("e:\\_Research\\Sound\\RawSignal_D22.wav", SignalDtoS(Normalize(D22))); + if (debug) SaveWav("d22.wav", SignalDtoS(Normalize(D22))); double[] D23 = Integrate(D21, bitLen * 8); - //if (debug) SaveWav("e:\\_Research\\Sound\\RawSignal_D23.wav", SignalDtoS(Normalize(D23))); + //if (debug) SaveWav("d23.wav", SignalDtoS(D23)); double[] D24 = new double[D22.Length]; double[] D25 = new double[D22.Length]; MinMax(D22, bitLen * 8, D24, D25); - //if (debug) SaveWav("e:\\_Research\\Sound\\RawSignal_D24.wav", SignalDtoS(Normalize(D24))); - //if (debug) SaveWav("e:\\_Research\\Sound\\RawSignal_D25.wav", SignalDtoS(Normalize(D25))); + + //if (debug) SaveWav("d24.wav", SignalDtoS(D24)); + //if (debug) SaveWav("d25.wav", SignalDtoS(D25)); + double noiseLevel = 0.0; int signalStart = 0; @@ -96,7 +187,7 @@ public byte[] Detect(short[] signal, ref string snr) } if ((signalStart != 0) && (signalEnd == 0) && - (D25[i] < 2.0 * noiseLevel)) + (D25[i] <= 2.0 * noiseLevel)) { signalEnd = i; break; @@ -112,8 +203,7 @@ public byte[] Detect(short[] signal, ref string snr) int signalStart2 = 0; int signalEnd2 = 0; - double signalAvgMin = D24[signalCenter]; - double signalAvgMax = D25[signalCenter]; + double signalAvgMin = D24[signalCenter - bitLen * 4]; for (int i = signalStart; i < signalEnd; i++) { if ((signalStart2 == 0) && @@ -133,155 +223,135 @@ public byte[] Detect(short[] signal, ref string snr) signalEnd2 = signalEnd; if (signalStart2 == 0) - throw new SignalException("Signal not found"); + throw new SignalException("Incorrect signal"); - signalStart2 += bitLen / 2; - signalEnd2 -= bitLen / 2; + double signalAvgLevel = D23[signalCenter]; + double signalStr = signalAvgLevel - noiseLevel; + snr = 99; + if (noiseLevel != 0.0) + snr = Math.Round(Math.Log10(signalStr / noiseLevel) * 20); - double signalStr = D23[(signalStart2 + signalEnd2) / 2] - noiseLevel; - snr = ((int)(Math.Log10(signalStr / noiseLevel) * 10)).ToString(); + signalStart2 = signalStart2 + F1size / 2 - bitLen / 2 + bitLen / 4; + signalEnd2 = signalEnd2 + F1size / 2 - bitLen / 2 - bitLen / 4; - if (debug) + double[] D2s = new double[signalEnd2 - signalStart2]; + for (int i = 0; i < D2s.Length; i++) { - D22 = Normalize(D22); - D22[signalStart] = -1.0; - D22[signalEnd] = -1.0; - D22[signalStart2] = 1.0; - D22[signalEnd2] = 1.0; - SaveWav("e:\\_Research\\Sound\\RawSignal_D22.wav", SignalDtoS((D22))); + double d2v = D2[i + signalStart2 - F1size / 2]; + D2s[i] = d2v * d2v; } - int F2size = 511; - int F3size = 511; - double[] F2 = ApplyGaussianWindow(MakeLowPassFilter( - Math.Abs(sineFreq2 - sineFreq1) / 2 + bitrate, F2size)); - double[] F3 = ApplyGaussianWindow(MakeLowPassFilter(bitrate, F3size)); + if (debug) SaveWav("d2s.wav", SignalDtoS(Normalize(D2s))); + + freq1 = (carrierFreq - carrierFreq * maxFreqDeviation) * 2; + freq2 = (carrierFreq + carrierFreq * maxFreqDeviation) * 2; + double estFreq = EstimateFrequency(D2s, freq1, freq2) / 2; + + // estFreq = freq * 0.99833; - signalStart2 += F1size / 2; - signalEnd2 += F1size / 2; + // freqDeviation = Math.Abs((estFreq - carrierFreq) / carrierFreq) * 1e6; - signalStart2 -= bitLen / 2; - signalEnd2 -= bitLen / 2; + double estBr = bitrate * estFreq / carrierFreq; + + double[] rrc = MakeRootRaisedCosine( + sampleRate / estBr, recvRrcBitCount, rrcBeta); + + double[] D35I = new double[signalEnd2 - signalStart2 + rrc.Length]; + double[] D35Q = new double[D35I.Length]; - double[] D35I = new double[signalEnd2 - signalStart2 + F3size + F2size]; - double[] D35Q = new double[signalEnd2 - signalStart2 + F3size + F2size]; for (int i = 0; i < D35I.Length; i++) { - D35I[i] = D1[signalStart2 - (F3size + F2size) / 2 + i] * Math.Cos(2 * Math.PI * sineFreqC * (i / (double)m_SampleRate)); - D35Q[i] = -D1[signalStart2 - (F3size + F2size) / 2 + i] * Math.Sin(2 * Math.PI * sineFreqC * (i / (double)m_SampleRate)); + double signalSample = D1[signalStart2 + i - rrc.Length / 2]; + double phase = 2 * Math.PI * estFreq * i / sampleRate; + D35I[i] = signalSample * Math.Cos(phase); + D35Q[i] = -signalSample * Math.Sin(phase); } - double[] D35If = Convolution(F2, D35I); - double[] D35Qf = Convolution(F2, D35Q); - double[] D36 = new double[D35If.Length]; - double[] D37 = new double[D35If.Length]; - for (int i = 0; i < D35If.Length; i++) + //if (debug) SaveWav("d35i.wav", SignalDtoS(Normalize(D35I))); + + + var D35If = Convolution(rrc, D35I); + var D35Qf = Convolution(rrc, D35Q); + + + var ph = GetPskPhase(D35If, D35Qf); + + double ca = Math.Cos(ph); + double sa = Math.Sin(ph); + for (int t = 0; t < D35If.Length; t++) { - D36[i] = Math.Atan2(D35Qf[i], D35If[i]); - if (i != 0) - { - double delta = D36[i] - D36[i - 1]; - if (delta > Math.PI) - delta -= Math.PI * 2; - if (delta < -Math.PI) - delta += Math.PI * 2; - D37[i] = delta / Math.PI; - } + var i2 = D35If[t] * ca - D35Qf[t] * sa; + var q2 = D35If[t] * sa + D35Qf[t] * ca; + D35If[t] = i2; + D35Qf[t] = q2; } - double[] D38 = Normalize2(Convolution(F3, D37)); - - signalStart2 -= (F3size + F2size) / 2; - signalEnd2 -= (F3size + F2size) / 2; - - double[] D5 = new double[D38.Length]; - Array.Copy(D38, D5, D5.Length); - - // Детектор фронтов - int pf1 = 0; - int pf2 = 0; - int pf3 = 0; - int pr1 = 0; - int pr2 = 0; - int pr3 = 0; - int ff = 0; - int fr = 0; - List fronts = new List(); - for (int i = 1; i < D5.Length; i++) - { - if ((D5[i - 1] > 0.3) && (D5[i] <= 0.3)) - pf1 = i; - if ((D5[i - 1] < 0.3) && (D5[i] >= 0.3)) - pr3 = i; - if ((D5[i - 1] > 0.0) && (D5[i] <= 0.0)) - pf2 = i; - if ((D5[i - 1] < 0.0) && (D5[i] >= 0.0)) - pr2 = i; - if ((D5[i - 1] > -0.3) && (D5[i] <= -0.3)) - pf3 = i; - if ((D5[i - 1] < -0.3) && (D5[i] >= -0.3)) - pr1 = i; - if ((pf1 != 0) && (pf2 > pf1) && (pf3 > pf2)) - { - ff = pf2; - D5[ff] = -1.0; - pf1 = 0; - pf2 = 0; - pf3 = 0; - } - if ((pr1 != 0) && (pr2 > pr1) && (pr3 > pr2)) - { - fr = pr2; - D5[fr] = -1.0; - pr1 = 0; - pr2 = 0; - pr3 = 0; - } + double maxAbsIf = MaxAbs(D35If); + double maxAbsQf = MaxAbs(D35Qf); + double maxAbsIfQf = Math.Max(maxAbsIf, maxAbsQf); + + if (debug) SaveWav("d35if.wav", SignalDtoS(Mul(D35If, 1.0 / maxAbsIfQf))); + if (debug) SaveWav("d35qf.wav", SignalDtoS(Mul(D35Qf, 1.0 / maxAbsIfQf))); - if ((ff != 0) && (fr > ff)) + var vecX = new List(); + var vecY = new List(); + for (int i = 1; i < D35If.Length; i++) + { + var s1 = D35If[i - 1]; + var s2 = D35If[i]; + if (s1 < 0 != s2 < 0) { - int favg = fr - (((fr - ff) + bitLen / 2) % bitLen - bitLen / 2) / 2; - fronts.Add(favg); - D5[favg] = 1.0; - fr = 0; - ff = 0; + var adj = s1 / (s1 - s2); + var angle = 2 * Math.PI * ((i - 1 + adj) * estBr / sampleRate % 1.0); + vecX.Add(Math.Cos(angle)); + vecY.Add(Math.Sin(angle)); } } - if (debug) - SaveWav("e:\\_Research\\Sound\\RawSignal_D5.wav", SignalDtoS(PadLeft(signalStart2, D5))); - if (fronts.Count == 0) - throw new SignalException("Incorrect signal"); + bool firstBit = true; + bool inversion = false; + var bits = new List(); + var bitLevels = new List(); + double[] D4 = new double[D35If.Length]; + double tf = (Math.Atan2(vecY.Sum(), vecX.Sum()) + Math.PI) / (2 * Math.PI) * sampleRate / estBr; + for (;;) + { + var ti = (int)Math.Round(tf); + if (ti >= D35If.Length) + break; + bool bit = D35If[ti] > 0.0; + if (firstBit) + { + inversion = bit; + firstBit = false; + } + bit ^= inversion; + bits.Add(bit); + double bitLevel = Math.Abs(D35If[ti]) / maxAbsIfQf; + D4[ti] = (bit ? 1.0 : -1.0) * bitLevel; + bitLevels.Add(bitLevel); - int startingBitCount = (fronts[0]) / bitLen; - int endingBitCount = (signalEnd2 - signalStart2 - fronts[fronts.Count - 1]) / bitLen; + tf += sampleRate / estBr; + } - if (startingBitCount < 1) - throw new SignalException("Incorrect signal"); - if (endingBitCount < 0) - throw new SignalException("Incorrect signal"); + if (debug) SaveWav("d4.wav", SignalDtoS(D4)); - List rawBits = new List(); - rawBits.AddRange(GetRawBits2(D38, - fronts[0] - bitLen * startingBitCount, startingBitCount, bitrate)); - for (int i = 1; i < fronts.Count; i++) - { - rawBits.AddRange(GetRawBits2(D38, - fronts[i - 1], Convert.ToInt32( - (fronts[i] - fronts[i - 1]) / (double)bitLen), bitrate)); - } - rawBits.AddRange(GetRawBits2(D38, - fronts[fronts.Count - 1], endingBitCount, bitrate)); + // Последние биты иногда содержат шум + bitLevels.RemoveRange(bitLevels.Count - 3, 3); + /* + bitLevelMin = (int)(bitLevels.Min() * 100.0); + bitLevelAvg = (int)(bitLevels.Average() * 100.0); + bitLevelMax = (int)(bitLevels.Max() * 100.0); + */ - rawBits = new List(Normalize(rawBits.ToArray())); + BitContainer bc = new BitContainer(); int stage = 0; - List fltRawBits = new List(); - for (int i = 0; i < rawBits.Count; i++) + for (int i = 0; i < bits.Count; i++) { - bool bit = rawBits[i].Value > 0; + bool bit = bits[i]; if (stage == 0) { if (bit) @@ -303,42 +373,72 @@ public byte[] Detect(short[] signal, ref string snr) } else if (stage == 3) { - fltRawBits.Add(rawBits[i]); + bc.Add(bit); } } - if (stage != 3) - throw new SignalException("Incorrect signal"); + bitLevelsDiagram.Fill(bitLevels.ToArray(), 0.001, 1.0, 40); - double[] bitsValue = new double[fltRawBits.Count]; - for (int i = 0; i < fltRawBits.Count; i++) - bitsValue[i] = fltRawBits[i].Value; + var data = bc.ExtractData(); + if (debug) File.WriteAllBytes("bytes", data); + return data; + } - m_DG1.Fill(bitsValue, -1.0, -0.001, 20); - m_DG2.Fill(bitsValue, 0.001, 1.0, 20); + double GetPskPhase(double[] i, double[] q) + { + double[] rotI = new double[i.Length]; + double[] rotQ = new double[i.Length]; - BitContainer bc = new BitContainer(); - for (int i = 0; i < fltRawBits.Count; i++) - bc.Add(fltRawBits[i].Value > 0); + double sumX = 0.0; + double sumY = 0.0; + for (int t = 0; t < i.Length; t++) + { + var a = Math.Atan2(q[t], i[t]) * 2.0; + var r = Math.Sqrt(i[t] * i[t] + q[t] * q[t]); + var px = r * Math.Cos(a); + var py = r * Math.Sin(a); + rotI[t] = px; + rotQ[t] = py; + sumX += px; + sumY += py; + } - return bc.ExtractData(); + return Math.PI - Math.Atan2(sumY, sumX) / 2.0; } - BitInfo[] GetRawBits2(double[] signal, int startOffset, int bitCount, double bitrate) - { - int bitLen = (int)(m_SampleRate / bitrate); - BitInfo[] rawBits = new BitInfo[bitCount]; - for (int i = 0; i < bitCount; i++) + double RootRaisedCosine(double t, double bitDuration, double beta) + { + double epsilon = 1e-12; + if (Math.Abs(t) < epsilon) { - double rawBit = 0.0; - int bitOffset = startOffset + i * bitLen; - for (int j = 0; j < bitLen / 2; j++) - rawBit += signal[bitOffset + j + bitLen / 4]; - rawBit /= bitLen / 2; - rawBits[i] = new BitInfo { Value = rawBit, Offset = bitOffset }; + return 1.0 + beta * (4.0 / Math.PI - 1.0); } - return rawBits; + else if (Math.Abs(Math.Abs(t) - bitDuration / (4.0 * beta)) > epsilon) + { + double f = t / bitDuration; + return ( + Math.Sin((Math.PI * f) * (1.0 - beta)) + + (4.0 * beta * f) * + Math.Cos((Math.PI * f) * (1.0 + beta))) / + ((Math.PI * f) * + (1.0 - Math.Pow(4.0 * beta * f, 2.0))); + } + else + { + return (beta / Math.Sqrt(2.0)) * + ((1.0 + 2.0 / Math.PI) * Math.Sin(Math.PI / (4.0 * beta)) + + (1.0 - 2.0 / Math.PI) * Math.Cos(Math.PI / (4.0 * beta))); + } + } + + double[] MakeRootRaisedCosine(double bitDuration, int bitCount, double beta) + { + int signalLen = (int)(bitCount * bitDuration) | 1; + double[] rootRaisedCosine = new double[signalLen]; + for (int i = 0; i < signalLen; i++) + rootRaisedCosine[i] = RootRaisedCosine(i - signalLen / 2, bitDuration, beta); + return rootRaisedCosine; } double[] PadLeft(int sampleCount, double[] signal) @@ -389,7 +489,7 @@ double[] MakeBandPassFilter(double Freq1, double Freq2, int SampleCount) double[] MakeLowPassFilter(double CutoffFreq, int SampleCount) { - double W = 2 * Math.PI * (CutoffFreq / m_SampleRate); + double W = 2 * Math.PI * (CutoffFreq / sampleRate); double A = W / Math.PI; int N = SampleCount / 2; @@ -583,6 +683,35 @@ double[] Convolution(double[] W1, double[] W2) } + double MaxAbs(double[] signal) + { + if (signal.Length == 0) + throw new Exception(); + + double min = signal[0]; + double max = signal[0]; + for (int i = 1; i < signal.Length; i++) + { + double val = signal[i]; + if (val < min) + min = val; + else if (val > max) + max = val; + } + + return Math.Max(Math.Abs(min), Math.Abs(max)); + } + + double[] Mul(double[] signal, double value) + { + double[] r = new double[signal.Length]; + + for (int i = 0; i < signal.Length; i++) + r[i] = signal[i] * value; + + return r; + } + double[] SignalStoD(short[] Signal) { diff --git a/SoundReceiver/MainWindow.xaml b/SoundReceiver/MainWindow.xaml index dd37a06..3dc5fed 100644 --- a/SoundReceiver/MainWindow.xaml +++ b/SoundReceiver/MainWindow.xaml @@ -1,7 +1,7 @@  + Title="Sound Receiver" Height="332" Width="587" MinWidth="470" MinHeight="332" WindowStartupLocation="CenterScreen"> @@ -9,7 +9,6 @@ - @@ -17,29 +16,26 @@ - - - + + - - - - - + + + + - + - - - - + + + - + - + @@ -47,10 +43,7 @@ - - - - + @@ -62,26 +55,9 @@ - - - - - - - - - - - - - - - - - - - - + + + diff --git a/SoundReceiver/MainWindow.xaml.cs b/SoundReceiver/MainWindow.xaml.cs index 085c323..0a537af 100644 --- a/SoundReceiver/MainWindow.xaml.cs +++ b/SoundReceiver/MainWindow.xaml.cs @@ -13,18 +13,16 @@ public partial class MainWindow : Window List m_Buf; WaveIn waveIn; - Detector m_D; + Detector detector; - Diagram m_DG1; - Diagram m_DG2; + Diagram bitLevelsDiagram; public MainWindow() { InitializeComponent(); - m_DG1 = new Diagram(Canvas1); - m_DG2 = new Diagram(Canvas2); - m_D = new Detector(m_DG1, m_DG2); + bitLevelsDiagram = new Diagram(Canvas1); + detector = new Detector(bitLevelsDiagram); waveIn = new WaveIn(); waveIn.WaveFormat = new WaveFormat(44100, 1); @@ -45,17 +43,13 @@ private void waveIn_DataAvailable(object sender, WaveInEventArgs e) void UpdateParamInfo() { - if (Freq1TextBlock == null) + if (FreqTextBlock == null) return; - m_D.sineFreq1 = Freq1Slider.Value; - Freq1TextBlock.Text = m_D.sineFreq1.ToString() + " Hz"; - m_D.sineFreq2 = Freq2Slider.Value; - Freq2TextBlock.Text = m_D.sineFreq2.ToString() + " Hz"; - m_D.bitrate = BitrateSlider.Value; - BitrateTextBlock.Text = m_D.bitrate.ToString() + " bps"; - - StartButton.IsEnabled = m_D.sineFreq1 != m_D.sineFreq2; + detector.carrierFreq = FreqSlider.Value; + FreqTextBlock.Text = detector.carrierFreq.ToString() + " Hz"; + detector.bitrate = BitrateSlider.Value; + BitrateTextBlock.Text = detector.bitrate.ToString() + " bps"; } private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) @@ -69,8 +63,7 @@ void StartButton_Click(object sender, RoutedEventArgs e) { waveIn.StartRecording(); - Freq1Slider.IsEnabled = false; - Freq2Slider.IsEnabled = false; + FreqSlider.IsEnabled = false; BitrateSlider.IsEnabled = false; StartButton.IsEnabled = false; StopButton.IsEnabled = true; @@ -91,16 +84,18 @@ void StopButton_Click(object sender, RoutedEventArgs e) void ProcessSignal() { - string snr = "—"; + string snrS = "—"; string resultStr = ""; try { + double snr; StringBuilder sb = new StringBuilder(); - byte[] data = m_D.Detect(m_Buf.ToArray(), ref snr); + byte[] data = detector.Detect(m_Buf.ToArray(), out snr); string unfiltered = Encoding.UTF8.GetString(data); foreach (char c in unfiltered) sb.Append(c < ' ' ? '�' : c); resultStr = sb.ToString(); + snrS = $"{snr,2} dB"; } catch (SignalException e) { @@ -111,11 +106,10 @@ void ProcessSignal() DispatcherPriority.Normal, new Action(() => { - SNRTextBlock.Text = snr; + SNRTextBlock.Text = snrS; MessageTextBox.Text = resultStr; - Freq1Slider.IsEnabled = true; - Freq2Slider.IsEnabled = true; + FreqSlider.IsEnabled = true; BitrateSlider.IsEnabled = true; StartButton.IsEnabled = true; })); diff --git a/SoundReceiver/SoundReceiver.csproj b/SoundReceiver/SoundReceiver.csproj index 9df2651..061e09a 100644 --- a/SoundReceiver/SoundReceiver.csproj +++ b/SoundReceiver/SoundReceiver.csproj @@ -40,6 +40,7 @@ + diff --git a/SoundTransmitter/MainWindow.xaml b/SoundTransmitter/MainWindow.xaml index dc88169..d4efded 100644 --- a/SoundTransmitter/MainWindow.xaml +++ b/SoundTransmitter/MainWindow.xaml @@ -1,10 +1,9 @@  + Title="Sound Transmitter" Height="136" Width="587" MinWidth="320" MinHeight="136" WindowStartupLocation="CenterScreen"> - @@ -16,20 +15,17 @@ - - - - - - - + + + + + - Hello, World! + Hello, World! - - - + + - + diff --git a/SoundTransmitter/MainWindow.xaml.cs b/SoundTransmitter/MainWindow.xaml.cs index 0cc2120..cf299a3 100644 --- a/SoundTransmitter/MainWindow.xaml.cs +++ b/SoundTransmitter/MainWindow.xaml.cs @@ -10,13 +10,15 @@ namespace SoundTransmitter { public partial class MainWindow : Window { - double m_Bitrate = 100; - double m_SineFreq1 = 800; - double m_SineFreq2 = 1200; + const int sampleRate = 44100; - const int m_SampleRate = 44100; + double bitrate = 100; + double carrierFreq = 1000; - string m_Message; + int sendRrcBitCount = 64; + double rrcBeta = 0.8; + + string message; public MainWindow() @@ -26,17 +28,15 @@ public MainWindow() void UpdateParamInfo() { - if (Freq1TextBlock == null) + if (FreqTextBlock == null) return; - m_SineFreq1 = Freq1Slider.Value; - Freq1TextBlock.Text = m_SineFreq1.ToString() + " Hz"; - m_SineFreq2 = Freq2Slider.Value; - Freq2TextBlock.Text = m_SineFreq2.ToString() + " Hz"; - m_Bitrate = BitrateSlider.Value; - BitrateTextBlock.Text = m_Bitrate.ToString() + " bps"; + carrierFreq = FreqSlider.Value; + FreqTextBlock.Text = carrierFreq.ToString() + " Hz"; + bitrate = BitrateSlider.Value; + BitrateTextBlock.Text = bitrate.ToString() + " bps"; - SendButton.IsEnabled = (m_SineFreq1 != m_SineFreq2) && (MessageTextBox.Text.Length != 0); + SendButton.IsEnabled = MessageTextBox.Text.Length != 0; } private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) @@ -47,55 +47,40 @@ private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs { SendButton.Content = (3 - i); })); - System.Threading.Thread.Sleep(500); + Thread.Sleep(500); } SendButton.Dispatcher.Invoke( DispatcherPriority.Normal, @@ -109,12 +94,9 @@ void MakeSignal() File.Delete(outName); - Freq1Slider.Dispatcher.Invoke( + FreqSlider.Dispatcher.Invoke( DispatcherPriority.Normal, - new Action(() => { Freq1Slider.IsEnabled = true; })); - Freq2Slider.Dispatcher.Invoke( - DispatcherPriority.Normal, - new Action(() => { Freq2Slider.IsEnabled = true; })); + new Action(() => { FreqSlider.IsEnabled = true; })); BitrateSlider.Dispatcher.Invoke( DispatcherPriority.Normal, new Action(() => { BitrateSlider.IsEnabled = true; })); @@ -132,147 +114,98 @@ void MakeSignal() double[] MakeSilence(double T) { - return new double[(int)(T * m_SampleRate)]; + return new double[(int)(T * sampleRate)]; } - byte[] Encode(byte[] Message) + double[] MakeFilteredDigitalSignal(double bitrate, byte[] message, + double rrcBeta, int rrcBitCount, int padSize, out double[] bitPositions) { - byte[] BitStream = new byte[Message.Length * 8]; - for (int i = 0; i < Message.Length; i++) + sbyte[] symbols = new sbyte[message.Length * 8]; + for (int i = 0; i < message.Length; i++) { for (int j = 0; j < 8; j++) { - bool Bit = false; - if ((Message[i] & (1 << (7 - j))) != 0) - Bit = true; - BitStream[i * 8 + j] = (byte)(Bit ? 1 : 0); + bool bit = false; + if ((message[i] & (1 << (7 - j))) != 0) + bit = true; + symbols[i * 8 + j] = (sbyte)(bit ? 1 : -1); } } - return new byte[] { 0, 0, 0, 0, 1, 0 }.Concat(BitStream).Concat(new byte[] { 0, 0, 0, 0 }).ToArray(); - } - double[] MakeGaussian(double Duration) - { - int SignalLen = (int)(Duration * m_SampleRate); - - double A = Math.PI * 2; - double[] Gaussian = new double[SignalLen]; - - for (int i = 0; i < SignalLen; i++) - { - double X = (2 * i / (double)SignalLen - 1); - Gaussian[i] = Math.Exp(-A * X * X); - } - return Gaussian; - } + var pad = Enumerable.Repeat(-1, padSize).ToArray(); + symbols = pad.Concat(new sbyte[] { 1, -1 }).Concat( + symbols).Concat(pad).ToArray(); - double[] MakeAmpSignal(double Bitrate, int SignalLen) - { - double BitLen = m_SampleRate / Bitrate; - double[] AmpSignal = new double[SignalLen]; - for (int i = 0; i < SignalLen; i++) - AmpSignal[i] = ((i > BitLen / 2) && (i < SignalLen - BitLen / 2)) ? 1 : -1; - return AmpSignal; - } + double bitDuration = sampleRate / bitrate; + int rrcLen = (int)(rrcBitCount * bitDuration); - double[] MakeDigitalSignal(double Bitrate, byte[] Bits) - { - int SignalLen = (int)((Bits.Length / Bitrate) * m_SampleRate); + int signalLen = (int)(symbols.Length * bitDuration); - double[] DigitalSignal = new double[SignalLen]; + bitPositions = new double[signalLen]; + double[] digitalSignal = new double[signalLen + rrcLen]; - for (int i = 0; i < SignalLen; i++) + for (int i = 0; i < symbols.Length; i++) { - int BitIndex = (int)(Bitrate * i / m_SampleRate); - DigitalSignal[i] = (Bits[BitIndex] == 1) ? 1 : -1; - } + double bitPosition = (i + 0.5) * bitDuration; + bitPositions[(int)bitPosition] = symbols[i]; - return DigitalSignal; - } + for (int j = 0; j < rrcLen; j++) + { + int bitPositionI = (int)Math.Floor(bitPosition); + double bitPositionF = bitPosition - bitPositionI; + digitalSignal[bitPositionI + j] += RootRaisedCosine( + j - rrcLen / 2 - bitPositionF, bitDuration, rrcBeta) * symbols[i]; + } + } - double[] PadSignal(double Bitrate, double BitCount, double[] Signal) - { - double[] Pad = new double[(int)(BitCount * m_SampleRate / Bitrate)]; - return Pad.Concat(Signal).Concat(Pad).ToArray(); + return digitalSignal; } - double[] Normalize(double[] Signal) + double RootRaisedCosine(double t, double bitDuration, double beta) { - double Min = double.MaxValue; - double Max = double.MinValue; - foreach (double D in Signal) + double epsilon = 1e-12; + if (Math.Abs(t) < epsilon) { - if (D < Min) - Min = D; - if (D > Max) - Max = D; + return 1.0 + beta * (4.0 / Math.PI - 1.0); } - - double MaxAbs = Math.Max(Math.Abs(Min), Math.Abs(Max)); - - double[] NormSignal = new double[Signal.Length]; - for (int i = 0; i < Signal.Length; i++) - NormSignal[i] = (Signal[i] / MaxAbs); - return NormSignal; - } - - double[] Convolution2(double[] W1, double[] W2) - { - int padLen = W1.Length / 2; - double[] pad1 = new double[padLen]; - double[] pad2 = new double[padLen]; - for (int i = 0; i < padLen; i++) - pad1[i] = W2[0]; - for (int i = 0; i < padLen; i++) - pad2[i] = W2[W2.Length - 1]; - return Convolution(W1, pad1.Concat(W2).Concat(pad2).ToArray()); - } - - double[] Convolution(double[] W1, double[] W2) - { - if (W2.Length <= W1.Length) - return new double[0]; - - double[] R = new double[W2.Length - W1.Length]; - - for (int i = 0; i < R.Length; i++) + else if (Math.Abs(Math.Abs(t) - bitDuration / (4.0 * beta)) > epsilon) + { + double f = t / bitDuration; + return ( + Math.Sin((Math.PI * f) * (1.0 - beta)) + + (4.0 * beta * f) * + Math.Cos((Math.PI * f) * (1.0 + beta))) / + ((Math.PI * f) * + (1.0 - Math.Pow(4.0 * beta * f, 2.0))); + } + else { - double Sum = 0; - for (int j = 0; j < W1.Length; j++) - Sum += W1[j] * W2[i + j]; - R[i] = Sum / W1.Length; + return (beta / Math.Sqrt(2.0)) * + ((1.0 + 2.0 / Math.PI) * Math.Sin(Math.PI / (4.0 * beta)) + + (1.0 - 2.0 / Math.PI) * Math.Cos(Math.PI / (4.0 * beta))); } - return R; } - double[] AModulation(double[] DataSignal, double[] AmpSignal) + double[] Mul(double[] signal, double value) { - double[] Signal = new double[Math.Min(DataSignal.Length, AmpSignal.Length)]; + double[] r = new double[signal.Length]; - for (int i = 0; i < Signal.Length; i++) - Signal[i] = DataSignal[i] * (AmpSignal[i] * 0.5 + 0.5); + for (int i = 0; i < signal.Length; i++) + r[i] = signal[i] * value; - return Signal; + return r; } - - double[] FModulation(double[] Signal, double Freq1, double Freq2) + + double[] AModulation(double[] signal, double freq) { - double CF = (Freq2 + Freq1) / 2; - double D = (Freq2 - Freq1) / 2; - double[] ModSignal = new double[Signal.Length]; - - double Phase = 0; - for (int i = 0; i < Signal.Length; i++) - { - Phase += 2 * Math.PI * (CF + D * Signal[i]) / (double)m_SampleRate; - ModSignal[i] = Math.Sin(Phase); - } - return ModSignal; + double[] modSignal = new double[signal.Length]; + for (int i = 0; i < signal.Length; i++) + modSignal[i] = signal[i] * Math.Cos(2 * Math.PI * (freq * i / sampleRate)); + return modSignal; } - short[] SignalDtoS(double[] Signal) { short[] SignalS = new short[Signal.Length]; @@ -281,26 +214,29 @@ short[] SignalDtoS(double[] Signal) return SignalS; } - void SaveWav(string Path, short[] Signal) - { - FileStream FS = File.Open(Path, FileMode.Create); - FS.Write(new byte[] { 0x52, 0x49, 0x46, 0x46 }, 0, 4); // "RIFF" - FS.Write(BitConverter.GetBytes((uint)(36 + Signal.Length * 2)), 0, 4); - FS.Write(new byte[] { 0x57, 0x41, 0x56, 0x45 }, 0, 4); // "WAVE" - FS.Write(new byte[] { 0x66, 0x6D, 0x74, 0x20 }, 0, 4); // "fmt" - FS.Write(BitConverter.GetBytes((uint)(16)), 0, 4); - FS.Write(BitConverter.GetBytes((ushort)(1)), 0, 2); - FS.Write(BitConverter.GetBytes((ushort)(1)), 0, 2); // mono - FS.Write(BitConverter.GetBytes((uint)(44100)), 0, 4); // Hz - FS.Write(BitConverter.GetBytes((uint)(44100 * 2)), 0, 4); - FS.Write(BitConverter.GetBytes((ushort)(2)), 0, 2); - FS.Write(BitConverter.GetBytes((ushort)(16)), 0, 2); // bps - FS.Write(new byte[] { 0x64, 0x61, 0x74, 0x61 }, 0, 4); // "data" - FS.Write(BitConverter.GetBytes((uint)(Signal.Length * 2)), 0, 4); - foreach (short V in Signal) - FS.Write(BitConverter.GetBytes(V), 0, 2); - - FS.Close(); + void SaveWav(string path, short[] signal, int channelCount, int sampleRate = sampleRate) + { + FileStream fs = File.Open(path, FileMode.Create); + fs.Write(new byte[] { 0x52, 0x49, 0x46, 0x46 }, 0, 4); // "RIFF" + fs.Write(BitConverter.GetBytes((uint)(36 + signal.Length * 2 * channelCount)), 0, 4); + fs.Write(new byte[] { 0x57, 0x41, 0x56, 0x45 }, 0, 4); // "WAVE" + fs.Write(new byte[] { 0x66, 0x6D, 0x74, 0x20 }, 0, 4); // "fmt" + fs.Write(BitConverter.GetBytes((uint)(16)), 0, 4); + fs.Write(BitConverter.GetBytes((ushort)(1)), 0, 2); + fs.Write(BitConverter.GetBytes((ushort)(channelCount)), 0, 2); + fs.Write(BitConverter.GetBytes((uint)(sampleRate)), 0, 4); // Hz + fs.Write(BitConverter.GetBytes((uint)(sampleRate * 2 * channelCount)), 0, 4); + fs.Write(BitConverter.GetBytes((ushort)(2 * channelCount)), 0, 2); + fs.Write(BitConverter.GetBytes((ushort)(16)), 0, 2); // bps + fs.Write(new byte[] { 0x64, 0x61, 0x74, 0x61 }, 0, 4); // "data" + fs.Write(BitConverter.GetBytes((uint)(signal.Length * 2 * channelCount)), 0, 4); + foreach (short v in signal) + { + fs.Write(BitConverter.GetBytes(v), 0, 2); + for (int i = 0; i < channelCount - 1; i++) + fs.Write(BitConverter.GetBytes((ushort)0), 0, 2); + } + fs.Close(); } } }