I’ve run a comparison test of
- the “old” GR dqpsk block
- the “new” one (dqpsk2)
- the “op25” cqpsk (Gardner/Costas) block
The resulting constellation diagrams are viewable at
http://www.lightlink.com/mhp/qpsk2/
The results are: Gardner is the winner, the old GR blocks take second,
the new ones are last
(intriguingly as shown in the symbol plot, things seem to start off
nicely but then go awry)…
The problem could be caused by dumb user error - D.U.E. ™ or
something fixable by simple
parameter tweakage?
The input file used in all three cases is a complex capture taken from a
system using so-called
“LSM/CQPSK”. Basically, that appears to mean something like: a)
simulcast distortion is added;
b) PI/4 DQPSK is used; c) pulse shaping is stretched so as to fit the
bandwidth of the signal into
a 12.5 KHz channel; d) a “non-standard” waveform is used which results
in the signal passing
through the origin (0,0) point, introducing a rather severe AM
component.
There are two further wrinkles in this particular case which should be
noted:
- the complex file was captured using a “very poor man’s USRP”, not a
real USRP
[ see A 455 KHz IF Downconverter for Digital Radio Reception ]. However the file formats
are fully compatible.
Further, a DBSRX capture exhibits all the same characteristics. - the receiving location is outside the designed coverage area of the
system
The results of all of this, plus the fact that the capture was made
prior to verifying that the
hardware even was functional for LSM, makes for a very “difficult”
specimen (perfect for
torture-testing RX codes)…
Best Regards
Max
#!/usr/bin/env python
import sys
import os
import math
from gnuradio import gr, gru, audio, eng_notation, blks2
from gnuradio.eng_option import eng_option
from optparse import OptionParser
from math import pi
class my_top_block(gr.top_block):
def init(self):
gr.top_block.init(self)
parser = OptionParser(option_class=eng_option)
parser.add_option("-d", "--debug", action="store_true",
default=False, help=“allow time at init to attach gdb”)
parser.add_option(“-i”, “–input-file”, type=“string”,
default=“in.dat”, help=“specify the input file”)
parser.add_option(“-o”, “–output-file”, type=“string”,
default=“out.dat”, help=“specify the output file”)
parser.add_option(“-s”, “–sample-rate”, type=“int”,
default=19200, help=“input sample rate”)
(options, args) = parser.parse_args()
sample_rate = options.sample_rate
symbol_rate = 4800
samples_per_symbol = sample_rate // symbol_rate
IN = gr.file_source(gr.sizeof_gr_complex, options.input_file)
print "sps %d" % samples_per_symbol
DEMOD = blks2.dqpsk2_demod(samples_per_symbol =
samples_per_symbol,
excess_bw = 0.35,
verbose = True,
log = True,
sync_out = False)
OUT = gr.file_sink(gr.sizeof_char, options.output_file)
self.connect(IN, DEMOD, OUT)
if options.debug:
print 'Ready for GDB to attach (pid = %d)' % (os.getpid(),)
raw_input("Press 'Enter' to continue...")
if name == “main”:
try:
my_top_block().run()
except KeyboardInterrupt:
tb.stop()