Skip to content

Commit 9f8c6b2

Browse files
committed
add option to generate board signal with no overlapping bands
1 parent b420b2d commit 9f8c6b2

File tree

5 files changed

+107
-20
lines changed

5 files changed

+107
-20
lines changed

doa_py/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,5 @@
113113
file for details.
114114
"""
115115

116-
__version__ = "0.2.2"
116+
__version__ = "0.2.3"
117117
__author__ = "Qian Xu"

doa_py/algorithm/broadband.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ def cssm(
144144
if fre_ref is None:
145145
# Find the frequency point with the maximum power
146146
fre_ref = fre_bins[np.argmax(np.abs(signal_fre_bins).sum(axis=0))]
147-
print(fre_ref)
148147

149148
# Calculate the manifold matrix corresponding to the pre-estimated angles at
150149
# the reference frequency point
@@ -214,7 +213,6 @@ def tops(
214213

215214
if fre_ref is None:
216215
fre_ref = fre_bins[np.argmax(np.abs(signal_fre_bins).sum(axis=(0, 2)))]
217-
print(fre_ref)
218216

219217
# index of reference frequency in FFT output
220218
ref_index = int(fre_ref / (fs / fre_bins.size))

doa_py/arrays.py

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import numpy as np
44

5-
from .signals import BroadSignal
5+
from .signals import BroadSignal, NarrowSignal, Signal
66

77
C = 3e8 # wave speed
88

@@ -88,11 +88,13 @@ def steering_vector(self, fre, angle_incidence, unit="deg"):
8888

8989
def received_signal(
9090
self,
91-
signal,
91+
signal: Signal,
9292
angle_incidence,
9393
snr=None,
9494
nsamples=100,
9595
amp=None,
96+
min_length_ratio=0.1,
97+
no_overlap=False,
9698
unit="deg",
9799
):
98100
"""Generate array received signal based on array signal model
@@ -110,6 +112,10 @@ def received_signal(
110112
snr: Signal-to-noise ratio. If set to None, no noise will be added
111113
nsamples (int): Number of snapshots, defaults to 100
112114
amp: The amplitude of each signal, 1d numpy array
115+
min_length_ratio (float): Minimum length ratio of the frequency
116+
range in (f_max - f_min)
117+
no_overlap (bool): If True, generate signals with non-overlapping
118+
subbands
113119
unit: The unit of the angle, `rad` represents radian,
114120
`deg` represents degree. Defaults to 'deg'.
115121
"""
@@ -118,16 +124,24 @@ def received_signal(
118124

119125
if isinstance(signal, BroadSignal):
120126
received = self._gen_broadband(
121-
signal, snr, nsamples, angle_incidence, amp
127+
signal,
128+
snr,
129+
nsamples,
130+
angle_incidence,
131+
amp,
132+
min_length_ratio,
133+
no_overlap,
122134
)
123-
else:
135+
if isinstance(signal, NarrowSignal):
124136
received = self._gen_narrowband(
125137
signal, snr, nsamples, angle_incidence, amp
126138
)
127139

128140
return received
129141

130-
def _gen_narrowband(self, signal, snr, nsamples, angle_incidence, amp):
142+
def _gen_narrowband(
143+
self, signal: NarrowSignal, snr, nsamples, angle_incidence, amp
144+
):
131145
"""Generate narrowband received signal
132146
133147
`azimuth` and `elevation` are already in radians
@@ -150,7 +164,16 @@ def _gen_narrowband(self, signal, snr, nsamples, angle_incidence, amp):
150164

151165
return received
152166

153-
def _gen_broadband(self, signal, snr, nsamples, angle_incidence, amp):
167+
def _gen_broadband(
168+
self,
169+
signal: BroadSignal,
170+
snr,
171+
nsamples,
172+
angle_incidence,
173+
amp,
174+
min_length_ratio=0.1,
175+
no_overlap=False,
176+
):
154177
"""Generate broadband received signal
155178
156179
`azimuth` and `elevation` are already in radians
@@ -162,7 +185,13 @@ def _gen_broadband(self, signal, snr, nsamples, angle_incidence, amp):
162185

163186
num_antennas = self._element_position.shape[0]
164187

165-
incidence_signal = signal.gen(n=num_signal, nsamples=nsamples, amp=amp)
188+
incidence_signal = signal.gen(
189+
n=num_signal,
190+
nsamples=nsamples,
191+
min_length_ratio=min_length_ratio,
192+
no_overlap=no_overlap,
193+
amp=amp,
194+
)
166195

167196
# generate array signal in frequency domain
168197
signal_fre_domain = np.fft.fft(incidence_signal, axis=1)

doa_py/signals.py

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,28 @@ def fs(self):
150150
return self._fs
151151

152152
@abstractmethod
153-
def gen(self, n, nsamples, amp=None):
153+
def gen(
154+
self, n, nsamples, min_length_ratio=0.1, no_overlap=False, amp=None
155+
):
156+
"""Generate sampled signals
157+
158+
Args:
159+
n (int): Number of signals
160+
nsamples (int): Number of snapshots
161+
min_length_ratio (float): Minimum length ratio of the frequency
162+
range in (f_max - f_min)
163+
no_overlap (bool): If True, generate signals with non-overlapping
164+
subbands
165+
amp (np.array): Amplitude of the signals (1D array of size n), used
166+
to define different amplitudes for different signals.
167+
By default it will generate equal amplitude signal.
168+
169+
Returns:
170+
signal (np.array): Sampled signals
171+
"""
154172
raise NotImplementedError
155173

156-
def _gen_fre_ranges(self, n, min_length_ratio=0.3):
174+
def _gen_fre_bands(self, n, min_length_ratio=0.1):
157175
"""Generate frequency ranges for each boardband signal
158176
159177
Args:
@@ -166,12 +184,41 @@ def _gen_fre_ranges(self, n, min_length_ratio=0.3):
166184
(n, 2)
167185
"""
168186
min_length = (self._f_max - self._f_min) * min_length_ratio
169-
ranges = np.zeros((n, 2))
187+
bands = np.zeros((n, 2))
170188
for i in range(n):
171189
length = self._rng.uniform(min_length, self._f_max - self._f_min)
172190
start = self._rng.uniform(self._f_min, self._f_max - length)
173-
ranges[i] = [start, start + length]
174-
return ranges
191+
bands[i] = [start, start + length]
192+
return bands
193+
194+
def _generate_non_overlapping_bands(self, n, min_length_ratio=0.1):
195+
"""Generate n non-overlapping frequency bands within a specified range.
196+
197+
Args:
198+
n (int): Number of non-overlapping bands to generate.
199+
min_length_ratio (float): Minimum length of each band as a ratio of
200+
the maximum possible length.
201+
202+
Returns:
203+
np.ndarray: An array of shape (n, 2) where each row represents a
204+
frequency band [start, end].
205+
"""
206+
max_length = (self._f_max - self._f_min) // n
207+
min_length = max_length * min_length_ratio
208+
209+
bands = np.zeros((n, 2))
210+
211+
for i in range(n):
212+
length = self._rng.uniform(min_length, max_length)
213+
start = self._rng.uniform(
214+
self._f_min + i * max_length,
215+
self._f_min + (i + 1) * max_length - length,
216+
)
217+
new_band = [start, start + length]
218+
219+
bands[i] = new_band
220+
221+
return bands
175222

176223

177224
class ChirpSignal(BroadSignal):
@@ -187,9 +234,15 @@ def __init__(self, f_min, f_max, fs, rng=None):
187234
"""
188235
super().__init__(f_min, f_max, fs, rng=rng)
189236

190-
def gen(self, n, nsamples, amp=None):
237+
def gen(
238+
self, n, nsamples, min_length_ratio=0.1, no_overlap=False, amp=None
239+
):
191240
amp = self._get_amp(amp, n)
192-
fre_ranges = self._gen_fre_ranges(n)
241+
if no_overlap:
242+
fre_ranges = self._generate_non_overlapping_bands(
243+
n, min_length_ratio
244+
)
245+
fre_ranges = self._gen_fre_bands(n, min_length_ratio)
193246

194247
t = np.arange(nsamples) * 1 / self._fs
195248
f0 = fre_ranges[:, 0]
@@ -222,9 +275,16 @@ def __init__(self, f_min, f_max, fs, ncarriers=100, rng=None):
222275
super().__init__(f_min, f_max, fs, rng=rng)
223276
self._ncarriers = ncarriers
224277

225-
def gen(self, n, nsamples, amp=None):
278+
def gen(
279+
self, n, nsamples, min_length_ratio=0.1, no_overlap=False, amp=None
280+
):
226281
amp = self._get_amp(amp, n)
227-
fre_ranges = self._gen_fre_ranges(n)
282+
if no_overlap:
283+
fre_ranges = self._generate_non_overlapping_bands(
284+
n, min_length_ratio
285+
)
286+
else:
287+
fre_ranges = self._gen_fre_bands(n, min_length_ratio)
228288

229289
# generate random carrier frequencies
230290
fres = self._rng.uniform(

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "doa_py"
3-
version = "0.2.2"
3+
version = "0.2.3"
44
description = "DOA estimation algorithms implemented in Python"
55
readme = "README.md"
66
license = { text = "MIT" }

0 commit comments

Comments
 (0)