|
@@ -8,80 +8,111 @@ namespace mri {
|
|
|
void LockIn::Analyse(const std::vector<double> &signal,double period_ms,
|
|
|
const std::vector<double> ×teps,
|
|
|
int samples_per_period,
|
|
|
- std::vector<double> &avg_sin,
|
|
|
- std::vector<double> &avg_cos) {
|
|
|
+ std::vector<double> &out_sin,
|
|
|
+ std::vector<double> &out_cos) {
|
|
|
period_ms_=period_ms;
|
|
|
total_samples_ = signal.size();
|
|
|
+ mag_sin_.resize(total_samples_);
|
|
|
+ mag_cos_.resize(total_samples_);
|
|
|
+ samples_per_period_ = samples_per_period;
|
|
|
// Stage 1: Multiply
|
|
|
GenerateSinCos(timesteps);
|
|
|
- std::vector<double> mul_sin, mul_cos;
|
|
|
- mul_sin.resize(total_samples_);
|
|
|
- mul_cos.resize(total_samples_);
|
|
|
+ Multiply(signal);
|
|
|
+ // Stage 2: Low-pas filter:
|
|
|
+ //FilterLowPassFirstOrder();
|
|
|
+ //FilterLowPassForthOrder();
|
|
|
+ //FilterLowPassPeriodAverage();
|
|
|
+ FilterLowPassWindowedSinc();
|
|
|
+
|
|
|
+
|
|
|
+ unsigned long filtered_size = mag_sin_.size();
|
|
|
+ out_sin.resize(filtered_size);
|
|
|
+ out_cos.resize(filtered_size);
|
|
|
+ for (unsigned long i = 0; i<filtered_size; ++i) {
|
|
|
+ out_sin[i] = mag_sin_[i];
|
|
|
+ out_cos[i] = mag_cos_[i];
|
|
|
+ // out_sin[i] = mul_sin_[i];
|
|
|
+ // out_cos[i] = mul_cos_[i];
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ };
|
|
|
+ // ********************************************************************** //
|
|
|
+ // ********************************************************************** //
|
|
|
+ // ********************************************************************** //
|
|
|
+ void LockIn::Multiply(const std::vector<double> &signal) {
|
|
|
+ mul_sin_.resize(total_samples_);
|
|
|
+ mul_cos_.resize(total_samples_);
|
|
|
for (unsigned long i = 0; i < total_samples_; ++i) {
|
|
|
- mul_sin[i] = signal[i]*lockin_sin_[i];
|
|
|
- mul_cos[i] = signal[i]*lockin_cos_[i];
|
|
|
+ // mul_sin_[i] = signal[i]*lockin_sin_[i];
|
|
|
+ // mul_cos_[i] = signal[i]*lockin_cos_[i];
|
|
|
+ mul_sin_[i] = lockin_sin_[i]+1;
|
|
|
+ mul_cos_[i] = lockin_cos_[i]+2;
|
|
|
}
|
|
|
- // Stage 2: Low-pas filter:
|
|
|
- //y[n] = a0 x[n] + b1 y[n-1]
|
|
|
- // a0 = 1-x
|
|
|
- // b1 = x
|
|
|
- // x = exp (-1/samples)
|
|
|
- /// a0 = (1-x)^4
|
|
|
- // b1 = 4x
|
|
|
- // b2 = -6 x^2
|
|
|
- // b3 = 4 x^3
|
|
|
- // b4 = - x^4
|
|
|
- double x = std::exp(-1.0/(samples_per_period*2));
|
|
|
- std::vector<double> mag_sin, mag_cos;
|
|
|
- mag_sin.resize(total_samples_);
|
|
|
- mag_cos.resize(total_samples_);
|
|
|
- avg_sin.resize(total_samples_);
|
|
|
- avg_cos.resize(total_samples_);
|
|
|
+ };
|
|
|
+ // ********************************************************************** //
|
|
|
+ // ********************************************************************** //
|
|
|
+ // ********************************************************************** //
|
|
|
+ void LockIn::FilterLowPassPeriodAverage() {
|
|
|
double mean_sin = 0.0, mean_cos = 0.0;
|
|
|
- for (int i = 0; i < samples_per_period; ++i){
|
|
|
- mag_sin[i] = mul_sin[i];
|
|
|
- mag_cos[i] = mul_cos[i];
|
|
|
- avg_sin[i] = mul_sin[i];
|
|
|
- avg_cos[i] = mul_cos[i];
|
|
|
- mean_sin += mul_sin[i];
|
|
|
- mean_cos += mul_cos[i];
|
|
|
+ for (int i = 0; i < samples_per_period_; ++i){
|
|
|
+ mag_sin_[i] = mul_sin_[i];
|
|
|
+ mag_cos_[i] = mul_cos_[i];
|
|
|
+ mean_sin += mul_sin_[i];
|
|
|
+ mean_cos += mul_cos_[i];
|
|
|
}
|
|
|
- mean_sin /= samples_per_period;
|
|
|
- mean_cos /= samples_per_period;
|
|
|
- avg_sin[samples_per_period] = mean_sin;
|
|
|
- avg_cos[samples_per_period] = mean_cos;
|
|
|
- //First order low-pass
|
|
|
- double a0 = 1-x, b1 = x;
|
|
|
- for (unsigned long n = samples_per_period+1; n<total_samples_; ++n) {
|
|
|
- mag_sin[n] = a0*mul_sin[n] + b1*mag_sin[n-1];
|
|
|
- mag_cos[n] = a0*mul_cos[n] + b1*mag_cos[n-1];
|
|
|
- avg_sin[n] = avg_sin[n-1]
|
|
|
- + (mul_sin[n] - mul_sin[n-samples_per_period])/samples_per_period;
|
|
|
- avg_cos[n] = avg_cos[n-1]
|
|
|
- + (mul_cos[n] - mul_cos[n-samples_per_period])/samples_per_period;
|
|
|
+ mean_sin /= samples_per_period_;
|
|
|
+ mean_cos /= samples_per_period_;
|
|
|
+ mag_sin_[samples_per_period_] = mean_sin;
|
|
|
+ mag_cos_[samples_per_period_] = mean_cos;
|
|
|
+ for (unsigned long n = samples_per_period_+1; n<total_samples_; ++n) {
|
|
|
+ mag_sin_[n] = mag_sin_[n-1]
|
|
|
+ + (mul_sin_[n] - mul_sin_[n-samples_per_period_])/samples_per_period_;
|
|
|
+ mag_cos_[n] = mag_cos_[n-1]
|
|
|
+ + (mul_cos_[n] - mul_cos_[n-samples_per_period_])/samples_per_period_;
|
|
|
}
|
|
|
- // // Forth order low-pass
|
|
|
- // double a0 = pow2(pow2(1-x)), b1 = 4*x, b2 = -6*pow2(x), b3 = 4*x*x*x, b4 = - pow2(pow2(x));
|
|
|
- // for (unsigned long n = 1; n<total_samples_; ++n) {
|
|
|
- // mag_sin[n] = a0*mul_sin[n] + b1*mag_sin[n-1]
|
|
|
- // + b2*mag_sin[n-2] + b3*mag_sin[n-3] + b4*mag_sin[n-4];
|
|
|
- // mag_cos[n] = a0*mul_cos[n] + b1*mag_cos[n-1]
|
|
|
- // + b2*mag_cos[n-2] + b3*mag_cos[n-3] + b4*mag_cos[n-4];
|
|
|
- // }
|
|
|
|
|
|
- // Stage 3: Evaluate magnitude and phase
|
|
|
- std::vector<double> out_magnitude, out_phase;
|
|
|
- out_magnitude.resize(total_samples_);
|
|
|
- out_phase.resize(total_samples_);
|
|
|
- for (unsigned long i = 0; i<total_samples_; ++i) {
|
|
|
- out_magnitude[i] = std::sqrt(
|
|
|
- pow2(avg_sin[i]) +
|
|
|
- pow2(avg_cos[i])
|
|
|
- );
|
|
|
- out_phase[i] = std::atan(avg_sin[i]/avg_cos[i]);
|
|
|
+ };
|
|
|
+ // ********************************************************************** //
|
|
|
+ // ********************************************************************** //
|
|
|
+ // ********************************************************************** //
|
|
|
+ //First order low-pass filter
|
|
|
+ // y[n] = a0 x[n] + b1 y[n-1]
|
|
|
+ // a0 = 1-x
|
|
|
+ // b1 = x
|
|
|
+ // x = exp (-1/samples)
|
|
|
+ void LockIn::FilterLowPassFirstOrder() {
|
|
|
+ double x = std::exp(-1.0/(samples_per_period_*2));
|
|
|
+ mag_sin_[0] = mul_sin_[0];
|
|
|
+ mag_cos_[0] = mul_cos_[0];
|
|
|
+ double a0 = 1-x, b1 = x;
|
|
|
+ for (unsigned long n = 1; n<total_samples_; ++n) {
|
|
|
+ mag_sin_[n] = a0*mul_sin_[n] + b1*mag_sin_[n-1];
|
|
|
+ mag_cos_[n] = a0*mul_cos_[n] + b1*mag_cos_[n-1];
|
|
|
+ }
|
|
|
+ };
|
|
|
+ // ********************************************************************** //
|
|
|
+ // ********************************************************************** //
|
|
|
+ // ********************************************************************** //
|
|
|
+ // Forth order low-pass filter
|
|
|
+ /// a0 = (1-x)^4
|
|
|
+ // b1 = 4x
|
|
|
+ // b2 = -6 x^2
|
|
|
+ // b3 = 4 x^3
|
|
|
+ // b4 = - x^4
|
|
|
+ void LockIn::FilterLowPassForthOrder() {
|
|
|
+ double x = std::exp(-1.0/(samples_per_period_*2));
|
|
|
+ for (int i = 0; i < 5; ++i){
|
|
|
+ mag_sin_[i] = mul_sin_[i];
|
|
|
+ mag_cos_[i] = mul_cos_[i];
|
|
|
+ }
|
|
|
+ double a0 = pow2(pow2(1-x)), b1 = 4*x, b2 = -6*pow2(x), b3 = 4*x*x*x, b4 = - pow2(pow2(x));
|
|
|
+ for (unsigned long n = 4; n<total_samples_; ++n) {
|
|
|
+ mag_sin_[n] = a0*mul_sin_[n] + b1*mag_sin_[n-1]
|
|
|
+ + b2*mag_sin_[n-2] + b3*mag_sin_[n-3] + b4*mag_sin_[n-4];
|
|
|
+ mag_cos_[n] = a0*mul_cos_[n] + b1*mag_cos_[n-1]
|
|
|
+ + b2*mag_cos_[n-2] + b3*mag_cos_[n-3] + b4*mag_cos_[n-4];
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
};
|
|
|
// ********************************************************************** //
|
|
|
// ********************************************************************** //
|
|
@@ -101,5 +132,44 @@ namespace mri {
|
|
|
// ********************************************************************** //
|
|
|
// ********************************************************************** //
|
|
|
// ********************************************************************** //
|
|
|
+ void LockIn::FilterLowPassWindowedSinc() {
|
|
|
+ double fc = 0.05; // Cutoff frequency as a fraction of the sampling rate (in (0, 0.5)).
|
|
|
+ int N = 1001; // Kernel size, should be odd.
|
|
|
+ int M = N-1;
|
|
|
+ int mid = static_cast<int>(M/2);
|
|
|
+ //Evaluate kernel
|
|
|
+ std::vector<double> h(N);
|
|
|
+ for (int i = 0; i<N; ++i) {
|
|
|
+ double x = static_cast<double>(i-mid);
|
|
|
+ if (i == mid) {h[i] = 2.0*PI_*fc;}
|
|
|
+ else {h[i] = std::sin(2*PI_*fc *x)/x;}
|
|
|
+ //Blackman window
|
|
|
+ h[i] *= 0.42 - 0.5*std::cos(2.0*PI_*i/M)+0.08*cos(4.0*PI_*i/M);
|
|
|
+ //std::cout<< x<< ": "<< h[i] <<std::endl;
|
|
|
+ }
|
|
|
+ //Normalize kernel
|
|
|
+ double sum = 0.0;
|
|
|
+ for (auto kern:h) sum+=kern;
|
|
|
+ for (auto &kern:h) kern/=sum;
|
|
|
+ // for (int i = 0; i<N; ++i) std::cout<< h[i] <<std::endl;
|
|
|
+ //Convolution
|
|
|
+ for (long j = 0; j < N; ++j) {
|
|
|
+ mag_sin_[j] = 0;
|
|
|
+ mag_cos_[j] = 0;
|
|
|
+ mul_sin_[j] = 0;
|
|
|
+ mul_cos_[j] = 0;
|
|
|
+ }
|
|
|
+ for (unsigned long j = N-1; j < total_samples_; ++j) {
|
|
|
+ mag_sin_[j] = 0;
|
|
|
+ mag_cos_[j] = 0;
|
|
|
+ for (int i =0; i<N; ++i) {
|
|
|
+ mag_sin_[j] += mul_sin_[j-i]*h[i];
|
|
|
+ mag_cos_[j] += mul_cos_[j-i]*h[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ // ********************************************************************** //
|
|
|
+ // ********************************************************************** //
|
|
|
+ // ********************************************************************** //
|
|
|
|
|
|
} // end of namespace mri
|