## 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: gds_csvwrite(FILENAME, GDSSTRUCT)
## usage: [F] = gds_csvwrite(FILENAME, GDSSTRUCT)
## usage: [F, MSG] = gds_csvwrite(FILENAME, GDSSTRUCT)
##
## Writes GDS-oscilloscope waveform data to a file in .CSV format.
##
## This is the format the oscilloscope also uses to store and read waveform
## data on a USB memory stick or SD card. It is also the format returned by
## the gdsh(1) command gds-csv.
##
## FILENAME is the file to write to.
##
## GDSSTRUCT is a structure containing the values of the header data
## for the .csv file. Like, for example, "Vertical Scale".
## Structure entries are called like the header keys, except that
## spaces are replaced by an underscore. So, for example,
## GDSSTRUCT.Vertical_Scale becomes "Vertical Scale" when writing the
## value to FILENAME.
##
## When gds_csvwrite() detects that the data should comply the v1.04
## GDS-2000 series firmware format, it tries to mimic errors/bugs
## in the v1.04 firmware file format.
##
## gds_csvwrite() inserts the current date/time in the file, not any
## date/time information that might be found in GDSSTRUCT on input.
## gds_csvwrite() inserts reasonable defaults for missing data.
##
## If F is zero, the writing was successful. If F contains -1 it
## wasn't, and MSG contains an error message.
##
## Example:
## The following example sets the minimum required fields in a GDSSTRUCT
## and stores the data:
##
## gs.Waveform_Data = [ 1 2 3 4 ];
## gs.Vertical_Scale = 1;
## gs.Horizontal_Scale = 1;
## gds_csvwrite("simple.csv", gs)
##
## See also: gds_csvread, gdsh

## Author: Thomas Weidenfeller
## Created: 2009-07-18
## Version: $Id: gds_csvwrite.m,v 1.9 2010/12/31 13:05:40 tw Exp $
## Keywords: Oscilloscope, GDS-2000, gds2000tools


