Skip to content

Commit cf226ec

Browse files
authored
Improvements in pvl_getISDdata.m, pvl_readtmy3.m, and pvl_spa.m
Correctly calculate distances between lat/lon points in pvl_getISDdata.m. Allow pvl_readtmy3.m to read TMY3 files from before January 2015 and after January 2015. Improve the default delta T calculation within pvl_spa.m
2 parents 5fd4a24 + 3abd593 commit cf226ec

File tree

3 files changed

+285
-26
lines changed

3 files changed

+285
-26
lines changed

pvl_getISDdata.m

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
function outfilen = pvl_getISDdata(lat,lon,yr,archive)
1+
function outfilen = pvl_getISDdata(latitude,longitude,year,archive)
22
%
33
% getISDdata: fetch data for one year of integrated surface data for location
44
%
55
% Syntax
66
% data = getISDdata(latitude,longitude,year,archive)
77
%
88
% Inputs:
9-
% latitude - latitude of location of interest
10-
% longitude - longitude of location of interest
9+
% latitude - latitude of location of interest in decimal degrees
10+
% longitude - longitude of location of interest in decimal degrees
1111
% year - desired year
1212
% archive - string specifying path to local ISD archive. Current
1313
% directory is used if archive is not specified.
1414
%
1515
% Output:
16-
% outfilen - name for the returned data file
16+
% outfilen - name for the returned data file in the form "USAF-WBAN-yyyy"
17+
% where USAF is the USAF ID of the station and WBAN is the WBAN ID of
18+
% the system, as provided by the ISD.
1719
%
1820
% Notes:
1921
% This function copies up to two files from NOAA's ftp site to the
@@ -40,18 +42,30 @@
4042
mod = vers(pos+6);
4143
if versyr<2013 || ((versyr==2013) && mod=='a')
4244
display(['Matlab ' vers ' detected'])
43-
display('Function pvl_getISDdata requires R2013b or later')
44-
return
45+
error('Function pvl_getISDdata requires R2013b or later')
4546
end
46-
47-
%% Get index data
48-
% Change pwd to a local directory if you want to use an archive
49-
if nargin < 4
47+
%% Check the input data
48+
p = inputParser;
49+
p.addRequired('latitude',@(x) all(isscalar(x) & isnumeric(x) & x<=90 & x>=-90));
50+
p.addRequired('longitude', @(x) all(isscalar(x) & isnumeric(x) & x<=180 & x>=-180));
51+
p.addRequired('year',@(x) all(isscalar(x) & isnumeric(x) & (x >= 1901)));
52+
p.addOptional('archive', @(x) ischar(x));
53+
p.parse(latitude, longitude, year, varargin{:});
54+
55+
defaultchecker = {'archive'};
56+
if ~any(strcmp(defaultchecker,p.UsingDefaults))
5057
archive = pwd;
5158
end
5259

