/*
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty
the Queen in Right of Canada (Communications Research Center Canada)
Copyright (C) 2017
Matthias P. Braendli, matthias.braendli@mpb.li
http://opendigitalradio.org
*/
/*
This file is part of ODR-DabMod.
ODR-DabMod is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
ODR-DabMod is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ODR-DabMod. If not, see .
*/
#include "PAPRStats.h"
#include
#include
#if defined(TEST)
/* compile with g++ -std=c++11 -Wall -DTEST PAPRStats.cpp -o paprtest */
# include
#endif
PAPRStats::PAPRStats(size_t num_blocks_to_accumulate) :
m_num_blocks_to_accumulate(num_blocks_to_accumulate)
{
}
void PAPRStats::process_block(const complexf* data, size_t data_len)
{
double norm_peak = 0;
double rms2 = 0;
for (size_t i = 0; i < data_len; i++) {
const double x_norm = std::norm(data[i]);
if (x_norm > norm_peak) {
norm_peak = x_norm;
}
rms2 += x_norm;
}
rms2 /= data_len;
#if defined(TEST)
std::cerr << "Accumulating peak " << norm_peak <<
" rms2 " << rms2 << std::endl;
#endif
m_squared_peaks.push_back(norm_peak);
m_squared_mean.push_back(rms2);
if (m_squared_mean.size() > m_num_blocks_to_accumulate) {
m_squared_mean.pop_front();
m_squared_peaks.pop_front();
}
}
double PAPRStats::calculate_papr() const
{
if (m_squared_mean.size() < m_num_blocks_to_accumulate) {
return 0;
}
if (m_squared_mean.size() != m_squared_peaks.size()) {
throw std::logic_error("Invalid PAPR measurement sizes");
}
double peak = 0;
double rms2 = 0;
for (size_t i = 0; i < m_squared_peaks.size(); i++) {
if (m_squared_peaks[i] > peak) {
peak = m_squared_peaks[i];
}
rms2 += m_squared_mean[i];
}
// This assumes all blocks given to process have the same length
rms2 /= m_squared_peaks.size();
#if defined(TEST)
std::cerr << "Calculate peak " << peak <<
" rms2 " << rms2 << std::endl;
#endif
return 10.0 * std::log10(peak / rms2);
}
void PAPRStats::clear()
{
m_squared_peaks.clear();
m_squared_mean.clear();
}
#if defined(TEST)
/* Test python code:
import numpy as np
vec = 0.5 * np.exp(np.complex(0, 0.3) * np.arange(40))
vec[26] = 10.0 * vec[26]
rms = np.mean(vec * np.conj(vec)).real
peak = np.amax(vec * np.conj(vec)).real
print("rms {}".format(rms))
print("peak {}".format(peak))
print(10. * np.log10(peak / rms))
*/
int main(int argc, char **argv)
{
using namespace std;
vector vec(40);
for (size_t i = 0; i < vec.size(); i++) {
vec[i] = polar(0.5, 0.3 * i);
if (i == 26) {
vec[i] *= 10;
}
cout << " " << vec[i];
}
cout << endl;
PAPRStats stats(4);
for (size_t i = 0; i < 3; i++) {
stats.process_block(vec.data(), vec.size());
}
const auto papr0 = stats.calculate_papr();
if (papr0 != 0) {
cerr << "Expected 0, got " << papr0 << endl;
}
stats.process_block(vec.data(), vec.size());
const auto papr1 = stats.calculate_papr();
cout << "PAPR = " << papr1 << " dB" << endl;
}
#endif