kopia lustrzana https://github.com/f4exb/sdrangel
				
				
				
			
		
			
				
	
	
		
			137 wiersze
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C++
		
	
	
			
		
		
	
	
			137 wiersze
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C++
		
	
	
// Copyright 2021 Rob Riggs <rob@mobilinkd.com>
 | 
						|
// All rights reserved.
 | 
						|
 | 
						|
#pragma once
 | 
						|
 | 
						|
#include "IirFilter.h"
 | 
						|
 | 
						|
#include <algorithm>
 | 
						|
#include <array>
 | 
						|
#include <cmath>
 | 
						|
#include <cstddef>
 | 
						|
 | 
						|
#include "export.h"
 | 
						|
 | 
						|
namespace modemm17 {
 | 
						|
 | 
						|
/**
 | 
						|
 * Deviation and zero-offset estimator.
 | 
						|
 *
 | 
						|
 * Accepts samples which are periodically used to update estimates of the
 | 
						|
 * input signal deviation and zero offset.
 | 
						|
 *
 | 
						|
 * Samples must be provided at the ideal sample point (the point with the
 | 
						|
 * peak bit energy).
 | 
						|
 *
 | 
						|
 * Estimates are expected to be updated at each sync word.  But they can
 | 
						|
 * be updated more frequently, such as during the preamble.
 | 
						|
 */
 | 
						|
class MODEMM17_API FreqDevEstimator
 | 
						|
{
 | 
						|
public:
 | 
						|
    using sample_filter_t = BaseIirFilter<3>;
 | 
						|
 | 
						|
	void reset()
 | 
						|
	{
 | 
						|
		min_est_ = 0.0f;
 | 
						|
		max_est_ = 0.0f;
 | 
						|
		min_var_ = 0.0f;
 | 
						|
		max_var_ = 0.0f;
 | 
						|
		min_count_ = 0;
 | 
						|
		max_count_ = 0;
 | 
						|
		min_cutoff_ = 0.0f;
 | 
						|
		max_cutoff_ = 0.0f;
 | 
						|
	}
 | 
						|
 | 
						|
	void sample(float sample)
 | 
						|
	{
 | 
						|
		if (sample < 1.5 * min_est_)
 | 
						|
		{
 | 
						|
			min_count_ = 1;
 | 
						|
			min_est_ = sample;
 | 
						|
			min_var_ = 0.0f;
 | 
						|
			min_cutoff_ = min_est_ * 0.666666f;
 | 
						|
		}
 | 
						|
		else if (sample < min_cutoff_)
 | 
						|
		{
 | 
						|
			min_count_ += 1;
 | 
						|
			min_est_ += sample;
 | 
						|
			float var = (min_est_ / min_count_) - sample;
 | 
						|
			min_var_ += var * var;
 | 
						|
		}
 | 
						|
		else if (sample > 1.5f * max_est_)
 | 
						|
		{
 | 
						|
			max_count_ = 1;
 | 
						|
			max_est_ = sample;
 | 
						|
			max_var_ = 0.0f;
 | 
						|
			max_cutoff_ = max_est_ * 0.666666f;
 | 
						|
		}
 | 
						|
		else if (sample > max_cutoff_)
 | 
						|
		{
 | 
						|
			max_count_ += 1;
 | 
						|
			max_est_ += sample;
 | 
						|
			float var = (max_est_ / max_count_) - sample;
 | 
						|
			max_var_ += var * var;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Update the estimates for deviation, offset, and EVM (error).  Note
 | 
						|
	 * that the estimates for error are using a sloppy implementation for
 | 
						|
	 * calculating variance to reduce the memory requirements.  This is
 | 
						|
	 * because this is designed for embedded use.
 | 
						|
	 */
 | 
						|
	void update()
 | 
						|
	{
 | 
						|
		if (max_count_ < 2 || min_count_ < 2) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
		float max_ = max_est_ / max_count_;
 | 
						|
		float min_ = min_est_ / min_count_;
 | 
						|
		deviation_ = (max_ - min_) / 6.0;
 | 
						|
		offset_ =  dc_filter_(std::max(std::min(max_ + min_, deviation_ * MAX_DC_ERROR), deviation_ * -MAX_DC_ERROR));
 | 
						|
		error_ = (std::sqrt(max_var_ / (max_count_ - 1)) + std::sqrt(min_var_ / (min_count_ - 1))) * 0.5;
 | 
						|
 | 
						|
        if (deviation_ > 0) {
 | 
						|
            idev_ = 1.0 / deviation_;
 | 
						|
        }
 | 
						|
 | 
						|
        min_cutoff_ = offset_ - deviation_ * 2;
 | 
						|
		max_cutoff_ = offset_ + deviation_ * 2;
 | 
						|
		max_est_ = max_;
 | 
						|
		min_est_ = min_;
 | 
						|
		max_count_ = 1;
 | 
						|
		min_count_ = 1;
 | 
						|
		max_var_ = 0.0f;
 | 
						|
		min_var_ = 0.0f;
 | 
						|
	}
 | 
						|
 | 
						|
	float deviation() const { return deviation_; }
 | 
						|
	float offset() const { return offset_; }
 | 
						|
	float error() const { return error_; }
 | 
						|
	float idev() const { return idev_; }
 | 
						|
 | 
						|
private:
 | 
						|
    // IIR with Nyquist of 1/4.
 | 
						|
    static const std::array<float, 3> dc_b;
 | 
						|
    static const std::array<float, 3> dc_a;
 | 
						|
    static const float MAX_DC_ERROR;
 | 
						|
 | 
						|
    float min_est_ = 0.0f;
 | 
						|
	float max_est_ = 0.0f;
 | 
						|
	float min_cutoff_ = 0.0f;
 | 
						|
	float max_cutoff_ = 0.0f;
 | 
						|
	float min_var_ = 0.0f;
 | 
						|
	float max_var_ = 0.0f;
 | 
						|
	size_t min_count_ = 0;
 | 
						|
	size_t max_count_ = 0;
 | 
						|
	float deviation_ = 0.0f;
 | 
						|
	float offset_ = 0.0f;
 | 
						|
	float error_ = 0.0f;
 | 
						|
	float idev_ = 1.0f;
 | 
						|
    sample_filter_t dc_filter_{dc_b, dc_a};
 | 
						|
};
 | 
						|
 | 
						|
} // modemm17
 |