From 1dff61a44292d294062d2d634fc0eb49c182e5b3 Mon Sep 17 00:00:00 2001 From: Lewin Probst <info@emirror.de> Date: Tue, 3 Dec 2019 10:46:49 +0100 Subject: [PATCH] Added offsets calculation using matrix A, B, X. --- .../+edit/+offsets/buttonCalculateOffsets.m | 57 ++++++++++ .../+offsets/checkboxForceOptimumOffsets.m | 35 ++++++ lib/+artoa/+controller/+edit/+offsets/open.m | 3 + .../+edit/+offsets/selectOffsetsAndDrift.m | 22 +++- .../+controller/+edit/+offsets/updateGui.m | 2 + lib/+artoa/+data/calculateSoundVelocity.m | 1 - lib/+artoa/+gui/+edit/offsets.m | 33 +++++- .../+offsets/extractOffsetsDriftsFromSolved.m | 20 ++++ lib/+artoa/+offsets/solve.m | 106 ++++++++++++++++++ 9 files changed, 274 insertions(+), 5 deletions(-) create mode 100644 lib/+artoa/+controller/+edit/+offsets/buttonCalculateOffsets.m create mode 100644 lib/+artoa/+controller/+edit/+offsets/checkboxForceOptimumOffsets.m create mode 100644 lib/+artoa/+offsets/extractOffsetsDriftsFromSolved.m create mode 100644 lib/+artoa/+offsets/solve.m diff --git a/lib/+artoa/+controller/+edit/+offsets/buttonCalculateOffsets.m b/lib/+artoa/+controller/+edit/+offsets/buttonCalculateOffsets.m new file mode 100644 index 0000000..554e3ae --- /dev/null +++ b/lib/+artoa/+controller/+edit/+offsets/buttonCalculateOffsets.m @@ -0,0 +1,57 @@ +function [] = buttonCalculateOffsets(~, ~) +%UNTITLED Summary of this function goes here +% Detailed explanation goes here + +global artoaWorkspace artoaDataInput; + +%% Initialize required variables +soundsources = artoa.controller.getSoundsourcesWithAppliedToa(); +satData = artoaWorkspace.satData; + +%% Calculate offsets + +[ + artoaWorkspace.editOffsets.calculatedMatrixA, ... + artoaWorkspace.editOffsets.calculatedMatrixB, ... + artoaWorkspace.editOffsets.calculatedMatrixX ... +] = artoa.offsets.solve( ... + artoaDataInput.rfb, ... + soundsources, ... + satData, ... + artoaWorkspace.temperature(artoaWorkspace.statusTemperature == 1), ... + artoaWorkspace.pressure(artoaWorkspace.statusPressure == 1), ... + artoaWorkspace.trackParameter.soundspeedMethodString, ... + artoa.data.getMember(artoaDataInput, {'ini', 'leapseconds'}) ... +); + +%% Store offsets in table +[offsets, drifts] = artoa.offsets.extractOffsetsDriftsFromSolved(artoaWorkspace.editOffsets.calculatedMatrixX); + +artoaWorkspace.editOffsets.soundsourceOffsets.OptimumTotalOffset = offsets; +artoaWorkspace.editOffsets.soundsourceOffsets.OptimumTotalDrift = drifts; + + +%% Update TOA + +% combine initial toa and current applied soundsources +toaData = artoaWorkspace.toaData; +toaData.toa = artoaDataInput.toaData.toa; + +%% Recalculate drift +artoa.controller.edit.offsets.updateWorkspaceOffsetsTable(); +artoa.controller.edit.offsets.updateGui(); + +%% Select offset and drift from the table +artoa.controller.edit.offsets.selectOffsetsAndDrift(); + +artoaWorkspace.toaData = artoa.toa.recalculate( ... + artoaWorkspace.float, ... + toaData, ... + artoa.controller.getSoundsourcesWithAppliedToa(), ... + artoaWorkspace.editOffsets.selectedOffsets ... +); + +artoa.controller.edit.timeOfArrival.plot(); + +end + diff --git a/lib/+artoa/+controller/+edit/+offsets/checkboxForceOptimumOffsets.m b/lib/+artoa/+controller/+edit/+offsets/checkboxForceOptimumOffsets.m new file mode 100644 index 0000000..49efe45 --- /dev/null +++ b/lib/+artoa/+controller/+edit/+offsets/checkboxForceOptimumOffsets.m @@ -0,0 +1,35 @@ +function [newValue] = checkboxForceOptimumOffsets(~, ~) +%CHECKBOXDOPPLERCORRECTION Summary of this function goes here +% Detailed explanation goes here + +global artoaGui artoaWorkspace artoaDataInput; + +newValue = logical(artoaGui.editOffsets.checkboxForceOptimumOffsets.Value); + +artoaWorkspace.editOffsets.forceOptimumOffsets = newValue; + +%% Update TOA + +% combine initial toa and current applied soundsources +toaData = artoaWorkspace.toaData; +toaData.toa = artoaDataInput.toaData.toa; + +%% Recalculate drift +artoa.controller.edit.offsets.updateWorkspaceOffsetsTable(); +artoa.controller.edit.offsets.updateGui(); + +%% Select offset and drift from the table +artoa.controller.edit.offsets.selectOffsetsAndDrift(); + +artoaWorkspace.toaData = artoa.toa.recalculate( ... + artoaWorkspace.float, ... + toaData, ... + artoa.controller.getSoundsourcesWithAppliedToa(), ... + artoaWorkspace.editOffsets.selectedOffsets ... +); + +artoa.controller.edit.timeOfArrival.plot(); + + +end + diff --git a/lib/+artoa/+controller/+edit/+offsets/open.m b/lib/+artoa/+controller/+edit/+offsets/open.m index bad746d..7d84639 100644 --- a/lib/+artoa/+controller/+edit/+offsets/open.m +++ b/lib/+artoa/+controller/+edit/+offsets/open.m @@ -15,6 +15,8 @@ end callbacks = struct(); callbacks.tableSoundsourceOffsetsEdit = @artoa.controller.edit.offsets.tableSoundsourceOffsetsEdit; +callbacks.buttonCalculateOffsets = @artoa.controller.edit.offsets.buttonCalculateOffsets; +callbacks.checkboxForceOptimumOffsets = @artoa.controller.edit.offsets.checkboxForceOptimumOffsets; %% Open the gui artoa.gui.edit.offsets(callbacks); @@ -26,6 +28,7 @@ end %% Create workspace variables artoaWorkspace.editOffsets = struct(); +artoaWorkspace.editOffsets.forceOptimumOffsets = false; artoa.controller.edit.offsets.updateWorkspaceOffsetsTable(); %% Update gui diff --git a/lib/+artoa/+controller/+edit/+offsets/selectOffsetsAndDrift.m b/lib/+artoa/+controller/+edit/+offsets/selectOffsetsAndDrift.m index d37ddd1..bb5bc2a 100644 --- a/lib/+artoa/+controller/+edit/+offsets/selectOffsetsAndDrift.m +++ b/lib/+artoa/+controller/+edit/+offsets/selectOffsetsAndDrift.m @@ -10,8 +10,16 @@ selectedOffsets = table(); %% Get required variables offsetsTable = artoaWorkspace.editOffsets.soundsourceOffsets; -%% Select float offset and drift; -selectedOffsets('Float', :) = selectFromRow(offsetsTable('Float', :)); +%% Select float offset and drift +if artoaWorkspace.editOffsets.forceOptimumOffsets + tmp = table(); + tmp.offset = offsetsTable{'Float', 'OptimumTotalOffset'}; + tmp.drift = offsetsTable{'Float', 'OptimumTotalDrift'}; + selectedOffsets('Float', :) = tmp; + clear tmp; +else + selectedOffsets('Float', :) = selectFromRow(offsetsTable('Float', :)); +end %% Run through all soundsources variableNames = offsetsTable.Properties.RowNames; @@ -19,7 +27,15 @@ for i = 1:length(variableNames) if strcmp('Float', variableNames{i}) continue; end - selectedOffsets(variableNames{i}, :) = selectFromRow(offsetsTable(variableNames{i}, :)); + if artoaWorkspace.editOffsets.forceOptimumOffsets + tmp = table(); + tmp.offset = offsetsTable{variableNames{i}, 'OptimumTotalOffset'}; + tmp.drift = offsetsTable{variableNames{i}, 'OptimumTotalDrift'}; + selectedOffsets(variableNames{i}, :) = tmp; + clear tmp; + else + selectedOffsets(variableNames{i}, :) = selectFromRow(offsetsTable(variableNames{i}, :)); + end end %% Store selected values diff --git a/lib/+artoa/+controller/+edit/+offsets/updateGui.m b/lib/+artoa/+controller/+edit/+offsets/updateGui.m index 91d1319..32df12f 100644 --- a/lib/+artoa/+controller/+edit/+offsets/updateGui.m +++ b/lib/+artoa/+controller/+edit/+offsets/updateGui.m @@ -16,6 +16,8 @@ fieldNames = fieldnames(fields); for i = 1:length(fieldNames) currentValue = fields.(fieldNames{i}); switch fieldNames{i} + case 'forceOptimumOffsets' + artoaGui.editOffsets.checkboxForceOptimumOffsets.Value = currentValue; case 'soundsourceOffsets' artoaGui.editOffsets.tableSoundsourceOffsets.Data = table2cell( ... currentValue ... diff --git a/lib/+artoa/+data/calculateSoundVelocity.m b/lib/+artoa/+data/calculateSoundVelocity.m index 4f50795..96c6a0d 100644 --- a/lib/+artoa/+data/calculateSoundVelocity.m +++ b/lib/+artoa/+data/calculateSoundVelocity.m @@ -6,7 +6,6 @@ function [calculatedSoundVelocity] = calculateSoundVelocity(pTemperature, pPress % pMethod The chosen method. Available methods are: % del grosso % linear -% soundsource % % Returns: % The calculated sound velocity in m/s. diff --git a/lib/+artoa/+gui/+edit/offsets.m b/lib/+artoa/+gui/+edit/offsets.m index bebec33..8198af2 100644 --- a/lib/+artoa/+gui/+edit/offsets.m +++ b/lib/+artoa/+gui/+edit/offsets.m @@ -12,7 +12,9 @@ windowTitle = [ 'ARTOA4 - Float ' num2str(artoaWorkspace.float.floatname) ' - Of availableCallbacks = { ... 'CloseRequestFcn', ... 'tableSoundsourceOffsetsSelect', ... - 'tableSoundsourceOffsetsEdit' ... + 'tableSoundsourceOffsetsEdit', ... + 'buttonCalculateOffsets', ... + 'checkboxForceOptimumOffsets' ... }; for i = 1:length(availableCallbacks) % check if a callback is undefined @@ -56,6 +58,35 @@ artoaGui.editOffsets.tableSoundsourceOffsets = uitable( ... 'CellEditCallback', pCallbacks.tableSoundsourceOffsetsEdit ... ); +%% Optimum offsets frame +artoaGui.editOffsets.frameOptimumOffsets = uipanel( ... + 'Title', 'Offset controls', ... + 'Units', 'normalized', ... + 'BackgroundColor', 'white', ... + 'Position', [left .05 fullwidth/2 .35] ... +); + +%% Setup calculate offsets button +artoaGui.editOffsets.buttonCalculateOffsets = uicontrol( ... + 'Parent', artoaGui.editOffsets.frameOptimumOffsets, ... + 'String', 'Calculate offsets', ... + 'Style', 'PushButton', ... + 'FontSize', 8, ... + 'Units', 'normalized', ... + 'Position', [.65 .1 .25 .2], ... + 'CallBack', pCallbacks.buttonCalculateOffsets ... +); + +artoaGui.editOffsets.checkboxForceOptimumOffsets = uicontrol( ... + 'Parent', artoaGui.editOffsets.frameOptimumOffsets, ... + 'String', 'Force using optimum offsets', ... + 'Style', 'checkbox', ... + 'FontSize', 8, ... + 'Units', 'normalized', ... + 'Position', [left .75 fullwidth/2 .2], ... + 'CallBack', pCallbacks.checkboxForceOptimumOffsets ... +); + end diff --git a/lib/+artoa/+offsets/extractOffsetsDriftsFromSolved.m b/lib/+artoa/+offsets/extractOffsetsDriftsFromSolved.m new file mode 100644 index 0000000..0bd4aea --- /dev/null +++ b/lib/+artoa/+offsets/extractOffsetsDriftsFromSolved.m @@ -0,0 +1,20 @@ +function [offsets, drifts] = extractOffsetsDriftsFromSolved(pX) +%EXTRACTOFFSETSDRIFTSFROMSOLVED Summary of this function goes here +% Detailed explanation goes here + +soundsourceCount = (length(pX) - 4) / 2; + +offsets = NaN(soundsourceCount + 1, 1); +drifts = NaN(soundsourceCount + 1, 1); + +offsets(1) = pX(end - 1); +drifts(1) = pX(end); + +for i = 2:soundsourceCount + 1 + startIndex = (i - 1) * 2 + 3; + offsets(i) = pX(startIndex); + drifts(i) = pX(startIndex + 1); +end + +end + diff --git a/lib/+artoa/+offsets/solve.m b/lib/+artoa/+offsets/solve.m new file mode 100644 index 0000000..98f6f45 --- /dev/null +++ b/lib/+artoa/+offsets/solve.m @@ -0,0 +1,106 @@ +function [a, b, x] = solve(pRfb, pSoundsources, pSatData, pAppliedTemperature, pAppliedPressure, pSoundspeedMethod, pLeapsecondsMatrix) +%UNTITLED Summary of this function goes here +% Detailed explanation goes here + +%% Initialize required variables +%pSoundsources = artoa.controller.getSoundsourcesWithAppliedToa(); +satPositions = [pSatData.lat_sat, pSatData.lon_sat]; +satDates = artoa.convert.dmy2rd(pSatData.day_sat, pSatData.month_sat, pSatData.year_sat); +toaDates = []; +satToas = []; +satDistances = []; +floatDetails = pRfb.FLOAT; + +%% Create table +results = struct(); + +%% Calculate SAT TOAs for every soundsource +fnames = fieldnames(pSoundsources); +for i = 1:length(fnames) + results.(fnames{i}) = table(); + [date, toa] = artoa.toa.predictFromGps( ... + pRfb, ... + pSoundsources.(fnames{i}), ... + struct( ... + 'temperature', pAppliedTemperature, ... + 'pressure', pAppliedPressure, ... + 'method', pSoundspeedMethod, ... + 'soundSource', NaN ... + ), ... + pLeapsecondsMatrix ... + ); + results.(fnames{i}).satDate = date; + results.(fnames{i}).satToa = toa; + results.(fnames{i}).daysSinceStart = ... + date ... + - artoa.convert.dmy2rd( ... + pSoundsources.(fnames{i}).begemis(3), ... + pSoundsources.(fnames{i}).begemis(2), ... + pSoundsources.(fnames{i}).begemis(1) ... + ); + tmpDistances = []; + % calculate distances + for oDistance = 1:length(toa) + if isnan(toa(oDistance)) | any(isnan(satPositions(oDistance, :))) + tmpDistances = [tmpDistances; NaN]; + continue; + end + tmpDistances = [ ... + tmpDistances; ... + artoa.data.calculateGeodist( ... + satPositions(oDistance, :), ... + pSoundsources.(fnames{i}).position ... + ) ... + ]; + end + results.(fnames{i}).satDistances = tmpDistances; + clear tmpDistances; +end + +%% Construct matrices A and B + +rowCount = length(satDates) * length(fnames); + +aCore = zeros(rowCount, 2 * length(fnames)); +b = zeros(rowCount, 1); +distances = zeros(rowCount, 1); +daysSinceFloatStart = NaN(rowCount, 1); +soundVelocity = NaN(rowCount, 1); +soundVelocity(:) = artoa.data.calculateSoundVelocity( ... + pAppliedTemperature, ... + pAppliedPressure, ... + pSoundspeedMethod ... +); + +for i = 1:length(fnames) + rowIndices = ((i - 1) * length(satDates) + 1):i * length(satDates); + startColIndex = (2 * (i - 1)) + 1; + distances(rowIndices, 1) = results.(fnames{i}).satDistances; + aCore(rowIndices, startColIndex) = 1; + aCore(rowIndices, startColIndex + 1) = results.(fnames{i}).daysSinceStart; + b(rowIndices, 1) = results.(fnames{i}).satToa; + daysSinceFloatStart(rowIndices, 1) = ... + results.(fnames{i}).satDate ... + - artoa.convert.dmy2rd( ... + floatDetails.launchtime(3), ... + floatDetails.launchtime(2), ... + floatDetails.launchtime(1) ... + ); +end + +a = [ ... + soundVelocity, distances, aCore, ones(size(daysSinceFloatStart)), daysSinceFloatStart ... +]; + +% remove all NaN from matrix +indicesToUse = all(~isnan(a), 2) & all(aCore >= 0, 2); + +%x = a(indicesToUse, :) * flipud(b(indicesToUse, :)); + +x = pinv(a(indicesToUse, :)) * b(indicesToUse, :); + + + + +end + -- GitLab