├── .gitattributes
├── MELPeModem.sln
├── Properties
└── AssemblyInfo.cs
├── Noise.cs
├── Constellation.cs
├── Program.cs
├── MELPeModem.csproj
├── sos2csharp.m
├── Samples.cs
├── MILSTD_188.cs
├── OFDMSync.cs
├── Generator.cs
├── BitDataTypes.cs
├── FFT.cs
├── SoftInterleaver.cs
├── ReedSolomon.cs
├── DataLink.cs
├── ToneDetector.cs
├── DataTypes.cs
├── EncodeDecode.cs
└── LFSRegister.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto !eol
2 | /BitDataTypes.cs -text
3 | /Constellation.cs -text
4 | /Correlator.cs -text
5 | /DataLink.cs -text
6 | /DataTypes.cs -text
7 | /EncodeDecode.cs -text
8 | /FFT.cs -text
9 | /Generator.cs -text
10 | /LFSRegister.cs -text
11 | /MELPeModem.csproj -text
12 | /MELPeModem.sln -text
13 | /MILSTD188_110B.cs -text
14 | /MILSTD188_110B_39.cs -text
15 | /MILSTD_188.cs -text
16 | /ModDemod.cs -text
17 | /Noise.cs -text
18 | /OFDMFFT.cs -text
19 | /OFDMSync.cs -text
20 | /Program.cs -text
21 | Properties/AssemblyInfo.cs -text
22 | /ReedSolomon.cs -text
23 | /Samples.cs -text
24 | /Shaper.cs -text
25 | /SoftInterleaver.cs -text
26 | /Sync.cs -text
27 | /Test.cs -text
28 | /ToneDetector.cs -text
29 | /VitDecoder.cs -text
30 | /sos2csharp.m -text
31 |
--------------------------------------------------------------------------------
/MELPeModem.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 10.00
3 | # Visual Studio 2008
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MELPeModem", "MELPeModem.csproj", "{C0B1B2BA-EC81-41E7-AEBB-7198D57C9AEF}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Any CPU = Debug|Any CPU
9 | Release|Any CPU = Release|Any CPU
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {C0B1B2BA-EC81-41E7-AEBB-7198D57C9AEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
13 | {C0B1B2BA-EC81-41E7-AEBB-7198D57C9AEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
14 | {C0B1B2BA-EC81-41E7-AEBB-7198D57C9AEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
15 | {C0B1B2BA-EC81-41E7-AEBB-7198D57C9AEF}.Release|Any CPU.Build.0 = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("MELPeModem")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Sollecon, Inc.")]
12 | [assembly: AssemblyProduct("MELPeModem")]
13 | [assembly: AssemblyCopyright("Copyright © Sollecon, Inc. 2008")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("f25cec23-bbef-4317-b9c8-1c1bac3bb043")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | [assembly: AssemblyVersion("1.0.0.0")]
33 | [assembly: AssemblyFileVersion("1.0.0.0")]
34 |
--------------------------------------------------------------------------------
/Noise.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace MELPeModem
6 | {
7 | class Noise
8 | {
9 | // We use Crypto library noise generation
10 | System.Security.Cryptography.RNGCryptoServiceProvider NoiseGen;
11 | float Amplitude;
12 | byte[] RandomNoiseBytes;
13 | float[] Result;
14 | int BuffSize;
15 |
16 | public Noise(int size, float Amp)
17 | {
18 | BuffSize = size;
19 | Amplitude = Amp;
20 | NoiseGen = new System.Security.Cryptography.RNGCryptoServiceProvider();
21 | RandomNoiseBytes = new byte[size];
22 | Result = new float[size];
23 | }
24 | public float[] Add(float[] I, float[] Q)
25 | {
26 | NoiseGen.GetBytes(RandomNoiseBytes);
27 | for (int i = 0; i < BuffSize; i++)
28 | {
29 | Result[i] = I[i] + Q[i] + Amplitude * Samples.ToFloat(RandomNoiseBytes[i]);
30 | }
31 | return Result;
32 | }
33 | public void Add(float[] result, float[] I, float[] Q)
34 | {
35 | NoiseGen.GetBytes(RandomNoiseBytes);
36 | for (int i = 0; i < BuffSize; i++)
37 | {
38 | result[i] += I[i] + Q[i] + Amplitude * Samples.ToFloat(RandomNoiseBytes[i]);
39 | }
40 | }
41 |
42 | public void Add(float[] result)
43 | {
44 | NoiseGen.GetBytes(RandomNoiseBytes);
45 | for (int i = 0; i < BuffSize; i++)
46 | {
47 | result[i] += Amplitude * Samples.ToFloat(RandomNoiseBytes[i]);
48 | }
49 | }
50 |
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Constellation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace MELPeModem
6 | {
7 | class Constellation
8 | {
9 | public static int[] Bits_Simple_BPSK = { 0, 1 };
10 | public static IQ[] IQ_Simple_BPSK = new IQ[] {IQ.UNITY, -IQ.UNITY};
11 |
12 | public const float SQR2_2 = 0.70710678f;
13 | public static int[] BitsToPhase_BPSK = { 1, 0 };
14 | public static float[] ITable_BPSK = { SQR2_2, -SQR2_2 };
15 | public static float[] QTable_BPSK = { SQR2_2, -SQR2_2 };
16 | public static IQ[] IQTable_BPSK = new IQ[2];
17 |
18 | public static int[] BitsToPhase_QPSK = { 0, 1, 3, 2 };
19 | public static float[] ITable_QPSK = { 1.0f, 0.0f, -1.0f, 0.0f };
20 | public static float[] QTable_QPSK = { 0.0f, 1.0f, 0.0f, -1.0f };
21 | public static IQ[] IQTable_QPSK = new IQ[4];
22 |
23 | public static float[] ITable_QPSK45 = { SQR2_2, -SQR2_2, -SQR2_2, SQR2_2 };
24 | public static float[] QTable_QPSK45 = { SQR2_2, SQR2_2, -SQR2_2, -SQR2_2 };
25 | public static IQ[] IQTable_QPSK45 = new IQ[4];
26 |
27 |
28 | public static int[] BitsToPhase_8PSK = { 1, 0, 2, 3, 6, 7, 5, 4 };
29 | public static float[] ITable_8PSK = { 1.0f, SQR2_2, 0.0f, -SQR2_2, -1.0f, -SQR2_2, 0.0f, SQR2_2 };
30 | public static float[] QTable_8PSK = { 0.0f, SQR2_2, 1.0f, SQR2_2, 0.0f, -SQR2_2, -1.0f, -SQR2_2 };
31 | public static IQ[] IQTable_8PSK = new IQ[8];
32 |
33 | public static float[] ITable_16QAM = { 0.866025f, 0.5f, 1.0f, 0.258819f, -0.5f, 0.0f, -0.866025f, -0.258819f, 0.5f, 0.0f, 0.866025f, 0.258819f, -0.866025f, -0.5f, -1.0f, -0.258819f };
34 | public static float[] QTable_16QAM = { 0.5f, 0.866025f, 0.0f, 0.258819f, 0.866025f, 1.0f, 0.5f, 0.258819f, -0.866025f, -1.0f, -0.5f, -0.258819f, -0.5f, -0.866025f, 0.0f, -0.258819f };
35 | public static IQ[] IQTable_16QAM = new IQ[16];
36 |
37 | public static float[] ITable_32QAM = {
38 | 0.866380f, 0.984849f, 0.499386f, 0.173415f, 0.520246f, 0.520246f, 0.173415f, 0.173415f,
39 | -0.866380f, -0.984849f, -0.499386f, -0.173415f, -0.520246f, -0.520246f, -0.173415f, -0.173415f,
40 | 0.866380f, 0.984849f, 0.499386f, 0.173415f, 0.520246f, 0.520246f, 0.173415f, 0.173415f,
41 | -0.866380f, -0.984849f, -0.499386f, -0.173415f, -0.520246f, -0.520246f, -0.173415f, -0.173415f };
42 | public static float[] QTable_32QAM = {
43 | 0.499386f, 0.173415f, 0.866380f, 0.984849f, 0.520246f, 0.173415f, 0.520246f, 0.173415f,
44 | 0.499386f, 0.173415f, 0.866380f, 0.984849f, 0.520246f, 0.173415f, 0.520246f, 0.173415f,
45 | -0.499386f, -0.173415f, -0.866380f, -0.984849f, -0.520246f, -0.173415f, -0.520246f, -0.173415f,
46 | -0.499386f, -0.173415f, -0.866380f, -0.984849f,-0.520246f, -0.173415f, -0.520246f, -0.173415f };
47 | public static IQ[] IQTable_32QAM = new IQ[32];
48 |
49 | public static int[] Table_1_to_1 = new int[64];
50 |
51 | public static int[] BitsToPhase_39 = { 1, 2, 0, 3 };
52 |
53 |
54 | static Constellation()
55 | {
56 | for (int i = 0; i < 64; i++)
57 | Table_1_to_1[i] = i;
58 | for (int i = 0; i < IQTable_BPSK.Length; i++)
59 | IQTable_BPSK[i] = new IQ(ITable_BPSK[i], QTable_BPSK[i]);
60 | for (int i = 0; i < IQTable_QPSK.Length; i++)
61 | IQTable_QPSK[i] = new IQ(ITable_QPSK[i], QTable_QPSK[i]);
62 | for (int i = 0; i < IQTable_QPSK45.Length; i++)
63 | IQTable_QPSK45[i] = new IQ(ITable_QPSK45[i], QTable_QPSK45[i]);
64 | for (int i = 0; i < IQTable_8PSK.Length; i++)
65 | IQTable_8PSK[i] = new IQ(ITable_8PSK[i], QTable_8PSK[i]);
66 | for (int i = 0; i < IQTable_16QAM.Length; i++)
67 | IQTable_16QAM[i] = new IQ(ITable_16QAM[i], QTable_16QAM[i]);
68 | for (int i = 0; i < IQTable_32QAM.Length; i++)
69 | IQTable_32QAM[i] = new IQ(ITable_32QAM[i], QTable_32QAM[i]);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 |
6 | namespace MELPeModem
7 | {
8 | class Program
9 | {
10 | static float PROCESSINGFREQ = 24000;
11 | static float OUTSAMPLEFREQ = 8000;
12 | static float INSAMPLEFREQ = 8000;
13 | static float SYMBOLRATE = 2400;
14 |
15 | static int InputBlockSize = (int)(PROCESSINGFREQ / SYMBOLRATE);
16 | static int OutputBlockSize = (int)(PROCESSINGFREQ / OUTSAMPLEFREQ);
17 |
18 | static string testmsg = "**** Hello World from Sollecon, Inc.!!!! **** ------ ";
19 |
20 | static void Main(string[] args)
21 | {
22 | Test t = new Test();
23 | t.Run();
24 |
25 | // Symbol encoder and modulator
26 | MILSTD188_110B mmodem = new MILSTD188_110B(MILSTD_188.Mode.D_4800N , INSAMPLEFREQ, PROCESSINGFREQ, OUTSAMPLEFREQ,
27 | Filters.Fill(Filters.rrc_180, 1), Filters.Fill(Filters.interp_24000, 1), Filters.Fill(Filters.decim_24000, 1));
28 |
29 | StringBuilder test = new StringBuilder();
30 | for (int i = 0; i < 377; i++)
31 | {
32 | test.Append(testmsg);
33 | }
34 |
35 | // Pack all data as 8-bit entities
36 | SerialData TXSymbols = new SerialData(7, 1, 2, SerialData.Parity.N);
37 | foreach (char CharToEncode in test.ToString())
38 | {
39 | int ByteToEncode = ((int)Convert.ToByte(CharToEncode)) & 0x00FF;
40 | TXSymbols.PutSymbol(ByteToEncode);
41 | }
42 | byte[] TxData = new byte[TXSymbols.BitsCount];
43 | int TxBits = TXSymbols.GetData(TxData);
44 |
45 |
46 | string outputFile = @"C:\test.raw";
47 | string inputFile = @"C:\test.raw";
48 |
49 | // Send datastream thru the modem - the output of the modem will be arrray of int datasymbols
50 | Samples outputSamples = new Samples(outputFile, 0.1f);
51 | mmodem.Tx.Start();
52 | mmodem.Tx.Process(TxData, 0, TxBits);
53 | mmodem.Tx.Finish();
54 |
55 | float[] SamplesOut = new float[mmodem.Tx.Count];
56 | mmodem.Tx.GetData(SamplesOut, 0);
57 | outputSamples.ToByte(SamplesOut);
58 | outputSamples.Close();
59 |
60 |
61 | Samples InputSamples = new Samples(inputFile);
62 | float[] SamplesIn = new float[100];
63 | int n;
64 | do
65 | {
66 | n = InputSamples.ToFloat(SamplesIn);
67 | mmodem.Rx.Process(SamplesIn, 0, n);
68 | } while (n > 0);
69 |
70 | byte[] RxData = new byte[mmodem.Rx.Count];
71 | int RxBits = mmodem.Rx.GetData(RxData);
72 |
73 | SerialData RxSymbols = new SerialData(7, 1, 2, SerialData.Parity.N);
74 | RxSymbols.PutData(RxData, 0, RxBits);
75 |
76 | if (inputFile.CompareTo(outputFile) == 0)
77 | {
78 | int errors = 0;
79 |
80 | if (RxBits != TxBits)
81 | {
82 | errors = 9999;
83 | }
84 |
85 | for (int idx = 0; idx < TxData.Length; idx++)
86 | {
87 | byte sent = TxData[idx];
88 | byte recd = RxData[idx];
89 | if (sent != recd)
90 | {
91 | errors++;
92 | }
93 | }
94 |
95 | for(int idx = 0; idx < TXSymbols.SymbolsCount; idx++)
96 | {
97 | int sent = TXSymbols[idx];
98 | int recd = RxSymbols[idx];
99 | if (sent != recd)
100 | {
101 | errors++;
102 | }
103 | }
104 | }
105 |
106 | InputSamples.Close();
107 |
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/MELPeModem.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.21022
7 | 2.0
8 | {C0B1B2BA-EC81-41E7-AEBB-7198D57C9AEF}
9 | Exe
10 | Properties
11 | MELPeModem
12 | MELPeModem
13 | Svn
14 | Svn
15 | Svn
16 | SubversionScc
17 |
18 |
19 |
20 |
21 | false
22 | publish\
23 | true
24 | Disk
25 | false
26 | Foreground
27 | 7
28 | Days
29 | false
30 | false
31 | true
32 | 0
33 | 1.0.0.%2a
34 | false
35 | true
36 | 2.0
37 | v3.5
38 |
39 |
40 | true
41 | full
42 | false
43 | bin\Debug\
44 | DEBUG;TRACE
45 | prompt
46 | 4
47 | MinimumRecommendedRules.ruleset
48 | false
49 | false
50 |
51 |
52 | pdbonly
53 | true
54 | bin\Release\
55 | TRACE
56 | prompt
57 | 4
58 | AllRules.ruleset
59 |
60 |
61 |
62 |
63 | 3.5
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
109 |
--------------------------------------------------------------------------------
/sos2csharp.m:
--------------------------------------------------------------------------------
1 | function sos2csharp(H, filename)
2 | % sos2csharp(H, filename) saves C# code to make filter H in filename
3 | % sos2csharp(H) displays C# code to make filter H
4 | % sos2csharp displays C# code to make an example fourth order chebyshev filter
5 | %
6 | % converts a sos direct form II filter to c# code.
7 | % Example:
8 | % H = design(fdesign.highpass('fst,fp,ast,ap',250, 300, 20, 3, 1000),'cheby1','MatchExactly', 'passband');
9 | % sos2csharp(H,'filter.cs')
10 |
11 | if nargin == 0
12 | H = design(fdesign.highpass('fst,fp,ast,ap',250, 300, 20, 3, 1000),'cheby1','MatchExactly', 'passband');
13 | end
14 | if nargin == 1
15 | filename = 'filter.cs';
16 | end
17 |
18 | %%%%%%%%%%%%%%%%%%%% declare variables %%%%%%%%%%%%%%%%%%%%%%%%%
19 | nStages = length(H.ScaleValues)-1;
20 |
21 | % declare and set constants
22 | str = sprintf('namespace PutNamespaceHere\n{\n public class IIRFilter\n {\n #region Fields\n');
23 | str = sprintf('%s double t, xi;\n',str);
24 | for iStage=1:nStages
25 | str = sprintf('%s double IC%g0 = 0, IC%g1 = 0;\n',str,iStage-1,iStage-1);
26 | end
27 | str = sprintf('%s #endregion\n',str);
28 |
29 | %%%%%%%%%%%%%%%%%%%% filter method %%%%%%%%%%%%%%%%%%%%%%%%%
30 | str = sprintf('%s\n #region Methods\n', str);
31 | str = sprintf('%s // requires output array y be already created by the calling function\n public void Filter(double[] x, double[] y)\n {\n',str);
32 | str = sprintf('%s int N = x.Length;\n',str);
33 | str = sprintf('%s for (int i=0; i 32767) Data = -32768 + (Data & 0x7FFF); // Extend the sign
40 | return ((float)Data) / 32768.0F;
41 | }
42 |
43 | public static float ToSingle(byte Data)
44 | {
45 | return (Convert.ToSingle(Data) - 127.0F) / 256.0F;
46 | }
47 |
48 | public static int ToFloat(byte[] inputBuff, float[] resultBuff, int numBytes)
49 | {
50 | for (int i = 0; i < numBytes / 2; i++)
51 | {
52 | byte LoByte = inputBuff[2 * i]; // Get LSB from the buffer
53 | byte HiByte = inputBuff[2 * i + 1]; // Get MSB from the buffer
54 | int Sample = ((HiByte & 0x00FF) << 8) + LoByte; // Create single 16-bit sample
55 | if (Sample > 32767) Sample = -32768 + (Sample & 0x7FFF); // Extend the sign
56 | resultBuff[i] = ((float)Sample) / 32768.0F;
57 | }
58 | return numBytes / 2;
59 | }
60 |
61 | public static int ToFloat(byte[] inputBuff, int inputIndex, float[] resultBuff, int resultIndex, int numBytes)
62 | {
63 | int SamplesToConvert = numBytes / 2;
64 | for (int i = 0; i < SamplesToConvert; i++)
65 | {
66 | byte LoByte = inputBuff[inputIndex]; // Get LSB from the buffer
67 | byte HiByte = inputBuff[inputIndex + 1]; // Get MSB from the buffer
68 | int Sample = ((HiByte & 0x00FF) << 8) + LoByte; // Create single 16-bit sample
69 | if (Sample > 32767) Sample = -32768 + (Sample & 0x7FFF); // Extend the sign
70 | resultBuff[resultIndex] = ((float)Sample) / 32768.0F;
71 | inputIndex += 2;
72 | resultIndex++;
73 | }
74 | return numBytes / 2;
75 | }
76 |
77 | public int ToFloat(float[] resultBuffer)
78 | {
79 | return ToFloat(resultBuffer, 0, resultBuffer.Length);
80 | }
81 |
82 | public int ToFloat(float[] resultBuffer, int startingIndex, int numSamples)
83 | {
84 | int SamplesRead = 0;
85 | int SamplesConverted = 0;
86 | int NumBytesToRead = numSamples * 2;
87 |
88 | do
89 | {
90 | int BytesToRead = Math.Min(NumBytesToRead, BYTES_BUFFSIZE);
91 | int n = File.Read(ByteBuff, 0, BytesToRead);
92 | SamplesConverted = ToFloat(ByteBuff, 0, resultBuffer, startingIndex, n);
93 | startingIndex += SamplesConverted;
94 | SamplesRead += SamplesConverted;
95 | NumBytesToRead -= n;
96 | } while ((NumBytesToRead > 0) && (SamplesConverted > 0));
97 | return SamplesRead;
98 | }
99 |
100 | public int ToByte(float[] inputBuff)
101 | {
102 | int BytesWritten = 0;
103 | int NumSamples = inputBuff.Length;
104 | int SampleIdx = 0;
105 | while (NumSamples > 0)
106 | {
107 | int nConv = Math.Min(NumSamples, SAMPLES_BUFFSIZE);
108 | int n = this.ToByte(inputBuff, SampleIdx, ByteBuff, 0, nConv);
109 | File.Write(ByteBuff, 0, n);
110 | BytesWritten += n;
111 | SampleIdx += nConv;
112 | NumSamples -= nConv;
113 | }
114 | return BytesWritten;
115 | }
116 |
117 | public int ToByte(float[] inputBuff, int numSamples)
118 | {
119 | int BytesWritten = 0;
120 | int SampleIdx = 0;
121 | // Output prepared samples into the file
122 | while (numSamples > 0)
123 | {
124 | int nConv = Math.Min(numSamples, SAMPLES_BUFFSIZE);
125 | int n = this.ToByte(inputBuff, SampleIdx, ByteBuff, 0, nConv);
126 | File.Write(ByteBuff, 0, n);
127 | BytesWritten += n;
128 | SampleIdx += nConv;
129 | numSamples -= nConv;
130 | }
131 | return BytesWritten;
132 | }
133 |
134 | public int ToByte(float[] inputBuff, int inputIndex, byte[] resultBuff, int outputIndex, int numSamples)
135 | {
136 | // Output prepared samples into the file
137 | for (int i = 0; i < numSamples; i++)
138 | {
139 | float Samp = inputBuff[inputIndex++] * this.ScalingFactor;
140 | int Sample = (int)Math.Floor((Samp * 32767.0 + 0.5));
141 | resultBuff[outputIndex] = (byte)(Sample & 0x0000FF);
142 | resultBuff[outputIndex + 1] = (byte)((Sample >> 8) & 0x00FF);
143 | outputIndex += 2;
144 | }
145 | return numSamples * 2;
146 | }
147 |
148 | public void Close()
149 | {
150 | if (this.FileName != null)
151 | {
152 | File.Close();
153 | FileName = null;
154 | }
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/MILSTD_188.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace MELPeModem
6 | {
7 | delegate int RxStateFunc(float[] incomingSamples, int startingIndex, int numSamples);
8 |
9 | class ModeInfo
10 | {
11 | public MILSTD_188.Mode Mode;
12 | public int D1, D2;
13 | public int PreambleSize;
14 | public int InterleaverRows;
15 | public int InterleaverColumns;
16 | public int UnknownDataSymbols;
17 | public int ProbeDataSymbols;
18 | public int BlockLength;
19 | public int RepeatDataBits;
20 | public int BitsPerSymbol;
21 | public int[] BitsToSymbolTable;
22 |
23 | public ModeInfo(MILSTD_188.Mode mod, int d1, int d2, int pSize, int row, int col, int unk, int prob, int block, int rpt, int bps, int[] table)
24 | {
25 | Mode = mod;
26 | D1 = d1; D2 = d2;
27 | PreambleSize = pSize;
28 | InterleaverRows = row;
29 | InterleaverColumns = col;
30 | UnknownDataSymbols = unk;
31 | ProbeDataSymbols = prob;
32 | BlockLength = block;
33 | RepeatDataBits = rpt;
34 | BitsPerSymbol = bps;
35 | BitsToSymbolTable = table;
36 | }
37 |
38 | public void GetModeInfo(out MILSTD_188.Mode mod, out int d1, out int d2, out int pSize, out int row, out int col, out int unk, out int prob, out int block, out int rpt, out int bps, out int[] table)
39 | {
40 | mod = Mode;
41 | d1 = D1; d2 = D2;
42 | pSize = PreambleSize;
43 | row = InterleaverRows;
44 | col = InterleaverColumns;
45 | unk = UnknownDataSymbols;
46 | prob = ProbeDataSymbols;
47 | block = BlockLength;
48 | rpt = RepeatDataBits;
49 | bps = BitsPerSymbol;
50 | table = BitsToSymbolTable;
51 | }
52 | }
53 |
54 | class Modes
55 | {
56 | List ModeArray = new List();
57 |
58 | public Modes(ModeInfo[] data) { foreach (ModeInfo m in data) ModeArray.Add(m); }
59 |
60 | public void Add(ModeInfo data) { ModeArray.Add(data); }
61 | public void Add(MILSTD_188.Mode mod, int d1, int d2, int pSize, int row, int col, int unk, int prob, int block, int rpt, int bps, int[] table)
62 | {
63 | ModeArray.Add(new ModeInfo(mod, d1, d2, pSize, row, col, unk, prob, block, rpt, bps, table));
64 | }
65 | public void Add(ModeInfo[] data) { foreach (ModeInfo m in data) ModeArray.Add(m); }
66 |
67 | public ModeInfo this[MILSTD_188.Mode modeIndex]
68 | {
69 | get
70 | {
71 | foreach (ModeInfo m in ModeArray)
72 | {
73 | if (m.Mode == modeIndex) return m;
74 | }
75 | return null;
76 | }
77 | }
78 |
79 | public ModeInfo this[int d1, int d2]
80 | {
81 | get
82 | {
83 | foreach (ModeInfo m in ModeArray)
84 | {
85 | if (m.D1 == d1 && m.D2 == d2) return m;
86 | }
87 | return null;
88 | }
89 | }
90 | }
91 |
92 |
93 | class MILSTD_188
94 | {
95 | static int[] BitReverseTable256 =
96 | {
97 | 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
98 | 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
99 | 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
100 | 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
101 | 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
102 | 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
103 | 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
104 | 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
105 | 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
106 | 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
107 | 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
108 | 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
109 | 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
110 | 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
111 | 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
112 | 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
113 | };
114 |
115 | public enum Mode
116 | {
117 | D_4800N,
118 | V_2400S,
119 | D_2400S,
120 | D_2400L,
121 | D_2400AS,
122 | D_2400AL,
123 | D_2400MS,
124 | D_2400ML,
125 | D_2400MAS,
126 | D_2400MAL,
127 | D_1200S,
128 | D_1200L,
129 | D_1200AS,
130 | D_1200AL,
131 | D_600S,
132 | D_600L,
133 | D_600AS,
134 | D_600AL,
135 | D_300S,
136 | D_300L,
137 | D_300AS,
138 | D_300AL,
139 | D_150S,
140 | D_150L,
141 | D_150AS,
142 | D_150AL,
143 | D_75S,
144 | D_75L,
145 | D_75AS,
146 | D_75AL
147 | }
148 |
149 | public static int EOM = 0x4B65A5B2;
150 |
151 | public static byte MSBFirst(byte msbFirstData)
152 | {
153 | return (byte)BitReverseTable256[msbFirstData];
154 | }
155 |
156 | public static byte MSBFirst(byte msbFirstData, int numberOfBits)
157 | {
158 | return (byte)BitReverseTable256[msbFirstData << (8 - numberOfBits)];
159 | }
160 |
161 | public static int MSBFirst(int msbFirstData)
162 | {
163 | return (BitReverseTable256[msbFirstData & 0xff] << 24) |
164 | (BitReverseTable256[(msbFirstData >> 8) & 0xff] << 16) |
165 | (BitReverseTable256[(msbFirstData >> 16) & 0xff] << 8) |
166 | (BitReverseTable256[(msbFirstData >> 24) & 0xff]);
167 | }
168 | public static int MSBFirst(int msbFirstData, int numberOfBits)
169 | {
170 | msbFirstData <<= (32 - numberOfBits);
171 | return (BitReverseTable256[msbFirstData & 0xff] << 24) |
172 | (BitReverseTable256[(msbFirstData >> 8) & 0xff] << 16) |
173 | (BitReverseTable256[(msbFirstData >> 16) & 0xff] << 8) |
174 | (BitReverseTable256[(msbFirstData >> 24) & 0xff]);
175 | }
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/OFDMSync.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace MELPeModem
7 | {
8 | class OFDMSync : DataProcessingModule
9 | {
10 | InputPin DataIn;
11 | OutputPin DataOut;
12 |
13 | int BlockSize;
14 | int FFTSize;
15 | int CPSize;
16 |
17 | int DataSize;
18 |
19 | int PutIndex;
20 | int SlicingIndex = 0;
21 | int PreviousSlicingIndex;
22 |
23 | IQ[] DataBuffer;
24 | float []CorrBuffer;
25 | float[] DiffFIRBuffer;
26 | float[] DiffIIRBuffer;
27 | float PrevValue = 0;
28 | FIR DiffFIR;
29 |
30 | float DiffIIR;
31 | float Alpha, OneMinusAlpha;
32 |
33 | float SNRValue;
34 |
35 | Queue OutputData = new Queue();
36 |
37 | public OFDMSync(int interpDecimFactor)
38 | {
39 | BlockSize = interpDecimFactor;
40 |
41 | // Calculate the size of the FFT Windows.
42 | // Guard Time will be BlockSize - FFTSize
43 | FFTSize = 1;
44 | while (FFTSize < BlockSize)
45 | {
46 | FFTSize <<= 1;
47 | }
48 | FFTSize >>= 1;
49 | CPSize = BlockSize - FFTSize;
50 | Init();
51 | }
52 |
53 |
54 | public float SNR
55 | {
56 | get { return SNRValue; }
57 | set { SNRValue = value; }
58 | }
59 | public override void Init()
60 | {
61 | PutIndex = 0;
62 | SNRValue = 1.0f;
63 | DataSize = 2 * BlockSize + CPSize;
64 | DataBuffer = new IQ[DataSize];
65 |
66 | Alpha = 2.0f / CPSize;
67 | OneMinusAlpha = 1.0f - Alpha;
68 |
69 | CorrBuffer = new float[BlockSize];
70 | DiffFIRBuffer = new float[BlockSize];
71 | DiffIIRBuffer = new float[BlockSize];
72 |
73 | PrevValue = 0;
74 | DiffIIR = 0;
75 |
76 | float[] coeffs = new float[CPSize / 2];
77 | for (int i = 0; i < CPSize / 2; i++)
78 | {
79 | coeffs[i] = 2.0f / CPSize;
80 | }
81 | DiffFIR = new FIR(coeffs, 1);
82 |
83 | OutputData.Clear();
84 | }
85 |
86 | public void StartCorrectionProcess(int currentIndex)
87 | {
88 | PreviousSlicingIndex = currentIndex;
89 | Array.Clear(DataBuffer, 0, DataSize);
90 | Init();
91 | }
92 |
93 | bool CalculateIndexCorrection(out bool skipNext)
94 | {
95 | bool getCurrent = false;
96 | skipNext = false;
97 | // if points are close to each other - no correction is needed
98 | if (Math.Abs(SlicingIndex - PreviousSlicingIndex) * 2 < BlockSize)
99 | {
100 | }
101 | else if (SlicingIndex > PreviousSlicingIndex) // Samples are too far from each other
102 | {
103 | getCurrent = true;
104 | }
105 | else // Samples are too close to each other
106 | {
107 | skipNext = true;
108 | }
109 | return getCurrent;
110 | }
111 |
112 | void CalculateCorrections()
113 | {
114 | // Calculate the initial correlation value
115 | float Gamma = 0;
116 | float Energy = 0;
117 | IQ Sample;
118 | IQ ShiftedSample;
119 |
120 | IQ PrevSample, PrevShiftedSample;
121 | float Value;
122 | float Diff;
123 |
124 | int Idx = 0;
125 | int ShiftedIdx = FFTSize;
126 | for (; Idx < CPSize; Idx++, ShiftedIdx++)
127 | {
128 | Sample = DataBuffer[Idx];
129 | ShiftedSample = DataBuffer[ShiftedIdx];
130 | Gamma += (Sample / ShiftedSample).R2;
131 | Energy += (Sample * Sample).R2 + (ShiftedSample * ShiftedSample).R2;
132 | }
133 |
134 | Value = Gamma - 0.5f * SNRValue * Energy;
135 | Diff = Value - PrevValue;
136 | CorrBuffer[0] = Value;
137 |
138 | DiffFIR.Process(Diff, out DiffFIRBuffer[0]);
139 | DiffIIR = OneMinusAlpha * DiffIIR + Alpha * Diff;
140 | DiffIIRBuffer[0] = DiffIIR;
141 |
142 | PrevValue = Value;
143 | // Now, calculate all other correlations
144 | Idx = CPSize;
145 | ShiftedIdx = BlockSize;
146 | for (int i = 1; i < BlockSize; i++, Idx++, ShiftedIdx++)
147 | {
148 | Sample = DataBuffer[Idx];
149 | ShiftedSample = DataBuffer[ShiftedIdx];
150 | PrevSample = DataBuffer[i-1];
151 | PrevShiftedSample = DataBuffer[i - 1 + FFTSize];
152 | Gamma += (Sample / ShiftedSample).R2 -
153 | (PrevSample / PrevShiftedSample).R2;
154 | Energy += (Sample * Sample).R2 + (ShiftedSample * ShiftedSample).R2 -
155 | ((PrevSample * PrevSample).R2 + (PrevShiftedSample * PrevShiftedSample).R2);
156 | Value = Gamma - 0.5f * SNRValue * Energy;
157 | Diff = Value - PrevValue;
158 |
159 | CorrBuffer[i] = Value;
160 | DiffFIR.Process(Diff, out DiffFIRBuffer[i]);
161 |
162 | DiffIIR = OneMinusAlpha * DiffIIR + Alpha * Diff;
163 | DiffIIRBuffer[i] = DiffIIR;
164 |
165 | PrevValue = Value;
166 | }
167 | }
168 |
169 | public int Process(IQ inData)
170 | {
171 | if (PutIndex < DataSize)
172 | {
173 | DataBuffer[PutIndex] = inData;
174 | }
175 | PutIndex++;
176 | if (PutIndex == DataSize)
177 | {
178 | CalculateCorrections();
179 | }
180 | return OutputData.Count;
181 | }
182 |
183 | public void Process(CNTRL_MSG controlParam, IQ inData)
184 | {
185 | if (controlParam == CNTRL_MSG.DATA_IN)
186 | {
187 | if (PutIndex < DataSize)
188 | {
189 | DataBuffer[PutIndex] = inData;
190 | }
191 | PutIndex++;
192 | if (PutIndex == DataSize)
193 | {
194 | CalculateCorrections();
195 | foreach (IQ IQData in OutputData) DataOut.Process(IQData);
196 | }
197 | }
198 | }
199 |
200 |
201 | public int Process(IQ[] inDataArray, int startIndex, int numToProcess)
202 | {
203 | for (int i = 0; i < numToProcess; i++)
204 | {
205 | Process(inDataArray[startIndex++]);
206 | }
207 | return OutputData.Count;
208 | }
209 |
210 | public int Process(float[] inDataArrayI, float[] inDataArrayQ, int startIndex, int numToProcess)
211 | {
212 | IQ Data;
213 | for (int i = 0; i < numToProcess; i++, startIndex++)
214 | {
215 | Data.I = inDataArrayI[startIndex];
216 | Data.Q = inDataArrayQ[startIndex];
217 | Process(Data);
218 | }
219 | return OutputData.Count;
220 | }
221 |
222 | public bool IsSyncReady
223 | {
224 | get { return (PutIndex >= DataSize); }
225 | }
226 |
227 | public int Count
228 | {
229 | get
230 | {
231 | return OutputData.Count;
232 | }
233 | }
234 |
235 | public IQ GetData()
236 | {
237 | return OutputData.Dequeue();
238 | }
239 |
240 | public int GetData(IQ[] outputArray)
241 | {
242 | int ret = OutputData.Count;
243 | OutputData.CopyTo(outputArray, 0);
244 | OutputData.Clear();
245 | return ret;
246 | }
247 |
248 | public int GetData(IQ[] outputArray, int arrayIndex)
249 | {
250 | int ret = OutputData.Count;
251 | OutputData.CopyTo(outputArray, arrayIndex);
252 | OutputData.Clear();
253 | return ret;
254 | }
255 |
256 |
257 | public int SymbolCorrection
258 | {
259 | get
260 | {
261 | return ((PutIndex - 1) - SlicingIndex) % BlockSize;
262 | }
263 | }
264 |
265 | public override void SetModuleParameters()
266 | {
267 | DataIn = new InputPin("DataIn", this.Process);
268 | DataOut = new OutputPin("DataOut");
269 | base.SetIOParameters("OFDM Timing Offset Estimator", new DataPin[] { DataIn, DataOut });
270 | }
271 | }
272 | }
273 |
--------------------------------------------------------------------------------
/Generator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace MELPeModem
6 | {
7 |
8 | class Quad
9 | {
10 | double I0, I1, Q0, Q1, Coeff;
11 | double DeltaPhase;
12 |
13 | public Quad()
14 | {
15 | I0 = 1;
16 | Q0 = 0;
17 | I1 = 1;
18 | Q1 = 0;
19 | Coeff = 0;
20 | DeltaPhase = 0;
21 | }
22 | public Quad(IQ initialVector, IQ delta, int offset)
23 | {
24 | double initialPhase = initialVector.Phase;
25 | double initialR = initialVector.R;
26 | DeltaPhase = delta.Phase;
27 | Coeff = 2 * Math.Sin(DeltaPhase);
28 |
29 | double newPhase = initialPhase + DeltaPhase * offset;
30 | I0 = initialR * Math.Cos(newPhase);
31 | Q0 = initialR * Math.Sin(newPhase);
32 | I1 = initialR * Math.Cos(newPhase + DeltaPhase);
33 | Q1 = initialR * Math.Sin(newPhase + DeltaPhase);
34 | }
35 |
36 | public Quad(IQ initialVector, IQ delta)
37 | {
38 | double initialPhase = initialVector.Phase;
39 | double initialR = initialVector.R;
40 | DeltaPhase = delta.Phase;
41 | Coeff = 2 * Math.Sin(DeltaPhase);
42 |
43 | I0 = initialVector.I;
44 | Q0 = initialVector.Q;
45 | I1 = initialR * Math.Cos(initialPhase + DeltaPhase);
46 | Q1 = initialR * Math.Sin(initialPhase + DeltaPhase);
47 | }
48 |
49 | public Quad(float outputFreq, float sampleFreq)
50 | {
51 | DeltaPhase = 2 * Math.PI * outputFreq / sampleFreq;
52 | Coeff = 2 * Math.Sin(DeltaPhase);
53 |
54 | double initialPhase = 0 ;
55 | I0 = 1;
56 | Q0 = 0;
57 | I1 = Math.Cos(initialPhase + DeltaPhase);
58 | Q1 = Math.Sin(initialPhase + DeltaPhase);
59 | }
60 |
61 | public Quad(float outputFreq, float sampleFreq, double initialPhase)
62 | {
63 | DeltaPhase = 2 * Math.PI * outputFreq / sampleFreq;
64 | Coeff = 2 * Math.Sin(DeltaPhase);
65 |
66 | I0 = Math.Cos(initialPhase);
67 | Q0 = Math.Sin(initialPhase);
68 | I1 = Math.Cos(initialPhase + DeltaPhase);
69 | Q1 = Math.Sin(initialPhase + DeltaPhase);
70 | }
71 |
72 | public Quad(float outputFreq, float sampleFreq, Quad initialQuad)
73 | {
74 | DeltaPhase = 2 * Math.PI * outputFreq / sampleFreq;
75 | Coeff = 2 * Math.Sin(DeltaPhase);
76 |
77 | float initialPhase = initialQuad.Value.Phase;
78 | I0 = initialQuad.Value.I;
79 | Q0 = initialQuad.Value.Q;
80 | I1 = Math.Cos(initialPhase + DeltaPhase);
81 | Q1 = Math.Sin(initialPhase + DeltaPhase);
82 | }
83 |
84 | public static Quad operator ++(Quad a)
85 | {
86 | double Q2 = a.Q0 + a.Coeff * a.I1;
87 | double I2 = a.I0 - a.Coeff * a.Q1;
88 | a.I0 = a.I1; a.I1 = I2;
89 | a.Q0 = a.Q1; a.Q1 = Q2;
90 | return a;
91 | }
92 | public IQ Next()
93 | {
94 | IQ result = new IQ((float)I0, (float)Q0);
95 | double Q2 = Q0 + Coeff * I1;
96 | double I2 = I0 - Coeff * Q1;
97 | I0 = I1; I1 = I2;
98 | Q0 = Q1; Q1 = Q2;
99 | return result;
100 | }
101 |
102 | public int Process(float data, out IQ outIQ)
103 | {
104 | outIQ.I = (float)I0 * data;
105 | outIQ.Q = (float)Q0 * data;
106 | double Q2 = Q0 + Coeff * I1;
107 | double I2 = I0 - Coeff * Q1;
108 | I0 = I1; I1 = I2;
109 | Q0 = Q1; Q1 = Q2;
110 | return 1;
111 | }
112 | public static implicit operator IQ(Quad a)
113 | {
114 | return new IQ((float)a.I0, (float)a.Q0);
115 | }
116 |
117 | public static IQ operator +(IQ a, Quad b)
118 | {
119 | return a + b.Value;
120 | }
121 |
122 | public static IQ operator +(Quad b, IQ a )
123 | {
124 | return a + b.Value;
125 | }
126 |
127 | public IQ Value
128 | {
129 | get { return new IQ((float)I0, (float)Q0); }
130 | set {
131 | double initialPhase = value.Phase;
132 | value = value/value.R;
133 | I0 = value.I;
134 | Q0 = value.Q;
135 | I1 = Math.Cos(initialPhase + DeltaPhase);
136 | Q1 = Math.Sin(initialPhase + DeltaPhase);
137 | }
138 | }
139 |
140 | public IQ NextValue
141 | {
142 | get { return new IQ((float)I1, (float)Q1); }
143 | }
144 | }
145 |
146 |
147 | class Generator
148 | {
149 | double OutputFrequency;
150 | double SamplingFrequency;
151 | double DeltaPhase;
152 |
153 | double S0, S1, Coeff;
154 |
155 | public Generator()
156 | {
157 | }
158 |
159 | public Generator(float FreqDest, float FreqSample) : this(FreqDest, FreqSample, 0)
160 | {
161 | }
162 |
163 | public Generator(float FreqDest, float FreqSample, float PhaseDest)
164 | {
165 | this.SamplingFrequency = FreqSample;
166 | this.OutputFrequency = FreqDest;
167 | this.DeltaPhase = (2 * Math.PI * FreqDest) / SamplingFrequency;
168 | this.Coeff = 2 * Math.Cos(DeltaPhase);
169 | this.Phase = PhaseDest;
170 | }
171 |
172 | ///
173 | /// Initializes the generator.
174 | ///
175 | /// Desired frequency in Hertz
176 | /// Sampling frequency in Hertz
177 | /// Starting phase in radians
178 | public void Init(float FreqDest, float FreqSample, float PhaseDest)
179 | {
180 | this.SamplingFrequency = FreqSample;
181 | this.OutputFrequency = FreqDest;
182 | this.DeltaPhase = (2 * Math.PI * FreqDest) / SamplingFrequency;
183 | this.Coeff = 2 * Math.Cos(DeltaPhase);
184 | this.Phase = PhaseDest;
185 | }
186 |
187 | public float Frequency
188 | {
189 | get { return (float)this.OutputFrequency; }
190 | set
191 | {
192 | float CurrentPhase = Phase;
193 | this.OutputFrequency = value;
194 | this.DeltaPhase = (2 * Math.PI * value) / SamplingFrequency;
195 | this.Coeff = 2 * Math.Cos(DeltaPhase);
196 | this.Phase = CurrentPhase;
197 | }
198 | }
199 | public float Phase
200 | {
201 | get
202 | {
203 | S0 = Math.Max(Math.Min(S0, 1.0), -1.0);
204 | double ph0 = Math.Asin(S0);
205 | // Now we have to resolve a phase ambiguity
206 | // Use the formula : cos(w) = (S1 - Cos(D)S0)/sin(D)
207 | // cos(D) = Coeff/2, sin(D) is always positive
208 | double CosW = S1 - Coeff * S0 / 2.0;
209 | if (CosW < 0)
210 | ph0 = Math.PI - ph0;
211 | if (ph0 < 0)
212 | ph0 += (2 * Math.PI);
213 | return (float) ph0;
214 | }
215 | set
216 | {
217 | S0 = Math.Sin(value);
218 | S1 = Math.Sin(value + DeltaPhase);
219 | }
220 | }
221 |
222 | public void SetFrequency(float newFrequency)
223 | {
224 | Frequency = newFrequency;
225 | }
226 |
227 | public void SetPhase(float newPhase)
228 | {
229 | Phase = newPhase;
230 | }
231 |
232 | public int GenerateVoid(int NumSamples)
233 | {
234 | double OldValue;
235 | for (int i = 0; i < NumSamples; i++)
236 | {
237 | OldValue = S0;
238 | S0 = S1;
239 | S1 = S1 * Coeff - OldValue;
240 | }
241 | return NumSamples;
242 | }
243 |
244 | public int Generate(float Value, float[] outputBuffer, int NumSamples)
245 | {
246 | double OldValue;
247 | for (int i = 0; i < NumSamples; i++)
248 | {
249 | OldValue = S0;
250 | S0 = S1;
251 | S1 = S1 * Coeff - OldValue;
252 | outputBuffer[i] = (float)OldValue * Value;
253 | }
254 | return NumSamples;
255 | }
256 |
257 | public int Generate(float[] outputBuffer, int NumSamples)
258 | {
259 | double OldValue;
260 | for (int i = 0; i < NumSamples; i++)
261 | {
262 | OldValue = S0;
263 | S0 = S1;
264 | S1 = S1 * Coeff - OldValue;
265 | outputBuffer[i] = (float)OldValue ;
266 | }
267 | return NumSamples;
268 | }
269 |
270 | public int Generate(float[] outputBuffer)
271 | {
272 | double OldValue;
273 | for (int i = 0; i < outputBuffer.Length; i++)
274 | {
275 | OldValue = S0;
276 | S0 = S1;
277 | S1 = S1 * Coeff - OldValue;
278 | outputBuffer[i] = (float)OldValue ;
279 | }
280 | return outputBuffer.Length;
281 | }
282 |
283 |
284 | public int Generate(float[] modulation, float[] outputBuffer)
285 | {
286 | double OldValue;
287 | int i = 0;
288 | foreach (float modval in modulation)
289 | {
290 | OldValue = S0;
291 | S0 = S1;
292 | S1 = S1 * Coeff - OldValue;
293 | outputBuffer[i++] = (float)OldValue * modval;
294 | }
295 | return modulation.Length;
296 | }
297 |
298 | public int Generate(float[] modulation, float[] outputBuffer, int startIndex)
299 | {
300 | double OldValue;
301 | int i = 0;
302 | foreach (float modval in modulation)
303 | {
304 | OldValue = S0;
305 | S0 = S1;
306 | S1 = S1 * Coeff - OldValue;
307 | outputBuffer[startIndex + i++] = (float)OldValue * modval;
308 | }
309 | return modulation.Length;
310 | }
311 |
312 | public int Process(float Value, out float outResult)
313 | {
314 | double OldValue;
315 | OldValue = S0;
316 | S0 = S1;
317 | S1 = S1 * Coeff - OldValue;
318 | outResult = (float)OldValue * Value;
319 | return 1;
320 | }
321 |
322 | public int Process(float[] inputSignal, int inputIndex, float[] outputBuffer, int numSamples)
323 | {
324 | double OldValue;
325 | for( int i = 0; i < numSamples; i++)
326 | {
327 | OldValue = S0;
328 | S0 = S1;
329 | S1 = S1 * Coeff - OldValue;
330 | outputBuffer[i] = (float)OldValue * inputSignal[inputIndex + i];
331 | }
332 | return numSamples;
333 | }
334 |
335 | ///
336 | /// Adds (mixes) the new samples into provided array
337 | ///
338 | /// The buffer where samples will be added
339 | /// Number of sampless to add
340 | /// Resulting array
341 | public int GenerateAdd(float Value, float[] outputBuffer, int NumSamples)
342 | {
343 | double OldValue;
344 | for (int i = 0; i < NumSamples; i++)
345 | {
346 | OldValue = S0;
347 | S0 = S1;
348 | S1 = S1 * Coeff - OldValue;
349 | outputBuffer[i] += (float)OldValue * Value;
350 | }
351 | return NumSamples;
352 | }
353 |
354 | public int GenerateAdd(float[] modulation, float[] outputBuffer)
355 | {
356 | double OldValue;
357 | int i = 0;
358 | foreach (float modval in modulation)
359 | {
360 | OldValue = S0;
361 | S0 = S1;
362 | S1 = S1 * Coeff - OldValue;
363 | outputBuffer[i++] += (float)OldValue * modval;
364 | }
365 | return modulation.Length;
366 | }
367 |
368 | public int GenerateAdd(float[] modulation, float[] outputBuffer, int startIndex)
369 | {
370 | double OldValue;
371 | int i = 0;
372 | foreach (float modval in modulation)
373 | {
374 | OldValue = S0;
375 | S0 = S1;
376 | S1 = S1 * Coeff - OldValue;
377 | outputBuffer[startIndex + i++] += (float)OldValue * modval;
378 | }
379 | return modulation.Length;
380 | }
381 | }
382 | }
383 |
--------------------------------------------------------------------------------
/BitDataTypes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace MELPeModem
6 | {
7 |
8 | ///
9 | /// The class that provides some useful functions to pack and unpack symbols into bit arrays
10 | ///
11 | class BitArray
12 | {
13 | int SymbolSize;
14 | int SymbolMask;
15 | List BitStorage;
16 |
17 | ///
18 | /// Constructor for the Non-Packed Data array.
19 | ///
20 | /// Specifies how many bits will be taken from the symbol.
21 | public BitArray(int bitsPerSymbol)
22 | {
23 | this.SymbolSize = bitsPerSymbol;
24 | this.SymbolMask = (1 << bitsPerSymbol) - 1;
25 | this.BitStorage = new List();
26 | Init();
27 | }
28 |
29 | public void Init()
30 | {
31 | BitStorage.Clear();
32 | }
33 |
34 | public void Clear()
35 | {
36 | Init();
37 | }
38 |
39 | public void Add(int symbol)
40 | {
41 | PutSymbol(symbol);
42 | }
43 |
44 | public void Add(int symbol, int numBits)
45 | {
46 | PutSymbol(symbol, numBits);
47 | }
48 |
49 | public void Add(int[] symbolArray)
50 | {
51 | PutSymbol(symbolArray);
52 | }
53 |
54 | public void Add(int[] symbolArray, int startingIndex, int symbolCount)
55 | {
56 | PutSymbol(symbolArray, startingIndex, symbolCount);
57 | }
58 |
59 | public void Add(byte []addBits)
60 | {
61 | int NumBits = (addBits.Length / this.SymbolSize) * this.SymbolSize;
62 | for(int i = 0; i < NumBits; i++)
63 | BitStorage.Add(addBits[i]);
64 | }
65 |
66 | public void Add(byte[] addBits, int startingIndex, int numBits)
67 | {
68 | int NumBits = (numBits / this.SymbolSize) * this.SymbolSize;
69 | for (int i = 0; i < NumBits; i++)
70 | BitStorage.Add(addBits[startingIndex++]);
71 | }
72 |
73 | public void PutSymbol(int symbol)
74 | {
75 | symbol &= SymbolMask;
76 |
77 | for (int i = 0; i < SymbolSize; i++)
78 | {
79 | BitStorage.Add((byte)(symbol & 0x0001));
80 | symbol >>= 1;
81 | }
82 | }
83 |
84 | public void PutSymbol(int symbol, int numBits)
85 | {
86 | numBits = (numBits / SymbolSize) * SymbolSize;
87 |
88 | int Mask = (int)((1L << numBits) - 1);
89 | symbol &= Mask;
90 | for (int i = 0; i < numBits; i++)
91 | {
92 | BitStorage.Add((byte)(symbol & 0x0001));
93 | symbol >>= 1;
94 | }
95 | }
96 |
97 | public void PutSymbol(int[] symbolArray)
98 | {
99 | foreach (int symbol in symbolArray)
100 | {
101 | PutSymbol(symbol);
102 | }
103 | }
104 |
105 | public void PutSymbol(int[] symbolArray, int startingIndex, int symbolCount)
106 | {
107 | for (int i = 0; i < symbolCount; i++)
108 | {
109 | PutSymbol(symbolArray[startingIndex++]);
110 | }
111 | }
112 |
113 | public int GetSymbol(int symbolIndex)
114 | {
115 | int Symb = 0;
116 | int GetByteIdx = symbolIndex * SymbolSize;
117 | for(int i = 0; i < SymbolSize; i++)
118 | {
119 | Symb |= (BitStorage[GetByteIdx++] & 0x0001) << i;
120 | }
121 | return Symb & SymbolMask;
122 | }
123 |
124 | public int this[int symbolIndex]
125 | {
126 | get { return GetSymbol(symbolIndex); }
127 | set
128 | {
129 | int symbol = value & SymbolMask;
130 | int SetByteIdx = symbolIndex * SymbolSize; // Position of the starting bit
131 | for (int i = 0; i < SymbolSize; i++)
132 | {
133 | BitStorage[SetByteIdx++] = (byte)(symbol & 0x0001);
134 | symbol >>= 1;
135 | }
136 | }
137 | }
138 |
139 | public int SymbolsCount
140 | {
141 | get { return BitStorage.Count / this.SymbolSize; }
142 | }
143 |
144 | public int BitsCount
145 | {
146 | get { return BitStorage.Count; }
147 | }
148 |
149 | public int GetData(byte[] outputArray)
150 | {
151 | int ret = BitStorage.Count;
152 | BitStorage.CopyTo(outputArray);
153 | BitStorage.Clear();
154 | return ret;
155 | }
156 |
157 | public int GetData(byte[] outputArray, int startingIndex)
158 | {
159 | int ret = BitStorage.Count;
160 | BitStorage.CopyTo(outputArray, startingIndex);
161 | BitStorage.Clear();
162 | return ret;
163 | }
164 |
165 | public int GetData(int[] outputSymbols)
166 | {
167 | int ret = SymbolsCount;
168 | for (int i = 0; i < SymbolsCount; i++)
169 | {
170 | outputSymbols[i] = GetSymbol(i);
171 | }
172 | BitStorage.Clear();
173 | return ret;
174 | }
175 |
176 | public int GetData(int[] outputSymbols, int startingIndex)
177 | {
178 | int ret = SymbolsCount;
179 | for (int i = 0; i < SymbolsCount; i++)
180 | {
181 | outputSymbols[startingIndex + i] = GetSymbol(i);
182 | }
183 | BitStorage.Clear();
184 | return ret;
185 | }
186 | }
187 |
188 |
189 | class SerialData
190 | {
191 | int StartBits;
192 | int DataBits;
193 | int StopBits;
194 | Parity ParityFlag;
195 |
196 | int SymbolSize;
197 | int SymbolMask;
198 | int ParityMask;
199 | int StopMask;
200 | State CurrentState;
201 | int CurrentData;
202 | int CurrentCount;
203 | List BitStorage;
204 | List SymbolStorage;
205 |
206 | static int[] bitcounts =
207 | { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
208 |
209 | int Bitcount(int u)
210 | {
211 | int n = 0;
212 | for (; u != 0; u >>= 4)
213 | n += bitcounts[u & 0x0f];
214 | return n;
215 | }
216 |
217 | public enum Parity
218 | {
219 | N = 0,
220 | E = 1,
221 | O = 2
222 | }
223 |
224 | public SerialData(int dataBits, int startBits, int stopBits, Parity parityBits)
225 | {
226 | StartBits = startBits;
227 | StopBits = stopBits;
228 | DataBits = dataBits;
229 | ParityFlag = parityBits;
230 |
231 | ParityMask = 1 << DataBits;
232 | SymbolMask = ParityMask - 1;
233 | DataBits += (ParityFlag == Parity.N) ? 0 : 1;
234 | StopMask = ((1 << StopBits) - 1) << DataBits;
235 | SymbolSize = (StartBits + DataBits + StopBits);
236 | BitStorage = new List();
237 | SymbolStorage = new List();
238 | Init();
239 | }
240 |
241 | public void Init()
242 | {
243 | BitStorage.Clear();
244 | SymbolStorage.Clear();
245 | CurrentState = State.START_SEARCH;
246 | CurrentCount = 0;
247 | CurrentData = 0;
248 | }
249 |
250 | public void Clear()
251 | {
252 | Init();
253 | }
254 |
255 | public void PutSymbol(int symbol)
256 | {
257 | symbol &= SymbolMask;
258 | SymbolStorage.Add(symbol);
259 |
260 | // Add parity (if necessary), stop and start bits
261 | if (ParityFlag != Parity.N)
262 | {
263 | int n = Bitcount(symbol) & 0x01;
264 | if (((n == 0) && (ParityFlag == Parity.O)) ||
265 | ((n != 0) && (ParityFlag == Parity.E)))
266 | {
267 | symbol |= ParityMask;
268 | }
269 | }
270 | symbol |= StopMask;
271 | symbol <<= StartBits;
272 |
273 | for (int i = 0; i < SymbolSize; i++)
274 | {
275 | BitStorage.Add( (byte) (symbol & 0x0001));
276 | symbol >>= 1;
277 | }
278 | }
279 |
280 | public void PutSymbol(int[] symbolArray)
281 | {
282 | foreach (int symbol in symbolArray)
283 | {
284 | PutSymbol(symbol);
285 | }
286 | }
287 |
288 | public void PutSymbol(int[] symbolArray, int startIndex, int numSymbols)
289 | {
290 | for (int i = 0; i < numSymbols; i++)
291 | {
292 | PutSymbol(symbolArray[startIndex + i]);
293 | }
294 | }
295 |
296 | public int this[int symbolIndex]
297 | {
298 | get { return SymbolStorage[symbolIndex]; }
299 | }
300 |
301 | enum State
302 | {
303 | START_SEARCH,
304 | START,
305 | DATA,
306 | STOP
307 | }
308 |
309 | public int PutData(byte[] bitArray, int startingIndex, int numBits)
310 | {
311 |
312 | for (int i = 0; i < numBits; i++)
313 | BitStorage.Add(bitArray[startingIndex + i]);
314 |
315 | int CurrentBit;
316 | int BitIndex = startingIndex;
317 | while (BitIndex < (startingIndex + numBits))
318 | {
319 | CurrentBit = bitArray[BitIndex] & 0x01;
320 | switch (CurrentState)
321 | {
322 | case State.START_SEARCH:
323 | if (CurrentBit == 0x00)
324 | {
325 | CurrentCount = 0;
326 | CurrentState = State.START;
327 | }
328 | else
329 | BitIndex++;
330 | break;
331 | case State.START:
332 | if (CurrentCount++ >= StartBits)
333 | {
334 | CurrentCount = 0;
335 | CurrentState = State.DATA;
336 | }
337 | else if (CurrentBit != 0x00)
338 | {
339 | CurrentState = State.START_SEARCH;
340 | }
341 | else
342 | BitIndex++;
343 | break;
344 | case State.DATA:
345 | if (CurrentCount >= DataBits)
346 | {
347 | CurrentData &= SymbolMask;
348 | CurrentState = State.STOP;
349 | }
350 | else
351 | {
352 | CurrentData |= (CurrentBit << CurrentCount);
353 | CurrentCount++;
354 | BitIndex++;
355 | }
356 | break;
357 | case State.STOP:
358 | if (CurrentBit == 0x00)
359 | {
360 | CurrentCount = 0;
361 | CurrentState = State.START;
362 | }
363 | else
364 | {
365 | SymbolStorage.Add(CurrentData);
366 | CurrentState = State.START_SEARCH;
367 | BitIndex++;
368 | }
369 | CurrentData = 0;
370 | break;
371 | }
372 | }
373 | return BitStorage.Count;
374 | }
375 |
376 | public int SymbolsCount
377 | {
378 | get { return SymbolStorage.Count; }
379 | }
380 |
381 | public int BitsCount
382 | {
383 | get { return BitStorage.Count; }
384 | }
385 |
386 | public int GetData(byte[] outputArray)
387 | {
388 | int ret = BitStorage.Count;
389 | BitStorage.CopyTo(outputArray);
390 | Init();
391 | return ret; ;
392 | }
393 |
394 | public int GetData(byte[] outputArray, int startingIndex)
395 | {
396 | int ret = BitStorage.Count;
397 | BitStorage.CopyTo(outputArray, startingIndex);
398 | Init();
399 | return ret; ;
400 | }
401 |
402 | public int GetData(int[] symbolArray)
403 | {
404 | int ret = SymbolStorage.Count;
405 | SymbolStorage.CopyTo(symbolArray);
406 | Init();
407 | return ret;
408 | }
409 |
410 | public int GetData(int[] symbolArray, int startingIndex)
411 | {
412 | int ret = SymbolStorage.Count;
413 | SymbolStorage.CopyTo(symbolArray, startingIndex);
414 | Init();
415 | return ret;
416 | }
417 | }
418 | }
419 |
--------------------------------------------------------------------------------
/FFT.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace MELPeModem
6 | {
7 |
8 | /*============================================================================
9 |
10 | fourierd.c - Don Cross
11 |
12 | http://www.intersrv.com/~dcross/fft.html
13 |
14 | Contains definitions for doing Fourier transforms
15 | and inverse Fourier transforms.
16 |
17 | This module performs operations on arrays of 'double'.
18 |
19 | Revision history:
20 |
21 | 1998 September 19 [Don Cross]
22 | Updated coding standards.
23 | Improved efficiency of trig calculations.
24 |
25 | ============================================================================*/
26 | class FFT
27 | {
28 | int NumSamples;
29 | int NumBitsNeeded;
30 | double[] st1;
31 | double[] st2;
32 | double[] ct1;
33 | double[] ct2;
34 |
35 | float[] fst1;
36 | float[] fst2;
37 | float[] fct1;
38 | float[] fct2;
39 |
40 | IQ[] T1;
41 | IQ[] T2;
42 |
43 | int[] ReverseBits_Table;
44 |
45 | public FFT(int numberOfSamples)
46 | {
47 | NumSamples = numberOfSamples;
48 | NumBitsNeeded = NumberOfBitsNeeded(NumSamples);
49 |
50 | st1 = new double[NumBitsNeeded+1];
51 | st2 = new double[NumBitsNeeded+1];
52 | ct1 = new double[NumBitsNeeded+1];
53 | ct2 = new double[NumBitsNeeded+1];
54 |
55 | fst1 = new float[NumBitsNeeded + 1];
56 | fst2 = new float[NumBitsNeeded + 1];
57 | fct1 = new float[NumBitsNeeded + 1];
58 | fct2 = new float[NumBitsNeeded + 1];
59 |
60 | T1 = new IQ[NumBitsNeeded + 1];
61 | T2 = new IQ[NumBitsNeeded + 1];
62 |
63 | ReverseBits_Table = new int[NumSamples];
64 |
65 | int TwiddlesIndex = 0;
66 | for (int BlockSize = 2; BlockSize <= 2 * NumSamples; BlockSize <<= 1)
67 | {
68 | Twiddles(BlockSize, out st2[TwiddlesIndex], out st1[TwiddlesIndex], out ct2[TwiddlesIndex], out ct1[TwiddlesIndex]);
69 | fst2[TwiddlesIndex] = (float)st2[TwiddlesIndex];
70 | fst1[TwiddlesIndex] = (float)st1[TwiddlesIndex];
71 | fct2[TwiddlesIndex] = (float)ct2[TwiddlesIndex];
72 | fct1[TwiddlesIndex] = (float)ct1[TwiddlesIndex];
73 | T1[TwiddlesIndex] = new IQ((float)ct1[TwiddlesIndex], (float)st1[TwiddlesIndex]);
74 | T2[TwiddlesIndex] = new IQ((float)ct2[TwiddlesIndex], (float)st2[TwiddlesIndex]);
75 |
76 | TwiddlesIndex++;
77 | }
78 |
79 | for (int i = 0; i < NumSamples; i++)
80 | {
81 | ReverseBits_Table[i] = ReverseBits(i, NumBitsNeeded);
82 | }
83 | }
84 |
85 | void Twiddles(int BlockSize, out double sm2, out double sm1, out double cm2, out double cm1)
86 | {
87 | double angle_numerator = 2.0 * Math.PI;
88 | double delta_angle = (BlockSize == 0) ? 0 : angle_numerator / (double)BlockSize;
89 | sm2 = Math.Sin(-2 * delta_angle);
90 | sm1 = Math.Sin(-delta_angle);
91 | cm2 = Math.Cos(-2 * delta_angle);
92 | cm1 = Math.Cos(-delta_angle);
93 | }
94 |
95 | public void ProcessIFFT(
96 | double[] RealIn,
97 | double[] ImagIn,
98 | double[] RealOut,
99 | double[] ImagOut)
100 | {
101 | ProcessFFT(ImagIn, RealIn, ImagOut, RealOut);
102 | /*
103 | ** Need to normalize if inverse transform...
104 | */
105 | double denom = (double)NumSamples;
106 |
107 | for (int i = 0; i < NumSamples; i++)
108 | {
109 | RealOut[i] /= denom;
110 | ImagOut[i] /= denom;
111 | }
112 | }
113 |
114 | public void ProcessIFFT(
115 | IQ[] dataIn,
116 | IQ[] dataOut)
117 | {
118 | int i, j, k, n;
119 | int BlockSize, BlockEnd;
120 |
121 | IQ T; // Temporary vriable for swapping
122 |
123 | /*
124 | ** Do simultaneous data copy and bit-reversal ordering into outputs...
125 | * For IFFT swap Real and Img parts
126 | */
127 | for (i = 0; i < NumSamples; i++)
128 | {
129 | j = ReverseBits_Table[i];
130 | dataOut[j].I = dataIn[i].Q;
131 | dataOut[j].Q = dataIn[i].I;
132 | }
133 |
134 | /*
135 | ** Do the FFT itself...
136 | */
137 |
138 | BlockEnd = 1;
139 | int TwiddlesIndex = 0;
140 | for (BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1)
141 | {
142 | IQ M1 = T1[TwiddlesIndex];
143 | IQ M2 = T2[TwiddlesIndex];
144 |
145 | float w = 2 * fct1[TwiddlesIndex];
146 |
147 | IQ A0, A1, A2;
148 |
149 | for (i = 0; i < NumSamples; i += BlockSize)
150 | {
151 | A2 = M2;
152 | A1 = M1;
153 | for (j = i, n = 0, k = i + BlockEnd; n < BlockEnd; j++, n++, k++)
154 | {
155 | A0 = (w * A1) - A2;
156 | A2 = A1;
157 | A1 = A0;
158 |
159 | T = A0 * dataOut[k];
160 | dataOut[k] = dataOut[j] - T;
161 | dataOut[j] += T;
162 | }
163 | }
164 | TwiddlesIndex++;
165 | BlockEnd = BlockSize;
166 | }
167 | /*
168 | ** Need to normalize if inverse transform...
169 | */
170 | float norm = 1.0f / 2.0f; // (float)Math.Sqrt(1.0 / 2);
171 | float t;
172 | for (i = 0; i < NumSamples; i++)
173 | {
174 | // Swap Real and Img parts
175 | t = dataOut[i].Q;
176 | dataOut[i].Q = dataOut[i].I * norm;
177 | dataOut[i].I = t * norm;
178 | }
179 | }
180 |
181 | public void ProcessFFT(
182 | double[] RealIn,
183 | double[] ImagIn,
184 | double[] RealOut,
185 | double[] ImagOut)
186 | {
187 | int i, j, k, n;
188 | int BlockSize, BlockEnd;
189 |
190 | double tr, ti; /* temp real, temp imaginary */
191 | /*
192 | ** Do simultaneous data copy and bit-reversal ordering into outputs...
193 | */
194 |
195 | for (i = 0; i < NumSamples; i++)
196 | {
197 | j = ReverseBits_Table[i];
198 | RealOut[j] = RealIn[i];
199 | ImagOut[j] = (ImagIn == null) ? 0.0 : ImagIn[i];
200 | }
201 |
202 | /*
203 | ** Do the FFT itself...
204 | */
205 |
206 | BlockEnd = 1;
207 | int TwiddlesIndex = 0;
208 | for (BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1)
209 | {
210 | double sm1, sm2, cm1, cm2;
211 | sm2 = st2[TwiddlesIndex];
212 | sm1 = st1[TwiddlesIndex];
213 | cm2 = ct2[TwiddlesIndex];
214 | cm1 = ct1[TwiddlesIndex];
215 |
216 | double w = 2 * cm1;
217 | double ar0, ar1, ar2, ai0, ai1, ai2;
218 |
219 | for (i = 0; i < NumSamples; i += BlockSize)
220 | {
221 | ar2 = cm2;
222 | ar1 = cm1;
223 |
224 | ai2 = sm2;
225 | ai1 = sm1;
226 |
227 | for (j = i, n = 0; n < BlockEnd; j++, n++)
228 | {
229 | ar0 = w * ar1 - ar2;
230 | ar2 = ar1;
231 | ar1 = ar0;
232 |
233 | ai0 = w * ai1 - ai2;
234 | ai2 = ai1;
235 | ai1 = ai0;
236 |
237 | k = j + BlockEnd;
238 | tr = ar0 * RealOut[k] - ai0 * ImagOut[k];
239 | ti = ar0 * ImagOut[k] + ai0 * RealOut[k];
240 |
241 | RealOut[k] = RealOut[j] - tr;
242 | ImagOut[k] = ImagOut[j] - ti;
243 |
244 | RealOut[j] += tr;
245 | ImagOut[j] += ti;
246 | }
247 | }
248 | TwiddlesIndex++;
249 | BlockEnd = BlockSize;
250 | }
251 | }
252 |
253 | public void ProcessFFT(
254 | IQ[] dataIn,
255 | IQ[] dataOut
256 | )
257 | {
258 | int i, j, k, n;
259 | int BlockSize, BlockEnd;
260 |
261 | IQ T; // Temporary vriable for swapping
262 |
263 | /*
264 | ** Do simultaneous data copy and bit-reversal ordering into outputs...
265 | */
266 | float norm = 2.0f / NumSamples;
267 | for (i = 0; i < NumSamples; i++)
268 | {
269 | j = ReverseBits_Table[i];
270 | dataOut[j] = dataIn[i] * norm;
271 | }
272 |
273 | /*
274 | ** Do the FFT itself...
275 | */
276 |
277 | BlockEnd = 1;
278 | int TwiddlesIndex = 0;
279 | for (BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1)
280 | {
281 | IQ M1 = T1[TwiddlesIndex];
282 | IQ M2 = T2[TwiddlesIndex];
283 |
284 | float w = 2 * fct1[TwiddlesIndex];
285 |
286 | IQ A0, A1, A2;
287 |
288 | for (i = 0; i < NumSamples; i += BlockSize)
289 | {
290 | A2 = M2;
291 | A1 = M1;
292 | for (j = i, n = 0, k = i + BlockEnd; n < BlockEnd; j++, n++, k++)
293 | {
294 | A0 = (w * A1) - A2;
295 | A2 = A1;
296 | A1 = A0;
297 |
298 | T = A0 * dataOut[k];
299 | dataOut[k] = dataOut[j] - T;
300 | dataOut[j] += T;
301 | }
302 | }
303 | TwiddlesIndex++;
304 | BlockEnd = BlockSize;
305 | }
306 | }
307 |
308 |
309 | public void ProcessFFT(
310 | IQ[] dataIn,
311 | int startingIndex,
312 | IQ[] dataOut
313 | )
314 | {
315 | int i, j, k, n;
316 | int BlockSize, BlockEnd;
317 |
318 | IQ T; // Temporary vriable for swapping
319 |
320 | /*
321 | ** Do simultaneous data copy and bit-reversal ordering into outputs...
322 | */
323 | float norm = 2.0f / NumSamples;
324 | for (i = 0; i < NumSamples; i++)
325 | {
326 | j = ReverseBits_Table[i];
327 | dataOut[j] = dataIn[startingIndex++] * norm;
328 | }
329 |
330 | /*
331 | ** Do the FFT itself...
332 | */
333 |
334 | BlockEnd = 1;
335 | int TwiddlesIndex = 0;
336 | for (BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1)
337 | {
338 | IQ M1 = T1[TwiddlesIndex];
339 | IQ M2 = T2[TwiddlesIndex];
340 |
341 | float w = 2 * fct1[TwiddlesIndex];
342 |
343 | IQ A0, A1, A2;
344 |
345 | for (i = 0; i < NumSamples; i += BlockSize)
346 | {
347 | A2 = M2;
348 | A1 = M1;
349 | for (j = i, n = 0, k = i + BlockEnd; n < BlockEnd; j++, n++, k++)
350 | {
351 | A0 = (w * A1) - A2;
352 | A2 = A1;
353 | A1 = A0;
354 |
355 | T = A0 * dataOut[k];
356 | dataOut[k] = dataOut[j] - T;
357 | dataOut[j] += T;
358 | }
359 | }
360 | TwiddlesIndex++;
361 | BlockEnd = BlockSize;
362 | }
363 | }
364 |
365 | public static int NumberOfBitsNeeded(int PowerOfTwo)
366 | {
367 | int i;
368 | for (i = 0; ; i++)
369 | {
370 | if ((PowerOfTwo & (1 << i)) != 0 )
371 | return i;
372 | }
373 | }
374 |
375 | static int ReverseBits(int index, int NumBits)
376 | {
377 | int i, rev;
378 |
379 | for (i = rev = 0; i < NumBits; i++)
380 | {
381 | rev = (rev << 1) | (index & 1);
382 | index >>= 1;
383 | }
384 |
385 | return rev;
386 | }
387 |
388 | public static double Index_to_frequency(int NumSamples, int Index)
389 | {
390 | if (Index >= NumSamples)
391 | return 0.0;
392 | else if (Index <= NumSamples / 2)
393 | return (double)Index / (double)NumSamples;
394 |
395 | return -(double)(NumSamples - Index) / (double)NumSamples;
396 | }
397 |
398 | public double Index_to_frequency(int Index)
399 | {
400 | if (Index >= NumSamples)
401 | return 0.0;
402 | else if (Index <= NumSamples / 2)
403 | return (double)Index / (double)NumSamples;
404 |
405 | return -(double)(NumSamples - Index) / (double)NumSamples;
406 | }
407 | }
408 | }
409 |
--------------------------------------------------------------------------------
/SoftInterleaver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace MELPeModem
6 | {
7 | abstract class Interleaver : DataProcessingModule
8 | {
9 | InputPin EncodeIn;
10 | OutputPin EncodeOut;
11 |
12 | InputPin DecodeIn;
13 | OutputPin DecodeOut;
14 |
15 |
16 | protected int SizeX, SizeY;
17 |
18 | int PutX, PutY;
19 | int GetX, GetY;
20 | protected int ColumnPutIncrement;
21 | protected int ColumnGetIncrement;
22 | int TotalLength;
23 |
24 | protected byte[,] Storage;
25 | Queue OutputData;
26 | protected int PutCounter = 0;
27 | protected int GetCounter = 0;
28 | protected Interleaver(int SizeX, int SizeY)
29 | {
30 | this.SizeX = SizeX;
31 | this.SizeY = SizeY;
32 | Storage = new byte[SizeX, SizeY];
33 | TotalLength = SizeX * SizeY;
34 | OutputData = new Queue(TotalLength);
35 | Clear();
36 | }
37 | protected Interleaver(int SizeX, int SizeY, int PutInc, int GetInc)
38 | : this(SizeX, SizeY)
39 | {
40 | this.ColumnPutIncrement = PutInc;
41 | this.ColumnGetIncrement = GetInc;
42 | }
43 |
44 | void Clear()
45 | {
46 | Array.Clear(Storage, 0, TotalLength);
47 | PutX = PutY = 0;
48 | GetX = GetY = 0;
49 |
50 | PutCounter = SizeX * SizeY;
51 | GetCounter = SizeX * SizeY;
52 | }
53 |
54 | public override void Init()
55 | {
56 | Clear();
57 | }
58 |
59 | protected virtual void NextPutPos(ref int Column, ref int Row)
60 | {
61 | Column += ColumnPutIncrement;
62 | if (Column >= SizeX)
63 | {
64 | Column -= SizeX;
65 | }
66 | else if (Column < 0)
67 | {
68 | Column += SizeX;
69 | }
70 | }
71 |
72 | protected virtual void NextGetPos(ref int Column, ref int Row)
73 | {
74 | Column += ColumnGetIncrement;
75 | if (Column >= SizeX)
76 | {
77 | Column -= SizeX;
78 | }
79 | else if (Column < 0)
80 | {
81 | Column += SizeX;
82 | }
83 | }
84 |
85 | byte this[int X, int Y]
86 | {
87 | get { return Storage[X, Y]; }
88 | set { Storage[X, Y] = value; }
89 | }
90 |
91 | protected virtual void ProcessEncode()
92 | {
93 | }
94 |
95 | protected virtual void ProcessDecode()
96 | {
97 | }
98 |
99 | public void ProcessEncode(CNTRL_MSG controlParam, byte dataByte)
100 | {
101 | if (controlParam == CNTRL_MSG.DATA_IN)
102 | {
103 | Storage[PutX, PutY] = dataByte;
104 | NextPutPos(ref PutX, ref PutY);
105 | PutCounter--;
106 | if (PutCounter == 0)
107 | {
108 | ProcessEncode();
109 | EncodeOut.Process(CNTRL_MSG.INTERLEAVER_FRAME);
110 | while (GetCounter > 0)
111 | {
112 | EncodeOut.Process(Storage[GetX, GetY]);
113 | NextGetPos(ref GetX, ref GetY);
114 | GetCounter--;
115 | }
116 | this.Init();
117 | }
118 | }
119 | }
120 |
121 | public void ProcessDecode(CNTRL_MSG controlParam, byte dataByte)
122 | {
123 | if (controlParam == CNTRL_MSG.DATA_IN)
124 | {
125 | Storage[GetX, GetY] = dataByte;
126 | NextGetPos(ref GetX, ref GetY);
127 | GetCounter--;
128 | if (GetCounter == 0)
129 | {
130 | ProcessDecode();
131 | DecodeOut.Process(CNTRL_MSG.INTERLEAVER_FRAME);
132 | while (PutCounter > 0)
133 | {
134 | DecodeOut.Process(Storage[PutX, PutY]);
135 | NextPutPos(ref PutX, ref PutY);
136 | PutCounter--;
137 | }
138 | this.Init();
139 | }
140 | }
141 | }
142 |
143 | public void ProcessEncode(byte dataByte)
144 | {
145 | Storage[PutX, PutY] = dataByte;
146 | NextPutPos(ref PutX, ref PutY);
147 | PutCounter--;
148 | if (PutCounter == 0)
149 | {
150 | ProcessEncode();
151 | while (GetCounter > 0)
152 | {
153 | OutputData.Enqueue(Storage[GetX, GetY]);
154 | NextGetPos(ref GetX, ref GetY);
155 | GetCounter--;
156 | }
157 | this.Init();
158 | }
159 | }
160 |
161 | public void ProcessDecode(byte dataByte)
162 | {
163 | Storage[GetX, GetY] = dataByte;
164 | NextGetPos(ref GetX, ref GetY);
165 | GetCounter--;
166 | if (GetCounter == 0)
167 | {
168 | ProcessDecode();
169 | while (PutCounter > 0)
170 | {
171 | OutputData.Enqueue(Storage[PutX, PutY]);
172 | NextPutPos(ref PutX, ref PutY);
173 | PutCounter--;
174 | }
175 | this.Init();
176 | }
177 | }
178 |
179 | public int EncodeBitsAvailable
180 | {
181 | get { return PutCounter; }
182 | }
183 |
184 | public int DecodeBitsAvailable
185 | {
186 | get { return GetCounter; }
187 | }
188 |
189 | public bool IsDataReady { get { return (OutputData.Count > 0);} }
190 |
191 | public int Length { get { return this.TotalLength; } }
192 |
193 | public int Count { get { return OutputData.Count; } }
194 |
195 | public byte GetData()
196 | {
197 | return OutputData.Dequeue();
198 | }
199 |
200 | public int GetData(byte[] outputArray)
201 | {
202 | int ret = this.OutputData.Count;
203 | OutputData.CopyTo(outputArray, 0);
204 | OutputData.Clear();
205 | return ret;
206 | }
207 |
208 | public int GetData(byte[] outputArray, int startingIndex)
209 | {
210 | int ret = this.OutputData.Count;
211 | OutputData.CopyTo(outputArray, startingIndex);
212 | OutputData.Clear();
213 | return ret;
214 | }
215 |
216 | public override void SetModuleParameters()
217 | {
218 | EncodeIn = new InputPin("EncIn", this.ProcessEncode);
219 | DecodeIn = new InputPin("DecIn", this.ProcessDecode);
220 | EncodeOut = new OutputPin("EncOut");
221 | DecodeOut = new OutputPin("DecOut");
222 | base.SetIOParameters("Interleaver EncoderDecoder", new DataPin[] { EncodeIn, DecodeIn, EncodeOut, DecodeOut });
223 | }
224 | }
225 |
226 | class Interleaver_188_110A : Interleaver
227 | {
228 | int PreviousColumn = 0;
229 | int RowPutIncrement = 9;
230 | int ColumnGetDecrement = 17;
231 |
232 | public Interleaver_188_110A(int XSize, int YSize)
233 | : base(XSize, YSize)
234 | {
235 | if (YSize < 40) // Special values for 75 bps
236 | {
237 | RowPutIncrement = 7;
238 | ColumnGetDecrement = 7;
239 | }
240 | }
241 |
242 | public override void Init()
243 | {
244 | PreviousColumn = 0;
245 | base.Init();
246 | }
247 |
248 | //Unknown data bits shall be loaded into the interleaver matrix starting at column zero as follows:
249 | //the first bit is loaded into row 0, the next bit is loaded into row 9, the third bit is loaded into row
250 | //18, and the fourth bit into row 27. Thus, the row location for the bits increases by 9 modulo 40.
251 | //This process continues until all 40 rows are loaded. The load then advances to column 1 and the
252 | //process is repeated until the matrix block is filled.
253 | protected override void NextPutPos(ref int Column, ref int Row)
254 | {
255 | Row += RowPutIncrement;
256 | if (Row >= SizeY)
257 | Row -= SizeY;
258 | if (Row == 0) Column += 1;
259 | }
260 |
261 | //The fetching sequence for all rates shall start with the first bit being taken from row zero, column
262 | //zero. The location of each successive fetched bit shall be determined by incrementing the row by
263 | //one and decrementing the column number by 17 (modulo number of columns in the interleaver
264 | //matrix). Thus, for 2400 bps with a long interleave setting, the second bit comes from row 1,
265 | //column 559, and the third bit from row 2, column 542. This interleaver fetch shall continue until
266 | //the row number reaches the maximum value. At this point, the row number shall be reset to zero,
267 | //the column number is reset to be one larger than the value it had when the row number was last
268 | //zero and the process continued until the entire matrix data block is unloaded.
269 | protected override void NextGetPos(ref int Column, ref int Row)
270 | {
271 | Row += 1; Column -= ColumnGetDecrement;
272 | if (Column < 0)
273 | Column += SizeX;
274 | if (Row >= SizeY)
275 | {
276 | Row = 0;
277 | Column = PreviousColumn + 1;
278 | PreviousColumn = Column;
279 | }
280 | }
281 | }
282 |
283 | class Interleaver_188_110A_4800 : Interleaver
284 | {
285 | public Interleaver_188_110A_4800()
286 | : base(1440, 1)
287 | {
288 | base.ColumnGetIncrement = 1;
289 | base.ColumnPutIncrement = 1;
290 | }
291 | }
292 |
293 | class Interleaver_188_110B_39 : Interleaver
294 | {
295 | int RSSymbolSize = 0;
296 | int Interleave = 0;
297 | int DataRows = 0; // How many Data symbols are needed to generate Parity symbols/rows
298 | int ParityRows = 0; // How many RS parity symbols/rows will be added
299 | int PutDataX;
300 | int[] WorkArray;
301 | int[] ParityArray;
302 | int[] ErasuresArray;
303 |
304 | ReedSolomon RS;
305 |
306 | public Interleaver_188_110B_39(int InterleaveDegree, int SuperblocksNumber,
307 | int RSSymbolSize, int RSSymbolNumber, int RSDataNumber)
308 | : base(RSSymbolSize * InterleaveDegree * SuperblocksNumber, RSSymbolNumber)
309 | {
310 | this.RSSymbolSize = RSSymbolSize;
311 | Interleave = InterleaveDegree * RSSymbolSize;
312 | PutDataX = Math.Min(Interleave, 8);
313 | DataRows = RSDataNumber;
314 | ParityRows = RSSymbolNumber - DataRows;
315 | WorkArray = new int[DataRows + ParityRows];
316 | ParityArray = new int[ParityRows];
317 | ErasuresArray = new int[DataRows + ParityRows];
318 | RS = new ReedSolomon();
319 | RS.Init(RSSymbolSize, 0x13, 1, 1,
320 | ParityRows /* how many parity symbols */,
321 | ((1 << RSSymbolSize) - 1) - (DataRows + ParityRows) /* how many pads */ );
322 | Init();
323 | }
324 |
325 | public override void Init()
326 | {
327 | base.Init();
328 | base.PutCounter = SizeX * DataRows; // Place 3 data symbols in case of RS(7, 3) or
329 | // 10 data symbols in case of RS(14, 10)
330 | }
331 |
332 | protected override void ProcessEncode()
333 | {
334 | int Col = 0;
335 | int Data = 0;
336 | while (Col < SizeX)
337 | {
338 | for (int row = 0; row < DataRows; row++)
339 | {
340 | Data = 0;
341 | for (int i = 0; i < RSSymbolSize; i++)
342 | Data |= (Storage[Col + i, row] & 0x0001) << i;
343 | WorkArray[row] = Data;
344 | }
345 |
346 | RS.Encode(WorkArray, ParityArray);
347 | for (int row = DataRows; row < SizeY; row++)
348 | {
349 | Data = ParityArray[row - DataRows];
350 | for (int i = 0; i < RSSymbolSize; i++)
351 | {
352 | Storage[Col + i, row] = (byte)(Data & 0x0001);
353 | Data >>= 1;
354 | }
355 |
356 | }
357 | Col += RSSymbolSize;
358 | }
359 | }
360 |
361 | protected override void ProcessDecode()
362 | {
363 | int Col = 0;
364 | int Data = 0;
365 | while (Col < SizeX)
366 | {
367 | for (int row = 0; row < SizeY; row++)
368 | {
369 | Data = 0;
370 | for (int i = 0; i < RSSymbolSize; i++)
371 | Data |= (Storage[Col + i, row] & 0x0001) << i;
372 | WorkArray[row] = Data;
373 | }
374 |
375 | RS.Decode(WorkArray, ErasuresArray, 0); // Currently we do not support erasures - should add later
376 | for (int row = 0; row < DataRows; row++)
377 | {
378 | Data = WorkArray[row];
379 | for (int i = 0; i < RSSymbolSize; i++)
380 | {
381 | Storage[Col + i, row] = (byte)(Data & 0x0001);
382 | Data >>= 1;
383 | }
384 | }
385 | Col += RSSymbolSize;
386 | }
387 | }
388 |
389 | protected override void NextGetPos(ref int Column, ref int Row)
390 | {
391 | Column += 1;
392 | if ((Column % Interleave) == 0)
393 | {
394 | Column -= Interleave;
395 | Row += 1;
396 | }
397 | if (Row >= SizeY)
398 | {
399 | Row = 0;
400 | Column += Interleave;
401 | }
402 | }
403 |
404 | protected override void NextPutPos(ref int Column, ref int Row)
405 | {
406 | Column += 1;
407 | if ((Column % PutDataX) == 0)
408 | {
409 | Column -= PutDataX;
410 | Row += 1;
411 | }
412 | else if (Column >= SizeX)
413 | {
414 | Column -= RSSymbolSize;
415 | Row += 1;
416 | }
417 | if (Row >= DataRows)
418 | {
419 | Row = 0;
420 | Column += PutDataX;
421 | }
422 | }
423 | }
424 |
425 | class Interleaver_188_110B : Interleaver
426 | {
427 | public Interleaver_188_110B(int Size, int Increment)
428 | : base(Size, 1, Increment, 1)
429 | {
430 | }
431 | }
432 | }
433 |
--------------------------------------------------------------------------------
/ReedSolomon.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace MELPeModem
6 | {
7 | /* Stuff common to all the general-purpose Reed-Solomon codecs
8 | * Copyright 2004 Phil Karn, KA9Q
9 | * May be used under the terms of the GNU Lesser General Public License (LGPL)
10 | */
11 | /* The guts of the Reed-Solomon encoder, meant to be #included
12 | * into a function body with the following typedefs, macros and variables supplied
13 | * according to the code parameters:
14 |
15 | * data_t - a typedef for the data symbol
16 | * data_t data[] - array of NN-NROOTS-PAD and type data_t to be encoded
17 | * data_t parity[] - an array of NROOTS and type data_t to be written with parity symbols
18 | * NROOTS - the number of roots in the RS code generator polynomial,
19 | * which is the same as the number of parity symbols in a block.
20 | Integer variable or literal.
21 | *
22 | * NN - the total number of symbols in a RS block. Integer variable or literal.
23 | * PAD - the number of pad symbols in a block. Integer variable or literal.
24 | * ALPHA_TO - The address of an array of NN elements to convert Galois field
25 | * elements in index (log) form to polynomial form. Read only.
26 | * INDEX_OF - The address of an array of NN elements to convert Galois field
27 | * elements in polynomial form to index (log) form. Read only.
28 | * MODNN - a function to reduce its argument modulo NN. May be inline or a macro.
29 | * GENPOLY - an array of NROOTS+1 elements containing the generator polynomial in index form
30 |
31 | * The memset() and memmove() functions are used. The appropriate header
32 | * file declaring these functions (usually ) must be included by the calling
33 | * program.
34 |
35 | * Copyright 2004, Phil Karn, KA9Q
36 | * May be used under the terms of the GNU Lesser General Public License (LGPL)
37 | */
38 |
39 |
40 | //typedef unsigned int data_t;
41 |
42 | //#define MODNN(x) modnn(rs,x)
43 |
44 | //#define MM (rs->mm)
45 | //#define NN (rs->nn)
46 | //#define ALPHA_TO (rs->alpha_to)
47 | //#define INDEX_OF (rs->index_of)
48 | //#define GENPOLY (rs->genpoly)
49 | //#define NROOTS (rs->nroots)
50 | //#define FCR (rs->fcr)
51 | //#define PRIM (rs->prim)
52 | //#define IPRIM (rs->iprim)
53 | //#define PAD (rs->pad)
54 | //#define A0 (NN)
55 |
56 |
57 | class ReedSolomon
58 | {
59 | /* Reed-Solomon codec control block */
60 | int MM; /* Bits per symbol */
61 | int NN; /* Symbols per block (= (1<= NN) {
76 | x -= NN;
77 | x = (x >> MM) + (x & NN);
78 | }
79 | return x;
80 | }
81 |
82 | /* Initialize a Reed-Solomon codec
83 | * symsize = symbol size, bits
84 | * gfpoly = Field generator polynomial coefficients
85 | * fcr = first root of RS code generator polynomial, index form
86 | * prim = primitive element to generate polynomial roots
87 | * nroots = RS code generator polynomial degree (number of roots)
88 | * pad = padding bytes at front of shortened block
89 | */
90 | public void Init(int symsize,int gfpoly,int fcr,int prim, int nroots,int pad)
91 | {
92 | int i, j, sr,root,iprim;
93 |
94 | /* Check parameter ranges */
95 | if(symsize < 0 || symsize > 8*sizeof(int))
96 | goto done;
97 | if(fcr < 0 || fcr >= (1<= (1<= (1<= ((1<genpoly[] by @**(root + x) */
143 | for (j = i; j > 0; j--)
144 | {
145 | if (GENPOLY[j] != 0)
146 | GENPOLY[j] = GENPOLY[j-1] ^ ALPHA_TO[MODNN(INDEX_OF[GENPOLY[j]] + root)];
147 | else
148 | GENPOLY[j] = GENPOLY[j-1];
149 | }
150 | /* rs->genpoly[0] can never be zero */
151 | GENPOLY[0] = ALPHA_TO[MODNN(INDEX_OF[GENPOLY[0]] + root)];
152 | }
153 | /* convert rs->genpoly[] to index form for quicker encoding */
154 | for (i = 0; i <= nroots; i++)
155 | GENPOLY[i] = INDEX_OF[GENPOLY[i]];
156 | done:;
157 | }
158 |
159 | public void Encode(int[] data, int[] parity)
160 | {
161 | int i, j;
162 | int feedback;
163 | Array.Clear(parity, 0, NROOTS);
164 |
165 | for(i=0;i < NN-NROOTS-PAD;i++)
166 | {
167 | feedback = INDEX_OF[data[i] ^ parity[0]];
168 | if(feedback != A0)
169 | { /* feedback term is non-zero */
170 | for(j=1;j 0)
243 | {
244 | /* Init lambda to be the erasure locator polynomial */
245 | lambda[1] = ALPHA_TO[MODNN(PRIM*(NN-1-eras_pos[0]))];
246 | for (i = 1; i < no_eras; i++)
247 | {
248 | u = MODNN(PRIM*(NN-1-eras_pos[i]));
249 | for (j = i+1; j > 0; j--)
250 | {
251 | tmp = INDEX_OF[lambda[j - 1]];
252 | if(tmp != A0)
253 | lambda[j] ^= ALPHA_TO[MODNN(u + tmp)];
254 | }
255 | }
256 | }
257 | for(i=0;i 0; j--)
332 | {
333 | if (reg[j] != A0) {
334 | reg[j] = MODNN(reg[j] + j);
335 | q ^= ALPHA_TO[reg[j]];
336 | }
337 | }
338 | if (q != 0)
339 | continue; /* Not a root */
340 |
341 | /* store root (index-form) and error location number */
342 | root[count] = i;
343 | loc[count] = k;
344 | /* If we've already found max possible roots,
345 | * abort the search to save time
346 | */
347 | if(++count == deg_lambda)
348 | break;
349 | }
350 | if (deg_lambda != count)
351 | {
352 | /*
353 | * deg(lambda) unequal to number of roots => uncorrectable
354 | * error detected
355 | */
356 | retval = count = -1;
357 | goto finish;
358 | }
359 | /*
360 | * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
361 | * x**NROOTS). in index form. Also find deg(omega).
362 | */
363 | deg_omega = deg_lambda-1;
364 | for (i = 0; i <= deg_omega;i++)
365 | {
366 | tmp = 0;
367 | for(j=i;j >= 0; j--)
368 | {
369 | if ((s[i - j] != A0) && (lambda[j] != A0))
370 | tmp ^= ALPHA_TO[MODNN(s[i - j] + lambda[j])];
371 | }
372 | omega[i] = INDEX_OF[tmp];
373 | }
374 |
375 | /*
376 | * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
377 | * inv(X(l))**(FCR-1) and den = lambda_pr(inv(X(l))) all in poly-form
378 | */
379 | for (j = count-1; j >=0; j--)
380 | {
381 | num1 = 0;
382 | for (i = deg_omega; i >= 0; i--)
383 | {
384 | if (omega[i] != A0)
385 | num1 ^= ALPHA_TO[MODNN(omega[i] + i * root[j])];
386 | }
387 | num2 = ALPHA_TO[MODNN(root[j] * (FCR - 1) + NN)];
388 | den = 0;
389 |
390 | /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
391 | for (i = Math.Min(deg_lambda,NROOTS-1) & ~1; i >= 0; i -=2)
392 | {
393 | if(lambda[i+1] != A0)
394 | den ^= ALPHA_TO[MODNN(lambda[i+1] + i * root[j])];
395 | }
396 |
397 | /* Apply error to data */
398 | if ((num1 != 0) && (loc[j] < (NN - PAD)))
399 | {
400 | data[loc[j]] ^= ALPHA_TO[MODNN(INDEX_OF[num1] + INDEX_OF[num2] + NN - INDEX_OF[den])];
401 | }
402 | }
403 | finish:
404 | if(eras_pos != null)
405 | {
406 | for(i=0;i(CNTRL_MSG controlEvent, T inData);
9 | delegate void RegisterFunction(ProcessFunction dataOutFunction);
10 |
11 | class DataPin
12 | {
13 | public string Name;
14 | public bool IsInputPin = false;
15 | }
16 |
17 | class InputPin : DataPin where T : struct
18 | {
19 | public ProcessFunction ProcessFunc;
20 | bool AllowMultipleIn;
21 |
22 | public InputPin(string pinName, ProcessFunction processingFunction)
23 | {
24 | Name = pinName;
25 | IsInputPin = true;
26 | ProcessFunc = processingFunction;
27 | AllowMultipleIn = false;
28 | }
29 |
30 | public InputPin(string pinName, ProcessFunction processingFunction, bool allowMultipleIn)
31 | {
32 | Name = pinName;
33 | IsInputPin = true;
34 | ProcessFunc = processingFunction;
35 | AllowMultipleIn = allowMultipleIn;
36 | }
37 |
38 | public void Process(CNTRL_MSG controlEvent, T inData)
39 | {
40 | if( ProcessFunc != null) this.ProcessFunc(controlEvent, inData);
41 | }
42 |
43 | public bool IsMultipleInAllowed { get { return AllowMultipleIn; } }
44 | }
45 |
46 | class OutputPin : DataPin where T : struct
47 | {
48 | RegisterFunction RegisterFunc;
49 | ProcessFunction ProcessFunc = null;
50 |
51 | public OutputPin(string pinName)
52 | {
53 | Name = pinName;
54 | RegisterFunc = this.DefaultRegister;
55 | }
56 |
57 | public OutputPin(string pinName, RegisterFunction registrationFunction)
58 | {
59 | Name = pinName;
60 | RegisterFunc = registrationFunction;
61 | }
62 |
63 | public void Register(ProcessFunction pFunc)
64 | {
65 | if(RegisterFunc != null) this.RegisterFunc(pFunc);
66 | }
67 |
68 | void DefaultRegister(ProcessFunction pFunc)
69 | {
70 | ProcessFunc = pFunc;
71 | }
72 |
73 |
74 | public void Process(CNTRL_MSG controlEvent, T inData)
75 | {
76 | if( ProcessFunc != null) this.ProcessFunc(controlEvent, inData);
77 | }
78 |
79 | public void Process(T inData)
80 | {
81 | if (ProcessFunc != null) this.ProcessFunc(CNTRL_MSG.DATA_IN, inData);
82 | }
83 |
84 | public void Process(CNTRL_MSG controlEvent)
85 | {
86 | if (ProcessFunc != null) this.ProcessFunc(controlEvent, default(T));
87 | }
88 | }
89 |
90 | interface IDataModule
91 | {
92 | string get_Name();
93 | DataPin[] get_InputPins();
94 | DataPin[] get_OutputPins();
95 | DataPin[] get_Pins();
96 | DataPin get_Pin(string pinName);
97 | void Init();
98 | }
99 |
100 |
101 | abstract class DataProcessingModule : IDataModule
102 | {
103 | public string Name;
104 | List InputPins = new List();
105 | List OutputPins = new List();
106 | DataPin this[string name]
107 | {
108 | get
109 | {
110 | foreach (DataPin dp in InputPins) if (dp.Name == name) return dp;
111 | foreach (DataPin dp in OutputPins) if (dp.Name == name) return dp;
112 | return null;
113 | }
114 | }
115 |
116 | public DataProcessingModule()
117 | {
118 | SetModuleParameters();
119 | }
120 |
121 | public DataProcessingModule(string moduleName)
122 | {
123 | this.Name = moduleName;
124 | SetModuleParameters();
125 | }
126 |
127 | public abstract void SetModuleParameters();
128 | public abstract void Init();
129 |
130 |
131 | protected void SetIOParameters(DataPin[] inputOutputPins)
132 | {
133 | foreach(DataPin pin in inputOutputPins)
134 | {
135 | if(pin.IsInputPin)
136 | InputPins.Add(pin);
137 | else
138 | OutputPins.Add(pin);
139 | }
140 | }
141 |
142 | protected void SetIOParameters(string moduleName, DataPin[] inputOutputPins)
143 | {
144 | Name = moduleName;
145 | foreach (DataPin pin in inputOutputPins)
146 | {
147 | if (pin.IsInputPin)
148 | InputPins.Add(pin);
149 | else
150 | OutputPins.Add(pin);
151 | }
152 | }
153 |
154 | protected void SetIOParameters(DataPin[] inputs, DataPin[] outputs)
155 | {
156 | if (inputs != null) InputPins.AddRange(inputs);
157 | if (outputs != null) OutputPins.AddRange(outputs);
158 | }
159 |
160 | protected void SetIOParameters(string moduleName, DataPin[] inputs, DataPin[] outputs)
161 | {
162 | Name = moduleName;
163 | if (inputs != null) InputPins.AddRange(inputs);
164 | if (outputs != null) OutputPins.AddRange(outputs);
165 | }
166 |
167 |
168 | string IDataModule.get_Name()
169 | {
170 | return Name;
171 | }
172 |
173 | DataPin[] IDataModule.get_InputPins()
174 | {
175 | return InputPins.ToArray();
176 | }
177 |
178 | DataPin[] IDataModule.get_OutputPins()
179 | {
180 | return OutputPins.ToArray();
181 | }
182 |
183 | DataPin[] IDataModule.get_Pins()
184 | {
185 | DataPin[] ret = new DataPin[OutputPins.Count + InputPins.Count];
186 | InputPins.CopyTo(ret, 0);
187 | OutputPins.CopyTo(ret, InputPins.Count);
188 | return ret;
189 | }
190 |
191 | public DataPin get_Pin(string pinName)
192 | {
193 | return this[pinName];
194 | }
195 | }
196 |
197 | enum CNTRL_MSG
198 | {
199 | INIT = 0,
200 | START,
201 | QUEUE_CLEAR,
202 |
203 | DATA_IN = 0x1000,
204 |
205 | NEW_STATE = 0x3000,
206 | NEW_SYMBOL,
207 | NEW_FRAME,
208 | INTERLEAVER_FRAME,
209 | SYMBOL_DETECTED,
210 | SYNC_DETECTED,
211 | EOM_DETECTED,
212 | FINISH,
213 | }
214 |
215 | struct DataPacket where T : struct
216 | {
217 | public CNTRL_MSG Control;
218 | public T Data;
219 | }
220 |
221 | class DataQueue where T : struct
222 | {
223 | Queue> Data = new Queue>();
224 |
225 | public void Init()
226 | {
227 | lock (Data)
228 | {
229 | Data.Clear();
230 | Data.TrimExcess();
231 | }
232 | }
233 |
234 | public void Clear()
235 | {
236 | Init();
237 | }
238 |
239 | public void PutData(T inData)
240 | {
241 | DataPacket dp;
242 | dp.Control = CNTRL_MSG.DATA_IN; dp.Data = inData;
243 | lock (Data)
244 | {
245 | Data.Enqueue(dp);
246 | }
247 | }
248 |
249 | public void Process(CNTRL_MSG inControl, T inData)
250 | {
251 | DataPacket dp;
252 | dp.Control = inControl; dp.Data = inData;
253 | lock (Data)
254 | {
255 | Data.Enqueue(dp);
256 | }
257 | }
258 |
259 | public DataPacket GetData()
260 | {
261 | DataPacket ret;
262 | lock (Data)
263 | {
264 | ret = Data.Dequeue();
265 | }
266 | return ret;
267 | }
268 |
269 | public int GetData(DataPacket[] outDataArray, int startingIndex)
270 | {
271 | int ret;
272 | lock (Data)
273 | {
274 | ret = Data.Count;
275 | Data.CopyTo(outDataArray, startingIndex);
276 | Data.Clear();
277 | }
278 | return ret;
279 | }
280 |
281 | public int Count { get { return Data.Count; } }
282 |
283 | }
284 |
285 | class ConnectionEntry where T: struct
286 | {
287 | public OutputPin AssociatedOutputPin;
288 | DataQueue q = new DataQueue();
289 | List> InputProcessFunctions = new List>();
290 |
291 | public ConnectionEntry(OutputPin outputPin)
292 | {
293 | AssociatedOutputPin = outputPin;
294 | outputPin.Register(q.Process);
295 | }
296 |
297 | public void AddInput(InputPin inputPin)
298 | {
299 | ProcessFunction pf = inputPin.ProcessFunc;
300 | if (!InputProcessFunctions.Contains(pf))
301 | {
302 | InputProcessFunctions.Add(pf);
303 | }
304 | }
305 |
306 | public void Init()
307 | {
308 | q.Clear();
309 | }
310 |
311 | public void Process(CNTRL_MSG inControl, T inData)
312 | {
313 | foreach (ProcessFunction f in InputProcessFunctions)
314 | {
315 | f(inControl, inData);
316 | }
317 | }
318 |
319 | public void Process()
320 | {
321 | DataPacket dp;
322 | while(q.Count > 0)
323 | {
324 | dp = q.GetData();
325 | Process(dp.Control, dp.Data);
326 | }
327 | }
328 | public int Count { get { return q.Count; } }
329 | }
330 |
331 |
332 | class DataConnections
333 | {
334 | System.Collections.ArrayList ConnectionsArray = new System.Collections.ArrayList();
335 | bool ContinueProcessing;
336 |
337 | public void Init()
338 | {
339 | foreach (object conn in ConnectionsArray)
340 | {
341 | if (conn is ConnectionEntry)
342 | {
343 | ((ConnectionEntry)conn).Init();
344 | }
345 | else if (conn is ConnectionEntry)
346 | {
347 | ((ConnectionEntry)conn).Init();
348 | }
349 | else if (conn is ConnectionEntry)
350 | {
351 | ((ConnectionEntry)conn).Init();
352 | }
353 | else if (conn is ConnectionEntry)
354 | {
355 | ((ConnectionEntry)conn).Init();
356 | }
357 | }
358 | Process(CNTRL_MSG.QUEUE_CLEAR);
359 | }
360 |
361 | public void Add(OutputPin pinA, InputPin pinB) where T:struct
362 | {
363 | foreach (object conn in ConnectionsArray)
364 | {
365 | ConnectionEntry ce = conn as ConnectionEntry;
366 | if ( (ce != null) && (ce.AssociatedOutputPin == pinA))
367 | {
368 | ce.AddInput(pinB);
369 | return;
370 | }
371 | }
372 | // No output pins were found - create a new queue
373 | ConnectionEntry newce = new ConnectionEntry(pinA);
374 | newce.AddInput(pinB);
375 | ConnectionsArray.Add(newce);
376 | }
377 |
378 | public void Add(DataPin pinA, DataPin pinB) where T : struct
379 | {
380 | if (pinA.IsInputPin == pinB.IsInputPin)
381 | return;
382 |
383 | OutputPin pOut = (pinA.IsInputPin ? pinB : pinA) as OutputPin;
384 | InputPin pIn = (pinA.IsInputPin ? pinA : pinB) as InputPin;
385 |
386 | foreach (object conn in ConnectionsArray)
387 | {
388 | ConnectionEntry ce = conn as ConnectionEntry;
389 | if ((ce != null) && (ce.AssociatedOutputPin == pOut))
390 | {
391 | ce.AddInput(pIn);
392 | return;
393 | }
394 | }
395 | // No output pins were found - create a new queue
396 | ConnectionEntry newce = new ConnectionEntry(pOut);
397 | newce.AddInput(pIn);
398 | ConnectionsArray.Add(newce);
399 | }
400 |
401 | public void Process(CNTRL_MSG inControl)
402 | {
403 | foreach (object conn in ConnectionsArray)
404 | {
405 | if (conn is ConnectionEntry)
406 | {
407 | ((ConnectionEntry)conn).Process(inControl, 0);
408 | }
409 | else if (conn is ConnectionEntry)
410 | {
411 | ((ConnectionEntry)conn).Process(inControl, 0);
412 | }
413 | else if (conn is ConnectionEntry)
414 | {
415 | ((ConnectionEntry)conn).Process(inControl, 0);
416 | }
417 | else if (conn is ConnectionEntry)
418 | {
419 | ((ConnectionEntry)conn).Process(inControl, IQ.ZERO);
420 | }
421 | }
422 | }
423 |
424 | public void Process(bool exitWhenQueuesEmpty)
425 | {
426 | ContinueProcessing = true;
427 |
428 | Process(CNTRL_MSG.INIT);
429 | Init();
430 | Process(CNTRL_MSG.START);
431 | while (ContinueProcessing)
432 | {
433 | foreach (object conn in ConnectionsArray)
434 | {
435 | if (conn is ConnectionEntry)
436 | {
437 | ((ConnectionEntry)conn).Process();
438 | }
439 | else if (conn is ConnectionEntry)
440 | {
441 | ((ConnectionEntry)conn).Process();
442 | }
443 | else if (conn is ConnectionEntry)
444 | {
445 | ((ConnectionEntry)conn).Process();
446 | }
447 | else if (conn is ConnectionEntry)
448 | {
449 | ((ConnectionEntry)conn).Process();
450 | }
451 | }
452 |
453 | if (exitWhenQueuesEmpty)
454 | {
455 | int TotalNumberInQueues = 0;
456 | foreach (object conn in ConnectionsArray)
457 | {
458 | if (conn is ConnectionEntry)
459 | {
460 | TotalNumberInQueues += ((ConnectionEntry)conn).Count;
461 | }
462 | else if (conn is ConnectionEntry)
463 | {
464 | TotalNumberInQueues += ((ConnectionEntry)conn).Count;
465 | }
466 | else if (conn is ConnectionEntry)
467 | {
468 | TotalNumberInQueues += ((ConnectionEntry)conn).Count;
469 | }
470 | else if (conn is ConnectionEntry)
471 | {
472 | TotalNumberInQueues += ((ConnectionEntry)conn).Count;
473 | }
474 | }
475 | // Now we have the total number of data elements in all queues
476 | if (TotalNumberInQueues == 0)
477 | {
478 | ContinueProcessing = false;
479 | }
480 | }
481 | }
482 | }
483 |
484 | public void Stop()
485 | {
486 | ContinueProcessing = false;
487 | }
488 | }
489 | }
490 |
--------------------------------------------------------------------------------
/ToneDetector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace MELPeModem
6 | {
7 | class OFDMDemodulator : DataProcessingModule
8 | {
9 | InputPin DataIn;
10 | OutputPin DataOut;
11 |
12 | Queue OutputData;
13 |
14 | int NFREQ;
15 | float[] Frequencies;
16 | float[] InitialPhases;
17 |
18 | float FreqAdj = 0;
19 |
20 | float SamplingFreq;
21 | int BlockSize;
22 | int ActiveSize;
23 | int CurrFreqIndex = 0;
24 |
25 |
26 | Quad[] IQGens; // Carrier generators
27 | IntegrateAndDump[] IQDemod; // Simple Integrate and dump demodulator
28 |
29 | IQ[] CorrRotate;
30 |
31 | public OFDMDemodulator(float lowFreq, float highFreq, int numFreq, float processingRate, float symbolRate, float activePart)
32 | {
33 | NFREQ = numFreq;
34 | SamplingFreq = processingRate;
35 | BlockSize = (int)((processingRate / symbolRate) + 0.5f);
36 | ActiveSize = (int)(BlockSize * activePart + 0.5f);
37 | if ((int)(BlockSize * symbolRate + 0.5f) != (int)processingRate)
38 | {
39 | throw new ApplicationException("The processingRate must be integer multiple of symbolRate");
40 | }
41 | IQGens = new Quad[NFREQ];
42 | IQDemod = new IntegrateAndDump[NFREQ];
43 | Frequencies = new float[NFREQ]; // array of all frequencies
44 | InitialPhases = new float[NFREQ];
45 |
46 | CorrRotate = new IQ[NFREQ];
47 |
48 | // Evenly distribute all frequencies between Hi and Lo
49 | float OneChannelBW;
50 | if (NFREQ <= 1)
51 | {
52 | OneChannelBW = (highFreq + lowFreq) / 2;
53 | lowFreq = OneChannelBW;
54 | }
55 | else
56 | {
57 | OneChannelBW = (highFreq - lowFreq) / (NFREQ - 1);
58 | }
59 |
60 | for (int i = 0; i < NFREQ; i++)
61 | {
62 | float Freq = lowFreq + i * OneChannelBW;
63 | Frequencies[i] = Freq;
64 | }
65 | Array.Clear(InitialPhases, 0, NFREQ);
66 | Init();
67 | }
68 |
69 | public OFDMDemodulator(float[] freqArray, int numFreq, float processingRate, float symbolRate, float activePart)
70 | {
71 | NFREQ = numFreq;
72 | SamplingFreq = processingRate;
73 | BlockSize = (int)( (processingRate / symbolRate) + 0.5f);
74 | ActiveSize = (int)(BlockSize * activePart + 0.5f);
75 | if ((int)(BlockSize * symbolRate + 0.5f) != (int)processingRate)
76 | {
77 | throw new ApplicationException("The processingRate must be integer multiple of symbolRate");
78 | }
79 | IQGens = new Quad[NFREQ];
80 | IQDemod = new IntegrateAndDump[NFREQ];
81 | Frequencies = new float[NFREQ]; // array of all frequencies
82 | InitialPhases = new float[NFREQ];
83 |
84 | CorrRotate = new IQ[NFREQ];
85 |
86 | freqArray.CopyTo(Frequencies, 0);
87 | Array.Clear(InitialPhases, 0, NFREQ);
88 | Init();
89 | }
90 |
91 | public override void Init()
92 | {
93 | for (int i = 0; i < NFREQ; i++)
94 | {
95 | IQGens[i] = new Quad(Frequencies[i], SamplingFreq, InitialPhases[i]);
96 | IQDemod[i] = new IntegrateAndDump(SYNC_TYPE.GARDNER_DD | SYNC_TYPE.MUELLER_NDA | SYNC_TYPE.QAMLD_NDA | SYNC_TYPE.ZERODET_NDA, BlockSize, ActiveSize);
97 | CorrRotate[i] = IQ.UNITY;
98 | }
99 | FreqAdj = 0;
100 | CurrFreqIndex = 0;
101 | OutputData = new Queue();
102 | }
103 |
104 | public void Init(float[] Phases)
105 | {
106 | Phases.CopyTo(InitialPhases, 0);
107 | Init();
108 | }
109 |
110 | public float FrequencyOffset
111 | {
112 | get { return FreqAdj; }
113 | set
114 | {
115 | FreqAdj = value;
116 | for (int i = 0; i < NFREQ; i++)
117 | {
118 | IQGens[i] = new Quad(Frequencies[i] + FreqAdj, SamplingFreq, IQGens[i]);
119 | }
120 | }
121 | }
122 |
123 | public int Index
124 | {
125 | get { return CurrFreqIndex; }
126 | set { CurrFreqIndex = value; }
127 | }
128 |
129 | public OFDMDemodulator this[int freqIndex]
130 | {
131 | get { CurrFreqIndex = freqIndex; return this; }
132 | }
133 |
134 | public OFDMDemodulator this[float freq]
135 | {
136 | get
137 | {
138 | CurrFreqIndex = Array.IndexOf(Frequencies, freq);
139 | return this;
140 | }
141 | }
142 |
143 | public float Frequency
144 | {
145 | get { return Frequencies[CurrFreqIndex]; }
146 | set
147 | {
148 | Frequencies[CurrFreqIndex] = value;
149 | IQGens[CurrFreqIndex] = new Quad(value, SamplingFreq, IQGens[CurrFreqIndex]);
150 | }
151 | }
152 |
153 | public float Phase
154 | {
155 | get { return IQGens[CurrFreqIndex].Value.Phase; }
156 | set
157 | {
158 | IQGens[CurrFreqIndex] = new Quad(Frequencies[CurrFreqIndex], SamplingFreq, value);
159 | }
160 | }
161 |
162 | public int Process(float[] incomingData, int sampleIndex, int numSamples)
163 | {
164 | while (numSamples-- > 0)
165 | {
166 | Process(incomingData[sampleIndex++]);
167 | }
168 | return OutputData.Count;
169 | }
170 |
171 | public int Process(float incomingSample)
172 | {
173 | IQ Data;
174 |
175 | bool DataReady = true;
176 | for (int idx = 0; idx < NFREQ; idx++)
177 | {
178 | IQGens[idx].Process(incomingSample, out Data);
179 | IQDemod[idx].Process(Data);
180 | DataReady = DataReady && (IQDemod[idx].Count > 0);
181 | }
182 | if (DataReady)
183 | {
184 | for (int idx = 0; idx < NFREQ; idx++)
185 | {
186 | Data = IQDemod[idx].GetData() * CorrRotate[idx];
187 | OutputData.Enqueue(Data);
188 | }
189 | }
190 | return OutputData.Count;
191 | }
192 |
193 | public void Process(CNTRL_MSG controlParam, float incomingSample)
194 | {
195 | if (controlParam == CNTRL_MSG.DATA_IN)
196 | {
197 | IQ Data;
198 | bool DataReady = true;
199 | for (int idx = 0; idx < NFREQ; idx++)
200 | {
201 | IQGens[idx].Process(incomingSample, out Data);
202 | IQDemod[idx].Process(Data);
203 | DataReady = DataReady && (IQDemod[idx].Count > 0);
204 | }
205 | if (DataReady)
206 | {
207 | DataOut.Process(CNTRL_MSG.NEW_SYMBOL);
208 | for (int idx = 0; idx < NFREQ; idx++)
209 | {
210 | Data = IQDemod[idx].GetData() * CorrRotate[idx];
211 | DataOut.Process(Data);
212 | }
213 | }
214 | }
215 | }
216 |
217 | ///
218 | /// Correction (Rotation) factor for the IQ data.
219 | ///
220 | public IQ RotateCorrection
221 | {
222 | set
223 | {
224 | this.CorrRotate[CurrFreqIndex] = value;
225 | }
226 | get { return this.CorrRotate[CurrFreqIndex]; }
227 | }
228 |
229 | public float SignalEnergy { get { return IQDemod[CurrFreqIndex].SignalEnergy; } }
230 |
231 | public float FrequencyEnergy { get { return IQDemod[CurrFreqIndex].FrequencyEnergy; } }
232 |
233 | public int Count { get { return OutputData.Count; } }
234 |
235 | public bool IsDataReady { get { return (OutputData.Count > 0); } }
236 |
237 | public int StartingOffset
238 | {
239 | get { return IQDemod[CurrFreqIndex].StartingOffset; }
240 | set { IQDemod[CurrFreqIndex].StartingOffset = value; }
241 | }
242 |
243 | public IQ GetData()
244 | {
245 | return OutputData.Dequeue();
246 | }
247 |
248 | public int GetData(IQ[] outData)
249 | {
250 | int ret = OutputData.Count;
251 | OutputData.CopyTo(outData, 0);
252 | OutputData.Clear();
253 | return ret;
254 | }
255 |
256 | public int GetData(IQ[] outData, int startingIndex)
257 | {
258 | int ret = OutputData.Count;
259 | OutputData.CopyTo(outData, startingIndex);
260 | OutputData.Clear();
261 | return ret;
262 | }
263 |
264 | public override void SetModuleParameters()
265 | {
266 | DataIn = new InputPin("DataIn", this.Process);
267 | DataOut = new OutputPin("DataOut");
268 | base.SetIOParameters("OFDMDemodulator", new DataPin[] { DataIn, DataOut });
269 | }
270 |
271 | }
272 |
273 | class IQDetector : DataProcessingModule
274 | {
275 | InputPin DataIn;
276 | OutputPin DataOut;
277 |
278 | Queue OutputData;
279 |
280 | double InitialPhase;
281 | int InitialValue;
282 | int SymbolsToDetectCorrection;
283 | bool DoTimingCorrection;
284 | bool FirstSymbol;
285 |
286 | float FreqAdj = 0;
287 | IQ CorrRotate = IQ.UNITY;
288 |
289 | float CarrierFrequency;
290 | float SymbolRate;
291 | float SamplingFreq;
292 | int BlockSize;
293 |
294 | Quad IQGenerator; // Carrier generator
295 | IntegrateAndDump IQDemodulator; // Simple Integrate and dump demodulator
296 | IQDecoder IQDecoder; // Simple BPSK decoder
297 |
298 | public IQDetector(float carrierFreq, float processingRate, float symbolRate, double initPhase, int initValue, int symbolsToUseInCorrection, bool useTimingCorrection)
299 | {
300 | CarrierFrequency = carrierFreq;
301 | SamplingFreq = processingRate;
302 | SymbolRate = symbolRate;
303 |
304 | BlockSize = (int)(processingRate / symbolRate + 0.5f);
305 | if ((int)(BlockSize * symbolRate + 0.5f) != (int)processingRate)
306 | {
307 | throw new ApplicationException("The processingRate must be integer multiple of symbolRate");
308 | }
309 | InitialPhase = initPhase;
310 | InitialValue = initValue;
311 | SymbolsToDetectCorrection = symbolsToUseInCorrection;
312 | DoTimingCorrection = useTimingCorrection;
313 | Init();
314 | }
315 |
316 | public IQDetector(float carrierFreq, float processingRate, float symbolRate)
317 | : this(carrierFreq, processingRate, symbolRate, 0, 0, -10, true)
318 | {
319 | }
320 |
321 | public override void Init()
322 | {
323 | IQGenerator = new Quad(CarrierFrequency, SamplingFreq, InitialPhase);
324 | SYNC_TYPE st = DoTimingCorrection ? SYNC_TYPE.GARDNER_DD | SYNC_TYPE.MUELLER_NDA | SYNC_TYPE.QAMLD_NDA | SYNC_TYPE.ZERODET_NDA : SYNC_TYPE.NONE;
325 | IQDemodulator = new IntegrateAndDump(st, BlockSize, BlockSize);
326 | IQDecoder = new IQDecoder(1, Constellation.Bits_Simple_BPSK, Constellation.IQ_Simple_BPSK, EncodingType.NON_DIFF);
327 |
328 | FreqAdj = 0;
329 | CorrRotate = IQ.UNITY;
330 | OutputData = new Queue();
331 | FirstSymbol = true;
332 | }
333 |
334 | public void Init(float initPhase)
335 | {
336 | InitialPhase = initPhase;
337 | Init();
338 | }
339 |
340 | public float FrequencyOffset
341 | {
342 | get { return FreqAdj + (float)((IQDecoder.RotateCorrection.Phase * SymbolRate) / (2.0 * Math.PI)); }
343 | set
344 | {
345 | FreqAdj = value;
346 | IQGenerator = new Quad(CarrierFrequency + FreqAdj, SamplingFreq, IQGenerator);
347 | }
348 | }
349 |
350 | public float Frequency
351 | {
352 | get { return CarrierFrequency; }
353 | set
354 | {
355 | CarrierFrequency = value;
356 | IQGenerator = new Quad(CarrierFrequency + FreqAdj, SamplingFreq, IQGenerator);
357 | }
358 | }
359 |
360 | public int Process(float[] incomingData, int sampleIndex, int numSamples)
361 | {
362 | while (numSamples-- > 0)
363 | {
364 | Process(incomingData[sampleIndex++]);
365 | }
366 | return OutputData.Count;
367 | }
368 |
369 | public int Process(float incomingSample)
370 | {
371 | IQ Data;
372 | IQ Target;
373 | IQ Diff;
374 | int BitData;
375 | IQGenerator.Process(incomingSample, out Data);
376 | IQDemodulator.Process(Data);
377 | while (IQDemodulator.Count > 0)
378 | {
379 | Data = IQDemodulator.GetData();
380 | if (Data != IQ.ZERO)
381 | {
382 | if (FirstSymbol)
383 | {
384 | FirstSymbol = false;
385 | Target = Constellation.IQ_Simple_BPSK[Constellation.Bits_Simple_BPSK[InitialValue]];
386 | Diff = Target / Data;
387 | this.CorrRotate = Diff / Data.R2;
388 | IQDecoder.StartCorrectionProcess(SymbolsToDetectCorrection);
389 | IQDecoder.PreviousIQ = Target;
390 | }
391 | IQDecoder.Process(Data * CorrRotate, out BitData);
392 | OutputData.Enqueue(BitData);
393 | // Re-adjust constellation
394 | Diff = IQDecoder.Target / Data;
395 | this.CorrRotate = Diff / Data.R2;
396 | }
397 | }
398 | return OutputData.Count;
399 | }
400 |
401 | public void Process(CNTRL_MSG controlParam, float incomingSample)
402 | {
403 | if (controlParam == CNTRL_MSG.DATA_IN)
404 | {
405 | IQ Data;
406 | IQ Target;
407 | IQ Diff;
408 | int BitData;
409 | IQGenerator.Process(incomingSample, out Data);
410 | IQDemodulator.Process(Data);
411 | while (IQDemodulator.Count > 0)
412 | {
413 | Data = IQDemodulator.GetData();
414 | if (Data != IQ.ZERO)
415 | {
416 | if (FirstSymbol)
417 | {
418 | FirstSymbol = false;
419 | Target = Constellation.IQ_Simple_BPSK[Constellation.Bits_Simple_BPSK[InitialValue]];
420 | Diff = Target / Data;
421 | this.CorrRotate = Diff / Data.R2;
422 | IQDecoder.StartCorrectionProcess(SymbolsToDetectCorrection);
423 | IQDecoder.PreviousIQ = Target;
424 | }
425 | IQDecoder.Process(Data * CorrRotate, out BitData);
426 | DataOut.Process(BitData);
427 | // Re-adjust constellation
428 | Diff = IQDecoder.Target / Data;
429 | this.CorrRotate = Diff / Data.R2;
430 | }
431 | }
432 | }
433 | }
434 |
435 | public void StartCorrectionProcess(int symbolsToUseInCorrection)
436 | {
437 | SymbolsToDetectCorrection = symbolsToUseInCorrection;
438 | IQDecoder.StartCorrectionProcess(SymbolsToDetectCorrection);
439 | }
440 |
441 |
442 | public float SignalEnergy { get { return IQDemodulator.SignalEnergy; } }
443 |
444 | public float FrequencyEnergy { get { return IQDemodulator.FrequencyEnergy; } }
445 |
446 | public int Count { get { return OutputData.Count; } }
447 |
448 | public int StartingOffset
449 | {
450 | get { return IQDemodulator.StartingOffset; }
451 | set { IQDemodulator.StartingOffset = value; }
452 | }
453 |
454 | public bool IsCorrectionReady
455 | {
456 | get { return IQDecoder.IsCorrectionReady; }
457 | }
458 |
459 | public bool IsDataReady { get { return (OutputData.Count > 0); } }
460 |
461 | public int GetData()
462 | {
463 | return OutputData.Dequeue();
464 | }
465 |
466 | public int GetData(int[] outData)
467 | {
468 | int ret = OutputData.Count;
469 | OutputData.CopyTo(outData, 0);
470 | OutputData.Clear();
471 | return ret;
472 | }
473 |
474 | public int GetData(int[] outData, int startingIndex)
475 | {
476 | int ret = OutputData.Count;
477 | OutputData.CopyTo(outData, startingIndex);
478 | OutputData.Clear();
479 | return ret;
480 | }
481 |
482 | public override void SetModuleParameters()
483 | {
484 | DataIn = new InputPin("DataIn", this.Process);
485 | DataOut = new OutputPin("DataOut");
486 | base.SetIOParameters("Frequency Detector", new DataPin[] { DataIn, DataOut });
487 | }
488 | }
489 | }
490 |
--------------------------------------------------------------------------------
/DataTypes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 |
6 | // The following data types are used in the project
7 | // Sample - the raw sample (float) that goes in/out of the DAC/ADC at SAMPLE_RATE
8 | // IQ - the IQ pair (float) that contain amplitude and phase information at SYMBOL_RATE
9 | // Symbol - the symbol to be encoded/decoded at SYMBOL_RATE
10 | // Value - any intermediate value (float)
11 | // Bits - Symbols packed into the byte array, usually at SYMBOL_RATE * BITS_PER_SYMBOL
12 |
13 | namespace MELPeModem
14 | {
15 | struct IQ
16 | {
17 | public IQ(float phase) {I = (float) Math.Cos(phase); Q = (float) Math.Sin(phase); }
18 | public IQ(float valueI, float valueQ) { I = valueI; Q = valueQ; }
19 | public float I, Q;
20 |
21 | public static IQ ZERO = new IQ(0, 0);
22 | public static IQ UNITY = new IQ(1, 0);
23 | public static IQ DEG45 = new IQ((float)Math.PI/4);
24 |
25 | public float R2
26 | {
27 | get { return I * I + Q * Q; }
28 | }
29 | public float R
30 | {
31 | get { return (float)Math.Sqrt(I * I + Q * Q); }
32 | }
33 |
34 | public IQ N2
35 | {
36 | get
37 | {
38 | float K = this.R2;
39 | return (K > 0) ? this / K : ZERO;
40 | }
41 | }
42 |
43 | public IQ N
44 | {
45 | get
46 | {
47 | float K = this.R;
48 | return (K > 0) ? this / K : ZERO;
49 | }
50 | }
51 |
52 | public IQ P
53 | {
54 | get
55 | {
56 | return new IQ(I*I, Q*Q) ;
57 | }
58 | }
59 |
60 | public IQ C
61 | {
62 | get { return new IQ(I, -Q); }
63 | }
64 |
65 | public float Phase
66 | {
67 | get { return (float)Math.Atan2(Q, I); }
68 | }
69 | public float Degrees
70 | {
71 | get { float ret = (float)(Math.Atan2(Q, I) * 180 / Math.PI); return ret; }
72 | }
73 | public float DeltaSin(IQ prevIQ)
74 | {
75 | return Q * prevIQ.I - I * prevIQ.Q ;
76 | }
77 | public float DeltaCos(IQ prevIQ)
78 | {
79 | return I * prevIQ.I + Q * prevIQ.Q;
80 | }
81 | public float DeltaSinR(IQ prevIQ)
82 | {
83 | return (prevIQ.R > 0) ? (Q * prevIQ.I - I * prevIQ.Q) / prevIQ.R2 : 0;
84 | }
85 | public float DeltaCosR(IQ prevIQ)
86 | {
87 | return (prevIQ.R > 0) ? (Q * prevIQ.Q + I * prevIQ.I) / prevIQ.R2 : 0;
88 | }
89 | public IQ Delta(IQ prevIQ)
90 | {
91 | float rI = (Q * prevIQ.Q + I * prevIQ.I);
92 | float rQ = (Q * prevIQ.I - I * prevIQ.Q);
93 | return new IQ(rI, rQ);
94 | }
95 |
96 | public IQ DeltaR(IQ prevIQ)
97 | {
98 | float K = prevIQ.R2;
99 | if (K > 0)
100 | {
101 | float rI = (Q * prevIQ.Q + I * prevIQ.I) / K;
102 | float rQ = (Q * prevIQ.I - I * prevIQ.Q) / K;
103 | return new IQ(rI, rQ);
104 | }
105 | else
106 | return ZERO;
107 | }
108 |
109 | public override int GetHashCode()
110 | {
111 | return I.GetHashCode() ^ Q.GetHashCode();
112 | }
113 | public override bool Equals(object other)
114 | {
115 | return (other is IQ) && Equals((IQ)other);
116 | }
117 | public static bool operator ==(IQ lhs, IQ rhs)
118 | {
119 | return lhs.Equals(rhs);
120 | }
121 | public static bool operator !=(IQ lhs, IQ rhs)
122 | {
123 | return !lhs.Equals(rhs);
124 | }
125 | private bool Equals(IQ other)
126 | {
127 | return (I == other.I) && (Q == other.Q);
128 | }
129 |
130 |
131 | public static IQ operator *(IQ a, IQ b)
132 | {
133 | float rI = a.I * b.I - a.Q * b.Q;
134 | float rQ = a.I * b.Q + a.Q * b.I;
135 | return new IQ(rI, rQ);
136 | }
137 |
138 | public static IQ operator *(IQ a, float n)
139 | {
140 | return new IQ(a.I * n, a.Q * n);
141 | }
142 |
143 | public static IQ operator *(float n, IQ a)
144 | {
145 | return new IQ(a.I * n, a.Q * n);
146 | }
147 |
148 | public static IQ operator /(IQ a, IQ b)
149 | {
150 | float rI = a.I * b.I + a.Q * b.Q;
151 | float rQ = a.Q * b.I - a.I * b.Q;
152 | return new IQ(rI, rQ);
153 | }
154 |
155 | public static IQ operator /(IQ a, float n)
156 | {
157 | return new IQ(a.I / n, a.Q / n);
158 | }
159 |
160 | public static IQ operator + (IQ a, IQ b)
161 | {
162 | return new IQ(a.I + b.I, a.Q + b.Q);
163 | }
164 | public static IQ operator -(IQ a, IQ b)
165 | {
166 | return new IQ(a.I - b.I, a.Q - b.Q);
167 | }
168 | public static IQ operator -(IQ a)
169 | {
170 | return new IQ(-a.I, - a.Q);
171 | }
172 | }
173 |
174 | struct Index
175 | {
176 | int Current;
177 | int MaxLen;
178 |
179 | public Index(int indexLen)
180 | {
181 | Current = 0; MaxLen = indexLen;
182 | }
183 | public Index(int currIndex, int indexLen)
184 | {
185 | Current = currIndex % indexLen; MaxLen = indexLen;
186 | }
187 |
188 | public void Init()
189 | {
190 | Current = 0;
191 | }
192 |
193 | public static implicit operator int(Index a)
194 | {
195 | return a.Current;
196 | }
197 |
198 | public static Index operator ++(Index a)
199 | {
200 | a.Current = (a.Current + 1) % a.MaxLen;
201 | return a;
202 | }
203 |
204 | public static Index operator --(Index a)
205 | {
206 | a.Current = (a.Current == 0) ? a.MaxLen - 1 : a.Current - 1;
207 | return a;
208 | }
209 |
210 | public static Index operator +(Index a, int b)
211 | {
212 | int val = (a.Current + b) % a.MaxLen;
213 | if (val < 0) val += a.MaxLen;
214 | return new Index(val, a.MaxLen);
215 | }
216 |
217 | public static Index operator -(Index a, int b)
218 | {
219 | int val = (a.Current - b) % a.MaxLen;
220 | if (val < 0) val += a.MaxLen;
221 | return new Index(val, a.MaxLen);
222 | }
223 |
224 | public static int operator -(Index a, Index b)
225 | {
226 | int ret = a.Current - b.Current;
227 | if (ret < 0) ret += a.MaxLen;
228 | return ret;
229 | }
230 |
231 | public int Value
232 | {
233 | get { return Current; }
234 | set { Current = value % MaxLen; if (Current < 0) Current += MaxLen; }
235 | }
236 | }
237 |
238 |
239 | struct BitGroup
240 | {
241 | enum Val
242 | {
243 | ZERO = -1,
244 | ONES = -2,
245 | ALT1 = -3,
246 | ALT0 = -4,
247 | COUNT_LSB = -5,
248 | COUNT_MSB = -6,
249 | }
250 | public int Position;
251 | public int Size;
252 |
253 | public BitGroup(int pos, int fieldSize)
254 | {
255 | this.Position = pos;
256 | this.Size = fieldSize;
257 | }
258 |
259 | }
260 |
261 | class DataSpreader : DataProcessingModule where T:struct
262 | {
263 | InputPin DataIn;
264 | OutputPin DataOut;
265 |
266 | int NumBitsIn;
267 | int NumBitsOut;
268 |
269 | T[] DataArray;
270 | BitGroup[] BGArray;
271 |
272 | List OutputData;
273 |
274 | int CurrentInIndex;
275 | int CurrentInCounter;
276 |
277 | int MaxBits;
278 | int MaxBitGroupIndex;
279 |
280 | public DataSpreader(int inputBlockSize, BitGroup[] bitgroupArray)
281 | {
282 | NumBitsIn = inputBlockSize;
283 | MaxBitGroupIndex = bitgroupArray.Length;
284 | BGArray = new BitGroup[MaxBitGroupIndex];
285 | bitgroupArray.CopyTo(BGArray, 0);
286 |
287 | MaxBits = 0;
288 | NumBitsOut = 0;
289 | foreach (BitGroup bg in bitgroupArray)
290 | {
291 | NumBitsOut += bg.Size;
292 | if (bg.Position >= MaxBits)
293 | {
294 | MaxBits = bg.Position;
295 | }
296 | }
297 | MaxBits += inputBlockSize; // Reserve this space for block 0
298 |
299 | DataArray = new T[MaxBits];
300 | OutputData = new List(NumBitsOut);
301 | Init();
302 | }
303 |
304 | public override void Init()
305 | {
306 | Array.Clear(DataArray, 0, MaxBits);
307 | OutputData.Clear();
308 | CurrentInIndex = 0;
309 | CurrentInCounter = 0;
310 | }
311 |
312 | public int Process(T incomingBit)
313 | {
314 | DataArray[CurrentInIndex++] = incomingBit; if (CurrentInIndex >= MaxBits) CurrentInIndex = 0;
315 | CurrentInCounter++;
316 | if (CurrentInCounter >= NumBitsIn)
317 | {
318 | CurrentInCounter = 0;
319 | int CurrentFrame = CurrentInIndex - NumBitsIn; if (CurrentFrame < 0) CurrentFrame += MaxBits;
320 | // Start from the beginning of the input block
321 |
322 | int CurrentOutIndex;
323 | int CurrentOutCounter;
324 | BitGroup bg;
325 |
326 | int CurrentBitGroupIndex = 0;
327 | while (CurrentBitGroupIndex < MaxBitGroupIndex)
328 | {
329 | bg = BGArray[CurrentBitGroupIndex++];
330 | CurrentOutIndex = CurrentFrame - bg.Position; if (CurrentOutIndex < 0) CurrentOutIndex += MaxBits;
331 | CurrentOutCounter = bg.Size;
332 | while (CurrentOutCounter-- > 0)
333 | {
334 | OutputData.Add(DataArray[CurrentOutIndex++]); if (CurrentOutIndex >= MaxBits) CurrentOutIndex = 0;
335 | }
336 | }
337 | }
338 | return this.OutputData.Count;
339 | }
340 |
341 | public void Process(CNTRL_MSG controlParam, T incomingBit)
342 | {
343 | if (controlParam == CNTRL_MSG.DATA_IN)
344 | {
345 | DataArray[CurrentInIndex++] = incomingBit; if (CurrentInIndex >= MaxBits) CurrentInIndex = 0;
346 | CurrentInCounter++;
347 | if (CurrentInCounter >= NumBitsIn)
348 | {
349 | CurrentInCounter = 0;
350 | int CurrentFrame = CurrentInIndex - NumBitsIn; if (CurrentFrame < 0) CurrentFrame += MaxBits;
351 | // Start from the beginning of the input block
352 |
353 | int CurrentOutIndex;
354 | int CurrentOutCounter;
355 | BitGroup bg;
356 |
357 | int CurrentBitGroupIndex = 0;
358 | while (CurrentBitGroupIndex < MaxBitGroupIndex)
359 | {
360 | bg = BGArray[CurrentBitGroupIndex++];
361 | CurrentOutIndex = CurrentFrame - bg.Position; if (CurrentOutIndex < 0) CurrentOutIndex += MaxBits;
362 | CurrentOutCounter = bg.Size;
363 | while (CurrentOutCounter-- > 0)
364 | {
365 | DataOut.Process(DataArray[CurrentOutIndex++]); if (CurrentOutIndex >= MaxBits) CurrentOutIndex = 0;
366 | }
367 | }
368 | }
369 | }
370 | }
371 |
372 | public int Count
373 | {
374 | get { return this.OutputData.Count; }
375 | }
376 |
377 | public bool IsDataReady { get { return this.Count > 0; } }
378 |
379 |
380 | public int GetData(T[] outputArray, int startingIndex)
381 | {
382 | int ret = this.OutputData.Count;
383 | OutputData.CopyTo(outputArray, startingIndex);
384 | OutputData.Clear();
385 | return ret;
386 | }
387 |
388 | public override void SetModuleParameters()
389 | {
390 | DataIn = new InputPin("DataIn", this.Process);
391 | DataOut = new OutputPin("DataOut");
392 | base.SetIOParameters("DataSpreader", new DataPin[] { DataIn, DataOut });
393 | }
394 |
395 | }
396 |
397 |
398 |
399 |
400 | class DataCombiner : DataProcessingModule
401 | {
402 | InputPin DataIn;
403 | OutputPin DataOut;
404 |
405 | int NumBitsOut;
406 | int NumBitsIn;
407 |
408 | byte[] DataArray;
409 | BitGroup[] BGArray;
410 |
411 | List OutputData;
412 |
413 | int CurrentFrame;
414 | int CurrentBitGroupIndex;
415 | int CurrentBGCounter;
416 | int CurrentInIndex;
417 | int CurrentInCounter;
418 |
419 | int MaxBits;
420 | int MaxBitGroupIndex;
421 |
422 | public DataCombiner(int outputBlockSize, BitGroup[] bitgroupArray)
423 | {
424 | NumBitsOut = outputBlockSize;
425 | MaxBitGroupIndex = bitgroupArray.Length;
426 | BGArray = new BitGroup[MaxBitGroupIndex];
427 | bitgroupArray.CopyTo(BGArray, 0);
428 |
429 | // Scan bitgroup array and find the extent of the
430 | // combining
431 | MaxBits = 0;
432 | NumBitsIn = 0;
433 | foreach (BitGroup bg in bitgroupArray)
434 | {
435 | NumBitsIn += bg.Size;
436 | if (bg.Position >= MaxBits)
437 | {
438 | MaxBits = bg.Position;
439 | }
440 | }
441 | MaxBits += outputBlockSize; // Reserve this space for block 0
442 |
443 | DataArray = new byte[MaxBits];
444 | OutputData = new List(NumBitsIn);
445 | Init();
446 | }
447 |
448 | public override void Init()
449 | {
450 | Array.Clear(DataArray, 0, MaxBits);
451 | OutputData.Clear();
452 | CurrentBitGroupIndex = 0;
453 | CurrentInCounter = 0;
454 | CurrentFrame = 0;
455 | BitGroup bg = BGArray[CurrentBitGroupIndex++];
456 | CurrentInIndex = CurrentFrame - bg.Position; if (CurrentInIndex < 0) CurrentInIndex += MaxBits;
457 | CurrentBGCounter = bg.Size;
458 | }
459 |
460 | public void Process(CNTRL_MSG controlParam, byte incomingBit)
461 | {
462 | if (controlParam == CNTRL_MSG.DATA_IN)
463 | {
464 | DataArray[CurrentInIndex++] += (byte)((incomingBit == 0) ? -1 : 1); if (CurrentInIndex >= MaxBits) CurrentInIndex = 0;
465 | CurrentBGCounter--;
466 | if (CurrentBGCounter <= 0)
467 | {
468 | BitGroup bg = BGArray[CurrentBitGroupIndex++]; if (CurrentBitGroupIndex >= MaxBitGroupIndex) CurrentBitGroupIndex = 0;
469 | CurrentInIndex = CurrentFrame - bg.Position; if (CurrentInIndex < 0) CurrentInIndex += MaxBits;
470 | CurrentBGCounter = bg.Size;
471 | }
472 | CurrentInCounter++;
473 | if (CurrentInCounter >= NumBitsIn)
474 | {
475 | CurrentInCounter = 0;
476 | // Output data
477 | for (int i = 0; i < NumBitsOut; i++)
478 | {
479 | DataOut.Process( (byte) ((DataArray[CurrentFrame++] > 0) ? 1 : 0)); if (CurrentFrame >= MaxBits) CurrentFrame = 0;
480 | }
481 | Array.Clear(DataArray, CurrentFrame, NumBitsOut);
482 | }
483 | }
484 | }
485 |
486 | public int Process( byte incomingBit)
487 | {
488 | DataArray[CurrentInIndex++] += (byte)((incomingBit == 0) ? -1 : 1); if (CurrentInIndex >= MaxBits) CurrentInIndex = 0;
489 | CurrentBGCounter--;
490 | if (CurrentBGCounter <= 0)
491 | {
492 | BitGroup bg = BGArray[CurrentBitGroupIndex++]; if (CurrentBitGroupIndex >= MaxBitGroupIndex) CurrentBitGroupIndex = 0;
493 | CurrentInIndex = CurrentFrame - bg.Position; if (CurrentInIndex < 0) CurrentInIndex += MaxBits;
494 | CurrentBGCounter = bg.Size;
495 | }
496 | CurrentInCounter++;
497 | if (CurrentInCounter >= NumBitsIn)
498 | {
499 | CurrentInCounter = 0;
500 | // Output data
501 | for (int i = 0; i < NumBitsOut; i++)
502 | {
503 | OutputData.Add( (byte) ((DataArray[CurrentFrame++] > 0) ? 1 : 0)); if (CurrentFrame >= MaxBits) CurrentFrame = 0;
504 | }
505 | Array.Clear(DataArray, CurrentFrame, NumBitsOut);
506 | }
507 | return this.OutputData.Count;
508 | }
509 |
510 | static void Combine(ref byte originalByte, byte newByte)
511 | {
512 | originalByte += (byte)((newByte == 0) ? -1 : 1);
513 | }
514 |
515 | static void Combine(ref IQ originalData, IQ newData)
516 | {
517 | originalData = originalData + newData;
518 | }
519 |
520 | public int Count
521 | {
522 | get { return OutputData.Count; }
523 | }
524 |
525 | public bool IsDataReady { get { return this.Count > 0; } }
526 |
527 |
528 | public int GetData(byte[] outputArray, int startingIndex)
529 | {
530 | int ret = this.OutputData.Count;
531 | OutputData.CopyTo(outputArray, startingIndex);
532 | OutputData.Clear();
533 | return ret;
534 | }
535 |
536 | public override void SetModuleParameters()
537 | {
538 | DataIn = new InputPin("DataIn", this.Process);
539 | DataOut = new OutputPin("DataOut");
540 | base.SetIOParameters("DataCombiner", new DataPin[] { DataIn, DataOut });
541 | }
542 | }
543 |
544 | }
545 |
--------------------------------------------------------------------------------
/EncodeDecode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace MELPeModem
6 | {
7 | [Flags]
8 | public enum EncodingType
9 | {
10 | NON_DIFF = 0x00,
11 |
12 | DIFF = 0x01,
13 | DIFF_ADD = 0x01,
14 | DIFF_SUB = 0x03,
15 | DIFF_XOR = 0x05,
16 | DIFF_XNOR = 0x07,
17 | DIFF_MASK = 0x07,
18 |
19 | SCRAMBLE = 0x010,
20 | SCRAMBLE_ADD = 0x010,
21 | SCRAMBLE_SUB = 0x030,
22 | SCRAMBLE_XOR = 0x050,
23 | SCRAMBLE_XNOR = 0x070,
24 | SCRAMBLE_MASK = 0x070,
25 |
26 | SAVE_IN1 = 0x0100,
27 | SAVE_IN2 = 0x0300,
28 | SAVE_OUT1 = 0x0500,
29 | SAVE_OUT2 = 0x0700,
30 | SAVE_MASK = 0x0700,
31 |
32 | DIFF_IQ = 0x8000
33 | }
34 |
35 | class IQEncoder
36 | {
37 | int BitsPerSymbol;
38 | int MaxSymbols;
39 | int SymbolMask;
40 |
41 | int[] BitsToSymbol;
42 | IQ[] SymbolToIQ;
43 |
44 | EncodingType EncType;
45 |
46 | int PrevSymbol;
47 | IQ PrevIQ;
48 |
49 | public IQEncoder(int bitsPerSymb, int[] tableBitsToSymb, IQ[] tableSymbToIQ, EncodingType mType)
50 | {
51 | BitsPerSymbol = bitsPerSymb;
52 | MaxSymbols = 1 << bitsPerSymb;
53 | SymbolMask = MaxSymbols - 1;
54 |
55 | BitsToSymbol = new int[tableBitsToSymb.Length];
56 | tableBitsToSymb.CopyTo(BitsToSymbol, 0);
57 |
58 | SymbolToIQ = new IQ[tableSymbToIQ.Length];
59 | tableSymbToIQ.CopyTo(SymbolToIQ, 0);
60 |
61 | EncType = mType;
62 | Init();
63 | }
64 |
65 | public IQEncoder(int bitsPerSymb, int[] tableBitsToSymb,
66 | float[] tableSymbToI, float[] tableSymbToQ, EncodingType mType)
67 | {
68 | BitsPerSymbol = bitsPerSymb;
69 | MaxSymbols = 1 << bitsPerSymb;
70 | SymbolMask = MaxSymbols - 1;
71 |
72 | BitsToSymbol = new int[tableBitsToSymb.Length];
73 | tableBitsToSymb.CopyTo(BitsToSymbol, 0);
74 |
75 | SymbolToIQ = new IQ[tableSymbToI.Length];
76 | for (int i = 0; i < tableSymbToI.Length; i++)
77 | {
78 | SymbolToIQ[i] = new IQ(tableSymbToI[i], tableSymbToQ[i]);
79 | }
80 | EncType = mType;
81 | Init();
82 | }
83 |
84 | public void Init()
85 | {
86 | PrevSymbol = 0;
87 | PrevIQ = IQ.UNITY;
88 | }
89 |
90 | int ProcessIn(int inBits)
91 | {
92 | int newSymbol = BitsToSymbol[inBits & SymbolMask];
93 | int OutSymbol = newSymbol;
94 | if ((EncType & EncodingType.DIFF) != 0)
95 | {
96 | switch (EncType & EncodingType.DIFF_MASK)
97 | {
98 | case EncodingType.DIFF_ADD:
99 | OutSymbol += PrevSymbol;
100 | break;
101 | case EncodingType.DIFF_SUB:
102 | OutSymbol -= PrevSymbol;
103 | break;
104 | case EncodingType.DIFF_XOR:
105 | OutSymbol ^= PrevSymbol;
106 | break;
107 | case EncodingType.DIFF_XNOR:
108 | OutSymbol ^= ~PrevSymbol;
109 | break;
110 | }
111 | switch (EncType & EncodingType.SAVE_MASK)
112 | {
113 | case EncodingType.SAVE_IN1:
114 | PrevSymbol = inBits;
115 | break;
116 | case EncodingType.SAVE_IN2:
117 | PrevSymbol = newSymbol;
118 | break;
119 | case EncodingType.SAVE_OUT1:
120 | PrevSymbol = OutSymbol;
121 | break;
122 | case EncodingType.SAVE_OUT2:
123 | PrevSymbol = OutSymbol;
124 | break;
125 | }
126 | OutSymbol &= SymbolMask;
127 | }
128 | return OutSymbol;
129 | }
130 |
131 | int ProcessScramble(int inBits, int scrambleSymbol, int scrambleMask)
132 | {
133 | int newSymbol = inBits;
134 | if ((EncType & EncodingType.SCRAMBLE) != 0)
135 | {
136 | switch (EncType & EncodingType.SCRAMBLE_MASK)
137 | {
138 | case EncodingType.SCRAMBLE_ADD:
139 | newSymbol += scrambleSymbol;
140 | break;
141 | case EncodingType.SCRAMBLE_SUB:
142 | newSymbol -= scrambleSymbol;
143 | break;
144 | case EncodingType.SCRAMBLE_XOR:
145 | newSymbol ^= scrambleSymbol;
146 | break;
147 | case EncodingType.SCRAMBLE_XNOR:
148 | newSymbol ^= ~scrambleSymbol;
149 | break;
150 | }
151 | if ((EncType & EncodingType.SAVE_MASK) == EncodingType.SAVE_OUT2) PrevSymbol = newSymbol;
152 | newSymbol &= scrambleMask;
153 | }
154 | return newSymbol;
155 | }
156 |
157 | public void Process(int inputBits, out IQ data)
158 | {
159 | int newSymbol = ProcessIn(inputBits);
160 | data = SymbolToIQ[newSymbol];
161 | if ( (EncType & EncodingType.DIFF_IQ) != 0)
162 | {
163 | data = PrevIQ * data;
164 | PrevIQ = data;
165 | }
166 | }
167 |
168 | public void Process(int inputBits, int scrambleSymbol, int scrambleMask, out IQ data)
169 | {
170 | int newSymbol = ProcessIn(inputBits);
171 | newSymbol = ProcessScramble(newSymbol, scrambleSymbol, scrambleMask);
172 | data = SymbolToIQ[newSymbol];
173 | if ((EncType & EncodingType.DIFF_IQ) != 0)
174 | {
175 | data = PrevIQ * data;
176 | PrevIQ = data;
177 | }
178 | }
179 |
180 |
181 | public int PreviousSymbol
182 | {
183 | get { return PrevSymbol; }
184 | set { PrevSymbol = value; }
185 | }
186 |
187 | public IQ PreviousIQ
188 | {
189 | get { return PrevIQ; }
190 | set { PrevIQ = value; }
191 | }
192 |
193 | }
194 |
195 | class IQDecoder
196 | {
197 | int BitsPerSymbol;
198 | int[] BitsToSymbol;
199 | IQ[] SymbolToIQ;
200 | int MaxSymbols;
201 | int SymbolMask;
202 | EncodingType EncType;
203 |
204 | int PrevSymbol;
205 | IQ PrevIQ;
206 | IQ TargetSymbol;
207 |
208 | IQ Rotate = IQ.UNITY;
209 | IQ FreqCorr = IQ.UNITY;
210 | IQ PrevRotate = IQ.UNITY;
211 |
212 | float Energy = 0;
213 |
214 | int SymbolsToProcess;
215 | int CurrentSymbolCnt;
216 |
217 | float Coeff1; // (1 - 1/N);
218 | float Coeff2; // 1/N;
219 |
220 | public IQDecoder(int bitsPerSymb, int[] tableBitsToSymb, IQ[] tableSymbToIQ, EncodingType mType)
221 | {
222 | BitsPerSymbol = bitsPerSymb;
223 | MaxSymbols = 1 << bitsPerSymb;
224 | SymbolMask = MaxSymbols - 1;
225 |
226 | BitsToSymbol = new int[tableBitsToSymb.Length];
227 | tableBitsToSymb.CopyTo(BitsToSymbol, 0);
228 |
229 | SymbolToIQ = new IQ[tableSymbToIQ.Length];
230 | tableSymbToIQ.CopyTo(SymbolToIQ, 0);
231 |
232 | EncType = mType;
233 | Init();
234 | }
235 |
236 | public IQDecoder(int bitsPerSymb, int[] tableBitsToSymb, float[] tableSymbToI, float[] tableSymbToQ, EncodingType mType)
237 | {
238 | BitsPerSymbol = bitsPerSymb;
239 | MaxSymbols = 1 << bitsPerSymb;
240 | SymbolMask = MaxSymbols - 1;
241 |
242 | BitsToSymbol = new int[tableBitsToSymb.Length];
243 | tableBitsToSymb.CopyTo(BitsToSymbol, 0);
244 |
245 | SymbolToIQ = new IQ[tableSymbToI.Length];
246 | for (int i = 0; i < tableSymbToI.Length; i++)
247 | {
248 | SymbolToIQ[i] = new IQ(tableSymbToI[i], tableSymbToQ[i]);
249 | }
250 | EncType = mType;
251 | Init();
252 | }
253 |
254 | public void Init()
255 | {
256 | PrevSymbol = 0;
257 | PrevIQ = IQ.UNITY;
258 | SymbolsToProcess = CurrentSymbolCnt = 0;
259 |
260 | Rotate = IQ.UNITY;
261 | FreqCorr = IQ.UNITY;
262 | PrevRotate = IQ.UNITY;
263 | Energy = 0;
264 | }
265 |
266 | public void StartCorrectionProcess(int numberOfSymbols)
267 | {
268 | SymbolsToProcess = numberOfSymbols;
269 | CurrentSymbolCnt = 0;
270 |
271 | Rotate = IQ.UNITY;
272 | FreqCorr = IQ.UNITY;
273 | PrevRotate = IQ.UNITY;
274 | Energy = 0;
275 |
276 | if (SymbolsToProcess < 0) // This is a continious mode
277 | {
278 | Coeff2 = -1.0f / (float)SymbolsToProcess;
279 | Coeff1 = 1 - Coeff2;
280 | }
281 | }
282 |
283 | public bool IsCorrectionReady
284 | {
285 | get { return (CurrentSymbolCnt >= SymbolsToProcess); }
286 | }
287 |
288 | int ProcessIn(int inBits)
289 | {
290 | int newSymbol = BitsToSymbol[inBits];
291 | int OutSymbol = newSymbol;
292 | if ((EncType & EncodingType.DIFF) != 0)
293 | {
294 | switch (EncType & EncodingType.DIFF_MASK)
295 | {
296 | case EncodingType.DIFF_ADD:
297 | OutSymbol += PrevSymbol;
298 | break;
299 | case EncodingType.DIFF_SUB:
300 | OutSymbol -= PrevSymbol;
301 | break;
302 | case EncodingType.DIFF_XOR:
303 | OutSymbol ^= PrevSymbol;
304 | break;
305 | case EncodingType.DIFF_XNOR:
306 | OutSymbol ^= ~PrevSymbol;
307 | break;
308 | }
309 | OutSymbol &= SymbolMask;
310 | }
311 | return OutSymbol;
312 | }
313 |
314 | int ProcessScramble(int inBits, int scrambleSymbol, int scrambleMask)
315 | {
316 | int newSymbol = inBits;
317 | if ((EncType & EncodingType.SCRAMBLE) != 0)
318 | {
319 | switch (EncType & EncodingType.SCRAMBLE_MASK)
320 | {
321 | case EncodingType.SCRAMBLE_ADD:
322 | newSymbol += scrambleSymbol;
323 | break;
324 | case EncodingType.SCRAMBLE_SUB:
325 | newSymbol -= scrambleSymbol;
326 | break;
327 | case EncodingType.SCRAMBLE_XOR:
328 | newSymbol ^= scrambleSymbol;
329 | break;
330 | case EncodingType.SCRAMBLE_XNOR:
331 | newSymbol ^= ~scrambleSymbol;
332 | break;
333 | }
334 | newSymbol &= scrambleMask;
335 | }
336 | return newSymbol;
337 | }
338 |
339 | int ProcessDiff( int inBits)
340 | {
341 | int newSymbol = BitsToSymbol[inBits];
342 | int OutSymbol = newSymbol;
343 | if ((EncType & EncodingType.DIFF) != 0)
344 | {
345 | switch (EncType & EncodingType.DIFF_MASK)
346 | {
347 | case EncodingType.DIFF_ADD:
348 | OutSymbol += PrevSymbol;
349 | break;
350 | case EncodingType.DIFF_SUB:
351 | OutSymbol -= PrevSymbol;
352 | break;
353 | case EncodingType.DIFF_XOR:
354 | OutSymbol ^= PrevSymbol;
355 | break;
356 | case EncodingType.DIFF_XNOR:
357 | OutSymbol ^= ~PrevSymbol;
358 | break;
359 | }
360 | OutSymbol &= SymbolMask;
361 |
362 | switch (EncType & EncodingType.SAVE_MASK)
363 | {
364 | case EncodingType.SAVE_IN1:
365 | PrevSymbol = inBits;
366 | break;
367 | case EncodingType.SAVE_IN2:
368 | PrevSymbol = newSymbol;
369 | break;
370 | case EncodingType.SAVE_OUT1:
371 | PrevSymbol = OutSymbol;
372 | break;
373 | case EncodingType.SAVE_OUT2:
374 | PrevSymbol = OutSymbol;
375 | break;
376 | }
377 | }
378 | return OutSymbol;
379 | }
380 |
381 | int ProcessDiff(int inBits, int scrambleSymbol, int scrambleMask)
382 | {
383 |
384 | int newSymbol = ProcessDiff(inBits);
385 | if ((EncType & EncodingType.SCRAMBLE) != 0)
386 | {
387 | switch (EncType & EncodingType.SCRAMBLE_MASK)
388 | {
389 | case EncodingType.SCRAMBLE_ADD:
390 | newSymbol += scrambleSymbol;
391 | break;
392 | case EncodingType.SCRAMBLE_SUB:
393 | newSymbol -= scrambleSymbol;
394 | break;
395 | case EncodingType.SCRAMBLE_XOR:
396 | newSymbol ^= scrambleSymbol;
397 | break;
398 | case EncodingType.SCRAMBLE_XNOR:
399 | newSymbol ^= ~scrambleSymbol;
400 | break;
401 | }
402 | if ((EncType & EncodingType.SAVE_MASK) == EncodingType.SAVE_OUT2) PrevSymbol = newSymbol;
403 | newSymbol &= scrambleMask;
404 | }
405 | return newSymbol;
406 | }
407 |
408 | void CalculateCorrections(IQ inputData)
409 | {
410 | IQ AngleError = (TargetSymbol / inputData);
411 | if (SymbolsToProcess < 0) // Continious mode
412 | {
413 | Energy = Energy * Coeff1 + inputData.R2 * Coeff2;
414 | Rotate = Rotate * Coeff1 + AngleError * Coeff2;
415 | FreqCorr = FreqCorr * Coeff1 + (AngleError / PrevRotate) * Coeff2;
416 | }else if (CurrentSymbolCnt < SymbolsToProcess)
417 | {
418 | Energy += inputData.R2;
419 | Rotate += AngleError;
420 | FreqCorr += AngleError / PrevRotate;
421 | CurrentSymbolCnt++;
422 | }
423 | PrevRotate = AngleError;
424 | }
425 |
426 | public void Process(IQ inputData, out int outputSymbol)
427 | {
428 | float MinDistance = float.MaxValue;
429 | int resultSymbol = 0;
430 | IQ Data;
431 |
432 | if ((EncType & EncodingType.DIFF_IQ) != 0)
433 | {
434 | Data = inputData / PrevIQ;
435 | PrevIQ = inputData;
436 | }
437 | else
438 | {
439 | Data = inputData;
440 | }
441 | // Simply go thru every point in constellation and find the best match
442 | for (int currSymbol = 0; currSymbol < MaxSymbols; currSymbol++)
443 | {
444 | int newSymbol = ProcessIn(currSymbol);
445 | IQ targetData = SymbolToIQ[newSymbol];
446 | float Distance = (targetData - Data).R2;
447 | if (Distance < MinDistance)
448 | {
449 | MinDistance = Distance;
450 | resultSymbol = currSymbol;
451 | TargetSymbol = targetData;
452 | }
453 | }
454 | ProcessDiff(resultSymbol);
455 | CalculateCorrections(Data);
456 | outputSymbol = resultSymbol;
457 | }
458 |
459 | public void Process(IQ inputData, int scrambleSymbol, int scrambleMask, out int outputSymbol)
460 | {
461 | float MinDistance = float.MaxValue;
462 | int resultSymbol = 0;
463 | IQ Data;
464 |
465 | if ((EncType & EncodingType.DIFF_IQ) != 0)
466 | {
467 | Data = inputData / PrevIQ;
468 | PrevIQ = inputData;
469 | }
470 | else
471 | {
472 | Data = inputData;
473 | }
474 | // Simply go thru every point in constellation and find the best match
475 | for (int currSymbol = 0; currSymbol < MaxSymbols; currSymbol++)
476 | {
477 | int newSymbol = ProcessIn(currSymbol);
478 | newSymbol = ProcessScramble(newSymbol, scrambleSymbol, scrambleMask);
479 | IQ TargetData = SymbolToIQ[newSymbol];
480 | float Distance = (TargetData - Data).R2;
481 | if (Distance < MinDistance)
482 | {
483 | MinDistance = Distance;
484 | resultSymbol = currSymbol;
485 | TargetSymbol = TargetData;
486 | }
487 | }
488 | ProcessDiff(resultSymbol, scrambleSymbol, scrambleMask);
489 | CalculateCorrections(Data);
490 | outputSymbol = resultSymbol;
491 | }
492 |
493 | ///
494 | /// Correction (Rotation) factor for the IQ data.
495 | ///
496 | public IQ RotateCorrection
497 | {
498 | get
499 | {
500 | return Energy == 0 ? IQ.UNITY : this.Rotate / Energy;
501 | }
502 | }
503 |
504 | ///
505 | /// Correction (Frequency) factor for the IQ data.
506 | ///
507 | public IQ FrequencyCorrection
508 | {
509 | get
510 | {
511 | return this.FreqCorr;
512 | }
513 | }
514 |
515 | public IQ Target
516 | {
517 | get { return TargetSymbol; }
518 | }
519 |
520 | public int PreviousSymbol
521 | {
522 | get { return PrevSymbol; }
523 | set { PrevSymbol = value; }
524 | }
525 |
526 | public IQ PreviousIQ
527 | {
528 | get { return PrevIQ; }
529 | set { PrevIQ = value; }
530 | }
531 |
532 | }
533 |
534 | }
535 |
--------------------------------------------------------------------------------
/LFSRegister.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace MELPeModem
6 | {
7 | public enum ConvEncoderType
8 | {
9 | Truncate, // The encoder does not terminate the stream and does not force into a specific state
10 | ZeroState, // The encoder/decoder that starts and ends with zero state
11 | TailBiting_Head, // Tailbiting encoder/decoder - the start and end states are the same - load with the head.
12 | TailBiting_Tail, // Tailbiting encoder/decoder - the start and end states are the same - load with the tail.
13 | }
14 |
15 | public struct LFSRState
16 | {
17 | public int CurrentState;
18 | public int CurrentCounter;
19 | }
20 |
21 |
22 | class LFSRegister
23 | {
24 | int CurrentState;
25 | int PolyHighBit; // The Order of the polinomial (highest bit)
26 | int Polynomial; // The generator polynomial
27 | int OutputMask; // The output bit(s) mask
28 | static int[] bitcounts =
29 | {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
30 |
31 | protected int Bitcount(int u)
32 | {
33 | int n = 0;
34 | for(; u != 0; u >>= 4)
35 | n += bitcounts[u & 0x0f];
36 | return n;
37 | }
38 |
39 | public LFSRegister(int order, int genPoly)
40 | {
41 | this.PolyHighBit = order - 1;
42 | this.Polynomial = genPoly;
43 | this.OutputMask = genPoly;
44 | }
45 |
46 | public LFSRegister(int order, int genPoly, int outputMask)
47 | {
48 | this.PolyHighBit = order - 1;
49 | this.Polynomial = genPoly;
50 | this.OutputMask = outputMask;
51 | }
52 |
53 | protected void Init(int Seed)
54 | {
55 | CurrentState = Seed;
56 | }
57 |
58 | protected void ShiftFibOneLeft()
59 | {
60 | int NextBit = Bitcount( CurrentState & Polynomial) & 0x01;
61 | CurrentState = (CurrentState << 1) | NextBit;
62 | }
63 |
64 | protected void ShiftFibOneRight()
65 | {
66 | int NextBit = Bitcount(CurrentState & Polynomial) & 0x01;
67 | CurrentState = (CurrentState >> 1) | (NextBit << PolyHighBit) ;
68 | }
69 |
70 | protected void ShiftGalOneRight()
71 | {
72 | int LastBit = (CurrentState & 0x01);
73 | CurrentState = (CurrentState >> 1) | (LastBit << PolyHighBit);
74 | if( LastBit != 0)
75 | CurrentState ^= Polynomial;
76 | }
77 |
78 | protected void ShiftGalOneLeft()
79 | {
80 | int LastBit = (CurrentState >> PolyHighBit) & 0x01;
81 | CurrentState = (CurrentState << 1) | LastBit;
82 | if (LastBit != 0)
83 | CurrentState ^= Polynomial;
84 | }
85 |
86 | protected int ShiftFibLeft(int nCount)
87 | {
88 | for (int i = 0; i < nCount; i++)
89 | ShiftFibOneLeft();
90 | return CurrentState;
91 | }
92 | protected int ShiftFibRight(int nCount)
93 | {
94 | for (int i = 0; i < nCount; i++)
95 | ShiftFibOneRight();
96 | return CurrentState;
97 | }
98 |
99 | protected int ShiftGalRight(int nCount)
100 | {
101 | for (int i = 0; i < nCount; i++)
102 | ShiftGalOneRight();
103 | return CurrentState;
104 | }
105 |
106 | protected int ShiftGalLeft(int nCount)
107 | {
108 | for (int i = 0; i < nCount; i++)
109 | ShiftGalOneLeft();
110 | return CurrentState;
111 | }
112 |
113 | public virtual LFSRState State
114 | {
115 | get { LFSRState s; s.CurrentState = this.CurrentState; s.CurrentCounter = 0; return s; }
116 | set { this.CurrentState = value.CurrentState; }
117 | }
118 |
119 | public int Value { get { return CurrentState; } }
120 |
121 | public int CurrentBit
122 | {
123 | get { return Bitcount(CurrentState & OutputMask); }
124 | }
125 | }
126 |
127 | class LFSR_188_110A : LFSRegister
128 | {
129 | int Counter;
130 | public LFSR_188_110A() : base(12, 0x0052)
131 | {
132 | this.Init();
133 | }
134 |
135 | public void Init()
136 | {
137 | base.Init(0x0BAD);
138 | base.ShiftGalLeft(8);
139 | this.Counter = 160;
140 | }
141 |
142 | public int DataNext()
143 | {
144 | int result = base.Value & 0x0007;
145 | ShiftGalLeft(8);
146 | this.Counter--;
147 | if (this.Counter <= 0)
148 | Init();
149 | return result;
150 | }
151 | public override LFSRState State
152 | {
153 | get { LFSRState st = base.State; st.CurrentCounter = this.Counter; return st; }
154 | set { base.State = value; this.Counter = value.CurrentCounter; }
155 | }
156 | }
157 |
158 | class LFSR__188_110B_39 : LFSRegister
159 | {
160 | public LFSR__188_110B_39(int order, int Polynomial) : base(order, Polynomial, 0x0000001)
161 | {
162 | }
163 | public new void Init(int seed) { base.Init(seed); }
164 | public void Shift() { ShiftFibOneLeft(); }
165 | public void Shift(int nCount) { ShiftFibLeft(nCount); }
166 | }
167 |
168 |
169 | ///
170 | /// Class to implement convolutional encoder 1:R rate with K-constraint
171 | ///
172 | class ConvEncoder : DataProcessingModule
173 | {
174 | InputPin DataIn;
175 | OutputPin DataOut;
176 | byte[] FirstBits;
177 |
178 | Queue OutputData;
179 |
180 | int TailbitingCounter;
181 |
182 | int Rate; // Number of output bits will be (n * Rate)
183 | int K; // Number of bits (including input) that are used to generate output
184 | int m; // number of memory locations
185 | int[] Polynomial;
186 | int PunctureMask;
187 | int PunctureSize;
188 |
189 | int CurrentState;
190 | int CurrentMaskCounter;
191 | ConvEncoderType EncoderType;
192 |
193 | static int[] bitcounts =
194 | { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
195 |
196 | public static int Bitcount(int u)
197 | {
198 | int n = 0;
199 | uint uu = (uint)u;
200 | for (; uu != 0; uu >>= 4)
201 | n += bitcounts[uu & 0x0f];
202 | return n;
203 | }
204 |
205 |
206 | ///
207 | /// Constructor for Rate R convolutional encoder.
208 | ///
209 | /// The number of bits sent out for each input bit.
210 | /// The number of bits (including current symbol) to use in calculations. Equal to Constraint + 1.
211 | /// The array of size [Rate] that has polynomial coefficients.
212 | /// The puncture mask. 1 in position means output bit. 0 means do not output.
213 | /// Total size of the puncture mask.
214 | public ConvEncoder(ConvEncoderType encType, int convRate, int polyDegree, int[] polynomArray, int punctureMask, int punctureMaskSize)
215 | {
216 | this.Rate = convRate;
217 | this.K = polyDegree;
218 | this.m = K - 1;
219 | this.Polynomial = new int[Rate];
220 | Array.Copy(polynomArray, this.Polynomial, Rate);
221 | this.PunctureSize = punctureMaskSize;
222 | this.PunctureMask = punctureMask;
223 | this.EncoderType = encType;
224 | FirstBits = new byte[m];
225 | OutputData = new Queue();
226 | Init();
227 | }
228 |
229 | public void Clear()
230 | {
231 | this.Init();
232 | }
233 |
234 | public override void Init()
235 | {
236 | CurrentState = 0;
237 | CurrentMaskCounter = 0;
238 | TailbitingCounter = m;
239 | OutputData.Clear();
240 | }
241 |
242 | public int State
243 | {
244 | get { return CurrentState; }
245 | set { CurrentState = value; }
246 | }
247 |
248 | public int NextState(int currState, int InputBit)
249 | {
250 | InputBit &= 0x0001;
251 | return (currState >> 1) | (InputBit << (m - 1));
252 | }
253 |
254 | public byte Output(int currState, byte InputBit)
255 | {
256 | int OutByte = 0;
257 | InputBit &= 0x0001;
258 | int Data = currState | (InputBit << m);
259 | // Go thru every polynomial
260 | for (int i = 0; i < this.Rate; i++)
261 | {
262 | OutByte |= ((Bitcount(Data & Polynomial[i]) & 0x0001) << i);
263 | }
264 | return (byte) OutByte;
265 | }
266 |
267 | int Process(byte[] outputArray, ref int outputIndex)
268 | {
269 | int Result = 0;
270 | // Go thru every polynomial and prepare "Rate" bits for each input bit
271 | for (int i = 0; i < this.Rate; i++)
272 | {
273 | if ((PunctureMask & (1 << CurrentMaskCounter)) != 0) // If bit is 1 in the Mask -> place result
274 | {
275 | outputArray[outputIndex++] = (byte)(Bitcount(CurrentState & Polynomial[i]) & 0x0001);
276 | Result++;
277 | }
278 | if (++CurrentMaskCounter >= PunctureSize) CurrentMaskCounter = 0;
279 | }
280 | // Update the State
281 | CurrentState >>= 1;
282 | return Result;
283 | }
284 |
285 | public void Process(byte inputByte)
286 | {
287 | CurrentState |= (inputByte & 0x0001) << m;
288 | // Go thru every polynomial and prepare "Rate" bits for each input bit
289 | for (int i = 0; i < this.Rate; i++)
290 | {
291 | if ((PunctureMask & (1 << CurrentMaskCounter)) != 0) // If bit is 1 in the Mask -> place result
292 | {
293 | OutputData.Enqueue((byte)(Bitcount(CurrentState & Polynomial[i]) & 0x0001));
294 | }
295 | if (++CurrentMaskCounter >= PunctureSize) CurrentMaskCounter = 0;
296 | }
297 | // Update the State
298 | CurrentState >>= 1;
299 | }
300 |
301 | void Process()
302 | {
303 | // Go thru every polynomial and prepare "Rate" bits for each input bit
304 | for (int i = 0; i < this.Rate; i++)
305 | {
306 | if ((PunctureMask & (1 << CurrentMaskCounter)) != 0) // If bit is 1 in the Mask -> place result
307 | {
308 | DataOut.Process((byte)(Bitcount(CurrentState & Polynomial[i]) & 0x0001));
309 | }
310 | if (++CurrentMaskCounter >= PunctureSize) CurrentMaskCounter = 0;
311 | }
312 | // Update the State
313 | CurrentState >>= 1;
314 | }
315 |
316 |
317 |
318 | ///
319 | /// Process supplied byte array and generate the output.
320 | ///
321 | /// Input byte array.
322 | /// Array that receives the result of the convolutional encoding.
323 | /// Number of input bits to process.
324 | /// Number of bits placed in the output array.
325 | public int Process(byte[] inputData, int inputIndex, byte[] outputArray, int outputIndex, int numInputBits)
326 | {
327 | int Result = 0;
328 |
329 | // Initialize the state with first/last "m" bits of the sequence
330 | if ( TailbitingCounter > 0 )
331 | {
332 | int BitsCount;
333 |
334 | if (this.EncoderType == ConvEncoderType.TailBiting_Head)
335 | {
336 | BitsCount = Math.Min(numInputBits, TailbitingCounter);
337 | while (BitsCount > 0)
338 | {
339 | byte Data = inputData[inputIndex++];
340 | CurrentState |= (Data & 0x0001) << (m - TailbitingCounter);
341 | FirstBits[m - TailbitingCounter] = Data;
342 | BitsCount--;
343 | TailbitingCounter--;
344 |
345 | numInputBits--;
346 | }
347 | }
348 | else if (this.EncoderType == ConvEncoderType.TailBiting_Tail)
349 | {
350 | int IdxByte = inputIndex + numInputBits - TailbitingCounter;
351 | BitsCount = Math.Min(numInputBits, TailbitingCounter);
352 | while (BitsCount > 0)
353 | {
354 | CurrentState |= (inputData[IdxByte++] & 0x0001) << (m - TailbitingCounter);
355 | BitsCount--;
356 | TailbitingCounter--;
357 | }
358 | }
359 | else
360 | {
361 | TailbitingCounter = 0;
362 | }
363 | }
364 |
365 | // Process all bits provided in the inputData array.
366 | while (numInputBits > 0)
367 | {
368 | // Add new bit to the register
369 | CurrentState |= (inputData[inputIndex++] & 0x0001) << m;
370 | Result += Process(outputArray, ref outputIndex);
371 | numInputBits--;
372 | }
373 | return Result;
374 | }
375 |
376 |
377 | public int Process(byte[] inputData, int inputIndex, int numInputBits)
378 | {
379 | // Initialize the state with first/last "m" bits of the sequence
380 | if (TailbitingCounter > 0)
381 | {
382 | int BitsCount;
383 |
384 | if (this.EncoderType == ConvEncoderType.TailBiting_Head)
385 | {
386 | BitsCount = Math.Min(numInputBits, TailbitingCounter);
387 | while (BitsCount > 0)
388 | {
389 | byte Data = inputData[inputIndex++];
390 | CurrentState |= (Data & 0x0001) << (m - TailbitingCounter);
391 | FirstBits[m - TailbitingCounter] = Data;
392 | BitsCount--;
393 | TailbitingCounter--;
394 |
395 | numInputBits--;
396 | }
397 | }
398 | else if (this.EncoderType == ConvEncoderType.TailBiting_Tail)
399 | {
400 | int IdxByte = inputIndex + numInputBits - TailbitingCounter;
401 | BitsCount = TailbitingCounter;
402 | while (BitsCount > 0)
403 | {
404 | CurrentState |= (inputData[IdxByte++] & 0x0001) << (m - TailbitingCounter);
405 | BitsCount--;
406 | TailbitingCounter--;
407 | }
408 | }
409 | else
410 | {
411 | TailbitingCounter = 0;
412 | }
413 | }
414 | // Process all bits provided in the inputData array.
415 | while (numInputBits-- > 0)
416 | {
417 | Process(inputData[inputIndex++]);
418 | }
419 | return OutputData.Count;
420 | }
421 |
422 |
423 | public void Process(CNTRL_MSG controlParam, byte incomingBit)
424 | {
425 | if (controlParam == CNTRL_MSG.DATA_IN)
426 | {
427 | // Initialize the state with first "m - 1" bits of the sequence
428 | if (TailbitingCounter > 0)
429 | {
430 | if (this.EncoderType == ConvEncoderType.TailBiting_Head)
431 | {
432 | CurrentState |= (incomingBit & 0x0001) << (m - TailbitingCounter);
433 | FirstBits[m - TailbitingCounter] = incomingBit;
434 | TailbitingCounter--;
435 | }
436 | else
437 | {
438 | TailbitingCounter = 0;
439 | }
440 | }
441 |
442 | if (TailbitingCounter == 0)
443 | {
444 | CurrentState |= (incomingBit & 0x0001) << m;
445 | Process();
446 | }
447 | }
448 | else if (controlParam == CNTRL_MSG.INTERLEAVER_FRAME)
449 | {
450 | // If zero-terminating sequence - add "m" zero bits at the end
451 | if (this.EncoderType == ConvEncoderType.ZeroState)
452 | {
453 | for (int BitsCount = 0; BitsCount < this.m; BitsCount++)
454 | {
455 | Process();
456 | }
457 | }
458 | else if (this.EncoderType == ConvEncoderType.TailBiting_Head)
459 | {
460 | // If tailbiting sequence - add "m" First bits at the end
461 | for (int BitsCount = 0; BitsCount < this.m; BitsCount++)
462 | {
463 | CurrentState |= (FirstBits[BitsCount] & 0x0001) << m;
464 | Process();
465 | }
466 | }
467 | }
468 | }
469 |
470 | public int Finish(byte[] outputArray, int outputIndex)
471 | {
472 | int Result = 0;
473 | // If zero-terminating sequence - add "m" zero bits at the end
474 | if (this.EncoderType == ConvEncoderType.ZeroState)
475 | {
476 | for (int BitsCount = 0; BitsCount < this.m; BitsCount++)
477 | {
478 | Result += Process(outputArray, ref outputIndex);
479 | }
480 | }
481 | else if (this.EncoderType == ConvEncoderType.TailBiting_Head)
482 | {
483 | // If tailbiting sequence - add "m" First bits at the end
484 | for (int BitsCount = 0; BitsCount < this.m; BitsCount++)
485 | {
486 | CurrentState |= (FirstBits[BitsCount] & 0x0001) << m;
487 | Result += Process(outputArray, ref outputIndex);
488 | }
489 | }
490 | return Result;
491 | }
492 |
493 | public int Finish()
494 | {
495 | // If zero-terminating sequence - add "m" zero bits at the end
496 | if (this.EncoderType == ConvEncoderType.ZeroState)
497 | {
498 | for (int BitsCount = 0; BitsCount < this.m; BitsCount++)
499 | {
500 | Process(0);
501 | }
502 | }
503 | else if (this.EncoderType == ConvEncoderType.TailBiting_Head)
504 | {
505 | // If tailbiting sequence - add "m" First bits at the end
506 | for (int BitsCount = 0; BitsCount < this.m; BitsCount++)
507 | {
508 | Process(FirstBits[BitsCount]);
509 | }
510 | }
511 | return OutputData.Count;
512 | }
513 |
514 | public bool IsDataReady { get { return (OutputData.Count > 0); } }
515 |
516 | public int Count
517 | {
518 | get { return OutputData.Count; }
519 | }
520 |
521 | public int GetData(byte[] outData)
522 | {
523 | int ret = OutputData.Count;
524 | OutputData.CopyTo(outData, 0);
525 | OutputData.Clear();
526 | return ret;
527 | }
528 |
529 | public int GetData(byte[] outData, int startingIndex)
530 | {
531 | int ret = OutputData.Count;
532 | OutputData.CopyTo(outData, startingIndex);
533 | OutputData.Clear();
534 | return ret;
535 | }
536 |
537 | public override void SetModuleParameters()
538 | {
539 | DataIn = new InputPin("DataIn", this.Process);
540 | DataOut = new OutputPin("DataOut");
541 | base.SetIOParameters("FEC Convolutional Encoder", new DataPin[] { DataIn, DataOut });
542 | }
543 | }
544 |
545 | }
546 |
--------------------------------------------------------------------------------