|
As part of my job at a major aerospace corporation I have been investigating
using various modulation schemes to send data over a very non-ideal path
that has both poor frequency response and a nonlinear amplitude response.
One technique I use to assess the merits of the various schemes is classic
eye patterns. Even though I’m an absolute O-Matrix novice and
a lousy programmer to boot, I found it very easy and fast to create the
required tools for evaluating each modulation scheme, one of which is
described below. In addition, I am able to easily distribute my applications
to colleagues using the
O-Matrix Development Kit.
Manchester is one of the simpler encoding schemes which is best described
by Figure 1 where a NRZ one is transmitted as a one-zero pair and a
zero is transmitted as a zero-one pair with the state change occurring
at the middle of the NRZ bit period.
Figure 1
A couple of it’s useful characteristics are that it’s easy to synchronize
with as it always has a transition in the middle of a NRZ bit period and
the other is that the spectrum doesn’t contain any energy at DC allowing
it to cohabitate on the same wire as a DC power.
Eye diagrams are a very useful tool for the qualitative assessment of signals
used to transmit digital data. It’s an old technique and my first exposure
to it was some 35+ years ago. Traditionally they were presented on an
oscilloscope by triggering on a system bit rate clock and setting the
oscilloscope time base or sweep speed such that 2 or 3 bit periods are
displayed on the screen. The result, due both to the persistence of the
oscilloscope display and the peculiarities of the human eye is a presentation
similar to that shown in figure 2 which actually has over 1000 Manchester
or BiPhase encoded bits overlaid. There is some rounding of the
waveform due to the transmission paths finite high frequency response
but other than that these would be very good eyes since they’re
“wide open” making it easy to determine whether a bit is a one or a zero.
This is very valuable in the development process since the simulation
times required to verify a bit error rate of 1 part in 10^9 or better is just
not practical, even a modest 1 part in 10^6 is just not practical except
perhaps for very simple networks.
Figure 2
In this instance the hardware doesn’t yet exist so the system was modeled
in Spice, some 5000+ components. For each modulation scheme I wrote a short
O-Matrix script to generate a piecewise-linear time and
amplitude representation of the signal to be used by Spice as the excitation
for the time domain analysis. For this case it involved generating random
ones and zeros, the Manchester carrier, sampling them at the desired
sample density and finally Exclusive-OR them to create the PWL data.
Hindsight being 20-20 it would have been smarter to Exclusive-OR them and
then sample the result. For this technique to be valid the number of bits
must be very large so it’s not unusual to generate files 100’s of k-bytes long.
The code to generate the Manchester data is shown below.
# Generate a random Manchester PWL input file for B2 Spice mcc 07/14/2006
# output is ((1/fnrz)/t)n samples at sr=1/t.
#
clear
ranseed # seed random number generator from system clock
# get inputs
fnrz = atod(input("The NRZ bit rate in bits/sec"))
n = atod(input("The number of random NRZ bits in the sequence"))
sr = atod(input("The sample rate in samples/second"))
t=1/sr
# generate and sample the NRZ bit stream
A=rand(n,1 # load the array with random numbers
tock=zeros(((1/fnrz)/t)*n,1) # load the array with zeros
nrz=zeros(((1/fnrz)/t)*n,1) # load the array with zeros
biphase=zeros(((1/fnrz)/t)*n,2) # load the array with zeros
for i = 1 to n begin # make the random number either a 1 or a 0
if A(i) < 0.5 then sample = 0
else sample = 1
for j = 1 to ((1/fnrz)/t) begin
nrz((i-1)*((1/fnrz)/t)+j) = sample # write the NRZ sample to the array
tock((i-1)*((1/fnrz)/t)+j) = (((i-1)*((1/fnrz)/t))+j-1)*t # write the time to the to the array
end
end
# generate and sample the carrier
carrier=zeros(((1/fnrz)/t)*n,1) # load the array with zeros
for i = 1 to n begin
for j = 1 to ((1/fnrz)/t/2) begin
carrier((i-1)*((1/fnrz)/t)+j) = 0 # first 1/2 cycle
end
for j= (1+((1/fnrz)/t)/2) to ((1/fnrz)/t) begin
carrier((i-1)*((1/fnrz)/t)+j) = 1 # second 1/2 cycle
end
end
# XOR the NRZ bit stream and the carrier to generate the Manchester bit stream
f = input("Enter the path and output file name") # specify output file name
if exists(f) then rmfile(f) # delete file if it already exists
biphase = [tock, 2*int(xor(carrier, nrz))-1] # exclusive OR the carrier and RZ bit stream
write(f,biphase) # write data to the designated file
close
The output from Spice is a large text file of time and amplitude samples
that are not at equal time intervals. Spice will ratchet down the size of
the time step as required to generate a reasonable solution. Since my
problem is very non-linear the result is very large files on the order
of 5 mega-bytes for even a modest number of random bits. This data could
of course be linearized and the density reduced via interpolation but
doing so has the potential to discard the very kind of disturbances that
would cause a bit error in the decode process. For this reason I decided
to error on the side of caution and deal with the large files.
To analyze the results I wrote a script in O-Matrix to plot
a traditional eye pattern from this data. While it was trivial to get a
basic display up and running, there were some subtleties to deal
with to obtain a clean plot. The resulting code is shown below.
# Plot "Eyes" from a B2 Spice or PSpice transient analysis output file mcc 08/09/2006
# rev 1 changed size fudge factor from 1.1 to 1.2
# rev 2 dropped fudge factor and now determine the max vector size required for tock & eyes.
# rev 3 fixed a minor problem that Brian Falk spotted in the second while loop of "make eyes"
clear
ginit
width = 1
# get user inputs
fin = input("Enter the path and input file name") # specify input file name
t = input("Enter the plot title") # specify plot title
fs = atod(input("Enter the NRZ bit rate in bits/sec"))
wl = atod(input("Enter the group size of NRZ bit periods to display"))
ng = atod(input("Enter the number of NRZ bit groups to skip before plotting"))
width = atoi(input("Set axis and line width in pixels: 1,3,5,7,9,11 or 13"))
nrow = nrows(fin) # get file size
read(fin,"char", 1) # read one line of text to skip the column titles
a=read(fin, "double",(nrow-1) ) # read the remaining rows as numbers
seconds=a.col(1) # extract time
amplitude=a.col(2) # extract amplitude
# set up plot stuff
gcolor({"red"}) # set trace color
gstyle("solid",width) # set style and width of plot lines
gxaxis("linear",0, wl/fs,2*wl,10)# set x scale
gxtitle("Seconds")
yscale = axiscale([amplitude]) # compute scale for Y axis
gyaxis("linear",yscale(1),yscale(2),yscale(3),yscale(4)) # scale Y axis
gxtickfont("courier",10)
gxtick("e10.1")
gxgrid("major")
gxtickfont("courier",10)
gytick("e10.1")
gygrid("major")
gytitle("Volts")
gtitle(t)
i = 1 # main index counter
j = 0 # window index counter
m = 0 # window counter
# determine the maximum size for the the tock and eye vectors
told = seconds(1) # time of previous window
kold = 0 # previous count
maxcount = 0 # maximum data points/window found
for k = 1 to nrow-1 begin
if (seconds(k) >= (ng*wl)/fs) then begin # skip specified settling time
if seconds(k) - told >= wl/fs then begin
if maxcount == 0 then begin
maxcount = k + 1
kold = k
end
if k - kold + 1 > maxcount then begin # if new size > maxcount
maxcount = k - kold + 1 # male maxcount = current size
kold = k # store new old k
told = seconds(k) # store new old time
end
end
end
end
sizeof = maxcount # make size of vectors = maxcount
# make Eyes
i = 1 # main index counter
j = 0 # window index counter
m = 0 # window counter
plottime=seconds(nrow-1)-(wl*ng)/fs # total plot time, constant for window control
flag = 0 # set flag to false
while i < nrow-1 begin # outer loop, do till end of file
if (seconds(i) >= (ng*wl)/fs) and (plottime - m*wl/fs >= wl/fs) then begin # controls when to plot complete windows
flag = -1 # set flag to true
j=0 # reset inner loop index to zero
tock=fill(1e30,sizeof,1)
eyes=fill(1e30,sizeof,1)
# inner loop, do till we have one full window
while seconds(i+j) - ((m*wl/fs) + (ng*wl)/fs) < wl/fs and i+j < nrow-1 begin
tock(j+1) = seconds(i+j) - ((m*wl/fs) + (ng*wl)/fs) # store normalized time
eyes(j+1) = amplitude(i+j) # store amplitude
j=j+1 # increment window index
end # end of inner loop
# a little kludge to set the unused vector locations to the last data point.
if j <> 0 and tock(j+1) == 1e30 then begin
for k = j to sizeof begin
tock(k) = tock(j)
eyes(k) = eyes(j)
end
end
m=m+1 # increment number of windows counter
gplot(tock,eyes)
end
if flag==0 then i = i + 1 # count by 1 during settling time
else i = i + j # step i by j after settling time
end # end of outer loop
close
Figures 3 and 4 are the results of one recent analysis
that show the effects of passing the Manchester signal down this
very large network, Figure 3 is near the beginning of the network and
Figure 4 is towards the end and indeed shows considerably more distortion.
Figure 3
Figure 4
Once I had this basic work completed I used the
O-Matrix Development Kit
along with the
The O-Matrix Virtual Machine Application Launcher
to share these with others working
on the project with me. I work in a very large company where many
different computational tools are used so I’ve found the Development Kit
quite useful for sharing similar small utilities with colleagues.
A partial list of some additional functions that I’ve shared this
way recently is shown below.
- FET Bias, computes the component values required to achieve an operating point within a specified tolerance.
- Phase Noise to Jitter, computes the RMS jitter from phase noise data.
- QPSK bit error rate vs. SNR
- 128 QAM Symbol Error Rate vs. SNR
- MTI Clutter Rejection vs. PRI to PRI Amplitude Jitter
- MTI Clutter Rejection vs. PRI to PRI Timing Jitter
- MTI Clutter Rejection vs. Pulse Width Jitter
- MTI Clutter Rejection Limitation due to Quantization Errors
The most popular of these has been the conversion of phase noise data in
dB relative to the carrier into RMS jitter in seconds. Those of us that
design data acquisition systems are concerned with the amount of jitter
on the clock for the A/D conversion process. However, off the shelf
oscillators are almost always specified in terms of phase noise relative
to the carrier (dBc) so I wrote this short utility in O-Matrix
to make this calculation. There are a number of my colleagues using this utility
thanks to the Development Kit and the Application Launcher.
The code for this short utility is shown below.
# Calculate rms jitter from phase noise data in dBc mcc 02/20/06
# Assumes that the noise is symmetric about the carrier
# A good rule of thumb is to integrate out to 2xFo
clear
clc
format double "f15.4"
fzero=atod(input("Oscillator Frequency(Hz)=?"))
n = atoi(input("Number of data points=?"))
f = fill(0d0,n,1) # create and fill vector with zero
a = fill(0d0,n,1) # create and fill vector with zero
Lf = fill(0d0,n,1) # create and fill vector with zero
Bf = fill(0d0,n,1) # create and fill vector with zero
sum = 0.0d0
for i = 1 to n begin # loop to get input data
f(i,1) = atod(input("Offset in Hz?")) # frequency offset from carrier
Lf(i,1) = atod(input("Noise level in dBc?")) # Noise level in dBc at the offset frequency
end
for i = 1 to n-1 begin
a(i,1)=(Lf(i+1,1) -Lf(i,1))/(log10(f(i+1,1))-log10(f(i,1))) # compute slopes (dBc/decade)
if a(i,1) == -10.0 then a(i,1)=a(i,1)+1d-6
sum = sum + (10^(Lf(i,1)/10)*f(i,1)^(-a(i,1)/10)*(1/(a(i)/10 +1d0))*( f(i+1,1)^(a(i)/10 + 1.0d0)
f(i,1)^(a(i,1)/10 +1.0d0)))
end
rmsj=sqrt(2*sum)/(2*pi*fzero)
clc # clear Command window screen
print "RMS Jitter =", rmsj/1d-12,"ps"
print
print " Input Data"
print "Oscillator Frequency(Hz)=", fzero
print "Freq", f,"Phase Noise (dBc)",Lf
# Test Values from Analog Devices App Note MT-008:
# "Converting Oscillator Phase Noise to Time Jitter" rev 10-03-2005 page 5
# Walt Kester
#
# Oscillator Frequency 2.25 Ghz Jitter = 1.57ps
# 100 Hz -82 dBc
# 1kHz -80 dBc
# 10kHz -77 dBc
# 100kHz -112 dBc
# 1Mhz -134 dBc
# 10 Mhz -146 dBc
# 4.5 Ghz -146 dBc
#
# Calculated with this code the Jitter = 1.5658 ps
This user story was contributed by
M.C. “Moe” Clayton, mclayton@twcny.rr.com
|
|