Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update software to v2.5.1 #20

Merged
merged 21 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ebf0bd1
Fix issues with events and cropping
mfacchinelli Feb 1, 2024
c6d80e0
Replace last element of file with `missing` to improve plot where dat…
mfacchinelli Feb 1, 2024
abdffbb
Update tests for science and HK for cropping
mfacchinelli Feb 1, 2024
fa0753e
Fix app issues
mfacchinelli Feb 1, 2024
617dde8
Science object with no data is considered empty in `mag.Instrument`
mfacchinelli Feb 1, 2024
4e83f45
Reintroduce filtering for charts
mfacchinelli Feb 1, 2024
e9d7ecb
Fix issue with stem chart not showing marker
mfacchinelli Feb 1, 2024
61027c0
Add tests for `mag.graphics.mixin.mustBeColor` and fix typo in existi…
mfacchinelli Feb 1, 2024
37673d5
Rename `cropDataBasedOnScience` to `cropToMatch`
mfacchinelli Feb 1, 2024
8a4f1e1
Add methods to replace data in science and in instrument during warm-up
mfacchinelli Feb 1, 2024
893e33e
Add tests for `replace` method
mfacchinelli Feb 1, 2024
52d73d5
Add tests to cover all dependent properties of HK types
mfacchinelli Feb 1, 2024
0fbb5fa
Add test for `mag.Science` magnitude
mfacchinelli Feb 1, 2024
4d20a75
Add tests for derivatives, displays, and fix magnitude test
mfacchinelli Feb 2, 2024
0dee10a
Add tests for `mag.PSD` and `computePSD`
mfacchinelli Feb 2, 2024
97c0a61
Add more tests for HK display
mfacchinelli Feb 2, 2024
a566846
Remove unnecessary `Sealed` attributes on classes
mfacchinelli Feb 2, 2024
6cd2bb8
Add tests for `mag.Instrument`
mfacchinelli Feb 2, 2024
08dbb0b
Fix typo
mfacchinelli Feb 5, 2024
c26dac1
Separate calculation of spectrogram in separate function
mfacchinelli Feb 5, 2024
40f8877
Add save/load utility class
mfacchinelli Feb 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/matlab.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
if: github.ref == 'refs/heads/main'
needs: test
env:
VERSION: "2.5.0"
VERSION: "2.5.1"
steps:
- name: Check out repository
uses: actions/checkout@v3
Expand Down
Binary file modified app/DataVisualization.mlapp
Binary file not shown.
36 changes: 18 additions & 18 deletions resources/ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# App

- Fix issue with default patterns on some computers
- Fix issue when event, meta data and HK patterns are empty
- Fix issue with closing invalid figures

# Software

- Add separate classes for each specific HK type
- Add reference frame to science meta data
- Make `crop` a method of `mag.TimeSeries`
- Make magnitude and derivatives dependent properties of `mag.Science`
- Customize `get` method of `mag.Data` to accept multiple property names
- Allow charts to have `mag.Data` as input
- Rename `mag.AutomatedAnalysis` to `mag.IMAPTestingAnalysis`
- Rename `mag.Result` to `mag.PSD`
- Remove support for `Filters` in charts
- Remove implicit conversion methods for `table`, `timetable` and `tabular`
- Fix issues with setting colors in charts
- Add tests for `mag.Data`, `mag.Science` and `mag.HK`
- Add tests for `mag.graphics.chart.Area`, `mag.graphics.chart.Scatter`, `mag.graphics.chart.Stairs` and `mag.graphics.chart.Stem` plots

# GitHub Workflows

- Update dependencies to latest versions
- Add method to `mag.Instrument` to fill warm-up with `missing` data
- Add method to `mag.Science` to replace periods with a filler variable
- Add save/load utility class
- Separate calculation of spectrogram in separate function
- Replace last element of file with `missing` to improve plot where data is missing
- Science object with no data is considered empty in `mag.Instrument`
- Reintroduce filtering for charts
- Rename `cropDataBasedOnScience` to `cropToMatch`
- Make sure derivative is empty when data is empty
- Remove unnecessary `Sealed` attributes on classes
- Fix issue with consecutive events of the same type missing a completion message
- Fix issue when cropping data and no timestamps are selected
- Fix issue with `mag.graphics.chart.Stem` not showing markers
- Add tests for `mag.Instrument`, `mag.PSD` and `mag.Science/computePSD`
- Add tests for `mag.graphics.mixin.mustBeColor`
- Fix typo in `MarkerSupportTestCase`
23 changes: 8 additions & 15 deletions src/analyze/+mag/@IMAPTestingAnalysis/IMAPTestingAnalysis.m
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
classdef (Sealed) IMAPTestingAnalysis < matlab.mixin.Copyable & mag.mixin.SetGet
classdef (Sealed) IMAPTestingAnalysis < matlab.mixin.Copyable & mag.mixin.SetGet & mag.mixin.SaveLoad
% IMAPTESTINGANALYSIS Automate analysis of an IMAP CPT or SFT folder.

