pysiral.retracker.samosa_wfm ============================ .. py:module:: pysiral.retracker.samosa_wfm .. autoapi-nested-parse:: This is the SAMOSA+ retracker variant that uses `samosa-waveform-model`, a re-implementation of the SAMPy package. The objective of the re-implementation is better performance (code optimization and multiprocessing) and a greater flexibility for retracking settings (sub-waveform retracking, limiting parameters of the fit) NOTES: ====== List of fit modes: - single fit - all parameters - double fit - all parameters - single fit - fixed mean square slope All variants with options for single core and multi-processing TODO: Implement sigma0/windspeed computation (per switch in config file) TODO: Computation of residuals should be configurable method Workflow 1. Colocate all input variables in dedicated list of input data classes 2. Initialize specified fit method (one class per fit method?) 3. Process waveforms with/without multiprocessing 4. Organize output Attributes ---------- .. autoapisummary:: pysiral.retracker.samosa_wfm.__author__ pysiral.retracker.samosa_wfm.VALID_METHOD_LITERAL pysiral.retracker.samosa_wfm.VALID_METHODS pysiral.retracker.samosa_wfm.DEFAULT_FIT_KWARGS pysiral.retracker.samosa_wfm.SAMOSA_WFM_COLLECT_FIT_PARAMS pysiral.retracker.samosa_wfm.NU_OCOG_COEFS Classes ------- .. autoapisummary:: pysiral.retracker.samosa_wfm.WaveformModelParametersFit pysiral.retracker.samosa_wfm.NormedWaveform pysiral.retracker.samosa_wfm.WaveformFitData pysiral.retracker.samosa_wfm.SAMOSAWaveformFitResult pysiral.retracker.samosa_wfm.SAMOSAWaveformFit pysiral.retracker.samosa_wfm.SAMOSAWaveformCollectionFit pysiral.retracker.samosa_wfm.SAMOSAModelParameterPrediction pysiral.retracker.samosa_wfm.SAMOSAPlusRetracker Functions --------- .. autoapisummary:: pysiral.retracker.samosa_wfm.samosa_fit_samosap_single pysiral.retracker.samosa_wfm.samosa_fit_samosap_standard pysiral.retracker.samosa_wfm.samosa_fit_samosap_specular pysiral.retracker.samosa_wfm.samosa_fit_samosap_single_step2 pysiral.retracker.samosa_wfm.samosa_fit_samosap_standard_step1 pysiral.retracker.samosa_wfm.samosa_fit_samosap_standard_step2 pysiral.retracker.samosa_wfm.get_model_from_args pysiral.retracker.samosa_wfm.total_velocity_from_vector pysiral.retracker.samosa_wfm.get_valid_epoch_range pysiral.retracker.samosa_wfm.get_epoch_bounds pysiral.retracker.samosa_wfm.sampy_misfit pysiral.retracker.samosa_wfm.compute_thermal_noise pysiral.retracker.samosa_wfm.epoch2range pysiral.retracker.samosa_wfm.get_look_angles pysiral.retracker.samosa_wfm.get_least_squares_parameter_sdev pysiral.retracker.samosa_wfm.get_sub_waveform_mask pysiral.retracker.samosa_wfm.get_trailing_edge_lower_envelope_mask pysiral.retracker.samosa_wfm.get_samosa_leading_edge_error pysiral.retracker.samosa_wfm.rmse pysiral.retracker.samosa_wfm.get_nu_from_ocog_width Module Contents --------------- .. py:data:: __author__ :value: 'Stefan Hendricks ' .. py:data:: VALID_METHOD_LITERAL .. py:data:: VALID_METHODS .. py:data:: DEFAULT_FIT_KWARGS .. py:data:: SAMOSA_WFM_COLLECT_FIT_PARAMS :value: False .. py:data:: NU_OCOG_COEFS :value: (11111080.7, 2.50396017) .. py:class:: WaveformModelParametersFit Bases: :py:obj:`samosa_waveform_model.WaveformModelParameters` .. py:attribute:: samosa_step :type: Literal['step1', 'step2'] :value: None .. py:attribute:: num_ddm_evaluations :type: int :value: -1 .. py:class:: NormedWaveform Data container for the input waveform .. py:attribute:: power :type: numpy.ndarray .. py:attribute:: range_bins :type: numpy.ndarray .. py:attribute:: tau :type: numpy.ndarray .. py:attribute:: ocog_width :type: float .. py:attribute:: scaling_factor :type: float .. py:attribute:: radar_mode_flag :type: int .. py:attribute:: window_delay :type: float .. py:attribute:: transmit_power :type: float .. py:attribute:: look_angles :type: numpy.ndarray .. py:attribute:: surface_type :type: str .. py:attribute:: surface_class :type: str .. py:attribute:: thermal_noise :type: float :value: 0.0 .. py:attribute:: first_maximum_index :type: int :value: None .. py:attribute:: idx :type: int :value: None .. py:attribute:: absolute_maximum :type: float .. py:method:: __post_init__() .. py:class:: WaveformFitData Dataclass that contains all data from l1b and l2 data objects required for the SAMOSA waveform model fit .. py:attribute:: idx :type: int .. py:attribute:: waveform_data :type: NormedWaveform .. py:attribute:: scenario_data :type: samosa_waveform_model.ScenarioData .. py:class:: SAMOSAWaveformFitResult Dataclass for all output parameters of the waveform fitting process .. py:attribute:: epoch :type: float .. py:attribute:: retracker_range :type: float .. py:attribute:: retracker_range_standard_error :type: float .. py:attribute:: significant_wave_height :type: float .. py:attribute:: significant_wave_height_standard_error :type: float .. py:attribute:: mean_square_slope :type: float .. py:attribute:: mean_square_slope_standard_error :type: float .. py:attribute:: thermal_noise :type: float .. py:attribute:: misfit :type: float .. py:attribute:: fit_mode :type: str :value: 'n/a' .. py:attribute:: waveform :type: numpy.ndarray :value: None .. py:attribute:: waveform_model :type: numpy.ndarray :value: None .. py:attribute:: sub_waveform_mask :type: numpy.ndarray :value: None .. py:attribute:: misfit_sub_waveform :type: float :value: None .. py:attribute:: number_of_model_evaluations_step1 :type: int :value: -1 .. py:attribute:: number_of_model_evaluations_step2 :type: int :value: -1 .. py:attribute:: fit_return_status_step1 :type: int :value: -2 .. py:attribute:: fit_return_status_step2 :type: int :value: -2 .. py:attribute:: sigma0 :type: float :value: 0.0 .. py:property:: is_sub_waveform_fit :type: bool .. py:property:: samosa_leading_edge_error :type: float .. py:class:: SAMOSAWaveformFit(scenario_data: samosa_waveform_model.ScenarioData, normed_waveform: NormedWaveform, waveform_model: Optional[samosa_waveform_model.SAMOSAWaveformModel] = None, sub_waveform_mask: Optional[numpy.ndarray] = None, waveform_model_kwargs: Optional[Dict] = None, step1_fixed_nu_value: float = 0.0, step2_fixed_swh_value: float = 0.0, amplitude_is_free_param: bool = True, method: str = None) Bases: :py:obj:`object` Class for fitting the SAMOSA waveform model to a single waveform. The purpose of this class is to minimize the CPU load for the fitting procedure. This class will therefore initiliaze the waveform model with all static parameters and then only re-compute the variables that directly depend on the fit parameters. Usage: fit_cls = SAMOSAWaveformFit(scenario_data, normed_waveform, ...) fit_result = scipy.optimize.least_squares(fit_cls.fit_func, ...) .. py:attribute:: samosa_waveform_model :value: None .. py:attribute:: normed_waveform .. py:attribute:: step1_fixed_nu_value :value: 0.0 .. py:attribute:: step2_fixed_swh_value :value: 0.0 .. py:attribute:: amplitude_is_free_param :value: True .. py:attribute:: nu_ocog_coefs :value: (11111080.7, 2.50396017) .. py:attribute:: sub_waveform_mask :value: None .. py:attribute:: method :value: None .. py:method:: fit_func(fit_args: List[float], *_) -> numpy.ndarray Fit function for the least square algorithm. Computes and returns the residual between waveform and SAMOSA+ waveform model. Also stores the last waveform model in class instance, which then can be accessed later after the optimization is complete. :param fit_args: Waveform model parameter :return: Difference between waveform and waveform model. be minimized by least square process. .. py:method:: fit_func_samosap_standard_step1(fit_args: List[float], *_) -> numpy.ndarray Fit of the first step in the SAMOSA+ fitting process. :param fit_args: :param _: :return: (Masked) residual vector .. py:method:: fit_func_samosap_standard_step2(fit_args: List[float], *_) -> numpy.ndarray Fit of the second step in the SAMOSA+ fitting process. :param fit_args: :param _: :return: Masked residual vector .. py:method:: compute_residuals(waveform_model: samosa_waveform_model.dataclasses.WaveformModelOutput) -> numpy.ndarray Compute the residuals between the waveform model and the waveform data depending on the sub-waveform mask. Note: The peak power is scaled to 1 in both the waveform and the model Therefore amplitude scaling is not applied and only thermal noise is added. :param waveform_model: SAMOSA+ waveform model output :return: residual vector .. py:class:: SAMOSAWaveformCollectionFit(fit_method: VALID_METHOD_LITERAL, predictor_method: str, use_multiprocessing: bool = False, num_processes: Optional[int] = None, samosap_fit_kwargs: Optional[Dict] = None, predictor_kwargs: Optional[Dict] = None, least_squares_kwargs: Optional[Dict] = None) Bases: :py:obj:`object` Handles the SAMOSA waveform model fitting of a waveform collection. Can be configured to use several fitting strategies with or without multiprocessing. The notation convention for class methods is `_fit_{fit_method}{_mp: use_multiprocessing: True}`. Therefore, each fit method needs to class methods: One for single process and one for parallel processes. :param fit_method: Name of the fitting method (choices: "mss_swh", "mss_preset", "mss_seperate") :param predictor_method: :param use_multiprocessing: Flag if to use multiprocessing (default: False) :param num_processes: Number of multiprocessing workers :param predictor_kwargs: Keyword arguments for the parameter predictor class :param least_squares_kwargs: Keyword arguments for scipy.optimize.least_squares .. py:attribute:: fit_method .. py:attribute:: predictor_method .. py:attribute:: samosap_fit_kwargs .. py:attribute:: least_squares_kwargs .. py:attribute:: predictor_kwargs .. py:attribute:: use_multiprocessing :value: False .. py:attribute:: num_processes :value: None .. py:method:: fit(waveform_collection: List[WaveformFitData]) -> List[SAMOSAWaveformFitResult] Main entry method for the waveform collection fitting. :param waveform_collection: Waveform collection data :return: List of waveform fit results .. py:method:: _select_fit_method() -> Callable Selects to appropiate fit method based on class configuration. :raises AttributeError": Invalid configuration :return: callable fit method .. py:method:: _fit_samosap_single(waveform_collection: List[WaveformFitData]) -> List[SAMOSAWaveformFitResult] Computes Samosa waveform model fit in the main process (no multiprocessing) :param waveform_collection: List of fit input :return: List of fit outputs .. py:method:: _fit_samosap_single_mp(waveform_collection: List[WaveformFitData]) -> List[SAMOSAWaveformFitResult] Computes Samosa waveform model fit with multiprocessing :param waveform_collection: List of fit input :return: List of fit outputs .. py:method:: _fit_samosap_standard(waveform_collection: List[WaveformFitData]) -> List[SAMOSAWaveformFitResult] Computes Samosa waveform model fit in the main process (no multiprocessing) :param waveform_collection: List of fit input :return: List of fit outputs .. py:method:: _fit_samosap_standard_mp(waveform_collection: List[WaveformFitData]) -> List[SAMOSAWaveformFitResult] Computes Samosa waveform model fit with multiprocessing :param waveform_collection: List of fit input :return: List of fit outputs .. py:method:: _fit_samosap_specular(waveform_collection: List[WaveformFitData]) -> List[SAMOSAWaveformFitResult] Computes Samosa waveform model fit in the main process (no multiprocessing) :param waveform_collection: List of fit input :return: List of fit outputs .. py:method:: _fit_samosap_specular_mp(waveform_collection: List[WaveformFitData]) -> List[SAMOSAWaveformFitResult] Computes Samosa waveform model fit with multiprocessing :param waveform_collection: List of fit input :return: List of fit outputs .. py:method:: _mp_fit(func, waveform_collection) .. py:class:: SAMOSAModelParameterPrediction(method: VALID_METHOD_LITERAL, surface_type: str, initial_guess: Dict[str, Tuple[float, float]], bounds: Dict[str, Tuple[float, float]], amplitude_is_free_parameter: Tuple[bool, bool] = (True, True)) Bases: :py:obj:`object` Class to get initial guess and bounds of SAMOSA waveform fit parameter TODO: Class Docstr .. py:attribute:: surface_type .. py:attribute:: amplitude_is_free_parameter :value: (True, True) .. py:attribute:: method .. py:attribute:: bounds .. py:attribute:: initial_guess .. py:attribute:: first_guess_method .. py:attribute:: bounds_method .. py:method:: get(waveform: NormedWaveform, **kwargs) -> Tuple[Tuple, Tuple, Tuple] Get first guess and fit bounds for fit parameters from input waveform. The specific parameters and their order in the return tuples is defined by the property `method` of this class. :param waveform: Waveform data :param kwargs: Potential keyword arguments, e.g. fit mode :return: Tuples of first guess, lower bounds and upper bounds for the fit parametes. .. py:method:: _get_first_guess_samosap_specular(waveform: NormedWaveform, **_) -> Tuple Estimate the first guess for the SAMOSA+ specular waveform fitting mode (fixed swh, only nu). Equivalent to SAMOSA+ standard fit method with mode 2. :param waveform: Waveform data :return: Parameter first guess (epoch, nu, amplitude) .. py:method:: _get_first_guess_samosap_single(waveform: NormedWaveform, **_) -> Tuple Estimate the first guess for the SAMOSA+ specular waveform fitting mode (fixed swh, only nu). Equivalent to SAMOSA+ standard fit method with mode 2. :param waveform: Waveform data :return: Parameter first guess (epoch, nu, amplitude) .. py:method:: _get_bounds_samosap_single(tau, first_guess) -> Tuple[Tuple, Tuple] :return: Parameter fit bounds: (lower bounds, upperbounds) [mode 1: epoch, swh, amplitude; mode 2: epoch, nu] .. py:method:: _get_bounds_samosap_specular(tau, first_guess, **_) -> Tuple[Tuple, Tuple] Estimate the fit bounds for the SAMOSA+ specular waveform fitting mode (fixed swh, only nu). Equivalent to SAMOSA+ standard fit method with mode 2. :param tau: range gates in seconds :param first_guess: first guess triplet :return: Parameter first guess (epoch, nu, amplitude) .. py:method:: _get_first_guess_samosap_standard(waveform: NormedWaveform, mode: int = -1) -> Tuple Estimate the first guess for the two-step SAMOSA+ waveform fitting approch. The fit parameter depend on the specific mode/step. First fit step (mode=1): [epoch, significant_waveheight, amplitude scale] Seconds fit step (mode=2): [epoch, mean square slope] :param waveform: Waveform data :param mode: The SAMOSA+ waveform model mode (conf.STEP in SAMPy) :raises ValueError: mode not 1 or 2 :return: Parameter first guess [mode 1: epoch, swh, amplitude; mode 2: epoch, nu] .. py:method:: _get_bounds_samosap_standard(tau, first_guess, mode: int = -1) -> Tuple[Any, Any] Estimate the parameter bounds for the two-step SAMOSA+ waveform fitting approch. The bounds depend on the specific mode/step. First fit step (mode=1): [epoch, significant_waveheight, amplitude scale] Seconds fit step (mode=2): [epoch, mean square slope, amplitude scale] The bounds for the epoch are set to end behind the first maximum. For the other parameter bounds need to be provided during the initialization of this class. :param tau: range gates in seconds :param first_guess: first guess triplet :param mode: The SAMOSA+ waveform model mode (conf.STEP in SAMPy) :raises ValueError: mode not 1 or 2 :return: Parameter fit bounds: (lower bounds, upperbounds) [mode 1: epoch, swh, amplitude; mode 2: epoch, nu] .. py:method:: _compile_bounds(epoch_bnds, param_bnds, amp_bnds, mode) -> Tuple[Any, Any] .. py:class:: SAMOSAPlusRetracker Bases: :py:obj:`pysiral.retracker.BaseRetracker` Main Retracker Class (all retrackers must be of instance BaseRetracker) # TODO: API clean-up is sorely needed. .. py:attribute:: _retracker_params .. py:method:: l2_retrack(rng: numpy.ndarray, wfm: numpy.ndarray, indices: numpy.ndarray, radar_mode: numpy.ndarray, is_valid: numpy.ndarray) -> None API method for the retracker interface in the Level-2 processor. :param rng: The range per waveform samples for all waveforms in the Level-1 data object :param wfm: All waveforms in the Level-1 data object :param indices: List of waveforms for the retracker :param radar_mode: Flag indicating the radar mode for all waveforms :param is_valid: Error flag for all waveforms :return: None (Output is added to the instance) .. py:method:: create_retracker_properties(n_records: int) -> None Initialize retracker properties with correct arrays shapes (shape = (n_records, )). The list of parameters depends on whether the SAMOSA_DEBUG_MODE flag is set. NOTE: The properties are set to an array, but can be accessed as `self.{property_name}` via the __getattr__ method. :param n_records: Number of Level-2 data points .. py:method:: _samosa_plus_retracker(rng: numpy.ndarray, wfm: numpy.ndarray, indices: numpy.ndarray, radar_mode: numpy.ndarray) -> List[SAMOSAWaveformFitResult] Performs the retracking via SAMOSA waveform model fits for all target waveforms. :param rng: Range window arrays :param wfm: Waveform arrays :param indices: Target waveform indices :param radar_mode: Radar modes :return: List of SAMOSA plus waveform fit results .. py:method:: _get_waveform_fit_data(rng: numpy.ndarray, wfm: numpy.ndarray, indices: numpy.ndarray, radar_mode: numpy.ndarray) -> List[WaveformFitData] Collects all the data required for the waveform model fits. Whenever possible, :param rng: :param wfm: :param indices: :param radar_mode: :return: .. py:method:: _get_scenario_data(l1: pysiral.l1data.Level1bData, l2: pysiral.l2data.Level2Data, idx: int, look_angles) -> samosa_waveform_model.ScenarioData Creates the scenario data for the SAMOSA waveform model. This dataclass is needed for the initialization of the SAMOSA waveform model class and contains information on the radar mode and radar processing settings as well as the geometry (location, altitude & altitude rate, attitude and velocity of the altimeter platform). The specific radar parameters are expected to be included in the `samosa_waveform_model` and are retrieved by specifying platform and radar mode. Geometry parameters are available in the Level-1 and Level-2 data containers. :param l1: Level-1 data container :param l2: Level-2 data container :param idx: Target Waveform index :param look_angles: :return: SAMOSA waveform model scenario data for specific waveform .. py:method:: _get_normed_waveform(rng: numpy.ndarray, wfm: numpy.ndarray, tau: numpy.ndarray, radar_mode: int, window_delay: float, transmit_power: float, look_angles: numpy.ndarray, first_maximum_index: int, ocog_width: float, idx: int = None) -> NormedWaveform Return a normed representation of the input waveform :param rng: :param wfm: :param radar_mode: :return: .. py:method:: _get_waveform_model_fit_configuration() -> Tuple[List, Dict] Constructs the arguments and keywords for the SAMOSAWaveformCollectionFit class, defining the fit method and its configration :return: arguments and keyword arguments for the waveform model .. py:method:: _set_range_uncertainty(fit_results: List[SAMOSAWaveformFitResult], indices: numpy.ndarray) -> None The retracker range uncertainty is computed directly from the epoch standard error (from the least squares cost function) in the waveform model fitting process. :param fit_results: A list of waveform model fit results :param indices: List of waveform indices .. py:method:: _apply_filter() -> None Apply a filter to the retracker results. E.g., range values exceeding a certain misfit value or leading edge error are masked. :return: None (Variables are changed in place .. py:method:: _store_retracker_properties(fit_results: List[SAMOSAWaveformFitResult], indices: numpy.ndarray) -> None Store the output of the SAMOSA+ retracker in the class and maps the fit result to the corresponding indices in the Level-2 data. :param fit_results: :param indices: :return: None .. py:method:: _l2_register_retracker_parameters() -> None Add retracker variables to the L2 data object. The specific variables depend on surface type. .. py:method:: __getattr__(item: str) -> Any Direct attribute access to the retracker properties dictionary :param item: parameter name :raise AttributeError: item not in self._retracker_params (see self.create_retracker_properties) :return: .. py:function:: samosa_fit_samosap_single(fit_data: WaveformFitData, samosap_fit_kwargs: Dict = None, predictor_kwargs: Dict = None, least_squares_kwargs: Dict = None, filter_trailing_edge_kwargs: Dict = None) -> SAMOSAWaveformFitResult Fits the SAMOSA waveform model with all free parameters (epoch, swh, mss, amplitude) using the two-step standard fitting approach used for open ocean waveforms with SAMOSA+. This fit is intended for waveforms classified as sea ice. :param fit_data: Input parameters for waveform fitting process. Mainly contains waveform model scenario data and waveform data. :param samosap_fit_kwargs: :param predictor_kwargs: Input parameter for parameter first guess and fit bounds :param least_squares_kwargs: Keyword arguments to `scipy.optimize.least_squares` :param filter_trailing_edge_kwargs: Keyword arguments to :return: SAMOSA+ waveform model fit result .. py:function:: samosa_fit_samosap_standard(fit_data: WaveformFitData, samosap_fit_kwargs: Dict = None, predictor_kwargs: Dict = None, least_squares_kwargs: Dict = None, filter_trailing_edge_kwargs: Dict = None) -> SAMOSAWaveformFitResult Fits the SAMOSA waveform model with all free parameters (epoch, swh, mss, amplitude) using the two-step standard fitting approach used for open ocean waveforms with SAMOSA+. This fit is intended for waveforms classified as sea ice. :param fit_data: Input parameters for waveform fitting process. Mainly contains waveform model scenario data and waveform data. :param samosap_fit_kwargs: Input parameters for SAMOSA+ fitting process :param predictor_kwargs: Input parameter for parameter first guess and fit bounds :param least_squares_kwargs: Keyword arguments to `scipy.optimize.least_squares` :param filter_trailing_edge_kwargs: Keyword arguments to :return: SAMOSA+ waveform model fit result .. py:function:: samosa_fit_samosap_specular(fit_data: WaveformFitData, samosap_fit_kwargs: Dict = None, predictor_kwargs: Dict = None, least_squares_kwargs: Dict = None) -> SAMOSAWaveformFitResult Fits the SAMOSA waveform model with (epoch, mss, [amplitude]) and a fixed swh in a single step that is equivalent of the fit step 2 in the default fit procedure This fit is intended for waveforms classified as lead waveforms. :param fit_data: Input parameters for waveform fitting process. Mainly contains waveform model scenario data and waveform data. :param samosap_fit_kwargs: Input parameters for SAMOSA+ fitting process :param predictor_kwargs: Input parameter for parameter first guess and fit bounds :param least_squares_kwargs: Keyword arguments to `scipy.optimize.least_squares` :return: SAMOSA+ waveform model fit result .. py:function:: samosa_fit_samosap_single_step2(waveform_data: NormedWaveform, scenario_data: samosa_waveform_model.ScenarioData, predictor: SAMOSAModelParameterPrediction, least_squares_kwargs: Optional[Dict] = None, sub_waveform_mask: Optional[numpy.ndarray] = None, amplitude_is_free_param: bool = True) -> Tuple[WaveformModelParametersFit, samosa_waveform_model.dataclasses.WaveformModelOutput, scipy.optimize.OptimizeResult] Performs fit step 2 of the SAMOSAPlus retracker (as implemented in SAMPY) :param waveform_data: :param scenario_data: :param predictor: :param least_squares_kwargs: :param sub_waveform_mask: :param amplitude_is_free_param: :return: Fit result waveform mode .. py:function:: samosa_fit_samosap_standard_step1(waveform_data: NormedWaveform, scenario_data: samosa_waveform_model.ScenarioData, predictor: SAMOSAModelParameterPrediction, step1_fixed_nu_value: float = 0.0, amplitude_is_free_param: bool = True, least_squares_kwargs: Optional[Dict] = None, sub_waveform_mask: Optional[numpy.ndarray] = None) -> Tuple[WaveformModelParametersFit, samosa_waveform_model.dataclasses.WaveformModelOutput, scipy.optimize.OptimizeResult] Performs fit step 1 of the SAMOSAPlus retracker (as implemented in SAMPY) :param waveform_data: :param scenario_data: :param predictor: :param amplitude_is_free_param: :param step1_fixed_nu_value: :param least_squares_kwargs: :param sub_waveform_mask: :return: Waveform model parameters, .. py:function:: samosa_fit_samosap_standard_step2(waveform_data: NormedWaveform, scenario_data: samosa_waveform_model.ScenarioData, predictor: SAMOSAModelParameterPrediction, amplitude_is_free_param: bool = True, step2_fixed_swh_value: float = 0.0, least_squares_kwargs: Optional[Dict] = None, sub_waveform_mask: Optional[numpy.ndarray] = None) -> Tuple[WaveformModelParametersFit, samosa_waveform_model.dataclasses.WaveformModelOutput, scipy.optimize.OptimizeResult] Performs fit step 2 of the SAMOSAPlus retracker (as implemented in SAMPY) :param waveform_data: :param scenario_data: :param predictor: :param amplitude_is_free_param: :param step2_fixed_swh_value: :param least_squares_kwargs: :param sub_waveform_mask: :return: Fit result waveform mode .. py:function:: get_model_from_args(samosa_waveform_model, args, amplitude_scale: float = 1.0, thermal_noise: float = 0.0) -> samosa_waveform_model.dataclasses.WaveformModelOutput Compute waveform model with final fit parameters. This function allows to compute the waveform model from fit parameter triplets. :param samosa_waveform_model: Initialized and configured SAMOSA+ Waveform model instance. :param args: list of [epoch, swh, mss] :param amplitude_scale: Optional scaling factor :param thermal_noise: Optional thermal noise :return: Waveform model .. py:function:: total_velocity_from_vector(velocity_x: float, velocity_y: float, velocity_z: float) -> float Compute total velocity from vector :param velocity_x: velocity x-component :param velocity_y: velocity y-component :param velocity_z: velocity z-component :return: Total velocity .. py:function:: get_valid_epoch_range(epoch: numpy.ndarray, earliest_fraction: float = 0.0, latest_fraction: float = 1.0) -> Tuple[float, float] Get bounds for epoch based on the valid fraction range in the range window. :param epoch: epoch array :param earliest_fraction: first valid epoch as fraction of the range window :param latest_fraction: last valid epoch as fraction of the range window :raises ValueError: fraction not in interval [0-1] or earliest > latest :return: (epoch lower bound, epoch upper bound) .. py:function:: get_epoch_bounds(tau: numpy.ndarray, epoch_first_guess_ns: float, tau_earliest_fraction: float = 0.02, tau_latest_fraction: float = 0.8, range_gates_after_fmi: int = 20) -> Tuple[float, float] Compute the fit bounds for the epoch. The lower epoch bound is defined by a fraction of the range window, while the upper epoch bound is defined as a certain number of range gates after the first guess (first maximum index). :param tau: range gates in nanoseconds :param epoch_first_guess_ns: First guess for the epoch (ususally location of first maximum) :param tau_earliest_fraction: Fraction of the range window for the earliest epoch bound :param tau_latest_fraction: Fraction of the range window for the latest epoch bound (not used) :param range_gates_after_fmi: Number of trailing range gates after the epoch first guess (defines the upper epoch bound) .. py:function:: sampy_misfit(residuals: numpy.ndarray, waveform_mask: Optional[numpy.ndarray] = None, waveform_scale: float = None) -> float Computes the SAMOSA waveform model misfit parameter according to SAMPy with optional misfit computation on sub-waveform. One deviation from the SAMPy approach is the optional misfit reduction due to waveform maximum power > 1. This may happen when the first maximum power is less than the maximum power. In this case the misfit value is artificially increased compared to the SAMPy approach and scenarious where the first maximum is also the absolute maximum power. This cannot be derived from the residuals alone and therefore need to be provided as apriori information. :param residuals: difference between waveform and waveform model :param waveform_mask: numpy index array of sub-waveform mask :param waveform_scale: Maximum power the waveform. (Only needed if maximum power > 1). :return: SAMOSA waveform model misfit .. py:function:: compute_thermal_noise(waveform: numpy.ndarray, start_index: int = 4, end_index: int = 12) -> float Compute thermal noise for waveform collection (from SAMPy). The strategy is to compute the media in a slice of the lowest values of the first half of the waveform (apparently the end of the waveform contains zeros). :param waveform: The waveform array :param start_index: start of the slice of sorted waveform power values (forced to valid range) :param end_index: end of the slice of sorted waveform power values (forced to valid range) :return: thermal noise in waveform power units .. py:function:: epoch2range(epoch: float, range_array: numpy.ndarray) -> float Computes the retracker range, defined as range of spacecraft center or mass to retracked elevation. :param epoch: retracker epoch in seconds :param range_array: :return: retracker range in meter. .. py:function:: get_look_angles(l1, index: int) -> numpy.ndarray Compute the lookangles based on l1b stack information # TODO: This functions raises a ValueError for NaN values in the classifiers (LRM?) :param l1: The level 1 data object :param index: Waveform index :return: .. py:function:: get_least_squares_parameter_sdev(optimize_result: scipy.optimize.OptimizeResult) -> numpy.ndarray Compute standard deviation of parameter from Jacobian. Solution from Stack Overflow: https://stackoverflow.com/questions/42388139 :param optimize_result: Return object from `scipy.optimize.least_squares` :return: List of fit parameter standard deviations .. py:function:: get_sub_waveform_mask(waveform_data: NormedWaveform, filter_trailing_edge_kwargs: Dict) -> numpy.ndarray Estimates a sub-waveform mask by flagging off-nadir backscatter elements that manifests as peaks on the trailing edge. This is done by estimating the lower envelope of the trailing edge and exclude all waveform power values that :param waveform_data: Waveform data :param filter_trailing_edge_kwargs: Configuration keyword arguments for the trailing edge filter. :return: sub-waveform mask (True: masked) .. py:function:: get_trailing_edge_lower_envelope_mask(waveform_power: numpy.ndarray, first_maximum_index: int, max_minimum_cleaning_passes: int = 5, noise_level_normed: float = 0.02, return_type: Literal['bool', 'indices'] = 'bool') -> numpy.ndarray Compute the indices of the waveforms trailing edge lower envelope. This method relies on the computation of relative minima via scipy. with added filtering. :param waveform_power: Waveform power values (any unit) :param first_maximum_index: Range gate index of the first maximum. :param max_minimum_cleaning_passes: Local minima on the trailing edge may not be in strictly decreasing in power depending on the off-nadir backscatter contanimation of the trailing edge. Local minima are iteratively removed that are not decreasing in power. :param noise_level_normed: Points within the noise level of the linear fit between successive local minima are added to the trailing edge mask. Noise level unit is the first maximum power. :param return_type: Options are a boolean array with the same dimensions as the waveform (default), or an index array. :return: .. py:function:: get_samosa_leading_edge_error(waveform_model: numpy.ndarray, waveform: numpy.ndarray, leading_edge_start_power_threshold: float = 0.005) -> float Compute the SAMOSA+ leading edge error value from the RMSE of the leading edge of the waveform and the fitted SAMOSA+ waveform model. This error is an additional quality control measure for the SAMOSA+ range e.g., in cases where the overall fit seems to be nominal, but the leading edge (and its tracking point) are not well represented. The leading edge will be estmated from the SAMOSA+ waveform model (because this is a lot easier than from the actual waveform). :param waveform_model: The SAMOSA+ waveform model. :param waveform: The actual waveform :param leading_edge_start_power_threshold: Power threshold in units of the maximum power of the SAMOSA+ waveform model to determine the start of the leading edge. :return: SAMOSA+ leading edge error value (RMSE between waveform and waveform model for leading edge only). .. py:function:: rmse(predictions: numpy.ndarray, targets: numpy.ndarray) -> float Compute root-mean-square-error (rmse) of 2 non-Nan arrays :param predictions: predicted values (non-NaN) :param targets: target values (non-NaN) :return: rmse value .. py:function:: get_nu_from_ocog_width(ocog_width: float, nu_ocog_coefs: Tuple[float, float], ocog_width_max: float = 50, nu_min: float = 0, nu_max: float = 1000000.0, default_nu: float = 0.0) -> float Compute the inverse mean square slope (nu) from an empirical parametrization based on ocog width. For stability reasons, the nu value is clipped to a minimum and maximum value. :param ocog_width: OCOG width (in range gates) :param nu_ocog_coefs: coeficients for the OCOG width to nu relation (c[0] * ocog_width ** (-c[1])) :param ocog_width_max: The maximum OCOG value for which this relation is valid :param nu_min: Minimum value for nu :param nu_max: Maximum value for nu :param default_nu: Default value for nu if the OCOG width is above the maximum value :return: Inverse mean square slope (nu)