Skip to content

Commit 63853b2

Browse files
authored
Merge branch 'isce-framework:develop' into develop
2 parents dd80c38 + 7bb4b5c commit 63853b2

21 files changed

+530
-121
lines changed

python/packages/nisar/pointing/doppler_lut_from_raw.py

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
def doppler_lut_from_raw(raw, *, freq_band='A', txrx_pol=None,
2222
orbit=None, attitude=None, ant=None,
2323
dem=None, num_rgb_avg=8, az_block_dur=4.0,
24-
time_interval=2.0, dop_method='CDE', subband=False,
24+
rangeline_limit=None, time_interval=2.0,
25+
dop_method='CDE', subband=False,
2526
polyfit_deg=3, polyfit=False, exclude_beams=None,
2627
out_path='.', plot=False, logger=None):
2728
"""Generates 2-D Doppler LUT as a function of slant range and azimuth time.
@@ -77,6 +78,10 @@ def doppler_lut_from_raw(raw, *, freq_band='A', txrx_pol=None,
7778
az_block_dur : float, default=4.0
7879
Azimuth block duration in seconds defining time-domain correlator
7980
length used in Doppler estimator.
81+
rangeline_limit : tuple[int | None, int | None], optional
82+
0-based range line [start, stop) indices to limit echo range lines to
83+
be processed. Default is all range lines. The start/stop will be
84+
limited to within [0, total rangelines)!
8085
time_interval : float, default=2.0
8186
Time stamp interval between azimuth blocks in seconds.
8287
It should not be larger than "az_block_dur".
@@ -269,10 +274,10 @@ def doppler_lut_from_raw(raw, *, freq_band='A', txrx_pol=None,
269274
logger.warning(
270275
f'Excluded beams {exclude_beams} is out of range of '
271276
f'active RX channels {list_rx_active}!'
272-
)
277+
)
273278
exclude_beams.intersection_update(list_rx_active)
274279
logger.warning(
275-
f'Updated list of excluded beams -> {exclude_beams}')
280+
f'Updated list of excluded beams -> {exclude_beams}')
276281
# initialize DEM object if None
277282
if dem is None:
278283
dem = DEMInterpolator()
@@ -417,9 +422,26 @@ def time_dop_est(echo, prf, lag=1, axis=None):
417422
sr_stop = sr_start + (num_blk_rg - 1) * sr_spacing
418423
slrg_per_blk = np.linspace(sr_start, sr_stop, num=num_blk_rg)
419424

425+
# set range line slice of echo
426+
rgl_start_stop = [0, tot_pulses]
427+
if rangeline_limit is not None:
428+
if rangeline_limit[0] is not None:
429+
rgl_start_stop[0] = min(
430+
max(rangeline_limit[0], 0),
431+
tot_pulses - 1)
432+
if rangeline_limit[1] is not None:
433+
rgl_start_stop[1] = max(
434+
min(rangeline_limit[1], tot_pulses),
435+
rgl_start_stop[0] + 1)
436+
logger.info('[start, stop) range line index to be processed -> '
437+
f'{rgl_start_stop}')
438+
# update number of range lines to be processed
439+
num_rgls = rgl_start_stop[1] - rgl_start_stop[0]
440+
logger.info(f'Number of range lines to be processed -> {num_rgls}')
441+
420442
# form the blocks of range lines / azimuth bins
421443
len_az_blk_dur, len_tm_int, num_blk_az = _get_az_block_interval_len(
422-
tot_pulses, az_block_dur, prf, time_interval)
444+
num_rgls, az_block_dur, prf, time_interval)
423445

424446
logger.info(
425447
f'Final full azimuth block duration -> {len_az_blk_dur/prf:.3f} (sec)')
@@ -434,7 +456,9 @@ def time_dop_est(echo, prf, lag=1, axis=None):
434456
logger.info(f'Total number of azimuth blocks -> {num_blk_az}')
435457

436458
slice_lines = _azblk_slice_gen(
437-
tot_pulses, len_az_blk_dur, len_tm_int, num_blk_az)
459+
num_rgls, len_az_blk_dur, len_tm_int,
460+
num_blk_az, idx_start=rgl_start_stop[0]
461+
)
438462

439463
# parse valid subswath index for all range lines used later
440464
valid_sbsw_all = raw.getSubSwaths(freq_band, txrx_pol[0])
@@ -447,8 +471,10 @@ def time_dop_est(echo, prf, lag=1, axis=None):
447471

448472
# initialize the azimuth time block and set an intermediate var
449473
half_az_blk_dur = (len_az_blk_dur - 1) / 2
450-
az_time_blk = np.full(num_blk_az, az_time[0] + half_az_blk_dur * pri,
451-
dtype=float)
474+
az_time_blk = np.full(
475+
num_blk_az,
476+
az_time[rgl_start_stop[0]] + half_az_blk_dur * pri,
477+
dtype=float)
452478
tm_int_pri_prod = len_tm_int * pri
453479

454480
# doppler centroid map is azimuth block by slant-range block
@@ -479,7 +505,7 @@ def time_dop_est(echo, prf, lag=1, axis=None):
479505
echo, rgb_limits = form_single_tap_dbf_echo(
480506
raw_dset, slice_line, el_trans, az_trans,
481507
pos_mid, vel_mid, quat_mid, sr_lsp, dem
482-
)
508+
)
483509
if exclude_beams is not None:
484510
mask_bad = np.zeros(rgb_limits[-1], dtype='bool')
485511
for n_b in exclude_beams:
@@ -489,7 +515,7 @@ def time_dop_est(echo, prf, lag=1, axis=None):
489515
f'For AZ block # {n_azblk + 1}, exclude slant ranges '
490516
f'(m, m) within -> ({sr_lsp[rgb_slice.start] :.3f}, '
491517
f'{sr_lsp[rgb_slice.stop - 1]:.3f}) '
492-
)
518+
)
493519
else: # single channel
494520
echo = raw_dset[slice_line]
495521

@@ -844,7 +870,8 @@ def _get_az_block_interval_len(num_pls: int, az_block_dur: float, prf: float,
844870
# if the block_dur + interval is too large then raise an exception!
845871
if (len_tm_int + len_az_blk_dur) > num_pls:
846872
raise ValueError(
847-
'Sum of azimuth block duration and time interval is large than '
873+
f'Sum of azimuth block duration {len_az_blk_dur / prf} (sec) '
874+
f'and time interval {len_tm_int / prf} (sec) is longer than '
848875
f'echo duration {(num_pls - 1) / prf} (sec)!'
849876
)
850877
# get number of blocks which must be at least 2!
@@ -858,15 +885,14 @@ def _get_az_block_interval_len(num_pls: int, az_block_dur: float, prf: float,
858885

859886

860887
def _azblk_slice_gen(num_pls: int, len_az_blk_dur: int, len_tm_int: int,
861-
num_blk_az: int):
888+
num_blk_az: int, idx_start: int = 0):
862889
"""Slice index generator for azimuth blocks/range lines"""
863890
# generate slice for azimuth block indexing
864-
i_str = 0
865-
i_stp = len_az_blk_dur
866-
for bb in range(num_blk_az):
891+
i_max = idx_start + num_pls
892+
for i_str in range(
893+
idx_start, idx_start + num_blk_az * len_tm_int, len_tm_int):
894+
i_stp = min(i_str + len_az_blk_dur, i_max)
867895
yield slice(i_str, i_stp)
868-
i_str += len_tm_int
869-
i_stp = min(i_str + len_az_blk_dur, num_pls)
870896

871897

872898
def _form_mask_valid_range(tot_rgbs, rgb_valid_sbsw):

python/packages/nisar/pointing/el_null_range_from_raw_ant.py

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def el_null_range_from_raw_ant(
5555
orbit=None,
5656
attitude=None,
5757
az_block_dur=3.0,
58+
rangeline_limit=None,
5859
apply_caltone=False,
5960
imbalances_right2left=None,
6061
sample_delays_wrt_left=None,
@@ -98,6 +99,10 @@ def el_null_range_from_raw_ant(
9899
duration of echo if it is too large.
99100
The min block duration must be equal or larger than nominal mean
100101
PRI (pulse repetition interval).
102+
rangeline_limit : tuple[int | None, int | None], optional
103+
0-based range line [start, stop) indices to limit echo range lines to
104+
be processed. Default is all range lines. The start/stop will be
105+
limited to within [0, total rangelines)!
101106
apply_caltone : bool, default=False
102107
Apply caltone coefficients to RX channels prior to Null formation.
103108
imbalances_right2left : sequence or array of complex float, optional
@@ -226,9 +231,25 @@ def el_null_range_from_raw_ant(
226231
if raw_dset.ndim != 3:
227232
raise RuntimeError(
228233
'Expected Multi-channel Raw echo aka Diagnostic Mode #2 (DM2)!')
229-
num_channels, num_rgls, num_rgbs = raw_dset.shape
234+
num_channels, num_rgls_tot, num_rgbs = raw_dset.shape
230235
logger.info('Shape of the echo data (channels, pulses, ranges) -> '
231-
f'({num_channels, num_rgls, num_rgbs})')
236+
f'({num_channels, num_rgls_tot, num_rgbs})')
237+
# set range line slice of echo
238+
rgl_start_stop = [0, num_rgls_tot]
239+
if rangeline_limit is not None:
240+
if rangeline_limit[0] is not None:
241+
rgl_start_stop[0] = min(
242+
max(rangeline_limit[0], 0),
243+
num_rgls_tot - 1)
244+
if rangeline_limit[1] is not None:
245+
rgl_start_stop[1] = max(
246+
min(rangeline_limit[1], num_rgls_tot),
247+
rgl_start_stop[0] + 1)
248+
logger.info('[start, stop) range line index to be processed -> '
249+
f'{rgl_start_stop}')
250+
# update number of range lines to be processed
251+
num_rgls = rgl_start_stop[1] - rgl_start_stop[0]
252+
logger.info(f'Number of range lines to be processed -> {num_rgls}')
232253

233254
# number of nulls
234255
num_nulls = num_channels - 1
@@ -277,8 +298,11 @@ def el_null_range_from_raw_ant(
277298
# [1, num_rgls]
278299
num_rgl_block = round(az_block_dur * prf)
279300
if num_rgl_block > num_rgls:
280-
logger.warning('Azimuth block duration exceeds max duration. '
281-
'It will be limited to total azimuth duration!')
301+
logger.warning(
302+
f'Azimuth block duration w/ lines # {num_rgl_block} exceeds max '
303+
f'duration w/ lines # {num_rgls}. It will be limited to total '
304+
'azimuth duration!'
305+
)
282306
num_rgl_block = num_rgls
283307
if num_rgl_block < 1:
284308
raise ValueError('Azimuth block duration is smaller than mean PRI!')
@@ -393,7 +417,9 @@ def el_null_range_from_raw_ant(
393417

394418
# get pair number and range line slice generator
395419
pairnum_rglslice = _pair_num_rgl_slice_gen(
396-
num_rgls, num_nulls, num_azimuth_block, num_rgl_block)
420+
num_rgls, num_nulls, num_azimuth_block, num_rgl_block,
421+
idx_start=rgl_start_stop[0]
422+
)
397423

398424
# check if there is matplotlib package needed for plotting if requested
399425
if plot and plt is None:
@@ -583,7 +609,7 @@ def form_overlap_antenna_pairs(ant, list_rx, rx_pol, is_half_p2p=False):
583609

584610

585611
def _pair_num_rgl_slice_gen(num_rgls, num_nulls, num_azimuth_block,
586-
num_rgl_block):
612+
num_rgl_block, idx_start=0):
587613
"""Generates pair number and respective range line slice.
588614
589615
Parameters
@@ -596,6 +622,8 @@ def _pair_num_rgl_slice_gen(num_rgls, num_nulls, num_azimuth_block,
596622
Number of azimuth blocks.
597623
num_rgl_block : int
598624
Number of range lines per azimuth block
625+
idx_start : int, default=0
626+
Start range line index.
599627
600628
Yields
601629
------
@@ -607,16 +635,17 @@ def _pair_num_rgl_slice_gen(num_rgls, num_nulls, num_azimuth_block,
607635
AZ block number starting from 1
608636
609637
"""
610-
i_start = 0
611-
i_stop = 0
612-
for cc in range(1, num_azimuth_block + 1):
638+
for cc, i_start in enumerate(
639+
range(idx_start,
640+
idx_start + num_azimuth_block * num_rgl_block, num_rgl_block
641+
),
642+
start=1):
613643
if cc == num_azimuth_block:
614-
i_stop = num_rgls
644+
i_stop = idx_start + num_rgls
615645
else:
616-
i_stop += num_rgl_block
646+
i_stop = i_start + num_rgl_block
617647
for nn in range(num_nulls):
618648
yield nn, slice(i_start, i_stop), cc
619-
i_start += num_rgl_block
620649

621650

622651
def _is_null_valid(rgb_p2p, rgb_valid_sbsw):

python/packages/nisar/pointing/el_rising_edge_from_raw_ant.py

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323

2424
def el_rising_edge_from_raw_ant(raw, ant, *, dem_interp=None,
2525
freq_band='A', txrx_pol=None,
26-
orbit=None, attitude=None,
27-
az_block_dur=3.0, beam_num=None,
26+
orbit=None, attitude=None, az_block_dur=3.0,
27+
rangeline_limit=None, beam_num=None,
2828
dbf_pow_norm=True, apply_weight=True,
2929
plot=False, out_path='.', logger=None):
3030
"""
@@ -63,6 +63,10 @@ def el_rising_edge_from_raw_ant(raw, ant, *, dem_interp=None,
6363
duration of echo if it is too large.
6464
The min block duration must be equal or larger than nominal mean
6565
PRI (pulse repetition interval).
66+
rangeline_limit : tuple[int | None, int | None], optional
67+
0-based range line [start, stop) indices to limit echo range lines to
68+
be processed. Default is all range lines. The start/stop will be
69+
limited to within [0, total rangelines)!
6670
beam_num : int, optional
6771
Beam number used for fetching a desired beam from antenna object simply
6872
for single-channel raw echo. It will be ignored for SweepSAR case!
@@ -223,9 +227,25 @@ def el_rising_edge_from_raw_ant(raw, ant, *, dem_interp=None,
223227

224228
# Get raw dataset
225229
raw_dset = raw.getRawDataset(freq_band, txrx_pol)
226-
num_rgls, num_rgbs = raw_dset.shape
230+
num_rgls_tot, num_rgbs = raw_dset.shape
227231
logger.info('Shape of the Raw echo data (pulses, ranges) -> '
228-
f'({num_rgls, num_rgbs})')
232+
f'({num_rgls_tot, num_rgbs})')
233+
# set range line slice of echo
234+
rgl_start_stop = [0, num_rgls_tot]
235+
if rangeline_limit is not None:
236+
if rangeline_limit[0] is not None:
237+
rgl_start_stop[0] = min(
238+
max(rangeline_limit[0], 0),
239+
num_rgls_tot - 1)
240+
if rangeline_limit[1] is not None:
241+
rgl_start_stop[1] = max(
242+
min(rangeline_limit[1], num_rgls_tot),
243+
rgl_start_stop[0] + 1)
244+
logger.info('(start, stop) range line index to be processed -> '
245+
f'{rgl_start_stop}')
246+
# update number of range lines to be processed
247+
num_rgls = rgl_start_stop[1] - rgl_start_stop[0]
248+
logger.info(f'Number of range lines to be processed -> {num_rgls}')
229249

230250
# EL angle margin and resolution in radians
231251
el_margin = np.deg2rad(el_margin_deg)
@@ -509,7 +529,9 @@ def _rgb_dbf2echo(nrgb_dbf):
509529
tx_trm_info, norm=True)
510530

511531
# generate rangeline slices
512-
rgl_slices = _rgl_slice_gen(num_rgls, num_azimuth_block, num_rgl_block)
532+
rgl_slices = _rgl_slice_gen(
533+
num_rgls, num_azimuth_block, num_rgl_block, idx_start=rgl_start_stop[0]
534+
)
513535

514536
# build ElPatternEst object used for 2-way power pattern est from echo
515537
# within only rising edge with 3rd-order to be used in roll cost function
@@ -594,10 +616,19 @@ def _rgb_dbf2echo(nrgb_dbf):
594616
idx_el_ant_str = bisect.bisect_left(rx_beams_el.angle, el_ant_min)
595617
idx_el_ant_stp = bisect.bisect_right(rx_beams_el.angle, el_ant_max)
596618
el_ant_slice = slice(idx_el_ant_str, idx_el_ant_stp)
619+
logger.info(
620+
'(min, max) EL angle coverage used from antenna file (deg, deg) '
621+
f'-> ({np.rad2deg(el_ant_min):.3f}, {np.rad2deg(el_ant_max):.3f})'
622+
)
597623
ant_el = rx_beams_el.angle[el_ant_slice]
624+
if ant_el.size < 2:
625+
raise RuntimeError(
626+
f'Not enough antenna EL angles w/ size {ant_el.size} to '
627+
'polyfit EL vs slant range'
628+
)
598629
logger.info(
599-
f'(min, max) antenna EL coverage -> ({np.rad2deg(ant_el[0]):.3f}'
600-
f', {np.rad2deg(ant_el[-1]):.3f}) (deg, deg)'
630+
'(min, max) antenna EL coverage used in edge method (deg, deg) '
631+
f'-> ({np.rad2deg(ant_el[0]):.3f}, {np.rad2deg(ant_el[-1]):.3f})'
601632
)
602633
# convert antenna EL angles to slant range and then compute weighting
603634
# factor based on relative SNR used in weighing cost function of
@@ -613,12 +644,9 @@ def _rgb_dbf2echo(nrgb_dbf):
613644
# build polyfit coeffs to represent EL (deg) as a function
614645
# of slant range (km) with either first or second order within
615646
# a relatively small rising edge region!
616-
if ant_el.size < 2:
617-
raise RuntimeError(
618-
'Not enough antenna EL angles to polyfit EL vs slant range')
619647
pf_sr2el = np.polyfit(
620648
ant_sr * 1e-3, np.rad2deg(ant_el), deg=min(ant_el.size - 1, 2)
621-
)
649+
)
622650
logger.info('Slantrange-to-elevation polyfit coeffs for AZ block # '
623651
f'{n_azblk} -> {pf_sr2el}')
624652
def _slantrange_to_elevation(
@@ -647,7 +675,7 @@ def _slantrange_to_elevation(
647675
slice_el_all = _dbf_windows_to_ant_el_coverge_slice(
648676
dwp_dbf[s_rgl], wl_dbf[s_rgl], rgb_first, sr_start,
649677
sr_spacing, ant_el, _rgb_dbf2echo, _slantrange_to_elevation
650-
)
678+
)
651679
# exclude the portion outside the EL coverage by zeroing out
652680
# its values.
653681
for cc, slice_el in enumerate(slice_el_all):
@@ -986,7 +1014,7 @@ def _form_ant2way_sweepsar(ant_pat_tx, ant_pat_rx, ant_el, tx_wgt,
9861014
return pow2db(ant_powpat_2way)
9871015

9881016

989-
def _rgl_slice_gen(num_rgls, num_azimuth_block, num_rgl_block):
1017+
def _rgl_slice_gen(num_rgls, num_azimuth_block, num_rgl_block, idx_start=0):
9901018
"""Generates pair number and respective range line slice.
9911019
9921020
Parameters
@@ -997,22 +1025,25 @@ def _rgl_slice_gen(num_rgls, num_azimuth_block, num_rgl_block):
9971025
Number of azimuth blocks.
9981026
num_rgl_block : int
9991027
Number of range lines per azimuth block
1028+
idx_start : int, default=0
1029+
Start index.
10001030
10011031
Yields
10021032
------
10031033
slice
10041034
Range line slices
10051035
10061036
"""
1007-
i_start = 0
1008-
i_stop = 0
1009-
for cc in range(1, num_azimuth_block + 1):
1037+
for cc, i_start in enumerate(
1038+
range(idx_start,
1039+
idx_start + num_azimuth_block * num_rgl_block,
1040+
num_rgl_block),
1041+
start=1):
10101042
if cc == num_azimuth_block:
1011-
i_stop = num_rgls
1043+
i_stop = idx_start + num_rgls
10121044
else:
1013-
i_stop += num_rgl_block
1045+
i_stop = i_start + num_rgl_block
10141046
yield slice(i_start, i_stop)
1015-
i_start += num_rgl_block
10161047

10171048

10181049
def _is_rising_edge_valid(rgb_fl, rgb_valid_sbsw):

0 commit comments

Comments
 (0)