summaryrefslogtreecommitdiff
path: root/TapTogglingAlgos
diff options
context:
space:
mode:
Diffstat (limited to 'TapTogglingAlgos')
-rw-r--r--TapTogglingAlgos/InformedMinCoeff.m109
-rw-r--r--TapTogglingAlgos/LeveragedLMS.m114
-rw-r--r--TapTogglingAlgos/MarginLock.m138
-rw-r--r--TapTogglingAlgos/MinCoeff.m8
-rw-r--r--TapTogglingAlgos/Threshold.m5
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