diff --git a/AutomaticQC/CTDSurfaceSoakQC.m b/AutomaticQC/CTDSurfaceSoakQC.m index 8edfd2287..dfc357688 100644 --- a/AutomaticQC/CTDSurfaceSoakQC.m +++ b/AutomaticQC/CTDSurfaceSoakQC.m @@ -1,4 +1,4 @@ -function [data flags paramsLog] = CTDSurfaceSoakQC( sample_data, data, k, type, auto ) +function [data, flags, paramsLog] = CTDSurfaceSoakQC( sample_data, data, k, type, auto ) %IMOSINOUTWATERQC Flags samples which were taken before and after the instrument was placed % in the water. % @@ -92,7 +92,7 @@ % only concerned here with pumped sensors or variables derived from pumped % observations -pumpedVar = {'TEMP', 'CNDC', 'DOX1', 'DOX2', 'PSAL', 'DENS'}; +pumpedVar = {'TEMP', 'CNDC', 'DOX', 'DOXY', 'DOX1', 'DOX2', 'DOXS', 'PSAL', 'DENS'}; ignoreVar = {'TIME', 'PROFILE', 'DIRECTION', 'LATITUDE', 'LONGITUDE', 'BOT_DEPTH', 'ETIME'}; qcSet = str2double(readProperty('toolbox.qc_set')); @@ -107,16 +107,16 @@ flags = ones(lenData, 1, 'int8')*rawFlag; if any(iPumped) % initially all data is raw - switch find(iPumped); - case 1 % temperature + switch pumpedVar{iPumped}; + case 'TEMP' % temperature if ~isempty(iTSS) flags = sample_data.variables{iTSS}.data; end - case {2, 5, 6} % dependent on conductivity + case {'CNDC', 'PSAL', 'DENS'} % dependent on conductivity if ~isempty(iCSS) flags = sample_data.variables{iCSS}.data; end - case {3, 4} % dependent on oxygen + case {'DOX', 'DOXY', 'DOX1', 'DOX2', 'DOXS'} % dependent on oxygen if ~isempty(iOSS) flags = sample_data.variables{iOSS}.data; end diff --git a/AutomaticQC/imosGlobalRangeQC.txt b/AutomaticQC/imosGlobalRangeQC.txt index 43540fdf8..3bf9f501c 100644 --- a/AutomaticQC/imosGlobalRangeQC.txt +++ b/AutomaticQC/imosGlobalRangeQC.txt @@ -6,7 +6,9 @@ DEPTH CPHL CHLU CHLF +DOX DOXY +DOXS DOX1 DOX2 UCUR diff --git a/AutomaticQC/imosRateOfChangeQC.txt b/AutomaticQC/imosRateOfChangeQC.txt index 891bdc46e..0ce40d608 100644 --- a/AutomaticQC/imosRateOfChangeQC.txt +++ b/AutomaticQC/imosRateOfChangeQC.txt @@ -3,7 +3,9 @@ PSAL = 2*stdDev PRES = 2*stdDev PRES_REL = 2*stdDev DEPTH = 2*stdDev +DOX = 2*stdDev DOXY = 2*stdDev +DOXS = 2*stdDev DOX1 = 2*stdDev DOX2 = 2*stdDev UCUR = 2*stdDev diff --git a/AutomaticQC/imosVerticalSpikeQC.txt b/AutomaticQC/imosVerticalSpikeQC.txt index 75e3e6773..c298df60e 100644 --- a/AutomaticQC/imosVerticalSpikeQC.txt +++ b/AutomaticQC/imosVerticalSpikeQC.txt @@ -6,6 +6,8 @@ DEPTH = 3 CPHL = PABIM CHLU = PABIM CHLF = PABIM +DOX = PABIM DOXY = PABIM +DOXS = PABIM DOX1 = PABIM DOX2 = PABIM \ No newline at end of file diff --git a/DDB/executeQuery.m b/DDB/executeQuery.m new file mode 100644 index 000000000..8da4ed93c --- /dev/null +++ b/DDB/executeQuery.m @@ -0,0 +1,64 @@ +function result = executeQuery( table, field, value) +%EXECUTEQUERY Wrapper around Java DDB interface, allowing queries to the DDB +% pass query to DDB database or CSV file Query +% +% Inputs: +% table - The table to query. +% +% field - Name of field on which to restrict query. If passed in as an +% empty matrix, the entire table is returned. +% +% value - Value of field on which to restrict query. +% +% Outputs: +% result - Vector of structs. +% +% See Also executeDDBQuery and executeCSVQuery +% +% +% Author: Peter Jansen +% + +% +% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated +% Marine Observing System (IMOS). +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% * Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in the +% documentation and/or other materials provided with the distribution. +% * Neither the name of the AODN/IMOS nor the names of its contributors +% may be used to endorse or promote products derived from this software +% without specific prior written permission. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. +% + +isDatabaseCSV = false; +ddb = readProperty('toolbox.ddb'); +if isdir(ddb) + isDatabaseCSV = true; +end +if isDatabaseCSV + result = executeCSVQuery(table, field, value); +else + result = executeDDBQuery(table, field, value); +end + +end + diff --git a/FlowManager/autoIMOSToolbox.m b/FlowManager/autoIMOSToolbox.m index be21a29fa..8d72f18ef 100644 --- a/FlowManager/autoIMOSToolbox.m +++ b/FlowManager/autoIMOSToolbox.m @@ -158,19 +158,12 @@ function autoIMOSToolbox(toolboxVersion, fieldTrip, dataDir, ppChain, qcChain, e [~, sourceFolder] = fileparts(dataDir); fprintf('%s\n', ['Processing field trip ' fieldTrip ' from folder ' sourceFolder]); -%check for CSV file import -isCSV = false; -ddb = readProperty('toolbox.ddb'); -if isdir(ddb) - isCSV = true; -end - % get infos from current field trip switch mode case 'profile' - [~, deps, sits, dataDir] = getCTDs(true, isCSV); + [~, deps, sits, dataDir] = getCTDs(true); case 'timeSeries' - [~, deps, sits, dataDir] = getDeployments(true, isCSV); + [~, deps, sits, dataDir] = getDeployments(true); end if isempty(deps) diff --git a/FlowManager/importManager.m b/FlowManager/importManager.m index 9c13746e9..fe1b8e3aa 100644 --- a/FlowManager/importManager.m +++ b/FlowManager/importManager.m @@ -85,7 +85,7 @@ rawFiles = {}; if ~isempty(ddb) || (~isempty(driver) && ~isempty(connection)) - [structs, rawFiles] = ddbImport(auto, iMooring, ddb, mode); + [structs, rawFiles] = ddbImport(auto, iMooring, mode); else if auto, error('manual import cannot be automated without deployment database'); end [structs, rawFiles] = manualImport(mode); @@ -192,7 +192,7 @@ end end -function [sample_data, rawFiles] = ddbImport(auto, iMooring, ddb, mode) +function [sample_data, rawFiles] = ddbImport(auto, iMooring, mode) %DDBIMPORT Imports data sets using metadata retrieved from a deployment % database. % @@ -202,8 +202,6 @@ % iMooring - Optional logical(comes with auto == true). Contains % the logical indices to extract only the deployments % from one mooring set of deployments. -% ddb - deployment database string attribute from -% toolboxProperties.txt % mode - toolbox execution mode. % % Outputs: @@ -216,18 +214,12 @@ rawFiles = {}; allFiles = {}; - %check for CSV file import - isCSV = false; - if isdir(ddb) - isCSV = true; - end - while true switch mode case 'profile' - [fieldTrip, deps, sits, dataDir] = getCTDs(auto, isCSV); % one entry is one CTD profile instrument file + [fieldTrip, deps, sits, dataDir] = getCTDs(auto); % one entry is one CTD profile instrument file case 'timeSeries' - [fieldTrip, deps, sits, dataDir] = getDeployments(auto, isCSV); % one entry is one moored instrument file + [fieldTrip, deps, sits, dataDir] = getDeployments(auto); % one entry is one moored instrument file end if isempty(fieldTrip), return; end @@ -341,7 +333,7 @@ % display status dialog to highlight any discrepancies (file not found % for a deployment, more than one file found for a deployment) if ~auto - [deps, allFiles] = dataFileStatusDialog(deps, allFiles, isCSV); + [deps, allFiles] = dataFileStatusDialog(deps, allFiles); % user cancelled file dialog if isempty(deps), continue; end @@ -379,7 +371,7 @@ waitbar(k / length(deps), progress, fileDisplay); end % import data - sample_data{end+1} = parse(deps(k), allFiles{k}, parsers, noParserPrompt, mode, isCSV); + sample_data{end+1} = parse(deps(k), allFiles{k}, parsers, noParserPrompt, mode); rawFiles{ end+1} = allFiles{k}; if iscell(sample_data{end}) @@ -436,7 +428,7 @@ % close progress dialog if ~auto, close(progress); end - function sam = parse(deployment, files, parsers, noParserPrompt, mode, isCSV) + function sam = parse(deployment, files, parsers, noParserPrompt, mode) %PARSE Parses a raw data file, returns a sample_data struct. % % Inputs: @@ -452,7 +444,7 @@ % sam - Struct containing sample data. % get the appropriate parser function - parser = getParserFunc(deployment, parsers, noParserPrompt, isCSV); + parser = getParserFunc(deployment, parsers, noParserPrompt); if isnumeric(parser) error(['no parser found for instrument ' deployment.InstrumentID]); end @@ -461,7 +453,7 @@ sam = parser(files, mode); end - function parser = getParserFunc(deployment, parsers, noParserPrompt, isCSV) + function parser = getParserFunc(deployment, parsers, noParserPrompt) %GETPARSERFUNC Searches for a parser function which is able to parse data % for the given deployment. % @@ -477,12 +469,7 @@ % function wasn't found. % - if isCSV - executeQueryFunc = @executeCSVQuery; - else - executeQueryFunc = @executeDDBQuery; - end - instrument = executeQueryFunc('Instruments', 'InstrumentID', deployment.InstrumentID); + instrument = executeQuery('Instruments', 'InstrumentID', deployment.InstrumentID); % there should be exactly one instrument if length(instrument) ~= 1 diff --git a/GUI/dataFileStatusDialog.m b/GUI/dataFileStatusDialog.m index c72ed5e85..cc152dcc5 100644 --- a/GUI/dataFileStatusDialog.m +++ b/GUI/dataFileStatusDialog.m @@ -1,4 +1,4 @@ -function [deployments files] = dataFileStatusDialog( deployments, files, isCSV ) +function [deployments files] = dataFileStatusDialog( deployments, files) %DATAFILESTATUSDIALOG Displays a list of deployments, and raw files for % each, allowing the user to verify/change which raw data files map to which % deployment. @@ -16,7 +16,6 @@ % % files - Cell array of cell arrays of strings, each containing the % list of file names corresponding to each deployment. -% isCSV - Logical for whether we use a ddb in .csv format or not. % % Outputs: % deployments - Same as input, potentially with some deployments removed. @@ -56,11 +55,10 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % - narginchk(3,3); + narginchk(2, 2); if ~isstruct(deployments), error('deployments must be a struct'); end if ~iscell (files), error('files must be a cell array'); end - if ~islogical(isCSV), error('isCSV must be a logical'); end % copy the inputs so we can rollback if the user cancels origDeployments = deployments; @@ -69,7 +67,7 @@ % get the toolbox execution mode mode = readProperty('toolbox.mode'); - deploymentDescs = genDepDescriptions(deployments, files, isCSV); + deploymentDescs = genDepDescriptions(deployments, files); % Sort data_samples % @@ -297,7 +295,7 @@ function fileAddCallback(source,ev) %% Description generation - function descs = genDepDescriptions(deployments, files, isCSV) + function descs = genDepDescriptions(deployments, files) %GENDEPDESCRIPTIONS Creates a cell array of descriptions of the given % deployments, suitable for use in the deployments list. % @@ -324,12 +322,7 @@ function fileAddCallback(source,ev) % get some site information if it exists - if isCSV - executeQueryFunc = @executeCSVQuery; - else - executeQueryFunc = @executeDDBQuery; - end - site = executeQueryFunc('Sites', 'Site', dep.Site); + site = executeQuery('Sites', 'Site', dep.Site); if ~isempty(site) diff --git a/GUI/startDialog.m b/GUI/startDialog.m index 640ed517f..3252c6d42 100644 --- a/GUI/startDialog.m +++ b/GUI/startDialog.m @@ -1,4 +1,4 @@ -function [fieldTrip dataDir] = startDialog(mode, isCSV) +function [fieldTrip dataDir] = startDialog(mode) %STARTDIALOG Displays a dialog prompting the user to select a Field Trip % and a directory which contains raw data files. % @@ -11,7 +11,6 @@ % Input: % % mode - String, toolox execution mode. -% isCSV - optional boolean (default = false). True if importing from csv files. % % Outputs: % @@ -53,11 +52,7 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % - narginchk(1,2); - - if nargin == 1 - isCSV = false; - end + narginchk(1, 1); dateFmt = readProperty('toolbox.dateFormat'); @@ -74,12 +69,7 @@ if isnan(highDate), highDate = now_utc; end % retrieve all field trip IDs; they are displayed as a drop down menu - if isCSV - executeQueryFunc = @executeCSVQuery; - else - executeQueryFunc = @executeDDBQuery; - end - fieldTrips = executeQueryFunc('FieldTrip', [], []); + fieldTrips = executeQuery('FieldTrip', [], []); if isempty(fieldTrips), error('No field trip entries in DDB'); end diff --git a/Graph/graphTimeSeries.m b/Graph/graphTimeSeries.m index 54355fe39..f0dabf5b0 100644 --- a/Graph/graphTimeSeries.m +++ b/Graph/graphTimeSeries.m @@ -98,7 +98,19 @@ end iTimeDim = getVar(sample_data.dimensions, 'TIME'); - xLimits = [min(sample_data.dimensions{iTimeDim}.data), max(sample_data.dimensions{iTimeDim}.data)]; + dataTime = sample_data.dimensions{iTimeDim}.data; + + if sample_data.meta.level == 1 + qcSet = str2double(readProperty('toolbox.qc_set')); + goodFlag = imosQCFlag('good', qcSet, 'flag'); + pGoodFlag = imosQCFlag('probablyGood', qcSet, 'flag'); + rawFlag = imosQCFlag('raw', qcSet, 'flag'); + + dimFlags = sample_data.dimensions{iTimeDim}.flags; + iGood = (dimFlags == goodFlag) | (dimFlags == pGoodFlag) | (dimFlags == rawFlag); + dataTime = dataTime(iGood); + end + xLimits = [min(dataTime), max(dataTime)]; xStep = (xLimits(2) - xLimits(1)) / 5; xTicks = xLimits(1):xStep:xLimits(2); xTickLabels = datestr(xTicks, 'dd-mm-yy HH:MM'); @@ -131,17 +143,15 @@ if ischar(varData), varData = str2num(varData); end % we assume data is an array of one single character if sample_data.meta.level == 1 - qcSet = str2double(readProperty('toolbox.qc_set')); - goodFlag = imosQCFlag('good', qcSet, 'flag'); - pGoodFlag = imosQCFlag('probablyGood', qcSet, 'flag'); - rawFlag = imosQCFlag('raw', qcSet, 'flag'); - % set x and y limits so that axis are optimised for good/probably good/raw data only varFlags = sample_data.variables{k}.flags; + dimFlags = sample_data.dimensions{iTimeDim}.flags; if iExtraVar varFlags = [varFlags; extra_sample_data.variables{iExtraVar}.flags]; + dimFlags = [dimFlags; extra_sample_data.dimensions{getVar(extra_sample_data.dimensions, 'TIME')}.flags]; end iGood = (varFlags == goodFlag) | (varFlags == pGoodFlag) | (varFlags == rawFlag); + iGood = iGood & ((dimFlags == goodFlag) | (dimFlags == pGoodFlag) | (dimFlags == rawFlag)); if any(iGood) varData = varData(iGood); if ischar(varData), varData = str2num(varData); end % we assume data is an array of one single character diff --git a/IMOS/imosParameters.txt b/IMOS/imosParameters.txt index df1d8645c..f54b7d9fe 100644 --- a/IMOS/imosParameters.txt +++ b/IMOS/imosParameters.txt @@ -53,10 +53,11 @@ DIR_MAG, 0, from_direction, DIRECTION, 0, direction_of_the_profile, , , , , , , , char DIRT, 0, to_direction, degree, clockwise, true north, E, 999999.0, 0.0, 360.0, float DIST_ALONG_BEAMS, 0, distance_from_sensor_along_beams, m, , sensor, Z, 999999.0, -12000.0, 12000.0, float -DOX1, 1, mole_concentration_of_dissolved_molecular_oxygen_in_sea_water, umol l-1, , , O, 999999.0, 0.0, 900000.0, float -DOX2, 1, moles_of_oxygen_per_unit_mass_in_sea_water, umol kg-1, , , O, 999999.0, 0.0, 880000.0, float +DOX, 0, volume_concentration_of_dissolved_molecular_oxygen_in_sea_water, ml l-1, , , O, 999999.0, 0.0, 200.0, float +DOX1, 1, mole_concentration_of_dissolved_molecular_oxygen_in_sea_water, umol l-1, , , O, 999999.0, 0.0, 1000.0, float +DOX2, 1, moles_of_oxygen_per_unit_mass_in_sea_water, umol kg-1, , , O, 999999.0, 0.0, 1000.0, float DOXS, 1, fractional_saturation_of_oxygen_in_sea_water, percent, , , O, 999999.0, , , float -DOXY, 1, mass_concentration_of_oxygen_in_sea_water, kg m-3, , , O, 999999.0, 0.0, 29.0, float +DOXY, 1, mass_concentration_of_oxygen_in_sea_water, mg l-1, , , O, 999999.0, 0.0, 29.0, float DOXY_TEMP, 1, temperature_of_sensor_for_oxygen_in_sea_water, degrees_Celsius,, , T, 999999.0, 0.0, 50.0, float DRYT, 0, dry_bulb_temperature, degrees_Celsius,, , M, 999999.0, , , float DYNHT, 0, dynamic_height, m, , , E, 999999.0, , , float @@ -124,7 +125,7 @@ SSWVT, 0, sea_surface_wave_to_directional_variance_spectral_densit Sv, 0, mean_volume_backscatter_coefficient, m-1, , , A, 9999.0, 0.0, 1.0, float SV_mean, 0, mean_volume_backscatter, decibel, , , A, 999999.0, -128.0, 0.0, float Sv_kurt, 0, kurtosis_volume_backscatter, m-1, , , A, 9999.0, 0.0, 1.0, float -Sv_pcnt_good, 0, percent_Sv_samples_included, percent, , , A, 9999.0, 0.0, 1.0, float +Sv_pcnt_good, 0, percent_Sv_samples_included, percent, , , A, 9999.0, 0.0, 100.0, float Sv_sd, 0, standard_deviation_volume_backscatter, m-1, , , A, 9999.0, 0.0, 1.0, float Sv_skew, 0, skewness_volume_backscatter, m-1, , , A, 9999.0, 0.0, 1.0, float Sv_unfilt, 0, mean_volume_backscatter_including_bad_data, m-1, , , A, 9999.0, 0.0, 1.0, float @@ -148,7 +149,6 @@ UCUR, 1, eastward_sea_water_velocity, UCUR_MAG, 1, eastward_sea_water_velocity, m s-1, , magnetic north, V, 999999.0, -10.0, 10.0, float UWND, 1, eastward_wind, m s-1, , , M, 999999.0, , , float VAVH, 1, sea_surface_wave_significant_height, m, , , W, 999999.0, 0.0, 100.0, float -VAVT, 1, sea_surface_wave_zero_upcrossing_period, Second, , , W, 999999.0, 0.0, 100.0, float VBSC, 0, volumetric_backscatter_coefficient, m-1 sr-1, , , E, 999999.0, , , float VCUR, 1, northward_sea_water_velocity, m s-1, , true north, V, 999999.0, -10.0, 10.0, float VCUR_MAG, 1, northward_sea_water_velocity, m s-1, , magnetic north, V, 999999.0, -10.0, 10.0, float diff --git a/Java/ddb.jar b/Java/ddb.jar index 435f27339..0265c8faf 100644 Binary files a/Java/ddb.jar and b/Java/ddb.jar differ diff --git a/Java/src/org/imos/ddb/JDBCDDB.java b/Java/src/org/imos/ddb/JDBCDDB.java index f6dc18ec9..1d83acddc 100644 --- a/Java/src/org/imos/ddb/JDBCDDB.java +++ b/Java/src/org/imos/ddb/JDBCDDB.java @@ -171,7 +171,7 @@ public ArrayList executeQuery( ArrayList instance = new ArrayList(); results.add(instance); - for (int i = 1; i < columnsNumber; i++) { + for (int i = 1; i <= columnsNumber; i++) { DBObject db = new DBObject(); diff --git a/NetCDF/makeNetCDFCompliant.m b/NetCDF/makeNetCDFCompliant.m index a662670e5..147a48045 100644 --- a/NetCDF/makeNetCDFCompliant.m +++ b/NetCDF/makeNetCDFCompliant.m @@ -142,13 +142,6 @@ sample_data.dimensions{k} = mergeAtts(sample_data.dimensions{k}, dimAtts); end - %check for CSV file import - isCSV = false; - ddb = readProperty('toolbox.ddb'); - if isdir(ddb) - isCSV = true; - end - % % variables % @@ -173,11 +166,11 @@ if isfield(sample_data.meta, 'deployment') iTime = getVar(sample_data.dimensions, 'TIME'); sample_data.variables{k}.sensor_serial_number = ... - getSensorSerialNumber(sample_data.variables{k}.name, sample_data.meta.deployment.InstrumentID, sample_data.dimensions{iTime}.data(1), isCSV); + getSensorSerialNumber(sample_data.variables{k}.name, sample_data.meta.deployment.InstrumentID, sample_data.dimensions{iTime}.data(1)); elseif isfield(sample_data.meta, 'profile') iTime = getVar(sample_data.variables, 'TIME'); sample_data.variables{k}.sensor_serial_number = ... - getSensorSerialNumber(sample_data.variables{k}.name, sample_data.meta.profile.InstrumentID, sample_data.variables{iTime}.data(1), isCSV); + getSensorSerialNumber(sample_data.variables{k}.name, sample_data.meta.profile.InstrumentID, sample_data.variables{iTime}.data(1)); end end end @@ -200,7 +193,7 @@ end end -function target = getSensorSerialNumber ( IMOSParam, InstrumentID, timeFirstSample, isCSV ) +function target = getSensorSerialNumber ( IMOSParam, InstrumentID, timeFirstSample ) %GETSENSORSERIALNUMBER gets the sensor serial number associated to an IMOS %paramter for a given deployment ID % @@ -208,12 +201,7 @@ target = ''; % query the ddb for all sensor config related to this instrument ID -if isCSV - executeQueryFunc = @executeCSVQuery; -else - executeQueryFunc = @executeDDBQuery; -end -InstrumentSensorConfig = executeQueryFunc('InstrumentSensorConfig', 'InstrumentID', InstrumentID); +InstrumentSensorConfig = executeQuery('InstrumentSensorConfig', 'InstrumentID', InstrumentID); lenConfig = length(InstrumentSensorConfig); % only consider relevant config based on timeFirstSample for i=1:lenConfig @@ -226,15 +214,17 @@ end if firstTest && secondTest % query the ddb for each sensor - Sensors = executeQueryFunc('Sensors', 'SensorID', InstrumentSensorConfig(i).SensorID); + Sensors = executeQuery('Sensors', 'SensorID', InstrumentSensorConfig(i).SensorID); if ~isempty(Sensors) % check if this sensor is associated to the current IMOS parameter if isfield(Sensors, 'Parameter') - parameters = textscan(Sensors.Parameter, '%s', 'Delimiter', ','); - if ~isempty(parameters) - parameters = parameters{1}; - if any(strcmpi(IMOSParam, parameters)) - target = Sensors.SerialNumber; + if ~isempty(Sensors.Parameter) + parameters = textscan(Sensors.Parameter, '%s', 'Delimiter', ','); + if ~isempty(parameters) + parameters = parameters{1}; + if any(strcmpi(IMOSParam, parameters)) + target = Sensors.SerialNumber; + end end end end diff --git a/Parser/SBE19Parse.m b/Parser/SBE19Parse.m index 249d14f78..6f3c8d907 100644 --- a/Parser/SBE19Parse.m +++ b/Parser/SBE19Parse.m @@ -412,7 +412,7 @@ headerExpr = '^\*\s*(SBE \S+|SeacatPlus)\s+V\s+(\S+)\s+SERIAL NO.\s+(\d+)'; %BDM (18/2/2011) - new header expressions to reflect newer SBE header info headerExpr2 = ''; -headerExpr3 = 'Sea-Bird (\S+) Data File:'; +headerExpr3 = 'Sea-Bird (.*?) *?Data File\:'; scanExpr = 'number of scans to average = (\d+)'; scanExpr2 = '*\s+ (\d+)'; memExpr = 'samples = (\d+), free = (\d+), casts = (\d+)'; @@ -437,10 +437,10 @@ voltCalExpr = 'volt (\d): offset = (\S+), slope = (\S+)'; otherExpr = '^\*\s*([^\s=]+)\s*=\s*([^\s=]+)\s*$'; firmExpr = '(\S+)'; -firmExpr2 = '^\*\s*FirmwareVersion:\s*(\S+)'; %SBE39plus +firmExpr2 = '^\*\s*FirmwareVersion:\s*(\S+)'; %SBE39plus sensorId = ''; sensorType = '<[tT]ype>(.*\S+.*)'; -serialExpr = '^\*\s*SerialNumber:\s*(\S+)'; %SBE39plus +serialExpr = '^\*\s*SerialNumber:\s*(\S+)'; %SBE39plus exprs = {... headerExpr headerExpr2 headerExpr3 scanExpr ... @@ -466,18 +466,22 @@ % header case 1 - header.instrument_model = tkns{1}{1}; + if ~isfield(header, 'instrument_model') + header.instrument_model = tkns{1}{1}; + end header.instrument_firmware = tkns{1}{2}; header.instrument_serial_no = tkns{1}{3}; % header2 case 2 - header.instrument_model = tkns{1}{1}; + if ~isfield(header, 'instrument_model') + header.instrument_model = tkns{1}{1}; + end header.instrument_serial_no = tkns{1}{2}; % header3 case 3 - header.instrument_model = tkns{1}{1}; + header.instrument_model = strrep(tkns{1}{1}, ' ', ''); % scan case 4 diff --git a/Parser/SBE26Parse.m b/Parser/SBE26Parse.m new file mode 100644 index 000000000..b94781dee --- /dev/null +++ b/Parser/SBE26Parse.m @@ -0,0 +1,149 @@ +function sample_data = SBE26Parse( filename, mode ) +%SBE26PARSE Parses a .tid data file from a Seabird SBE26 +% TP logger. +% +% This function is able to read in a .tid data file retrieved +% from a Seabird SBE26 Temperature and Pressure Logger. It is +% assumed the file consists in the following columns: +% +% - measurement number +% - date and time (mm/dd/yyyy HH:MM:SS) of beginning of measurement +% - pressure in psia +% - temperature in degrees Celsius +% +% Inputs: +% filename - cell array of files to import (only one supported). +% mode - Toolbox data type mode. +% +% Outputs: +% sample_data - Struct containing sample data. +% +% Author: Guillaume Galibert +% + +% +% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated +% Marine Observing System (IMOS). +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% * Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in the +% documentation and/or other materials provided with the distribution. +% * Neither the name of the AODN/IMOS nor the names of its contributors +% may be used to endorse or promote products derived from this software +% without specific prior written permission. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. +% +narginchk(1,2); + +if ~iscellstr(filename) + error('filename must be a cell array of strings'); +end + +% only one file supported currently +filename = filename{1}; + +formatSpec = '%*d %f %f %f %f %f %f %f %f'; + +% read in every line in the file +try + fid = fopen(filename, 'rt'); + data = textscan(fid, formatSpec, 'Delimiter', {' ', '/', ':'}, 'MultipleDelimsAsOne', true); + + fclose(fid); + +catch e + if fid ~= -1, fclose(fid); end + rethrow(e); +end + +Y = data{3}; +M = data{1}; +D = data{2}; +H = data{4}; +MN = data{5}; +S = data{6}; + +time = datenum(Y, M, D, H, MN+2, S); % we assume a measurement is averaged over 4 minutes +pressure = data{7} * 0.6894757; % 1psi = 0.6894757dbar +temperature = data{8}; + +% create sample data struct, +% and copy all the data in +sample_data = struct; + +sample_data.toolbox_input_file = filename; +sample_data.meta.featureType = mode; + +sample_data.meta.instrument_make = 'Seabird'; +sample_data.meta.instrument_model = 'SBE26'; + +sample_data.meta.instrument_firmware = ''; + +sample_data.meta.instrument_serial_no = ''; + +sample_data.meta.instrument_sample_interval = median(diff(time*24*3600)); + +sample_data.dimensions = {}; +sample_data.variables = {}; + +% generate time data from header information +sample_data.dimensions{1}.name = 'TIME'; +sample_data.dimensions{1}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.dimensions{1}.name, 'type'))); +sample_data.dimensions{1}.data = sample_data.dimensions{1}.typeCastFunc(time); +sample_data.dimensions{1}.comment = 'Time stamp corresponds to the centre of the measurement which lasts 4 minutes.'; + +sample_data.variables{end+1}.name = 'TIMESERIES'; +sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); +sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(1); +sample_data.variables{end}.dimensions = []; +sample_data.variables{end+1}.name = 'LATITUDE'; +sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); +sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(NaN); +sample_data.variables{end}.dimensions = []; +sample_data.variables{end+1}.name = 'LONGITUDE'; +sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); +sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(NaN); +sample_data.variables{end}.dimensions = []; +sample_data.variables{end+1}.name = 'NOMINAL_DEPTH'; +sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); +sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(NaN); +sample_data.variables{end}.dimensions = []; + +% create a variable for each parameter +coordinates = 'TIME LATITUDE LONGITUDE NOMINAL_DEPTH'; + +% dimensions definition must stay in this order : T, Z, Y, X, others; +% to be CF compliant +sample_data.variables{end+1}.dimensions = 1; +sample_data.variables{end }.name = 'PRES_REL'; +sample_data.variables{end }.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); +sample_data.variables{end }.data = sample_data.variables{end}.typeCastFunc(pressure); +sample_data.variables{end }.coordinates = coordinates; +% let's document the constant pressure atmosphere offset previously +% applied by SeaBird software on the absolute presure measurement +sample_data.variables{end}.applied_offset = sample_data.variables{end}.typeCastFunc(-14.7*0.689476); + +sample_data.variables{end+1}.dimensions = 1; +sample_data.variables{end }.name = 'TEMP'; +sample_data.variables{end }.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); +sample_data.variables{end }.data = sample_data.variables{end}.typeCastFunc(temperature); +sample_data.variables{end }.coordinates = coordinates; + +end \ No newline at end of file diff --git a/Parser/YSI6SeriesParse.m b/Parser/YSI6SeriesParse.m index eb2360d0a..d058f1b9b 100644 --- a/Parser/YSI6SeriesParse.m +++ b/Parser/YSI6SeriesParse.m @@ -222,145 +222,16 @@ sample_data.variables{end}.comment = ... 'Dissolved oxygen saturation from ROX optical sensor.'; - % mg/l => umol/l + % mg/l case 'odo2' - sample_data.variables{end}.name = 'DOX1'; + sample_data.variables{end}.name = 'DOXY'; sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(field' * 44.660/1.429); % O2 density = 1.429kg/m3 + sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(field'); sample_data.variables{end}.comment = ... - ['Dissolved oxygen from ROX optical sensor originally expressed '... - 'in mg/l, O2 density = 1.429kg/m3 and 1ml/l = 44.660umol/l were assumed.']; + 'Dissolved oxygen from ROX optical sensor.'; end sample_data.variables{end}.coordinates = coordinates; end - - % Let's add DOX1/DOX2 if PSAL/CNDC, TEMP and DOXS are present and DOX1 not - % already present - doxs = getVar(sample_data.variables, 'DOXS'); - dox1 = getVar(sample_data.variables, 'DOX1'); - if doxs ~= 0 && dox1 == 0 - doxs = sample_data.variables{doxs}; - name = 'DOX1'; - - % to perform this conversion, we need temperature, - % and salinity/conductivity+pressure data to be present - temp = getVar(sample_data.variables, 'TEMP'); - psal = getVar(sample_data.variables, 'PSAL'); - cndc = getVar(sample_data.variables, 'CNDC'); - pres = getVar(sample_data.variables, 'PRES'); - - % if any of this data isn't present, - % we can't perform the conversion - if temp ~= 0 && (psal ~= 0 || (cndc ~= 0 && pres ~= 0)) - temp = sample_data.variables{temp}; - if psal ~= 0 - psal = sample_data.variables{psal}; - else - cndc = sample_data.variables{cndc}; - pres = sample_data.variables{pres}; - % conductivity is in S/m and gsw_C3515 in mS/cm - crat = 10*cndc.data ./ gsw_C3515; - - % we need to use relative pressure using gsw_P0 = 101325 Pa - psal.data = gsw_SP_from_R(crat, temp.data, pres.data - gsw_P0/10^4); - end - - % O2 solubility (Garcia and Gordon, 1992-1993) - % - solubility = O2sol(psal.data, temp.data, 'ml/l'); - - % O2 saturation to O2 concentration measured - % O2 saturation (per cent) = 100* [O2/O2sol] - % - % that is to say : O2 = O2sol * O2sat / 100 - data = solubility .* doxs.data / 100; - - % conversion from ml/l to umol/l - data = data * 44.660; - comment = ['Originally expressed in % of saturation, using Garcia '... - 'and Gordon equations (1992-1993) and ml/l coefficients, assuming 1ml/l = 44.660umol/l.']; - - sample_data.variables{end+1}.dimensions = 1; - sample_data.variables{end}.comment = comment; - sample_data.variables{end}.name = name; - sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(data); - sample_data.variables{end}.coordinates = coordinates; - - % Let's add DOX2 - name = 'DOX2'; - - % O2 solubility (Garcia and Gordon, 1992-1993) - % - solubility = O2sol(psal.data, temp.data, 'umol/kg'); - - % O2 saturation to O2 concentration measured - % O2 saturation (per cent) = 100* [O2/O2sol] - % - % that is to say : O2 = O2sol * O2sat / 100 - data = solubility .* doxs.data / 100; - comment = ['Originally expressed in % of saturation, using Garcia '... - 'and Gordon equations (1992-1993) and umol/kg coefficients.']; - - sample_data.variables{end+1}.dimensions = 1; - sample_data.variables{end}.comment = comment; - sample_data.variables{end}.name = name; - sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(data); - sample_data.variables{end}.coordinates = coordinates; - end - end - - % Let's add a new parameter if DOX1, PSAL/CNDC, TEMP and PRES are present - dox1 = getVar(sample_data.variables, 'DOX1'); - dox2 = getVar(sample_data.variables, 'DOX2'); - if dox1 ~= 0 && dox2 == 0 - dox1 = sample_data.variables{dox1}; - name = 'DOX2'; - - % umol/l -> umol/kg - % - % to perform this conversion, we need to calculate the - % density of sea water; for this, we need temperature, - % salinity, and pressure data to be present - temp = getVar(sample_data.variables, 'TEMP'); - pres = getVar(sample_data.variables, 'PRES'); - psal = getVar(sample_data.variables, 'PSAL'); - cndc = getVar(sample_data.variables, 'CNDC'); - - % if any of this data isn't present, - % we can't perform the conversion to umol/kg - if temp ~= 0 && pres ~= 0 && (psal ~= 0 || cndc ~= 0) - temp = sample_data.variables{temp}; - pres = sample_data.variables{pres}; - if psal ~= 0 - psal = sample_data.variables{psal}; - else - cndc = sample_data.variables{cndc}; - % conductivity is in S/m and gsw_C3515 in mS/cm - crat = 10*cndc.data ./ gsw_C3515; - - % we need to use relative pressure using gsw_P0 = 101325 Pa - psal.data = gsw_SP_from_R(crat, temp.data, pres.data - gsw_P0/10^4); - end - - % calculate density from salinity, temperature and pressure - dens = sw_dens(psal.data, temp.data, pres.data - gsw_P0/10^4); % cannot use the GSW SeaWater library TEOS-10 as we don't know yet the position - - % umol/l -> umol/kg (dens in kg/m3 and 1 m3 = 1000 l) - data = dox1.data .* 1000.0 ./ dens; - comment = ['Originally expressed in mg/l, assuming O2 density = 1.429kg/m3, 1ml/l = 44.660umol/l '... - 'and using density computed from Temperature, Salinity and Pressure '... - 'with the CSIRO SeaWater library (EOS-80) v1.1.']; - - sample_data.variables{end+1}.dimensions = 1; - sample_data.variables{end}.comment = comment; - sample_data.variables{end}.name = name; - sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(data); - sample_data.variables{end}.coordinates = coordinates; - end - end end diff --git a/Parser/addAWACWaveToSample.m b/Parser/addAWACWaveToSample.m new file mode 100644 index 000000000..22e06f590 --- /dev/null +++ b/Parser/addAWACWaveToSample.m @@ -0,0 +1,200 @@ +function sample_data = addAWACWaveToSample(sample_data, waveData, filename) +%ADDAWACWAVETOSAMPLE Adds AWAC wave parameters found in a .wap file to the +%existing sample_data created while reading the .wpr file. +% +% This function performs a mapping between the AWAC wave parameters and the +% IMOS parameters in order to add them to the sample_data structure. +% +% Inputs: +% sample_data - Struct containing sample data. +% waveData - struct containing data read in from the wave data text files. +% filename - The name of a raw AWAC binary file (.wpr). +% +% Outputs: +% sample_data - Struct containing sample data, this will be a cell array of two structs. +% +% Author: Guillaume Galibert +% + +% +% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated +% Marine Observing System (IMOS). +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% * Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in the +% documentation and/or other materials provided with the distribution. +% * Neither the name of the AODN/IMOS nor the names of its contributors +% may be used to endorse or promote products derived from this software +% without specific prior written permission. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. +% +narginchk(3, 3); + +if ~isstruct(sample_data), error('sample_data must be a struct'); end +if ~isstruct(waveData), error('waveData must be a struct'); end +if ~ischar(filename), error('filename must be a string'); end + +% turn sample data into a cell array +temp{1} = sample_data; +sample_data = temp; +clear temp; + +% copy wave data into a sample_data struct; start with a copy of the +% first sample_data struct, as all the metadata is the same +sample_data{2} = sample_data{1}; + +[filePath, fileRadName, ~] = fileparts(filename); +filename = fullfile(filePath, [fileRadName '.wap']); + +sample_data{2}.toolbox_input_file = filename; +sample_data{2}.meta.head = []; +sample_data{2}.meta.hardware = []; +sample_data{2}.meta.user = []; +sample_data{2}.meta.instrument_sample_interval = median(diff(waveData.Time*24*3600)); + +avgInterval = []; +if isfield(waveData, 'summary') + iMatch = ~cellfun(@isempty, regexp(waveData.summary, 'Wave - Number of samples [0-9]')); + if any(iMatch) + nSamples = textscan(waveData.summary{iMatch}, 'Wave - Number of samples %f'); + + iMatch = ~cellfun(@isempty, regexp(waveData.summary, 'Wave - Sampling rate [0-9\.] Hz')); + if any(iMatch) + samplingRate = textscan(waveData.summary{iMatch}, 'Wave - Sampling rate %f Hz'); + avgInterval = nSamples{1}/samplingRate{1}; + end + end +end +sample_data{2}.meta.instrument_average_interval = avgInterval; +if isempty(avgInterval), avgInterval = '?'; end + +magExt = '_MAG'; +magBiasComment = ''; +magDec = 0; +if waveData.isMagBias + magExt = ''; + magBiasComment = waveData.magBiasComment; + magDec = waveData.magDec; +end + +% add dimensions with their data mapped +dims = { + 'TIME', waveData.Time, ['Time stamp corresponds to the start of the measurement which lasts ' num2str(avgInterval) ' seconds.']; ... + 'FREQUENCY_1', waveData.pwrFrequency, ''; ... + 'FREQUENCY_2', waveData.dirFrequency, ''; ... + ['DIR' magExt], waveData.Direction, magBiasComment + }; + +nDims = size(dims, 1); +sample_data{2}.dimensions = cell(nDims, 1); +for i=1:nDims + sample_data{2}.dimensions{i}.name = dims{i, 1}; + sample_data{2}.dimensions{i}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(dims{i, 1}, 'type'))); + sample_data{2}.dimensions{i}.data = sample_data{2}.dimensions{i}.typeCastFunc(dims{i, 2}); + sample_data{2}.dimensions{i}.comment = dims{i, 3}; +end +clear dims; + +% add information about the middle of the measurement period +sample_data{2}.dimensions{1}.seconds_to_middle_of_measurement = sample_data{2}.meta.instrument_average_interval/2; + +% add variables with their dimensions and data mapped +if isfield(waveData, 'SpectraType') + vars = { + 'TIMESERIES', [], 1; ... + 'LATITUDE', [], NaN; ... + 'LONGITUDE', [], NaN; ... + 'NOMINAL_DEPTH', [], NaN; ... + 'VDEN', [1 2], waveData.pwrSpectrum; ... + ['SSWD' magExt], [1 3], waveData.dirSpectrum; ... + 'WSSH', 1, waveData.SignificantHeight; ... % Significant height (Hm0) (m). This is the classic estimate sometimes referred to as Hs. It is calculated from the energy spectrum, Hmo = 4sqrt(sum(M0)). + 'WHTH', 1, waveData.MeanOneThirdHeight; ... % Mean 1/3 height (H3) (m). This is the mean of the 1/3 largest waves in a record. It is a time series based estimate. Typically this value is 5% larger than Hmo, yet variations can be greater or smaller. Note: AST only. + 'WHTE', 1, waveData.MeanOneTenthHeight; ... % Mean 1/10 height (H10) (m). This is the mean of the 1/10 largest waves in a record. It is a time series based estimate when AST is available. When AST is not available, then this estimate may simply be presented as a linear extrapolation of Hmo, whereby H10 = 1.27Hm0. Note: AST only. + 'WMXH', 1, waveData.MaximumHeight; ... % Maximum height (Hmax) (m). This is the largest wave in a record. It is a time series based estimate when AST is available. When AST is not available, then this estimate may simply be presented as a linear extrapolation of Hmo, whereby Hmax = 1.67Hm0. Note: AST only. + 'WMSH', 1, waveData.MeanHeight; ... % Mean height (Hmean) (m). This is the mean value of all waves in a record. It is a time series based estimate when AST is available. When AST is not available, then this estimate is marked as an invalid value and not displayed. + 'WPSM', 1, waveData.MeanPeriod; ... % Mean period (Tm02) (s). This is the average period for all the waves in the burst and it is calculated from the energy spectrum according to the first and second moment of the energy spectrum: Tm02 = sqrt(M0/M02) The value is reported in seconds. + 'WPMH', 1, waveData.MeanZeroCrossingPeriod; ... % Mean zerocrossing period (Tz) (s). This is the mean period calculated from the zero-crossing technique. It is calculated as the mean of all the periods in the wave burst. The value is reported in seconds. + 'WPTH', 1, waveData.MeanOneThirdPeriod; ... % Mean 1/3 Period (T3) (s). This is the mean period associated with the 1/3 largest waves (H3) in a record, where the period is calculated from the zero-crossing technique. The value is reported in seconds. + 'WPTE', 1, waveData.MeanOneTenthPeriod; ... % Mean 1/10 Period (T10) (s). This is the mean period associated with the 1/10 largest waves (H10) in a record, where the period is calculated from the zero-crossing technique. The value is reported in seconds. + 'WMPP', 1, waveData.MaximumPeriod; ... % Maximum Period (Tmax) (s). This is the mean period associated with the largest wave (Hmax) in a record, where the period is calculated from the zero-crossing technique. The value is reported in seconds. + 'WPPE', 1, waveData.PeakPeriod; ... % Peak period (Tp) (s). This is the period of the waves corresponding to the peak frequency for the wave spectrum. The value is reported in seconds. + ['WPDI' magExt], 1, waveData.PeakDirection; ... % Peak direction (DirTp) (deg). This is the direction of the wave corresponding to the peak period. The direction is reported as “from” and is reported in degrees. + ['SSDS' magExt], 1, waveData.DirectionalSpread; ... % Directional spread (SprTp) (deg). The directional spread is a measure of the directional variance. The estimate is calculated for the peak frequency. The value is reported in degrees. + ['VDIR' magExt], 1, waveData.MeanDirection; ... % Mean direction (Mdir) (deg). This value is a weighted average of all the directions in the wave spectrum. It is weighted according to the energy at each frequency. The direction is reported as “from” and is reported in degrees. + 'TEMP', 1, waveData.Temperature; ... + 'PRES_REL', 1, waveData.MeanPressure; ... % Mean Pressure (dbar). + 'VOLT', 1, waveData.Battery; ... + ['HEADING' magExt], 1, waveData.Heading; ... + 'PITCH', 1, waveData.Pitch; ... + 'ROLL', 1, waveData.Roll; ... + ['SSWV' magExt], [1 3 4], waveData.fullSpectrum; ... + 'SPCT', 1, waveData.SpectraType % Spectrum type (0-Pressure, 1-Velocity, 3-AST). + }; +else + vars = { + 'TIMESERIES', [], 1; ... + 'LATITUDE', [], NaN; ... + 'LONGITUDE', [], NaN; ... + 'NOMINAL_DEPTH', [], NaN; ... + 'VDEN', [1 2], waveData.pwrSpectrum; ... + ['SSWD' magExt], [1 3], waveData.dirSpectrum; ... + 'WSSH', 1, waveData.SignificantHeight; ... % Significant height (Hm0) (m). This is the classic estimate sometimes referred to as Hs. It is calculated from the energy spectrum, Hmo = 4sqrt(sum(M0)). + 'WPMH', 1, waveData.MeanZeroCrossingPeriod; ... % Mean zerocrossing period (Tz) (s). This is the mean period calculated from the zero-crossing technique. It is calculated as the mean of all the periods in the wave burst. The value is reported in seconds. + 'WPPE', 1, waveData.PeakPeriod; ... % Peak period (Tp) (s). This is the period of the waves corresponding to the peak frequency for the wave spectrum. The value is reported in seconds. + ['WPDI' magExt], 1, waveData.PeakDirection; ... % Peak direction (DirTp) (deg). This is the direction of the wave corresponding to the peak period. The direction is reported as “from” and is reported in degrees. + ['SSDS' magExt], 1, waveData.DirectionalSpread; ... % Directional spread (SprTp) (deg). The directional spread is a measure of the directional variance. The estimate is calculated for the peak frequency. The value is reported in degrees. + ['VDIR' magExt], 1, waveData.MeanDirection; ... % Mean direction (Mdir) (deg). This value is a weighted average of all the directions in the wave spectrum. It is weighted according to the energy at each frequency. The direction is reported as “from” and is reported in degrees. + 'TEMP', 1, waveData.Temperature; ... + 'PRES_REL', 1, waveData.MeanPressure; ... % Mean Pressure (dbar). + 'VOLT', 1, waveData.Battery; ... + ['HEADING' magExt], 1, waveData.Heading; ... + 'PITCH', 1, waveData.Pitch; ... + 'ROLL', 1, waveData.Roll; ... + ['SSWV' magExt], [1 3 4], waveData.fullSpectrum + }; +end +clear waveData; + +nVars = size(vars, 1); +sample_data{2}.variables = cell(nVars, 1); +for i=1:nVars + sample_data{2}.variables{i}.name = vars{i, 1}; + sample_data{2}.variables{i}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(vars{i, 1}, 'type'))); + sample_data{2}.variables{i}.dimensions = vars{i, 2}; + if ~isempty(vars{i, 2}) % we don't want this for scalar variables + if any(strcmpi(vars{i, 1}, {'VDEN', 'SSWD_MAG', 'WSSH', 'WHTH', 'WHTE', ... + 'WMXH', 'WMSH', 'WPSM', 'WPMH', 'WPTH', 'WPTE', 'WMPP', 'WPPE', ... + 'WPDI_MAG', 'SSDS_MAG', 'VDIR_MAG', 'SSWV_MAG'})) + sample_data{2}.variables{i}.coordinates = 'TIME LATITUDE LONGITUDE'; % data at the surface, can be inferred from standard/long names + else + sample_data{2}.variables{i}.coordinates = 'TIME LATITUDE LONGITUDE NOMINAL_DEPTH'; + end + end + sample_data{2}.variables{i}.data = sample_data{2}.variables{i}.typeCastFunc(vars{i, 3}); + + if any(strcmpi(vars{i, 1}, {'SSWD', 'WPDI', 'SSDS', 'VDIR', 'HEADING', 'SSWV'})) + sample_data.variables{i}.compass_correction_applied = magDec; + sample_data.variables{i}.comment = magBiasComment; + end +end +clear vars; + +end + diff --git a/Parser/awacParse.m b/Parser/awacParse.m index 1546460be..a5fd55630 100644 --- a/Parser/awacParse.m +++ b/Parser/awacParse.m @@ -347,98 +347,5 @@ % no wave data, no problem if isempty(waveData), return; end -% turn sample data into a cell array -temp{1} = sample_data; -sample_data = temp; -clear temp; - -% copy wave data into a sample_data struct; start with a copy of the -% first sample_data struct, as all the metadata is the same -sample_data{2} = sample_data{1}; - -[filePath, fileRadName, ~] = fileparts(filename); -filename = fullfile(filePath, [fileRadName '.wap']); - -sample_data{2}.toolbox_input_file = filename; -sample_data{2}.meta.head = []; -sample_data{2}.meta.hardware = []; -sample_data{2}.meta.user = []; -sample_data{2}.meta.instrument_sample_interval = median(diff(waveData.Time*24*3600)); - -avgInterval = []; -if isfield(waveData, 'summary') - iMatch = ~cellfun(@isempty, regexp(waveData.summary, 'Wave - Number of samples [0-9]')); - if any(iMatch) - nSamples = textscan(waveData.summary{iMatch}, 'Wave - Number of samples %f'); - - iMatch = ~cellfun(@isempty, regexp(waveData.summary, 'Wave - Sampling rate [0-9\.] Hz')); - if any(iMatch) - samplingRate = textscan(waveData.summary{iMatch}, 'Wave - Sampling rate %f Hz'); - avgInterval = nSamples{1}/samplingRate{1}; - end - end -end -sample_data{2}.meta.instrument_average_interval = avgInterval; -if isempty(avgInterval), avgInterval = '?'; end - -% we assume no correction for magnetic declination has been applied - -% add dimensions with their data mapped -dims = { - 'TIME', waveData.Time, ['Time stamp corresponds to the start of the measurement which lasts ' num2str(avgInterval) ' seconds.']; ... - 'FREQUENCY_1', waveData.pwrFrequency, ''; ... - 'FREQUENCY_2', waveData.dirFrequency, ''; ... - 'DIR_MAG', waveData.Direction, '' - }; - -nDims = size(dims, 1); -sample_data{2}.dimensions = cell(nDims, 1); -for i=1:nDims - sample_data{2}.dimensions{i}.name = dims{i, 1}; - sample_data{2}.dimensions{i}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(dims{i, 1}, 'type'))); - sample_data{2}.dimensions{i}.data = sample_data{2}.dimensions{i}.typeCastFunc(dims{i, 2}); -end -clear dims; - -% add information about the middle of the measurement period -sample_data{2}.dimensions{1}.seconds_to_middle_of_measurement = sample_data{2}.meta.instrument_average_interval/2; - -% add variables with their dimensions and data mapped -vars = { - 'TIMESERIES', [], 1; ... - 'LATITUDE', [], NaN; ... - 'LONGITUDE', [], NaN; ... - 'NOMINAL_DEPTH',[], NaN; ... - 'VDEN', [1 2], waveData.pwrSpectrum; ... % sea_surface_wave_variance_spectral_density - 'SSWD_MAG', [1 3], waveData.dirSpectrum; ... % sea_surface_wave_direction_spectral_density - 'WSSH', 1, waveData.SignificantHeight; ... % sea_surface_wave_spectral_significant_height - 'VAVT', 1, waveData.MeanZeroCrossingPeriod; ... % sea_surface_wave_zero_upcrossing_period - 'VDIR_MAG', 1, waveData.MeanDirection; ... % sea_surface_wave_from_direction - 'SSDS_MAG', 1, waveData.DirectionalSpread; ... % sea_surface_wave_directional_spread - 'TEMP', 1, waveData.Temperature; ... - 'PRES_REL', 1, waveData.MeanPressure; ... - 'VOLT', 1, waveData.Battery; ... - 'HEADING_MAG', 1, waveData.Heading; ... - 'PITCH', 1, waveData.Pitch; ... - 'ROLL', 1, waveData.Roll; ... - 'SSWV_MAG', [1 3 4], waveData.fullSpectrum; ... % sea_surface_wave_directional_variance_spectral_density - 'SPCT', 1, waveData.SpectraType % awac_spectra_calculation_method - }; -clear waveData; - -nVars = size(vars, 1); -sample_data{2}.variables = cell(nVars, 1); -for i=1:nVars - sample_data{2}.variables{i}.name = vars{i, 1}; - sample_data{2}.variables{i}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(vars{i, 1}, 'type'))); - sample_data{2}.variables{i}.dimensions = vars{i, 2}; - if ~isempty(vars{i, 2}) % we don't want this for scalar variables - if any(strcmpi(vars{i, 1}, {'VDEN', 'SSWD_MAG', 'WSSH', 'VAVT', 'VDIR_MAG', 'SSDS_MAG', 'SSWV_MAG'})) - sample_data{2}.variables{i}.coordinates = 'TIME LATITUDE LONGITUDE'; % data at the surface, can be inferred from standard/long names - else - sample_data{2}.variables{i}.coordinates = 'TIME LATITUDE LONGITUDE NOMINAL_DEPTH'; - end - end - sample_data{2}.variables{i}.data = sample_data{2}.variables{i}.typeCastFunc(vars{i, 3}); -end -clear vars; \ No newline at end of file +% add wave data to existing sample_data +sample_data = addAWACWaveToSample(sample_data, waveData, filename); diff --git a/Parser/convertSBEcnvVar.m b/Parser/convertSBEcnvVar.m index 2753cbd0f..22a02137b 100644 --- a/Parser/convertSBEcnvVar.m +++ b/Parser/convertSBEcnvVar.m @@ -141,27 +141,31 @@ comment = 'Artificial chlorophyll data computed from bio-optical sensor raw counts measurements.'; % oxygen (mg/l) - % mg/l => umol/l - case {'oxsolMg0x2FL', 'oxsatMg0x2FL', 'sbeox0Mg0x2FL'} - name = 'DOX1'; - data = data .* 44.660/1.429; % O2 density = 1.429kg/m3 - comment = 'Originally expressed in mg/l, O2 density = 1.429kg/m3 and 1ml/l = 44.660umol/l were assumed.'; + % mg/l + case 'sbeox0Mg0x2FL' + name = 'DOXY'; + comment = ''; % oxygen (ml/l) - % ml/l => umol/l + % ml/l case 'sbeox0ML0x2FL' - name = 'DOX1'; - data = data .* 44.660; - comment = 'Originally expressed in ml/l, 1ml/l = 44.660umol/l was assumed.'; + name = 'DOX'; + comment = ''; + % oxygen (umol/L) + % umol/L + case 'sbeox0Mm0x2FL' + name = 'DOX1'; + comment = ''; + % oxygen (umol/Kg) % umol/Kg - case {'oxsolMm0x2FKg', 'oxsatMm0x2FKg', 'sbeox0Mm0x2FKg', 'sbeopoxMm0x2FKg'} + case {'sbeox0Mm0x2FKg', 'sbeopoxMm0x2FKg'} name = 'DOX2'; comment = ''; - % Oxygen, SBE 63 [% saturation] - case 'sbeopoxPS' + % Oxygen [% saturation] + case {'sbeopoxPS', 'sbeox0PS'} name = 'DOXS'; comment = ''; @@ -215,7 +219,7 @@ origName = name; name = getVoltageName(origName, instHeader); if ~strcmpi(name, 'not_assigned') - name = ['volt_', getVoltageName(origName, instHeader)]; + name = ['volt_', name]; comment = getVoltageComment(origName, procHeader); else name = ''; @@ -324,6 +328,6 @@ end end -name = strrep(name, ' ', '_'); +name = regexprep(name, '[^a-zA-Z0-9]', '_'); % to make a valid var name for structure, only keep word characters end \ No newline at end of file diff --git a/Parser/readAWACWaveAscii.m b/Parser/readAWACWaveAscii.m index 0946510dc..8be91d380 100644 --- a/Parser/readAWACWaveAscii.m +++ b/Parser/readAWACWaveAscii.m @@ -76,6 +76,23 @@ % 30 Current direction (wave cell) (degrees) % 31 Error Code % +% OR% +% 1 Month (1-12) +% 2 Day (1-31) +% 3 Year +% 4 Hour (0-23) +% 5 Minute (0-59) +% 6 Second (0-59) +% 7 Significant height (Hs) (m) +% 8 Mean zerocrossing period (Tm02) (s) +% 9 Peak period (Tp) (s) +% 10 Peak direction (DirTp) (deg) +% 11 Directional spread (Spr1) (deg) +% 12 Mean direction (Mdir) (deg) +% 13 Mean Pressure (m) +% 14 Unidirectivity index +% 15 Error Code +% % Assumed file layout for power spectra data file (.was): % % Frequency Vector (Hz) @@ -151,22 +168,30 @@ waveData = []; % transform the filename into processed wave data filenames -[path, name] = fileparts(filename); +[filePath, fileRadName] = fileparts(filename); -summaryFile = fullfile(path, [name '.hdr']); -headerFile = fullfile(path, [name '.whd']); -waveFile = fullfile(path, [name '.wap']); -dirFreqFile = fullfile(path, [name '.wdr']); -pwrFreqFile = fullfile(path, [name '.was']); -pwrFreqDirFile = fullfile(path, [name '.wds']); +% from nortek instrument data conversion step +summaryFile = fullfile(filePath, [fileRadName '.hdr']); +headerFile = fullfile(filePath, [fileRadName '.whd']); +% from storm/quickwave processing step +waveSummaryFile = fullfile(filePath, [fileRadName '.whr']); +waveFile = fullfile(filePath, [fileRadName '.wap']); +dirFreqFile = fullfile(filePath, [fileRadName '.wdr']); +pwrFreqFile = fullfile(filePath, [fileRadName '.was']); +pwrFreqDirFile = fullfile(filePath, [fileRadName '.wds']); -% test the files exist -if ~exist(headerFile, 'file') || ~exist(waveFile, 'file') || ... - ~exist(dirFreqFile, 'file') || ~exist(pwrFreqFile, 'file') || ... - ~exist(pwrFreqDirFile, 'file') - fprintf('%s\n', ['Info : To read wave data related to ' name ... - ', .whd, .wap, .wdr, .was, .wds are necessary ' ... - '(use QuickWave or Storm Nortek softwares).']); +% test if the files exist +requiredFiles = {summaryFile headerFile waveSummaryFile ... + waveFile dirFreqFile pwrFreqFile pwrFreqDirFile}; +iFiles = arrayfun(@(x) ~exist(char(x),'file'), requiredFiles); +if any(iFiles) + for i = find(iFiles) + [~, fName, fExt] = fileparts(requiredFiles{i}); + disp(['Missing file : ' fName fExt]); + end + fprintf('%s\n', ['Info : To read wave data related to ' fileRadName ... + ', .whd, .whr, .wap, .wdr, .was, .wds are necessary ' ... + '(use instrument software data conversion and QuickWave or Storm Nortek softwares).']); return; end @@ -180,12 +205,40 @@ fclose(summaryFileID); end + if exist(waveSummaryFile, 'file') + waveSummaryFileID = fopen(waveSummaryFile); + waveSummary = textscan(waveSummaryFileID, '%s', 'Delimiter', ''); + waveData.waveSummary = waveSummary{1}; + fclose(waveSummaryFileID); + end + header = importdata(headerFile); wave = importdata(waveFile); dirFreq = importdata(dirFreqFile); pwrFreq = importdata(pwrFreqFile); pwrFreqDir = importdata(pwrFreqDirFile); + % need to check if during waves processing has had compass/directional + % offset applied + tkns = regexp(waveData.waveSummary, '(?i:Directional Offset\s+)(.*)(?i:\s+deg\s*)', 'tokens'); + directionalOffset = str2double(tkns{~cellfun(@isempty, tkns)}{1}{1}); + + tkns = regexp(waveData.waveSummary, '(?i:Compass offset\s+)(.*)(?i:\s+deg\s*)', 'tokens'); + compassOffset = str2double(tkns{~cellfun(@isempty, tkns)}{1}{1}); + + % have always assumed (and only observed) that in whr file, directionalOffset == compassOffset + if directionalOffset ~= compassOffset + throw(MException('readAWACWaveAscii:offsetError','Uncertain how to handle different directionalOffset and compassOffset')); + end + waveData.isMagBias = false; + waveData.magDec = directionalOffset; + if waveData.magDec ~= 0 + waveData.isMagBias = true; + waveData.magBiasComment = ['A compass correction of ' num2str(waveData.magDec) ... + 'degrees has been applied to the data during wave processing stage ' ... + '(usually to account for magnetic declination).']; + end + % Transform local missing value (-9.00) to NaN wave(wave == -9.00) = NaN; dirFreq(dirFreq == -9.00) = NaN; @@ -230,25 +283,53 @@ waveData.Temperature(iHeader) = header(:,17); clear header iHeader; - waveData.SpectraType = 9*ones(nTime, 1, 'uint8'); waveData.SignificantHeight = nan(nTime, 1); - waveData.MeanZeroCrossingPeriod = nan(nTime, 1); waveData.PeakPeriod = nan(nTime, 1); + waveData.MeanZeroCrossingPeriod = nan(nTime, 1); waveData.PeakDirection = nan(nTime, 1); waveData.DirectionalSpread = nan(nTime, 1); waveData.MeanDirection = nan(nTime, 1); - waveData.MeanPressure = nan(nTime, 1); waveData.UnidirectivityIndex = nan(nTime, 1); + waveData.MeanPressure = nan(nTime, 1); + + if size(wave,2) == 31 + waveData.SpectraType = 9*ones(nTime, 1, 'uint8'); + waveData.MeanOneThirdHeight = nan(nTime, 1); + waveData.MeanOneTenthHeight = nan(nTime, 1); + waveData.MaximumHeight = nan(nTime, 1); + waveData.MeanHeight = nan(nTime, 1); + waveData.MeanPeriod = nan(nTime, 1); + waveData.MeanOneThirdPeriod = nan(nTime, 1); + waveData.MeanOneTenthPeriod = nan(nTime, 1); + waveData.MaximumPeriod = nan(nTime, 1); - waveData.SpectraType(iWave) = wave(:,7); - waveData.SignificantHeight(iWave) = wave(:,8); - waveData.PeakPeriod(iWave) = wave(:,14); - waveData.MeanZeroCrossingPeriod(iWave) = wave(:,15); - waveData.PeakDirection(iWave) = wave(:,19); - waveData.DirectionalSpread(iWave) = wave(:,20); - waveData.MeanDirection(iWave) = wave(:,21); - waveData.UnidirectivityIndex(iWave) = wave(:,22); - waveData.MeanPressure(iWave) = wave(:,23); + waveData.SpectraType(iWave) = wave(:,7); + waveData.SignificantHeight(iWave) = wave(:,8); + waveData.MeanOneThirdHeight(iWave) = wave(:,9); + waveData.MeanOneTenthHeight(iWave) = wave(:,10); + waveData.MaximumHeight(iWave) = wave(:,11); + waveData.MeanHeight(iWave) = wave(:,12); + waveData.MeanPeriod(iWave) = wave(:,13); + waveData.PeakPeriod(iWave) = wave(:,14); + waveData.MeanZeroCrossingPeriod(iWave) = wave(:,15); + waveData.MeanOneThirdPeriod(iWave) = wave(:,16); + waveData.MeanOneTenthPeriod(iWave) = wave(:,17); + waveData.MaximumPeriod(iWave) = wave(:,18); + waveData.PeakDirection(iWave) = wave(:,19); + waveData.DirectionalSpread(iWave) = wave(:,20); + waveData.MeanDirection(iWave) = wave(:,21); + waveData.UnidirectivityIndex(iWave) = wave(:,22); + waveData.MeanPressure(iWave) = wave(:,23); + else + waveData.SignificantHeight(iWave) = wave(:,7); + waveData.MeanZeroCrossingPeriod(iWave) = wave(:,8); + waveData.PeakPeriod(iWave) = wave(:,9); + waveData.PeakDirection(iWave) = wave(:,10); + waveData.DirectionalSpread(iWave) = wave(:,11); + waveData.MeanDirection(iWave) = wave(:,12); + waveData.MeanPressure(iWave) = wave(:,13); + waveData.UnidirectivityIndex(iWave) = wave(:,14); + end clear wave; % let's have a look at the different frequency given in each file @@ -272,21 +353,25 @@ waveData.fullSpectrum = nan(nTime, nDirFreq, nDir); % we should have nTime samples so : - nFreqFullSpectrum = length(pwrFreqDir(2:end,:)) / nTime; + nFreqFullSpectrum = round(length(pwrFreqDir(2:end,:)) / nTime); % rearrange full power spectrum matrix so dimensions % are ordered: time, frequency, direction start = 2; for i=1:nTime - waveData.fullSpectrum(i, :, :) = pwrFreqDir(start:start+nFreqFullSpectrum-1,:); - start = start+nFreqFullSpectrum; + if start+nFreqFullSpectrum-1 <= size(pwrFreqDir,1) + waveData.fullSpectrum(i, :, :) = pwrFreqDir(start:start+nFreqFullSpectrum-1,:); + start = start+nFreqFullSpectrum; + else + break; + end end clear pwrFreqDir catch e - fprintf('%s\n', ['Warning : Wave data related to ' name ... + fprintf('%s\n', ['Warning : Wave data related to ' fileRadName ... ' hasn''t been read successfully.']); errorString = getErrorString(e); fprintf('%s\n', ['Error says : ' errorString]); end -end \ No newline at end of file +end diff --git a/Parser/readSBE19cnv.m b/Parser/readSBE19cnv.m index f625a3834..b3e65f74e 100644 --- a/Parser/readSBE19cnv.m +++ b/Parser/readSBE19cnv.m @@ -96,45 +96,6 @@ comment.(nn) = c; end - % Let's add a new parameter if DOX1, PSAL/CNDC, TEMP and PRES are present and - % not DOX2 - if isfield(data, 'DOX1') && ~isfield(data, 'DOX2') - - % umol/l -> umol/kg - % - % to perform this conversion, we need to calculate the - % density of sea water; for this, we need temperature, - % salinity, and pressure data to be present - temp = isfield(data, 'TEMP'); - pres = isfield(data, 'PRES_REL'); - psal = isfield(data, 'PSAL'); - cndc = isfield(data, 'CNDC'); - - % if any of this data isn't present, - % we can't perform the conversion to umol/kg - if temp && pres && (psal || cndc) - temp = data.TEMP; - pres = data.PRES_REL; - if psal - psal = data.PSAL; - else - cndc = data.CNDC; - % conductivity is in S/m and gsw_C3515 in mS/cm - crat = 10*cndc ./ gsw_C3515; - - psal = gsw_SP_from_R(crat, temp, pres); - end - - % calculate density from salinity, temperature and pressure - dens = sw_dens(psal, temp, pres); % cannot use the GSW SeaWater library TEOS-10 as we don't know yet the position - - % umol/l -> umol/kg (dens in kg/m3 and 1 m3 = 1000 l) - data.DOX2 = data.DOX1 .* 1000.0 ./ dens; - comment.DOX2 = ['Originally expressed in mg/l, assuming O2 density = 1.429kg/m3, 1ml/l = 44.660umol/l '... - 'and using density computed from Temperature, Salinity and Pressure '... - 'with the CSIRO SeaWater library (EOS-80) v1.1.']; - end - end end function [name, data, comment] = convertData(name, data, instHeader, procHeader, mode) diff --git a/Parser/readSBE19hex.m b/Parser/readSBE19hex.m index 6ba4cb30b..e43ac1e1e 100644 --- a/Parser/readSBE19hex.m +++ b/Parser/readSBE19hex.m @@ -192,44 +192,6 @@ end end - % Let's add a new parameter if DOX1, PSAL/CNDC, TEMP and PRES are present - if isfield(newData, 'DOX1') - % umol/l -> umol/kg - % - % to perform this conversion, we need to calculate the - % density of sea water; for this, we need temperature, - % salinity, and pressure data to be present - temp = isfield(newData, 'TEMP'); - pres = isfield(newData, 'PRES'); - psal = isfield(newData, 'PSAL'); - cndc = isfield(newData, 'CNDC'); - - % if any of this data isn't present, - % we can't perform the conversion to umol/kg - if temp && pres && (psal || cndc) - temp = newData.TEMP; - pres = newData.PRES; - if psal - psal = newData.PSAL; - else - cndc = newData.CNDC; - % conductivity is in S/m and gsw_C3515 in mS/cm - crat = 10*cndc ./ gsw_C3515; - - % we need to use relative pressure using gsw_P0 = 101325 Pa - psal = gsw_SP_from_R(crat, temp, pres - gsw_P0/10^4); - end - - % calculate density from salinity, temperature and pressure - dens = sw_dens(psal, temp, pres - gsw_P0/10^4); % cannot use the GSW SeaWater library TEOS-10 as we don't know yet the position - - % umol/l -> umol/kg (dens in kg/m3 and 1 m3 = 1000 l) - newData.DOX2 = newData.DOX1 .* 1000.0 ./ dens; - comment.DOX2 = ['Originally expressed in umol/l, assuming 1l = 0.001m3 '... - 'and using density computed from Temperature, Salinity and Pressure '... - 'with the CSIRO SeaWater library (EOS-80) v1.1.']; - end - end end function check = checkField(struc, fieldName, fieldValue) diff --git a/Parser/readSBE37cnv.m b/Parser/readSBE37cnv.m index 3deebec1b..49ed4c73e 100644 --- a/Parser/readSBE37cnv.m +++ b/Parser/readSBE37cnv.m @@ -86,45 +86,6 @@ comment.(nn) = c; end - % Let's add a new parameter if DOX1, PSAL/CNDC, TEMP and PRES are present and - % not DOX2 - if isfield(data, 'DOX1') && ~isfield(data, 'DOX2') - - % umol/l -> umol/kg - % - % to perform this conversion, we need to calculate the - % density of sea water; for this, we need temperature, - % salinity, and pressure data to be present - temp = isfield(data, 'TEMP'); - pres = isfield(data, 'PRES_REL'); - psal = isfield(data, 'PSAL'); - cndc = isfield(data, 'CNDC'); - - % if any of this data isn't present, - % we can't perform the conversion to umol/kg - if temp && pres && (psal || cndc) - temp = data.TEMP; - pres = data.PRES_REL; - if psal - psal = data.PSAL; - else - cndc = data.CNDC; - % conductivity is in S/m and gsw_C3515 in mS/cm - crat = 10*cndc ./ gsw_C3515; - - psal = gsw_SP_from_R(crat, temp, pres); - end - - % calculate density from salinity, temperature and pressure - dens = sw_dens(psal, temp, pres); % cannot use the GSW SeaWater library TEOS-10 as we don't know yet the position - - % umol/l -> umol/kg (dens in kg/m3 and 1 m3 = 1000 l) - data.DOX2 = data.DOX1 .* 1000.0 ./ dens; - comment.DOX2 = ['Originally expressed in mg/l, assuming O2 density = 1.429kg/m3, 1ml/l = 44.660umol/l '... - 'and using density computed from Temperature, Salinity and Pressure '... - 'with the CSIRO SeaWater library (EOS-80) v1.1.']; - end - end end function [name, data, comment] = convertData(name, data, instHeader, procHeader, mode) diff --git a/Parser/readWQMdat.m b/Parser/readWQMdat.m index 47fac7bc3..7bbda19c5 100644 --- a/Parser/readWQMdat.m +++ b/Parser/readWQMdat.m @@ -50,9 +50,9 @@ % Temperature ('TEMP'): Degrees Celsius % Pressure ('PRES_REL'): Decibars % Salinity ('PSAL'): 1e^(-3) (PSS) -% Dissolved Oxygen ('DOXY'): kg/m^3 +% Dissolved Oxygen ('DOXY'): mg/l % Dissolved Oxygen ('DOX1'): mmol/m^3 -% Dissolved Oxygen ('DOX2'): umol/kg +% Dissolved Oxygen ('DOX'): ml/l % Chlorophyll ('CPHL'): mg/m^3 % Chlorophyll ('CHLU'): mg/m^3 (user coefficient) % Chlorophyll ('CHLF'): mg/m^3 (factory coefficient) @@ -129,9 +129,9 @@ params{end+1} = {'Temp(C)', {'TEMP', ''}}; params{end+1} = {'Pres(dbar)', {'PRES_REL', ''}}; params{end+1} = {'Sal(PSU)', {'PSAL', ''}}; - params{end+1} = {'DO(mg/l)', {'DOX1_3', ''}}; - params{end+1} = {'DO(mmol/m^3)', {'DOX1_1', ''}}; - params{end+1} = {'DO(ml/l)', {'DOX1_2', ''}}; + params{end+1} = {'DO(mg/l)', {'DOXY', ''}}; + params{end+1} = {'DO(mmol/m^3)', {'DOX1', ''}}; % mmol/m3 <=> umol/l + params{end+1} = {'DO(ml/l)', {'DOX', ''}}; params{end+1} = {'CHL(ug/l)', {'CPHL', 'Artificial chlorophyll data '... 'computed from bio-optical sensor raw counts measurements. The '... 'fluorometre is equipped with a 470nm peak wavelength LED to irradiate and a '... @@ -276,43 +276,7 @@ [name, comment] = getParamDetails(fields{k}, params); data = samples{k-1}; - - % some fields are not in IMOS uom - scale them so that they are - switch upper(fields{k}) - - % WQM provides conductivity S/m; exactly like we want it to be! - - % WQM can provide Dissolved Oxygen in mmol/m3, - % hopefully 1 mmol/m3 = 1 umol/l - % exactly like we want it to be! - case upper('DO(mmol/m^3)') % DOX1_1 - comment = 'Originally expressed in mmol/m3, 1l = 0.001m3 was assumed.'; - isUmolPerL = true; - - % convert dissolved oxygen in ml/l to umol/l - case upper('DO(ml/l)') % DOX1_2 - comment = 'Originally expressed in ml/l, 1ml/l = 44.660umol/l was assumed.'; - isUmolPerL = true; - - % ml/l -> umol/l - % - % Conversion factors from Saunders (1986) : - % https://darchive.mblwhoilibrary.org/bitstream/handle/1912/68/WHOI-89-23.pdf?sequence=3 - % 1ml/l = 43.57 umol/kg (with dens = 1.025 kg/l) - % 1ml/l = 44.660 umol/l - - data = data .* 44.660; - - % convert dissolved oxygen in mg/L to umol/l. - case upper('DO(mg/l)') % DOX1_3 - data = data * 44.660/1.429; % O2 density = 1.429 kg/m3 - comment = 'Originally expressed in mg/l, O2 density = 1.429kg/m3 and 1ml/l = 44.660umol/l were assumed.'; - isUmolPerL = true; - % WQM provides chlorophyll in ug/L; we need it in mg/m^3, - % hopefully it is equivalent. - end - coordinates = 'TIME LATITUDE LONGITUDE NOMINAL_DEPTH'; % dimensions definition must stay in this order : T, Z, Y, X, others; @@ -336,67 +300,6 @@ case upper('DO(mg/l)') % DOX1_3 % present, but temp/pressure/salinity data is not) sample_data.variables(cellfun(@isempty, sample_data.variables)) = []; - % Let's add a new parameter - if isUmolPerL - data = getVar(sample_data.variables, 'DOX1_1'); - comment = ['Originally expressed in mmol/m3, assuming 1l = 0.001m3 '... - 'and using density computed from Temperature, Salinity and Pressure '... - 'with the CSIRO SeaWater library (EOS-80) v1.1.']; - if data == 0 - data = getVar(sample_data.variables, 'DOX1_2'); - comment = ['Originally expressed in ml/l, assuming 1ml/l = 44.660umol/l '... - 'and using density computed from Temperature, Salinity and Pressure '... - 'with the CSIRO SeaWater library (EOS-80) v1.1.']; - if data == 0 - data = getVar(sample_data.variables, 'DOX1_3'); - comment = ['Originally expressed in mg/l, assuming O2 density = 1.429kg/m3, 1ml/l = 44.660umol/l '... - 'and using density computed from Temperature, Salinity and Pressure '... - 'with the CSIRO SeaWater library (EOS-80) v1.1.']; - end - end - data = sample_data.variables{data}; - data = data.data; - name = 'DOX2'; - - % umol/l -> umol/kg - % - % to perform this conversion, we need to calculate the - % density of sea water; for this, we need temperature, - % salinity, and pressure data to be present - temp = getVar(sample_data.variables, 'TEMP'); - pres = getVar(sample_data.variables, 'PRES_REL'); - psal = getVar(sample_data.variables, 'PSAL'); - cndc = getVar(sample_data.variables, 'CNDC'); - - % if any of this data isn't present, - % we can't perform the conversion to umol/kg - if temp ~= 0 && pres ~= 0 && (psal ~= 0 || cndc ~= 0) - temp = sample_data.variables{temp}; - pres = sample_data.variables{pres}; - if psal ~= 0 - psal = sample_data.variables{psal}; - else - cndc = sample_data.variables{cndc}; - % conductivity is in S/m and gsw_C3515 in mS/cm - crat = 10*cndc.data ./ gsw_C3515; - psal = struct; - psal.data = gsw_SP_from_R(crat, temp.data, pres.data); - end - - % calculate density from salinity, temperature and pressure - dens = sw_dens(psal.data, temp.data, pres.data); % cannot use the GSW SeaWater library TEOS-10 as we don't know yet the position - - % umol/l -> umol/kg (dens in kg/m3 and 1 m3 = 1000 l) - data = data .* 1000.0 ./ dens; - - sample_data.variables{end+1}.dimensions = 1; - sample_data.variables{end}.name = name; - sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(data); - sample_data.variables{end}.coordinates = coordinates; - sample_data.variables{end}.comment = comment; - end - end end function [fields, format, jThere] = getFormat(fid, required, params) diff --git a/Parser/readWQMraw.m b/Parser/readWQMraw.m index 9b69b6954..e3977b0be 100644 --- a/Parser/readWQMraw.m +++ b/Parser/readWQMraw.m @@ -17,9 +17,9 @@ % Temperature ('TEMP'): Degrees Celsius % Pressure ('PRES_REL'): Decibars % Salinity ('PSAL'): 1e^(-3) (PSS) -% Dissolved Oxygen ('DOXY'): kg/m^3 +% Dissolved Oxygen ('DOXY'): mg/l % Dissolved Oxygen ('DOX1'): mmol/m^3 -% Dissolved Oxygen ('DOX2'): mol/kg +% Dissolved Oxygen ('DOXS'): % % fluorescence ('CPHL'): mg/m^3 % Chlorophyll ('CHLU'): mg/m^3 (user coefficient) % Chlorophyll ('CHLF'): mg/m^3 (factory coefficient) @@ -87,9 +87,9 @@ params{end+1} = {'temperature', {'TEMP', ''}}; params{end+1} = {'pressure', {'PRES_REL', ''}}; params{end+1} = {'salinity', {'PSAL', ''}}; - params{end+1} = {'DO(mg/l)', {'DOX1_3', ''}}; - params{end+1} = {'DO(mmol/m^3)', {'DOX1_1', ''}}; - params{end+1} = {'oxygen', {'DOX1_2', ''}}; + params{end+1} = {'DO(mg/l)', {'DOXY', ''}}; + params{end+1} = {'DO(mmol/m^3)', {'DOX1', ''}}; % mmol/m3 <=> umol/l + params{end+1} = {'oxygenSat', {'DOXS', ''}}; params{end+1} = {'fluorescence', {'FLU2', ''}}; params{end+1} = {'F_Cal_CHL', {'CHLF', 'Artificial chlorophyll data '... 'computed from bio-optical sensor raw counts measurements using factory calibration coefficient. The '... @@ -187,47 +187,6 @@ [name, comment] = getParamDetails(varlabel{k}, params); data = wqmdata.(varlabel{k}); - - % some fields are not in IMOS uom - scale them so that they are - switch varlabel{k} - - % WQM provides conductivity in S/m; exactly like we want it to be! - - % convert dissolved oxygen in mg/L to umol/l. - case 'DO(mg/l)' - data = data * 44.660/1.429; % O2 density = 1.429kg/m3 - comment = 'Originally expressed in mg/l, O2 density = 1.429kg/m3 and 1ml/l = 44.660umol/l were assumed.'; - isUmolPerL = true; - - % WQM can provide Dissolved Oxygen in mmol/m3, - % hopefully 1 mmol/m3 = 1 umol/l - % exactly like we want it to be! - case 'DO(mmol/m^3)' - comment = 'Originally expressed in mmol/m3, 1l = 0.001m3 was assumed.'; - isUmolPerL = true; - - % convert dissolved oxygen in ml/l to umol/l - case 'oxygen' - comment = 'Originally expressed in ml/l, 1ml/l = 44.660umol/l was assumed.'; - isUmolPerL = true; - - % ml/l -> umol/l - % - % Conversion factors from Saunders (1986) : - % https://darchive.mblwhoilibrary.org/bitstream/handle/1912/68/WHOI-89-23.pdf?sequence=3 - % 1ml/l = 43.57 umol/kg (with dens = 1.025 kg/l) - % 1ml/l = 44.660 umol/l - - data = data .* 44.660; - - % WQM provides chlorophyll in ug/L; we need it in mg/m^3, - % hopefully it is equivalent. - - case 'PAR' - % don't seem to know what to do with PAR yet - continue; - - end % dimensions definition must stay in this order : T, Z, Y, X, others; % to be CF compliant @@ -250,67 +209,6 @@ % present, but temp/pressure/salinity data is not) sample_data.variables(cellfun(@isempty, sample_data.variables)) = []; - % Let's add a new parameter - if isUmolPerL - data = getVar(sample_data.variables, 'DOX1_1'); - comment = ['Originally expressed in mmol/m3, assuming 1l = 0.001m3 '... - 'and using density computed from Temperature, Salinity and Pressure '... - 'with the CSIRO SeaWater library (EOS-80) v1.1.']; - if data == 0 - data = getVar(sample_data.variables, 'DOX1_2'); - comment = ['Originally expressed in ml/l, assuming 1ml/l = 44.660umol/l '... - 'and using density computed from Temperature, Salinity and Pressure '... - 'with the CSIRO SeaWater library (EOS-80) v1.1.']; - if data == 0 - data = getVar(sample_data.variables, 'DOX1_3'); - comment = ['Originally expressed in mg/l, assuming O2 density = 1.429kg/m3, 1ml/l = 44.660umol/l '... - 'and using density computed from Temperature, Salinity and Pressure '... - 'with the CSIRO SeaWater library (EOS-80) v1.1.']; - end - end - data = sample_data.variables{data}; - data = data.data; - name = 'DOX2'; - - % umol/l -> umol/kg - % - % to perform this conversion, we need to calculate the - % density of sea water; for this, we need temperature, - % salinity, and pressure data to be present - temp = getVar(sample_data.variables, 'TEMP'); - pres = getVar(sample_data.variables, 'PRES_REL'); - psal = getVar(sample_data.variables, 'PSAL'); - cndc = getVar(sample_data.variables, 'CNDC'); - - % if any of this data isn't present, - % we can't perform the conversion to umol/kg - if temp ~= 0 && pres ~= 0 && (psal ~= 0 || cndc ~= 0) - temp = sample_data.variables{temp}; - pres = sample_data.variables{pres}; - if psal ~= 0 - psal = sample_data.variables{psal}; - else - cndc = sample_data.variables{cndc}; - % conductivity is in S/m and gsw_C3515 in mS/cm - crat = 10*cndc.data ./ gsw_C3515; - - psal.data = gsw_SP_from_R(crat, temp.data, pres.data); - end - - % calculate density from salinity, temperature and pressure - dens = sw_dens(psal.data, temp.data, pres.data); % cannot use the GSW SeaWater library TEOS-10 as we don't know yet the position - - % umol/l -> umol/kg (dens in kg/m3 and 1 m3 = 1000 l) - data = data .* 1000.0 ./ dens; - - sample_data.variables{end+1}.dimensions = 1; - sample_data.variables{end}.comment = comment; - sample_data.variables{end}.name = name; - sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(data); - sample_data.variables{end}.coordinates = 'TIME LATITUDE LONGITUDE NOMINAL_DEPTH'; - end - end end function [name, comment] = getParamDetails(field, params) @@ -485,7 +383,7 @@ WQM.instrument='Wetlabs WQM'; WQM.samp_units='datenumber'; -WQM.varlabel={'conductivity','temperature','pressure','salinity','oxygen','U_Cal_CHL','backscatterance'}; +WQM.varlabel={'conductivity','temperature','pressure','salinity','oxygenSat','U_Cal_CHL','backscatterance'}; WQM.varunits={'S/m','C','dbar','PSU','ml/l','ug/l','NTU'}; @@ -634,9 +532,9 @@ O2.C = C52{2}; O2.E = E52{2}; -oxygen = A(:,4); +oxygenSat = A(:,4); -WQM.oxygen = O2cal(oxygen, O2, WQM); +WQM.oxygenSat = O2cal(oxygenSat, O2, WQM); % FLNTU Sensor chl = textscan(headerLine{~cellfun('isempty',strfind(headerLine,'UserCHL'))},'%8c%f%f\n'); @@ -698,9 +596,9 @@ function O2=O2cal(freq,Cal,CTD) % WQM uses SBE-43F -% Oxygen = Soc * (output + Foffset)*(1.0+A*T+B*T^2+C*T^3)*Oxsat(T,S)*exp(E*P/K), +% Oxygen = Soc * (output + Foffset)*(1.0+A*T+B*T^2+C*T^3)*exp(E*P/K), % where output=SBE-43F oxygen sensor output frequency in Hz, -% K=T[degK], OxSat()=oxygen saturation[ml/l] function of Weiss +% K=T[degK] Soc=Cal.Soc; FOffset=Cal.FOffset; @@ -716,9 +614,8 @@ P2=(1+A.*T+B.*T.^2+C.*T.^3); K=T+273.15; P3=exp(E.*P./K); -oxsat=sw_satO2(S,T); % cannot use the GSW SeaWater library TEOS-10 since there is not any function provided for oxygen. -O2=P1.*P2.*oxsat.*P3; +O2=P1.*P2.*P3; % is 30000 a bad flag can't tell but O2 is bad if freq sticks O2(freq==30000)=NaN; end diff --git a/Parser/readXR620.m b/Parser/readXR620.m index 768ff2721..8a420a8dc 100644 --- a/Parser/readXR620.m +++ b/Parser/readXR620.m @@ -290,22 +290,15 @@ %Speed of sound (m/s) case 'SoSUN', name = 'SSPD'; - %Rinko dissolved O2 concentration (mg/l) => (umol/l) - case 'rdO2C' - name = 'DOX1'; - comment.(vars{k}) = ['Originally expressed in mg/l, ' ... - 'O2 density = 1.429kg/m3 and 1ml/l = 44.660umol/l were assumed.']; - data.(vars{k}) = data.(vars{k}) * 44.660/1.429; % O2 density = 1.429 kg/m3 + %Rinko dissolved O2 concentration (mg/l) + case 'rdO2C', name = 'DOXY'; % Oxyguard dissolved O2 (%) case 'D_O2', name = 'DOXS'; - % Oxyguard dissolved O2 concentration (ml/l) => (umol/l) - case 'dO2C' - name = 'DOX1'; - comment.(vars{k}) = ['Originally expressed in ml/l, ' ... - '1ml/l = 44.660umol/l was assumed.']; - data.(vars{k}) = data.(vars{k}) * 44.660; + % Oxyguard dissolved O2 concentration (ml/l) + case 'dO2C', name = 'DOX'; + end if ~isempty(name) @@ -326,154 +319,7 @@ sample_data.variables{end }.coordinates = 'TIME LATITUDE LONGITUDE DEPTH'; end end - end - - % Let's add DOX1/DOX2 if PSAL/CNDC, TEMP and DOXS are present and DOX1 not - % already present - doxs = getVar(sample_data.variables, 'DOXS'); - dox1 = getVar(sample_data.variables, 'DOX1'); - if doxs ~= 0 && dox1 == 0 - doxs = sample_data.variables{doxs}; - name = 'DOX1'; - - % to perform this conversion, we need temperature, - % and salinity/conductivity+pressure data to be present - temp = getVar(sample_data.variables, 'TEMP'); - psal = getVar(sample_data.variables, 'PSAL'); - cndc = getVar(sample_data.variables, 'CNDC'); - pres = getVar(sample_data.variables, 'PRES'); - - % if any of this data isn't present, - % we can't perform the conversion - if temp ~= 0 && (psal ~= 0 || (cndc ~= 0 && pres ~= 0)) - temp = sample_data.variables{temp}; - if psal ~= 0 - psal = sample_data.variables{psal}; - else - cndc = sample_data.variables{cndc}; - pres = sample_data.variables{pres}; - % conductivity is in S/m and gsw_C3515 in mS/cm - crat = 10*cndc.data ./ gsw_C3515; - - % we need to use relative pressure using gsw_P0 = 101325 Pa - psal.data = gsw_SP_from_R(crat, temp.data, pres.data - gsw_P0/10^4); - end - - % O2 solubility (Garcia and Gordon, 1992-1993) - % - solubility = O2sol(psal.data, temp.data, 'ml/l'); - - % O2 saturation to O2 concentration measured - % O2 saturation (per cent) = 100* [O2/O2sol] - % - % that is to say : O2 = O2sol * O2sat / 100 - data = solubility .* doxs.data / 100; - - % conversion from ml/l to umol/l - data = data * 44.660; - comment = ['Originally expressed in % of saturation, using Garcia '... - 'and Gordon equations (1992-1993) and ml/l coefficients, assuming 1ml/l = 44.660umol/l.']; - - sample_data.variables{end+1}.dimensions = [1 dimensions]; - sample_data.variables{end}.comment = comment; - sample_data.variables{end}.name = name; - sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); - if nA == 0 - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(data(iD)); - else - % we need to padd data with NaNs so that we fill MAXZ - % dimension - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc([[data(iD); dNaN], [data(~iD); aNaN]]); - end - sample_data.variables{end}.coordinates = 'TIME LATITUDE LONGITUDE DEPTH'; - - % Let's add DOX2 - name = 'DOX2'; - - % O2 solubility (Garcia and Gordon, 1992-1993) - % - solubility = O2sol(psal.data, temp.data, 'umol/kg'); - - % O2 saturation to O2 concentration measured - % O2 saturation (per cent) = 100* [O2/O2sol] - % - % that is to say : O2 = O2sol * O2sat / 100 - data = solubility .* doxs.data / 100; - comment = ['Originally expressed in % of saturation, using Garcia '... - 'and Gordon equations (1992-1993) and umol/kg coefficients.']; - - sample_data.variables{end+1}.dimensions = [1 dimensions]; - sample_data.variables{end}.comment = comment; - sample_data.variables{end}.name = name; - sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); - if nA == 0 - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(data(iD)); - else - % we need to padd data with NaNs so that we fill MAXZ - % dimension - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc([[data(iD); dNaN], [data(~iD); aNaN]]); - end - sample_data.variables{end}.coordinates = 'TIME LATITUDE LONGITUDE DEPTH'; - end - end - - % Let's add a new parameter if DOX1, PSAL/CNDC, TEMP and PRES are - % present and DOX2 not already present - dox1 = getVar(sample_data.variables, 'DOX1'); - dox2 = getVar(sample_data.variables, 'DOX2'); - if dox1 ~= 0 && dox2 == 0 - dox1 = sample_data.variables{dox1}; - name = 'DOX2'; - - % umol/l -> umol/kg - % - % to perform this conversion, we need to calculate the - % density of sea water; for this, we need temperature, - % salinity, and pressure data to be present - temp = getVar(sample_data.variables, 'TEMP'); - pres = getVar(sample_data.variables, 'PRES'); - psal = getVar(sample_data.variables, 'PSAL'); - cndc = getVar(sample_data.variables, 'CNDC'); - - % if any of this data isn't present, - % we can't perform the conversion to umol/kg - if temp ~= 0 && pres ~= 0 && (psal ~= 0 || cndc ~= 0) - temp = sample_data.variables{temp}; - pres = sample_data.variables{pres}; - if psal ~= 0 - psal = sample_data.variables{psal}; - else - cndc = sample_data.variables{cndc}; - % conductivity is in S/m and gsw_C3515 in mS/cm - crat = 10*cndc.data ./ gsw_C3515; - - % we need to use relative pressure using gsw_P0 = 101325 Pa - psal.data = gsw_SP_from_R(crat, temp.data, pres.data - gsw_P0/10^4); - end - - % calculate density from salinity, temperature and pressure - dens = sw_dens(psal.data, temp.data, pres.data - gsw_P0/10^4); % cannot use the GSW SeaWater library TEOS-10 as we don't know yet the position - - % umol/l -> umol/kg (dens in kg/m3 and 1 m3 = 1000 l) - data = dox1.data .* 1000.0 ./ dens; - comment = ['Originally expressed in mg/l, assuming O2 density = 1.429kg/m3, 1ml/l = 44.660umol/l '... - 'and using density computed from Temperature, Salinity and Pressure '... - 'with the CSIRO SeaWater library (EOS-80) v1.1.']; - - sample_data.variables{end+1}.dimensions = [1 dimensions]; - sample_data.variables{end}.comment = comment; - sample_data.variables{end}.name = name; - sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); - if nA == 0 - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(data(iD)); - else - % we need to padd data with NaNs so that we fill MAXZ - % dimension - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc([[data(iD); dNaN], [data(~iD); aNaN]]); - end - sample_data.variables{end}.coordinates = 'TIME LATITUDE LONGITUDE DEPTH'; - end - end + end case 'timeSeries' sample_data.dimensions{1}.name = 'TIME'; @@ -554,22 +400,15 @@ %Speed of sound (m/s) case 'SoSUN', name = 'SSPD'; - %Rinko dissolved O2 concentration (mg/l) => (umol/l) - case 'rdO2C' - name = 'DOX1'; - comment.(fields{k}) = ['Originally expressed in mg/l, ' ... - 'O2 density = 1.429kg/m3 and 1ml/l = 44.660umol/l were assumed.']; - data.(fields{k}) = data.(fields{k}) * 44.660/1.429; % O2 density = 1.429 kg/m3 + %Rinko dissolved O2 concentration (mg/l) + case 'rdO2C', name = 'DOXY'; % Oxyguard dissolved O2 (%) case 'D_O2', name = 'DOXS'; - % Oxyguard dissolved O2 concentration (ml/l) => (umol/l) - case 'dO2C' - name = 'DOX1'; - comment.(fields{k}) = ['Originally expressed in ml/l, ' ... - '1ml/l = 44.660umol/l was assumed.']; - data.(fields{k}) = data.(fields{k}) * 44.660; + % Oxyguard dissolved O2 concentration (ml/l) + case 'dO2C', name = 'DOX'; + end if ~isempty(name) @@ -583,136 +422,8 @@ sample_data.variables{end}.comment = comment.(fields{k}); end end - - % Let's add DOX1/DOX2 if PSAL/CNDC, TEMP and DOXS are present and DOX1 not - % already present - doxs = getVar(sample_data.variables, 'DOXS'); - dox1 = getVar(sample_data.variables, 'DOX1'); - if doxs ~= 0 && dox1 == 0 - doxs = sample_data.variables{doxs}; - name = 'DOX1'; - - % to perform this conversion, we need temperature, - % and salinity/conductivity+pressure data to be present - temp = getVar(sample_data.variables, 'TEMP'); - psal = getVar(sample_data.variables, 'PSAL'); - cndc = getVar(sample_data.variables, 'CNDC'); - pres = getVar(sample_data.variables, 'PRES'); - - % if any of this data isn't present, - % we can't perform the conversion - if temp ~= 0 && (psal ~= 0 || (cndc ~= 0 && pres ~= 0)) - temp = sample_data.variables{temp}; - if psal ~= 0 - psal = sample_data.variables{psal}; - else - cndc = sample_data.variables{cndc}; - pres = sample_data.variables{pres}; - % conductivity is in S/m and gsw_C3515 in mS/cm - crat = 10*cndc.data ./ gsw_C3515; - - % we need to use relative pressure using gsw_P0 = 101325 Pa - psal.data = gsw_SP_from_R(crat, temp.data, pres.data - gsw_P0/10^4); - end - - % O2 solubility (Garcia and Gordon, 1992-1993) - % - solubility = O2sol(psal.data, temp.data, 'ml/l'); - - % O2 saturation to O2 concentration measured - % O2 saturation (per cent) = 100* [O2/O2sol] - % - % that is to say : O2 = O2sol * O2sat / 100 - data = solubility .* doxs.data / 100; - - % conversion from ml/l to umol/l - data = data * 44.660; - comment = ['Originally expressed in % of saturation, using Garcia '... - 'and Gordon equations (1992-1993) and ml/l coefficients, assuming 1ml/l = 44.660umol/l.']; - - sample_data.variables{end+1}.dimensions = 1; - sample_data.variables{end}.comment = comment; - sample_data.variables{end}.name = name; - sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(data); - sample_data.variables{end}.coordinates = coordinates; - - % Let's add DOX2 - name = 'DOX2'; - - % O2 solubility (Garcia and Gordon, 1992-1993) - % - solubility = O2sol(psal.data, temp.data, 'umol/kg'); - - % O2 saturation to O2 concentration measured - % O2 saturation (per cent) = 100* [O2/O2sol] - % - % that is to say : O2 = O2sol * O2sat / 100 - data = solubility .* doxs.data / 100; - comment = ['Originally expressed in % of saturation, using Garcia '... - 'and Gordon equations (1992-1993) and umol/kg coefficients.']; - - sample_data.variables{end+1}.dimensions = 1; - sample_data.variables{end}.comment = comment; - sample_data.variables{end}.name = name; - sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(data); - sample_data.variables{end}.coordinates = coordinates; - end - end - - % Let's add a new parameter if DOX1, PSAL/CNDC, TEMP and PRES are - % present and DOX2 not already present - dox1 = getVar(sample_data.variables, 'DOX1'); - dox2 = getVar(sample_data.variables, 'DOX2'); - if dox1 ~= 0 && dox2 == 0 - dox1 = sample_data.variables{dox1}; - name = 'DOX2'; - - % umol/l -> umol/kg - % - % to perform this conversion, we need to calculate the - % density of sea water; for this, we need temperature, - % salinity, and pressure data to be present - temp = getVar(sample_data.variables, 'TEMP'); - pres = getVar(sample_data.variables, 'PRES'); - psal = getVar(sample_data.variables, 'PSAL'); - cndc = getVar(sample_data.variables, 'CNDC'); - - % if any of this data isn't present, - % we can't perform the conversion to umol/kg - if temp ~= 0 && pres ~= 0 && (psal ~= 0 || cndc ~= 0) - temp = sample_data.variables{temp}; - pres = sample_data.variables{pres}; - if psal ~= 0 - psal = sample_data.variables{psal}; - else - cndc = sample_data.variables{cndc}; - % conductivity is in S/m and gsw_C3515 in mS/cm - crat = 10*cndc.data ./ gsw_C3515; - - % we need to use relative pressure using gsw_P0 = 101325 Pa - psal.data = gsw_SP_from_R(crat, temp.data, pres.data - gsw_P0/10^4); - end - - % calculate density from salinity, temperature and pressure - dens = sw_dens(psal.data, temp.data, pres.data - gsw_P0/10^4); % cannot use the GSW SeaWater library TEOS-10 as we don't know yet the position - - % umol/l -> umol/kg (dens in kg/m3 and 1 m3 = 1000 l) - data = dox1.data .* 1000.0 ./ dens; - comment = ['Originally expressed in mg/l, assuming O2 density = 1.429kg/m3, 1ml/l = 44.660umol/l '... - 'and using density computed from Temperature, Salinity and Pressure '... - 'with the CSIRO SeaWater library (EOS-80) v1.1.']; - - sample_data.variables{end+1}.dimensions = 1; - sample_data.variables{end}.comment = comment; - sample_data.variables{end}.name = name; - sample_data.variables{end}.typeCastFunc = str2func(netcdf3ToMatlabType(imosParameters(sample_data.variables{end}.name, 'type'))); - sample_data.variables{end}.data = sample_data.variables{end}.typeCastFunc(data); - sample_data.variables{end}.coordinates = coordinates; - end - end - end + + end end function header = readHeader(fid) diff --git a/Parser/workhorseParse.m b/Parser/workhorseParse.m index 1c51ad537..3b5b1627c 100644 --- a/Parser/workhorseParse.m +++ b/Parser/workhorseParse.m @@ -215,16 +215,17 @@ heading = heading / 100.0; % check for electrical/magnetic heading bias (usually magnetic declination) - isMagBias = false; + magExt = '_MAG'; + magBiasComment = ''; % we set a static value for this variable to the most frequent value found magDec = mode(fixed.headingBias)*0.01; % Scaling: LSD = 0.01degree; Range = -179.99 to 180.00degrees if magDec ~= 0 - isMagBias = true; + magExt = ''; magBiasComment = ['A compass correction of ' num2str(magDec) ... 'degrees has been applied to the data by a technician using RDI''s software ' ... '(usually to account for magnetic declination).']; end - + speed = sqrt(vnrth.^2 + veast.^2); direction = getDirectionFromUV(veast, vnrth); @@ -332,12 +333,6 @@ sample_data.dimensions{1}.seconds_to_middle_of_measurement = sample_data.meta.instrument_average_interval/2; % add variables with their dimensions and data mapped - if isMagBias - magExt = ''; - else - magExt = '_MAG'; - end - vars = { 'TIMESERIES', [], 1; ... 'LATITUDE', [], NaN; ... @@ -349,10 +344,10 @@ 'ECUR', [1 2], evel; ... 'CSPD', [1 2], speed; ... ['CDIR' magExt], [1 2], direction; ... - 'ABSIC1', [1 3], backscatter1; ... - 'ABSIC2', [1 3], backscatter2; ... - 'ABSIC3', [1 3], backscatter3; ... - 'ABSIC4', [1 3], backscatter4; ... + 'ABSIC1', [1 3], backscatter1; ... + 'ABSIC2', [1 3], backscatter2; ... + 'ABSIC3', [1 3], backscatter3; ... + 'ABSIC4', [1 3], backscatter4; ... 'TEMP', 1, temperature; ... 'PRES_REL', 1, pressure; ... 'PSAL', 1, salinity; ... diff --git a/Preprocessing/Transform/sbe43OxygenTransform.m b/Preprocessing/Transform/sbe43OxygenTransform.m index 1f6172237..5ccb2e8dc 100644 --- a/Preprocessing/Transform/sbe43OxygenTransform.m +++ b/Preprocessing/Transform/sbe43OxygenTransform.m @@ -16,8 +16,8 @@ % the raw oxygen voltage data. % % Outputs: -% data - array of oxygen concentration values, umol/l -% name - new variable name 'DOX1' +% data - array of oxygen concentration values, ml/l +% name - new variable name 'DOX' % comment - string which can be used for variable comment % history - string which can be used for the dataset history % @@ -65,25 +65,25 @@ if presRel == 0 pres = getVar(sam.variables, 'PRES'); end - doxy = varIdx; + doVolt = varIdx; - if ~(temp && psal && (presRel || pres) && doxy), return; end + if ~(temp && psal && (presRel || pres) && doVolt), return; end - data = sam.variables{doxy}.data; - name = sam.variables{doxy}.name; - comment = sam.variables{doxy}.comment; + data = sam.variables{doVolt}.data; + name = sam.variables{doVolt}.name; + comment = sam.variables{doVolt}.comment; history = sam.history; % measured parameters T = sam.variables{temp}.data; if presRel == 0 P = sam.variables{pres}.data; - P = P - 14.7*0.689476; + P = P - 14.7*0.689476; % SeaBird's atmospheric correction else P = sam.variables{presRel}.data; end S = sam.variables{psal}.data; - V = sam.variables{doxy}.data; + V = sam.variables{doVolt}.data; % calibration coefficients params.Soc = 1.0; @@ -100,7 +100,13 @@ params.H3 = 1450; % calculated values - params.oxsol = oxygenSolubility(T, S); + [lat, lon] = getLatLonForGSW(sam); + + if isempty(lat) || isempty(lon), return; end % cancelled by user + + SA = gsw_SA_from_SP(S, P, lon, lat); + potT = gsw_pt0_from_t(SA, T, P); + params.oxsol = gsw_O2sol_SP_pt(S, potT); params.dVdt = [0.0; diff(V)]; params.tauTP = params.Tau20 * exp(params.D1 .* P + params.D2 .* (T - 20.0)); params.K = T + 273.15; @@ -108,17 +114,13 @@ V = hysteresisCorrection(V, T, P, params); data = oxygenConcentration(V, T, S, P, params); - % convert from ml/l to umol/l - % - % Conversion factors from Saunders (1986) : - % https://darchive.mblwhoilibrary.org/bitstream/handle/1912/68/WHOI-89-23.pdf?sequence=3 - % 1ml/l = 43.57 umol/kg (with dens = 1.025 kg/l) - % 1ml/l = 44.660 umol/l - - data = data .* 44.660; - - name = 'DOX1'; - sbe43OxygenTransformComment = ['sbe43OxygenTransform: ' name ' derived from ' sam.variables{doxy}.name '.']; + name = 'DOX'; + sbe43OxygenTransformComment = ['sbe43OxygenTransform: ' name ' derived from ' ... + sam.variables{doVolt}.name ' following SeaBird application note 64 ' ... + '(http://www.seabird.com/application_notes/AN64-3.htm). Oxygen ' ... + 'solubility at atmospheric pressure was derived from TEMP, PSAL, ' presName ... + ', LATITUDE and LONGITUDE using gsw_O2sol_SP_pt, gsw_pt0_from_t ' ... + 'and gsw_SA_from_SP from the Gibbs-SeaWater toolbox (TEOS-10) v3.06.']; if isempty(comment) comment = sbe43OxygenTransformComment; else @@ -170,52 +172,6 @@ exp(E .* P ./ K); end - -function oxsol = oxygenSolubility(T, S) -%OXYGENSOLUBILITY Computes oxygen solubility for the given temperature and -%salinity. -% -% This function is an implementation of the Computation of Oxygen -% Solubility equation, as specified in Seabird Application Note 64, -% Appendix A. -% -% Inputs: -% T - temperature, degrees celsius -% S - Salinity, PSU -% -% Outputs: -% oxsol - Oxygen solubility -% - - A0 = 2.00907; - A1 = 3.22014; - A2 = 4.0501; - A3 = 4.94457; - A4 = 0.256847; - A5 = 3.88767; - B0 = -0.00624523; - B1 = -0.00737614; - B2 = -0.010341; - B3 = -0.00817083; - C0 = -0.000000488682; - - Ts = log((298.15 - T) ./ (273.15 + T)); - - oxsol = exp(... - A0 + ... - A1 .* Ts + ... - A2 .* Ts.^2 + ... - A3 .* Ts.^3 + ... - A4 .* Ts.^4 + ... - A5 .* Ts.^5 + ... - S .* ( B0 + ... - B1 .* Ts + ... - B2 .* Ts.^2 + ... - B3 .* Ts.^3 ) + ... - C0 .* S.^2); - -end - function V = hysteresisCorrection(V, T, P, params) %HYSTERESISCORRECTION Applies hysteresis correction on the given oxygen % voltage data. diff --git a/Preprocessing/depthPP.m b/Preprocessing/depthPP.m index ab3254b7d..0e93607e4 100644 --- a/Preprocessing/depthPP.m +++ b/Preprocessing/depthPP.m @@ -81,6 +81,7 @@ % check wether height or target depth information is documented isSensorHeight = false; isSensorTargetDepth = false; +isSiteTargetDepth = false; if isfield(sample_data{1}, 'instrument_nominal_height') if ~isempty(sample_data{1}.instrument_nominal_height) @@ -94,6 +95,18 @@ end end +if isfield(sample_data{1}, 'site_nominal_depth') + if ~isempty(sample_data{1}.site_nominal_depth) + isSiteTargetDepth = true; + end +end + +if isfield(sample_data{1}, 'site_depth_at_deployment') + if ~isempty(sample_data{1}.site_depth_at_deployment) + isSiteTargetDepth = true; + end +end + % read options from parameter file depthFile = ['Preprocessing' filesep 'depthPP.txt']; same_family = readProperty('same_family', depthFile, ','); @@ -161,7 +174,7 @@ useItsOwnPresRel(iCurSam) = readDatasetParameter(sample_data{iCurSam}.toolbox_input_file, currentPProutine, 'useItsOwnPresRel', useItsOwnPresRel(iCurSam)); % look for nearest instruments with depth or pressure information - if ~isProfile && (isSensorHeight || isSensorTargetDepth) + if ~isProfile && (isSensorHeight || isSensorTargetDepth) && isSiteTargetDepth % let's see if part of a mooring with pressure data from other % sensors for iOtherSam = 1:nDatasets @@ -312,9 +325,9 @@ else if ~isProfile fprintf('%s\n', ['Warning : ' sample_data{iCurSam}.toolbox_input_file ... - ' please document either instrument_nominal_height or instrument_nominal_depth '... + ' please document site_nominal_depth or site_depth_at_deployment and either instrument_nominal_height or instrument_nominal_depth '... 'global attributes so that an actual depth can be '... - 'computed from other pressure sensors in the mooring']); + 'computed from 1 or 2 other pressure sensors in the mooring']); end continue; end @@ -532,7 +545,7 @@ computedDepth = - gsw_z_from_p(relPres, sample_data{iCurSam}.geospatial_lat_min); clear relPres; computedDepthComment = ['depthPP: Depth computed using the ' ... - 'Gibbs-SeaWater toolbox (TEOS-10) v3.05 from latitude and ' ... + 'Gibbs-SeaWater toolbox (TEOS-10) v3.06 from latitude and ' ... presComment '.']; else % latitude does change in the dataset, so we use the mean @@ -543,7 +556,7 @@ computedDepth = - gsw_z_from_p(relPres, meanLat); clear relPres; computedDepthComment = ['depthPP: Depth computed using the ' ... - 'Gibbs-SeaWater toolbox (TEOS-10) v3.05 from mean latitude and ' ... + 'Gibbs-SeaWater toolbox (TEOS-10) v3.06 from mean latitude and ' ... presComment '.']; end else @@ -556,7 +569,7 @@ else % if no pressure data, try to compute it from other sensors in the % mooring, otherwise go to next sample data - if isSensorHeight || isSensorTargetDepth + if (isSensorHeight || isSensorTargetDepth) && isSiteTargetDepth if isempty(nearestInsts{iCurSam}) fprintf('%s\n', ['Warning : ' descSam{iCurSam} ... ' has no neighbouring pressure sensor on this mooring from ' ... @@ -566,10 +579,11 @@ iFirst = firstNearestInst(iCurSam); iSecond = secondNearestInst(iCurSam); - if iSecond == 0 && iFirst ~= 0 + if iSecond == 0 && iFirst ~= 0 + tidalAmplitudeComment = ' Tidal amplitude is not accurate.'; fprintf('%s\n', ['Warning : ' descSam{iCurSam} ... ' has its actual depth inferred from only one neighbouring pressure sensor ' ... - 'on mooring']); + 'on mooring.' tidalAmplitudeComment]); % we found only one sensor presIdxOther = getVar(sample_data{nearestInsts{iCurSam}(iFirst)}.variables, 'PRES'); presRelIdxOther = getVar(sample_data{nearestInsts{iCurSam}(iFirst)}.variables, 'PRES_REL'); @@ -593,34 +607,43 @@ 'precision at a deployed depth)']; end - % compute pressure at current sensor assuming sensors - % repartition on a vertical line between current sensor - % and the nearest. This is the best we can do as we can't - % have any idea of the angle of the mooring with one - % pressure sensor (could consider the min pressure value - % in the future?). - % - % computedDepth = depthOther + distOtherCurSensor - % computedHeight = heightOther - distOtherCurSensor - % - % vertical axis is positive down when talking about - % depth + % compute pressure at current sensor using trigonometry and + % assuming sensors repartition on a line between the + % nearest pressure sensor and the mooring's anchor % + % the only drawback is that tidal amplitude is + % flattenned by static value of siteDepth + if isfield(sample_data{iCurSam}, 'site_nominal_depth') + if ~isempty(sample_data{iCurSam}.site_nominal_depth) + siteDepth = sample_data{iCurSam}.site_nominal_depth; + end + end + + if isfield(sample_data{iCurSam}, 'site_depth_at_deployment') + if ~isempty(sample_data{iCurSam}.site_depth_at_deployment) + siteDepth = sample_data{iCurSam}.site_depth_at_deployment; + end + end + if isSensorTargetDepth - distOtherCurSensor = sample_data{iCurSam}.instrument_nominal_depth - sample_data{nearestInsts{iCurSam}(iFirst)}.instrument_nominal_depth; - signOtherCurSensor = sign(distOtherCurSensor); - - distOtherCurSensor = abs(distOtherCurSensor); + nominalHeightOther = siteDepth - sample_data{nearestInsts{iCurSam}(iFirst)}.instrument_nominal_depth; + nominalHeightCurSensor = siteDepth - sample_data{iCurSam}.instrument_nominal_depth; else - distOtherCurSensor = sample_data{nearestInsts{iCurSam}(iFirst)}.instrument_nominal_height - sample_data{iCurSam}.instrument_nominal_height; - signOtherCurSensor = -sign(distOtherCurSensor); - % 0 => two sensors at the same depth - % 1 => current sensor is deeper than other sensor - % -1 => current sensor is lower than other sensor - - distOtherCurSensor = abs(distOtherCurSensor); + nominalHeightOther = sample_data{nearestInsts{iCurSam}(iFirst)}.instrument_nominal_height; + nominalHeightCurSensor = sample_data{iCurSam}.instrument_nominal_height; end + % theta is the angle between the vertical and line + % formed by the sensors + % + % cos(theta) = heightOther/nominalHeightOther + % and + % cos(theta) = heightCurSensor/nominalHeightCurSensor + % + % computedDepth = nominalSiteDepth - nominalHeightCurSensor * (nominalSiteDepth - zOther) / nominalHeightOther + % + % pressure = density*gravity*depth + % if ~isempty(sample_data{iCurSam}.geospatial_lat_min) && ~isempty(sample_data{iCurSam}.geospatial_lat_max) % compute depth with Gibbs-SeaWater toolbox % depth ~= - gsw_z_from_p(relative_pressure, latitude) @@ -628,20 +651,20 @@ zOther = - gsw_z_from_p(relPresOther, sample_data{iCurSam}.geospatial_lat_min); computedDepthComment = ['depthPP: Depth inferred from only one neighbouring pressure sensor ' ... descOtherSam{iCurSam}{iFirst + 1} ', using the Gibbs-SeaWater toolbox ' ... - '(TEOS-10) v3.05 from latitude and ' presComment '.']; + '(TEOS-10) v3.06 from latitude and ' presComment '.' tidalAmplitudeComment]; else meanLat = sample_data{iCurSam}.geospatial_lat_min + ... (sample_data{iCurSam}.geospatial_lat_max - sample_data{iCurSam}.geospatial_lat_min)/2; zOther = - gsw_z_from_p(relPresOther, meanLat); computedDepthComment = ['depthPP: Depth inferred from only one neighbouring pressure sensor ' ... descOtherSam{iCurSam}{iFirst + 1} ', using the Gibbs-SeaWater toolbox ' ... - '(TEOS-10) v3.05 from mean latitude and ' presComment '.']; + '(TEOS-10) v3.06 from mean latitude and ' presComment '.' tidalAmplitudeComment]; end else % without latitude information, we assume 1dbar ~= 1m zOther = relPresOther; computedDepthComment = ['depthPP: Depth inferred from only one neighbouring pressure sensor ' ... - descOtherSam{iCurSam}{iFirst + 1} ' with ' presComment ', assuming 1dbar ~= 1m.']; + descOtherSam{iCurSam}{iFirst + 1} ' with ' presComment ', assuming 1dbar ~= 1m.' tidalAmplitudeComment]; end clear relPresOther; @@ -653,7 +676,7 @@ zOther = interp1(tOther, zOther, tCur); clear tOther tCur; - computedDepth = zOther + signOtherCurSensor*distOtherCurSensor; + computedDepth = siteDepth - nominalHeightCurSensor * (siteDepth - zOther) / nominalHeightOther; clear zOther; elseif iSecond ~= 0 && iFirst ~= 0 presIdxFirst = getVar(sample_data{nearestInsts{iCurSam}(iFirst)}.variables, 'PRES'); @@ -730,7 +753,7 @@ computedDepthComment = ['depthPP: Depth inferred from ' ... '2 neighbouring pressure sensors ' descOtherSam{iCurSam}{iFirst + 1} ... ' and ' descOtherSam{iCurSam}{iSecond + 1} ', using the ' ... - 'Gibbs-SeaWater toolbox (TEOS-10) v3.05 from latitude and ' ... + 'Gibbs-SeaWater toolbox (TEOS-10) v3.06 from latitude and ' ... presComment '.']; else meanLat = sample_data{iCurSam}.geospatial_lat_min + ... @@ -742,7 +765,7 @@ computedDepthComment = ['depthPP: Depth inferred from ' ... '2 neighbouring pressure sensors ' descOtherSam{iCurSam}{iFirst + 1} ... ' and ' descOtherSam{iCurSam}{iSecond + 1} ', using the ' ... - 'Gibbs-SeaWater toolbox (TEOS-10) v3.05 from mean latitude and ' ... + 'Gibbs-SeaWater toolbox (TEOS-10) v3.06 from mean latitude and ' ... presComment '.']; end else @@ -790,9 +813,9 @@ end else fprintf('%s\n', ['Warning : ' sample_data{iCurSam}.toolbox_input_file ... - ' please document either instrument_nominal_height or instrument_nominal_depth ' ... + ' please document site_nominal_depth or site_depth_at_deployment and either instrument_nominal_height or instrument_nominal_depth ' ... 'global attributes so that an actual depth can be ' ... - 'computed from other pressure sensors in the mooring']); + 'computed from 1 or 2 other pressure sensors in the mooring']); continue; end diff --git a/Preprocessing/magneticDeclinationPP.m b/Preprocessing/magneticDeclinationPP.m index c73b79a98..38d9dd330 100644 --- a/Preprocessing/magneticDeclinationPP.m +++ b/Preprocessing/magneticDeclinationPP.m @@ -86,7 +86,9 @@ end if any(iMagDataSet == i) - if isempty(sample_data{i}.instrument_nominal_depth) + if isempty(sample_data{i}.instrument_nominal_depth) || ... + isempty(sample_data{i}.geospatial_lat_min) || ... + isempty(sample_data{i}.geospatial_lon_min) disp(['Warning : no instrument_nominal_depth/geospatial_lat_min/geospatial_lon_min documented for magneticDeclinationPP to be applied on ' sample_data{i}.toolbox_input_file]); prompt = {'Depth:', 'Latitude (South -ve)', 'Longitude (West -ve)'}; dlg_title = 'Coords'; diff --git a/Preprocessing/oxygenPP.m b/Preprocessing/oxygenPP.m new file mode 100644 index 000000000..a88474bef --- /dev/null +++ b/Preprocessing/oxygenPP.m @@ -0,0 +1,268 @@ +function sample_data = oxygenPP( sample_data, qcLevel, auto ) +%OXYGENPP adds an oxygen solubility at atmospheric pressure variable +% OXSOL_SURFACE and other oxygen data to the given data sets. +% +% This function uses the Gibbs-SeaWater toolbox (TEOS-10) to derive consistent oxygen +% data when possible from salinity, temperature and pressure. It adds oxygen +% data as new variables in the data sets. Data sets which do not contain +% oxygen, temperature, pressure and depth variables or nominal depth +% information are left unmodified. +% +% From : http://doi.org/10.13155/39795 (note: Argo DOXY is IMOS DOX2) +% +% The unit of DOXY is umol/kg in Argo data and the oxygen measurements are sent from Argo floats in +% another unit such as umol/L for the Optode and ml/L for the SBE-IDO. Thus the unit conversion is carried out by DACs as follows: +% O2 [umol/L] = 44.6596 . O2 [ml/L] +% O2 [umol/kg] = O2 [umol/L] / pdens +% Here, pdens is the potential density of water [kg/L] at zero pressure and at the potential temperature +% (e.g., 1.0269 kg/L; e.g., UNESCO, 1983). The value of 44.6596 is derived from the molar volume of the oxygen gas, 22.3916 L/mole, +% at standard temperature and pressure (0 C, 1 atmosphere; e.g., Garcia and Gordon, 1992). +% This unit conversion follows the "Recommendations on the conversion between oxygen quantities for Bio-Argo floats and other autonomous sensor platforms" +% by SCOR Working Group 142 on "Quality Control Procedures for Oxygen and Other Biogeochemical Sensors on Floats and Gliders" [RD13] . +% The unit conversion should always be done with the best available temperature, i.e., TEMP of the CTD unit. +% +% Inputs: +% sample_data - cell array of data sets, ideally with conductivity, +% temperature and pressure variables. +% qcLevel - string, 'raw' or 'qc'. Some pp not applied when 'raw'. +% auto - logical, run pre-processing in batch mode. +% +% Input Parameters +% TEMP, PSAL, PRES_REL/PRES or DEPTH/nominal_depth +% DOX +% DOXY +% DOXS +% DOX1 +% DOX2 +% +% Outputs: +% sample_data - the same data sets, with oxygen (umol/kg) +% oxygen saturation, oxygen solubility variables added. +% +% Output Parameters +% OXSOL_SURFACE +% DOX1 +% DOX2 +% DOXS + +% +% Author: Peter Jansen +% Contributor: Guillaume Galibert +% + +% +% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated +% Marine Observing System (IMOS). +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% * Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in the +% documentation and/or other materials provided with the distribution. +% * Neither the name of the AODN/IMOS nor the names of its contributors +% may be used to endorse or promote products derived from this software +% without specific prior written permission. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. +% +narginchk(2, 3); + +if ~iscell(sample_data), error('sample_data must be a cell array'); end +if isempty(sample_data), return; end + +% no modification of data is performed on the raw FV00 dataset except +% local time to UTC conversion +if strcmpi(qcLevel, 'raw'), return; end + +% auto logical in input to enable running under batch processing +if nargin<3, auto=false; end + +for k = 1:length(sample_data) + + sam = sample_data{k}; + + tempIdx = getVar(sam.variables, 'TEMP'); + psalIdx = getVar(sam.variables, 'PSAL'); + + [presRel, presName] = getPresRelForGSW(sam); + + doxIdx = getVar(sam.variables, 'DOX'); + doxyIdx = getVar(sam.variables, 'DOXY'); + dox1Idx = getVar(sam.variables, 'DOX1'); + dox2Idx = getVar(sam.variables, 'DOX2'); + doxsIdx = getVar(sam.variables, 'DOXS'); + + % cancel if psal, temp, pres/pres_rel or depth/nominal depth and any DO parameter not present in data set + if ~(psalIdx && tempIdx && ~isempty(presName) && (doxIdx || doxyIdx || dox1Idx || dox2Idx || doxsIdx)), continue; end + + % data set already contains dissolved oxygen DOX1/DOX2 or DOXS + if (dox1Idx && dox2Idx && doxsIdx), continue; end + + temp = sam.variables{tempIdx}.data; + psal = sam.variables{psalIdx}.data; + + % calculate the potential temperature with a reference sea pressure of 0dbar + [lat, lon] = getLatLonForGSW(sam); + + if isempty(lat) || isempty(lon), continue; end % cancelled by user + + SA = gsw_SA_from_SP(psal, presRel, lon, lat); + potTemp = gsw_pt0_from_t(SA, temp, presRel); + + % calculate the potential density at 0dbar from potential temperature + CT = gsw_CT_from_pt(SA, potTemp); + potDens = gsw_rho(SA, CT, 0); + potDensComment = ['Potential density at atmospheric ' ... + 'pressure was derived from TEMP, PSAL, ' presName ', LATITUDE ' ... + 'and LONGITUDE using gsw_rho, gsw_SA_from_SP, gsw_CT_from_pt and gsw_pt0_from_t ' ... + 'from the Gibbs-SeaWater toolbox (TEOS-10) v3.06.']; + + % get dimensions and coordinates values from temperature + dimensions = sam.variables{tempIdx}.dimensions; + if isfield(sam.variables{tempIdx}, 'coordinates') + coordinates = sam.variables{tempIdx}.coordinates; + else + coordinates = ''; + end + + % calculate oxygen solubility at atmospheric pressure + oxsolSurf = gsw_O2sol_SP_pt(psal, potTemp); % sea bird calculates OXSOL using psal and local temperature, not potential temperature + oxsolSurfComment = ['OXSOL_SURFACE derived from TEMP, PSAL, ' presName ... + ', LATITUDE and LONGITUDE using gsw_O2sol_SP_pt, gsw_pt0_from_t ' ... + 'and gsw_SA_from_SP from the Gibbs-SeaWater toolbox (TEOS-10) v3.06.']; + + % add oxygen solubility at atmospheric pressure as new variable in data set + sample_data{k} = addVar(... + sample_data{k}, ... + 'OXSOL_SURFACE', ... + oxsolSurf, ... + dimensions, ... + ['oxygenPP.m: ' oxsolSurfComment], ... + coordinates); + + historyComment = oxsolSurfComment; + + SBEProcManualRef = 'See SeaBird data processing manual (http://www.seabird.com/document/sbe-data-processing-manual).'; + argoO2ProcRef = 'See Argo oxygen processing (http://doi.org/10.13155/39795).'; + + % calculate DOX1 when necessary with order of preference from DOX, DOXY, DOX2 and DOXS + dox1Comment = ''; + if dox1Idx == 0 % umol/l + if doxsIdx % percent of saturation + doxs = sam.variables{doxsIdx}.data; + dox1 = doxs .* oxsolSurf .* potDens / 1000; % 1m3 = 1000l + dox1Comment = ['DOX1 derived using DOX1 = DOXS * OXSOL_SURFACE * ' ... + 'potential density / 1000. ' oxsolSurfComment ' ' potDensComment ' ' ... + SBEProcManualRef ' ' argoO2ProcRef]; + end + if dox2Idx % umol/kg + dox2 = sam.variables{dox2Idx}.data; + dox1 = dox2 .* potDens / 1000; % 1m3 = 1000l + dox1Comment = ['DOX1 derived using DOX1 = (DOX2 / 1000) * ' ... + 'potential density. ' potDensComment ' ' argoO2ProcRef]; + end + if doxyIdx % mg/l + doxy = sam.variables{doxyIdx}.data; + dox1 = doxy * (1000 / 31.9988); % O2 molar mass = 31.9988g/mol + dox1Comment = 'DOX1 derived using DOX1 = DOXY * (1000 / 31.9988).'; + end + if doxIdx % ml/l + dox = sam.variables{doxIdx}.data; + dox1 = dox * 44.6596; % 1ml/l = 44.6596umol/l + dox1Comment = 'DOX1 derived using DOX1 = DOX * 44.6596.'; + end + + % add dissolved oxygen in umol/l data as new variable in data set + sample_data{k} = addVar(... + sample_data{k}, ... + 'DOX1', ... + dox1, ... + dimensions, ... + ['oxygenPP.m: ' dox1Comment], ... + coordinates); + + historyComment = [historyComment ' ' dox1Comment]; + end + + % calculate DOX2 when necessary with order of preference from DOX, DOXY, DOX1, DOXS + dox2Comment = ''; + if dox2Idx == 0 % umol/kg + if doxsIdx % percent of saturation + doxs = sam.variables{doxsIdx}.data; + dox2 = doxs / 100 .* oxsolSurf; + dox2Comment = ['DOX2 derived using DOX2 = DOXS / 100 * OXSOL_SURFACE. ' ... + oxsolSurfComment ' ' SBEProcManualRef]; % although SeaBird uses local temperature + end + if dox1Idx % umol/l + dox1 = sam.variables{dox1Idx}.data; + dox2 = dox1 * 1000 ./ potDens; % 1m3 = 1000l + dox2Comment = ['DOX2 derived using DOX2 = DOX1 * 1000 / ' ... + 'potential density. ' potDensComment ' ' argoO2ProcRef]; + end + if doxyIdx % mg/l + doxy = sam.variables{doxyIdx}.data; + dox2 = doxy * (1000 / 31.9988) * 1000 ./ potDens; % O2 molar mass = 31.9988g/mol + dox2Comment = ['DOX2 derived using DOX2 = DOXY * (1000 / 31.9988) / ' ... + 'potential density. ' potDensComment ' ' argoO2ProcRef]; + end + if doxIdx % ml/l + dox = sam.variables{doxIdx}.data; + dox2 = dox * 44.6596 * 1000 ./ potDens; % 1ml/l = 44.6596umol/l + dox2Comment = ['DOX2 derived from using = DOX * 44.6596 * 1000 / ' ... + 'potential density. ' potDensComment ' ' argoO2ProcRef]; + end + + % add dissolved oxygen in umol/kg data as new variable in data set + sample_data{k} = addVar(... + sample_data{k}, ... + 'DOX2', ... + dox2, ... + dimensions, ... + ['oxygenPP.m: ' dox2Comment], ... + coordinates); + + historyComment = [historyComment ' ' dox2Comment]; + else + dox2 = sam.variables{dox2Idx}.data; + end + + % calculate DOXS from DOX2 and OXSOL_SURFACE + doxsComment = ''; + if doxsIdx == 0 + doxs = 100 * dox2 ./ oxsolSurf; + doxsComment = ['DOXS derived using DOXS = 100 * DOX2 / OXSOL_SURFACE. ' ... + oxsolSurfComment ' ' SBEProcManualRef]; % although SeaBird uses local temperature + + % add oxygen saturation in % data as new variable in data set + sample_data{k} = addVar(... + sample_data{k}, ... + 'DOXS', ... + doxs, ... + dimensions, ... + ['oxygenPP.m: ' doxsComment], ... + coordinates); + + historyComment = strtrim([historyComment ' ' doxsComment]); + end + + history = sample_data{k}.history; + if isempty(history) + sample_data{k}.history = sprintf('%s - %s', datestr(now_utc, readProperty('exportNetCDF.dateFormat')), ['oxygenPP.m: ' historyComment]); + else + sample_data{k}.history = sprintf('%s\n%s - %s', history, datestr(now_utc, readProperty('exportNetCDF.dateFormat')), ['oxygenPP.m: ' historyComment]); + end +end diff --git a/Preprocessing/rinkoDoPP.m b/Preprocessing/rinkoDoPP.m index f79d271a2..d5638112a 100644 --- a/Preprocessing/rinkoDoPP.m +++ b/Preprocessing/rinkoDoPP.m @@ -88,8 +88,6 @@ end end - psalIdx = getVar(sam.variables, 'PSAL'); - % volt DO, volt temp DO, and pres/pres_rel or nominal depth not present in data set if ~(voltDOIdx && voltTempDOIdx && (isPresVar || isDepthInfo)), continue; end @@ -159,21 +157,15 @@ % correction for pressure DO = DO.*(1 + E*presRel/100); % pressRel/100 => conversion dBar to MPa (see rinko correction formula pdf). DO is in % of dissolved oxygen during calibration at this stage. - if psalIdx > 0 - psal = sam.variables{psalIdx}.data; - - % conversion in concentration using solubility and following garcia & gourdon (1992) - solubilityDO = O2sol(psal, tempDO, 'ml/l'); - concentDO = solubilityDO.*DO/100; % in ml/l - concentDO = concentDO*44.660; % in umol/l. 1ml/l = 44.660 umol/l. - end - % add DO data as new variable in data set dimensions = sam.variables{voltDOIdx}.dimensions; - doComment = ['rinkoDoPP.m: dissolved oxygen in % of saturation derived from rinko dissolved oxygen and temperature voltages and ' presName ' using the RINKO III Correction method on Temperature and Pressure with instrument and calibration coefficients.']; - concentDoComment = [doComment ' Garcia and Gordon equations (1992-1993) and ml/l coefficients, assuming 1ml/l = 44.660umol/l, have been used to convert in umol/l.']; - tempDoComment = 'rinkoDoPP.m: temperature for dissolved oxygen sensor derived from rinko temperature voltages.'; + doComment = ['rinkoDoPP.m: dissolved oxygen in % of saturation derived ' ... + 'from rinko dissolved oxygen and temperature voltages and ' presName ... + ' using the RINKO III Correction method on Temperature and Pressure ' ... + 'with instrument and calibration coefficients.']; + tempDoComment = ['rinkoDoPP.m: temperature for dissolved oxygen sensor ' ... + 'derived from rinko temperature voltages.']; if isfield(sam.variables{voltDOIdx}, 'coordinates') coordinates = sam.variables{voltDOIdx}.coordinates; @@ -197,24 +189,12 @@ tempDoComment, ... coordinates); - if psalIdx > 0 - sample_data{k} = addVar(... - sample_data{k}, ... - 'DOX1', ... - concentDO, ... - dimensions, ... - concentDoComment, ... - coordinates); - end - history = sample_data{k}.history; if isempty(history) sample_data{k}.history = sprintf('%s - %s', datestr(now_utc, readProperty('exportNetCDF.dateFormat')), doComment); sample_data{k}.history = sprintf('%s\n%s - %s', history, datestr(now_utc, readProperty('exportNetCDF.dateFormat')), tempDoComment); - if psalIdx > 0, sample_data{k}.history = sprintf('%s\n%s - %s', history, datestr(now_utc, readProperty('exportNetCDF.dateFormat')), concentDoComment); end else sample_data{k}.history = sprintf('%s\n%s - %s', history, datestr(now_utc, readProperty('exportNetCDF.dateFormat')), doComment); sample_data{k}.history = sprintf('%s\n%s - %s', history, datestr(now_utc, readProperty('exportNetCDF.dateFormat')), tempDoComment); - if psalIdx > 0, sample_data{k}.history = sprintf('%s\n%s - %s', history, datestr(now_utc, readProperty('exportNetCDF.dateFormat')), concentDoComment); end end end \ No newline at end of file diff --git a/Preprocessing/salinityPP.m b/Preprocessing/salinityPP.m index 4ec5a3d6c..6e6f2edd2 100644 --- a/Preprocessing/salinityPP.m +++ b/Preprocessing/salinityPP.m @@ -67,88 +67,20 @@ sam = sample_data{k}; + % data set already contains salinity + if getVar(sam.variables, 'PSAL'), continue; end + cndcIdx = getVar(sam.variables, 'CNDC'); tempIdx = getVar(sam.variables, 'TEMP'); - presIdx = getVar(sam.variables, 'PRES'); - presRelIdx = getVar(sam.variables, 'PRES_REL'); - isPresVar = logical(presIdx || presRelIdx); - - isDepthInfo = false; - depthType = 'variables'; - depthIdx = getVar(sam.(depthType), 'DEPTH'); - if depthIdx == 0 - depthType = 'dimensions'; - depthIdx = getVar(sam.(depthType), 'DEPTH'); - end - if depthIdx > 0, isDepthInfo = true; end - - if isfield(sam, 'instrument_nominal_depth') - if ~isempty(sam.instrument_nominal_depth) - isDepthInfo = true; - end - end + [presRel, presName] = getPresRelForGSW(sam); - % cndc, temp, and pres/pres_rel or nominal depth not present in data set - if ~(cndcIdx && tempIdx && (isPresVar || isDepthInfo)), continue; end - - % data set already contains salinity - if getVar(sam.variables, 'PSAL'), continue; end + % cndc, temp, and pres/pres_rel or depth/nominal depth not present in data set + if ~(cndcIdx && tempIdx && ~isempty(presName)), continue; end cndc = sam.variables{cndcIdx}.data; temp = sam.variables{tempIdx}.data; - % pressure information used for Salinity computation is from the - % PRES or PRES_REL variables in priority - if isPresVar - if presRelIdx > 0 - presRel = sam.variables{presRelIdx}.data; - presName = 'PRES_REL'; - else - % update from a relative pressure like SeaBird computes - % it in its processed files, substracting a constant value - % 10.1325 dbar for nominal atmospheric pressure - presRel = sam.variables{presIdx}.data - gsw_P0/10^4; - presName = 'PRES substracting a constant value 10.1325 dbar for nominal atmospheric pressure'; - end - else - % when no pressure variable exists, we use depth information either - % from the DEPTH variable or from the instrument_nominal_depth - % global attribute - if depthIdx > 0 - % with depth data - depth = sam.(depthType){depthIdx}.data; - presName = 'DEPTH'; - else - % with nominal depth information - depth = sam.instrument_nominal_depth*ones(size(temp)); - presName = 'instrument_nominal_depth'; - end - - % any depth values <= -5 are discarded (reminder, depth is - % positive down), this allow use of gsw_p_from_z without error. - depth(depth <= -5) = NaN; - - % pressure information needed for Salinity computation is either - % retrieved from gsw_p_from_z when latitude is available or by - % simply assuming 1dbar ~= 1m - if ~isempty(sam.geospatial_lat_min) && ~isempty(sam.geospatial_lat_max) - % compute depth with Gibbs-SeaWater toolbox - % relative_pressure ~= gsw_p_from_z(-depth, latitude) - if sam.geospatial_lat_min == sam.geospatial_lat_max - presRel = gsw_p_from_z(-depth, sam.geospatial_lat_min); - else - meanLat = sam.geospatial_lat_min + ... - (sam.geospatial_lat_max - sam.geospatial_lat_min)/2; - presRel = gsw_p_from_z(-depth, meanLat); - end - else - % without latitude information, we assume 1dbar ~= 1m - presRel = depth; - presName = [presName ' (assuming 1 m ~ 1 dbar)']; - end - end - % calculate C(S,T,P)/C(35,15,0) ratio % conductivity is in S/m and gsw_C3515 in mS/cm R = 10*cndc ./ gsw_C3515; @@ -157,7 +89,7 @@ psal = gsw_SP_from_R(R, temp, presRel); dimensions = sam.variables{tempIdx}.dimensions; - salinityComment = ['salinityPP.m: derived from CNDC, TEMP and ' presName ' using the Gibbs-SeaWater toolbox (TEOS-10) v3.05']; + salinityComment = ['salinityPP.m: derived from CNDC, TEMP and ' presName ' using the Gibbs-SeaWater toolbox (TEOS-10) v3.06']; if isfield(sam.variables{tempIdx}, 'coordinates') coordinates = sam.variables{tempIdx}.coordinates; diff --git a/Preprocessing/timeDriftPP.m b/Preprocessing/timeDriftPP.m index b8e7cecfe..c2cbf2dc1 100644 --- a/Preprocessing/timeDriftPP.m +++ b/Preprocessing/timeDriftPP.m @@ -70,13 +70,6 @@ % auto logical in input to enable running under batch processing if nargin<3, auto=false; end -%check for CSV file import -isCSV = false; -ddb = readProperty('toolbox.ddb'); -if isdir(ddb) - isCSV = true; -end - descs = {}; nSample = length(sample_data); startOffsets = zeros(nSample, 1); @@ -89,15 +82,14 @@ descs{k} = genSampleDataDesc(sample_data{k}); %check to see if the offsets are available already from the ddb - if isCSV - if isfield(sample_data{k}.meta.deployment,'StartOffset') + if isfield(sample_data{k}.meta, 'deployment') + if isfield(sample_data{k}.meta.deployment, 'StartOffset') startOffsets(k) = sample_data{k}.meta.deployment.StartOffset; end - if isfield(sample_data{k}.meta.deployment,'EndOffset') + if isfield(sample_data{k}.meta.deployment, 'EndOffset') endOffsets(k) = sample_data{k}.meta.deployment.EndOffset; end - else - if all(isfield(sample_data{k}.meta.deployment,{'TimeDriftInstrument', 'TimeDriftGPS'})) + if all(isfield(sample_data{k}.meta.deployment, {'TimeDriftInstrument', 'TimeDriftGPS'})) if ~isempty(sample_data{k}.meta.deployment.TimeDriftInstrument) && ~isempty(sample_data{k}.meta.deployment.TimeDriftGPS) endOffsets(k) = (sample_data{k}.meta.deployment.TimeDriftInstrument - sample_data{k}.meta.deployment.TimeDriftGPS)*3600*24; end @@ -339,4 +331,4 @@ function endoffsetFieldCallback(source, ev) newtime = time - tarray; end end -end \ No newline at end of file +end diff --git a/Seawater/gsw_CT_from_pt.m b/Seawater/gsw_CT_from_pt.m new file mode 100644 index 000000000..64ca3739d --- /dev/null +++ b/Seawater/gsw_CT_from_pt.m @@ -0,0 +1,112 @@ +function CT = gsw_CT_from_pt(SA,pt) + +% gsw_CT_from_pt Conservative Temperature from potential temperature +%========================================================================== +% +% USAGE: +% CT = gsw_CT_from_pt(SA,pt) +% +% DESCRIPTION: +% Calculates Conservative Temperature of seawater from potential +% temperature (whose reference sea pressure is zero dbar). +% +% INPUT: +% SA = Absolute Salinity [ g/kg ] +% pt = potential temperature (ITS-90) [ deg C ] +% +% SA & pt need to have the same dimensions. +% +% OUTPUT: +% CT = Conservative Temperature (ITS-90) [ deg C ] +% +% AUTHOR: +% David Jackett, Trevor McDougall and Paul Barker [ help@teos-10.org ] +% +% VERSION NUMBER: 3.05 (27th January 2015) +% +% REFERENCES: +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org +% See section 3.3 of this TEOS-10 Manual. +% +% The software is available from http://www.TEOS-10.org +% +%========================================================================== + +%-------------------------------------------------------------------------- +% Check variables and resize if necessary +%-------------------------------------------------------------------------- + +if ~(nargin == 2) + error('gsw_CT_from_pt: Requires two inputs') +end %if + +[ms,ns] = size(SA); +[mt,nt] = size(pt); + +if (mt ~= ms | nt ~= ns) + error('gsw_CT_from_pt: SA and pt must have same dimensions') +end + +if ms == 1 + SA = SA.'; + pt = pt.'; + transposed = 1; +else + transposed = 0; +end + +%-------------------------------------------------------------------------- +% Start of the calculation +%-------------------------------------------------------------------------- + +% This line ensures that SA is non-negative. +SA(SA < 0) = 0; + +sfac = 0.0248826675584615; % sfac = 1/(40.*(35.16504/35)). + +x2 = sfac.*SA; +x = sqrt(x2); +y = pt.*0.025; % normalize for F03 and F08. + +pot_enthalpy = 61.01362420681071 + y.*(168776.46138048015 + ... + y.*(-2735.2785605119625 + y.*(2574.2164453821433 + ... + y.*(-1536.6644434977543 + y.*(545.7340497931629 + ... + (-50.91091728474331 - 18.30489878927802.*y).*y))))) + ... + x2.*(268.5520265845071 + y.*(-12019.028203559312 + ... + y.*(3734.858026725145 + y.*(-2046.7671145057618 + ... + y.*(465.28655623826234 + (-0.6370820302376359 - ... + 10.650848542359153.*y).*y)))) + ... + x.*(937.2099110620707 + y.*(588.1802812170108 + ... + y.*(248.39476522971285 + (-3.871557904936333 - ... + 2.6268019854268356.*y).*y)) + ... + x.*(-1687.914374187449 + x.*(246.9598888781377 + ... + x.*(123.59576582457964 - 48.5891069025409.*x)) + ... + y.*(936.3206544460336 + ... + y.*(-942.7827304544439 + y.*(369.4389437509002 + ... + (-33.83664947895248 - 9.987880382780322.*y).*y)))))); + +%-------------------------------------------------------------------------- +% The above polynomial for pot_enthalpy is the full expression for +% potential entahlpy in terms of SA and pt, obtained from the Gibbs +% function as below. The above polynomial has simply collected like powers +% of x and y so that it is computationally faster than calling the Gibbs +% function twice as is done in the commented code below. When this code +% below is run, the results are identical to calculating pot_enthalpy as +% above, to machine precision. +% +% pr0 = zeros(size(SA)); +% pot_enthalpy = gsw_gibbs(0,0,0,SA,pt,pr0) - ... +% (273.15 + pt).*gsw_gibbs(0,1,0,SA,pt,pr0); +% +%-----------------This is the end of the alternative code------------------ + +CT = pot_enthalpy./gsw_cp0; + +if transposed + CT = CT.'; +end + +end diff --git a/Seawater/gsw_O2sol_SP_pt.m b/Seawater/gsw_O2sol_SP_pt.m new file mode 100644 index 000000000..a4d9c848d --- /dev/null +++ b/Seawater/gsw_O2sol_SP_pt.m @@ -0,0 +1,114 @@ +function O2sol = gsw_O2sol_SP_pt(SP,pt) + +% gsw_O2sol_SP_pt solubility of O2 in seawater +%========================================================================== +% +% USAGE: +% O2sol = gsw_O2sol_SP_pt(SP,pt) +% +% DESCRIPTION: +% Calculates the oxygen concentration expected at equilibrium with air at +% an Absolute Pressure of 101325 Pa (sea pressure of 0 dbar) including +% saturated water vapor. This function uses the solubility coefficients +% derived from the data of Benson and Krause (1984), as fitted by Garcia +% and Gordon (1992, 1993). +% +% Note that this algorithm has not been approved by IOC and is not work +% from SCOR/IAPSO Working Group 127. It is included in the GSW +% Oceanographic Toolbox as it seems to be oceanographic best practice. +% +% INPUT: +% SP = Practical Salinity (PSS-78) [ unitless ] +% pt = potential temperature (ITS-90) referenced [ deg C ] +% to one standard atmosphere (0 dbar). +% +% SP & pt need to have the same dimensions. +% +% OUTPUT: +% O2sol = solubility of oxygen in micro-moles per kg [ umol/kg ] +% +% AUTHOR: Roberta Hamme, Paul Barker and Trevor McDougall +% [ help@teos-10.org ] +% +% VERSION NUMBER: 3.05 (27th January 2015) +% +% REFERENCES: +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org +% +% Benson, B.B., and D. Krause, 1984: The concentration and isotopic +% fractionation of oxygen dissolved in freshwater and seawater in +% equilibrium with the atmosphere. Limnology and Oceanography, 29, +% 620-632. +% +% Garcia, H.E., and L.I. Gordon, 1992: Oxygen solubility in seawater: +% Better fitting equations. Limnology and Oceanography, 37, 1307-1312. +% +% Garcia, H.E., and L.I. Gordon, 1993: Erratum: Oxygen solubility in +% seawater: better fitting equations. Limnology and Oceanography, 38, +% 656. +% +% The software is available from http://www.TEOS-10.org +% +%========================================================================== + +%-------------------------------------------------------------------------- +% Check variables and resize if necessary +%-------------------------------------------------------------------------- + +if nargin ~=2 + error('gsw_O2sol_SP_pt: Requires two inputs') +end %if + +[ms,ns] = size(SP); +[mt,nt] = size(pt); + +if (mt ~= ms | nt ~= ns) + error('gsw_O2sol_SP_pt: SP and pt must have same dimensions') +end + +if ms == 1 + SP = SP.'; + pt = pt.'; + transposed = 1; +else + transposed = 0; +end + +%-------------------------------------------------------------------------- +% Start of the calculation +%-------------------------------------------------------------------------- + +x = SP; % Note that salinity argument is Practical Salinity, this is + % beacuse the major ionic components of seawater related to Cl + % are what affect the solubility of non-electrolytes in seawater. + +pt68 = pt.*1.00024; % pt68 is the potential temperature in degress C on + % the 1968 International Practical Temperature Scale IPTS-68. + +y = log((298.15 - pt68)./(gsw_T0 + pt68)); + +% The coefficents below are from the second column of Table 1 of Garcia and +% Gordon (1992) +a0 = 5.80871; +a1 = 3.20291; +a2 = 4.17887; +a3 = 5.10006; +a4 = -9.86643e-2; +a5 = 3.80369; +b0 = -7.01577e-3; +b1 = -7.70028e-3; +b2 = -1.13864e-2; +b3 = -9.51519e-3; +c0 = -2.75915e-7; + +O2sol = exp(a0 + y.*(a1 + y.*(a2 + y.*(a3 + y.*(a4 + a5*y)))) ... + + x.*(b0 + y.*(b1 + y.*(b2 + b3*y)) + c0*x)); + +if transposed + O2sol = O2sol.'; +end + +end \ No newline at end of file diff --git a/Seawater/gsw_SA_from_SP.m b/Seawater/gsw_SA_from_SP.m new file mode 100644 index 000000000..08ffd37bf --- /dev/null +++ b/Seawater/gsw_SA_from_SP.m @@ -0,0 +1,183 @@ +function [SA, in_ocean] = gsw_SA_from_SP(SP,p,long,lat) + +% gsw_SA_from_SP Absolute Salinity from Practical Salinity +%========================================================================== +% +% USAGE: +% [SA, in_ocean] = gsw_SA_from_SP(SP,p,long,lat) +% +% DESCRIPTION: +% Calculates Absolute Salinity from Practical Salinity. Since SP is +% non-negative by definition, this function changes any negative input +% values of SP to be zero. +% +% INPUT: +% SP = Practical Salinity (PSS-78) [ unitless ] +% p = sea pressure [ dbar ] +% ( i.e. absolute pressure - 10.1325 dbar ) +% long = longitude in decimal degrees [ 0 ... +360 ] +% or [ -180 ... +180 ] +% lat = latitude in decimal degrees north [ -90 ... +90 ] +% +% p, lat & long may have dimensions 1x1 or Mx1 or 1xN or MxN, +% where SP is MxN. +% +% OUTPUT: +% SA = Absolute Salinity [ g/kg ] +% in_ocean = 0, if long and lat are a long way from the ocean +% = 1, if long and lat are in the ocean +% Note. This flag is only set when the observation is well and truly on +% dry land; often the warning flag is not set until one is several +% hundred kilometres inland from the coast. +% +% AUTHOR: +% David Jackett, Trevor McDougall & Paul Barker [ help@teos-10.org ] +% +% VERSION NUMBER: 3.05 (27th January 2015) +% +% REFERENCES: +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org +% See section 2.5 and appendices A.4 and A.5 of this TEOS-10 Manual. +% +% McDougall, T.J., D.R. Jackett, F.J. Millero, R. Pawlowicz and +% P.M. Barker, 2012: A global algorithm for estimating Absolute Salinity. +% Ocean Science, 8, 1123-1134. +% http://www.ocean-sci.net/8/1123/2012/os-8-1123-2012.pdf +% +% The software is available from http://www.TEOS-10.org +% +%========================================================================== + +%-------------------------------------------------------------------------- +% Check variables and resize if necessary +%-------------------------------------------------------------------------- + +if ~(nargin==4) + error('gsw_SA_from_SP: Requires four inputs') +end %if + +[ms,ns] = size(SP); +[mp,np] = size(p); + +if (mp == 1) & (np == 1) % p is a scalar - fill to size of SP + p = p*ones(size(SP)); +elseif (ns == np) & (mp == 1) % p is row vector, + p = p(ones(1,ms), :); % copy down each column. +elseif (ms == mp) & (np == 1) % p is column vector, + p = p(:,ones(1,ns)); % copy across each row. +elseif (ns == mp) & (np == 1) % p is a transposed row vector, + p = p.'; % transposed then + p = p(ones(1,ms), :); % copy down each column. +elseif (ms == mp) & (ns == np) + % ok +else + error('gsw_SA_from_SP: Inputs array dimensions arguments do not agree') +end %if + +[mla,nla] = size(lat); + +if (mla == 1) & (nla == 1) % lat is a scalar - fill to size of SP + lat = lat*ones(size(SP)); +elseif (ns == nla) & (mla == 1) % lat is a row vector, + lat = lat(ones(1,ms), :); % copy down each column. +elseif (ms == mla) & (nla == 1) % lat is a column vector, + lat = lat(:,ones(1,ns)); % copy across each row. +elseif (ns == mla) & (nla == 1) % lat is a transposed row vector, + lat = lat.'; % transposed then + lat = lat(ones(1,ms), :); % copy down each column. +elseif (ms == mla) & (ns == nla) + % ok +else + error('gsw_SA_from_SP: Inputs array dimensions arguments do not agree') +end %if + +[mlo,nlo] = size(long); +long(long < 0) = long(long < 0) + 360; + +if (mlo == 1) & (nlo == 1) % long is a scalar - fill to size of SP + long = long*ones(size(SP)); +elseif (ns == nlo) & (mlo == 1) % long is a row vector, + long = long(ones(1,ms), :); % copy down each column. +elseif (ms == mlo) & (nlo == 1) % long is a column vector, + long = long(:,ones(1,ns)); % copy across each row. +elseif (ns == mlo) & (nlo == 1) % long is a transposed row vector, + long = long.'; % transposed then + long = long(ones(1,ms), :); % copy down each column. +elseif (ms == nlo) & (mlo == 1) % long is a transposed column vector, + long = long.'; % transposed then + long = long(:,ones(1,ns)); % copy down each column. +elseif (ms == mlo) & (ns == nlo) + % ok +else + error('gsw_SA_from_SP: Inputs array dimensions arguments do not agree') +end %if + +if ms == 1 + SP = SP.'; + p = p.'; + lat = lat.'; + long = long.'; + transposed = 1; +else + transposed = 0; +end + +% remove out of range values. +SP(p < 100 & SP > 120) = NaN; +SP(p >= 100 & SP > 42) = NaN; + +% change standard blank fill values to NaN's. +SP(abs(SP) == 99999 | abs(SP) == 999999) = NaN; +p(abs(p) == 99999 | abs(p) == 999999) = NaN; +long(abs(long) == 9999 | abs(long) == 99999) = NaN; +lat(abs(lat) == 9999 | abs(lat) == 99999) = NaN; + +if any(p < -1.5 | p > 12000) + error('gsw_SA_from_SP: pressure is out of range') +end +if any(long < 0 | long > 360) + error('gsw_SA_from_SP: longitude is out of range') +end +if any(abs(lat) > 90) + error('gsw_SA_from_SP: latitude is out of range') +end + +%-------------------------------------------------------------------------- +% Start of the calculation +%-------------------------------------------------------------------------- + +% This ensures that SP is non-negative. +SP(SP < 0) = 0; + +[Iocean] = find(~isnan(SP + p + lat + long)); + +SA = nan(size(SP)); +SAAR = SA; +in_ocean = SA; + +% The following function (gsw_SAAR) finds SAAR in the non-Baltic parts of +% the world ocean. (Actually, this gsw_SAAR look-up table returns values +% of zero in the Baltic Sea since SAAR in the Baltic is a function of SP, +% not space. +[SAAR(Iocean), in_ocean(Iocean)] = gsw_SAAR(p(Iocean),long(Iocean),lat(Iocean)); + +SA(Iocean) = gsw_uPS*SP(Iocean).*(1 + SAAR(Iocean)); + +% Here the Practical Salinity in the Baltic is used to calculate the +% Absolute Salinity there. +SA_baltic(Iocean) = gsw_SA_from_SP_Baltic(SP(Iocean),long(Iocean),lat(Iocean)); + +if any(~isnan(SA_baltic(Iocean))) + [Ibaltic] = find(~isnan(SA_baltic(Iocean))); + SA(Iocean(Ibaltic)) = SA_baltic(Iocean(Ibaltic)); +end + +if transposed + SA = SA'; + in_ocean = in_ocean'; +end + +end diff --git a/Seawater/gsw_SSO.m b/Seawater/gsw_SSO.m new file mode 100644 index 000000000..241c1efbd --- /dev/null +++ b/Seawater/gsw_SSO.m @@ -0,0 +1,43 @@ +function SSO = gsw_SSO + +% gsw_SSO Standard Ocean Reference Salinity +%========================================================================== +% +% USAGE: +% SSO = gsw_SSO +% +% DESCRIPTION: +% SSO is the Standard Ocean Reference Salinity (35.16504 g/kg). +% +% SSO is the best estimate of the Absolute Salinity of Standard Seawater +% when the seawater sample has a Practical Salinity, SP, of 35 +% (Millero et al., 2008), and this number is a fundmental part of the +% TEOS-10 definition of seawater. +% +% OUTPUT: +% SSO = Standard Ocean Reference Salinity. [ g/kg ] +% +% AUTHOR: +% Trevor McDougall and Paul Barker [ help@teos-10.org ] +% +% VERSION NUMBER: 3.05 (27th January 2015) +% +% REFERENCES: +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org. +% See appendices A.3, A.5 and Table D.4 of this TEOS-10 Manual. +% +% Millero, F. J., R. Feistel, D. G. Wright, and T. J. McDougall, 2008: +% The composition of Standard Seawater and the definition of the +% Reference-Composition Salinity Scale, Deep-Sea Res. I, 55, 50-72. +% See Table 4 and section 5 of this paper. +% +% The software is available from http://www.TEOS-10.org +% +%========================================================================== + +SSO = 35.16504; + +end diff --git a/Seawater/gsw_T0.m b/Seawater/gsw_T0.m new file mode 100644 index 000000000..16b34c215 --- /dev/null +++ b/Seawater/gsw_T0.m @@ -0,0 +1,34 @@ +function T0 = gsw_T0 + +% gsw_T0 Celcius zero point +%========================================================================== +% +% USAGE: +% T0 = gsw_T0 +% +% DESCRIPTION: +% The Celcius zero point; 273.15 K. That is T = t + T0 where T is the +% Absolute Temperature (in degrees K) and t is temperature in degrees C. +% +% OUTPUT: +% T0 = the Celcius zero point. [ K ] +% +% AUTHOR: +% Trevor McDougall and Paul Barker [ help@teos-10.org ] +% +% VERSION NUMBER: 3.05 (27th January 2015) +% +% REFERENCES: +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org. +% See Table D.1 of this TEOS-10 Manual. +% +% The software is available from http://www.TEOS-10.org +% +%========================================================================== + +T0 = 273.15; + +end diff --git a/Seawater/gsw_cp0.m b/Seawater/gsw_cp0.m new file mode 100644 index 000000000..60e449b7f --- /dev/null +++ b/Seawater/gsw_cp0.m @@ -0,0 +1,35 @@ +function cp0 = gsw_cp0 + +% gsw_cp0 the "specific heat" for use with CT +%========================================================================== +% +% USAGE: +% cp0 = gsw_cp0 +% +% DESCRIPTION: +% The "specific heat" for use with Conservative Temperature. cp0 is the +% ratio of potential enthalpy to Conservative Temperature. +% +% OUTPUT: +% cp0 = The "specific heat" for use [ J/(kg K) ] +% with Conservative Temperature +% +% AUTHOR: +% Trevor McDougall and Paul Barker [ help@teos-10.org ] +% +% VERSION NUMBER: 3.05 (27th January 2015) +% +% REFERENCES: +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org. +% See Eqn. (3.3.3) and Table D.5 of this TEOS-10 Manual. +% +% The software is available from http://www.TEOS-10.org +% +%========================================================================== + +cp0 = 3991.86795711963; + +end diff --git a/Seawater/gsw_p_from_z.m b/Seawater/gsw_p_from_z.m index 08f855dfb..186b47d9d 100644 --- a/Seawater/gsw_p_from_z.m +++ b/Seawater/gsw_p_from_z.m @@ -1,6 +1,6 @@ function p = gsw_p_from_z(z,lat,geo_strf_dyn_height,sea_surface_geopotental) -% gsw_p_from_z pressure from height (76-term equation) +% gsw_p_from_z pressure from height (75-term equation) %========================================================================== % % USAGE: @@ -61,18 +61,19 @@ % temperature and density of seawater. J. Atmosph. Ocean. Tech., 20, % pp. 730-741. % -% McDougall T.J. and S.J. Wotherspoon, 2013: A simple modification of +% McDougall, T.J., and S.J. Wotherspoon, 2013: A simple modification of % Newton's method to achieve convergence of order 1 + sqrt(2). Applied -% Mathematics Letters, 29, 20-25. +% Mathematics Letters, 29, pp. 20-25. % -% Moritz, 2000: Goedetic reference system 1980. J. Geodesy, 74, 128-133. +% Moritz, H., 2000: Geodetic reference system 1980. J. Geodesy, 74, +% pp. 128-133. % % Roquet, F., G. Madec, T.J. McDougall, P.M. Barker, 2015: Accurate % polynomial expressions for the density and specifc volume of seawater -% using the TEOS-10 standard. Ocean Modelling. +% using the TEOS-10 standard. Ocean Modelling, 90, pp. 29-43. % % Saunders, P.M., 1981: Practical conversion of pressure to depth. -% Journal of Physical Oceanography, 11, 573-574. +% Journal of Physical Oceanography, 11, pp. 573-574. % % This software is available from http://www.TEOS-10.org % diff --git a/Seawater/gsw_pt0_from_t.m b/Seawater/gsw_pt0_from_t.m new file mode 100644 index 000000000..15a497805 --- /dev/null +++ b/Seawater/gsw_pt0_from_t.m @@ -0,0 +1,135 @@ +function pt0 = gsw_pt0_from_t(SA,t,p) + +% gsw_pt0_from_t potential temperature with a +% reference sea pressure of zero dbar +% ========================================================================= +% +% USAGE: +% pt0 = gsw_pt0_from_t(SA,t,p) +% +% DESCRIPTION: +% Calculates potential temperature with reference pressure, p_ref = 0 dbar. +% The present routine is computationally faster than the more general +% function "gsw_pt_from_t(SA,t,p,p_ref)" which can be used for any +% reference pressure value. +% This subroutine calls "gsw_entropy_part(SA,t,p)", +% "gsw_entropy_part_zerop(SA,pt0)" and "gsw_gibbs_pt0_pt0(SA,pt0)". +% +% INPUT: +% SA = Absolute Salinity [ g/kg ] +% t = in-situ temperature (ITS-90) [ deg C ] +% p = sea pressure [ dbar ] +% ( i.e. absolute pressure - 10.1325 dbar ) +% +% SA & t need to have the same dimensions. +% p may have dimensions 1x1 or Mx1 or 1xN or MxN, where SA & t are MxN. +% +% OUTPUT: +% pt0 = potential temperature [ deg C ] +% with reference sea pressure (p_ref) = 0 dbar. +% Note. The reference sea pressure of the output, pt0, is zero dbar. +% +% AUTHOR: +% Trevor McDougall, David Jackett, Claire Roberts-Thomson and Paul Barker. +% [ help@teos-10.org ] +% +% VERSION NUMBER: 3.05 (27th January 2015) +% +% REFERENCES: +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org +% See section 3.1 of this TEOS-10 Manual. +% +% McDougall T. J. and S. J. Wotherspoon, 2013: A simple modification of +% Newton's method to achieve convergence of order 1 + sqrt(2). Applied +% Mathematics Letters, 29, 20-25. +% +% The software is available from http://www.TEOS-10.org +% +%========================================================================== + +%-------------------------------------------------------------------------- +% Check variables and resize if necessary +%-------------------------------------------------------------------------- + +if ~(nargin == 3) + error('gsw_pt0_from_t: Requires 3 inputs - Absolute Salinity, temperature, and pressure') +end %if + +[ms,ns] = size(SA); +[mt,nt] = size(t); +[mp,np] = size(p); + +if (ms ~= mt | ns ~= nt ) + error('gsw_pt0_from_t: Input arguments do not have the same dimensions') +end %if + +if (mp == 1) & (np == 1) % p scalar - fill to size of SA + p = p*ones(size(SA)); +elseif (ns == np) & (mp == 1) % p is row vector, + p = p(ones(1,ms), :); % copy down each column. +elseif (ms == mp) & (np == 1) % p is column vector, + p = p(:,ones(1,ns)); % copy across each row. +elseif (ns == mp) & (np == 1) % p is a transposed row vector, + p = p.'; % transposed then + p = p(ones(1,ms), :); % copy down each column. +elseif (ms == mp) & (ns == np) + % ok +else + error('gsw_pt0_from_t: Inputs array dimensions arguments do not agree') +end %if + +if ms == 1 + SA = SA.'; + t = t.'; + p = p.'; + transposed = 1; +else + transposed = 0; +end + +%-------------------------------------------------------------------------- +% Start of the calculation +%-------------------------------------------------------------------------- + +% This line ensures that SA is non-negative. +SA(SA < 0) = 0; + +SSO = gsw_SSO; % from section 2.4 of IOC et al. (2010). + +s1 = SA*(35./SSO); + +pt0 = t + p.*( 8.65483913395442e-6 - ... + s1.* 1.41636299744881e-6 - ... + p.* 7.38286467135737e-9 + ... + t.*(-8.38241357039698e-6 + ... + s1.* 2.83933368585534e-8 + ... + t.* 1.77803965218656e-8 + ... + p.* 1.71155619208233e-10)); + +dentropy_dt = gsw_cp0./((gsw_T0 + pt0).*(1 - 0.05.*(1 - SA./SSO))); + +true_entropy_part = gsw_entropy_part(SA,t,p); + +for Number_of_iterations = 1:2 + pt0_old = pt0; + dentropy = gsw_entropy_part_zerop(SA,pt0_old) - true_entropy_part; + pt0 = pt0_old - dentropy./dentropy_dt ; % this is half way through the modified method (McDougall and Wotherspoon, 2012) + pt0m = 0.5*(pt0 + pt0_old); + dentropy_dt = -gsw_gibbs_pt0_pt0(SA,pt0m); + pt0 = pt0_old - dentropy./dentropy_dt; +end + +if transposed + pt0 = pt0.'; +end + +% maximum error of 6.3x10^-9 degrees C for one iteration. +% maximum error is 1.8x10^-14 degrees C for two iterations +% (two iterations is the default, "for Number_of_iterations = 1:2"). +% These errors are over the full "oceanographic funnel" of +% McDougall et al. (2013), which reaches down to p = 8000 dbar. + +end diff --git a/Seawater/gsw_rho.m b/Seawater/gsw_rho.m new file mode 100644 index 000000000..a9bf9c680 --- /dev/null +++ b/Seawater/gsw_rho.m @@ -0,0 +1,233 @@ +function rho = gsw_rho(SA,CT,p) + +% gsw_rho in-situ density (75-term equation) +%========================================================================== +% +% USAGE: +% rho = gsw_rho(SA,CT,p) +% +% DESCRIPTION: +% Calculates in-situ density from Absolute Salinity and Conservative +% Temperature, using the computationally-efficient expression for +% specific volume in terms of SA, CT and p (Roquet et al., 2015). +% +% Note that potential density with respect to reference pressure, pr, is +% obtained by calling this function with the pressure argument being pr +% (i.e. "gsw_rho(SA,CT,pr)"). +% +% Note that this 75-term equation has been fitted in a restricted range of +% parameter space, and is most accurate inside the "oceanographic funnel" +% described in McDougall et al. (2003). The GSW library function +% "gsw_infunnel(SA,CT,p)" is avaialble to be used if one wants to test if +% some of one's data lies outside this "funnel". +% +% INPUT: +% SA = Absolute Salinity [ g/kg ] +% CT = Conservative Temperature (ITS-90) [ deg C ] +% p = sea pressure [ dbar ] +% ( i.e. absolute pressure - 10.1325 dbar ) +% +% SA & CT need to have the same dimensions. +% p may have dimensions 1x1 or Mx1 or 1xN or MxN, where SA & CT are MxN. +% +% OUTPUT: +% rho = in-situ density [ kg/m ] +% +% AUTHOR: +% Paul Barker and Trevor McDougall [ help@teos-10.org ] +% +% VERSION NUMBER: 3.05 (27th November, 2015) +% +% REFERENCES: +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org +% See appendix A.20 and appendix K of this TEOS-10 Manual. +% +% McDougall, T.J., D.R. Jackett, D.G. Wright and R. Feistel, 2003: +% Accurate and computationally efficient algorithms for potential +% temperature and density of seawater. J. Atmosph. Ocean. Tech., 20, +% pp. 730-741. +% +% Roquet, F., G. Madec, T.J. McDougall, P.M. Barker, 2015: Accurate +% polynomial expressions for the density and specifc volume of seawater +% using the TEOS-10 standard. Ocean Modelling, 90, pp. 29-43. +% +% The software is available from http://www.TEOS-10.org +% +%========================================================================== + +%-------------------------------------------------------------------------- +% Check variables and resize if necessary +%-------------------------------------------------------------------------- + +if ~(nargin == 3) + error('gsw_rho: Requires three inputs') +end + +[ms,ns] = size(SA); +[mt,nt] = size(CT); +[mp,np] = size(p); + +if (mt ~= ms | nt ~= ns) + error('gsw_rho: SA and CT must have same dimensions') +end + +if (mp == 1) & (np == 1) % p scalar - fill to size of SA + p = p*ones(size(SA)); +elseif (ns == np) & (mp == 1) % p is row vector, + p = p(ones(1,ms), :); % copy down each column. +elseif (ms == mp) & (np == 1) % p is column vector, + p = p(:,ones(1,ns)); % copy across each row. +elseif (ns == mp) & (np == 1) % p is a transposed row vector, + p = p.'; % transposed then + p = p(ones(1,ms), :); % copy down each column. +elseif (ms == mp) & (ns == np) + % ok +else + error('gsw_rho: Inputs array dimensions arguments do not agree') +end + +if ms == 1 + SA = SA.'; + CT = CT.'; + p = p.'; + transposed = 1; +else + transposed = 0; +end + +%-------------------------------------------------------------------------- +% Start of the calculation +%-------------------------------------------------------------------------- + +% This line ensures that SA is non-negative. +SA(SA < 0) = 0; + +%deltaS = 24; +sfac = 0.0248826675584615; % sfac = 1/(40*(35.16504/35)). +offset = 5.971840214030754e-1; % offset = deltaS*sfac. + +x2 = sfac.*SA; +xs = sqrt(x2 + offset); +ys = CT.*0.025; +z = p.*1e-4; + +v000 = 1.0769995862e-3; +v001 = -6.0799143809e-5; +v002 = 9.9856169219e-6; +v003 = -1.1309361437e-6; +v004 = 1.0531153080e-7; +v005 = -1.2647261286e-8; +v006 = 1.9613503930e-9; +v010 = -1.5649734675e-5; +v011 = 1.8505765429e-5; +v012 = -1.1736386731e-6; +v013 = -3.6527006553e-7; +v014 = 3.1454099902e-7; +v020 = 2.7762106484e-5; +v021 = -1.1716606853e-5; +v022 = 2.1305028740e-6; +v023 = 2.8695905159e-7; +v030 = -1.6521159259e-5; +v031 = 7.9279656173e-6; +v032 = -4.6132540037e-7; +v040 = 6.9111322702e-6; +v041 = -3.4102187482e-6; +v042 = -6.3352916514e-8; +v050 = -8.0539615540e-7; +v051 = 5.0736766814e-7; +v060 = 2.0543094268e-7; +v100 = -3.1038981976e-4; +v101 = 2.4262468747e-5; +v102 = -5.8484432984e-7; +v103 = 3.6310188515e-7; +v104 = -1.1147125423e-7; +v110 = 3.5009599764e-5; +v111 = -9.5677088156e-6; +v112 = -5.5699154557e-6; +v113 = -2.7295696237e-7; +v120 = -3.7435842344e-5; +v121 = -2.3678308361e-7; +v122 = 3.9137387080e-7; +v130 = 2.4141479483e-5; +v131 = -3.4558773655e-6; +v132 = 7.7618888092e-9; +v140 = -8.7595873154e-6; +v141 = 1.2956717783e-6; +v150 = -3.3052758900e-7; +v200 = 6.6928067038e-4; +v201 = -3.4792460974e-5; +v202 = -4.8122251597e-6; +v203 = 1.6746303780e-8; +v210 = -4.3592678561e-5; +v211 = 1.1100834765e-5; +v212 = 5.4620748834e-6; +v220 = 3.5907822760e-5; +v221 = 2.9283346295e-6; +v222 = -6.5731104067e-7; +v230 = -1.4353633048e-5; +v231 = 3.1655306078e-7; +v240 = 4.3703680598e-6; +v300 = -8.5047933937e-4; +v301 = 3.7470777305e-5; +v302 = 4.9263106998e-6; +v310 = 3.4532461828e-5; +v311 = -9.8447117844e-6; +v312 = -1.3544185627e-6; +v320 = -1.8698584187e-5; +v321 = -4.8826139200e-7; +v330 = 2.2863324556e-6; +v400 = 5.8086069943e-4; +v401 = -1.7322218612e-5; +v402 = -1.7811974727e-6; +v410 = -1.1959409788e-5; +v411 = 2.5909225260e-6; +v420 = 3.8595339244e-6; +v500 = -2.1092370507e-4; +v501 = 3.0927427253e-6; +v510 = 1.3864594581e-6; +v600 = 3.1932457305e-5; + +v = v000 + xs.*(v100 + xs.*(v200 + xs.*(v300 + xs.*(v400 + xs.*(v500 ... + + v600.*xs))))) + ys.*(v010 + xs.*(v110 + xs.*(v210 + xs.*(v310 + xs.*(v410 ... + + v510.*xs)))) + ys.*(v020 + xs.*(v120 + xs.*(v220 + xs.*(v320 + v420.*xs))) ... + + ys.*(v030 + xs.*(v130 + xs.*(v230 + v330.*xs)) + ys.*(v040 + xs.*(v140 ... + + v240*xs) + ys.*(v050 + v150.*xs + v060.*ys))))) + z.*(v001 + xs.*(v101 ... + + xs.*(v201 + xs.*(v301 + xs.*(v401 + v501.*xs)))) + ys.*(v011 + xs.*(v111 ... + + xs.*(v211 + xs.*(v311 + v411.*xs))) + ys.*(v021 + xs.*(v121 + xs.*(v221 ... + + v321.*xs)) + ys.*(v031 + xs.*(v131 + v231.*xs) + ys.*(v041 + v141.*xs ... + + v051.*ys)))) + z.*(v002 + xs.*(v102 + xs.*(v202 + xs.*(v302 + v402.*xs))) ... + + ys.*(v012 + xs.*(v112 + xs.*(v212 + v312.*xs)) + ys.*(v022 + xs.*(v122 ... + + v222.*xs) + ys.*(v032 + v132.*xs + v042.*ys))) + z.*(v003 + xs.*(v103 ... + + v203.*xs) + ys.*(v013 + v113.*xs + v023.*ys) + z.*(v004 + v104.*xs + v014.*ys ... + + z.*(v005 + v006.*z))))); + +rho = 1./v; + +%-------------------------------------------------------------------------- +% This function calculates rho using the computationally-efficient +% expression for specific volume in terms of SA, CT and p. If one wanted +% to compute rho from SA, CT, and p with the full TEOS-10 Gibbs function, +% the following lines of code will enable this. +% +% t = gsw_t_from_CT(SA,CT,p); +% rho = gsw_rho_t_exact(SA,t,p); +% +% or call the following, it is identical to the lines above. +% +% rho = gsw_rho_CT_exact(SA,CT,p) +% +% or call the following, it is identical to the lines above. +% +% [rho, ~, ~] = gsw_rho_alpha_beta_CT_exact(SA,CT,p) +% +%-----------------This is the end of the alternative code------------------ + +if transposed + rho = rho.'; +end + +end + diff --git a/Seawater/gsw_uPS.m b/Seawater/gsw_uPS.m new file mode 100644 index 000000000..c9d88584f --- /dev/null +++ b/Seawater/gsw_uPS.m @@ -0,0 +1,39 @@ +function uPS = gsw_uPS + +% gsw_uPS unit conversion factor for salinities +%========================================================================== +% +% USAGE: +% uPS = gsw_uPS +% +% DESCRIPTION: +% The unit conversion factor for salinities (35.16504/35) g/kg (Millero et +% al., 2008). Reference Salinity SR is uPS times Practical Salinity SP. +% +% OUTPUT: +% uPS = unit conversion factor for salinities [ g/kg ] +% +% AUTHOR: +% Trevor McDougall and Paul Barker [ help@teos-10.org ] +% +% VERSION NUMBER: 3.05 (27th January 2015) +% +% REFERENCES: +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org. +% See section 2.4 and Table D.4 of this TEOS-10 Manual. +% +% Millero, F. J., R. Feistel, D. G. Wright, and T. J. McDougall, 2008: +% The composition of Standard Seawater and the definition of the +% Reference-Composition Salinity Scale, Deep-Sea Res. I, 55, 50-72. +% See section 6, Eqn. (6.1) of this paper. +% +% The software is available from http://www.TEOS-10.org +% +%========================================================================== + +uPS = 35.16504/35; + +end diff --git a/Seawater/gsw_z_from_p.m b/Seawater/gsw_z_from_p.m index 62704a2b1..5ae48748e 100644 --- a/Seawater/gsw_z_from_p.m +++ b/Seawater/gsw_z_from_p.m @@ -48,7 +48,7 @@ % Trevor McDougall, Claire Roberts-Thomson & Paul Barker. % [ help@teos-10.org ] % -% VERSION NUMBER: 3.04 (10th December, 2013) +% VERSION NUMBER: 3.05 (27th January 2015) % % REFERENCES: % IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of @@ -61,11 +61,12 @@ % temperature and density of seawater. J. Atmosph. Ocean. Tech., 20, % pp. 730-741. % -% Moritz (2000) Goedetic reference system 1980. J. Geodesy, 74, 128-133. +% Moritz, H., 2000: Geodetic reference system 1980. J. Geodesy, 74, +% pp. 128-133. % % Roquet, F., G. Madec, T.J. McDougall, P.M. Barker, 2015: Accurate % polynomial expressions for the density and specifc volume of seawater -% using the TEOS-10 standard. Ocean Modelling. +% using the TEOS-10 standard. Ocean Modelling, 90, pp. 29-43. % % This software is available from http://www.TEOS-10.org % diff --git a/Seawater/gsw_Hill_ratio_at_SP2.m b/Seawater/library/gsw_Hill_ratio_at_SP2.m similarity index 100% rename from Seawater/gsw_Hill_ratio_at_SP2.m rename to Seawater/library/gsw_Hill_ratio_at_SP2.m diff --git a/Seawater/library/gsw_SAAR.m b/Seawater/library/gsw_SAAR.m new file mode 100644 index 000000000..d1639c94f --- /dev/null +++ b/Seawater/library/gsw_SAAR.m @@ -0,0 +1,399 @@ +function [SAAR, in_ocean] = gsw_SAAR(p,long,lat) + +% gsw_SAAR Absolute Salinity Anomaly Ratio (excluding the Baltic Sea) +%========================================================================== +% +% USAGE: +% [SAAR, in_ocean] = gsw_SAAR(p,long,lat) +% +% DESCRIPTION: +% Calculates the Absolute Salinity Anomaly Ratio, SAAR, in the open ocean +% by spatially interpolating the global reference data set of SAAR to the +% location of the seawater sample. +% +% This function uses version 3.0 of the SAAR look up table (15th May 2011). +% +% The Absolute Salinity Anomaly Ratio in the Baltic Sea is evaluated +% separately, since it is a function of Practical Salinity, not of space. +% The present function returns a SAAR of zero for data in the Baltic Sea. +% The correct way of calculating Absolute Salinity in the Baltic Sea is by +% calling gsw_SA_from_SP. +% +% INPUT: +% p = sea pressure [ dbar ] +% ( i.e. absolute pressure - 10.1325 dbar ) +% long = Longitude in decimal degrees [ 0 ... +360 ] +% or [ -180 ... +180 ] +% lat = Latitude in decimal degrees north [ -90 ... +90 ] +% +% p, long & lat need to be vectors and have the same dimensions. +% +% OUTPUT: +% SAAR = Absolute Salinity Anomaly Ratio [ unitless ] +% in_ocean = 0, if long and lat are a long way from the ocean +% = 1, if long and lat are in or near the ocean +% Note. This flag is only set when the observation is well and truly on +% dry land; often the warning flag is not set until one is several +% hundred kilometres inland from the coast. +% +% AUTHOR: +% David Jackett [ help@teos-10.org ] +% +% MODIFIED: +% Paul Barker and Trevor McDougall +% Acknowledgment. Matlab programming assisance from Sunke Schmidtko. +% +% VERSION NUMBER: 3.05 (27th January 2015) +% +% REFERENCES: +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org +% +% McDougall, T.J., D.R. Jackett, F.J. Millero, R. Pawlowicz and +% P.M. Barker, 2012: A global algorithm for estimating Absolute Salinity. +% Ocean Science, 8, 1123-1134. +% http://www.ocean-sci.net/8/1123/2012/os-8-1123-2012.pdf +% +% See also gsw_SA_from_SP, gsw_deltaSA_atlas +% +% Reference page in Help browser +% doc gsw_SAAR +% Note that this reference page includes the code contained in gsw_SAAR. +% We have opted to encode this programme as it is a global standard and +% such we cannot allow anyone to change it. +% +%========================================================================== + +%-------------------------------------------------------------------------- +% Check variables and resize if necessary +%-------------------------------------------------------------------------- + +if ~(nargin == 3) + error('gsw_SAAR: Requires three inputs') +end %if + +[mp,np] = size(p); +[mla,nla] = size(lat); +[mlo,nlo] = size(long); + +if (mp ~= mla) | (mp ~=mlo) | (np ~= nla) | (np ~= nlo) + error('gsw_SAAR: Inputs need be of the same size') +end %if + +if any(p < -1.5) + error('gsw_SAAR: pressure needs to be positive') +end + +%set any pressures between 0 and -1.5 to be equal to 0 (i.e. the surface) +p(p < 0) = 0; + +%-------------------------------------------------------------------------- +% Start of the calculation (extracting from a look up table) +%-------------------------------------------------------------------------- + +persistent SAAR_ref lats_ref longs_ref p_ref ndepth_ref + +if isempty(SAAR_ref) + gsw_data = 'gsw_data_v3_0.mat'; + + gsw_data_file = which(gsw_data); + + load (gsw_data_file,'SAAR_ref','lats_ref','longs_ref','p_ref', ... + 'ndepth_ref'); +end + +% precalculate constants +nx = length(longs_ref); +ny = length(lats_ref); +nz = length(p_ref); +nyz = ny.*nz; + +n0 = length(p); + +dlongs_ref = longs_ref(2) - longs_ref(1); +dlats_ref = lats_ref(2) - lats_ref(1); + +indsx0 = floor(1 + (nx-1)*(long - longs_ref(1))./(longs_ref(nx) - longs_ref(1))); +indsx0 = indsx0(:); +indsx0(indsx0 == nx) = nx - 1; + +indsy0 = floor(1 + (ny-1)*(lat - lats_ref(1))./(lats_ref(ny) - lats_ref(1))); +indsy0 = indsy0(:); +indsy0(indsy0 == ny) = ny - 1; + +% Assign a pressure bin for each bottle. +indsz0 = ones(n0,1); +for I = 2:nz + indsz0(p >= p_ref(I-1) & p < p_ref(I)) = I - 1; +end +indsz0(p >= p_ref(nz)) = nz-1; + +indsy0_indsx0_ny = indsy0 + indsx0.*ny; +indsn1 = indsy0_indsx0_ny - ny; %4 xy grid points surrounding the data +indsn2 = indsy0_indsx0_ny; +indsn3 = indsy0_indsx0_ny + 1; +indsn4 = indsy0_indsx0_ny + (1 - ny); + +nmax = max([ndepth_ref(indsn1)';ndepth_ref(indsn2)';ndepth_ref(indsn3)';ndepth_ref(indsn4)']); + +if any(indsz0(:)' > nmax) + inds1 = find(indsz0(:)' > nmax); % casts deeper than GK maximum + + p(inds1) = p_ref(nmax(inds1)); % have reset p here so have to reset indsz0 + + indsz0(inds1) = nmax(inds1) - 1; +end + +indsyx_tmp = indsy0_indsx0_ny.*nz; % precalculate constants for loop +inds0 = indsz0 + indsyx_tmp - (nyz + nz); + +data_indices = [indsx0,indsy0,indsz0,inds0]; +data_inds = data_indices(:,3); + +r1 = (long(:) - longs_ref(indsx0))./(longs_ref(indsx0+1) - longs_ref(indsx0)); +s1 = (lat(:) - lats_ref(indsy0))./(lats_ref(indsy0+1) - lats_ref(indsy0)); +t1 = (p(:) - p_ref(indsz0))./(p_ref(indsz0+1) - p_ref(indsz0)); + +sa_upper = NaN(size(data_inds)); +sa_lower = sa_upper; +SAAR = sa_upper; +in_ocean = ones(size(SAAR)); + +indsyx_tmp = indsy0_indsx0_ny.*nz; % precalculate constants for loop +saar_nan = nan(4,n0); + +for k = 1:nz-1 + + inds_k = find(indsz0 == k); + + if ~isempty(inds_k) + + indsXYZ = k + indsyx_tmp(inds_k); + + inds_di = find(data_inds == k); + + % level k interpolation + saar = saar_nan; + + saar(:,inds_k) = SAAR_ref([(indsXYZ-(nz+nyz))'; (indsXYZ - nz)'; (indsXYZ)'; (indsXYZ -nyz)']); + + inds_pan = find(abs(long(inds_k)-277.6085)<=17.6085 & ... + abs(lat(inds_k)-9.775) <= 9.775); + + if ~isempty(inds_pan) + inds = inds_k(inds_pan); + saar(:,inds) = gsw_saar_add_barrier(saar(:,inds),long(inds), ... + lat(inds),longs_ref(indsx0(inds)),lats_ref(indsy0(inds)),dlongs_ref,dlats_ref); + end + + if any(isnan(sum(saar(:,inds_k)))) + inds = inds_k(isnan(sum(saar(:,inds_k)))); + saar(:,inds) = gsw_saar_add_mean(saar(:,inds)); + end + + sa_upper(inds_di) = (1-s1(inds_di)).*(saar(1,inds_k)' + ... + r1(inds_di).*(saar(2,inds_k)'-saar(1,inds_k)')) + ... + s1(inds_di).*(saar(4,inds_k)' + ... + r1(inds_di).*(saar(3,inds_k)'-saar(4,inds_k)')); % level k+1 interpolation + + saar = saar_nan; + saar(:,inds_k) = SAAR_ref([(indsXYZ+(1-nz-nyz))'; (indsXYZ+(1-nz))'; (indsXYZ+1)'; (indsXYZ+(1-nyz))';]); + + if ~isempty(inds_pan) + inds = inds_k(inds_pan); + saar(:,inds) = gsw_saar_add_barrier(saar(:,inds),long(inds), ... + lat(inds),longs_ref(indsx0(inds)),lats_ref(indsy0(inds)),dlongs_ref,dlats_ref); + end + + if any(isnan(sum(saar(:,inds_k)))) + inds = inds_k(isnan(sum(saar(:,inds_k)))); + saar(:,inds) = gsw_saar_add_mean(saar(:,inds)); + end + + sa_lower(inds_di) = (1-s1(inds_di)).*(saar(1,inds_k)' + ... + r1(inds_di).*(saar(2,inds_k)'-saar(1,inds_k)')) + ... + s1(inds_di).*(saar(4,inds_k)' + ... + r1(inds_di).*(saar(3,inds_k)'-saar(4,inds_k)')); + + if any(isfinite(sa_upper(inds_di)) & isnan(sa_lower(inds_di))) + inds_different = find(isfinite(sa_upper(inds_di)) & isnan(sa_lower(inds_di))); + sa_lower(inds_di(inds_different)) = sa_upper(inds_di(inds_different)); + end + + SAAR(inds_di) = sa_upper(inds_di) + t1(inds_di).*(sa_lower(inds_di) - sa_upper(inds_di)); + + end +end + +inds = find(~isfinite(SAAR)); +SAAR(inds) = 0; + +in_ocean(inds) = 0; + +end + +%########################################################################## + +function SAAR = gsw_saar_add_mean(saar) + +% gsw_saar_add_mean +%========================================================================== +% +% USAGE: +% SAAR = gsw_saar_add_mean(saar) +% +% DESCRIPTION: +% Replaces NaN's with nanmean of the 4 adjacent neighbours +% +% INPUT: +% saar = Absolute Salinity Anomaly Ratio of the 4 adjacent neighbours +% [ unitless ] +% +% OUTPUT: +% SAAR = nanmean of the 4 adjacent neighbours [ unitless ] +% +% AUTHOR: +% David Jackett +% +% MODIFIED: +% Paul Barker and Trevor McDougall +% Aknowlegments. Matlab programming assisance from Sjoerd Groeskamp. +% +% VERSION NUMBER: 3.05 (27th January 2015) +% +% REFERENCES: +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org +% +% McDougall, T.J., D.R. Jackett, F.J. Millero, R. Pawlowicz and +% P.M. Barker, 2012: A global algorithm for estimating Absolute Salinity. +% Ocean Science, 8, 1123-1134. +% http://www.ocean-sci.net/8/1123/2012/os-8-1123-2012.pdf +% +% The software is available from http://www.TEOS-10.org +% +%========================================================================== + +if exist('nanmean','file') + saar_nanmean = nanmean(saar); + saar_nanmean(2,:) = saar_nanmean; + saar_nanmean(3:4,:) = saar_nanmean; + nans = isnan(saar); + [Inans] = find(isnan(saar)); + saar_mean_nans = nans(Inans).*saar_nanmean(Inans); + saar(Inans) = saar_mean_nans; +else + saar_mean = mean(saar); + inds_nan = find(isnan(saar_mean)); + no_nan = length(inds_nan); + for kk = 1:no_nan + col = inds_nan(kk); + [Inn] = find(~isnan(saar(:,col))); + if ~isempty(Inn) + saar(isnan(saar(:,col)),col) = sum(saar(Inn,col))./numel(Inn); + end + end +end + +SAAR = saar; + +end + +%########################################################################## + +function SAAR = gsw_saar_add_barrier(saar,long,lat,longs_ref,lats_ref,dlongs_ref,dlats_ref) + +% gsw_saar_add_barrier +%========================================================================== +% +% USAGE: +% SAAR = gsw_saar_add_barrier(saar,long,lat,longs_ref,lats_ref,dlongs_ref,dlats_ref) +% +% DESCRIPTION: +% Adds a barrier through Central America (Panama) and then averages +% over the appropriate side of the barrier +% +% INPUT: +% saar = Absolute Salinity Anomaly Ratio [ unitless ] +% long = Longitudes of data in decimal degrees east [ 0 ... +360 ] +% lat = Latitudes of data in decimal degrees north [ -90 ... +90 ] +% longs_ref = Longitudes of regular grid in decimal degrees east [ 0 ... +360 ] +% lats_ref = Latitudes of regular grid in decimal degrees north [ -90 ... +90 ] +% dlongs_ref = Longitude difference of regular grid in decimal degrees [ deg longitude ] +% dlats_ref = Latitude difference of regular grid in decimal degrees [ deg latitude ] +% +% OUTPUT: +% SAAR = Absolute Salinity Anomaly Ratio [ unitless ] +% +% AUTHOR: +% David Jackett +% +% MODIFIED: +% Paul Barker and Trevor McDougall +% +% VERSION NUMBER: 3.05 (27th January 2015) +% +% REFERENCES: +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org +% +% McDougall, T.J., D.R. Jackett, F.J. Millero, R. Pawlowicz and +% P.M. Barker, 2012: A global algorithm for estimating Absolute Salinity. +% Ocean Science, 8, 1123-1134. +% http://www.ocean-sci.net/8/1123/2012/os-8-1123-2012.pdf +% +% The software is available from http://www.TEOS-10.org +% +%========================================================================== + +longs_pan = [260.0000 272.5900 276.5000 278.6500 280.7300 295.2170]; + +lats_pan = [ 19.5500 13.9700 9.6000 8.1000 9.3300 0]; + +lats_lines0 = interp1(longs_pan,lats_pan,long); + +lats_lines1 = interp1(longs_pan,lats_pan,longs_ref); +lats_lines2 = interp1(longs_pan,lats_pan,(longs_ref+dlongs_ref)); + +for k0 = 1:length(long) + if lats_lines0(k0) <= lat(k0) + above_line0 = 1; + else + above_line0 = 0; + end + if lats_lines1(k0) <= lats_ref(k0) + above_line(1) = 1; + else + above_line(1) = 0; + end + if lats_lines1(k0) <= (lats_ref(k0) + dlats_ref) + above_line(4) = 1; + else + above_line(4) = 0; + end + if lats_lines2(k0) <= lats_ref(k0) + above_line(2) = 1; + else + above_line(2) = 0; + end + if lats_lines2(k0) <= (lats_ref(k0) + dlats_ref) + above_line(3) = 1; + else + above_line(3) = 0; + end + saar(above_line ~= above_line0,k0) = nan; % indices of different sides of CA line +end + +if any(isnan(saar)) + saar = gsw_saar_add_mean(saar); +end + +SAAR = saar; + +end diff --git a/Seawater/library/gsw_SA_from_SP_Baltic.m b/Seawater/library/gsw_SA_from_SP_Baltic.m new file mode 100644 index 000000000..0c02ef68d --- /dev/null +++ b/Seawater/library/gsw_SA_from_SP_Baltic.m @@ -0,0 +1,78 @@ +function SA_baltic = gsw_SA_from_SP_Baltic(SP,long,lat) + +% gsw_SA_from_SP_Baltic Calculates Absolute Salinity in the Baltic Sea +%========================================================================== +% +% USAGE: +% SA_baltic = gsw_SA_from_SP_Baltic(SP,long,lat) +% +% DESCRIPTION: +% Calculates Absolute Salinity in the Baltic Sea, from Practical Salinity. +% Since SP is non-negative by definition, this function changes any +% negative input values of SP to be zero. +% Note. This programme will only produce Absolute Salinity values for the +% Baltic Sea. +% +% INPUT: +% SP = Practical Salinity (PSS-78) [ unitless ] +% long = Longitude in decimal degrees east [ 0 ... +360 ] +% lat = Latitude in decimal degrees north [ -90 ... +90 ] +% +% OUTPUT: +% SA_baltic = Absolute Salinity in the Baltic Sea [ g kg^-1 ] +% +% AUTHOR: +% David Jackett, Trevor McDougall & Paul Barker [ help@teos-10.org ] +% +% VERSION NUMBER: 3.05 (27th January 2015) +% +% REFERENCES: +% Feistel, R., S. Weinreben, H. Wolf, S. Seitz, P. Spitzer, B. Adel, +% G. Nausch, B. Schneider and D. G. Wright, 2010: Density and Absolute +% Salinity of the Baltic Sea 2006-2009. Ocean Science, 6, 3-24. +% http://www.ocean-sci.net/6/3/2010/os-6-3-2010.pdf +% +% IOC, SCOR and IAPSO, 2010: The international thermodynamic equation of +% seawater - 2010: Calculation and use of thermodynamic properties. +% Intergovernmental Oceanographic Commission, Manuals and Guides No. 56, +% UNESCO (English), 196 pp. Available from http://www.TEOS-10.org +% +% McDougall, T.J., D.R. Jackett, F.J. Millero, R. Pawlowicz and +% P.M. Barker, 2012: A global algorithm for estimating Absolute Salinity. +% Ocean Science, 8, 1123-1134. +% http://www.ocean-sci.net/8/1123/2012/os-8-1123-2012.pdf +% +% The software is available from http://www.TEOS-10.org +% +%========================================================================== + +if ~(nargin == 3) + error('gsw_SA_from_SP_Baltic: Requires 3 inputs') +end %if + +% This ensures that SP is non-negative. +SP(SP < 0) = 0; + +xb1 = 12.6; +xb2 = 7; +xb3 = 26; +xb1a = 45; +xb3a = 26; + +yb1 = 50; +yb2 = 59; +yb3 = 69; + +SA_baltic = nan(size(SP)); + +if any(xb2 - -% -% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated -% Marine Observing System (IMOS). -% All rights reserved. -% -% Redistribution and use in source and binary forms, with or without -% modification, are permitted provided that the following conditions are met: -% -% * Redistributions of source code must retain the above copyright notice, -% this list of conditions and the following disclaimer. -% * Redistributions in binary form must reproduce the above copyright -% notice, this list of conditions and the following disclaimer in the -% documentation and/or other materials provided with the distribution. -% * Neither the name of the AODN/IMOS nor the names of its contributors -% may be used to endorse or promote products derived from this software -% without specific prior written permission. -% -% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -% POSSIBILITY OF SUCH DAMAGE. -% - -%---------------------- -% Check input parameters -%---------------------- -if nargin ~=3 - error('O2sol.m: Must pass 3 parameters') -end %if - -% Check S,T dimensions and verify consistent -[ms,ns] = size(S); -[mt,nt] = size(T); - - -% Check that T&S have the same shape or are singular -if ((ms~=mt) | (ns~=nt)) & (ms+ns>2) & (mt+nt>2) - error('O2sol: S & T must have same dimensions or be singular') -end %if - -%------ -% BEGIN -%------ - -% convert T to scaled temperature -temp_S = log((298.15 - T)./(273.15 + T)); - -% constants from Table 1 of Garcia & Gordon for the fit to Benson and Krause (1984) -if strcmpi(unit, 'umol/kg') - A0_o2 = 5.80871; - A1_o2 = 3.20291; - A2_o2 = 4.17887; - A3_o2 = 5.10006; - A4_o2 = -9.86643e-2; - A5_o2 = 3.80369; - B0_o2 = -7.01577e-3; - B1_o2 = -7.70028e-3; - B2_o2 = -1.13864e-2; - B3_o2 = -9.51519e-3; - C0_o2 = -2.75915e-7; -elseif strcmpi(unit, 'ml/l') - A0_o2 = 2.00907; - A1_o2 = 3.22014; - A2_o2 = 4.05010; - A3_o2 = 4.94457; - A4_o2 = -2.56847e-1; - A5_o2 = 3.88767; - B0_o2 = -6.24523e-3; - B1_o2 = -7.37614e-3; - B2_o2 = -1.03410e-2; - B3_o2 = -8.17083e-3; - C0_o2 = -4.88682e-7; -else - error('O2sol: unit must be umol/kg or ml/l'); -end - -% Corrected Eqn (8) of Garcia and Gordon 1992 -conc_O2 = exp(A0_o2 + A1_o2*temp_S + A2_o2*temp_S.^2 + A3_o2*temp_S.^3 + A4_o2*temp_S.^4 + A5_o2*temp_S.^5 + ... - S.*(B0_o2 + B1_o2*temp_S + B2_o2*temp_S.^2 + B3_o2*temp_S.^3) + C0_o2*S.^2); - -return \ No newline at end of file diff --git a/Util/getCTDs.m b/Util/getCTDs.m index cb4540049..5368de1fe 100644 --- a/Util/getCTDs.m +++ b/Util/getCTDs.m @@ -1,4 +1,4 @@ -function [fieldTrip ctds sites dataDir] = getCTDs(auto, isCSV) +function [fieldTrip ctds sites dataDir] = getCTDs(auto) %GETCTDS Prompts the user for a field trip ID and data directory. % Retrieves and returns the field trip, all ctds from the DDB that % are related to the field trip, and the selected data directory. @@ -7,7 +7,6 @@ % auto - if true, the user is not prompted to select a field % trip/directory; the values in toolboxProperties are % used. -% isCSV - If true, look for csv files rather than using database. % % Outputs: % fieldTrip - field trip struct - the field trip selected by the user. @@ -52,16 +51,10 @@ ctds = struct; sites = struct; -if isCSV - executeQueryFunc = @executeCSVQuery; -else - executeQueryFunc = @executeDDBQuery; -end - % prompt the user to select a field trip and % directory which contains raw data files if ~auto - [fieldTrip dataDir] = startDialog('profile', isCSV); + [fieldTrip dataDir] = startDialog('profile'); % if automatic, just get the defaults from toolboxProperties.txt else dataDir = readProperty('startDialog.dataDir.profile'); @@ -70,7 +63,7 @@ if isempty(dataDir), error('startDialog.dataDir.profile is not set'); end if isnan(fieldTrip), error('startDialog.fieldTrip.profile is not set'); end - fieldTrip = executeQueryFunc('FieldTrip', 'FieldTripID', fieldTrip); + fieldTrip = executeQuery('FieldTrip', 'FieldTripID', fieldTrip); end % user cancelled start dialog @@ -79,18 +72,18 @@ fId = fieldTrip.FieldTripID; % query the ddb for all ctds related to this field trip -ctds = executeQueryFunc('CTDData', 'FieldTrip', fId); +ctds = executeQuery('CTDData', 'FieldTrip', fId); % query the ddb for all sites related to these ctds lenDep = length(ctds); for i=1:lenDep if i==1 - tempVal = executeQueryFunc('Sites', 'Site', ctds(i).Site); + tempVal = executeQuery('Sites', 'Site', ctds(i).Site); % A CTDData doesn't necessarily has an associated site, % CTDData already contains some site information if ~isempty(tempVal), sites = tempVal; end else - tempVal = executeQueryFunc('Sites', 'Site', ctds(i).Site); + tempVal = executeQuery('Sites', 'Site', ctds(i).Site); if ~isempty(tempVal), sites(i) = tempVal; end end end \ No newline at end of file diff --git a/Util/getDeployments.m b/Util/getDeployments.m index 8b716b21f..9bda6c2f4 100644 --- a/Util/getDeployments.m +++ b/Util/getDeployments.m @@ -1,4 +1,4 @@ -function [fieldTrip deployments sites dataDir] = getDeployments(auto, isCSV) +function [fieldTrip deployments sites dataDir] = getDeployments(auto) %GETDEPLOYMENTS Prompts the user for a field trip ID and data directory. % Retrieves and returns the field trip, all deployments from the DDB that % are related to the field trip, and the selected data directory. @@ -7,7 +7,6 @@ % auto - if true, the user is not prompted to select a field % trip/directory; the values in toolboxProperties are % used. -% isCSV - If true, look for csv files rather than using database. % % Outputs: % fieldTrip - field trip struct - the field trip selected by the user. @@ -50,21 +49,15 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE % POSSIBILITY OF SUCH DAMAGE. % -narginchk(2,2); +narginchk(1,1); deployments = struct; sites = struct; -if isCSV - executeQueryFunc = @executeCSVQuery; -else - executeQueryFunc = @executeDDBQuery; -end - % prompt the user to select a field trip and % directory which contains raw data files if ~auto - [fieldTrip, dataDir] = startDialog('timeSeries', isCSV); + [fieldTrip, dataDir] = startDialog('timeSeries'); % if automatic, just get the defaults from toolboxProperties.txt else dataDir = readProperty('startDialog.dataDir.timeSeries'); @@ -73,7 +66,7 @@ if isempty(dataDir), error('startDialog.dataDir.timeSeries is not set'); end if isnan(fieldTrip), error('startDialog.fieldTrip.timeSeries is not set'); end - fieldTrip = executeQueryFunc('FieldTrip', 'FieldTripID', fieldTrip); + fieldTrip = executeQuery('FieldTrip', 'FieldTripID', fieldTrip); end % user cancelled start dialog @@ -82,14 +75,14 @@ fId = fieldTrip.FieldTripID; % query the ddb/csv file for all deployments related to this field trip -deployments = executeQueryFunc('DeploymentData', 'EndFieldTrip', fId); +deployments = executeQuery('DeploymentData', 'EndFieldTrip', fId); % query the ddb for all sites related to these deployments lenDep = length(deployments); for i=1:lenDep if i==1 - sites = executeQueryFunc('Sites', 'Site', deployments(i).Site); + sites = executeQuery('Sites', 'Site', deployments(i).Site); else - sites(i) = executeQueryFunc('Sites', 'Site', deployments(i).Site); + sites(i) = executeQuery('Sites', 'Site', deployments(i).Site); end end diff --git a/Util/getLatLonForGSW.m b/Util/getLatLonForGSW.m new file mode 100644 index 000000000..0833eb635 --- /dev/null +++ b/Util/getLatLonForGSW.m @@ -0,0 +1,85 @@ +function [ lat, lon ] = getLatLonForGSW( sam ) +%GETLATLONFORGSW retrieves values of latitude and longitude for +% use in the Gibbs-SeaWater toolbox (TEOS-10). +% +% In priority will be considered in sam the following source of lat/lon +% values: +% 1. geospatial_lat_min/max and geospatial_lon_min/max +% +% Inputs: +% sam - structure data set. +% +% Outputs: +% lat - the latitude data retrieved from sam for use in GSW. +% lon - the longitude data retrieved from sam for use in GSW. +% +% Author: Guillaume Galibert +% + +% +% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated +% Marine Observing System (IMOS). +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% * Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in the +% documentation and/or other materials provided with the distribution. +% * Neither the name of the AODN/IMOS nor the names of its contributors +% may be used to endorse or promote products derived from this software +% without specific prior written permission. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. +% +narginchk(1, 1); + +if ~isstruct(sam), error('sam must be a struct'); end +if isempty(sam), return; end + +lat = []; +lon = []; + +if ~isempty(sam.geospatial_lat_min) && ~isempty(sam.geospatial_lat_max) && ... + ~isempty(sam.geospatial_lon_min) && ~isempty(sam.geospatial_lon_max) + if sam.geospatial_lat_min == sam.geospatial_lat_max + lat = sam.geospatial_lat_min; + else + lat = sam.geospatial_lat_min + ... + (sam.geospatial_lat_max - sam.geospatial_lat_min)/2; + end + if sam.geospatial_lon_min == sam.geospatial_lon_max + lon = sam.geospatial_lon_min; + else + lon = sam.geospatial_lon_min + ... + (sam.geospatial_lon_max - sam.geospatial_lon_min)/2; + end +else + disp(['Warning : no geospatial_lat_min/geospatial_lon_min documented for oxygenPP to be applied on ' sam.toolbox_input_file]); + prompt = {'Latitude (South -ve)', 'Longitude (West -ve)'}; + dlg_title = 'Coords'; + num_lines = 1; + defaultans = {'0', '0'}; + answer = inputdlg(prompt,dlg_title,num_lines,defaultans); + + if isempty(answer), return; end + + lat = str2double(answer(1)); + lon = str2double(answer(2)); +end + +end + diff --git a/Util/getPresRelForGSW.m b/Util/getPresRelForGSW.m new file mode 100644 index 000000000..363ac98f8 --- /dev/null +++ b/Util/getPresRelForGSW.m @@ -0,0 +1,136 @@ +function [ presRel, presName ] = getPresRelForGSW( sam ) +%GETPRESRELFORGSW retrieves values of pressure due to sea water in sam for +% use in the Gibbs-SeaWater toolbox (TEOS-10). +% +% In priority will be considered in sam the following source of presRel +% values: +% 1. PRES_REL +% 2. PRES - 1 atmosphere +% 3. gsw_p_from_z(-DEPTH, LATITUDE) +% 4. DEPTH +% 5. gsw_p_from_z(-instrument_nominal_depth, LATITUDE) +% 6. instrument_nominal_depth +% +% Inputs: +% sam - structure data set. +% +% Outputs: +% presRel - the pressure due to sea water data retrieved from sam for +% use in GSW. +% presName - the name of the variable in sam and the method used to +% produce presRel. +% +% Author: Guillaume Galibert +% + +% +% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated +% Marine Observing System (IMOS). +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% * Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in the +% documentation and/or other materials provided with the distribution. +% * Neither the name of the AODN/IMOS nor the names of its contributors +% may be used to endorse or promote products derived from this software +% without specific prior written permission. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. +% +narginchk(1, 1); + +if ~isstruct(sam), error('sam must be a struct'); end +if isempty(sam), return; end + +presRel = NaN; +presName = ''; + +presIdx = getVar(sam.variables, 'PRES'); +presRelIdx = getVar(sam.variables, 'PRES_REL'); +isPresVar = logical(presIdx || presRelIdx); + +isDepthInfo = false; +depthType = 'variables'; +depthIdx = getVar(sam.(depthType), 'DEPTH'); +if depthIdx == 0 + depthType = 'dimensions'; + depthIdx = getVar(sam.(depthType), 'DEPTH'); +end +if depthIdx > 0, isDepthInfo = true; end + +if isfield(sam, 'instrument_nominal_depth') + if ~isempty(sam.instrument_nominal_depth) + isDepthInfo = true; + end +end + +if ~(isPresVar || isDepthInfo), return; end + +% pressure information used for Gibbs SeaWater toolbox is from the +% PRES or PRES_REL variables in priority +if isPresVar + if presRelIdx > 0 + presRel = sam.variables{presRelIdx}.data; + presName = 'PRES_REL'; + else + % update from a relative pressure like SeaBird computes + % it in its processed files, substracting a constant value + % 10.1325 dbar for nominal atmospheric pressure + presRel = sam.variables{presIdx}.data - gsw_P0/10^4; + presName = 'PRES substracting a constant value 10.1325 dbar for nominal atmospheric pressure'; + end +else + % when no pressure variable exists, we use depth information either + % from the DEPTH variable or from the instrument_nominal_depth + % global attribute + if depthIdx > 0 + % with depth data + depth = sam.(depthType){depthIdx}.data; + presName = 'DEPTH'; + else + % with nominal depth information + depth = sam.instrument_nominal_depth*ones(size(temp)); + presName = 'instrument_nominal_depth'; + end + + % any depth values <= -5 are discarded (reminder, depth is + % positive down), this allow use of gsw_p_from_z without error. + depth(depth <= -5) = NaN; + + % pressure information needed for Salinity computation is either + % retrieved from gsw_p_from_z when latitude is available or by + % simply assuming 1dbar ~= 1m + if ~isempty(sam.geospatial_lat_min) && ~isempty(sam.geospatial_lat_max) + % compute depth with Gibbs-SeaWater toolbox + % relative_pressure ~= gsw_p_from_z(-depth, latitude) + if sam.geospatial_lat_min == sam.geospatial_lat_max + presRel = gsw_p_from_z(-depth, sam.geospatial_lat_min); + else + meanLat = sam.geospatial_lat_min + ... + (sam.geospatial_lat_max - sam.geospatial_lat_min)/2; + presRel = gsw_p_from_z(-depth, meanLat); + end + else + % without latitude information, we assume 1dbar ~= 1m + presRel = depth; + presName = [presName ' (assuming 1 m ~ 1 dbar)']; + end +end + +end + diff --git a/Util/parseAttributeValue.m b/Util/parseAttributeValue.m index dbaa34937..89c47bc3b 100644 --- a/Util/parseAttributeValue.m +++ b/Util/parseAttributeValue.m @@ -167,12 +167,6 @@ connection = readProperty('toolbox.ddb.connection'); if isempty(ddb) && (isempty(driver) || isempty(connection)), return; end - %check for CSV file import - isCSV = false; - if isdir(ddb) - isCSV = true; - end - % get the relevant deployment/CTD cast if isfield(sample_data.meta, 'profile') deployment = sample_data.meta.profile; @@ -214,12 +208,7 @@ % a foreign key, so our only choice is to give up if isempty(field_value), return; end - if isCSV - executeQueryFunc = @executeCSVQuery; - else - executeQueryFunc = @executeDDBQuery; - end - result = executeQueryFunc(related_table, related_pkey, field_value); + result = executeQuery(related_table, related_pkey, field_value); if length(result) ~= 1, return; end diff --git a/imosToolbox.m b/imosToolbox.m index 1d1b12f4b..615f212b5 100644 --- a/imosToolbox.m +++ b/imosToolbox.m @@ -49,7 +49,7 @@ function imosToolbox(auto, varargin) % % Set current toolbox version -toolboxVersion = ['2.5.31 - ' computer]; +toolboxVersion = ['2.5.32 - ' computer]; if nargin == 0, auto = 'manual'; end diff --git a/imosToolbox_Linux64.bin b/imosToolbox_Linux64.bin index e2980a022..cfc23a59f 100755 Binary files a/imosToolbox_Linux64.bin and b/imosToolbox_Linux64.bin differ diff --git a/imosToolbox_Win32.exe b/imosToolbox_Win32.exe index f653fd3d5..4abe06fe4 100644 Binary files a/imosToolbox_Win32.exe and b/imosToolbox_Win32.exe differ diff --git a/imosToolbox_Win64.exe b/imosToolbox_Win64.exe index e31e8f24d..aed6a16fc 100644 Binary files a/imosToolbox_Win64.exe and b/imosToolbox_Win64.exe differ diff --git a/snapshot/buildBinaries.py b/snapshot/buildBinaries.py index 329a8ad94..d24e8dbe0 100644 --- a/snapshot/buildBinaries.py +++ b/snapshot/buildBinaries.py @@ -53,9 +53,9 @@ # # create snapshot # -print('\n--building Matlab binaries') -matlabOpts = '-nodisplay -nojvm -wait -logfile "%s"' % compilerLog -matlabCmd = "addpath('Util'); try, imosCompile(); exit(); catch e, disp(e.message); end;" +print('\n--running Matlab unit tests and building binaries') +matlabOpts = '-nosplash -wait -logfile "%s"' % compilerLog +matlabCmd = "addpath('Util', 'test'); try, runTests(); imosCompile(); exit(); catch e, disp(e.message); end;" os.system('cd %s && matlab %s -r "%s"' % (exportDir, matlabOpts, matlabCmd)) print('\n--removing local git tree') diff --git a/test/SBE19Parse/SBE16plus_example1.cnv b/test/SBE19Parse/SBE16plus_example1.cnv new file mode 100644 index 000000000..a7a19700c --- /dev/null +++ b/test/SBE19Parse/SBE16plus_example1.cnv @@ -0,0 +1,346 @@ +* Sea-Bird SBE16plus Data File: +* FileName = C:\Field\Data\Seabird\SBE16plus_01607135_2017_07_18.hex +* Software version 2.3.1 +* Temperature SN = 7135 +* Conductivity SN = 7135 +* System UpLoad Time = Jul 18 2017 00:09:21 +* +* +* 2.3.1 +* 10-Mar-2014 +* +* +* +* +* Sea-Bird Electronics, Inc. +* 2.5 +* 01 May 2012 11:25 +* 2.3 +* +* +* +* +* 17 JUL 2012 +* +* +* temperature0 +* 01607135 +* +* +* conductivity-0 +* 01607135 +* +* +* strain-0 +* 3635907 +* +* +* +* +* not assigned +* not assigned +* +* +* not assigned +* not assigned +* +* +* not assigned +* not assigned +* +* +* not assigned +* not assigned +* +* +* not assigned +* not assigned +* +* +* not assigned +* not assigned +* +* +* not assigned +* not assigned +* +* +* +* +* 2017-07-18T00:03:02 +* not logging +* +* +* 13.1 +* 8.6 +* 62.0 +* 61.9 +* +* +* +* +* 0.3 +* +* +* +* +* 0.4 +* +* +* 326088 +* 15528 +* 3117716 +* 21 +* 8 +* +* +* +* +* 900 +* 18 +* run pump for 0.5 sec +* 4.0 +* 4.0 +* yes +* +* +* 7.5 +* +* +* yes +* yes +* yes +* no +* no +* no +* no +* no +* no +* no +* no +* no +* +* yes +* yes +* converted decimal +* yes +* no +* no +* no +* +* +* 01607135 +* 03-Nov-16 +* 1.290557e-03 +* 2.664605e-04 +* -5.230504e-07 +* 1.566289e-07 +* 0.000000e+00 +* +* +* 01607135 +* 03-Nov-16 +* -9.807054e-01 +* 1.467953e-01 +* -2.753795e-04 +* 4.361402e-05 +* -9.570000e-08 +* 3.250000e-06 +* 1.000000e+00 +* +* +* 3635907 +* 01-Nov-16 +* 1.000558e-01 +* 1.549771e-03 +* 7.998279e-12 +* 5.256527e+05 +* 3.749852e+00 +* -3.908351e-02 +* 2.507438e+01 +* -1.250000e-04 +* 0.000000e+00 +* -5.094646e+01 +* 5.746956e+01 +* -7.111227e-01 +* 0.000000e+00 +* 5.080000e+02 +* +* +* -4.714632e-02 +* 1.247358e+00 +* +* +* -4.748947e-02 +* 1.247746e+00 +* +* +* -4.706526e-02 +* 1.248734e+00 +* +* +* -4.777368e-02 +* 1.247745e+00 +* +* +* -4.693369e-02 +* 1.247567e+00 +* +* +* -4.754105e-02 +* 1.247420e+00 +* +* +* 9.999977e-01 +* +* +* +* +* +* +* +* +* hdr 1 06 Feb 2017 05:59:59 samples 1 to 2000, int = 900, stop = logging +* hdr 2 27 Feb 2017 02:00:14 samples 2001 to 4000, int = 900, stop = logging +* hdr 3 19 Mar 2017 22:00:14 samples 4001 to 6000, int = 900, stop = logging +* hdr 4 09 Apr 2017 18:00:14 samples 6001 to 8000, int = 900, stop = logging +* hdr 5 30 Apr 2017 14:00:14 samples 8001 to 10000, int = 900, stop = logging +* hdr 6 21 May 2017 10:00:14 samples 10001 to 12000, int = 900, stop = logging +* hdr 7 11 Jun 2017 06:00:14 samples 12001 to 14000, int = 900, stop = logging +* hdr 8 02 Jul 2017 02:00:14 samples 14001 to 15528, int = 900, stop = stop cmd +# nquan = 8 +# nvalues = 15528 +# units = specified +# name 0 = timeS: Time, Elapsed [seconds] +# name 1 = prdM: Pressure, Strain Gauge [db] +# name 2 = tv290C: Temperature [ITS-90, deg C] +# name 3 = c0S/m: Conductivity [S/m] +# name 4 = par: PAR/Irradiance, Biospherical/Licor +# name 5 = turbWETntu0: Turbidity, WET Labs ECO [NTU] +# name 6 = flECO-AFL: Fluorescence, WET Labs ECO-AFL/FL [mg/m^3] +# name 7 = flag: 0.000e+00 +# span 0 = 0.000, 13974300.0 +# span 1 = -0.218, 33.442 +# span 2 = 23.5302, 34.8496 +# span 3 = -0.000027, 5.662199 +# span 4 = 3.9705e-02, 2.8047e+03 +# span 5 = -2.3480, 1063.4887 +# span 6 = -0.1003, 43.5424 +# span 7 = 0.0000e+00, 0.0000e+00 +# interval = seconds: 900 +# start_time = Feb 06 2017 06:00:03 [Instrument's time stamp, first data scan] +# bad_flag = -9.990e-29 +# +# +# +# +# 7135 +# 11-Nov-16 +# 1.27513700e-003 +# 2.72348200e-004 +# -1.26833800e-006 +# 1.87886100e-007 +# 1.00000000 +# 0.0000 +# +# +# +# +# +# 7135 +# 11-Nov-16 +# 1 +# +# 0.0000 +# 2000.0000 +# 0 +# +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.0 +# -9.57000000e-008 +# +# +# -9.80614100e-001 +# 1.46767700e-001 +# -2.66339000e-004 +# 4.19254200e-005 +# -9.57000000e-008 +# 3.2500e-006 +# +# 0.00000000e+000 +# +# 1.00000000 +# 0.00000 +# +# +# +# +# +# 7135 +# 01-Nov-16 +# 1.14414700e-001 +# 1.54977000e-003 +# 7.99990900e-012 +# -5.08050900e+001 +# 5.72931200e+001 +# -6.57655600e-001 +# 5.25653000e+005 +# 6.41101400e+000 +# -1.39786500e-001 +# 2.50743800e+001 +# -1.25000000e-004 +# 0.00000000e+000 +# 0.000000 +# +# +# +# +# +# FLS-2744 +# 04-Jan-17 +# 2.50000000e+001 +# +# 0.0150 +# +# +# +# +# +# NTUS-478 +# 04-Jan-17 +# 214.539000 +# +# 0.020100 +# +# +# +# +# +# PARS-303 +# 31-Jan-17 +# 0.89700000 +# 1.37900000 +# 1000000000.00000000 +# 1.35900000 +# 0.00000000 +# +# +# +# datcnv_date = Jul 22 2017 01:53:03, 7.26.6.28 [datcnv_vars = 7] +# datcnv_in = C:\Projects\20170614_ITF15Trip6759\Data\DARBGF-1702-SUB\SBE16\SBE16plus_01607135_2017_07_18.hex C:\Projects\20170614_ITF15Trip6759\Data\DARBGF-1702-SUB\SBE16\SBE16V2_7135_20170722.xmlcon +# datcnv_skipover = 0 +# file_type = ascii +*END* + 0.000 -0.200 26.3672 0.000068 1.1981e+02 1063.4887 37.5551 0.000e+00 + 900.000 -0.201 26.5527 0.000071 3.7105e+02 1063.3741 35.6916 0.000e+00 + 1800.000 -0.206 26.7712 0.000068 3.8913e+02 -2.0370 -0.0755 0.000e+00 + 2700.000 -0.199 26.9260 0.000068 3.5269e+02 -2.0370 -0.0755 0.000e+00 + 3600.000 -0.203 27.0905 0.000065 3.8158e+02 -2.0370 -0.0755 0.000e+00 + 4500.000 -0.206 27.1738 0.000065 1.5807e+02 -2.0370 -0.0641 0.000e+00 + 5400.000 -0.206 27.1970 0.000065 3.3703e+02 8.3895 -0.0755 0.000e+00 + 6300.000 -0.206 27.1773 0.000068 2.3390e+02 -2.0370 -0.0717 0.000e+00 + 7200.000 -0.204 27.1756 0.000065 1.9718e+02 -2.0698 -0.0755 0.000e+00 + 8100.000 -0.204 27.2080 0.000065 1.2120e+02 -2.0534 -0.0736 0.000e+00 diff --git a/test/SBE19Parse/SBE16plus_example2.cnv b/test/SBE19Parse/SBE16plus_example2.cnv new file mode 100644 index 000000000..ba984669e --- /dev/null +++ b/test/SBE19Parse/SBE16plus_example2.cnv @@ -0,0 +1,601 @@ +* Sea-Bird SBE16plus Data File: +* FileName = C:\Users\jan079\Desktop\SBE16plus_01606330_2016_03_30.hex +* Software version 2.4.1 +* Temperature SN = 6330 +* Conductivity SN = 6330 +* System UpLoad Time = Mar 30 2016 00:16:11 +* +* +* 2.4.1 +* 24-Jul-2014 +* +* +* +* +* +* Sea-Bird Electronics, Inc. +* +* 2.5.2 +* +* 12 Mar 2013 11:50 +* +* 2.3 +* +* +* +* +* +* +* +* +* +* 25 MAR 2009 +* +* +* +* +* +* temperature0 +* +* 01606330 +* +* +* +* +* +* conductivity-0 +* +* 01606330 +* +* +* +* +* +* strain-0 +* +* 2774616 +* +* +* +* +* +* +* +* +* +* SBE 43 OXY +* +* 1634 +* +* +* +* +* +* PAR +* +* 134 +* +* +* +* +* +* ECO-FLNTUS CHL +* +* FLNTUS-1186 +* +* +* +* +* +* ECO-FLNTUS TURB +* +* FLNTUS-1186 +* +* +* +* +* +* Optode-3975-DPhase +* +* 1419 +* +* +* +* +* +* Optode-3975-Temp +* +* 1419 +* +* +* +* +* +* GTD +* +* 29-101-15 +* +* +* +* +* +* +* +* +* 2016-03-30T00:06:43 +* +* not logging +* +* +* +* +* +* 11.2 +* +* 9.2 +* +* 61.5 +* +* 47.4 +* +* +* +* +* +* +* +* +* +* 6.0 +* +* +* +* +* +* +* +* +* +* 74.7 +* +* +* +* +* +* 239112 +* +* 8856 +* +* 2428112 +* +* 27 +* +* 5 +* +* +* +* +* +* +* +* +* 3600 +* +* 4 +* +* run pump during sample +* +* 40.0 +* +* 0.0 +* +* no +* +* +* +* +* +* 7.5 +* +* +* +* +* +* yes +* +* yes +* +* yes +* +* yes +* +* yes +* +* yes +* +* no +* +* no +* +* no +* +* no +* +* no +* +* no +* +* +* +* yes +* +* yes +* +* converted decimal +* +* yes +* +* no +* +* no +* +* no +* +* +* +* +* +* +* 01606330 +* +* 25-Mar-2014 +* +* 1.284542e-03 +* +* 2.646954e-04 +* +* -8.391007e-07 +* +* 1.736590e-07 +* +* 0.000000e+00 +* +* +* +* +* +* 01606330 +* +* 25-Mar-2014 +* +* -9.994907e-01 +* +* 1.443822e-01 +* +* -4.957909e-04 +* +* 5.552159e-05 +* +* -9.570000e-08 +* +* 3.250000e-06 +* +* 1.000000e+00 +* +* +* +* +* +* 2774616 +* +* 25-Mar-2014 +* +* -1.802867e+00 +* +* 8.874922e-03 +* +* -1.498780e-10 +* +* 5.249102e+05 +* +* -6.018898e+00 +* +* -3.001014e-01 +* +* 2.520688e+01 +* +* 7.750000e-04 +* +* 0.000000e+00 +* +* -6.184066e+01 +* +* 5.364670e+01 +* +* -7.480319e-01 +* +* 0.000000e+00 +* +* 2.900000e+03 +* +* +* +* +* +* -4.719685e-02 +* +* 1.247874e+00 +* +* +* +* +* +* -4.718527e-02 +* +* 1.248123e+00 +* +* +* +* +* +* -4.676632e-02 +* +* 1.248288e+00 +* +* +* +* +* +* -4.728632e-02 +* +* 1.247814e+00 +* +* +* +* +* +* -4.863685e-02 +* +* 1.249162e+00 +* +* +* +* +* +* -4.848105e-02 +* +* 1.250054e+00 +* +* +* +* +* +* 9.999986e-01 +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* hdr 1 27 Mar 2015 00:00:45 samples 1 to 2000, int = 3600, stop = logging +* +* hdr 2 18 Jun 2015 08:00:45 samples 2001 to 4000, int = 3600, stop = logging +* +* hdr 3 09 Sep 2015 16:00:45 samples 4001 to 6000, int = 3600, stop = logging +* +* hdr 4 02 Dec 2015 00:00:45 samples 6001 to 8000, int = 3600, stop = logging +* +* hdr 5 23 Feb 2016 08:00:45 samples 8001 to 8856, int = 3600, stop = stop cmd +* +* +* +* +# nquan = 21 +# nvalues = 8856 +# units = specified +# name 0 = timeJV2: Time, Instrument [julian days] +# name 1 = timeK: Time, Instrument [seconds] +# name 2 = tv290C: Temperature [ITS-90, deg C] +# name 3 = c0S/m: Conductivity [S/m] +# name 4 = prdM: Pressure, Strain Gauge [db] +# name 5 = sal00: Salinity, Practical [PSU] +# name 6 = depSM: Depth [salt water, m], lat = -46.00 +# name 7 = sbeox0ML/L: Oxygen, SBE 43 [ml/l] +# name 8 = sbeox0PS: Oxygen, SBE 43 [% saturation] +# name 9 = sbeox0Mm/L: Oxygen, SBE 43 [umol/l] +# name 10 = sbeox0Mm/Kg: Oxygen, SBE 43 [umol/kg] +# name 11 = oxsolMm/Kg: Oxygen Saturation, Garcia & Gordon [umol/kg] +# name 12 = v0: Voltage 0 +# name 13 = v1: Voltage 1 +# name 14 = v2: Voltage 2 +# name 15 = v3: Voltage 3 +# name 16 = v4: Voltage 4 +# name 17 = v5: Voltage 5 +# name 18 = potemp090C: Potential Temperature [ITS-90, deg C] +# name 19 = sigma-é00: Density [sigma-theta, kg/m^3] +# name 20 = flag: 0.000e+00 +# span 0 = 86.000498, 454.958831 +# span 1 = 480729643, 512607643 +# span 2 = 7.4457, 13.9244 +# span 3 = 0.024874, 4.196046 +# span 4 = -0.207, 42.391 +# span 5 = 0.1781, 35.0780 +# span 6 = -0.205, 42.039 +# span 7 = 5.3095, 7.8436 +# span 8 = 87.788, 103.569 +# span 9 = 237.121, 350.296 +# span 10 = 231.051, 350.287 +# span 11 = 252.99758, 374.73372 +# span 12 = 2.4389, 2.7999 +# span 13 = 0.0000, 3.7054 +# span 14 = 0.0697, 1.0746 +# span 15 = 0.0562, 4.9339 +# span 16 = 1.9097, 2.1485 +# span 17 = 1.4487, 2.1066 +# span 18 = 7.4457, 13.9201 +# span 19 = -0.3185, 26.8518 +# span 20 = 0.0000e+00, 0.0000e+00 +# interval = seconds: 3600 +# start_time = Mar 27 2015 00:00:43 [Instrument's time stamp, first data scan] +# bad_flag = -9.990e-29 +# +# +# +# +# 6330 +# 25-Mar-2014 +# 1.28454200e-003 +# 2.64695400e-004 +# -8.39100700e-007 +# 1.73659000e-007 +# 1.00000000 +# 0.0000 +# +# +# +# +# +# 6330 +# 25-Mar-2014 +# 1 +# +# 0.0000 +# 2000.0000 +# 0 +# +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.0 +# -9.57000000e-008 +# +# +# -9.99490700e-001 +# 1.44382200e-001 +# -4.95790900e-004 +# 5.55215900e-005 +# -9.57000000e-008 +# 3.2500e-006 +# +# 0.00000000e+000 +# +# 1.00000000 +# 0.00000 +# +# +# +# +# +# 2774616 +# 25-Mar-2014 +# -1.80286700e+000 +# 8.87492200e-003 +# -1.49878000e-010 +# -6.18406600e+001 +# 5.36467000e+001 +# -7.48031900e-001 +# 5.24910200e+005 +# -6.01889800e+000 +# -3.00101400e-001 +# 2.52068800e+001 +# 7.75000000e-004 +# 0.00000000e+000 +# 0.000000 +# +# +# +# +# +# 1634 +# 03-Jun-14 +# 1 +# +# +# 0.0000 +# 0.0000e+000 +# 0.0000 +# 0.00e+000 +# 0.0000 +# 0.0 +# +# +# +# 4.4960e-001 +# -0.4568 +# -3.4010e-003 +# 1.6619e-004 +# -2.6032e-006 +# 2.5826e+000 +# 1.92634e-004 +# -4.64803e-002 +# 3.6000e-002 +# 4.5800 +#

