diff --git a/lib/+artoa/+controller/+edit/+offsets/buttonCalculateOffsets.m b/lib/+artoa/+controller/+edit/+offsets/buttonCalculateOffsets.m new file mode 100644 index 0000000000000000000000000000000000000000..554e3ae6faee1e5b640b9256259ce820334909a4 --- /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 0000000000000000000000000000000000000000..49efe4569be623a8242aa371795bc7ba56211966 --- /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 bad746d33015e189d93861a6c561903e5da1b223..7d846394495ba90a591e8d531c5a73df45bd1f26 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 d37ddd11595b966c8f2bb0f8d0a6332effa6fd23..bb5bc2a4686bef83f37438df66e6830fe6e1c078 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 91d1319f46a096df0f28f7bd6543b53c0bcd398f..32df12f30f30f9252dd41704eb27637664066bff 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 4f5079526a7dcb3543d3cc555c9fd673d1d814a9..96c6a0ddbac9822699fdb1e6cdbf353f6cc4b11b 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 bebec33eb046b2bea1637e8c3bddd8b65b872162..8198af2b72dee8a3e04e134b63fe61340718e506 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 0000000000000000000000000000000000000000..0bd4aea47184ae137e20e8ffc638a7fdd669ea3d --- /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 0000000000000000000000000000000000000000..98f6f45f5eb30558cf0d83368898e85df7891aa1 --- /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 +