summaryrefslogtreecommitdiff
path: root/TapTogglingAlgos/MarginLock.m
blob: c54ae256ddc21ebadcc836be986abb27d6c615e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
classdef MarginLock < handle
    % Version: 0.1
    % Description:
    %   If the smallest registered COM is above 3dB for some period,
    %   then assume it is safe to turn off the most "insignificant"
    %   tap (which now is, kind of, the smallest one)
    %
    %   However, if smallest COM drops below 3dB for some period,
    %   then activate the fallback mechanism and turn on the recently
    %   disabled tap and terminate the algorithm
    
    properties
        lastDisabledTap
        internalFFETapsMask
        lastUpdateTime
        reachedHighErr

        cooldownPeriod
        errCollectionPeriod

        initialErrCounter
        maxErr
        runningMeanErr
        errCounter
        cooldownCounter
        reachedHighCOM
        reducedErrSeqCounter
        increasedErrSeqCounter
    end

    methods
        function obj = MarginLock(LMSUpdateObj)
            obj.lastDisabledTap = -1;
            obj.internalFFETapsMask = LMSUpdateObj.FFE_TapsMask;
            obj.lastUpdateTime = LMSUpdateObj.StartFFEAdaptTime * LMSUpdateObj.WindowSize;
            obj.reachedHighErr = 0;

            obj.cooldownPeriod = 2;
            obj.errCollectionPeriod = 10;

            obj.initialErrCounter = 0;
            obj.maxErr = 0;
            obj.runningMeanErr = 0;
            obj.errCounter = 0;
            obj.cooldownCounter = 0;
            obj.reachedHighCOM = 0;
            obj.reducedErrSeqCounter = 0;
            obj.increasedErrSeqCounter = 0;
        end

        function FFETapsMask = GetFFETapsMask(obj, LMSUpdateObj)
            initialTogglingStart = LMSUpdateObj.LearningRatesAdjustTimes(1) + 8;
            canStartAlgo = LMSUpdateObj.IterCount > initialTogglingStart * LMSUpdateObj.WindowSize;
            errVecSize = max(size(LMSUpdateObj.ErrorVec));
            inCooldown = obj.cooldownCounter > 0;

            dlevDiff1 = LMSUpdateObj.Dlev_0p5 - LMSUpdateObj.Dlev_0p16;
            dlevDiff2 = LMSUpdateObj.Dlev_0p16 - LMSUpdateObj.Dlev_m0p16;
            dlevDiff3 = LMSUpdateObj.Dlev_m0p16 - LMSUpdateObj.Dlev_m0p5;
            minDlevDiff = min([dlevDiff1 dlevDiff2 dlevDiff3]);
            margin = minDlevDiff / 2;

            % if eye is not open yet, monitor when high enough COM is
            % reached
            if canStartAlgo && ~obj.reachedHighCOM && ~obj.reachedHighErr
                obj.maxErr = max(obj.maxErr, max(abs(LMSUpdateObj.ErrorVec)));
                obj.initialErrCounter = obj.initialErrCounter + 1;
            end

            % since maximum error can not be used very accurately for
            % monitoring high COM, the threshold for initial COM becomes 4dB
            if canStartAlgo && ~obj.reachedHighCOM && ~obj.reachedHighErr
                if obj.initialErrCounter >= 3 * LMSUpdateObj.WindowSize
                    approxMinCOM = 20*log10(margin / obj.maxErr);

                    if approxMinCOM >= 4
                        obj.reachedHighCOM = 1;
                    end

                    obj.initialErrCounter = 0;
                    obj.maxErr = 0;
                end
            end
            
            if canStartAlgo && ~inCooldown && obj.reachedHighCOM && ~obj.reachedHighErr
                obj.runningMeanErr = (sum(abs(LMSUpdateObj.ErrorVec)) + obj.runningMeanErr * obj.errCounter * errVecSize) / ((obj.errCounter + 1) * errVecSize);
                obj.errCounter = obj.errCounter + 1;
            end

            if canStartAlgo && inCooldown && ~obj.reachedHighErr
                obj.cooldownCounter = obj.cooldownCounter - 1;
            end

            weights = abs(LMSUpdateObj.TapWeights);

            % Take into account the neighboring taps as well
            weights_padded = [0 weights 0];
            weights_avg = (weights_padded(1:end-2) + 2*weights_padded(2:end-1) + weights_padded(3:end)) / 3;
            weights_avg(~obj.internalFFETapsMask) = inf;

            [~, minTapIndex] = min(weights_avg);

            if obj.errCounter >= obj.errCollectionPeriod * LMSUpdateObj.WindowSize && ~obj.reachedHighErr
                std = obj.runningMeanErr * sqrt(pi / 2);
                %std = std * 1.05; % due to ISI, samples are not all iid, so std is within +-5% with 95% CI
                possibleMaxErr = 4.75 * std; % trying to find COM over 1e6 symbols with 90% confidence => using 5std
                statMinCOM = 20*log10(margin / possibleMaxErr);

                if statMinCOM >= 3
                    % Stop the LMS adaptation of FFE and DFE taps
                    LMSUpdateObj.setLearningRates(0, 0, LMSUpdateObj.MuDlev);

                    obj.internalFFETapsMask(minTapIndex) = 0;
                    obj.lastDisabledTap = minTapIndex;
                    obj.lastUpdateTime = LMSUpdateObj.IterCount;

                    % cooldown for some LMS iterations so that other taps can
                    % adapt to the recently turned off tap and then start
                    % measuring the error distribution
                    obj.cooldownCounter = obj.cooldownPeriod * LMSUpdateObj.WindowSize;
                else
                    obj.reachedHighErr = 1;
                    if obj.lastDisabledTap ~= -1
                        obj.internalFFETapsMask(obj.lastDisabledTap) = 1;
                    end
    
                    % to make sure it throws an error in case tried being accessed
                    obj.lastDisabledTap = -1;
                end

                obj.errCounter = 0;
                obj.runningMeanErr = 0;
            end
        
            FFETapsMask = obj.internalFFETapsMask;
        end
    end
end