第一次提交
This commit is contained in:
164
.gitignore
vendored
Normal file
164
.gitignore
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
# 唐超添加
|
||||
/.idea
|
||||
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
72
Setup_Window.m
Normal file
72
Setup_Window.m
Normal file
@ -0,0 +1,72 @@
|
||||
function [windows] = Setup_Window()
|
||||
|
||||
windows = [];
|
||||
% -----Attention: fitting window range for sFLD<EFBFBD><EFBFBD>3FLD<EFBFBD><EFBFBD>SFM<EFBFBD><EFBFBD>SVD<EFBFBD><EFBFBD>DOAS;----- %
|
||||
% <EFBFBD><EFBFBD><EFBFBD><EFBFBD>sFLD, iFLD, pFLD
|
||||
FLD = [];
|
||||
FLD.outerH = [656.0, 656.2]; % <EFBFBD>˵㲨<EFBFBD>Σ<EFBFBD>ȡ<EFBFBD><EFBFBD>ֵ
|
||||
FLD.outerB = [687.0, 687.2];
|
||||
FLD.outerW = [717.0, 717.2];
|
||||
FLD.outerA = [758.6, 758.8];
|
||||
FLD.windowH = [656, 659]; % <EFBFBD><EFBFBD>Χ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Сֵ
|
||||
FLD.windowB = [686, 689];
|
||||
FLD.windowW = [717, 722];
|
||||
FLD.windowA = [758, 770];
|
||||
windows.FLD = FLD;
|
||||
|
||||
% 3FLD
|
||||
FLD3 = [];
|
||||
FLD3.FLD3_leftH = [656.0, 656.2]; FLD3.FLD3_rightH = [659.0, 659.2];
|
||||
FLD3.FLD3_leftB = [687.0, 687.2]; FLD3.FLD3_rightB = [689.0, 689.2];
|
||||
FLD3.FLD3_leftW = [717.0, 717.2]; FLD3.FLD3_rightW = [722.0, 722.2];
|
||||
FLD3.FLD3_leftA = [758.6, 758.8]; FLD3.FLD3_rightA = [770.0, 770.2];
|
||||
FLD3.windowH = [656, 659]; % <EFBFBD><EFBFBD>Χ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Сֵ
|
||||
FLD3.windowB = [686, 689];
|
||||
FLD3.windowW = [717, 722];
|
||||
FLD3.windowA = [758, 770];
|
||||
windows.FLD3 = FLD3;
|
||||
|
||||
% SFM
|
||||
SFM = [];
|
||||
SFM.SFM_windowH = [656, 667]; % <EFBFBD><EFBFBD><EFBFBD>η<EFBFBD>Χ
|
||||
SFM.SFM_windowB = [687, 698];
|
||||
SFM.SFM_windowW = [717, 728];
|
||||
SFM.SFM_windowA = [759, 770];
|
||||
SFM.SFM_Ha = [656.0 656.2]; % ij<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӫ<EFBFBD><EFBFBD>ֵ,SFM<EFBFBD><EFBFBD>SVD<EFBFBD>㷨ֱ<EFBFBD>Ӷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
SFM.SFM_O2B = [687.0 687.2];
|
||||
SFM.SFM_H2O = [720.0 720.2];
|
||||
SFM.SFM_O2A = [760.0 760.2];
|
||||
windows.SFM = SFM;
|
||||
% SVD
|
||||
SVD = [];
|
||||
SVD.SVD_windowH = [655, 670];
|
||||
SVD.SVD_windowB = [675, 710]; % wide O2B and O2A fitting window; % e.g. narrow O2A fitting window:[759.86 762.79];
|
||||
SVD.SVD_windowW = [700, 740];
|
||||
SVD.SVD_windowA = [740, 780];
|
||||
SVD.SVD_Ha = [656.0 656.2]; % ij<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӫ<EFBFBD><EFBFBD>ֵ,SFM<EFBFBD><EFBFBD>SVD<EFBFBD>㷨ֱ<EFBFBD>Ӷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
SVD.SVD_O2B = [687.0 687.2];
|
||||
SVD.SVD_H2O = [720.0 720.2];
|
||||
SVD.SVD_O2A = [760.0 760.2];
|
||||
% Example SVD settings
|
||||
% if rolling > 0, abs(rolling) = the number of spectra in a centered moving window used for training the SVD
|
||||
% if rolling = 0, it will use all spectra from the day for training the SVD
|
||||
% if rolling < 0, it will use that number of spectra but evenly dispersed throughout the day for training the SVD
|
||||
SVD.rolling = 0; % moving window of 5 spectra
|
||||
windows.SVD = SVD;
|
||||
|
||||
% DOAS, F_SFM, <EFBFBD><EFBFBD><EFBFBD>ɷַ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҳ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD><EFBFBD>η<EFBFBD>Χ<EFBFBD>ڷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Сֵ
|
||||
Others = []; % <EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>õIJ<EFBFBD><EFBFBD>η<EFBFBD>Χ<EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD>Сֵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
Others.WindowH = [656, 659]; % Attention: ע<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
Others.WindowB = [686, 689];
|
||||
Others.WindowW = [717, 722];
|
||||
Others.WindowA = [758, 770];
|
||||
windows.Others = Others;
|
||||
|
||||
% -----Attention: Modify the range of spectrum;----- %
|
||||
% --<EFBFBD>õ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>նȣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ⱥͷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
windows.wl_680 = [676, 680]; % 676-680nm, red
|
||||
windows.wl_705 = [703, 707]; % 703-707nm, red
|
||||
windows.wl_740 = [736, 740]; % 736-740nm, rededge
|
||||
windows.wl_770 = [770, 774]; % 770-774nm, near_red
|
||||
windows.wl_800 = [795, 799]; % 795-799nm, near_red
|
||||
end
|
310
sif_methods.py
Normal file
310
sif_methods.py
Normal file
@ -0,0 +1,310 @@
|
||||
import numpy as np
|
||||
from scipy.special import eval_legendre
|
||||
from scipy import optimize
|
||||
# 所有提取方法均基于xarray数据,xarray数据的变量与波长、时间、光谱仪绑定
|
||||
def cal_inside_bands_ave(data):
|
||||
'''
|
||||
根据多个光谱找出窗口内数据最低点对应波长
|
||||
'''
|
||||
sky_spec = data.sky
|
||||
veg_spec = data.veg
|
||||
wvl_inside_band_l = np.mean(veg_spec.idxmin(dim='Wavelength')).values
|
||||
wvl_inside_band_e = np.mean(sky_spec.idxmin(dim='Wavelength')).values
|
||||
return[wvl_inside_band_l,wvl_inside_band_e]
|
||||
|
||||
def cal_outside_values_mean(data,outer):
|
||||
'''
|
||||
计算肩部窗口的均值
|
||||
'''
|
||||
_data = data.where((data.Wavelength>outer[0])&(data.Wavelength<outer[1]),drop=True)
|
||||
wvl_outer_mean = _data['Wavelength'].mean().values
|
||||
_mean = _data.mean(dim = 'Wavelength')
|
||||
E_out = _mean.sky.values
|
||||
L_out = _mean.veg.values
|
||||
return L_out,E_out,wvl_outer_mean
|
||||
|
||||
def sfld(data,wl_range,outer):
|
||||
|
||||
"""
|
||||
Standard FLD (Huaize Feng)
|
||||
|
||||
input:
|
||||
data,xarray dataset
|
||||
wl_range, a window around Fraunhofer lines position, [start,end]
|
||||
outer, a window outside the Fraunhofer lines, [start,end]
|
||||
"""
|
||||
sif = []
|
||||
refl = []
|
||||
nmeas_ = data.Measures.size
|
||||
|
||||
data = data.where((data.Wavelength>wl_range[0])&(data.Wavelength<wl_range[1]),drop=True)
|
||||
[wvl_inside_band_l,wvl_inside_band_e]=cal_inside_bands_ave(data)
|
||||
for i in range(0,nmeas_):
|
||||
_data = data.isel(Measures=i)
|
||||
veg_out,sky_out,_ = cal_outside_values_mean(_data,outer)
|
||||
veg_in = _data.veg.sel(Wavelength = wvl_inside_band_l,method='nearest').values
|
||||
sky_in = _data.sky.sel(Wavelength = wvl_inside_band_e,method='nearest').values
|
||||
_sif = (sky_out*veg_in - sky_in*veg_out)/(sky_out - sky_in)
|
||||
_refl = (veg_in - _sif)/ sky_in
|
||||
sif.append(_sif)
|
||||
refl.append(_refl)
|
||||
return[sif,refl]
|
||||
|
||||
def fld3(data,wl_range,outer_left,outer_right):
|
||||
"""
|
||||
3FLD (Huaize Feng)
|
||||
input:
|
||||
data,xarray dataset
|
||||
wl_range, a window around Fraunhofer lines position, [start,end]
|
||||
outer_*, a window outside the Fraunhofer lines, [start,end]
|
||||
outer_left, left window outside the Fraunhofer lines
|
||||
outer_right, left window outside the Fraunhofer lines
|
||||
return:
|
||||
list[sif,reflectance]
|
||||
"""
|
||||
sif = []
|
||||
refl = []
|
||||
nmeas_ = data.Measures.size
|
||||
|
||||
data = data.where((data.Wavelength>wl_range[0])&(data.Wavelength<wl_range[1]),drop=True)
|
||||
[wvl_inside_band_l,wvl_inside_band_e]=cal_inside_bands_ave(data)
|
||||
for i in range(0,nmeas_):
|
||||
_data = data.isel(Measures=i)
|
||||
veg_out_left,sky_out_left,wvl_outer_left = cal_outside_values_mean(_data,outer_left)
|
||||
veg_out_right,sky_out_right,wvl_outer_right = cal_outside_values_mean(_data,outer_right)
|
||||
veg_in = _data.veg.sel(Wavelength = wvl_inside_band_l,method='nearest').values
|
||||
sky_in = _data.sky.sel(Wavelength = wvl_inside_band_e,method='nearest').values
|
||||
|
||||
# 根据离吸收峰的距离的反比进行赋权
|
||||
wight_left = (wvl_outer_right - wvl_inside_band_e)/(wvl_outer_right - wvl_outer_left)
|
||||
wight_right = (wvl_inside_band_e - wvl_outer_left)/(wvl_outer_right - wvl_outer_left)
|
||||
|
||||
_sif = (veg_in - (sky_in/((wight_left*sky_out_left) + (wight_right*sky_out_right))) * ((wight_left*veg_out_left) + (wight_right*veg_out_right))) / (1-(sky_in / ((wight_left*sky_out_left) + (wight_right*sky_out_right))))
|
||||
_refl = (veg_in - _sif)/ sky_in
|
||||
sif.append(_sif)
|
||||
refl.append(_refl)
|
||||
return[sif,refl]
|
||||
|
||||
def sfm(data,wl_range,band):
|
||||
|
||||
"""
|
||||
Spectral Fitting Method (Huaize Feng)
|
||||
input:
|
||||
data,
|
||||
xarray dataset
|
||||
wl_range,[start,end]
|
||||
a window around Fraunhofer lines position, ,about 10 nm
|
||||
for fitting a ployniam or gussian function.
|
||||
band,float/int
|
||||
exact position of the Fraunhofer line
|
||||
return:
|
||||
list[sif,reflectance,rmse,B]
|
||||
sif, numpy array, sif at the Fraunhofer line for each measurement
|
||||
reflectance, numpy array
|
||||
rmse, RMSE
|
||||
B, numpy array, the parameters of the fitting equation
|
||||
[a,b,c,d,e,f]: a, b, c for polynimal refelectance
|
||||
d, float, MAX sif value [0,10] mw/m2/nm/sr
|
||||
e, position of MAX sif value - wl_range, [0,wavelength.size], nm
|
||||
f, full width at half maximum, [0,wavelength.size*5]
|
||||
"""
|
||||
sif,refl,rmse,B = [],[],[],[]
|
||||
data = data.where((data.Wavelength>wl_range[0])&(data.Wavelength<wl_range[1]),drop=True)
|
||||
abosorb_line_position = np.where(data.Wavelength == data.Wavelength.sel(Wavelength=760,method='nearest').values)
|
||||
_nmeas = data.Measures.size
|
||||
_nwvl = data.Wavelength.size
|
||||
_x = (data.Wavelength -np.min(data.Wavelength)).values
|
||||
poly_refl = [_x**2,_x,np.ones(_nwvl)]
|
||||
poly_sif = [_x**2,_x,np.ones(_nwvl)]
|
||||
|
||||
for i in range(0,_nmeas):
|
||||
sky_spec = data.sky[i].values
|
||||
veg_spec = data.veg[i].values
|
||||
_X = np.concatenate([poly_refl*sky_spec,poly_refl]).T
|
||||
_B = np.linalg.lstsq(_X,veg_spec,rcond=-1)
|
||||
_sif = np.array(poly_sif).T.dot(_B[0][-3:])
|
||||
_refl = (veg_spec - _sif) / (sky_spec+0.000001)
|
||||
sif.append(_sif[abosorb_line_position][0])
|
||||
refl.append(_refl[abosorb_line_position][0])
|
||||
rmse.append(np.sqrt(np.sum(_B[1]**2)/_nwvl))
|
||||
B.append(_B[0])
|
||||
return [sif,refl,rmse,B]
|
||||
|
||||
|
||||
def f(x, a, b, c, d, e, f):
|
||||
sky_spec = x['sky_spec']
|
||||
_x = x['_x']
|
||||
refl = a * _x**2*sky_spec + b* _x*sky_spec + c*sky_spec
|
||||
sif = d*np.exp(-(_x-e)**2/f)
|
||||
y_hat = refl + sif
|
||||
return y_hat
|
||||
|
||||
def f_cal(X, a,b,c,d,e,f):
|
||||
sky_spec = X['sky_spec']
|
||||
_x = X['_x']
|
||||
refl = a * _x**2*sky_spec + b* _x*sky_spec + c*sky_spec
|
||||
sif = d*np.exp(-(_x-e)**2/f)
|
||||
y_hat = refl + sif
|
||||
return sif,refl
|
||||
|
||||
def sfm_gaussian(data,wl_range,band=760):
|
||||
|
||||
'''
|
||||
Spectral Fitting Method (Gaussian Ver. (Huaize Feng))
|
||||
input:
|
||||
data,
|
||||
xarray dataset
|
||||
wl_range,[start,end]
|
||||
a window around Fraunhofer lines position, ,about 10 nm
|
||||
for fitting a polynomial or gaussian function.
|
||||
band,float/int
|
||||
exact position of the Fraunhofer line
|
||||
return:
|
||||
list[sif,reflectance,rmse,B]
|
||||
sif, numpy array, sif at the Fraunhofer line for each measurement
|
||||
reflectance, numpy array
|
||||
rmse, RMSE
|
||||
B, numpy array, the parameters of the fitting equation
|
||||
[a,b,c,d,e,f]: a, b, c for polynimal refelectance
|
||||
d, float, MAX sif value [0,10] mw/m2/nm/sr
|
||||
e, position of MAX sif value - wl_range, [0,wavelength.size], nm
|
||||
f, full width at half maximum
|
||||
'''
|
||||
sif,refl,rmse,B = [],[],[],[]
|
||||
data = data.where((data.Wavelength>wl_range[0])&(data.Wavelength<wl_range[1]),drop=True)
|
||||
abosorb_line_position = np.where(data.Wavelength == data.Wavelength.sel(Wavelength=band,method='nearest').values)
|
||||
_nmeas = data.Measures.size
|
||||
_nwvl = data.Wavelength.size
|
||||
_x = (data.Wavelength -np.min(data.Wavelength)).values
|
||||
poly_refl = [_x**2,_x,np.ones(_nwvl)]
|
||||
poly_sif = [_x**2,_x,np.ones(_nwvl)]
|
||||
|
||||
bounds=((-np.inf, -np.inf,-np.inf, 0, 0, 0), (np.inf, np.inf,np.inf, 10, _nwvl, _nwvl*5))
|
||||
|
||||
for i in range(0,_nmeas):
|
||||
sky_spec = data.sky[i].values
|
||||
veg_spec = data.veg[i].values
|
||||
_X = {'sky_spec':sky_spec,'_x':_x}
|
||||
_B = optimize.curve_fit(f, _X, veg_spec,bounds=bounds)[0]
|
||||
# print(_B)
|
||||
|
||||
_sif,_ = f_cal(_X,*_B)
|
||||
# print(_sif)
|
||||
_rmse = np.sqrt(np.sum((f(_X,*_B)-veg_spec)**2)/_nwvl)
|
||||
_refl = (veg_spec - _sif) / sky_spec
|
||||
sif.append(_sif[abosorb_line_position][0])
|
||||
refl.append(_refl[abosorb_line_position][0])
|
||||
rmse.append( _rmse)
|
||||
B.append(_B[0])
|
||||
return [sif,refl,rmse,B]
|
||||
|
||||
def doas(data,wl_range,band=760):
|
||||
|
||||
"""
|
||||
Spectral Fitting Method (Huaize Feng)
|
||||
input:
|
||||
data,
|
||||
xarray dataset
|
||||
wl_range,[start,end]
|
||||
a window around Fraunhofer lines position, ,about 10 nm
|
||||
for fitting a ployniam or gussian function.
|
||||
band,float/int
|
||||
exact position of the Fraunhofer line
|
||||
return:
|
||||
list[sif,reflectance,rmse,B]
|
||||
sif, numpy array, sif at the Fraunhofer line for each measurement
|
||||
reflectance, numpy array
|
||||
rmse, RMSE
|
||||
B, numpy array, the parameters of the fitting equation
|
||||
[a,b,c,d,e,f]: a, b, c for polynimal refelectance
|
||||
d, float, MAX sif value [0,10] mw/m2/nm/sr
|
||||
e, position of MAX sif value - wl_range, [0,wavelength.size], nm
|
||||
f, full width at half maximum, [0,wavelength.size*5]
|
||||
"""
|
||||
sif,refl,rmse,B = [],[],[],[]
|
||||
data = data.where((data.Wavelength>wl_range[0])&(data.Wavelength<wl_range[1]),drop=True)
|
||||
absorb_line_position = np.where(data.Wavelength == data.Wavelength.sel(Wavelength=band,method='nearest').values)
|
||||
_nmeas = data.Measures.size
|
||||
_wvl = data.Wavelength.values
|
||||
_nwvl = _wvl.size
|
||||
_x = np.interp(_wvl, (_wvl.min(),_wvl.max()),(-1,1))
|
||||
# normalize standard SIF template by the mean value
|
||||
_hf = data.hf.values/(np.mean(data.hf.values))
|
||||
# create base function by legendre polynomial equations
|
||||
_ref_base = np.array([eval_legendre(n, _x) for n in np.arange(6)])
|
||||
|
||||
for i in range(0,_nmeas):
|
||||
print(data.Measures[i].values)
|
||||
sky_spec = data.sky[i].values
|
||||
veg_spec = data.veg[i].values
|
||||
_X = np.concatenate([_ref_base,(_hf/(veg_spec+0.000001111)).reshape(1,-1)]).T
|
||||
_y = np.log(veg_spec+0.000001111) - np.log(sky_spec+0.00000111111)
|
||||
_B = np.linalg.lstsq(_X,_y,rcond=-1)
|
||||
_sif = _hf * _B[0][-1]
|
||||
_refl = (veg_spec - _sif) / (sky_spec+0.0000011111)
|
||||
sif.append(_sif[absorb_line_position][0])
|
||||
refl.append(_refl[absorb_line_position][0])
|
||||
rmse.append(np.sqrt(np.sum(_B[1]**2)/_nwvl))
|
||||
B.append(_B[0])
|
||||
|
||||
return [sif,refl,rmse,B]
|
||||
|
||||
def svd(data,wl_range,band=760,num_vector=20,pow_of_refl = 5):
|
||||
|
||||
"""
|
||||
Singular Vector Decomposition (Huaize Feng)
|
||||
input:
|
||||
data,
|
||||
xarray dataset
|
||||
wl_range,[start,end]
|
||||
a window around Fraunhofer lines position, ,about 10 nm
|
||||
for fitting a polynomial or gaussian function.
|
||||
band,float/int
|
||||
exact position of the Fraunhofer line
|
||||
num_vector,int
|
||||
pca保留的观测维度(个数),相当于去除大气的噪音
|
||||
pow_of_refl,int
|
||||
反射率所用多项式的最高次数
|
||||
Highest power of the polynomial equation denoting reflectance
|
||||
return:
|
||||
list[sif,reflectance,rmse,B]
|
||||
sif, numpy array, sif at the Fraunhofer line for each measurement
|
||||
reflectance, numpy array
|
||||
rmse, RMSE
|
||||
B, numpy array, the parameters of the fitting equation
|
||||
[a,b,c,d,e,f]: a, b, c for polynimal refelectance
|
||||
d, float, MAX sif value [0,10] mw/m2/nm/sr
|
||||
e, position of MAX sif value - wl_range, [0,wavelength.size], nm
|
||||
f, full width at half maximum, [0,wavelength.size*5]
|
||||
"""
|
||||
sif,refl,rmse,B = [],[],[],[]
|
||||
data = data.where((data.Wavelength>wl_range[0])&(data.Wavelength<wl_range[1]),drop=True)
|
||||
abosorb_line_position = np.where(data.Wavelength == data.Wavelength.sel(Wavelength=band,method='nearest').values)
|
||||
_nmeas = data.Measures.size
|
||||
_wvl = data.Wavelength.values
|
||||
_nwvl = _wvl.size
|
||||
_hf = data.hf.values # 重采样后的标准sif
|
||||
|
||||
u, s, vh = np.linalg.svd(data.sky) # Svd分解----------------------------------------------------------------------------------------------------
|
||||
v = -vh
|
||||
|
||||
tmp1=(_wvl-np.mean(_wvl)).reshape(-1,1)
|
||||
tmp2=np.arange(pow_of_refl+1)
|
||||
p1 = np.power(tmp1, tmp2)*v[0].reshape(-1,1)
|
||||
p2 = np.power((_wvl-np.mean(_wvl)).reshape(-1,1), np.arange(pow_of_refl+1))*v[1].reshape(-1,1)
|
||||
p3 = v[:,2:num_vector]
|
||||
_X = np.concatenate([p1,p2,p3,_hf.reshape(-1,1)],axis=1)
|
||||
|
||||
for i in range(0,_nmeas):
|
||||
sky_spec = data.sky[i].values
|
||||
veg_spec = data.veg[i].values
|
||||
_y = veg_spec
|
||||
_B = np.linalg.lstsq(_X,_y,rcond=-1) # https://www.zhihu.com/question/40540185?sort=created
|
||||
_sif = _hf * _B[0][-1]
|
||||
_refl = (veg_spec - _sif) / (sky_spec + 0.0000001)
|
||||
sif.append(_sif[abosorb_line_position][0])
|
||||
refl.append(_refl[abosorb_line_position][0])
|
||||
rmse.append(np.sqrt(np.sum(_B[1]**2)/_nwvl))
|
||||
B.append(_B[0])
|
||||
return [sif,refl,rmse,B]
|
||||
|
251
sif_retrieval.py
Normal file
251
sif_retrieval.py
Normal file
@ -0,0 +1,251 @@
|
||||
# Multi Processing Version.
|
||||
# 2022/1/14 14:52
|
||||
|
||||
# 唐超:如何将此文件打包成一个exe
|
||||
# (1)cd D:\csharp_vs2017\easySif\sifAlgorithm_Python_exe
|
||||
# (2)D:\software\Anaconda3\envs\python_36\Scripts\pyi-makespec.exe -F D:\PycharmProjects\sif\sif_retrieval.py
|
||||
# (3)在生成的spec文件中加入import sys
|
||||
# sys.setrecursionlimit(5000)
|
||||
# (4)D:\software\Anaconda3\envs\python_36\Scripts\pyinstaller.exe D:\csharp_vs2017\easySif\sifAlgorithm_Python_exe\sif_retrieval.spec
|
||||
|
||||
import os
|
||||
import sys
|
||||
from glob import glob
|
||||
import ast
|
||||
import csv
|
||||
import sif_methods
|
||||
import argparse
|
||||
from multiprocessing import Pool
|
||||
import multiprocessing
|
||||
from scipy.interpolate import splev, splrep
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
try:
|
||||
import xarray as xr
|
||||
except Exception as e:
|
||||
print(e)
|
||||
cmd = 'pip install xarray -i https://pypi.tuna.tsinghua.edu.cn/simple --user'
|
||||
os.system(cmd)
|
||||
import xarray as xr
|
||||
finally:
|
||||
pass
|
||||
|
||||
|
||||
def intep(standard, x2):
|
||||
'''
|
||||
插值,根据给定的波长序列将标准SIF重新插值
|
||||
输入:标准SIF,两列,一列波长一列值;给定新的波长序列
|
||||
输出:插值后的SIF值
|
||||
'''
|
||||
spl = splrep(standard.index, standard)
|
||||
y2 = splev(x2, spl)
|
||||
return y2
|
||||
|
||||
|
||||
def sel_method(x):
|
||||
'''
|
||||
选择方法,
|
||||
输入:方法字符串
|
||||
输出:方法
|
||||
'''
|
||||
return {
|
||||
'sfld': sif_methods.sfld,
|
||||
'3fld': sif_methods.fld3,
|
||||
'svd': sif_methods.svd,
|
||||
'sfm': sif_methods.sfm,
|
||||
'sfm_gaussian': sif_methods.sfm_gaussian,
|
||||
'doas': sif_methods.doas
|
||||
}.get(x, sif_methods.sfld)
|
||||
|
||||
|
||||
def read_files(f):
|
||||
'''
|
||||
使用csv.reader读取文件
|
||||
'''
|
||||
with open(f) as csvfile:
|
||||
data = list(csv.reader(csvfile)) # 读csv并转化为list
|
||||
# data = np.array(data)
|
||||
data = np.array(data, dtype=object) # 转化为np数组
|
||||
data = [np.array(d) for d in data]
|
||||
csvfile.close()
|
||||
return data
|
||||
|
||||
|
||||
def Convert(lst):
|
||||
'''
|
||||
针对<键名 键值...>组织形式的文件头解析
|
||||
'''
|
||||
# tc
|
||||
# res_dct = {lst[i]: lst[i + 1] for i in range(0, lst.size-1, 2)}
|
||||
res_dct = {lst[i]: lst[i + 1] for i in range(0, len(lst)-1, 2)}
|
||||
return res_dct
|
||||
|
||||
# parsing
|
||||
|
||||
|
||||
def data_parser(data, dt):
|
||||
shp = np.arange(len(data)).tolist()
|
||||
_iter = iter(shp)
|
||||
|
||||
# 读取csv的元信息(第一行)
|
||||
data[next(_iter)][1:] # 略过csv的第一行
|
||||
header_dict = {'Measure': dt}
|
||||
# header_dict = Convert(data[next(_iter)][1:])
|
||||
header_dict.update(Convert(data[next(_iter)]))
|
||||
|
||||
# 读取波长等信息
|
||||
ls = []
|
||||
for i in range(int(header_dict['TotalSpectrometer'])):
|
||||
info = Convert(data[next(_iter)][1:]) # 略过第3行
|
||||
wvl = np.array(data[next(_iter)][1:])
|
||||
info['Wavelength'] = (wvl[wvl != '']).astype('float')
|
||||
info.update(header_dict)
|
||||
# info = my_dict
|
||||
ls.append(info)
|
||||
|
||||
# 读取光谱数据?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
|
||||
data = data[next(_iter)+1:]
|
||||
data = pd.DataFrame(data)
|
||||
data[['spec', 'point']] = data[0].str.split('_', expand=True) #??
|
||||
data = data.set_index(['spec', 'point'])
|
||||
data = data.iloc[:, 3:]
|
||||
data.columns.name = 'Wavelength'
|
||||
data = data.astype('float')
|
||||
data = data.T.unstack().to_xarray()
|
||||
# _ ['spec'] = f
|
||||
data['Measures'] = dt
|
||||
data = data.expand_dims('Measures')
|
||||
return [data, ls] # data是光谱数据,ls是元信息
|
||||
|
||||
|
||||
def map_files(f):
|
||||
'''
|
||||
遍历文件
|
||||
'''
|
||||
dt = pd.to_datetime(f.split('\\')[-1][:-4], format='%Y_%m_%d_%H_%M_%S')
|
||||
data = read_files(f)
|
||||
data = data_parser(data, dt)
|
||||
# par_ls.append(par)
|
||||
return data # data是列表:第一个是光谱数据,第二个是元信息
|
||||
|
||||
# def map_files(i):
|
||||
# result = i * i
|
||||
# return result
|
||||
|
||||
|
||||
def processing(standard_sif, folder, out_file, pars, data, header, sky_p='P1', method='sfld'):
|
||||
'''
|
||||
提取算法调用
|
||||
'''
|
||||
# 参数解析为list
|
||||
pars = ast.literal_eval(pars)
|
||||
standard_sif = pd.read_csv(standard_sif, index_col=[0])
|
||||
point_ls = {}
|
||||
ls = []
|
||||
# 遍历光谱仪
|
||||
for spec in data['spec'].values:
|
||||
if "SIF" not in spec:
|
||||
continue
|
||||
else:
|
||||
#
|
||||
_ = data.sel(spec=spec) # ???????????????
|
||||
# 根据光谱仪提取对应的波长
|
||||
wvl = header['Wavelength'][spec]
|
||||
size = wvl.size
|
||||
# 使用波长的长度对数据进行截取(减少多光谱仪数据不一致导致的空值)
|
||||
_ = _.isel(Wavelength=xr.DataArray(
|
||||
np.arange(0, size), dims="Wavelength"))
|
||||
_['Wavelength'] = wvl
|
||||
# _ = _.where((_.Wavelength>731.3)&(_.Wavelength<782),drop=True)
|
||||
|
||||
sky = _.sel(point=sky_p, drop=True).rename('sky')
|
||||
for p in _.point:
|
||||
if p == sky_p:
|
||||
continue
|
||||
else:
|
||||
veg = _.sel(point=p, drop=True).rename('veg')
|
||||
input_each = xr.merge([sky, veg])
|
||||
_hf = intep(standard_sif, input_each.Wavelength.values) # 将标准sif插值匹配到数据的波长
|
||||
input_each['hf'] = (['Wavelength'], _hf)
|
||||
# 调用方法
|
||||
retr_method = sel_method(method)
|
||||
print('Running {} method on {} of spectrometer {}'.format(
|
||||
method, p.values, spec), flush=True)
|
||||
print('Processing ...', flush=True)
|
||||
sif = retr_method(input_each, *pars)[0]#---------------------------------------------------------------------------------------------
|
||||
point_ls.update({str(p.values): sif})
|
||||
_sif = pd.DataFrame(point_ls)
|
||||
point_ls = {}
|
||||
_sif.index = sky.Measures.values
|
||||
_sif.index.name = 'Measures'
|
||||
_sif = _sif.to_xarray()
|
||||
_sif = _sif.assign_coords(spec=spec)
|
||||
ls.append(_sif)
|
||||
sif = xr.merge(ls)
|
||||
sif.to_dataframe().to_csv(out_file)
|
||||
|
||||
|
||||
|
||||
|
||||
# # 需要输入的参数
|
||||
# # 标准SIF曲线
|
||||
# standard_sif = 'standard_sif.csv'
|
||||
# # 天空辐射对应的点
|
||||
# sky_p = 'P1'
|
||||
# # 存放文件夹
|
||||
# folder = '2021_12_21'
|
||||
# # 提取方法需要的输入参数,用括号括起来
|
||||
# pars = ([740,770],760)
|
||||
|
||||
# processing(standard_sif,folder,outfile,pars,sky_p='P1',method='svd')
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description="SIF retrieval process")
|
||||
parser.add_argument("standard_sif")
|
||||
parser.add_argument("folder")
|
||||
parser.add_argument("outfile")
|
||||
parser.add_argument("pars")
|
||||
parser.add_argument("sky_p", default='P1')
|
||||
parser.add_argument("method", default='sfld')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
# 解析命令行参数
|
||||
inputs = parse_args()
|
||||
|
||||
# 读取数据
|
||||
cpus = os.cpu_count()
|
||||
pool = Pool(cpus)
|
||||
# print('tc-----------!', flush=True)
|
||||
files = glob(inputs.folder+'/*.csv')
|
||||
print('Total files: ', len(files), flush=True)
|
||||
|
||||
print('Reading files ...', flush=True)
|
||||
_ = pool.map(map_files, files)
|
||||
print('\tdone!', flush=True)
|
||||
|
||||
# 取出光谱数据
|
||||
# data = [d[0] for d in _]
|
||||
# for x in files:
|
||||
# map_files(files)
|
||||
|
||||
data = [d[0] for d in _]
|
||||
data = xr.concat(data, dim='Measures')
|
||||
# data.to_netcdf('C:\\fhz\\neibuwenjian\\tangchao\_Data\\temp.nc')
|
||||
|
||||
# 取出元数据
|
||||
header = _[0][1]
|
||||
_ = []
|
||||
header = pd.DataFrame(header).set_index('Model')
|
||||
# print(header, flush=True)
|
||||
|
||||
# sif算法处理
|
||||
processing(inputs.standard_sif, inputs.folder, inputs.outfile,
|
||||
inputs.pars, data, header, inputs.sky_p, inputs.method)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
1022
standard_sif.csv
Normal file
1022
standard_sif.csv
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user