mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
Had a second bash at the PLL. Probably I should read some of the literature.
This commit is contained in:
parent
63f39608a6
commit
e061e849d4
@ -1306,7 +1306,10 @@
|
||||
4B2409591C45DF85004DA684 /* SignalProcessing */,
|
||||
4B69FB391C4D908A00B5F0AA /* Storage */,
|
||||
);
|
||||
indentWidth = 4;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 4;
|
||||
usesTabs = 1;
|
||||
};
|
||||
4BB73E9F1B587A5100552FC2 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
|
@ -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))")
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user