-3.3000e-002

+#

5.0000e+003

+#

1.4500e+003

+#
+#
+#
+# +# +# +# +# +# +# +# +# +# +# +# +# +# +# +#
+# datcnv_date = Aug 03 2017 08:46:00, 7.23.2 [datcnv_vars = 20] +# datcnv_in = E:\ABOS\git\imos-toolbox\data\SBE16plus_01606330_2016_03_30.hex E:\ABOS\git\imos-toolbox\data\SBE16plusV2-2014-03-25.xmlcon +# datcnv_skipover = 0 +# datcnv_ox_hysteresis_correction = yes +# datcnv_ox_tau_correction = yes +# file_type = ascii +*END* + 86.000498 480729643 12.2532 4.016157 29.666 34.9083 29.420 5.6252 93.395 251.223 244.745 262.05291 2.5718 2.5427 0.1717 0.0672 2.0315 1.9203 12.2493 26.4699 0.000e+00 + 86.042164 480733243 12.2192 4.011572 29.798 34.8954 29.551 5.6337 93.463 251.603 245.116 262.26070 2.5729 2.6698 0.1768 0.0715 2.0322 1.9165 12.2153 26.4665 0.000e+00 + 86.083831 480736843 12.2306 4.012741 30.280 34.8960 30.029 5.6338 93.486 251.606 245.119 262.19812 2.5733 2.6338 0.1897 0.0706 2.0314 1.9180 12.2266 26.4647 0.000e+00 + 86.125498 480740443 12.2477 4.014675 30.349 34.8987 30.098 5.6362 93.561 251.712 245.223 262.10029 2.5750 2.7508 0.1611 0.0673 2.0309 1.9205 12.2438 26.4636 0.000e+00 + 86.167164 480744043 12.2996 4.022008 29.488 34.9219 29.244 5.6143 93.311 250.733 244.267 261.77738 2.5696 2.9944 0.1527 0.0684 2.0313 1.9250 12.2957 26.4715 0.000e+00 + 86.208831 480747643 12.3096 4.024103 30.139 34.9326 29.889 5.6164 93.373 250.831 244.360 261.70375 2.5708 2.7315 0.1742 0.0693 2.0303 1.9264 12.3056 26.4779 0.000e+00 + 86.250498 480751243 12.3131 4.024429 30.501 34.9323 30.249 5.6206 93.448 251.014 244.540 261.68511 2.5724 2.5073 0.1800 0.0696 2.0290 1.9266 12.3091 26.4769 0.000e+00 + 86.292164 480754843 12.3044 4.023079 30.089 34.9275 29.840 5.6178 93.383 250.891 244.420 261.74084 2.5711 2.2530 0.1814 0.0660 2.0300 1.9258 12.3005 26.4749 0.000e+00 + 86.333831 480758443 12.2903 4.021290 30.488 34.9232 30.236 5.6179 93.354 250.897 244.426 261.82532 2.5703 1.5058 0.1719 0.0713 2.0311 1.9245 12.2862 26.4743 0.000e+00 + 86.375498 480762043 12.2899 4.021303 30.242 34.9237 29.992 5.6145 93.297 250.744 244.277 261.82608 2.5691 0.0310 0.1691 0.0651 2.0323 1.9240 12.2860 26.4748 0.000e+00 + 86.417164 480765643 12.2887 4.021164 30.938 34.9232 30.682 5.6227 93.431 251.111 244.634 261.83366 2.5719 0.0314 0.1851 0.0708 2.0294 1.9247 12.2846 26.4747 0.000e+00 + 86.458831 480769243 12.2889 4.021237 30.222 34.9240 29.972 5.6134 93.277 250.694 244.228 261.83107 2.5686 0.0314 0.1861 0.0719 2.0320 1.9246 12.2849 26.4752 0.000e+00 + 86.500498 480772843 12.2898 4.021476 30.405 34.9254 30.153 5.6172 93.344 250.866 244.395 261.82359 2.5701 0.0313 0.1897 0.0673 2.0312 1.9248 12.2858 26.4762 0.000e+00 + 86.542164 480776443 12.2912 4.021596 30.071 34.9254 29.822 5.6156 93.319 250.793 244.324 261.81595 2.5696 0.0314 0.1881 0.0688 2.0313 1.9248 12.2873 26.4759 0.000e+00 + 86.583831 480780043 12.2911 4.021562 29.691 34.9254 29.445 5.6157 93.321 250.799 244.330 261.81672 2.5698 0.0313 0.1876 0.0697 2.0314 1.9244 12.2872 26.4758 0.000e+00 + 86.625498 480783643 12.2920 4.021915 30.174 34.9277 29.924 5.6157 93.324 250.797 244.328 261.80746 2.5697 0.0311 0.1963 0.0695 2.0307 1.9248 12.2880 26.4775 0.000e+00 + 86.667164 480787243 12.2803 4.020319 29.489 34.9235 29.245 5.6216 93.397 251.062 244.586 261.87860 2.5715 0.0314 0.2045 0.0661 2.0301 1.9239 12.2764 26.4765 0.000e+00 + 86.708831 480790843 12.2477 4.016011 29.835 34.9120 29.588 5.6292 93.453 251.402 244.918 262.07616 2.5727 0.0314 0.2074 0.0687 2.0301 1.9200 12.2438 26.4739 0.000e+00 + 86.750498 480794443 12.2356 4.013851 29.669 34.9024 29.424 5.6325 93.479 251.549 245.063 262.15936 2.5733 0.0313 0.1825 0.0687 2.0311 1.9197 12.2317 26.4687 0.000e+00 + 86.792164 480798043 12.2416 4.015034 30.106 34.9081 29.857 5.6309 93.467 251.477 244.991 262.11631 2.5729 0.0313 0.1748 0.0686 2.0305 1.9182 12.2377 26.4720 0.000e+00 + 86.833831 480801643 12.2178 4.012084 29.621 34.9018 29.376 5.6316 93.428 251.508 245.022 262.25662 2.5721 0.0314 0.1717 0.0686 2.0314 1.9155 12.2139 26.4717 0.000e+00 + 86.875498 480805243 12.2741 4.019774 30.423 34.9236 30.171 5.6066 93.135 250.390 243.931 261.91197 2.5653 1.4967 0.1748 0.0673 2.0330 1.9223 12.2701 26.4778 0.000e+00 \ No newline at end of file diff --git a/test/SBE19Parse/SBE19plus_example.cnv b/test/SBE19Parse/SBE19plus_example.cnv new file mode 100644 index 000000000..d58ebee99 --- /dev/null +++ b/test/SBE19Parse/SBE19plus_example.cnv @@ -0,0 +1,128 @@ +* Sea-Bird SBE19plus Data File: +* FileName = C:\Moorings\Instruments\Seabird\SBE19+\#4550\Test_#4550_16102007.hex +* Software Version 1.53 +* Temperature SN = 4550 +* Conductivity SN = 4550 +* System UpLoad Time = Oct 15 2007 22:44:30 +** #4550 test dip n16102007 +* ds +* SeacatPlus V 1.4D SERIAL NO. 4550 15 Oct 2007 22:45:32 +* vbatt = 13.7, vlith = 8.3, ioper = 63.2 ma, ipump = 86.2 ma, +* iext01 = 78.2 ma, iext23 = 48.5 ma, +* status = not logging +* number of scans to average = 1 +* samples = 3136, free = 438369, casts = 1 +* mode = profile, minimum cond freq = 3220, pump delay = 40 sec +* autorun = no, ignore magnetic switch = no +* battery type = ALKALINE, battery cutoff = 7.5 volts +* pressure sensor = strain gauge, range = 508.0 +* SBE 38 = no, Gas Tension Device = no +* Ext Volt 0 = yes, Ext Volt 1 = yes, Ext Volt 2 = yes, Ext Volt 3 = yes +* echo commands = yes +* output format = raw HEX +* S> +* +* SeacatPlus V 1.4D SERIAL NO. 4550 15 Oct 2007 22:45:39 +* temperature: 04-jan-04 +* TA0 = 1.252846e-03 +* TA1 = 2.602469e-04 +* TA2 = 9.298618e-08 +* TA3 = 1.414404e-07 +* TOFFSET = 0.000000e+00 +* conductivity: 04-jan-04 +* G = -1.008771e+00 +* H = 1.367372e-01 +* I = -2.165181e-04 +* J = 3.426306e-05 +* CF0 = 2.719748e+03 +* CPCOR = -9.570000e-08 +* CTCOR = 3.250000e-06 +* CSLOPE = 1.000000e+00 +* pressure S/N = 4712, range = 508 psia: 22-jan-04 +* PA0 = -5.251767e-02 +* PA1 = 1.548220e-03 +* PA2 = 7.906893e-12 +* PTCA0 = 5.244425e+05 +* PTCA1 = 1.017406e+00 +* PTCA2 = -1.383457e-01 +* PTCB0 = 2.503050e+01 +* PTCB1 = -1.700000e-03 +* PTCB2 = 0.000000e+00 +* PTEMPA0 = -7.909481e+01 +* PTEMPA1 = 4.898603e+01 +* PTEMPA2 = -4.618534e-01 +* POFFSET = 0.000000e+00 +* volt 0: offset = -4.624333e-02, slope = 1.247090e+00 +* volt 1: offset = -4.609000e-02, slope = 1.249782e+00 +* volt 2: offset = -4.606667e-02, slope = 1.249771e+00 +* volt 3: offset = -4.582333e-02, slope = 1.249673e+00 +* EXTFREQSF = 9.999982e-01 +* cast 1 15 Oct 2007 22:32:14 samples 1 to 3136, avg = 1, stop = mag switch +* S> +# nquan = 8 +# nvalues = 3136 +# units = specified +# name 0 = tv290C: Temperature [ITS-90, deg C] +# name 1 = c0S/m: Conductivity [S/m] +# name 2 = prdM: Pressure, Strain Gauge [db] +# name 3 = par: PAR/Irradiance, Biospherical/Licor +# name 4 = sbeox0Mg/L: Oxygen, SBE 43 [mg/l] +# name 5 = flECO-AFL: Fluorescence, Wetlab ECO-AFL/FL [mg/m^3] +# name 6 = upoly0: Upoly 0, turbidity +# name 7 = flag: 0.000e+00 +# span 0 = 11.0565, 13.6879 +# span 1 = 0.000030, 3.949913 +# span 2 = -0.104, 10.762 +# span 3 = 2.4298e+00, 1.2431e+03 +# span 4 = 4.64806, 6.71399 +# span 5 = -0.0736, 5.4021 +# span 6 = 0.3002258, 16.136039 +# span 7 = 0.0000e+00, 0.0000e+00 +# interval = seconds: 0.25 +# start_time = Oct 15 2007 22:32:14 +# bad_flag = -9.990e-29 +# sensor 0 = Frequency 0 temperature, 4550, 04-Jan-04 +# sensor 1 = Frequency 1 conductivity, 4550, 04-Jan-04, cpcor = -9.5700e-08 +# sensor 2 = Pressure Number +# sensor 3 = Extrnl Volt 0 irradiance (PAR), primary, 4686, 1/29/04 +# sensor 4 = Extrnl Volt 1 Oxygen, SBE, primary, 0606, 1/31/04 +# sensor 5 = Extrnl Volt 2 WET Labs, ECO_AFL +# sensor 6 = Extrnl Volt 3 userpoly 0, 0096, 8/16/07 +# datcnv_date = Oct 15 2007 22:59:53, 5.37e +# datcnv_in = C:\Moorings\Instruments\Seabird\SBE19+\#4550\Test_#4550_16102007.hex C:\Moorings\Instruments\Seabird\SBE19+\#4550\4550_15102007.con +# datcnv_skipover = 0 +# file_type = ascii +*END* + 13.6717 0.000045 -0.093 3.6466e+02 6.26102 -0.0095 0.3639322 0.000e+00 + 13.6724 0.000039 -0.096 3.6071e+02 6.26093 -0.0087 0.3650767 0.000e+00 + 13.6731 0.000039 -0.093 3.6626e+02 6.26174 -0.0065 0.3662211 0.000e+00 + 13.6743 0.000048 -0.094 3.6924e+02 6.26335 -0.0538 0.3456214 0.000e+00 + 13.6753 0.000039 -0.096 3.7184e+02 6.26146 -0.0591 0.3479103 0.000e+00 + 13.6762 0.000039 -0.095 3.7473e+02 6.25957 -0.0583 0.3475288 0.000e+00 + 13.6774 0.000039 -0.094 3.7591e+02 6.26326 -0.0324 0.3326513 0.000e+00 + 13.6784 0.000039 -0.094 3.7684e+02 6.26018 -0.0347 0.3345586 0.000e+00 + 13.6793 0.000048 -0.095 3.7657e+02 6.26154 -0.0332 0.3330327 0.000e+00 + 13.6802 0.000048 -0.092 3.7737e+02 6.26349 -0.0347 0.3402808 0.000e+00 + 13.6813 0.000039 -0.091 3.7459e+02 6.26069 -0.0324 0.3414252 0.000e+00 + 13.6825 0.000039 -0.094 3.7413e+02 6.26231 -0.0347 0.3402808 0.000e+00 + 13.6833 0.000039 -0.097 3.7374e+02 6.26251 1.9482 13.889148 0.000e+00 + 13.6843 0.000039 -0.095 3.7591e+02 6.26032 1.9497 13.884570 0.000e+00 + 13.6847 0.000039 -0.093 3.7512e+02 6.26263 1.9490 13.885333 0.000e+00 + 13.6852 0.000039 -0.094 3.6891e+02 6.26315 5.3990 16.132605 0.000e+00 + 13.6861 0.000048 -0.095 3.5337e+02 6.26215 5.4021 16.136039 0.000e+00 + 13.6874 0.000039 -0.094 3.3061e+02 6.26170 5.4013 16.122687 0.000e+00 + 13.6879 0.000039 -0.093 3.1713e+02 6.25955 -0.0019 0.3479103 0.000e+00 + 13.6878 0.000048 -0.093 3.3688e+02 6.26194 0.0004 0.3467659 0.000e+00 + 13.6847 0.000039 -0.097 3.5250e+02 6.26027 0.0004 0.3479103 0.000e+00 + 13.6802 0.000039 -0.093 3.1089e+02 6.26438 -0.0416 0.3715618 0.000e+00 + 13.6738 0.000039 -0.095 3.3125e+02 6.26193 -0.0446 0.3727062 0.000e+00 + 13.6661 0.000039 -0.094 3.1998e+02 6.26439 -0.0454 0.3707988 0.000e+00 + 13.6625 0.000048 -0.095 3.0817e+02 6.26396 -0.0301 0.3700359 0.000e+00 + 13.6619 0.000039 -0.095 3.0510e+02 6.26670 -0.0332 0.3707988 0.000e+00 + 13.6622 0.000039 -0.094 3.2933e+02 6.26459 -0.0324 0.3704173 0.000e+00 + 13.6603 0.000048 -0.095 3.3382e+02 6.26573 -0.0530 0.3528695 0.000e+00 + 13.6571 0.000039 -0.094 3.3842e+02 6.26672 -0.0568 0.3528695 0.000e+00 + 13.6548 0.000039 -0.094 3.8030e+02 6.26612 -0.0591 0.3501991 0.000e+00 + 13.6511 0.000039 -0.095 3.5331e+02 6.26719 -0.0721 0.3578286 0.000e+00 + 13.6456 0.000048 -0.094 3.8765e+02 6.26937 -0.0736 0.3574472 0.000e+00 + 13.6422 0.000039 -0.096 3.7657e+02 6.26862 -0.0713 0.3589731 0.000e+00 \ No newline at end of file diff --git a/test/SBE19Parse/SBE25plus_example.cnv b/test/SBE19Parse/SBE25plus_example.cnv new file mode 100644 index 000000000..980143665 --- /dev/null +++ b/test/SBE19Parse/SBE25plus_example.cnv @@ -0,0 +1,379 @@ +* +* +* +* 1.0.1-RC1 +* May 12 2016 17:01:04 +* +* +* +* +* 2017-07-17T04:32:43 +* +* +* 17.8 +* 0.5 +* 7.3 +* 3.2 +* 0.0 +* 41.5 +* 11.5 +* +* +* 3932160 +* 1976467456 +* 54613 +* 27450936 +* 0 +* 0 +* +* +* +* Sea-Bird Electronics, Inc +* 1.0.1-RC1 +* May 12 2016 17:01:04 +* 1.0 +* +* +* 2012-06-05T09:00:00 +* +* +* temperature-0 +* 1449 +* +* +* conductivity-0 +* 041182 +* +* +* strain-0 +* 2099823 +* +* +* +* +* SBE 43 OXY +* 430609 +* +* +* NOT SET +* NOT SET +* +* +* WETSTAR +* WS3S-163 +* +* +* CSTAR +* CST-1310PR +* +* +* NTUS TURB +* NTUS-204 +* +* +* NOT SET +* NOT SET +* +* +* PAR +* PARS-142 +* +* +* NOT SET +* NOT SET +* +* +* NOT SET +* NOT SET +* +* +* NOT SET +* NOT SET +* +* +* +* +* +* +* 115200 +* 1 +* +* +* 1 +* ser1 +* 9600 +* 1 +* S> +* ts +* 0 +* start +* 254 +* 254 +* 1 +* 3 +* 60 +* +* +* 1 +* ser2 +* 9600 +* 1 +* S> +* ts +* 0 +* start +* 254 +* 254 +* 1 +* 3 +* 60 +* +* +* +* 0 +* 0 +* 0 +* 0 +* 3000 +* 60 +* 0 +* +* +* 0 +* 1 +* 1 +* 0 +* 1 +* 1 +* 1 +* 1 +* 1 +* 0 +* 8 +* +* +* +* +* 2099823 +* 2016-12-05 +* -7.259445e-01 +* 5.537804e-04 +* 2.567358e-13 +* -9.062648e+01 +* 4.241693e+01 +* 1.059932e+00 +* 8.402710e+06 +* -2.249790e+02 +* 7.479463e+00 +* 1.041081e+02 +* -6.131089e-03 +* 0.000000e+00 +* 0.000000e+00 +* 2.900000e+03 +* +* +* +* +* +* +* +* +* +* +* +* +* 2017-07-17T04:32:46 +* +# nquan = 7 +# nvalues = 130494 +# units = specified +# name 0 = timeS: Time, Elapsed [seconds] +# name 1 = t090C: Temperature [ITS-90, deg C] +# name 2 = prdM: Pressure, Strain Gauge [db] +# name 3 = c0S/m: Conductivity [S/m] +# name 4 = wetStar: Fluorescence, WET Labs WETstar [mg/m^3] +# name 5 = turbWETntu0: Turbidity, WET Labs ECO [NTU] +# name 6 = flag: 0.000e+00 +# span 0 = 0.000, 8155.813 +# span 1 = 27.2397, 31.5110 +# span 2 = 0.205, 1.054 +# span 3 = 0.006190, 5.568382 +# span 4 = -0.4620, 5.9310 +# span 5 = -1.2808, 128.6220 +# span 6 = 0.0000e+00, 0.0000e+00 +# interval = seconds: 0.0625 +# start_time = Jul 17 2017 04:32:46 [Instrument's time stamp, header] +# bad_flag = -9.990e-29 +# +# +# +# +# 1449 +# 14-Dec-16 +# 1 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.000 +# 4.32378931e-003 +# 6.27135652e-004 +# 1.93500544e-005 +# 1.40866912e-006 +# 1000.000 +# 1.00000000 +# 0.0000 +# +# +# +# +# +# 1182 +# 01-Dec-16 +# 1 +# +# 0.0000 +# 2000.0000 +# 0 +# +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.0 +# -9.57000000e-008 +# +# +# -4.05082640e+000 +# 5.42965466e-001 +# 1.38678282e-004 +# 2.22753651e-005 +# -9.57000000e-008 +# 3.2500e-006 +# +# 0.00000000e+000 +# +# 1.00000000 +# 0.00000 +# +# +# +# +# +# 1009 +# 05-Dec-2016 +# -7.25944500e-001 +# 5.53780400e-004 +# 2.56735800e-013 +# -9.06264800e+001 +# 4.24169300e+001 +# 1.05993200e+000 +# 8.40271000e+006 +# -2.24979000e+002 +# 7.47946300e+000 +# 1.04108125e+002 +# -6.13108851e-003 +# 0.00000000e+000 +# 0.000000 +# +# +# +# +# +# 0609 +# 16-Nov-16 +# 1 +# +# +# 0.0000 +# 0.0000e+000 +# 0.0000 +# 0.00e+000 +# 0.0000 +# 0.0 +# +# +# +# 4.4032e-001 +# -0.5051 +# -4.9798e-003 +# 2.0182e-004 +# -2.9475e-006 +# 2.5826e+000 +# 1.92634e-004 +# -4.64803e-002 +# 3.6000e-002 +# 1.3600 +#

