## Copyright (C) 2009, 2010 Thomas Weidenfeller
## 
## This file is part of gds2000tools/gds-octave.
## 
## gds2000tools/gds-octave is free software: you can redistribute it
## and/or modify it under the terms of the GNU General Public License
## version 3 as published by the Free Software Foundation.
## 
## gds2000tools/gds-octave is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
## 
## You should have received a copy of the GNU General Public License
## along with gds2000tools/gds-octave.  If not,
## see <http://www.gnu.org/licenses/>.
##

## usage: [F, YABS] = gds_spectrum(GDSSTRUCT)
## usage: [F, YABS] = gds_spectrum(GDSSTRUCT, @FUNC)
## usage: [YABS] = gds_spectrum(GDSSTRUCT)
## usage: [YABS] = gds_spectrum(GDSSTRUCT, @FUNC)
## usage: gds_spectrum(GDSSTRUCT)
## usage: gds_spectrum(GDSSTRUCT, @FUNC)
##
## Calculate spectrum of waveform data from GDSSTRUCT. If no output argument
## is used, then the spectrum is also plotted.
##
## Except for applying a window (see FUNC below), the function does not
## perform any of the advanced signal processing techniques, like Welch's
## method (pwelch()).
##
## GDSSTRUCT is a GDS data structure, for example, loaded with gds_csvread()
## from an oscilloscope .csv waveform sampling data file.
##
## FUNC is a handler to a function to be used for windowing the sampled data
## before calculating the discrete Fourier transform of it. This mitigates
## errors introduced due to the sampling interval usually not being aligned
## with the period of the signal.
##
## The provided function must accept one argument, the width of the
## window in sampling points.
##
## If FUNC is not provided then a Hanning window is used. See hanning().
##
## F is a vector containing the frequency axis of the spectrum. It can be
## used for the X coordinates in a plot() command.
##
## YABS is a vector containing the magnitude of the frequencies in F.
## It can be used for the Y coordinates in a plot() command. The data is
## appropriately scaled, so the values represent signal amplitudes in 
## Volts.
##
## If no return value is requested then the data is plotted instead.
##
## Example:
##
## > function [r] = boxwin(n)   # create a boxwindow function
## >> r = ones(n, 1);           # As opposite to hanning(), the ones() function
## >> endfunction               # can't be used directly as gds_spectrum() arg.
## >
## > gs = gds_csvread("DS0018.CSV");         # read the data
## > [f, Yabs] = gds_spectrum(gs, @boxwin);  # calculate spectrum
## > plot(f, Yabs);                          # plot result
## > xlabel("Frequency / Hz");
## > ylabel("Amplitude / V");
##
## See also: barlett, blackman, chebwin, fft, gds_csvread, gds_plotspectrum,
## hamming, hanning, kaiser, ones, pwelch, stft
##

## Author: Thomas Weidenfeller
## Created: 2009-06-12
## Version: $Id: gds_spectrum.m,v 1.14 2010/12/31 13:05:40 tw Exp $
## Keywords: Oscilloscope, GDS-2000, gds2000tools, fft

function [f, Yabs] = gds_spectrum (gs, func)
	if(nargin < 1 || nargin > 2 || !isstruct(gs))
		print_usage();
	elseif(nargin < 2)
		func = @hanning;
	endif

	##
	## Divert to gds_plotspectrum if no one is interested in the
	## output vectors. gds_plotspectrum will call this function
	## recursively.
	##
	if(nargout == 0)
		gds_plotspectrum(gs, func);
		return;
	endif

	##
	## Determine sampling period
	##
	dt = __gds_sampling_period(gs);

	##
	## Check if we have the data we should have
	L = __gds_memory_length(gs);

	##
	## TODO: More input data consistency checks
	## TODO: Check gs.Horizontal_Mode == "Main" or other mode
	##	for which an FFT make sense
	##

	##
	## Bump FFT length to the nearest power of 2
	## This is more a tradition than absolutely necessary
	## with octave's FFT speed.
	## As a side effect it introduces some smoothing.
	##
	N        = 2^nextpow2(L);
	N_half   = N / 2;
	N_halfp1 = N_half + 1;

	##
	## Apply window. Transform data. Extract interesting frequency
	## data from FFT. Calculate magnitude, correcting magnitude for
	## only using half of the FFT result, except for the DC component
	## in Y(1)
	##
	y = gds_struct2val(gs) .* func(L);	# Apply window function
	Yabs = (abs(fft(y, N)(1:N_halfp1)))';	# Get magnitude
	Yabs = [ (Yabs(1) / L) (Yabs(2:N_half) * 2 / L) (Yabs(N_halfp1) / L) ];

	##
	## If two output arguments are expected, calculate
	## a frequency vector, so an x axis can be plotted correctly
	## using this vector. Otherwise return YABS as the first
	## and only value.
	##
	if(nargout > 1)
		f = [0:N_half] / N / dt;
	else
		f = Yabs;
	endif

endfunction

%!demo
%! ##gs = gds_csvread("DS0008.CSV"); ## read waveform from file
%! gs = __gds_demo_gs();      ## for now get a demo gds waveform data structure
%! [f, Yabs] = gds_spectrum(gs); 
%! subplot(1,1,1);
%! plot(f, Yabs); 
%! xlabel("Frequency / Hz");
%! ylabel("Amplitude / V");
