1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Had a second bash at the PLL. Probably I should read some of the literature.

This commit is contained in:
Thomas Harte 2016-07-27 16:24:24 -04:00
parent 63f39608a6
commit e061e849d4
3 changed files with 40 additions and 30 deletions

View File

@ -1306,7 +1306,10 @@
4B2409591C45DF85004DA684 /* SignalProcessing */,
4B69FB391C4D908A00B5F0AA /* Storage */,
);
indentWidth = 4;
sourceTree = "<group>";
tabWidth = 4;
usesTabs = 1;
};
4BB73E9F1B587A5100552FC2 /* Products */ = {
isa = PBXGroup;

View File

@ -22,31 +22,31 @@ class DPLLTests: XCTestCase {
pll.runForCycles(bitLength/2)
}
XCTAssert((pll.stream&0xffffff) == 0xdddddd, "PLL should have synchronised and clocked repeating 0xd nibbles; got \(String(pll.stream, radix: 16, uppercase: false))")
XCTAssert((pll.stream&0xffffffff) == 0xdddddddd, "PLL should have synchronised and clocked repeating 0xd nibbles; got \(String(pll.stream, radix: 16, uppercase: false))")
}
func testPerfectInput() {
let pll = DigitalPhaseLockedLoopBridge(clocksPerBit: 100, tolerance: 20, historyLength: 5)
let pll = DigitalPhaseLockedLoopBridge(clocksPerBit: 100, tolerance: 20, historyLength: 3)
testRegularNibblesOnPLL(pll, bitLength: 100)
}
func testFastButRegular() {
let pll = DigitalPhaseLockedLoopBridge(clocksPerBit: 100, tolerance: 20, historyLength: 5)
let pll = DigitalPhaseLockedLoopBridge(clocksPerBit: 100, tolerance: 20, historyLength: 3)
testRegularNibblesOnPLL(pll, bitLength: 90)
}
func testSlowButRegular() {
let pll = DigitalPhaseLockedLoopBridge(clocksPerBit: 100, tolerance: 20, historyLength: 5)
let pll = DigitalPhaseLockedLoopBridge(clocksPerBit: 100, tolerance: 20, historyLength: 3)
testRegularNibblesOnPLL(pll, bitLength: 110)
}
func testTenPercentSinePattern() {
func testFivePercentSinePattern() {
let pll = DigitalPhaseLockedLoopBridge(clocksPerBit: 100, tolerance: 20, historyLength: 3)
var angle = 0.0
// clock in two 1s, a 0, and a 1, 200 times over
for _ in 0 ..< 200 {
let bitLength: UInt = UInt(100 + 5 * sin(angle))
let bitLength: UInt = UInt(100 + 2 * sin(angle))
pll.runForCycles(bitLength/2)
pll.addPulse()
@ -55,7 +55,7 @@ class DPLLTests: XCTestCase {
angle = angle + 0.1
}
let endOfStream = (pll.stream&0xffffff);
XCTAssert(endOfStream == 0xaaaaaa || endOfStream == 0x555555, "PLL should have synchronised and clocked repeating 0xa or 0x5 nibbles; got \(String(pll.stream, radix: 16, uppercase: false))")
let endOfStream = pll.stream&0xffffffff;
XCTAssert(endOfStream == 0xaaaaaaaa || endOfStream == 0x55555555, "PLL should have synchronised and clocked repeating 0xa or 0x5 nibbles; got \(String(pll.stream, radix: 16, uppercase: false))")
}
}

View File

@ -7,6 +7,8 @@
//
#include "DigitalPhaseLockedLoop.hpp"
#include <algorithm>
#include <cstdlib>
using namespace Storage;
@ -46,43 +48,48 @@ void DigitalPhaseLockedLoop::run_for_cycles(int number_of_cycles)
void DigitalPhaseLockedLoop::add_pulse()
{
int *const _pulse_history_array = (int *)_pulse_history.get();
int outgoing_pulse_time = 0;
if(_samples_collected < _length_of_history)
if(_samples_collected <= _length_of_history)
{
_samples_collected++;
}
else
{
// perform a linear regression
int sum_xy = 0;
int sum_x = 0;
int sum_y = 0;
int sum_x_squared = 0;
for(size_t pulse = 0; pulse < _length_of_history; pulse++)
{
int x = _pulse_history_array[pulse] / (int)_current_window_length;
int y = _pulse_history_array[pulse] % (int)_current_window_length;
outgoing_pulse_time = _pulse_history_array[0];
sum_xy += x*y;
sum_x += x;
sum_y += y;
sum_x_squared += x*x;
// temporary: perform an exhaustive search for the ideal window length
int minimum_error = __INT_MAX__;
int ideal_length = 0;
for(int c = _clocks_per_bit - _tolerance; c < _clocks_per_bit + _tolerance; c++)
{
int total_error = 0;
const int half_window = c >> 1;
for(size_t pulse = 1; pulse < _length_of_history; pulse++)
{
int difference = _pulse_history_array[pulse] - _pulse_history_array[pulse-1];
difference += half_window;
const int steps = difference / c;
const int offset = difference%c - half_window;
total_error += abs(offset / steps);
}
if(total_error < minimum_error)
{
minimum_error = total_error;
ideal_length = c;
}
}
int gradient = (_length_of_history*sum_xy - sum_x*sum_y) / (_length_of_history*sum_x_squared - sum_x*sum_x);
_current_window_length += gradient / 2;
if(_current_window_length < _clocks_per_bit - _tolerance) _current_window_length = _clocks_per_bit - _tolerance;
if(_current_window_length > _clocks_per_bit + _tolerance) _current_window_length = _clocks_per_bit + _tolerance;
// use a spring mechanism to effect a lowpass filter
_current_window_length = ((ideal_length + (_current_window_length*3)) + 2) >> 2;
}
// therefore, there was a 1 in this window
_window_was_filled = true;
if(_delegate) _delegate->digital_phase_locked_loop_output_bit(1);
// shift history one to the left, storing new value; act as though the outgoing pulse were exactly halfway through its
// window for adjustment purposes
int outgoing_pulse_time = _pulse_history_array[0];//_pulse_history_array[0] + (_current_window_length >> 1);
// shift history one to the left, storing new value, potentially with a centring adjustment
for(size_t pulse = 1; pulse < _length_of_history; pulse++)
{
_pulse_history_array[pulse - 1] = _pulse_history_array[pulse] - outgoing_pulse_time;