Skip to content

Commit 2f4e510

Browse files
authored
Merge pull request #8 from AUAS-Pulsar/pylint
Pylint
2 parents b99633e + 0340a53 commit 2f4e510

File tree

15 files changed

+316
-269
lines changed

15 files changed

+316
-269
lines changed

.coverage

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ install:
1616
- pip install -f requirements.txt
1717

1818
script:
19-
- pylint *.py
19+
- pylint */*.py
2020
- coverage run -a tests/test_filterbank.py
2121
- coverage run -a tests/test_header.py
2222
- coverage run -a tests/test_fft.py

examples/visualize_psd.py

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,47 @@
1-
import os,sys,inspect
2-
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
3-
parentdir = os.path.dirname(currentdir)
4-
sys.path.insert(0,parentdir)
5-
6-
from rtlsdr import RtlSdr
7-
import matplotlib.pyplot as plt
8-
import numpy as np
9-
from fourier import fourier
10-
from plot import psd
1+
"""
2+
Example of plotting a Power Spectral Density plot, using RTLSDR data
3+
"""
4+
# pylint: disable-all
5+
import os
6+
import sys
7+
import inspect
8+
CURRENT_DIR = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
9+
PARENT_DIR = os.path.dirname(CURRENT_DIR)
10+
sys.path.insert(0, PARENT_DIR)
11+
from rtlsdr import RtlSdr # pylint: disable-msg=C0413
12+
import matplotlib.pyplot as plt # pylint: disable-msg=C0413
13+
import numpy as np # pylint: disable-msg=C0413
14+
from plot import psd # pylint: disable-msg=C0413
1115

1216
# Initiate RtlSdr
13-
sdr = RtlSdr()
17+
SDR = RtlSdr()
1418

1519
# configure device
16-
sdr.sample_rate = 2.4e6
17-
sdr.center_freq = 102.2e6
20+
SDR.sample_rate = 2.4e6
21+
SDR.center_freq = 102.2e6
1822

1923
# Read samples
20-
samples = sdr.read_samples(1024)
24+
SAMPLES = SDR.read_samples(1024)
2125

2226
# Close RTLSDR device connection
23-
sdr.close()
27+
SDR.close()
2428

2529
# Number of samples equals the length of samples
26-
N = samples.shape[0]
30+
N = SAMPLES.shape[0]
2731

28-
# T equals N/Fs
29-
T = N/sdr.sample_rate
32+
# T equals N/Fs
33+
T = N/SDR.sample_rate
3034

3135
# Get the powerlevels and the frequencies
32-
Pxx, freqs, _ = psd(samples, NFFT=1024, Fs=sdr.sample_rate/1e6, scale_by_freq=True, sides='twosided')
36+
PXX, freqs, _ = psd(SAMPLES, nfft=1024, sample_rate=SDR.sample_rate/1e6, # pylint: disable-msg=C0103
37+
scale_by_freq=True, sides='twosided')
3338

34-
# Calculate the powerlevel dB's
35-
power_levels = 10*np.log10(Pxx/(sdr.sample_rate/1e6))
39+
# Calculate the powerlevel dB's
40+
POWER_LEVELS = 10*np.log10(PXX/(SDR.sample_rate/1e6))
3641

3742
# Add the center frequency to the frequencies so it matches the actual frequencies
38-
freqs = freqs + sdr.center_freq/1e6
43+
freqs = freqs + SDR.center_freq/1e6 # pylint: disable-msg=C0103
3944

4045
# Plot the PSD
41-
plt.plot(freqs, power_levels)
46+
plt.plot(freqs, POWER_LEVELS) # pylint: disable-msg=C0103
4247
plt.show()

examples/visualize_time_domain.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,34 @@
1+
"""
2+
Example using matplotlib to plot raw RTLSDR data
3+
"""
4+
# pylint: disable-all
15
from rtlsdr import RtlSdr
26
import matplotlib.pyplot as plt
37
import numpy as np
48

59
# Initiate RtlSdr
6-
sdr = RtlSdr()
10+
SDR = RtlSdr()
711

812
# configure device
9-
sdr.sample_rate = 2.4e6
10-
sdr.center_freq = 102.2e6
11-
# sdr.gain = 0
13+
SDR.sample_rate = 2.4e6
14+
SDR.center_freq = 102.2e6
1215

1316
# Read samples
14-
samples = sdr.read_samples(256*1024)
17+
SAMPLES = SDR.read_samples(256*1024)
1518

1619
# Close RTLSDR device connection
17-
sdr.close()
20+
SDR.close()
1821

1922
# Number of samples equals the length of samples
20-
N = samples.shape[0]
23+
N = SAMPLES.shape[0]
2124

22-
# T equals N/Fs
23-
T = N/sdr.sample_rate
25+
# T equals N/Fs
26+
T = N/SDR.sample_rate
2427

2528
# Define the x axis
26-
x = np.linspace(0.0, T, N)
29+
X = np.linspace(0.0, T, N)
2730

2831
# Generate the plot
29-
fig, ax = plt.subplots()
30-
ax.plot(x, samples)
32+
FIG, AX = plt.subplots()
33+
AX.plot(X, SAMPLES)
3134
plt.show()

filterbank/filterbank.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def __init__(self, filename, freq_range=None, time_range=None):
3535
else:
3636
raise FileNotFoundError(filename)
3737

38+
3839
def read_filterbank(self, freq_range=None, time_range=None):
3940
"""
4041
Read filterbank file to 3d numpy array
@@ -68,6 +69,7 @@ def read_filterbank(self, freq_range=None, time_range=None):
6869
# search for start of next chunk
6970
fil.seek(self.n_bytes * (self.n_chans - i_1), 1)
7071

72+
7173
def setup_freqs(self, freq_range=None):
7274
"""
7375
Calculate the frequency range
@@ -98,6 +100,7 @@ def setup_freqs(self, freq_range=None):
98100

99101
return chan_start_idx, chan_stop_idx
100102

103+
101104
def setup_time(self, time_range=None):
102105
"""
103106
Calculate the time range
@@ -122,6 +125,7 @@ def setup_time(self, time_range=None):
122125

123126
return ii_start, n_ints
124127

128+
125129
def setup_chans(self, freq_range=None):
126130
"""
127131
Calculate the channel range
@@ -133,6 +137,7 @@ def setup_chans(self, freq_range=None):
133137

134138
return i_0, i_1
135139

140+
136141
def select_data(self, freq_start=None, freq_stop=None, time_start=None, time_stop=None):
137142
"""
138143
Select a range of data from the filterbank file

filterbank/header.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from struct import unpack
66
import numpy as np
77

8+
89
HEADER_KEYWORD_TYPES = {
910
b'telescope_id': b'<l',
1011
b'machine_id': b'<l',
@@ -31,6 +32,7 @@
3132
b'src_dej': b'angle',
3233
}
3334

35+
3436
def read_header(filename):
3537
"""
3638
Read Filterbank header and return a dictionary of key-value pairs
@@ -54,6 +56,7 @@ def read_header(filename):
5456

5557
return header_dict
5658

59+
5760
def read_next_header_keyword(file_header):
5861
"""
5962
Read key-value pair from header
@@ -83,6 +86,7 @@ def read_next_header_keyword(file_header):
8386

8487
return keyword, val
8588

89+
8690
def fil_double_to_angle(angle):
8791
"""
8892
Reads a little-endian double in ddmmss.s (or hhmmss.s) format and then
@@ -102,6 +106,7 @@ def fil_double_to_angle(angle):
102106

103107
return data_matrix
104108

109+
105110
def len_header(filename):
106111
"""
107112
Return the length of the header in bytes

fourier/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
from .fourier import *
1+
"""
2+
import file for fourier.py
3+
"""
4+
from .fourier import fft_freq, fft_vectorized, dft_slow

fourier/fourier.py

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,55 @@
1-
import numpy as np
1+
"""
2+
Fourier algorithm related utils
3+
"""
24

5+
import numpy as np
36

4-
def DFT_slow(x):
5-
""" Compute the discrete Fourier Transform of the one-dimensional array x"""
6-
x = np.asarray(x)
7-
N = x.shape[0]
8-
n = np.arange(N)
9-
k = n.reshape((N, 1))
10-
M = np.exp(-2j * np.pi * k * n / N)
11-
return np.dot(M, x)
127

8+
def dft_slow(input_data):
9+
"""
10+
Compute the discrete Fourier Transform of the one-dimensional array x
11+
"""
12+
input_data = np.asarray(input_data)
13+
data_length = input_data.shape[0]
14+
data_sorted = np.arange(data_length)
15+
data_array = data_sorted.reshape((data_length, 1))
16+
exp_data = np.exp(-2j * np.pi * data_array * data_sorted / data_length)
17+
return np.dot(exp_data, input_data)
1318

14-
def FFT_vectorized(x):
15-
"""A vectorized, non-recursive version of the Cooley-Tukey FFT"""
16-
x = np.asarray(x)
17-
N = x.shape[0]
1819

19-
if np.log2(N) % 1 > 0:
20-
raise ValueError("size of x must be a power of 2")
20+
def fft_vectorized(input_data):
21+
"""
22+
A vectorized, non-recursive version of the Cooley-Tukey FFT
23+
"""
24+
input_data = np.asarray(input_data)
25+
data_length = input_data.shape[0]
26+
27+
if np.log2(data_length) % 1 > 0:
28+
raise ValueError("size of input data must be a power of 2")
2129

2230
# N_min here is equivalent to the stopping condition above,
2331
# and should be a power of 2
24-
N_min = min(N, 32)
25-
26-
# Perform an O[N^2] DFT on all length-N_min sub-problems at once
27-
n = np.arange(N_min)
28-
k = n[:, None]
29-
M = np.exp(-2j * np.pi * n * k / N_min)
30-
X = np.dot(M, x.reshape((N_min, -1)))
32+
min_data = min(data_length, 32)
33+
34+
# Perform an O[N^2] DFT on all length-min_data sub-problems at once
35+
data_sorted = np.arange(min_data)
36+
data_matrix = data_sorted[:, None]
37+
exp_data = np.exp(-2j * np.pi * data_sorted * data_matrix / min_data)
38+
dot_product = np.dot(exp_data, input_data.reshape((min_data, -1)))
3139

3240
# build-up each level of the recursive calculation all at once
33-
while X.shape[0] < N:
34-
X_even = X[:, :X.shape[1] // 2]
35-
X_odd = X[:, X.shape[1] // 2:]
36-
factor = np.exp(-1j * np.pi * np.arange(X.shape[0])
37-
/ X.shape[0])[:, None]
38-
X = np.vstack([X_even + factor * X_odd,
39-
X_even - factor * X_odd])
41+
while dot_product.shape[0] < data_length:
42+
data_even = dot_product[:, :dot_product.shape[1] // 2]
43+
data_odd = dot_product[:, dot_product.shape[1] // 2:]
44+
factor = np.exp(-1j * np.pi * np.arange(dot_product.shape[0])
45+
/ dot_product.shape[0])[:, None]
46+
dot_product = np.vstack([data_even + factor * data_odd,
47+
data_even - factor * data_odd])
4048

41-
return X.ravel()
49+
return dot_product.ravel()
4250

4351

44-
def fftfreq(n, d=1.0):
52+
def fft_freq(window_len, spacing=1.0):
4553
"""
4654
Return the Discrete Fourier Transform sample frequencies.
4755
@@ -75,14 +83,13 @@ def fftfreq(n, d=1.0):
7583
>>> freq = np.fft.fftfreq(n, d=timestep)
7684
>>> freq
7785
array([ 0. , 1.25, 2.5 , 3.75, -5. , -3.75, -2.5 , -1.25])
78-
7986
"""
80-
81-
val = 1.0 / (n * d)
82-
results = np.empty(n, int)
83-
N = (n-1)//2 + 1
84-
p1 = np.arange(0, N, dtype=int)
85-
results[:N] = p1
86-
p2 = np.arange(-(n//2), 0, dtype=int)
87-
results[N:] = p2
88-
return results * val
87+
88+
val = 1.0 / (window_len * spacing)
89+
results = np.empty(window_len, int)
90+
window_half = (window_len-1)//2 + 1
91+
window_p1 = np.arange(0, window_half, dtype=int)
92+
results[:window_half] = window_p1
93+
window_p2 = np.arange(-(window_len//2), 0, dtype=int)
94+
results[window_half:] = window_p2
95+
return results * val

plot/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
from .plot import *
1+
"""
2+
Initialize plot.py and make it importable
3+
"""
4+
from .plot import psd

0 commit comments

Comments
 (0)