diff options
Diffstat (limited to 'TapTogglingAlgos')
| -rw-r--r-- | TapTogglingAlgos/InformedMinCoeff.m | 109 | ||||
| -rw-r--r-- | TapTogglingAlgos/LeveragedLMS.m | 114 | ||||
| -rw-r--r-- | TapTogglingAlgos/MarginLock.m | 138 | ||||
| -rw-r--r-- | TapTogglingAlgos/MinCoeff.m | 8 | ||||
| -rw-r--r-- | TapTogglingAlgos/Threshold.m | 5 |
5 files changed, 374 insertions, 0 deletions
diff --git a/TapTogglingAlgos/InformedMinCoeff.m b/TapTogglingAlgos/InformedMinCoeff.m new file mode 100644 index 0000000..cb68d33 --- /dev/null +++ b/TapTogglingAlgos/InformedMinCoeff.m @@ -0,0 +1,109 @@ +classdef InformedMinCoeff < handle + % InformedMinCoeff + % Toggles taps based on the available "Error Budget" (Slack). + % + % Logic: + % 1. Calculate Error Limit (half distance between Dlevs). + % 2. Calculate Current Max Error. + % 3. Slack = Limit - Current. + % 4. Turn off smallest taps as long as sum(|Tap| * 0.5) < Slack. + % 5. Wait for LMS to adapt, then repeat. + + properties + WaitCycles % How many iterations to wait between updates + IterationCounter % Internal counter + ReloadPeriod + CurrentMask % The currently active mask + MaxInputVal % Worst case input assumption (0.5) + MaxCycleError + end + + methods + function obj = InformedMinCoeff(LMSUpdateObj) + obj.IterationCounter = 0; + obj.ReloadPeriod = 5; + obj.MaxInputVal = 0.5; + obj.MaxCycleError = 0; + + obj.WaitCycles = obj.ReloadPeriod * LMSUpdateObj.WindowSize; + + tapCount = LMSUpdateObj.NumPreTaps + 1 + LMSUpdateObj.NumPostTaps; + obj.CurrentMask = ones(1, tapCount); + end + + function mask = GetFFETapsMask(obj, lmsObj) + % Only update logic if adaptation is active + if lmsObj.Adapting == 0 + mask = obj.CurrentMask; + return; + end + + % Determine Current Max Error (From the latest window) + maxStepError = max(abs(lmsObj.ErrorVec)); + if maxStepError > obj.MaxCycleError + obj.MaxCycleError = maxStepError; + end + currentMaxError = obj.MaxCycleError; + + + obj.IterationCounter = obj.IterationCounter + 1; + if obj.IterationCounter < obj.WaitCycles + mask = obj.CurrentMask; + return; + end + + % Error Tolerance = min(distance_between_dlevs) / 2 + dlevs = sort([lmsObj.Dlev_m0p5, lmsObj.Dlev_m0p16, lmsObj.Dlev_0p16, lmsObj.Dlev_0p5]); + maxAcceptableError = min(diff(dlevs)) / 2; + + slack = maxAcceptableError - currentMaxError; + + % Determine Taps to Turn Off + if slack <= 0 + % Negative or zero slack: Cannot turn off anything. + % Ideally, we should turn everything ON to recover. + mask = obj.CurrentMask; + else + % Get magnitude of current taps + absTaps = abs(lmsObj.TapWeights); + + % Sort taps to find candidates (smallest first) + % We need original indices to create the mask later + [sortedVals, originalIndices] = sort(absTaps, 'ascend'); + + accumulatedErrorCost = 0; + tapsToDisableIndices = []; + + for k = 1:length(sortedVals) + % Calculate worst-case error contribution of this tap + % ErrorDelta = |Coeff * MaxInput| + cost = sortedVals(k) * obj.MaxInputVal; + + if (accumulatedErrorCost + cost) < slack + % We can afford to turn this off + accumulatedErrorCost = accumulatedErrorCost + cost; + tapsToDisableIndices(end+1) = originalIndices(k); + else + % Cannot afford any more + break; + end + end + + % Create new mask + newMask = ones(size(lmsObj.TapWeights)); + newMask(tapsToDisableIndices) = 0; + + % Force Main Cursor (Max Tap) to stay ON for safety + [~, maxIdx] = max(absTaps); + newMask(maxIdx) = 1; + + obj.CurrentMask = newMask; + end + + % Reset Counter and return + obj.MaxCycleError = 0; + obj.IterationCounter = 0; + mask = obj.CurrentMask; + end + end +end
\ No newline at end of file diff --git a/TapTogglingAlgos/LeveragedLMS.m b/TapTogglingAlgos/LeveragedLMS.m new file mode 100644 index 0000000..ed20f67 --- /dev/null +++ b/TapTogglingAlgos/LeveragedLMS.m @@ -0,0 +1,114 @@ +classdef LeveragedLMS < handle + % Version: 0.2 (min-COM-based) + % 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 + + lastMeanErr + maxErr + reachedHighCOM + reducedErrSeqCounter + increasedErrSeqCounter + + targetCounters + targetCounterIndex + end + + methods + function obj = LeveragedLMS(LMSUpdateObj) + obj.lastDisabledTap = -1; + obj.internalFFETapsMask = LMSUpdateObj.FFE_TapsMask; + obj.lastUpdateTime = LMSUpdateObj.StartFFEAdaptTime * LMSUpdateObj.WindowSize; + obj.reachedHighErr = 0; + + obj.lastMeanErr = inf; + obj.maxErr = 0; + obj.reachedHighCOM = 0; + obj.reducedErrSeqCounter = 0; + obj.increasedErrSeqCounter = 0; + + obj.targetCounters = [2 1]; + obj.targetCounterIndex = 1; + end + + function FFETapsMask = GetFFETapsMask(obj, LMSUpdateObj) + initialTogglingStart = LMSUpdateObj.StartFFEAdaptTime + 5; + + if ismember(LMSUpdateObj.IterCount, LMSUpdateObj.LearningRatesAdjustTimes * LMSUpdateObj.WindowSize) + obj.targetCounterIndex = obj.targetCounterIndex + 1; + end + + obj.maxErr = max(obj.maxErr, max(abs(LMSUpdateObj.ErrorVec))); + + if mod(LMSUpdateObj.IterCount, LMSUpdateObj.WindowSize) == 0 && ... + LMSUpdateObj.IterCount > initialTogglingStart * LMSUpdateObj.WindowSize && ~obj.reachedHighErr + %avgErr = mean(abs(LMSUpdateObj.ErrorVec)); + + 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]); + t = minDlevDiff / 2; + + minCOM = 20*log10(t / obj.maxErr); + + if minCOM >= 3 + obj.reducedErrSeqCounter = obj.reducedErrSeqCounter + 1; + obj.increasedErrSeqCounter = 0; + obj.reachedHighCOM = 1; + else + obj.reducedErrSeqCounter = 0; + if obj.reachedHighCOM + obj.increasedErrSeqCounter = obj.increasedErrSeqCounter + 1; + end + end + %obj.lastMeanErr = avgErr * 1.125; + + obj.maxErr = 0; + 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.reducedErrSeqCounter == obj.targetCounters(obj.targetCounterIndex) && ... + LMSUpdateObj.IterCount > initialTogglingStart * LMSUpdateObj.WindowSize && ~obj.reachedHighErr + obj.internalFFETapsMask(minTapIndex) = 0; + obj.lastDisabledTap = minTapIndex; + obj.lastUpdateTime = LMSUpdateObj.IterCount; + obj.reducedErrSeqCounter = 0; + obj.increasedErrSeqCounter = 0; + end + + if obj.increasedErrSeqCounter == obj.targetCounters(obj.targetCounterIndex)*2 && ~obj.reachedHighErr + 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; + obj.reducedErrSeqCounter = 0; + obj.increasedErrSeqCounter = 0; + end + + FFETapsMask = obj.internalFFETapsMask; + end + end +end
\ No newline at end of file diff --git a/TapTogglingAlgos/MarginLock.m b/TapTogglingAlgos/MarginLock.m new file mode 100644 index 0000000..c54ae25 --- /dev/null +++ b/TapTogglingAlgos/MarginLock.m @@ -0,0 +1,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
\ No newline at end of file diff --git a/TapTogglingAlgos/MinCoeff.m b/TapTogglingAlgos/MinCoeff.m new file mode 100644 index 0000000..1bfa568 --- /dev/null +++ b/TapTogglingAlgos/MinCoeff.m @@ -0,0 +1,8 @@ +function mask = MinCoeff(taps, toggleCount, ~) + mask = ones(size(taps)); + + [~, sortedIdx] = sort(abs(taps), 'ascend'); + zeroIdx = sortedIdx(1:toggleCount); + + mask(zeroIdx) = 0; + end
\ No newline at end of file diff --git a/TapTogglingAlgos/Threshold.m b/TapTogglingAlgos/Threshold.m new file mode 100644 index 0000000..e81f10b --- /dev/null +++ b/TapTogglingAlgos/Threshold.m @@ -0,0 +1,5 @@ +function mask = Threshold(taps, thresholdPercent, ~) + mask = ones(size(taps)); + maxTapVal = max(abs(taps)); + mask(abs(taps) < thresholdPercent*maxTapVal) = 0; +end
\ No newline at end of file |