function [fret, msgret] = gds_csvwrite (filename, gs)
	if(nargin != 2 || !isstruct(gs))
		print_usage();
	endif

	v104bugs = false;	# mimic v1.04 firmware bugs

	##
	## Some basic checks and setups
	##
	gs.Memory_Length = __gds_memory_length(gs);
	
	if(!isfield(gs, "Trigger_Level"))
		gs.Trigger_Level = 0.0;
	endif

	if(!isfield(gs, "Source"))
		gs.Source = "gds-octave"; # TODO: Could this create trouble?
	endif

	## TODO: Only on GDS-1000A? What would be a good default?
	## if(!isfield(gs, "Probe"))
	## 	gs.Probe = "";
	## endif

	if(!isfield(gs, "Vertical_Units"))
		gs.Vertical_Units = "V";
	endif

	if(!isfield(gs, "Vertical_Scale"))
		##
		## Can't guess a default Vertical Scale
		## This invalidates the whole waveform data.
		##
		error("Vertical_Scale not defined");
	endif

	if(!isfield(gs, "Vertical_Position"))
		gs.Vertical_Position = 0.0;
	endif

	if(!isfield(gs, "Horizontal_Units"))
		if(isfield(gs, "Horizontal_Mode") && strcmp(gs.Horizontal_Mode, "XY Mode"))
			gs.Horizontal_Units = "V";
		else
			gs.Horizontal_Units = "s";
		endif
	endif

	if(!isfield(gs, "Horizontal_Scale") && isfield(gs, "Sampling_Period"))
		gs.Horizontal_Scale = gds_val2wd(gs.Sampling_Period, 1);
	elseif(!isfield(gs, "Sampling_Period"))
		if(isfield(gs, "Horizontal_Scale"))
			gs.Sampling_Period = gds_wd2val(gs.Horizontal_Scale, 1);
		else
			error("Neither Sampling_Period or Horizontal_Scale defined");
		endif
	endif

	if(!isfield(gs, "Horizontal_Position"))
		gs.Horizontal_Position = 0.0;
	endif

	if(!isfield(gs, "Horizontal_Mode"))
		gs.Horizontal_Mode = "Main"; # TODO: a bold guess
	endif

	if(strcmp(gs.Horizontal_Mode, "Roll Mode"))
		## TODO: Following values are random guesses
		if(!isfield(gs, "Display_Last_Dot_Address"))
			gs.Display_Last_Dot_Address = gs.Memory_Length
		endif
		if(!isfield(gs, "Waveform_Last_Dot_Address"))
			gs.Waveform_Last_Dot_Address = gs.Memory_Length
		endif
	endif


	if(isfield(gs, "Firmwave"))
		v104bugs = true;	# Mimic errors independent of actual version
		gs.Firmware = gs.Firmwave;
		gs = rmfield(gs, "Firmwave");
	endif

	if(!isfield(gs, "Firmware"))
		gs.Firmware = "V1.00"; # TODO: never really seen this version
	endif

	if(strcmp(gs.Firmware, "V1.04"))
		v104bugs = true;	# mimic the v1.04 firmware bugs
	endif

	gs.Time = strftime("%d-%b-%y %R", localtime(time()));

	## GDS-1000A only? What would be a good default?
	##if(!isfield(gs, "Mode"))
	##	gs.Mode = "";
	##endif

	##
	## Prepare for writing
	##
	if(v104bugs)
		lastsep = ",";
		fw_string = "Firmwave";
	else
		lastsep = "";
		fw_string = "Firmware";
	endif

	[f, msg] = fopen(filename, "wb");
	if(f == -1)
		if(nargout > 0) 
			fret = f;
			if(nargout > 1)
				msgret = msg;
			endif
		endif
		return;
	endif

	##
	## TODO: Does order matter?
	## For now we assume so, and write in the following order:
	## - Original header data in original order
	## - Any additional data that happens to be in gs
	## - The Waveform Data
	##
	order = [ "Memory_Length"
		"Trigger_Level"
		"Source"
		"Probe"
		"Vertical_Units"
		"Vertical_Scale"
		"Vertical_Position"
		"Horizontal_Units"
		"Horizontal_Scale"
		"Horizontal_Position"
		"Horizontal_Mode"
		"Sampling_Period"
		"Firmware"
		"Time"
		"Mode" ];
	for i = 1:rows(order)
		key = deblank(order(i,:));
		if(isfield(gs, key))
			value = getfield(gs, key);
			gs = rmfield(gs, key);  	# mark as processed
			if(strcmp(key, "Firmware"))
				key = fw_string;
			endif
			if(isnumeric(value))
				value = num2str(value, "%G");
			endif
			key = strrep(key, "_", " ");
			fprintf(f, "%s,%s%s\r\n", key, value, lastsep);
		endif
	endfor

	##
	## Write any other, unknown header data
	##
	for [value, key] = gs
		if(!strcmp(key, "Waveform_Data"))
			if(isnumeric(value))
				value = num2str(value);
			endif
			key = strrep(key, "_", " ");
			fprintf(f, "%s,%s%s\r\n", key, value, lastsep);
		endif
	endfor

	##
	## Finally the waveform data
	##
	fprintf(f, "Waveform Data%s\r\n", lastsep);
	for i = 1:length(gs.Waveform_Data)
		fprintf(f, "%G%s\r\n", gs.Waveform_Data(i), lastsep);
	endfor

	f = fclose(f);
	if(nargout > 0) 
		fret = f;
		if(nargout > 1)
			msgret = "close";
		endif
	endif
	return;
endfunction

%!demo
%! ## Create a temporary file with waveform data
%! fname = tmpnam();
%! gs0 = __gds_demo_gs();
%! gds_csvwrite(fname, gs0);
%! ## Read data back from the temporary file and plot
%! ## both the original and the just read data
%! gs1 = gds_csvread(fname);
%! gs1.Source = [ gs1.Source " - reread"];
%! gds_plot(gs0, gs1);
%! unlink(fname);
