forked from jgibbard/iqtool
-
Notifications
You must be signed in to change notification settings - Fork 0
/
iqplot.py
193 lines (148 loc) · 6.43 KB
/
iqplot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/usr/bin/env python
#Copyright (c) 2017 James Gibbard
#Plot an IQ data file based on provided parameters
#Tested with python 2.7 and 3.6
#Requires numpy and matplotlib
import argparse
from sys import byteorder
import numpy as np
import matplotlib.pyplot as plt
def plotIQ(data, Fs):
if Fs == None:
plt.plot(np.real(data), label='I')
plt.plot(np.imag(data), label='Q')
else:
plt.plot(np.real(data), label='I')
plt.plot(np.imag(data), label='Q')
plt.grid(True)
plt.legend(loc='upper right', frameon=True)
plt.show()
def plotPSD(data,fftWindow, Fs):
assert fftWindow in ['rectangular', 'bartlett', 'blackman',
'hamming', 'hanning']
N = len(data)
#Generate the selected window
if fftWindow == "rectangular":
window = np.ones(N)
elif fftWindow == "bartlett":
window = np.bartlett(N)
elif args.fftWindow == "blackman":
window = np.blackman(N)
elif fftWindow == "hamming":
window = np.hamming(N)
elif fftWindow == "hanning":
window = np.hanning(N)
dft = np.fft.fft(data*window)
if Fs == None:
#If the sample rate is known then plot PSD as
#Power/Freq in (dB/Hz)
plt.psd(data*window, NFFT=N)
else:
#If sample rate is not known then plot PSD as
#Power/Freq as (dB/rad/sample)
plt.psd(data*window, NFFT=N, Fs=Fs)
plt.show()
def plotSpectrogram(data, fftWindow, fftSize, Fs):
if fftSize == None:
N = len(data)
else:
N = fftSize
if Fs == None:
Fs = 2
if fftWindow == "rectangular":
plt.specgram(data, NFFT=N, Fs=Fs,
window=lambda data: data*np.ones(len(data)), noverlap=int(N/10))
elif fftWindow == "bartlett":
plt.specgram(data, NFFT=N, Fs=Fs,
window=lambda data: data*np.bartlett(len(data)), noverlap=int(N/10))
elif args.fftWindow == "blackman":
plt.specgram(data, NFFT=N, Fs=Fs,
window=lambda data: data*np.blackman(len(data)), noverlap=int(N/10))
elif fftWindow == "hamming":
plt.specgram(data, NFFT=N, Fs=Fs,
window=lambda data: data*np.hamming(len(data)), noverlap=int(N/10))
elif fftWindow == "hanning":
plt.specgram(data, NFFT=N, Fs=Fs,
window=lambda data: data*np.hanning(len(data)), noverlap=int(N/10))
plt.show()
if __name__ == '__main__':
#Generate command line parser to parse inputs
cliParser = argparse.ArgumentParser(description='Plots quadrature IQ signals')
#Get the filename of the input file
cliParser.add_argument('filename', type=str, help='input filename')
cliParser.add_argument('-s', '--startSample', type=int,
help='sample to begin plot from (default=0)', default=0)
cliParser.add_argument('-o', '--offset', type=int,
help='offset in bytes from begining of file (default=0)', default=0)
cliParser.add_argument('-n', '--numberOfSamples', type=int,
help='number of samples to plot', default=0)
cliParser.add_argument('-fs', '--sampleRate', type=float,
help='sets the sample rate [sps] (default=1e6)')
cliParser.add_argument('-f', '--format', type=str,
help='Output format (default=int16)',
choices=["int8", "int16", "int32", "uint8", "uint16", "uint32",
"float16", "float32", "float64"],
default='int16')
cliParser.add_argument('-be', '--bigendian', action='store_true',
help='output data in big endian format (default=False)')
cliParser.add_argument('-qi', '--orderQI', action='store_true',
help='store output data as Q then I (Default = I then Q)')
cliParser.add_argument('-p', '--plotType', type=str,
help='Plot Type (default=iq)', choices=['iq', 'psd', 'spec'],
default='iq')
cliParser.add_argument('-w', '--fftWindow', type=str,
help='FFT window type (default=rectangular)',
choices=['rectangular', 'bartlett', 'blackman', 'hamming', 'hanning'],
default='rectangular')
cliParser.add_argument('-fw', '--fftWidth', type=int,
help='FFT width for spectrogram')
args = cliParser.parse_args()
#By default the file is read from an offset of 0 bytes
fileOffset = 0
#Set initial offset in bytes
#Useful if the file has a header that should be ignored
if args.offset != 0:
fileOffset = args.offset
#Convert sample offset to offset in bytes depending on datatype
if args.startSample != 0:
if args.format[-1:] == "8":
fileOffset += 2 * 1 * args.startSample
elif args.format[-2:] == "16":
fileOffset += 2 * 2 * args.startSample
elif args.format[-2:] == "32":
fileOffset += 2 * 4 * args.startSample
elif args.format[-2:] == "64":
fileOffset += 2 * 8 * args.startSample
#Open the file in binary read mode
with open(args.filename, "rb") as f:
#Seek to the absolue offset set by offset and startSample arguments
f.seek(fileOffset, 0)
if args.numberOfSamples != 0:
#Read twice the number of samples as each sample has a
#real and imaginary part
data = np.fromfile(f, dtype=args.format,
count=args.numberOfSamples*2)
else:
#If number of samples is not specified read to the end of the file
data = np.fromfile(f, dtype=args.format)
#If system byteorder is different to desired input byte order
#Then swich the endianness
if byteorder == 'little':
if args.bigendian == True:
data = data.byteswap()
elif byteorder == 'big':
if args.bigendian == False:
data = data.byteswap()
#Convert to complex data to complex128 data type
#This is two double precision (64bit) floating point numbers
if args.orderQI:
data = data[1::2] + 1j * data[0::2]
else:
data = data[0::2] + 1j * data[1::2]
#Select plotting function
if args.plotType == 'iq':
plotIQ(data, args.sampleRate)
elif args.plotType == 'psd':
plotPSD(data, args.fftWindow, args.sampleRate)
elif args.plotType == 'spec':
plotSpectrogram(data, args.fftWindow, args.fftWidth, args.sampleRate)