blob: 1f89a77b2b715bb3258433dbd380348236cba073 [file] [log] [blame]
/*
* Generate packet error rates for OFDM rates given signal level and
* packet length.
*/
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "wmediumd.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
/* Code rates for convolutional codes */
enum fec_rate {
FEC_RATE_1_2,
FEC_RATE_2_3,
FEC_RATE_3_4,
};
struct rate {
int mbps;
int mqam;
enum fec_rate fec;
};
/*
* rate sets are defined in drivers/net/wireless/mac80211_hwsim.c#hwsim_rates.
*/
static struct rate rateset[] = {
/*
* XXX:
* For rate = 1, 2, 5.5, 11 Mbps, we will use mqam and fec of closest
* rate. Because these rates are not OFDM rate.
*/
{ .mbps = 10, .mqam = 2, .fec = FEC_RATE_1_2 },
{ .mbps = 20, .mqam = 2, .fec = FEC_RATE_1_2 },
{ .mbps = 55, .mqam = 2, .fec = FEC_RATE_1_2 },
{ .mbps = 110, .mqam = 4, .fec = FEC_RATE_1_2 },
{ .mbps = 60, .mqam = 2, .fec = FEC_RATE_1_2 },
{ .mbps = 90, .mqam = 2, .fec = FEC_RATE_3_4 },
{ .mbps = 120, .mqam = 4, .fec = FEC_RATE_1_2 },
{ .mbps = 180, .mqam = 4, .fec = FEC_RATE_3_4 },
{ .mbps = 240, .mqam = 16, .fec = FEC_RATE_1_2 },
{ .mbps = 360, .mqam = 16, .fec = FEC_RATE_3_4 },
{ .mbps = 480, .mqam = 64, .fec = FEC_RATE_2_3 },
{ .mbps = 540, .mqam = 64, .fec = FEC_RATE_3_4 },
};
static size_t rate_len = ARRAY_SIZE(rateset);
static double n_choose_k(double n, double k)
{
int i;
double c = 1;
if (n < k || !k)
return 0;
if (k > n - k)
k = n - k;
for (i = 1; i <= k; i++)
c *= (n - (k - i)) / i;
return c;
}
static double dot(double *v1, double *v2, int len)
{
int i;
double val = 0;
for (i = 0; i < len; i++)
val += v1[i] * v2[i];
return val;
}
/*
* Compute bit error rate for BPSK at a given SNR.
* See http://en.wikipedia.org/wiki/Phase-shift_keying
*/
static double bpsk_ber(double snr_db)
{
double snr = pow(10, (snr_db / 10.));
return .5 * erfc(sqrt(snr));
}
/*
* Compute bit error rate for M-QAM at a given SNR.
* See http://www.dsplog.com/2012/01/01/symbol-error-rate-16qam-64qam-256qam/
*/
static double mqam_ber(int m, double snr_db)
{
double k = sqrt(1. / ((2./3) * (m - 1)));
double snr = pow(10, (snr_db / 10.));
double e = erfc(k * sqrt(snr));
double sqrtm = sqrt(m);
double b = 2 * (1 - 1./sqrtm) * e;
double c = (1 - 2./sqrtm + 1./m) * pow(e, 2);
double ser = b - c;
return ser / log2(m);
}
/*
* Compute packet (frame) error rate given a length
*/
static double per(double ber, enum fec_rate rate, int frame_len)
{
/* free distances for each fec_rate */
int d_free[] = { 10, 6, 5 };
/* initial rate code coefficients */
double a_d[3][10] = {
/* FEC_RATE_1_2 */
{ 11, 0, 38, 0, 193, 0, 1331, 0, 7275, 0 },
/* FEC_RATE_2_3 */
{ 1, 16, 48, 158, 642, 2435, 9174, 34701, 131533, 499312 },
/* FEC_RATE_3_4 */
{ 8, 31, 160, 892, 4512, 23297, 120976, 624304, 3229885, 16721329 }
};
double p_d[ARRAY_SIZE(a_d[0])] = {};
double rho = ber;
double prob_uncorrected;
int k;
size_t i;
for (i = 0; i < ARRAY_SIZE(p_d); i++) {
double sum_prob = 0;
int d = d_free[rate] + i;
if (d & 1) {
for (k = (d + 1)/2; k <= d; k++)
sum_prob += n_choose_k(d, k) * pow(rho, k) *
pow(1 - rho, d - k);
} else {
for (k = d/2 + 1; k <= d; k++)
sum_prob += n_choose_k(d, k) * pow(rho, k) *
pow(1 - rho, d - k);
sum_prob += .5 * n_choose_k(d, d/2) * pow(rho, d/2) *
pow(1 - rho, d/2);
}
p_d[i] = sum_prob;
}
prob_uncorrected = dot(p_d, a_d[rate], ARRAY_SIZE(a_d[rate]));
if (prob_uncorrected > 1)
prob_uncorrected = 1;
return 1.0 - pow(1 - prob_uncorrected, 8 * frame_len);
}
double get_error_prob_from_snr(double snr, unsigned int rate_idx, u32 freq,
int frame_len)
{
int m;
enum fec_rate fec;
double ber;
if (snr <= 0.0)
return 1.0;
if (freq > 5000)
rate_idx += 4;
if (rate_idx >= rate_len)
return 1.0;
m = rateset[rate_idx].mqam;
fec = rateset[rate_idx].fec;
if (m == 2)
ber = bpsk_ber(snr);
else
ber = mqam_ber(m, snr);
return per(ber, fec, frame_len);
}
static double get_error_prob_from_per_matrix(struct wmediumd *ctx, double snr,
unsigned int rate_idx, u32 freq,
int frame_len, struct station *src,
struct station *dst)
{
int signal_idx;
signal_idx = snr + NOISE_LEVEL - ctx->per_matrix_signal_min;
if (signal_idx < 0)
return 1.0;
if (signal_idx >= ctx->per_matrix_row_num)
return 0.0;
if (freq > 5000)
rate_idx += 4;
if (rate_idx >= rate_len)
return 1.0;
return ctx->per_matrix[signal_idx * rate_len + rate_idx];
}
int read_per_file(struct wmediumd *ctx, const char *file_name)
{
FILE *fp;
char line[256];
int signal;
size_t i;
float *temp;
fp = fopen(file_name, "r");
if (fp == NULL) {
w_flogf(ctx, LOG_ERR, stderr,
"fopen failed %s\n", strerror(errno));
return EXIT_FAILURE;
}
ctx->per_matrix_signal_min = 1000;
while (fscanf(fp, "%s", line) != EOF){
if (line[0] == '#') {
if (fgets(line, sizeof(line), fp) == NULL) {
w_flogf(ctx, LOG_ERR, stderr,
"Failed to read comment line\n");
return EXIT_FAILURE;
}
continue;
}
signal = atoi(line);
if (ctx->per_matrix_signal_min > signal)
ctx->per_matrix_signal_min = signal;
if (signal - ctx->per_matrix_signal_min < 0) {
w_flogf(ctx, LOG_ERR, stderr,
"%s: invalid signal=%d\n", __func__, signal);
return EXIT_FAILURE;
}
temp = realloc(ctx->per_matrix, sizeof(float) * rate_len *
++ctx->per_matrix_row_num);
if (temp == NULL) {
w_flogf(ctx, LOG_ERR, stderr,
"Out of memory(PER file)\n");
return EXIT_FAILURE;
}
ctx->per_matrix = temp;
for (i = 0; i < rate_len; i++) {
if (fscanf(fp, "%f", &ctx->per_matrix[
(signal - ctx->per_matrix_signal_min) *
rate_len + i]) == EOF) {
w_flogf(ctx, LOG_ERR, stderr,
"Not enough rate found\n");
return EXIT_FAILURE;
}
}
}
ctx->get_error_prob = get_error_prob_from_per_matrix;
return EXIT_SUCCESS;
}
int index_to_rate(size_t index, u32 freq)
{
if (freq > 5000)
index += 4;
if (index >= rate_len)
index = rate_len - 1;
return rateset[index].mbps;
}