60+
lat = p.Results.latitude;
61+
lon = p.Results.longitude;
62+
yr = p.Results.year;
63+
64+
%% Get index data
65+
% Change pwd to a local directory if you want to use an archive
66+
5367
isdfile = 'isd-history.csv';
54-
fname = [archive '\' isdfile];
68+
fname = [archive filesep isdfile];
5569
if ~exist(fname,'file')
5670
% Get index file
5771
ftphandle = ftp('ftp.ncdc.noaa.gov');
@@ -69,17 +83,23 @@
6983
index.Properties.VariableNames{'ELEV_M_'} = 'ELEV';
7084
index.USAF = char(index.USAF);
7185
index.WBAN = char(index.WBAN);
72-
index.LAT(strcmp('',index.LAT)) = {'99999'};
73-
index.LON(strcmp('',index.LON)) = {'99999'};
74-
index.ELEV(strcmp('',index.ELEV)) = {'99999'};
86+
index.LAT(strcmp('',index.LAT)) = {'NaN'};
87+
index.LON(strcmp('',index.LON)) = {'NaN'};
88+
index.ELEV(strcmp('',index.ELEV)) = {'NaN'};
7589
index.LAT = str2num(char(index.LAT)); %#ok<*ST2NM>
7690
index.LON = str2num(char(index.LON));
7791
index.ELEV = str2num(char(index.ELEV));
7892
index.BEGIN = datenum(char(index.BEGIN),'yyyymmdd');
7993
index.END = datenum(char(index.END),'yyyymmdd');
8094

81-
%% Find row for closest location (might iterate through several locations)
82-
dist = hypot(index.LAT-lat,index.LON-lon);
95+
%% Find row for closest location (might iterate through several locations) using haversine formula
96+
% calculate the argument for the haversine formula, make sure the argument
97+
% is between -1 and 1
98+
arg = sqrt((sind((index.LAT - lat) ./ 2)).^2 + cosd(index.LAT).*cosd(lat).*(sind((index.LON-lon)./2)).^2);
99+
arg(arg < -1) = -1;
100+
arg(arg > 1) = 1;
101+
% compute the distance in meters between the provided lat/lon and the each lat/lon from the isd.
102+
dist = 2 .* 6371008.8 .* asin(arg); % mean earth radius is 6371.0088 km
83103
[~,idx] = sort(dist);
84104

85105
% Find first row that contains year (might iterate through several years)
@@ -99,10 +119,15 @@
99119

100120
%
101121
% get file from ftp server if not in archive
102-
fname = [archive '\' outfilen];
122+
fname = [archive filesep outfilen];
103123
if ~exist(fname,'file')
104124
addr = sprintf('ftp://ftp.ncdc.noaa.gov/pub/data/noaa/%4d/',yr);
105-
gunzip([addr '/' outfilen '.gz'],archive);
125+
try
126+
gunzip([addr '/' outfilen '.gz'],archive);
127+
catch
128+
error(['Unable to reach the ftp site for the integrated surface database.' ...
129+
' The data file cannot be accessed.']);
130+
end
106131
%% Alt approach if gunzip doesn't seem to be working
107132
% % cd(ftphandle,num2str(yr));
108133
% % gunzip(mget(ftphandle,[fname '.gz']));

pvl_readtmy3.m

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
% If a FileName is not provided, the user will be prompted to browse to
1616
% an appropriate TMY3 file.
1717
%
18+
% TMY3 format was changed slightly on January 19, 2015 ([2]) to include
19+
% Present weather variables along with changes to other variables.
20+
%
1821
% Input
1922
% FileName - an optional argument which allows the user to select which
2023
% TMY3 format file should be read. A file path may also be necessary if
@@ -106,12 +109,23 @@
106109
% TMYData.Lprecipquantity - The period of accumulation for the liquid precipitation depth field, hour
107110
% TMYData.LprecipSource - See [1], Table 1-5, 8760x1 cell array of strings
108111
% TMYData.LprecipUncertainty - See [1], Table 1-6
112+
% TMYData.PresWth - Present weather code, see [2]. Only available in
113+
% TMY3 files created after the January 2015 TMY3 update.
114+
% TMYData.PresWthSource - Present weather code source, see [2]. Only
115+
% available in TMY3 files created after the Jan. 2015 TMY3 update.
116+
% TMYData.PresWthUncertainty - Present weather code uncertainty, see
117+
% [2]. Only available in TMY3 files after the Jan. 2015 update.
109118
%
110119
% Reference
111120
% [1] Wilcox, S and Marion, W., 2008. Users Manual for TMY3 Data Sets,
112121
% NREL/TP-581-43156, National Renewable Energy Laboratory. Available at
113122
% <http://www.nrel.gov/docs/fy08osti/43156.pdf>.
114123
%
124+
% [2] National Solar Radiation Data Base, 1991-2005 Update: Typical
125+
% Meteorological Year 3.
126+
% http://rredc.nrel.gov/solar/old_data/nsrdb/1991-2005/tmy3/ retrieved June
127+
% 6, 2016.
128+
%
115129
%
116130
% See also
117131
% DATEVEC PVL_MAKELOCATIONSTRUCT PVL_MAKETIMESTRUCT PVL_READTMY2
@@ -136,15 +150,118 @@
136150
Header2 = textscan(FileID, '%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s',1,'Delimiter',',');
137151
DataLines = textscan(FileID, '%s%s%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%f%s%f',8760,'Delimiter',',');
138152
ST = fclose(FileID);
139-
%% Read in the data from the struct created from the textscan
153+
154+
% Parse out the header information since it is common to both formats
140155
TMYData.SiteID = Header1{1};
141156
TMYData.StationName = Header1{2};
142157
TMYData.StationState = Header1{3};
143158
TMYData.SiteTimeZone = Header1{4};
144159
TMYData.SiteLatitude = Header1{5};
145160
TMYData.SiteLongitude = Header1{6};
146161
TMYData.SiteElevation = Header1{7};
162+
%% Determine if the file is from pre-January 19, 2015 or post-20150119
163+
if isempty(strfind(char(DataLines{1,1}{1,1}), 'PresWth'))
164+
% The string 'PresWth' was not found in DataLines{1,1}{1,1}, so we have a
165+
% pre-2015 TMY3 file
166+
TMYData = ParsePre2015TMY(DataLines);
167+
else
168+
% The string 'PresWth' was found in DataLines{1,1}{1,1}, so we have a
169+
% post-2015 TMY3 file. We'll need to re-import the file to accomodate
170+
% the additional columns.
171+
FileID = fopen(FilePathAndNameAndExt);
172+
Header1 = textscan(FileID, '%f%q%q%f%f%f%f', 1,'Delimiter',',');
173+
Header2 = textscan(FileID, '%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s',1,'Delimiter',',');
174+
DataLines = textscan(FileID, '%s%s%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%s%f%f%f%s%f%f%s%f',8760,'Delimiter',',');
175+
ST = fclose(FileID);
176+
177+
TMYData = ParsePost2015TMY(DataLines);
178+
end
179+
end
147180

181+
function TMYData = ParsePre2015TMY(DataLines)
182+
183+
%% Read in the data from the struct created from the textscan for TMY3
184+
% This fuction will read pre-2015 TMY3 files and sort them into column
185+
% vectors. A different format is used for TMY3 files after the January 2015
186+
% update, and thus requires a different function.
187+
188+
TMYData.DateString = DataLines{1};
189+
TMYData.TimeString = DataLines{2};
190+
TMYData.ETR = DataLines{3};
191+
TMYData.ETRN = DataLines{4};
192+
TMYData.GHI = DataLines{5};
193+
TMYData.GHISource = DataLines{6};
194+
TMYData.GHIUncertainty = DataLines{7};
195+
TMYData.DNI = DataLines{8};
196+
TMYData.DNISource = DataLines{9};
197+
TMYData.DNIUncertainty = DataLines{10};
198+
TMYData.DHI = DataLines{11};
199+
TMYData.DHISource = DataLines{12};
200+
TMYData.DHIUncertainty = DataLines{13};
201+
TMYData.GHillum = DataLines{14};
202+
TMYData.GHillumSource = DataLines{15};
203+
TMYData.GHillumUncertainty = DataLines{16};
204+
TMYData.DNillum = DataLines{17};
205+
TMYData.DNillumSource = DataLines{18};
206+
TMYData.DNillumUncertainty = DataLines{19};
207+
TMYData.DHillum = DataLines{20};
208+
TMYData.DHillumSource = DataLines{21};
209+
TMYData.DHillumUncertainty = DataLines{22};
210+
TMYData.Zenithlum = DataLines{23};
211+
TMYData.ZenithlumSource = DataLines{24};
212+
TMYData.ZenithlumUncertainty = DataLines{25};
213+
TMYData.TotCld = DataLines{26};
214+
TMYData.TotCldSource = DataLines{27};
215+
TMYData.TotCldUnertainty = DataLines{28};
216+
TMYData.OpqCld = DataLines{29};
217+
TMYData.OpqCldSource = DataLines{30};
218+
TMYData.OpqCldUncertainty = DataLines{31};
219+
TMYData.DryBulb = DataLines{32};
220+
TMYData.DryBulbSource = DataLines{33};
221+
TMYData.DryBulbUncertainty = DataLines{34};
222+
TMYData.DewPoint = DataLines{35};
223+
TMYData.DewPointSource = DataLines{36};
224+
TMYData.DewPointUncertainty = DataLines{37};
225+
TMYData.RHum = DataLines{38};
226+
TMYData.RHumSource = DataLines{39};
227+
TMYData.RHumUncertainty = DataLines{40};
228+
TMYData.Pressure = DataLines{41};
229+
TMYData.PressureSource = DataLines{42};
230+
TMYData.PressureUncertainty = DataLines{43};
231+
TMYData.Wdir = DataLines{44};
232+
TMYData.WdirSource = DataLines{45};
233+
TMYData.WdirUncertainty = DataLines{46};
234+
TMYData.Wspd = DataLines{47};
235+
TMYData.WspdSource = DataLines{48};
236+
TMYData.WspdUncertainty = DataLines{49};
237+
TMYData.Hvis = DataLines{50};
238+
TMYData.HvisSource = DataLines{51};
239+
TMYData.HvisUncertainty = DataLines{52};
240+
TMYData.CeilHgt = DataLines{53};
241+
TMYData.CeilHgtSource = DataLines{54};
242+
TMYData.CeilHgtUncertainty = DataLines{55};
243+
TMYData.Pwat = DataLines{56};
244+
TMYData.PwatSource = DataLines{57};
245+
TMYData.PwatUncertainty = DataLines{58};
246+
TMYData.AOD = DataLines{59};
247+
TMYData.AODSource = DataLines{60};
248+
TMYData.AODUncertainty = DataLines{61};
249+
TMYData.Alb = DataLines{62};
250+
TMYData.AlbSource = DataLines{63};
251+
TMYData.AlbUncertainty = DataLines{64};
252+
TMYData.Lprecipdepth = DataLines{65};
253+
TMYData.Lprecipquantity = DataLines{66};
254+
TMYData.LprecipSource = DataLines{67};
255+
TMYData.LprecipUncertainty = DataLines{68};
256+
%% Create a MATLAB datenum from the string date and time
257+
TMYData.DateNumber = datenum(strcat(TMYData.DateString,'-',TMYData.TimeString),'mm/dd/yyyy-HH:MM'); %MATLAB datenum format
258+
end
259+
260+
261+
262+
function TMYData = ParsePost2015TMY(DataLines)
263+
264+
%% Read in the data from the struct created from the textscan
148265
TMYData.DateString = DataLines{1};
149266
TMYData.TimeString = DataLines{2};
150267
TMYData.ETR = DataLines{3};
@@ -213,5 +330,9 @@
213330
TMYData.Lprecipquantity = DataLines{66};
214331
TMYData.LprecipSource = DataLines{67};
215332
TMYData.LprecipUncertainty = DataLines{68};
333+
TMYData.PresWth = DataLines{69};
334+
TMYData.PresWthSource = DataLines{70};
335+
TMYData.PresWthUncertainty = DataLines{71};
216336
%% Create a MATLAB datenum from the string date and time
217-
TMYData.DateNumber = datenum(strcat(TMYData.DateString,'-',TMYData.TimeString),'mm/dd/yyyy-HH:MM'); %MATLAB datenum format
337+
TMYData.DateNumber = datenum(strcat(TMYData.DateString,'-',TMYData.TimeString),'mm/dd/yyyy-HH:MM'); %MATLAB datenum format
338+
end

0 commit comments

Comments
 (0)