├── .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 | --------------------------------------------------------------------------------