diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 72ae5a618..1b0319dee 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1306,7 +1306,10 @@ 4B2409591C45DF85004DA684 /* SignalProcessing */, 4B69FB391C4D908A00B5F0AA /* Storage */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; + usesTabs = 1; }; 4BB73E9F1B587A5100552FC2 /* Products */ = { isa = PBXGroup; diff --git a/OSBindings/Mac/Clock SignalTests/DPLLTests.swift b/OSBindings/Mac/Clock SignalTests/DPLLTests.swift index ada109d86..214160a98 100644 --- a/OSBindings/Mac/Clock SignalTests/DPLLTests.swift +++ b/OSBindings/Mac/Clock SignalTests/DPLLTests.swift @@ -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))") } } diff --git a/Storage/Disk/DigitalPhaseLockedLoop.cpp b/Storage/Disk/DigitalPhaseLockedLoop.cpp index 50c716a36..3add6446d 100644 --- a/Storage/Disk/DigitalPhaseLockedLoop.cpp +++ b/Storage/Disk/DigitalPhaseLockedLoop.cpp @@ -7,6 +7,8 @@ // #include "DigitalPhaseLockedLoop.hpp" +#include +#include 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;