Nothing stops you correcting for a preamp gain, an attenuator, or a second
cable today except that the API has no place to put them. cable_losses is
the only correction the signature admits, and it is not special: it is a
frequency/amplitude array that gets deduplicated, interpolated onto the
analyzer frequencies, and added. Any number of others would go through the
same three steps.
Proposed signature
A keyword-only mapping of name to array, alongside cable_losses rather than
in place of it:
def apply_antenna_factor(
analyzer_readings: npt.NDArray,
antenna_factors: npt.NDArray,
cable_losses: npt.NDArray | None = None,
keep_max: bool = True,
allow_extrapolation: bool = False,
*,
corrections: Mapping[str, npt.NDArray] | None = None,
) -> npt.NDArray:
Each value is deduplicated with _remove_duplicate_frequencies under the same
keep_max, interpolated with _interpolate_at under the same
allow_extrapolation, and added to the result. remove_antenna_factor takes
the same argument and subtracts them, so that applying and removing with the
same corrections still round trips.
The key is what the correction is, and it reaches the user in
_interpolate_at's error message, which today reads
3 of 40 frequencies fall outside the frequencies covered by the antenna
factors, which span ...
and would read outside the frequencies covered by the preamp gain for
corrections={"preamp gain": ...}. So the names want to be lowercase noun
phrases. That is the whole reason for a mapping rather than a list: a
positional array that fails to cover the sweep can only be reported by index.
cable_losses stays, and is exactly equivalent to
corrections={"cable losses": ...}. Passing both is fine and they are merged;
passing cable_losses and a correction literally named cable losses should
raise rather than pick one.
Signs follow the existing convention. apply_antenna_factor adds every
correction, as it does with the antenna factors and cable losses, per Eqn 7.62
in Paul. Anything that has to come off instead — a preamp gain, say — is
negated by the caller, which is already what the docstring says to do to the
antenna factors to get the H-field form.
The part that needs a decision
apply_antenna_factor_show_af_cl returns
(incident_field, antenna_factors, cable_losses), and the interpolated
corrections belong in that return. A fourth element breaks every
a, b, c = ... at the call site, so it cannot simply grow.
The way out is a successor that returns a NamedTuple — incident_field,
antenna_factors, cable_losses, and corrections as a dict keyed by the
same names — with apply_antenna_factor_show_af_cl kept as a thin wrapper
that returns the first three. That also retires a name that has never read
well.
Why not the signature this issue originally proposed
The issue was filed in 2013 asking for:
def apply_antenna_factor(base_array, keep_max=True, *args, **kwargs):
Three things argue against it now:
- It breaks every positional caller, and
keep_max moving into second
position means the breakage is silent for anyone passing antenna factors
positionally: they become the keep_max flag.
allow_extrapolation was added since, and it is a keyword that has to reach
_interpolate_at for each correction. In **kwargs it sits in the same bag
as the correction arrays themselves, so the function has to guess which keys
are settings and which are data.
- There are three public entry points now rather than one, and
_interpolate_at wants a name per correction for its error messages.
Variadic positional args have none to give it.
The capability being asked for is the same one. Only the spelling has been
brought forward.
Nothing stops you correcting for a preamp gain, an attenuator, or a second
cable today except that the API has no place to put them.
cable_lossesisthe only correction the signature admits, and it is not special: it is a
frequency/amplitude array that gets deduplicated, interpolated onto the
analyzer frequencies, and added. Any number of others would go through the
same three steps.
Proposed signature
A keyword-only mapping of name to array, alongside
cable_lossesrather thanin place of it:
Each value is deduplicated with
_remove_duplicate_frequenciesunder the samekeep_max, interpolated with_interpolate_atunder the sameallow_extrapolation, and added to the result.remove_antenna_factortakesthe same argument and subtracts them, so that applying and removing with the
same corrections still round trips.
The key is what the correction is, and it reaches the user in
_interpolate_at's error message, which today readsand would read
outside the frequencies covered by the preamp gainforcorrections={"preamp gain": ...}. So the names want to be lowercase nounphrases. That is the whole reason for a mapping rather than a list: a
positional array that fails to cover the sweep can only be reported by index.
cable_lossesstays, and is exactly equivalent tocorrections={"cable losses": ...}. Passing both is fine and they are merged;passing
cable_lossesand a correction literally namedcable lossesshouldraise rather than pick one.
Signs follow the existing convention.
apply_antenna_factoradds everycorrection, as it does with the antenna factors and cable losses, per Eqn 7.62
in Paul. Anything that has to come off instead — a preamp gain, say — is
negated by the caller, which is already what the docstring says to do to the
antenna factors to get the H-field form.
The part that needs a decision
apply_antenna_factor_show_af_clreturns(incident_field, antenna_factors, cable_losses), and the interpolatedcorrections belong in that return. A fourth element breaks every
a, b, c = ...at the call site, so it cannot simply grow.The way out is a successor that returns a
NamedTuple—incident_field,antenna_factors,cable_losses, andcorrectionsas a dict keyed by thesame names — with
apply_antenna_factor_show_af_clkept as a thin wrapperthat returns the first three. That also retires a name that has never read
well.
Why not the signature this issue originally proposed
The issue was filed in 2013 asking for:
Three things argue against it now:
keep_maxmoving into secondposition means the breakage is silent for anyone passing antenna factors
positionally: they become the
keep_maxflag.allow_extrapolationwas added since, and it is a keyword that has to reach_interpolate_atfor each correction. In**kwargsit sits in the same bagas the correction arrays themselves, so the function has to guess which keys
are settings and which are data.
_interpolate_atwants a name per correction for its error messages.Variadic positional args have none to give it.
The capability being asked for is the same one. Only the spelling has been
brought forward.