properties (Constant, Hidden)
% VERSION Version number.
Version (1, 1) string = mag.version()
properties (Constant)
Version = mag.version()
end

properties
Expand Down Expand Up @@ -288,7 +287,7 @@ function load(this)
rampMode.Secondary = this.SecondaryRamp;

if rampMode.HasScience
rampMode.cropDataBasedOnScience();
rampMode.cropToMatch();
else
rampMode = mag.Instrument.empty();
end
Expand Down Expand Up @@ -400,16 +399,10 @@ function load(this)
return;
end

result.Primary.Data = result.Primary.Data(primaryPeriod, :);
result.Primary.Data.Properties.Events = result.Primary.Data.Properties.Events(primaryPeriod, :);

result.Secondary.Data = result.Secondary.Data(secondaryPeriod, :);
result.Secondary.Data.Properties.Events = result.Secondary.Data.Properties.Events(secondaryPeriod, :);
result.crop(primaryPeriod, secondaryPeriod);

if isempty(result.Primary.Data) || isempty(result.Secondary.Data)
result = mag.Instrument.empty();
else
result.cropDataBasedOnScience();
end
end
end
Expand All @@ -424,7 +417,7 @@ function load(this)
elseif isa(object, "struct")

% Recreate object based on version.
if isfield(object, "Outboard")
if isequal(object.OriginalVersion, 1.0)

% Convert object to version 2.0 and recursively
% dispatch it.
Expand All @@ -447,9 +440,9 @@ function load(this)
end

loadedObject = mag.IMAPTestingAnalysis.loadobj(loadedObject);
elseif isfield(object, "PrimaryRamp")
elseif isequal(object.OriginalVersion, 2.0)

% Convert object directly to version 3.0.
% Convert object directly to version 2.5.
loadedObject = mag.IMAPTestingAnalysis();
loadedObject.Results = mag.Instrument();

Expand Down
11 changes: 11 additions & 0 deletions src/analyze/+mag/@IMAPTestingAnalysis/loadEventsData.m
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,16 @@ function loadEventsData(this)
ae = acknowledgeEvents([acknowledgeEvents.timestamp] >= e.CommandTimestamp);
ce = completedEvents([completedEvents.timestamp] >= e.CommandTimestamp);

% Remove responses to subsequent commands of the same type.
similarSubsequentEvents = events(([events.Type] == e.Type) & ([events.SubType] == e.SubType) & ([events.CommandTimestamp] > e.CommandTimestamp));

if ~isempty(similarSubsequentEvents)

ae = ae([ae.timestamp] < similarSubsequentEvents(1).CommandTimestamp);
ce = ce([ce.timestamp] < similarSubsequentEvents(1).CommandTimestamp);
end

% Find acknoledgement time.
if isfield(ae, "type") && isfield(ae, "subtype")

ae = ae((str2double([ae.type]) == e.Type) & (str2double([ae.subtype]) == e.SubType));
Expand All @@ -164,6 +174,7 @@ function loadEventsData(this)
end
end

% Find completion time.
if isfield(ce, "type") && isfield(ce, "subtype")

ce = ce((str2double([ce.type]) == e.Type) & (str2double([ce.subtype]) == e.SubType));
Expand Down
5 changes: 5 additions & 0 deletions src/analyze/+mag/@IMAPTestingAnalysis/loadScienceData.m
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ function loadScienceData(this, primaryMetaData, secondaryMetaData)
secondary = ps.apply(secondary, smd);
end

% Remove last data point, to avoid continuous lines when data is
% missing.
primary{end, ["x", "y", "z"]} = missing();
secondary{end, ["x", "y", "z"]} = missing();

%% Convert to timetable

primaryData = vertcat(primaryData, table2timetable(primary, RowTimes = "t")); %#ok<AGROW>
Expand Down
18 changes: 9 additions & 9 deletions src/data/+mag/+event/Event.m
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@

methods (Hidden, Sealed)

function tableThis = timetable(this)
function timetableThis = timetable(this)
% TIMETABLE Convert events to timetable.

emptyTime = datetime.empty();
emptyTime.TimeZone = "UTC";

