2016-07-12 02:12:58 +00:00
|
|
|
//
|
|
|
|
// DigitalPhaseLockedLoop.cpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 11/07/2016.
|
|
|
|
// Copyright © 2016 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "DigitalPhaseLockedLoop.hpp"
|
2016-07-27 20:24:24 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cstdlib>
|
2016-07-13 00:23:56 +00:00
|
|
|
|
|
|
|
using namespace Storage;
|
|
|
|
|
2016-07-28 15:32:14 +00:00
|
|
|
DigitalPhaseLockedLoop::DigitalPhaseLockedLoop(int clocks_per_bit, int tolerance, size_t length_of_history) :
|
2017-03-26 18:34:47 +00:00
|
|
|
clocks_per_bit_(clocks_per_bit),
|
|
|
|
tolerance_(tolerance),
|
|
|
|
phase_(0),
|
|
|
|
window_length_(clocks_per_bit),
|
2017-07-16 02:40:38 +00:00
|
|
|
phase_error_pointer_(0),
|
|
|
|
phase_error_history_(length_of_history, 0) {}
|
2016-07-13 00:23:56 +00:00
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
void DigitalPhaseLockedLoop::run_for_cycles(int number_of_cycles) {
|
2016-12-03 16:59:28 +00:00
|
|
|
phase_ += number_of_cycles;
|
2017-03-26 18:34:47 +00:00
|
|
|
if(phase_ >= window_length_) {
|
2016-12-03 16:59:28 +00:00
|
|
|
int windows_crossed = phase_ / window_length_;
|
2016-08-03 01:28:50 +00:00
|
|
|
|
2016-07-28 18:35:39 +00:00
|
|
|
// check whether this triggers any 0s, if anybody cares
|
2017-03-26 18:34:47 +00:00
|
|
|
if(delegate_) {
|
2016-12-03 16:59:28 +00:00
|
|
|
if(window_was_filled_) windows_crossed--;
|
2016-07-28 18:35:39 +00:00
|
|
|
for(int c = 0; c < windows_crossed; c++)
|
2016-12-03 16:59:28 +00:00
|
|
|
delegate_->digital_phase_locked_loop_output_bit(0);
|
2016-07-13 00:23:56 +00:00
|
|
|
}
|
2016-07-28 18:35:39 +00:00
|
|
|
|
2016-12-03 16:59:28 +00:00
|
|
|
window_was_filled_ = false;
|
|
|
|
phase_ %= window_length_;
|
2016-07-13 00:23:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
void DigitalPhaseLockedLoop::add_pulse() {
|
|
|
|
if(!window_was_filled_) {
|
2016-12-03 16:59:28 +00:00
|
|
|
if(delegate_) delegate_->digital_phase_locked_loop_output_bit(1);
|
|
|
|
window_was_filled_ = true;
|
|
|
|
post_phase_error(phase_ - (window_length_ >> 1));
|
2016-07-14 23:42:01 +00:00
|
|
|
}
|
2016-07-28 15:32:14 +00:00
|
|
|
}
|
2016-07-14 23:42:01 +00:00
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
void DigitalPhaseLockedLoop::post_phase_error(int error) {
|
2016-07-28 15:32:14 +00:00
|
|
|
// use a simple spring mechanism as a lowpass filter for phase
|
2016-12-03 16:59:28 +00:00
|
|
|
phase_ -= (error + 1) >> 1;
|
2016-07-14 23:42:01 +00:00
|
|
|
|
2016-07-28 15:32:14 +00:00
|
|
|
// use the average of the last few errors to affect frequency
|
2017-07-16 02:40:38 +00:00
|
|
|
size_t phase_error_history_size = phase_error_history_.size();
|
2016-07-13 00:23:56 +00:00
|
|
|
|
2017-07-16 02:40:38 +00:00
|
|
|
phase_error_history_[phase_error_pointer_] = error;
|
2016-12-03 16:59:28 +00:00
|
|
|
phase_error_pointer_ = (phase_error_pointer_ + 1)%phase_error_history_size;
|
2016-07-13 00:23:56 +00:00
|
|
|
|
2016-07-28 15:32:14 +00:00
|
|
|
int total_error = 0;
|
2017-03-26 18:34:47 +00:00
|
|
|
for(size_t c = 0; c < phase_error_history_size; c++) {
|
2017-07-16 02:40:38 +00:00
|
|
|
total_error += phase_error_history_[c];
|
2016-07-13 00:23:56 +00:00
|
|
|
}
|
2016-07-28 15:32:14 +00:00
|
|
|
int denominator = (int)(phase_error_history_size * 4);
|
2016-12-03 16:59:28 +00:00
|
|
|
window_length_ += (total_error + (denominator >> 1)) / denominator;
|
|
|
|
window_length_ = std::max(std::min(window_length_, clocks_per_bit_ + tolerance_), clocks_per_bit_ - tolerance_);
|
2016-07-13 00:23:56 +00:00
|
|
|
}
|