2
2
import journal
3
3
from scipy .fft import fft , ifft , fftfreq
4
4
from scipy .signal import resample
5
+ from dataclasses import dataclass
5
6
6
7
import isce3
7
8
from nisar .workflows .focus import cosine_window
8
9
9
10
10
- def get_meta_data_bandpass (slc_product , freq ):
11
- """Get meta data from SLC object.
12
-
13
- Parameters
14
- ----------
15
- slc_product : nisar.products.readers.SLC
16
- slc object
17
- freq : {'A', 'B'}
18
- frequency band
19
-
20
- Returns
21
- -------
22
- meta_data : dict
23
- dict containing meta_data
24
- """
25
- meta_data = dict ()
26
- rdr_grid = slc_product .getRadarGrid (freq )
27
- meta_data ['rg_pxl_spacing' ] = rdr_grid .range_pixel_spacing
28
- meta_data ['wavelength' ] = rdr_grid .wavelength
29
- meta_data ['rg_sample_freq' ] = isce3 .core .speed_of_light * \
30
- 0.5 / meta_data ['rg_pxl_spacing' ]
31
- meta_data ['rg_bandwidth' ] = slc_product .getSwathMetadata (
32
- freq ).processed_range_bandwidth
33
- meta_data ['center_frequency' ] = isce3 .core .speed_of_light / \
34
- meta_data ['wavelength' ]
35
- meta_data ['slant_range' ] = rdr_grid .slant_range
36
- return meta_data
11
+ @dataclass (frozen = True )
12
+ class bandpass_meta_data :
13
+ # slant range spacing
14
+ rg_pxl_spacing : float
15
+ # wavelength
16
+ wavelength : float
17
+ # sampling frequency
18
+ rg_sample_freq : float
19
+ #bandiwdth
20
+ rg_bandwidth : float
21
+ #center frequency
22
+ center_freq : float
23
+ #slant range
24
+ slant_range : 'method'
25
+
26
+ @classmethod
27
+ def load_from_slc (cls , slc_product , freq ):
28
+ """Get meta data from SLC object.
29
+ Parameters
30
+ ----------
31
+ slc_product : nisar.products.readers.SLC
32
+ slc object
33
+ freq : {'A', 'B'}
34
+ frequency band
35
+ Returns
36
+ -------
37
+ meta_data : bandpass_meta_data
38
+ bandpass meta data object
39
+ """
40
+ rdr_grid = slc_product .getRadarGrid (freq )
41
+ rg_sample_freq = isce3 .core .speed_of_light * 0.5 / rdr_grid .range_pixel_spacing
42
+ rg_bandwidth = slc_product .getSwathMetadata (freq ).processed_range_bandwidth
43
+ center_frequency = isce3 .core .speed_of_light / rdr_grid .wavelength
44
+ return cls (rdr_grid .range_pixel_spacing , rdr_grid .wavelength , rg_sample_freq ,
45
+ rg_bandwidth , center_frequency , rdr_grid .slant_range )
37
46
38
47
39
48
def check_range_bandwidth_overlap (ref_slc , sec_slc , pols ):
@@ -61,14 +70,14 @@ def check_range_bandwidth_overlap(ref_slc, sec_slc, pols):
61
70
mode = dict ()
62
71
63
72
for freq , pol_list in pols .items ():
64
- ref_meta_data = get_meta_data_bandpass (ref_slc , freq )
65
- sec_meta_data = get_meta_data_bandpass (sec_slc , freq )
66
-
67
- ref_wvl = ref_meta_data [ ' wavelength' ]
68
- sec_wvl = sec_meta_data [ ' wavelength' ]
69
- ref_bw = ref_meta_data [ ' rg_bandwidth' ]
70
- sec_bw = sec_meta_data [ ' rg_bandwidth' ]
71
-
73
+ ref_meta_data = bandpass_meta_data . load_from_slc (ref_slc , freq )
74
+ sec_meta_data = bandpass_meta_data . load_from_slc (sec_slc , freq )
75
+
76
+ ref_wvl = ref_meta_data . wavelength
77
+ sec_wvl = sec_meta_data . wavelength
78
+ ref_bw = ref_meta_data . rg_bandwidth
79
+ sec_bw = sec_meta_data . rg_bandwidth
80
+
72
81
# check if two SLCs have same bandwidth and center frequency
73
82
if (ref_wvl != sec_wvl ) or (ref_bw != sec_bw ):
74
83
if ref_bw > sec_bw :
@@ -133,7 +142,7 @@ def bandpass_shift_spectrum(self,
133
142
high_frequency : float
134
143
high frequency band to be passed [Hz]
135
144
new_center_frequency : float
136
- new center frequency for bandpass [Hz]
145
+ new center frequency for new bandpassed slc [Hz]
137
146
window_function : str
138
147
window type {tukey, kaiser, cosine}
139
148
window_shape : float
@@ -148,8 +157,8 @@ def bandpass_shift_spectrum(self,
148
157
-------
149
158
resampled_slc or slc_demodulate: numpy.ndarray
150
159
numpy array of bandpassed slc
151
- if resampling is True, return resampled_slc
152
- if resampling is False, return slc_demodulate
160
+ if resampling is True, return resampled slc with bandpass and demodulation
161
+ if resampling is False, return slc with bandpass and demodulation without resampling
153
162
meta : dict
154
163
dict containing meta data of bandpassed slc
155
164
center_frequency, rg_bandwidth, range_spacing, slant_range
@@ -237,11 +246,8 @@ def bandpass_spectrum(self,
237
246
238
247
Returns
239
248
-------
240
- filtered_slc : numpy.ndarray
249
+ slc_bandpassed : numpy.ndarray
241
250
numpy array of bandpassed slc
242
- meta : dict
243
- dict containing meta data of bandpassed slc
244
- center_frequency, rg_bandwidth, range_spacing, slant_range
245
251
"""
246
252
error_channel = journal .error ('splitspectrum.bandpass_spectrum' )
247
253
@@ -292,11 +298,11 @@ def bandpass_spectrum(self,
292
298
spectrum_target = fft (slc_raster , n = fft_size ) / \
293
299
window_target
294
300
# apply new bandpass window to spectrum
295
- slc_bp = ifft (spectrum_target
301
+ slc_bandpassed = ifft (spectrum_target
296
302
* window_bandpass
297
303
* np .sqrt (resampling_scale_factor ), n = fft_size )
298
304
299
- return slc_bp
305
+ return slc_bandpassed
300
306
301
307
def demodulate_slc (self , slc_array , diff_frequency , rg_sample_freq ):
302
308
""" Demodulate SLC
@@ -404,32 +410,99 @@ def get_range_bandpass_window(self,
404
410
window_shape = window_shape
405
411
)
406
412
407
- elif window_kind == 'kaiser' or window_kind == 'cosine' :
408
- if ( window_kind == 'kaiser' ) and not (window_shape > 0 ):
413
+ elif window_kind == 'kaiser' :
414
+ if not (window_shape > 0 ):
409
415
err_str = f"Expected pedestal bigger than 0, got { window_shape } ."
410
416
error_channel .log (err_str )
411
417
raise ValueError (err_str )
412
- if (window_kind == 'cosine' ) and not (0 <= window_shape <= 1 ):
418
+
419
+ filter_1d = self .construct_range_bandpass_kaiser (
420
+ frequency_range = frequency ,
421
+ freq_low = freq_low ,
422
+ freq_high = freq_high ,
423
+ window_shape = window_shape
424
+ )
425
+
426
+ elif window_kind == 'cosine' :
427
+ if not (0 <= window_shape <= 1 ):
413
428
err_str = f"Expected window_shape between 0 and 1, got { window_shape } ."
414
429
error_channel .log (err_str )
415
430
raise ValueError (err_str )
416
-
417
- filter_1d = self .construct_range_bandpass_kaiser_cosine (
431
+ filter_1d = self .construct_range_bandpass_cosine (
418
432
frequency_range = frequency ,
419
433
freq_low = freq_low ,
420
434
freq_high = freq_high ,
421
- window_function = window_kind ,
422
435
window_shape = window_shape
423
- )
424
-
436
+ )
437
+
425
438
else :
426
439
err_str = f"window { window_kind } not in (Kaiser, Cosine, Tukey)."
427
440
error_channel .log (err_str )
428
441
raise ValueError (err_str )
429
442
430
443
return filter_1d
431
444
432
- def construct_range_bandpass_kaiser_cosine (self ,
445
+ def construct_range_bandpass_cosine (self ,
446
+ frequency_range ,
447
+ freq_low ,
448
+ freq_high ,
449
+ window_shape ):
450
+ '''Generate a Cosine bandpass window
451
+
452
+ Parameters
453
+ ----------
454
+ frequency_range : np.ndarray
455
+ Discrete Fourier Transform sample frequency range bins[Hz]
456
+ freq_low : float
457
+ low frequency to be passed [Hz]
458
+ freq_high: float
459
+ high frequency to be passed [Hz]
460
+ window_shape : float
461
+ parameter for the cosine window
462
+
463
+ Returns
464
+ -------
465
+ filter_1d : np.ndarray
466
+ one dimensional Cosine bandpass filter in frequency domain
467
+ '''
468
+ filter_1d = self ._construct_range_bandpass_kaiser_cosine (frequency_range ,
469
+ freq_low ,
470
+ freq_high ,
471
+ cosine_window ,
472
+ window_shape )
473
+ return filter_1d
474
+
475
+ def construct_range_bandpass_kaiser (self ,
476
+ frequency_range ,
477
+ freq_low ,
478
+ freq_high ,
479
+ window_shape ):
480
+ '''Generate a Kaiser bandpass window
481
+
482
+ Parameters
483
+ ----------
484
+ frequency_range : np.ndarray
485
+ Discrete Fourier Transform sample frequency range bins[Hz]
486
+ freq_low : float
487
+ low frequency to be passed [Hz]
488
+ freq_high: float
489
+ high frequency to be passed [Hz]
490
+ window_shape : float
491
+ parameter for the kaiser window
492
+
493
+ Returns
494
+ -------
495
+ filter_1d : np.ndarray
496
+ one dimensional kaiser bandpass filter in frequency domain
497
+ '''
498
+ filter_1d = self ._construct_range_bandpass_kaiser_cosine (frequency_range ,
499
+ freq_low ,
500
+ freq_high ,
501
+ np .kaiser ,
502
+ window_shape )
503
+ return filter_1d
504
+
505
+ def _construct_range_bandpass_kaiser_cosine (self ,
433
506
frequency_range ,
434
507
freq_low ,
435
508
freq_high ,
@@ -445,8 +518,8 @@ def construct_range_bandpass_kaiser_cosine(self,
445
518
low frequency to be passed [Hz]
446
519
freq_high: float
447
520
high frequency to be passed [Hz]
448
- window_function : str
449
- window type {kaiser, cosine }
521
+ window_function : class function
522
+ window type {np. kaiser, cosine_window }
450
523
window_shape : float
451
524
parameter for the kaiser window
452
525
@@ -484,11 +557,10 @@ def construct_range_bandpass_kaiser_cosine(self,
484
557
subband_length = idx_freq_high - idx_freq_low + 1
485
558
486
559
filter_1d = np .zeros ([fft_size ], dtype = 'complex' )
487
- if window_function == 'kaiser' :
488
- subwindow = np .kaiser (subband_length , window_shape )
489
- elif window_function == 'cosine' :
490
- subwindow = cosine_window (subband_length , window_shape )
491
-
560
+
561
+ # window_function is function class {np.kaiser or consine}
562
+ subwindow = window_function (subband_length , window_shape )
563
+
492
564
if idx_freq_low >= idx_freq_high :
493
565
filter_1d [idx_freq_low :] = subwindow [0 : fft_size - idx_freq_low ]
494
566
filter_1d [: idx_freq_high + 1 ] = subwindow [fft_size - idx_freq_low :]
@@ -531,7 +603,7 @@ def construct_range_bandpass_tukey(self,
531
603
# Get the absolute value of shifted frequency
532
604
freq = frequency_range [i ]
533
605
freqabs = np .abs (freq - freq_mid )
534
- # Passband. Pass original signals within a selected range of frequencies
606
+ # Passband. i.e. range of frequencies that can pass through a filter
535
607
if (freq <= (freq_high - df )) and (freq >= (freq_low + df )):
536
608
filter_1d [i ] = 1
537
609
# Transition region
0 commit comments