Fix Chirp Correlator and Cal Type in Raw, NET, RSLC EAP and NEB#231
Fix Chirp Correlator and Cal Type in Raw, NET, RSLC EAP and NEB#231rad-eng-59 merged 42 commits intoisce-framework:developfrom
Conversation
|
@Tyler-g-hudson , FYI your valuable comments from #192 are already incorporated in this PR. |
bhawkins
left a comment
There was a problem hiding this comment.
Wow! You've done an admirable job sorting out all the vagaries of the loopback cal data. Honestly, I'm not sure I understand every detail and might've made some stupid comments as a result.
I ended up writing down a lot of comments while reading through the changes, but I think there's only one that I consider a blocker. It's the one to do with the crosspol indexing, and I'll go add some emoji to make it stand out. It may just be a misunderstanding on my part.
| f'Missing dataset {p_ds_i} in {self.filename}.' | ||
| f' Detailed error -> {err}' | ||
| ) | ||
| return None |
There was a problem hiding this comment.
I think I prefer an exception instead of a special return value, but maybe I'll reconsider after seeing the usage. I guess it's a private method (leading underscore in name), so maybe that should be considered as well.
There was a problem hiding this comment.
The intention was to handle those old and/or simulated NISAR products and test cases. This behavior is also documented. I'll add more for clarification of the intention.
There was a problem hiding this comment.
I understand the intent. My point is that requiring the user to check return values is error-prone and not Pythonic. A mandatory user-side if/else block on the return value is worse than a try/catch block since neglecting the former could cause all sorts of weird behavior, while neglecting the latter halts the program immediately. I'd agree that it might be nice to raise a NotImplementedError here with a nice message rather than the h5py KeyError. Anyways, I don't consider this a blocking issue.
There was a problem hiding this comment.
My hands are tight here because of other external factors such as exiting ISCE3 test sample files w/ old formats, broken L0B spec, etc. I had to compromise to avoid a bunch of failures. Also, None is a valid return value not an exception as described in the document. I'm a big fan of try and exception but that won't here. Try your approach and see how things will fall apart.
There was a problem hiding this comment.
Let's consider two designs from the user perspective.
Design A:
cc = _parse_chirpcorrelator_from_hrt_qfsp(txrx_pol)
if cc is None:
# handle L0Bs with missing telemetry
Design B:
try:
cc = _parse_chirpcorrelator_from_hrt_qfsp(txrx_pol)
except NotImplementedError:
# handle L0Bs with missing telemetry
How does Design A handle "test sample files w/ old formats, broken L0B spec, etc." better than B? If you're saying that our unit tests or end-to-end tests won't pass with Design B, then it sounds like we have a lot of places that are missing the necessary logic to avoid incorrect behavior. I actually doubt that's the case since this is a new function, and all the usages should be contained in this PR.
There was a problem hiding this comment.
No point of arguing here. We need an offline meeting for this.
| except KeyError as err: | ||
| warn(f'Missing dataset "{p_type}" in ' | ||
| f'"{self.filename}". Detailed error -> {err}') | ||
| return None |
There was a problem hiding this comment.
The explicit None is for clarity and readability rather than using implicit None return for such scenario where we have different return type. This doesn't make it non-pythonic given this is exceptional scenario where None is returned under the special circumstance, in this case, for missing filed value in L0B. So, I think its existence is more help than harm.
There was a problem hiding this comment.
Isn't the Pythonic way to handle an "exceptional scenario" an... Exception? 😆
There was a problem hiding this comment.
Nope, because None is a valid return to handle special circumstances as explained in the docstring. Perhaps, there are numpy/scipy functions that return two different types of output depending on the input.
| ds_rgl_idx = f5[rgl_idx_path] | ||
| except KeyError as err: | ||
| warn(f'Can not parse range line index from HRT. Error -> {err}') | ||
| return None |
There was a problem hiding this comment.
ditto (see previous rational)
| raise ValueError('"channel_adj_factors" are zeros for all ' | ||
| 'active RX channels!') | ||
| rx_wgt *= cor_fact[:, None] | ||
| elif len(channel_adj_factors) == 1: # fixed scalar |
There was a problem hiding this comment.
Not sure I understand the use case for a scalar channel_adj_factors.
There was a problem hiding this comment.
A fixed overall scalar for all channels rather than repeating every single value in an array.
There was a problem hiding this comment.
Yeah, I just mean we already have two other ways to get a constant scaling into the processor, which is arguably too many already.
There was a problem hiding this comment.
I believe there is another reason related to a isce3 test suite and sample product that breaks w/o this conditional block.
bhawkins
left a comment
There was a problem hiding this comment.
Thanks for the quick revisions! The thing I was worried about turned out to be a red herring. For the sake of completeness, I documented a few minor gripes, but they shouldn't be considered blockers. LGTM.
|
We reviewed this PR and agreed that in few places where a function returns None, perhaps the better approach is to raise an exception. |
Well, there were two flavors discussed in the meeting. I have gone with the latter one which requires minimum update w/o breaking any code while preserve consistency among functions with handling of missing datasets in L0B products. I have tested it over a short QP and DP. Let me know if there is still objection so I can provide the correct updates to Alice in regards to updating L0B process. Note that I'm trying to keep two separate repos, this one and external one, consistent and synced. |
| True if the L0B product is quad pol otherwise False. | ||
|
|
||
| """ | ||
| return polarization_type_from_raw(raw) == PolarizationTypeId.quad |
There was a problem hiding this comment.
| return polarization_type_from_raw(raw) == PolarizationTypeId.quad | |
| poltype = polarization_type_from_raw(raw) | |
| if poltype is None: | |
| raise ValueError("L0B does not contain polarization telemetry " | |
| "so cannot determine whether it is quad pol") | |
| return poltype |
When there's a special return value, not checking for it is almost always a bug. Raising an exception here is another way to address the edge case I identified in chirpcorrelator_caltype_from_raw.
There was a problem hiding this comment.
This doesn't make sense here. This function return a boolean not a pol type to decide whether it is quad pol or not. If the respective field is missing in L0B, then the return pol type value will be None and this logic leads to False, that is it won't be quad pol. The warn message already issued due to nonexistent key.
There was a problem hiding this comment.
I understand the logic. IMO it's incorrect behavior for is_raw_quad_pol to return False when the telemetry is missing. It could still be QP (for example, there could be four images in the file). "I don't know" isn't the same as "I know it's false."
Next you're going to say, okay fine, let's have this function return None to indicate that. And then I'll say okay great, now we have to check for None in chirpcorrelator_caltype_from_raw and we'll finally have correct C-style error handling in Python. 😜
There was a problem hiding this comment.
I have just replaced return with raise and guess what, isce3 tests like the noise estimator (NET) , focus.py, fail, thanks to simulated and/or old L0B products. If the warning message for missing field along with the dcostring of the function are not clear to the user then I am not sure how propagating exception over several module will be more useful for the user whom runs functions blindfoldly. Any suggestion?
There was a problem hiding this comment.
Great! I think it's good when the code issues an error when there's a mismatch between the logic and the input data. I'll build this PR with the suggestion above, run the tests, and see if I can come up with a good suggestion for any failures.
There was a problem hiding this comment.
I just did some changes to accommodate your suggestions except for this function with a reasonable exception.. Let me test it and upload the commit and see if you like it or not. Give me a few minutes plz.
There was a problem hiding this comment.
LOL I stepped away for dinner and you'd already changed everything by the time I got back. You're quick!
hfattahi
left a comment
There was a problem hiding this comment.
LGTM. Thanks @rad-eng-59 for this important addition. I have tested InSAR workflow with RSLCs generated with this PR and confirm the significant improvement in phase and offsets between pairs acquired with different sampling characteristics.
|
@bhawkins , @hfattahi , sorry for the last minute update. I have simply added three lines to better support old or sim L0B products including the edge case for determining quad pol when its respective field/id is missing in L0B . Please check out the new commit and let me know if you both are okay with that prior to merging. Thanks |
bhawkins
left a comment
There was a problem hiding this comment.
The latest tweak looks fine to me.
* Add a new mod to nisar.antenna to compute RX imbalances from Raw/L0B * Modify pattern.py to compute and apply RX imbalances per freq band * Update beamformer to allow single scalar for RX channel adjustment * Pass frequency band to EAP block in focus.py * Add a function to compute pulsewidth delay in sequential TX * Modify pattern.py to adjustment RD for the second band in RX DBF * Fix the log in pattern.py * Correct the sign for RD correction of band B in RX DBF pattern * Use slant range diff between A and B in place of pulsewidth delay * Parse caltone frequency from DRT in L0B * Add onboard DBF delay offset of 2.1474 us to RD in pattern.py * Warn if array size in rx channel imbalance class is not 12 * Augment Raw w/ QP-support chirp correlator and caltype functions * Fix noise estimator module to support full QP noise products * Fix TxTRM of TX pattern formaton in pattern.py in case of QP * Fix full NESZ estimation in focus.py for QP * Add support for QP in computing RX channel imbalance * Get caltone freq from runconfig rather than DRT in focus.py for now * Fix handling of CPI update in MEE noise estimator * Handle special case when caltone and chirp freq is the same * Add optional arg `delay_ofs_dbf` to ant pattern ctor in focus.py * Issue warning for too small value of caltone frequency in drt
This PR is the merged combination of the two exiting PR #192 and PR #211 plus some minor improvements.
The main reason for the merge is that these two older PRs are fixing mainly the the same underlying issue with telemetry but for two different applications. Besides, this avoids merging conflict in future while placing the respective common helper functions under appropriate namespaces at once. Thus, once this PR is reviewed and ready to merge, those two older PRs can be safely removed.
Here is the summary of key updates: