2016-07-11 22:12:58 -04: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 16:24:24 -04:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cstdlib>
|
2016-07-12 20:23:56 -04:00
|
|
|
|
|
|
|
using namespace Storage;
|
|
|
|
|
2017-11-11 15:28:40 -05:00
|
|
|
DigitalPhaseLockedLoop::DigitalPhaseLockedLoop(int clocks_per_bit, std::size_t length_of_history) :
|
2017-07-16 16:49:04 -04:00
|
|
|
offset_history_(length_of_history, 0),
|
2017-10-17 22:13:37 -04:00
|
|
|
window_length_(clocks_per_bit),
|
|
|
|
clocks_per_bit_(clocks_per_bit) {}
|
2016-07-12 20:23:56 -04:00
|
|
|
|
2017-07-27 22:05:29 -04:00
|
|
|
void DigitalPhaseLockedLoop::run_for(const Cycles cycles) {
|
2017-07-24 21:04:47 -04:00
|
|
|
offset_ += cycles.as_int();
|
|
|
|
phase_ += cycles.as_int();
|
2017-03-26 14:34:47 -04:00
|
|
|
if(phase_ >= window_length_) {
|
2016-12-03 11:59:28 -05:00
|
|
|
int windows_crossed = phase_ / window_length_;
|
2016-08-02 21:28:50 -04:00
|
|
|
|
2016-07-28 14:35:39 -04:00
|
|
|
// check whether this triggers any 0s, if anybody cares
|
2017-03-26 14:34:47 -04:00
|
|
|
if(delegate_) {
|
2016-12-03 11:59:28 -05:00
|
|
|
if(window_was_filled_) windows_crossed--;
|
2016-07-28 14:35:39 -04:00
|
|
|
for(int c = 0; c < windows_crossed; c++)
|
2016-12-03 11:59:28 -05:00
|
|
|
delegate_->digital_phase_locked_loop_output_bit(0);
|
2016-07-12 20:23:56 -04:00
|
|
|
}
|
2016-07-28 14:35:39 -04:00
|
|
|
|
2016-12-03 11:59:28 -05:00
|
|
|
window_was_filled_ = false;
|
|
|
|
phase_ %= window_length_;
|
2016-07-12 20:23:56 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-26 14:34:47 -04:00
|
|
|
void DigitalPhaseLockedLoop::add_pulse() {
|
|
|
|
if(!window_was_filled_) {
|
2016-12-03 11:59:28 -05:00
|
|
|
if(delegate_) delegate_->digital_phase_locked_loop_output_bit(1);
|
|
|
|
window_was_filled_ = true;
|
2017-07-16 16:49:04 -04:00
|
|
|
post_phase_offset(phase_, offset_);
|
|
|
|
offset_ = 0;
|
2016-07-14 19:42:01 -04:00
|
|
|
}
|
2016-07-28 11:32:14 -04:00
|
|
|
}
|
2016-07-14 19:42:01 -04:00
|
|
|
|
2017-07-21 20:58:17 -04:00
|
|
|
void DigitalPhaseLockedLoop::post_phase_offset(int new_phase, int new_offset) {
|
|
|
|
offset_history_[offset_history_pointer_] = new_offset;
|
2017-07-16 19:03:50 -04:00
|
|
|
offset_history_pointer_ = (offset_history_pointer_ + 1) % offset_history_.size();
|
2017-07-16 16:49:04 -04:00
|
|
|
|
2017-07-16 19:03:50 -04:00
|
|
|
// use an unweighted average of the stored offsets to compute current window size,
|
|
|
|
// bucketing them by rounding to the nearest multiple of the base clocks per bit
|
2017-07-16 16:49:04 -04:00
|
|
|
int total_spacing = 0;
|
|
|
|
int total_divisor = 0;
|
|
|
|
for(int offset : offset_history_) {
|
|
|
|
int multiple = (offset + (clocks_per_bit_ >> 1)) / clocks_per_bit_;
|
|
|
|
if(!multiple) continue;
|
|
|
|
total_divisor += multiple;
|
|
|
|
total_spacing += offset;
|
|
|
|
}
|
2017-07-16 17:12:12 -04:00
|
|
|
if(total_divisor) {
|
|
|
|
window_length_ = total_spacing / total_divisor;
|
|
|
|
}
|
2017-07-16 16:49:04 -04:00
|
|
|
|
2017-07-21 20:58:17 -04:00
|
|
|
int error = new_phase - (window_length_ >> 1);
|
2017-07-16 16:49:04 -04:00
|
|
|
|
2016-07-28 11:32:14 -04:00
|
|
|
// use a simple spring mechanism as a lowpass filter for phase
|
2016-12-03 11:59:28 -05:00
|
|
|
phase_ -= (error + 1) >> 1;
|
2016-07-12 20:23:56 -04:00
|
|
|
}
|