summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2017-09-27 20:29:14 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2017-09-28 13:36:48 +0200
commit4574756f50fd34355aa9d6bfc8a5662f87e88661 (patch)
tree66f72df54f173e9d5ef23380ce61c75b9ba3318b
parent16c4bcb085457514438d35bbbe11ef979e36bb85 (diff)
downloaddabmod-4574756f50fd34355aa9d6bfc8a5662f87e88661.tar.gz
dabmod-4574756f50fd34355aa9d6bfc8a5662f87e88661.tar.bz2
dabmod-4574756f50fd34355aa9d6bfc8a5662f87e88661.zip
Add CFR statistics to RC
-rw-r--r--doc/example.ini7
-rw-r--r--src/OfdmGenerator.cpp71
-rw-r--r--src/OfdmGenerator.h14
3 files changed, 71 insertions, 21 deletions
diff --git a/doc/example.ini b/doc/example.ini
index 512e196..21f15ea 100644
--- a/doc/example.ini
+++ b/doc/example.ini
@@ -127,16 +127,17 @@ dac_clk_rate=0
; and
;dac_clk_rate=128000000
-; Settings for crest factor reduction
+; Settings for crest factor reduction. Statistics for ratio of
+; samples that were clipped are available through the RC.
[cfr]
enable=0
; At what amplitude the signal should be clipped
-clip=1.0
+clip=70.0
; How much to clip the error signal used to compensate the effect
; of clipping
-error_clip=1.0
+error_clip=0.05
[firfilter]
; The FIR Filter can be used to create a better spectral quality.
diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp
index 8f5d8a0..4d76dbc 100644
--- a/src/OfdmGenerator.cpp
+++ b/src/OfdmGenerator.cpp
@@ -36,7 +36,9 @@
#include <stdexcept>
#include <assert.h>
#include <string>
+#include <numeric>
+static const size_t MAX_CLIP_STATS = 10;
OfdmGenerator::OfdmGenerator(size_t nbSymbols,
size_t nbCarriers,
@@ -68,6 +70,7 @@ OfdmGenerator::OfdmGenerator(size_t nbSymbols,
RC_ADD_PARAMETER(cfr, "Enable crest factor reduction");
RC_ADD_PARAMETER(clip, "CFR: Clip to amplitude");
RC_ADD_PARAMETER(errorclip, "CFR: Limit error");
+ RC_ADD_PARAMETER(clip_stats, "CFR: statistics (clip ratio, errorclip ratio)");
if (inverse) {
myPosDst = (nbCarriers & 1 ? 0 : 1);
@@ -170,13 +173,13 @@ int OfdmGenerator::process(Buffer* const dataIn, Buffer* dataOut)
"OfdmGenerator::process output size not valid!");
}
- myNumClip = 0;
- myNumErrorClip = 0;
-
// It is not guaranteed that fftw keeps the FFT input vector intact.
// That's why we copy it to the reference.
std::vector<complexf> reference;
+ size_t num_clip = 0;
+ size_t num_error_clip = 0;
+
for (size_t i = 0; i < myNbSymbols; ++i) {
myFftIn[0][0] = 0;
myFftIn[0][1] = 0;
@@ -196,7 +199,9 @@ int OfdmGenerator::process(Buffer* const dataIn, Buffer* dataOut)
if (myCfr) {
complexf *symbol = reinterpret_cast<complexf*>(myFftOut);
- cfr_one_iteration(symbol, reference.data());
+ const auto stat = cfr_one_iteration(symbol, reference.data());
+ num_clip += stat.clip_count;
+ num_error_clip += stat.errclip_count;
}
memcpy(out, myFftOut, mySpacing * sizeof(FFT_TYPE));
@@ -206,19 +211,35 @@ int OfdmGenerator::process(Buffer* const dataIn, Buffer* dataOut)
}
if (myCfr) {
- etiLog.level(debug) << "CFR: " << myNumClip << " clipped, " <<
- myNumErrorClip << " err clipped";
+ std::lock_guard<std::mutex> lock(myCfrRcMutex);
+
+ const double num_samps = myNbSymbols * mySpacing;
+ const double clip_ratio = (double)num_clip / num_samps;
+
+ myClipRatios.push_back(clip_ratio);
+ while (myClipRatios.size() > MAX_CLIP_STATS) {
+ myClipRatios.pop_front();
+ }
+
+ const double errclip_ratio = (double)num_error_clip / num_samps;
+ myErrorClipRatios.push_back(errclip_ratio);
+ while (myErrorClipRatios.size() > MAX_CLIP_STATS) {
+ myErrorClipRatios.pop_front();
+ }
}
return sizeOut;
}
-void OfdmGenerator::cfr_one_iteration(complexf *symbol, const complexf *reference)
+OfdmGenerator::cfr_iter_stat_t OfdmGenerator::cfr_one_iteration(
+ complexf *symbol, const complexf *reference)
{
// use std::norm instead of std::abs to avoid calculating the
// square roots
const float clip_squared = myCfrClip * myCfrClip;
+ OfdmGenerator::cfr_iter_stat_t ret;
+
// Clip
for (size_t i = 0; i < mySpacing; i++) {
const float mag_squared = std::norm(symbol[i]);
@@ -228,7 +249,7 @@ void OfdmGenerator::cfr_one_iteration(complexf *symbol, const complexf *referenc
// = x * sqrt(clip_squared) / sqrt(mag_squared)
// = x * sqrt(clip_squared / mag_squared)
symbol[i] *= std::sqrt(clip_squared / mag_squared);
- myNumClip++;
+ ret.clip_count++;
}
}
@@ -245,8 +266,8 @@ void OfdmGenerator::cfr_one_iteration(complexf *symbol, const complexf *referenc
std::vector<float> error_norm(mySpacing);
for (size_t i = 0; i < mySpacing; i++) {
- // FFTW computes an unnormalised trasform, i.e. a FFT-IFFT pair
- // or vice-versa give back the original vector scaled by a factor
+ // FFTW computes an unnormalised transform, i.e. a FFT-IFFT pair
+ // or vice-versa gives back the original vector scaled by a factor
// FFT-size. Because we're comparing our constellation point
// (calculated with IFFT-clip-FFT) against reference (input to
// the IFFT), we need to divide by our FFT size.
@@ -260,7 +281,7 @@ void OfdmGenerator::cfr_one_iteration(complexf *symbol, const complexf *referenc
if (mag_squared > err_clip_squared) {
error *= std::sqrt(err_clip_squared / mag_squared);
- myNumErrorClip++;
+ ret.errclip_count++;
}
// Update the input to the FFT directly to avoid another copy for the
@@ -269,12 +290,10 @@ void OfdmGenerator::cfr_one_iteration(complexf *symbol, const complexf *referenc
fft_in[i] = constellation_point + error;
}
- auto minmax = std::minmax_element(error_norm.begin(), error_norm.end());
- etiLog.level(debug) << "err min: " << std::sqrt(*minmax.first)
- << " max: " << std::sqrt(*minmax.second);
-
// Run our error-compensated symbol through the IFFT again
fftwf_execute(myFftPlan); // IFFT from myFftIn to myFftOut
+
+ return ret;
}
@@ -294,6 +313,9 @@ void OfdmGenerator::set_parameter(const std::string& parameter,
else if (parameter == "errorclip") {
ss >> myCfrErrorClip;
}
+ else if (parameter == "clip_stats") {
+ throw ParameterError("Parameter 'clip_stats' is read-only");
+ }
else {
stringstream ss;
ss << "Parameter '" << parameter
@@ -315,6 +337,25 @@ const std::string OfdmGenerator::get_parameter(const std::string& parameter) con
else if (parameter == "errorclip") {
ss << std::fixed << myCfrErrorClip;
}
+ else if (parameter == "clip_stats") {
+ std::lock_guard<std::mutex> lock(myCfrRcMutex);
+ if (myClipRatios.empty() or myErrorClipRatios.empty()) {
+ ss << "No stats available";
+ }
+ else {
+ const double avg_clip_ratio =
+ std::accumulate(myClipRatios.begin(), myClipRatios.end(), 0.0) /
+ myClipRatios.size();
+
+ const double avg_errclip_ratio =
+ std::accumulate(myErrorClipRatios.begin(), myErrorClipRatios.end(), 0.0) /
+ myErrorClipRatios.size();
+
+ ss << "Statistics : " << std::fixed <<
+ avg_clip_ratio * 100 << "%"" samples clipped, " <<
+ avg_errclip_ratio * 100 << "%"" errors clipped";
+ }
+ }
else {
ss << "Parameter '" << parameter <<
"' is not exported by controllable " << get_rc_name();
diff --git a/src/OfdmGenerator.h b/src/OfdmGenerator.h
index ad39f5b..9ac0387 100644
--- a/src/OfdmGenerator.h
+++ b/src/OfdmGenerator.h
@@ -68,7 +68,13 @@ class OfdmGenerator : public ModCodec, public RemoteControllable
const std::string& parameter) const override;
protected:
- void cfr_one_iteration(complexf *symbol, const complexf *reference);
+ struct cfr_iter_stat_t {
+ size_t clip_count = 0;
+ size_t errclip_count = 0;
+ };
+
+ cfr_iter_stat_t cfr_one_iteration(
+ complexf *symbol, const complexf *reference);
fftwf_plan myFftPlan;
fftwf_complex *myFftIn, *myFftOut;
@@ -85,14 +91,16 @@ class OfdmGenerator : public ModCodec, public RemoteControllable
unsigned myZeroSize;
bool myCfr; // Whether to enable crest factor reduction
+ mutable std::mutex myCfrRcMutex;
float myCfrClip;
float myCfrErrorClip;
fftwf_plan myCfrFft;
fftwf_complex *myCfrPostClip;
fftwf_complex *myCfrPostFft;
- size_t myNumClip;
- size_t myNumErrorClip;
+ // Statistics for CFR
+ std::deque<double> myClipRatios;
+ std::deque<double> myErrorClipRatios;
};