static void ModulateQPSK() { // Manual adjust values int sampleRate = 8000; int symbolCount = 1000; float symbolRate = 250; // Symbols per second int carrierHz = 1000; string outputPath = Path.Combine(LocalPath, "qpsk_output.8k.3ch.pcm32f"); // Fixed values float symbolPeriod = 1f / symbolRate; int symbolLengthSamples = sampleRate / (int)symbolRate; Vco vco = new Vco(sampleRate, carrierHz); PseudoRandom random = new PseudoRandom(23); using (Stream output = File.Create(outputPath)) { for (int i = 0; i < symbolCount; i++) { // Generate 2 data bits int bits = (int)random.Next(0, 4); // Generate symbol samples for (int p = 0; p < symbolLengthSamples; p++) { // Generate symbol sample float sampleI = (float)vco.Cos() * ((bits >> 1) * 2 - 1); float sampleQ = (float)vco.Sin() * ((bits & 1) * 2 - 1); vco.Next(); // Reduce amplitude so the sum equals 1.0 sampleI *= 0.707f; sampleQ *= 0.707f; // Combine I and Q samples float sampleOutput = sampleI + sampleQ; // Output sample to file output.Write(BitConverter.GetBytes(sampleOutput), 0, 4); output.Write(BitConverter.GetBytes(((bits >> 1) * 2f - 1)), 0, 4); output.Write(BitConverter.GetBytes(((bits & 1) * 2f - 1)), 0, 4); } } } }
static void ModulateOffsetQPSK() { // Manual adjust values int sampleRate = 8000; int symbolCount = 1000; float symbolRate = 250; // Symbols per second int carrierHz = 1000; string outputPath = Path.Combine(LocalPath, "oqpsk_output.8k.3ch.pcm32f"); // Fixed values float symbolPeriod = 1f / symbolRate; int symbolLengthSamples = sampleRate / (int)symbolRate; Vco vco = new Vco(sampleRate, carrierHz); PseudoRandom random = new PseudoRandom(23); BiQuadraticFilter iFilter = new BiQuadraticFilter(BiQuadraticFilter.Type.LOWPASS, symbolRate, sampleRate, 0.707); BiQuadraticFilter qFilter = new BiQuadraticFilter(BiQuadraticFilter.Type.LOWPASS, symbolRate, sampleRate, 0.707); using (Stream output = File.Create(outputPath)) { int bits = 0; int nextBits = 0; for (int i = 0; i < symbolCount * 2; i++) { bool even = i % 2 == 0; // On each half-symbol period, shift in one data bit for transmission // while keeping the previous other arm's bit transmitting if (even) { nextBits = (int)random.Next(0, 4); bits = (bits & 0x01) | (nextBits & 0x02); } else { bits = (bits & 0x02) | (nextBits & 0x01); } // Generate symbol samples for (int p = 0; p < symbolLengthSamples / 2; p++) { // Generate symbol sample // Also filter symbols with a low pass filter for basic pulse shaping float sampleI = (float)vco.Cos() * (float)iFilter.filter((bits >> 1) * 2 - 1); float sampleQ = (float)vco.Sin() * (float)qFilter.filter((bits & 1) * 2 - 1); vco.Next(); // Reduce amplitude so the sum equals 1.0 sampleI *= 0.707f; sampleQ *= 0.707f; // Combine I and Q samples float sampleOutput = sampleI + sampleQ; // Output sample to file output.Write(BitConverter.GetBytes(sampleOutput), 0, sizeof(float)); output.Write(BitConverter.GetBytes(((bits >> 1) * 2f - 1)), 0, sizeof(float)); output.Write(BitConverter.GetBytes(((bits & 1) * 2f - 1)), 0, sizeof(float)); } } } }