-3.3000e-002

+#

5.0000e+003

+#

1.4500e+003

+#
+#
+#
+# +# +# +# +# +# +# WS3S-163 +# 28-Dec-2016 +# 6.600 +# +# 0.070 +# +# +# +# +# +# CST-1310PR +# 26-Jan-2017 +# 21.9516 +# -1.2073 +# 0.250 +# +# +# +# +# +# NTURTD-558 +# 09-Dec-2016 +# 26.000000 +# +# 0.053000 +# +# +# +# +# +# +# +# +# 0392 +# 29-Nov-2016 +# 0.88750000 +# 1.32920000 +# 1000000000.00000000 +# 1.35890000 +# 0.00000000 +# +# +# +# +# +#
+# datcnv_date = Jul 17 2017 23:01:21, 7.26.6.28 [datcnv_vars = 6] +# datcnv_in = C:\Projects\20170614_ITF15Trip6759\Data\CalBathCTD_1009\2017-07-17T043241 SBE0251009ITF17018.xml C:\Projects\20170614_ITF15Trip6759\Data\CalBathCTD_1009\25Plus_1009_20161116.xmlcon +# datcnv_skipover = 0 +# file_type = ascii +*END* + 0.000 31.3914 0.239 0.006202 -0.1845 -0.2017 0.000e+00 + 0.063 31.4212 0.240 0.006199 -0.1850 -0.2017 0.000e+00 + 0.125 31.4284 0.239 0.006193 -0.4620 -0.1937 0.000e+00 + 0.188 31.4320 0.228 0.006199 -0.0793 26.1455 0.000e+00 + 0.250 31.4339 0.240 0.006202 -0.4620 36.8751 0.000e+00 + 0.313 31.4325 0.240 0.006204 -0.4620 36.3495 0.000e+00 + 0.375 31.4284 0.244 0.006199 -0.4620 5.3883 0.000e+00 + 0.438 31.4271 0.240 0.006199 -0.4620 -1.2094 0.000e+00 + 0.500 31.4246 0.232 0.006195 -0.4620 -0.3405 0.000e+00 + 0.563 31.4245 0.235 0.006200 -0.4620 -0.3624 0.000e+00 diff --git a/test/SBE19Parse/SBE37_example.cnv b/test/SBE19Parse/SBE37_example.cnv new file mode 100644 index 000000000..94ea88e45 --- /dev/null +++ b/test/SBE19Parse/SBE37_example.cnv @@ -0,0 +1,292 @@ +* Sea-Bird SBE37SM-RS232 Data File: +* FileName = C:\FIELD\Trip6634\SBE37\SBE37087421702.hex +* Software version SeatermV2 2.4.1 +* Temperature SN = 8742 +* Conductivity SN = 8742 +* System UpLoad Time = Feb 13 2017 23:41:28 +* sample interval = 10 seconds +* +* +* 2.4.1 +* 24-Jul-2014 +* +* +* +* +* +* Sea-Bird Electronics, Inc. +* +* 3.0j +* +* 22 June 2010 14:30 +* +* 41647 +* +* 41610B +* +* 41611D +* +* 31 Oct 2011 +* +* SBE 37 FirmwareLoader V 1.0 +* +* +* +* +* +* temperature-1 +* +* 03708742 +* +* +* +* +* +* conductivity-1 +* +* 03708742 +* +* +* +* +* +* strain-0 +* +* 3418683 +* +* +* +* +* +* +* +* +* 2017-02-13T23:20:13 +* +* +* +* +* +* 6.94 +* +* 3.12 +* +* +* +* +* +* 8388585 +* +* 559239 +* +* 1 +* +* 15 +* +* +* +* no, out of memory +* +* +* +* +* yes +* +* no +* +* converted engineering +* +* yes +* +* no +* +* no +* +* 10 +* +* no +* +* +* +* +* +* +* 03708742 +* +* 12-07-2016 +* +* -1.735021e-04 +* +* 3.260736e-04 +* +* -6.096529e-06 +* +* 2.436910e-07 +* +* +* +* +* +* 03708742 +* +* 12-07-2016 +* +* -9.780402e-01 +* +* 1.336480e-01 +* +* -8.312528e-05 +* +* 2.425185e-05 +* +* -9.570000e-08 +* +* 3.250000e-06 +* +* 2.463883e-07 +* +* +* +* +* +* 3418683 +* +* 12-07-2016 +* +* 1.406163e-01 +* +* 1.747486e-03 +* +* 1.181905e-11 +* +* 5.243889e+05 +* +* 1.255491e+00 +* +* -9.448175e-02 +* +* 2.502638e+01 +* +* 7.500000e-05 +* +* 0.000000e+00 +* +* -5.650160e+01 +* +* 5.489303e-02 +* +* -6.702464e-07 +* +* 0.000000e+00 +* +* 5.080000e+02 +* +* +* +* +* +* +* +* +* +* +* +# nquan = 5 +# nvalues = 559239 +# units = specified +# name 0 = timeS: Time, Elapsed [seconds] +# name 1 = tv290C: Temperature [ITS-90, deg C] +# name 2 = cond0S/m: Conductivity [S/m] +# name 3 = prdM: Pressure, Strain Gauge [db] +# name 4 = flag: 0.000e+00 +# span 0 = 0.000, 5592380.00 +# span 1 = 21.9211, 30.3350 +# span 2 = -0.051305, 5.597191 +# span 3 = -0.204, 102.968 +# span 4 = 0.0000e+00, 0.0000e+00 +# interval = seconds: 10 +# start_time = Jul 30 2016 00:00:01 [Instrument's time stamp, first data scan] +# bad_flag = -9.990e-29 +# +# +# +# +# 8742 +# 12-07-2016 +# -1.73502100e-004 +# 3.26073600e-004 +# -6.09652900e-006 +# 2.43691000e-007 +# 1.00000000 +# 0.0000 +# +# +# +# +# +# 8742 +# 12-07-2016 +# 1 +# +# 0.0000 +# 2000.0000 +# 1 +# +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.0 +# -9.57000000e-008 +# +# +# -9.78040200e-001 +# 1.33648000e-001 +# -8.31252800e-005 +# 2.42518500e-005 +# -9.57000000e-008 +# 3.2500e-006 +# +# 2.46388300e-007 +# +# 1.00000000 +# 0.00000 +# +# +# +# +# +# 3418683 +# 12-07-2016 +# 1.40616300e-001 +# 1.74748600e-003 +# 1.18190500e-011 +# -5.65016000e+001 +# 5.48930300e-002 +# -6.70246400e-007 +# 5.24388900e+005 +# 1.25549100e+000 +# -9.44817500e-002 +# 2.50263800e+001 +# 7.50000000e-005 +# 0.00000000e+000 +# 0.000000 +# +# +# +# datcnv_date = Feb 13 2017 23:42:01, 7.22.5 [datcnv_vars = 4] +# datcnv_in = C:\FIELD\Trip6634\SBE37\sbe37087421702.hex C:\FIELD\Trip6634\SBE37\SBE37087421702.xmlcon +# datcnv_skipover = 0 +# file_type = ascii +*END* + 0.000 22.4577 -0.000021 -0.022 0.000e+00 + 10.000 22.4580 -0.000024 -0.021 0.000e+00 + 20.000 22.4572 -0.000024 -0.021 0.000e+00 + 30.000 22.4577 -0.000024 -0.021 0.000e+00 + 40.000 22.4581 -0.000024 -0.019 0.000e+00 + 50.000 22.4583 -0.000024 -0.021 0.000e+00 + 60.000 22.4588 -0.000024 -0.021 0.000e+00 + 70.000 22.4584 -0.000024 -0.022 0.000e+00 + 80.000 22.4602 -0.000024 -0.019 0.000e+00 + 90.000 22.4618 -0.000027 -0.022 0.000e+00 + 100.000 22.4620 -0.000024 -0.019 0.000e+00 diff --git a/test/SBE19Parse/SBE39plus_example.cnv b/test/SBE19Parse/SBE39plus_example.cnv new file mode 100644 index 000000000..cafea980d --- /dev/null +++ b/test/SBE19Parse/SBE39plus_example.cnv @@ -0,0 +1,30 @@ +* Sea-Bird SBE39plus Data File: +* SerialNumber: 03907660 +* FirmwareVersion: 4.2.0 +* PressureInstalled: yes +# nquan = 4 +# nvalues = 288277 +# units = specified +# name 0 = t090C: Temperature [ITS-90, deg C] +# name 1 = prM: Pressure [db] +# name 2 = timeJ: Julian Days +# name 3 = flag: Flag +# span 0 = 7.9006, 30.4858 +# span 1 = -0.236, 421.222 +# span 2 = 211.000012, 411.191678 +# span 3 = 0.0000e+00, 0.0000e+00 +# interval = seconds: 60.000000 +# start_time = Jul 29 2016 00:00:01 +# bad_flag = -9.990e-29 +# datcnv_date = Feb 14 2017 06:13:13, SeatermV2 version 2.6.1.12 +# datcnv_in = C:\Field\Data\Seabird\76601702.xml +# file_type = ascii +*END* + 22.1976 -0.025 211.000012 0.0000e+00 + 22.2136 -0.040 211.000706 0.0000e+00 + 22.2250 -0.041 211.001400 0.0000e+00 + 22.2350 -0.034 211.002095 0.0000e+00 + 22.2447 -0.038 211.002789 0.0000e+00 + 22.2573 -0.025 211.003484 0.0000e+00 + 22.2669 -0.031 211.004178 0.0000e+00 + 22.2784 -0.022 211.004873 0.0000e+00 diff --git a/test/SBE19Parse/SBE9_example.cnv b/test/SBE19Parse/SBE9_example.cnv new file mode 100644 index 000000000..5b843e084 --- /dev/null +++ b/test/SBE19Parse/SBE9_example.cnv @@ -0,0 +1,264 @@ +* Sea-Bird SBE 9 Data File: +* FileName = \\sol-nas\Data\Field Trips\6759\Data\LivewireCTD\NRSDAR\IMOS_ANMN-NRS_CPTU_170715_FV00_CTDPRO_0700.hex +* Software Version Seasave V 7.23.2 +* Temperature SN = 5165 +* Conductivity SN = 4406 +* Number of Bytes Per Scan = 44 +* Number of Voltage Words = 5 +* Number of Scans Averaged by the Deck Unit = 1 +* Append System Time to Every Scan +* System UpLoad Time = Jul 14 2017 21:27:12 +* NMEA Latitude = 12 20.71 S +* NMEA Longitude = 130 42.78 E +* NMEA UTC (Time) = Jul 14 2017 21:27:10 +* Store Lat/Lon Data = Append to Every Scan +* SBE 11plus V 5.1g +* number of scans to average = 1 +* pressure baud rate = 9600 +* NMEA baud rate = 4800 +* surface PAR voltage added to scan +* A/D offset = 2 +* Latitude/Longitude added to scan +* GPIB address = 1 +* advance primary conductivity 0.073 seconds +* advance secondary conductivity 0.073 seconds +* autorun on power up is disabled +* S> +** Ship: Solander +** Station: NRSDAR +** Operator: JW +** 0700 (2130 UTC) +* System UTC = Jul 14 2017 21:27:12 +# nquan = 10 +# nvalues = 7127 +# units = specified +# name 0 = timeY: Time, System [seconds] +# name 1 = c0mS/cm: Conductivity [mS/cm] +# name 2 = prDM: Pressure, Digiquartz [db] +# name 3 = depSM: Depth [salt water, m] +# name 4 = t090C: Temperature [ITS-90, deg C] +# name 5 = sal00: Salinity, Practical [PSU] +# name 6 = CStarTr0: Beam Transmission, WET Labs C-Star [%] +# name 7 = cpar: CPAR/Corrected Irradiance [%] +# name 8 = flECO-AFL: Fluorescence, WET Labs ECO-AFL/FL [mg/m^3] +# name 9 = flag: 0.000e+00 +# span 0 = 1500067632, 1500067929 +# span 1 = 4.458492, 53.948377 +# span 2 = -1.906, 18.108 +# span 3 = -1.895, 18.004 +# span 4 = 26.2308, 26.7154 +# span 5 = 2.2918, 34.3414 +# span 6 = 38.8090, 77.6957 +# span 7 = 1.2111e-11, 8.1312e+01 +# span 8 = 0.0062, 0.7535 +# span 9 = 0.0000e+00, 0.0000e+00 +# interval = seconds: 0.0416667 +# start_time = Jul 14 2017 21:27:12 [System UTC, first data scan.] +# bad_flag = -9.990e-29 +# +# +# +# +# 5165 +# 04-May-17 +# 1 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.000 +# 4.36215353e-003 +# 6.38014996e-004 +# 2.23111663e-005 +# 2.04820968e-006 +# 1000.000 +# 1.00000000 +# 0.0000 +# +# +# +# +# +# 4406 +# 21-Apr-17 +# 1 +# +# 0.0000 +# 2000.0000 +# 0 +# +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.0 +# -9.57000000e-008 +# +# +# -9.80690680e+000 +# 1.31331722e+000 +# -2.60380945e-003 +# 2.42896521e-004 +# -9.57000000e-008 +# 3.2500e-006 +# +# 0.00000000e+000 +# +# 1.00000000 +# 0.00000 +# +# +# +# +# +# 1233 +# 02-May-17 +# -4.058365e+004 +# -7.722238e-001 +# 1.217800e-002 +# 3.470900e-002 +# 0.000000e+000 +# 3.020273e+001 +# -5.366770e-004 +# 3.993990e-006 +# 2.858150e-009 +# 0.99997000 +# -0.63090 +# 0.000000e+000 +# 1.277210e-002 +# -9.290330e+000 +# +# +# +# +# +# 6003 +# 04-May-17 +# 1 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.000 +# 4.35846556e-003 +# 6.68823803e-004 +# 2.70950809e-005 +# 2.15574357e-006 +# 1000.000 +# 1.00000000 +# 0.0000 +# +# +# +# +# +# 4437 +# 19-Apr-17 +# 1 +# +# 0.0000 +# 2000.0000 +# 0 +# +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.00000000e+000 +# 0.0 +# -9.57000000e-008 +# +# +# -9.80939884e+000 +# 1.71074815e+000 +# -7.38892108e-004 +# 1.56481204e-004 +# -9.57000000e-008 +# 3.2500e-006 +# +# 0.00000000e+000 +# +# 1.00000000 +# 0.00000 +# +# +# +# +# +# FLRTD-3870 +# 20-Apr-17 +# 6.00000000e+000 +# +# 0.0710 +# +# +# +# +# +# +# +# +# CST-1728DR +# 19-May-17 +# 21.3460 +# -0.0256 +# 0.250 +# +# +# +# +# +# +# +# +# 65820 +# 24-May-17 +# 15.000 +# 0.000 +# +# +# +# +# +# +# +# +# 70561 +# 30-May-17 +# 1.00000000 +# 0.00000000 +# 18761726079.00000000 +# 1.00000000 +# -0.05491958 +# +# +# +# +# +# +# +# +# +# +# +# 20506 +# 30-May-17 +# 1690.60950000 +# 1.00000000 +# +# +# +# datcnv_date = Jul 15 2017 19:50:44, 7.26.6.28 [datcnv_vars = 9] +# datcnv_in = \\SOL-NAS\Data\Field Trips\6759\Data\LivewireCTD\NRSDAR\IMOS_ANMN-NRS_CPTU_170715_FV00_CTDPRO_0700.hex \\SOL-NAS\Data\Field Trips\6759\Data\LivewireCTD\911plus_1233_dualTC_20170714.xmlcon +# datcnv_skipover = 0 +# file_type = ascii +*END* + 1500067632 53.916716 2.357 2.343 26.6892 34.3347 65.6283 3.1136e+01 0.4824 0.000e+00 + 1500067632 53.916716 2.330 2.317 26.6892 34.3348 65.6283 3.1044e+01 0.4824 0.000e+00 + 1500067632 53.916287 2.311 2.297 26.6892 34.3345 65.6283 3.0682e+01 0.4824 0.000e+00 + 1500067632 53.916216 2.330 2.317 26.6890 34.3345 65.6283 3.0503e+01 0.4824 0.000e+00 + 1500067632 53.916858 2.284 2.271 26.6885 34.3354 65.6283 3.0503e+01 0.4824 0.000e+00 + 1500067632 53.916787 2.311 2.297 26.6887 34.3352 65.6283 3.0324e+01 0.4824 0.000e+00 + 1500067632 53.916287 2.330 2.317 26.6885 34.3350 65.6283 3.0147e+01 0.4824 0.000e+00 + 1500067632 53.916573 2.311 2.297 26.6883 34.3353 65.6283 3.0147e+01 0.4824 0.000e+00 + 1500067632 53.916287 2.284 2.271 26.6879 34.3354 65.6283 2.9882e+01 0.4824 0.000e+00 + 1500067632 53.915858 2.284 2.271 26.6879 34.3351 65.6283 2.9620e+01 0.4824 0.000e+00 diff --git a/test/SBE19Parse/testSBE19Parse.m b/test/SBE19Parse/testSBE19Parse.m new file mode 100644 index 000000000..977dda1d5 --- /dev/null +++ b/test/SBE19Parse/testSBE19Parse.m @@ -0,0 +1,56 @@ +function testSBE19Parse() +%TESTSBE19PARSE exercises the function SBE19Parse. This function parses +% Sea-Bird DataProcessor generated files. +% +% Author: Peter Jansen +% Contributor: Guillaume Galibert +% + +% +% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated +% Marine Observing System (IMOS). +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% * Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in the +% documentation and/or other materials provided with the distribution. +% * Neither the name of the AODN/IMOS nor the names of its contributors +% may be used to endorse or promote products derived from this software +% without specific prior written permission. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. +% + +% we check that the instrument model description is properly picked-up by +% the regexp in parseInstrumentHeader function from SBE19Parse +testFilesInstruments = {... + 'SBE19plus_example.cnv', 'SBE19plus'; ... + 'SBE9_example.cnv', 'SBE9'; ... + 'SBE16plus_example1.cnv', 'SBE16plus'; ... + 'SBE16plus_example2.cnv', 'SBE16plus'; ... + 'SBE25plus_example.cnv', 'SBE25plus'; ... + 'SBE37_example.cnv', 'SBE37SM-RS232'; ... + 'SBE39plus_example.cnv', 'SBE39plus'}; + +for i=1:length(testFilesInstruments) + clear sample_data; + sample_data = SBE19Parse({fullfile('test', 'SBE19Parse', testFilesInstruments{i, 1})}, 'timeSeries'); + assert(strcmp(sample_data.meta.instrument_model, testFilesInstruments{i, 2}), ... + ['Failed to parse ' testFilesInstruments{i, 1}]); +end +end \ No newline at end of file diff --git a/test/oxygenPP/SBE16plus_example1.cnv b/test/oxygenPP/SBE16plus_example1.cnv new file mode 100644 index 000000000..698940a03 --- /dev/null +++ b/test/oxygenPP/SBE16plus_example1.cnv @@ -0,0 +1,18 @@ +* Sea-Bird SBE16plus Data File: +* FileName = SBE16plus_example.hex +* Software version 2.4.1 + +# name 0 = timeJV2: Time, Instrument [julian days] +# name 1 = timeK: Time, Instrument [seconds] +# name 2 = tv290C: Temperature [ITS-90, deg C] +# name 3 = prdM: Pressure, Strain Gauge [db] +# name 4 = sal00: Salinity, Practical [PSU] +# name 5 = sbeox0ML/L: Oxygen, SBE 43 [ml/l] +# name 6 = flag: 0.000e+00 +# bad_flag = -9.990e-29 + +*END* + 86.000498 480729643 12.253200 29.666 34.9083 5.6252 0.000e+00 + 86.042164 480733243 12.229500 6330.549 32.8283 13.6856 0.000e+00 + 86.083831 480736843 14.996401 100.000 35.0000 5.6252 0.000e+00 + 86.125498 480740443 14.996401 3000.000 35.0000 5.6252 0.000e+00 diff --git a/test/oxygenPP/SBE16plus_example2.cnv b/test/oxygenPP/SBE16plus_example2.cnv new file mode 100644 index 000000000..0d7da31e1 --- /dev/null +++ b/test/oxygenPP/SBE16plus_example2.cnv @@ -0,0 +1,15 @@ +* Sea-Bird SBE16plus Data File: +* FileName = SBE16plus_example.hex +* Software version 2.4.1 + +# name 0 = timeJV2: Time, Instrument [julian days] +# name 1 = timeK: Time, Instrument [seconds] +# name 2 = tv290C: Temperature [ITS-90, deg C] +# name 3 = prdM: Pressure, Strain Gauge [db] +# name 4 = sal00: Salinity, Practical [PSU] +# name 5 = sbeox0Mm/L: Oxygen, SBE 43 [umol/l] +# name 6 = flag: 0.000e+00 +# bad_flag = -9.990e-29 + +*END* + 86.000498 480729643 12.253200 29.666 35.0000 251.2192 0.000e+00 diff --git a/test/oxygenPP/SBE16plus_example3.cnv b/test/oxygenPP/SBE16plus_example3.cnv new file mode 100644 index 000000000..224c3657e --- /dev/null +++ b/test/oxygenPP/SBE16plus_example3.cnv @@ -0,0 +1,15 @@ +* Sea-Bird SBE16plus Data File: +* FileName = SBE16plus_example.hex +* Software version 2.4.1 + +# name 0 = timeJV2: Time, Instrument [julian days] +# name 1 = timeK: Time, Instrument [seconds] +# name 2 = tv290C: Temperature [ITS-90, deg C] +# name 3 = prdM: Pressure, Strain Gauge [db] +# name 4 = sal00: Salinity, Practical [PSU] +# name 5 = sbeox0PS: Oxygen, SBE 43 [% saturation] +# name 6 = flag: 0.000e+00 +# bad_flag = -9.990e-29 + +*END* + 86.000498 480729643 12.253200 29.666 35.0000 90.0 0.000e+00 diff --git a/test/oxygenPP/testOxygenPP.m b/test/oxygenPP/testOxygenPP.m new file mode 100644 index 000000000..d9ff32c77 --- /dev/null +++ b/test/oxygenPP/testOxygenPP.m @@ -0,0 +1,93 @@ +function testOxygenPP() +%TESTOXYGENPP exercises the function oxygenPP. This function is a +% pre-processing routine that adds oxygen parameters to the dataset. +% +% Author: Peter Jansen +% Contributor: Guillaume Galibert +% + +% +% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated +% Marine Observing System (IMOS). +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% * Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in the +% documentation and/or other materials provided with the distribution. +% * Neither the name of the AODN/IMOS nor the names of its contributors +% may be used to endorse or promote products derived from this software +% without specific prior written permission. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. +% +clear sample_data; + +testDir = fullfile('test', 'oxygenPP'); + +% Test 1: Oxygen Solubility from TEMP and PSAL +sample_data{1} = SBE19Parse({fullfile(testDir, 'SBE16plus_example1.cnv')}, 'timeSeries'); + +sample_data{1}.geospatial_lat_min = -46; +sample_data{1}.geospatial_lat_max = -46; +sample_data{1}.geospatial_lon_min = 142; +sample_data{1}.geospatial_lon_max = 142; + +sample_data = oxygenPP(sample_data, 'qc'); + +oxsolSurfSBE = sample_data{1}.variables{getVar(sample_data{1}.variables, 'OXSOL_SURFACE')}.data; +dox2SBE = sample_data{1}.variables{getVar(sample_data{1}.variables, 'DOX2')}.data; +doxsSBE = sample_data{1}.variables{getVar(sample_data{1}.variables, 'DOXS')}.data; + +assert(any(abs(oxsolSurfSBE - [262.05291; 271.6910; 247.8694; 250.209]) <= 1e-2), 'Failed: Oxygen Solubility from SBE TEMP and PSAL Check'); + +% Test 2: DOX2 from DOX +assert(any(abs(dox2SBE - [244.745; 596.267; 244.8609; 244.8362]) <= 1e-2), 'Failed: DOX2 from SBE DOX Check'); + +% Test 3: DOXS from DOX +assert(any(abs(doxsSBE/100 - [0.93395; 2.1946267; 0.9878625; 0.9785269]) <= 1e-4), 'Failed: DOXS from SBE DOX Check'); + +% test 4: DOX2 from DOX1 +clear sample_data; +sample_data{1} = SBE19Parse({fullfile(testDir, 'SBE16plus_example2.cnv')}, 'timeSeries'); + +sample_data{1}.geospatial_lat_min = -46; +sample_data{1}.geospatial_lat_max = -46; +sample_data{1}.geospatial_lon_min = 142; +sample_data{1}.geospatial_lon_max = 142; + +sample_data = oxygenPP(sample_data, 'qc'); + +dox2SBE = sample_data{1}.variables{getVar(sample_data{1}.variables, 'DOX2')}.data; + +assert(any(abs(dox2SBE - 244.7239) <= 1e-2), 'Failed: DOX2 from SBE DOX1 Check'); + +% test 5: DOX2 from DOXS +clear sample_data; +sample_data{1} = SBE19Parse({fullfile(testDir, 'SBE16plus_example3.cnv')}, 'timeSeries'); + +sample_data{1}.geospatial_lat_min = -46; +sample_data{1}.geospatial_lat_max = -46; +sample_data{1}.geospatial_lon_min = 142; +sample_data{1}.geospatial_lon_max = 142; + +sample_data = oxygenPP(sample_data, 'qc'); + +dox2SBE = sample_data{1}.variables{getVar(sample_data{1}.variables, 'DOX2')}.data; + +assert(any(abs(dox2SBE - 235.6999) <= 1e-2), 'Failed: DOX2 from SBE DOXS Check'); +end \ No newline at end of file diff --git a/test/runTests.m b/test/runTests.m new file mode 100644 index 000000000..8e4c302a9 --- /dev/null +++ b/test/runTests.m @@ -0,0 +1,54 @@ +function runTests() +%RUNTESTS Runs the different unit tests. +% +% Author: Guillaume Galibert +% + +% +% Copyright (c) 2016, Australian Ocean Data Network (AODN) and Integrated +% Marine Observing System (IMOS). +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% * Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in the +% documentation and/or other materials provided with the distribution. +% * Neither the name of the AODN/IMOS nor the names of its contributors +% may be used to endorse or promote products derived from this software +% without specific prior written permission. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. +% + +path = pwd; + +% set Matlab path for this session (add all recursive directories to Matlab +% path) +searchPath = textscan(genpath(path), '%s', 'Delimiter', pathsep); +searchPath = searchPath{1}; +iPathToRemove = ~cellfun(@isempty, strfind(searchPath, [filesep '.'])); +searchPath(iPathToRemove) = []; +searchPath = cellfun(@(x)([x pathsep]), searchPath, 'UniformOutput', false); +searchPath = [searchPath{:}]; +addpath(searchPath); + +% run the unit tests +testOxygenPP(); +testSBE19Parse(); + +end + diff --git a/toolboxProperties.txt b/toolboxProperties.txt index b1184f30f..5c236c85a 100644 --- a/toolboxProperties.txt +++ b/toolboxProperties.txt @@ -48,12 +48,12 @@ autoQCManager.autoQCChain.profile = imosImpossibleDateQC imosImpossibleLocationS preprocessManager.preprocessPrompt = true % default set of preprocessing routines -preprocessManager.preprocessDefaultChain.timeSeries = depthPP salinityPP magneticDeclinationPP absiDecibelBasicPP adcpNortekVelocityEnu2BeamPP adcpBinMappingPP adcpNortekVelocityBeam2EnuPP -preprocessManager.preprocessDefaultChain.profile = depthPP salinityPP +preprocessManager.preprocessDefaultChain.timeSeries = depthPP salinityPP oxygenPP magneticDeclinationPP absiDecibelBasicPP adcpNortekVelocityEnu2BeamPP adcpBinMappingPP adcpNortekVelocityBeam2EnuPP +preprocessManager.preprocessDefaultChain.profile = depthPP salinityPP oxygenPP % last selected set of preprocessing routines -preprocessManager.preprocessChain.timeSeries = depthPP salinityPP magneticDeclinationPP absiDecibelBasicPP adcpNortekVelocityEnu2BeamPP adcpBinMappingPP adcpNortekVelocityBeam2EnuPP -preprocessManager.preprocessChain.profile = depthPP salinityPP +preprocessManager.preprocessChain.timeSeries = depthPP salinityPP oxygenPP magneticDeclinationPP absiDecibelBasicPP adcpNortekVelocityEnu2BeamPP adcpBinMappingPP adcpNortekVelocityBeam2EnuPP +preprocessManager.preprocessChain.profile = depthPP salinityPP oxygenPP % file status dialog formatting styles % can be one of 'bold', 'normal', 'italic', or an HTML colour (e.g. 'red',