A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
two-ray-to-three-gpp-ch-calibration.py
Go to the documentation of this file.
1#!/usr/bin/env python
2
3"""
4Companion TwoRaySpectrumPropagationLossModel calibration script
5
6Copyright (c) 2022 SIGNET Lab, Department of Information Engineering,
7University of Padova
8
9This program is free software: you can redistribute it and/or modify it under
10the terms of the GNU General Public License as published by the Free Software
11Foundation, either version 3 of the License, or (at your option) any later
12version.
13
14This program is distributed in the hope that it will be useful, but WITHOUT
15ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License along with
19this program. If not, see <http://www.gnu.org/licenses/>.
20"""
21
22import argparse as argp
23import contextlib
24from itertools import product
25from pathlib import Path
26
27import joblib
28import numpy as np
29import pandas as pd
30import seaborn as sns
31from matplotlib import pyplot as plt
32from tqdm import tqdm
33
34# Command line arguments
35parser = argp.ArgumentParser(formatter_class=argp.ArgumentDefaultsHelpFormatter)
36parser.add_argument(
37 "--num_search_grid_params",
38 default=30,
39 help="Number of values for each parameter of the search grids",
40)
41parser.add_argument(
42 "--num_refinements", default=1, help="Number of refinement local search runs to be carried out"
43)
44parser.add_argument(
45 "--ref_data_fname",
46 default="two-ray-to-three-gpp-splm-calibration.csv",
47 help="Filename of the fit reference data, obtained from ns-3",
48)
49parser.add_argument(
50 "--fit_out_fname", default="two-ray-splm-fitted-params.txt", help="Filename of the fit results"
51)
52parser.add_argument(
53 "--c_plus_plus_out_fname",
54 default="two-ray-cplusplus-fitted-params.txt",
55 help="Filename of the fit results, encoded as a C++ data structure to be imported in ns-3",
56)
57parser.add_argument(
58 "--figs_folder",
59 default="FiguresTwoRayThreeGppChCalibration/",
60 help="Output folder for the fit results figures",
61)
62parser.add_argument("--epsilon", default=1e-7, help="Tolerance value for the preliminary tests")
63parser.add_argument(
64 "--preliminary_fit_test",
65 default=True,
66 help="Whether to run preliminary tests which check the correctness of the script functions",
67)
68parser.add_argument(
69 "--fit_ftr_to_threegpp",
70 default=True,
71 help="Whether to run the calibration with respect to the 3GPP reference channel gains",
72)
73parser.add_argument(
74 "--output_ns3_table",
75 default=True,
76 help="Whether to output the code for importing the calibration results in ns-3",
77)
78parser.add_argument(
79 "--plot_fit_results",
80 default=False,
81 help="Whether to plot a comparison of the reference data ECDFs vs the fitted FTR distributions",
82)
83
84args = parser.parse_args()
85# Number of values for each parameter of the search grids
86num_search_grid_params = int(args.num_search_grid_params)
87# Number of refinement local search runs to be carried out
88num_refinements = int(args.num_refinements)
89# Filename of the fit reference data, obtained from ns-3
90ref_data_fname = args.ref_data_fname
91# Filename of the fit results
92fit_out_fname = args.fit_out_fname
93# Filename of the fit results, encoded as a C++ data structure to be imported in ns-3
94c_plus_plus_out_fname = args.c_plus_plus_out_fname
95# Output folder for the fit results figures
96figs_folder = args.figs_folder
97# Tolerance value for the preliminary tests
98epsilon = float(args.epsilon)
99# Whether to run preliminary tests which check the correctness of the script functions
100preliminary_fit_test = bool(args.preliminary_fit_test)
101# Whether to run the calibration with respect to the 3GPP reference channel gains
102fit_ftr_to_threegpp = bool(args.fit_ftr_to_threegpp)
103# Whether to output the code for importing the calibration results in ns-3
104output_ns3_table = bool(args.output_ns3_table)
105# Whether to plot a comparison of the reference data ECDFs vs the fitted FTR distributions
106plot_fit_results = bool(args.plot_fit_results)
107
108
109@contextlib.contextmanager
110def tqdm_joblib(tqdm_object):
111 """
112 Context manager to patch joblib to report into tqdm progress bar given as argument.
113 Taken from: https://stackoverflow.com/questions/24983493/tracking-progress-of-joblib-parallel-execution
114
115 """
116
117 class TqdmBatchCompletionCallback(joblib.parallel.BatchCompletionCallBack):
118 def __call__(self, *args, **kwargs):
119 tqdm_object.update(n=self.batch_size)
120 return super().__call__(*args, **kwargs)
121
122 old_batch_callback = joblib.parallel.BatchCompletionCallBack
123 joblib.parallel.BatchCompletionCallBack = TqdmBatchCompletionCallback
124 try:
125 yield tqdm_object
126 finally:
127 joblib.parallel.BatchCompletionCallBack = old_batch_callback
128 tqdm_object.close()
129
130
131
133
141
142 def __init__(self, m: float, sigma: float, k: float, delta: float):
143 """! The initializer.
144 @param self: the object pointer
145 @param m: Parameter m for the Gamma variable. Used both as the shape and rate parameters.
146 @param sigma: Parameter sigma. Used as the variance of the amplitudes of the normal diffuse components.
147 @param k: Parameter K. Expresses ratio between dominant specular components and diffuse components.
148 @param delta: Parameter delta [0, 1]. Expresses how similar the amplitudes of the two dominant specular components are.
149 """
150
151 self.m = m
152 self.sigma = sigma
153 self.k = k
154 self.delta = delta
155
156 def __init__(self):
157 """! The initializer with default values.
158 @param self: the object pointer
159 """
160
161 self.m = 1
162 self.sigma = 1.0
163 self.k = 0.0
164 self.delta = 0.0
165
166 def __str__(self):
167 """! The initializer with default values.
168 @param self: the object pointer
169 @returns A string reporting the value of each of the FTR fading model parameters
170 """
171
172 return f"m: {self.m}, sigma: {self.sigma}, k: {self.k}, delta: {self.delta}"
173
174
175def get_ftr_ecdf(params: FtrParams, n_samples: int, db=False):
176 """! Returns the ECDF for the FTR fading model, for a given parameter grid.
177 @param params: The FTR parameters grid.
178 @param n_samples: The number of samples of the output ECDF
179 @param db: Whether to return the ECDF with the gain expressed in dB
180 @returns The ECDF for the FTR fading model
181 """
182
183 assert params.delta >= 0 and params.delta <= 1.0
184
185 # Compute the specular components amplitudes from the FTR parameters
186 cmn_sqrt_term = np.sqrt(1 - params.delta**2)
187 v1 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 - cmn_sqrt_term))
188 v2 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 + cmn_sqrt_term))
189
190 assert abs((v1**2 + v2**2) / (2 * params.sigma) - params.k) < 1e-5
191 if params.k > 0:
192 assert abs((2 * v1 * v2) / (v1**2 + v2**2) - params.delta) < 1e-4
193 else:
194 assert v1 == v2 == params.k
195
196 sqrt_gamma = np.sqrt(np.random.gamma(shape=params.m, scale=1 / params.m, size=n_samples))
197
198 # Sample the random phases of the specular components, which are uniformly distributed in [0, 2*PI]
199 phi1 = np.random.uniform(low=0, high=1.0, size=n_samples)
200 phi2 = np.random.uniform(low=0, high=1.0, size=n_samples)
201
202 # Sample the normal-distributed real and imaginary parts of the diffuse components
203 x = np.random.normal(scale=np.sqrt(params.sigma), size=n_samples)
204 y = np.random.normal(scale=np.sqrt(params.sigma), size=n_samples)
205
206 # Compute the channel response by combining the above terms
207 compl_phi1 = np.vectorize(complex)(np.cos(phi1), np.sin(phi1))
208 compl_phi2 = np.vectorize(complex)(np.cos(phi2), np.sin(phi2))
209 compl_xy = np.vectorize(complex)(x, y)
210 h = (
211 np.multiply(sqrt_gamma, compl_phi1) * v1
212 + np.multiply(sqrt_gamma, compl_phi2) * v2
213 + compl_xy
214 )
215
216 # Compute the squared norms
217 power = np.square(np.absolute(h))
218
219 if db:
220 power = 10 * np.log10(power)
221
222 return np.sort(power)
223
224
225def compute_ftr_mean(params: FtrParams):
226 """! Computes the mean of the FTR fading model, given a specific set of parameters.
227 @param params: The FTR fading model parameters.
228 """
229
230 cmn_sqrt_term = np.sqrt(1 - params.delta**2)
231 v1 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 - cmn_sqrt_term))
232 v2 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 + cmn_sqrt_term))
233
234 mean = v1**2 + v2**2 + 2 * params.sigma
235
236 return mean
237
238
239def compute_ftr_th_mean(params: FtrParams):
240 """! Computes the mean of the FTR fading model using the formula reported in the corresponding paper,
241 given a specific set of parameters.
242 @param params: The FTR fading model parameters.
243 """
244
245 return 2 * params.sigma * (1 + params.k)
246
247
248def compute_anderson_darling_measure(ref_ecdf: list, target_ecdf: list) -> float:
249 """! Computes the Anderson-Darling measure for the specified reference and targets distributions.
250 In particular, the Anderson-Darling measure is defined as:
251 \f$A^2 = -N -S\f$, where \f$S = \sum_{i=1}^N \frac{2i - 1}{N} \left[ ln F(Y_i) + ln F(Y_{N + 1 - i}) \right]\f$.
252
253 See https://www.itl.nist.gov/div898/handbook/eda/section3/eda35e.htm for further details.
254
255 @param ref_ecdf: The reference ECDF.
256 @param target_ecdf: The target ECDF we wish to match the reference distribution to.
257 @returns The Anderson-Darling measure for the specified reference and targets distributions.
258 """
259
260 assert len(ref_ecdf) == len(target_ecdf)
261
262 n = len(ref_ecdf)
263 mult_factors = np.linspace(start=1, stop=n, num=n) * 2 + 1
264 ecdf_values = compute_ecdf_value(ref_ecdf, target_ecdf)
265
266 # First and last elements of the ECDF may lead to NaNs
267 with np.errstate(divide="ignore"):
268 log_a_plus_b = np.log(ecdf_values) + np.log(1 - np.flip(ecdf_values))
269
270 valid_idxs = np.isfinite(log_a_plus_b)
271 A_sq = -np.dot(mult_factors[valid_idxs], log_a_plus_b[valid_idxs])
272
273 return A_sq
274
275
276def compute_ecdf_value(ecdf: list, data_points: float) -> np.ndarray:
277 """! Given an ECDF and data points belonging to its domain, returns their associated EDCF value.
278 @param ecdf: The ECDF, represented as a sorted list of samples.
279 @param data_points: A list of data points belonging to the same domain as the samples.
280 @returns The ECDF value of the domain points of the specified ECDF
281 """
282
283 ecdf_values = []
284 for point in data_points:
285 idx = np.searchsorted(ecdf, point) / len(ecdf)
286 ecdf_values.append(idx)
287
288 return np.asarray(ecdf_values)
289
290
291def get_sigma_from_k(k: float) -> float:
292 """! Computes the value for the FTR parameter sigma, given k, yielding a unit-mean fading process.
293 @param k: The K parameter of the FTR fading model, which represents the ratio of the average power
294 of the dominant components to the power of the remaining diffuse multipath.
295 @returns The value for the FTR parameter sigma, given k, yielding a unit-mean fading process.
296 """
297
298 return 1 / (2 + 2 * k)
299
300
302 ref_data: pd.DataFrame, ref_params_combo: tuple, num_params: int, num_refinements: int
303) -> str:
304 """! Estimate the FTR parameters yielding the closest ECDF to the reference one.
305
306 Uses a global search to estimate the FTR parameters yielding the best fit to the reference ECDF.
307 Then, the search is refined by repeating the procedure in the neighborhood of the parameters
308 identified with the global search. Such a neighborhood is determined as the interval whose center
309 is the previous iteration best value, and the lower and upper bounds are the first lower and upper
310 values which were previously considered, respectively.
311
312 @param ref_data: The reference data, represented as a DataFrame of samples.
313 @param ref_params_combo: The specific combination of simulation parameters corresponding
314 to the reference ECDF
315 @param num_params: The number of values of each parameter in the global and local search grids.
316 @param num_refinements: The number of local refinement search to be carried out after the global search.
317
318 @returns An estimate of the FTR parameters yielding the closest ECDF to the reference one.
319 """
320
321 # Retrieve the reference ECDF
322 ref_ecdf = ref_data.query(
323 "scen == @ref_params_combo[0] and cond == @ref_params_combo[1] and fc == @ref_params_combo[2]"
324 )
325
326 # Perform the fit
327 n_samples = len(ref_ecdf)
328 best_params = FtrParams()
329 best_ad = np.inf
330
331 # The m and K parameters can range in ]0, +inf[
332 m_and_k_ub = 4
333 m_and_k_lb = 0.001
334 m_and_k_step = (m_and_k_ub - m_and_k_lb) / n_samples
335
336 # The delta parameter can range in [0, 1]
337 delta_step = 1 / n_samples
338
339 # Define the coarse grid
340 coarse_search_grid = {
341 # m must be in [0, +inf]
342 "m": np.power(
343 np.ones(num_params) * 10,
344 np.linspace(start=m_and_k_lb, stop=m_and_k_ub, endpoint=True, num=num_params),
345 ),
346 # k must be in [0, +inf]
347 "k": np.power(
348 np.ones(num_params) * 10,
349 np.linspace(start=m_and_k_lb, stop=m_and_k_ub, endpoint=True, num=num_params),
350 ),
351 # delta must be in [0, 1]
352 "delta": np.linspace(start=0.0, stop=1.0, endpoint=True, num=num_params),
353 # sigma determined from k, due to the unit-mean constraint
354 }
355
356 for element in product(*coarse_search_grid.values()):
357 # Create FTR params object
358 params = FtrParams()
359 params.m = element[0]
360 params.k = element[1]
361 params.delta = element[2]
362 params.sigma = get_sigma_from_k(params.k)
363
364 # Retrieve the corresponding FTR ECDF
365 ftr_ecdf = get_ftr_ecdf(params, n_samples, db=True)
366 ad_meas = compute_anderson_darling_measure(ref_ecdf, ftr_ecdf)
367
368 if ad_meas < best_ad:
369 best_params = params
370 best_ad = ad_meas
371
372 for _ in range(num_refinements):
373 # Refine search in the neighborhood of the previously identified params
374 finer_search_grid = {
375 "m": np.power(
376 np.ones(num_params) * 10,
377 np.linspace(
378 start=max(0, np.log10(best_params.m) - m_and_k_step),
379 stop=np.log10(best_params.m) + m_and_k_step,
380 endpoint=True,
381 num=num_params,
382 ),
383 ),
384 "k": np.power(
385 np.ones(num_params) * 10,
386 np.linspace(
387 start=max(0, np.log10(best_params.k) - m_and_k_step),
388 stop=np.log10(best_params.k) + m_and_k_step,
389 endpoint=True,
390 num=num_params,
391 ),
392 ),
393 "delta": np.linspace(
394 start=max(0, best_params.delta - delta_step),
395 stop=min(1, best_params.delta + delta_step),
396 endpoint=True,
397 num=num_params,
398 ),
399 # sigma determined from k, due to the unit-mean constraint
400 }
401
402 m_and_k_step = (
403 np.log10(best_params.m) + m_and_k_step - max(0, np.log10(best_params.m) - m_and_k_step)
404 ) / n_samples
405 delta_step = (
406 min(1, best_params.delta + 1 / num_params) - max(0, best_params.delta - 1 / num_params)
407 ) / n_samples
408
409 for element in product(*finer_search_grid.values()):
410 # Create FTR params object
411 params = FtrParams()
412 params.m = element[0]
413 params.k = element[1]
414 params.delta = element[2]
415 params.sigma = get_sigma_from_k(params.k)
416
417 # Retrieve the corresponding FTR ECDF
418 ftr_ecdf = get_ftr_ecdf(params, n_samples, db=True)
419 ad_meas = compute_anderson_darling_measure(ref_ecdf, ftr_ecdf)
420
421 if ad_meas < best_ad:
422 best_params = params
423 best_ad = ad_meas
424
425 out_str = (
426 f"{ref_params_combo[0]}\t{ref_params_combo[1]}\t{ref_params_combo[2]}"
427 + f" \t{best_params.sigma}\t{best_params.k}\t{best_params.delta}\t{best_params.m}\n"
428 )
429
430 return out_str
431
432
433def append_ftr_params_to_cpp_string(text: str, params: FtrParams) -> str:
434 text += f"TwoRaySpectrumPropagationLossModel::FtrParams({np.format_float_scientific(params.m)}, {np.format_float_scientific(params.sigma)}, \
435 {np.format_float_scientific(params.k)}, {np.format_float_scientific(params.delta)})"
436
437 return text
438
439
440def print_cplusplus_map_from_fit_results(fit: pd.DataFrame, out_fname: str):
441 """
442 Prints to a file the results of the FTR fit, as C++ code.
443
444 Args:
445 fit (pd.DataFrame): A Pandas Dataframe holding the results of the FTR fit.
446 out_fname (str): The name of the file to print the C++ code to.
447 """
448
449 out_str = "{"
450
451 for scen in set(fit["scen"]):
452 out_str += f'{{"{scen}",\n{{'
453
454 for cond in set(fit["cond"]):
455 out_str += f"{{ChannelCondition::LosConditionValue::{cond}, \n"
456
457 # Print vector of carrier frequencies
458 freqs = np.sort(list(set(fit["fc"])))
459 out_str += "{{"
460 for fc in freqs:
461 out_str += f"{float(fc)}, "
462 out_str = out_str[0:-2]
463 out_str += "},\n{"
464
465 # Load corresponding fit results
466 for fc in freqs:
467 fit_line = fit.query("scen == @scen and cond == @cond and fc == @fc")
468 assert fit_line.reset_index().shape[0] == 1
469
470 params = FtrParams()
471 params.m = fit_line.iloc[0]["m"]
472 params.k = fit_line.iloc[0]["k"]
473 params.delta = fit_line.iloc[0]["delta"]
474 params.sigma = fit_line.iloc[0]["sigma"]
475
476 # Print vector of corresponding FTR parameters
477 out_str = append_ftr_params_to_cpp_string(out_str, params)
478 out_str += ", "
479
480 out_str = out_str[0:-2]
481 out_str += "}"
482 out_str += "}},\n"
483
484 out_str = out_str[0:-2]
485 out_str += "}},\n"
486
487 out_str = out_str[0:-2]
488 out_str += "}\n"
489
490 with open(out_fname, "w", encoding="utf-8") as f:
491 f.write(out_str)
492
493
494if __name__ == "__main__":
495
498
499 # Load reference data obtained from the ns-3 TR 38.901 implementation
500 df = pd.read_csv(ref_data_fname, sep="\t")
501 # Linear gain --> gain in dB
502 df["gain"] = 10 * np.log10(df["gain"])
503
504 # Retrieve the possible parameters configurations
505 scenarios = set(df["scen"])
506 is_los = set(df["cond"])
507 frequencies = np.sort(list(set(df["fc"])))
508
509
512
513 if preliminary_fit_test:
514 params = FtrParams()
515 get_ftr_ecdf(params, 100)
516
517 # Make sure the mean is indeed independent from the delta parameter
518 mean_list = []
519 params = FtrParams()
520 for delta in np.linspace(start=0, stop=1, num=100):
521 params.delta = delta
522 mean_list.append(compute_ftr_mean(params))
523
524 avg_mean = np.mean(mean_list)
525 assert np.all(np.abs(mean_list - avg_mean) < epsilon)
526
527 # Make sure that we are indeed generating parameters yielding unit-mean
528 mean_list.clear()
529 mean_th_list = []
530 params = FtrParams()
531 for k in np.linspace(start=1, stop=500, num=50):
533 params.sigma = sigma
534 params.k = k
535 mean_list.append(compute_ftr_mean(params))
536 mean_th_list.append(compute_ftr_th_mean(params))
537
538 assert np.all(np.abs(mean_list - np.float64(1.0)) < epsilon)
539 assert np.all(np.abs(mean_th_list - np.float64(1.0)) < epsilon)
540
541 if fit_ftr_to_threegpp:
542 # Parallel search for the different simulation parameters combination
543 with tqdm_joblib(
544 tqdm(
545 desc="Fitting FTR to the 3GPP fading model",
546 total=(len(scenarios) * len(is_los) * len(frequencies)),
547 )
548 ) as progress_bar:
549 res = joblib.Parallel(n_jobs=10)(
550 joblib.delayed(fit_ftr_to_reference)(
551 df, params_comb, num_search_grid_params, num_refinements
552 )
553 for params_comb in product(scenarios, is_los, frequencies)
554 )
555
556 with open(fit_out_fname, "w", encoding="utf-8") as f:
557 f.write("scen\tcond\tfc\tsigma\tk\tdelta\tm\n")
558 for line in res:
559 f.write(line)
560
561 if output_ns3_table:
562 # Load the fit results
563 fit = pd.read_csv(fit_out_fname, delimiter="\t")
564
565 # Output the C++ data structure
566 print_cplusplus_map_from_fit_results(fit, c_plus_plus_out_fname)
567
568 if plot_fit_results:
569 # Set Seaborn defaults and setup output folder
570 sns.set(rc={"figure.figsize": (7, 5)})
571 sns.set_theme()
572 sns.set_style("darkgrid")
573
574 fit = pd.read_csv(fit_out_fname, delimiter="\t")
575 # Create folder if it does not exist
576 Path(figs_folder).mkdir(parents=True, exist_ok=True)
577 ad_measures = []
578
579 for params_comb in product(scenarios, is_los, frequencies):
580 data_query = (
581 "scen == @params_comb[0] and cond == @params_comb[1] and fc == @params_comb[2]"
582 )
583
584 # Load corresponding reference data
585 ref_data = df.query(data_query)
586
587 # Create FTR params object
588 fit_line = fit.query(data_query)
589 assert fit_line.reset_index().shape[0] == 1
590 params = FtrParams()
591 params.m = fit_line.iloc[0]["m"]
592 params.k = fit_line.iloc[0]["k"]
593 params.delta = fit_line.iloc[0]["delta"]
594 params.sigma = fit_line.iloc[0]["sigma"]
595
596 # Retrieve the corresponding FTR ECDF
597 ftr_ecdf = get_ftr_ecdf(params, len(ref_data), db=True)
598
599 # Compute the AD measure
600 ad_meas = compute_anderson_darling_measure(np.sort(ref_data["gain"]), ftr_ecdf)
601 ad_measures.append(np.sqrt(ad_meas))
602
603 sns.ecdfplot(data=ref_data, x="gain", label="38.901 reference model")
604 sns.ecdfplot(ftr_ecdf, label=f"Fitted FTR, sqrt(AD)={round(np.sqrt(ad_meas), 2)}")
605 plt.xlabel("End-to-end channel gain due to small scale fading [dB]")
606 plt.legend()
607 plt.savefig(
608 f"{figs_folder}{params_comb[0]}_{params_comb[1]}_{params_comb[2]/1e9}GHz_fit.png",
609 dpi=500,
610 bbox_inches="tight",
611 )
612 plt.clf()
613
614 # Plot ECDF of the scaled and normalized AD measures
615 sns.ecdfplot(ad_measures, label="AD measures")
616 plt.xlabel("Anderson-Darling goodness-of-fit")
617 plt.legend()
618 plt.savefig(f"{figs_folder}AD_measures.png", dpi=500, bbox_inches="tight")
619 plt.clf()
def __str__(self)
The initializer with default values.
def __init__(self, float m, float sigma, float k, float delta)
The initializer.
str append_ftr_params_to_cpp_string(str text, FtrParams params)
float compute_anderson_darling_measure(list ref_ecdf, list target_ecdf)
Computes the Anderson-Darling measure for the specified reference and targets distributions.
def compute_ftr_th_mean(FtrParams params)
Computes the mean of the FTR fading model using the formula reported in the corresponding paper,...
str fit_ftr_to_reference(pd.DataFrame ref_data, tuple ref_params_combo, int num_params, int num_refinements)
Estimate the FTR parameters yielding the closest ECDF to the reference one.
def get_ftr_ecdf(FtrParams params, int n_samples, db=False)
Returns the ECDF for the FTR fading model, for a given parameter grid.
def print_cplusplus_map_from_fit_results(pd.DataFrame fit, str out_fname)
float get_sigma_from_k(float k)
Computes the value for the FTR parameter sigma, given k, yielding a unit-mean fading process.
def compute_ftr_mean(FtrParams params)
Computes the mean of the FTR fading model, given a specific set of parameters.
np.ndarray compute_ecdf_value(list ecdf, float data_points)
Given an ECDF and data points belonging to its domain, returns their associated EDCF value.
#define list