tableThis = struct2table(struct(Time = emptyTime, ...
timetableThis = struct2table(struct(Time = emptyTime, ...
Mode = double.empty(0, 1), ...
PrimaryRate = double.empty(0, 1), ...
SecondaryRate = double.empty(0, 1), ...
Expand All @@ -51,21 +51,21 @@
Range = double.empty(0, 1), ...
Sensor = string.empty(0, 1), ...
Label = string.empty(0, 1)));
tableThis = table2timetable(tableThis, RowTimes = "Time");
timetableThis = table2timetable(timetableThis, RowTimes = "Time");

for t = 1:numel(this)

tt = this(t).convertToTimeTable();
tableThis = outerjoin(tableThis, tt, MergeKeys = true, Keys = ["Time", intersect(tableThis.Properties.VariableNames, tt.Properties.VariableNames)]);
timetableThis = outerjoin(timetableThis, tt, MergeKeys = true, Keys = ["Time", intersect(timetableThis.Properties.VariableNames, tt.Properties.VariableNames)]);
end

tableThis = sortrows(tableThis);
timetableThis = sortrows(timetableThis);

fillVariables = intersect(["Mode", "PrimaryRate", "SecondaryRate", "PacketFrequency", "Range"], tableThis.Properties.VariableNames);
tableThis(:, fillVariables) = fillmissing(tableThis(:, fillVariables), "previous");
fillVariables = intersect(["Mode", "PrimaryRate", "SecondaryRate", "PacketFrequency", "Range"], timetableThis.Properties.VariableNames);
timetableThis(:, fillVariables) = fillmissing(timetableThis(:, fillVariables), "previous");

tableThis{contains(tableThis.Label, "Config"), ["PrimaryRate", "SecondaryRate", "PacketFrequency", "Duration"]} = missing();
tableThis{contains(tableThis.Label, "Ramp"), "Range"} = missing();
timetableThis{contains(timetableThis.Label, "Config"), ["PrimaryRate", "SecondaryRate", "PacketFrequency", "Duration"]} = missing();
timetableThis{contains(timetableThis.Label, "Ramp"), "Range"} = missing();
end
end

Expand Down
2 changes: 1 addition & 1 deletion src/data/+mag/+hk/Power.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
classdef (Sealed) Power < mag.HK
classdef Power < mag.HK
% POWER Class containing MAG power HK packet data.

properties (Dependent)
Expand Down
2 changes: 1 addition & 1 deletion src/data/+mag/+hk/Processor.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
classdef (Sealed) Processor < mag.HK
classdef Processor < mag.HK
% PROCESSOR Class containing MAG processor HK packet data.

properties (Dependent)
Expand Down
2 changes: 1 addition & 1 deletion src/data/+mag/+hk/SID15.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
classdef (Sealed) SID15 < mag.HK
classdef SID15 < mag.HK
% SID15 Class containing MAG SID15 HK packet data.

properties (Dependent)
Expand Down
2 changes: 1 addition & 1 deletion src/data/+mag/+hk/Status.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
classdef (Sealed) Status < mag.HK
classdef Status < mag.HK
% STATUS Class containing MAG status HK packet data.

properties (Dependent)
Expand Down
7 changes: 6 additions & 1 deletion src/data/+mag/HK.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ function crop(this, timeFilter)
for i = 1:numel(this)

this(i).Data = this(i).Data(timeFilter, :);
this(i).MetaData.Timestamp = this(i).Time(1);

if isempty(this(i).Time)
this(i).MetaData.Timestamp = NaT(TimeZone = mag.process.DateTime.TimeZone);
else
this(i).MetaData.Timestamp = this(i).Time(1);
end
end
end

Expand Down
38 changes: 29 additions & 9 deletions src/data/+mag/Instrument.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@
end

function value = get.HasScience(this)
value = ~isempty(this.Primary) && ~isempty(this.Secondary);

value = ~isempty(this.Primary) && ~isempty(this.Secondary) && ...
~isempty(this.Primary.Data) && ~isempty(this.Secondary.Data);
end

function value = get.HasHK(this)
Expand Down Expand Up @@ -90,6 +92,20 @@
sensor = supportedSensors(locSelected);
end

function fillWarmUp(this, timePeriod, filler)
% FILLWARMUP Replace beginning of science mode with filler
% variable.

arguments
this (1, 1) mag.Instrument
timePeriod (1, 1) duration = minutes(1)
filler (1, 1) double = missing()
end

this.Primary.replace(timePeriod, filler);
this.Secondary.replace(timePeriod, filler);
end

function crop(this, primaryFilter, secondaryFilter)
% CROP Crop data based on selected filters for primary and
% secondary science.
Expand All @@ -101,7 +117,7 @@ function crop(this, primaryFilter, secondaryFilter)
end

this.cropScience(primaryFilter, secondaryFilter);
this.cropDataBasedOnScience();
this.cropToMatch();
end

function cropScience(this, primaryFilter, secondaryFilter)
Expand All @@ -118,22 +134,26 @@ function cropScience(this, primaryFilter, secondaryFilter)
this.Secondary.crop(secondaryFilter);
end

function cropDataBasedOnScience(this)
% CROPDATABASEDONSCIENCE Crop meta data, events and HK based on
% science timestamps.
function cropToMatch(this, startTime, endTime)
% CROPTOMATCH Crop meta data, events and HK based on science
% timestamps or specified timestamps.

timeRange = this.TimeRange;
arguments
this (1, 1) mag.Instrument
startTime (1, 1) datetime = this.TimeRange(1)
endTime (1, 1) datetime = this.TimeRange(2)
end

% Filter events.
if ~isempty(this.Events)
this.Events = this.Events(isbetween([this.Events.CommandTimestamp], timeRange(1), timeRange(2), "closed"));
this.Events = this.Events(isbetween([this.Events.CommandTimestamp], startTime, endTime, "closed"));
end

% Filter HK.
this.HK.crop(timerange(timeRange(1), timeRange(2), "closed"));
this.HK.crop(timerange(startTime, endTime, "closed"));

% Adjust meta data.
this.MetaData.Timestamp = timeRange(1);
this.MetaData.Timestamp = startTime;
end

function resample(this, targetFrequency)
Expand Down
39 changes: 31 additions & 8 deletions src/data/+mag/Science.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
classdef (Sealed) Science < mag.TimeSeries & matlab.mixin.CustomDisplay
classdef Science < mag.TimeSeries & matlab.mixin.CustomDisplay
% SCIENCE Class containing MAG science data.

properties (Dependent)
Expand Down Expand Up @@ -60,15 +60,15 @@
end

function dx = get.dX(this)
dx = [diff(this.X); missing()];
dx = this.computeDerivative(this.X);
end

function dy = get.dY(this)
dy = [diff(this.Y); missing()];
dy = this.computeDerivative(this.Y);
end

function dz = get.dZ(this)
dz = [diff(this.Z); missing()];
dz = this.computeDerivative(this.Z);
end

function range = get.Range(this)
Expand Down Expand Up @@ -102,7 +102,11 @@ function crop(this, timeFilter)
this.Data.Properties.Events = this.Data.Properties.Events(timePeriod, :);
end

this.MetaData.Timestamp = this.Time(1);
if isempty(this.Time)
this.MetaData.Timestamp = NaT(TimeZone = mag.process.DateTime.TimeZone);
else
this.MetaData.Timestamp = this.Time(1);
end
end

function resample(this, targetFrequency)
Expand Down Expand Up @@ -194,6 +198,25 @@ function filter(this, numeratorOrFilter, denominator)
this.Data{1:numCoefficients, ["x", "y", "z"]} = missing();
end

function replace(this, timeFilter, filler)
% REPLACE Replace length of data specified by time filter with
% filler variable.

arguments
this (1, 1) mag.Science
timeFilter (1, 1) {mustBeA(timeFilter, ["duration", "timerange", "withtol"])}
filler (1, 1) double = missing()
end

if isa(timeFilter, "duration")
timePeriod = timerange(this.Time(1), this.Time(1) + timeFilter, "closed");
elseif isa(timeFilter, "timerange") || isa(timeFilter, "withtol")
timePeriod = timeFilter;
end

this.Data{timePeriod, ["x", "y", "z"]} = filler;
end

function data = computePSD(this, options)
% COMPUTEPSD Compute the power spectral density of the magnetic
% field measurements.
Expand All @@ -213,11 +236,11 @@ function filter(this, numeratorOrFilter, denominator)
% Filter out data.
if isempty(options.Start)

t = this.Data.t;
t = this.Time;
locFilter = true(size(this.Data, 1), 1);
else

t = (this.Data.t - options.Start);
t = (this.Time - options.Start);

locFilter = t > 0;

Expand All @@ -229,7 +252,7 @@ function filter(this, numeratorOrFilter, denominator)
% Compute PSD.
dt = seconds(median(diff(t(locFilter))));

[psd, f] = psdtsh(this.Data{locFilter, ["x", "y", "z"]}, dt, options.FFTType, options.NW);
[psd, f] = psdtsh(this.XYZ(locFilter, :), dt, options.FFTType, options.NW);
psd = psd .^ 0.5;

data = mag.PSD(table(f, psd(:, 1), psd(:, 2), psd(:, 3), VariableNames = ["f", "x", "y", "z"]));
Expand Down
Loading
Loading