#!/usr/bin/gawk -f
###############################################################################
# $Id: gds-csv-to-afg.awk,v 1.2 2010/09/26 21:53:58 tw Exp $
#
# Script to convert to Rigol DG function generator formats.
#
# Copyright (C) 2009, 2010 Thomas Weidenfeller
# 
# This file is part of gds2000tools/gds-funcgen.
# 
# gds2000tools/gds-funcgen 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-funcgen 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-funcgen.
# If not, see <http://www.gnu.org/licenses/>.
###############################################################################


BEGIN {
	#
	# Tables
	#
	CSV = 0; SCPI = 1
	FORMAT_NAME[CSV]  	= "csv"
	FORMAT_NAME[SCPI] 	= "scpi"

	N_TYPES = 0;
	AFG3051   = N_TYPES++
	AFG3081   = N_TYPES++

	DEV_TYPE_NAME[AFG3051] 	= "AFG-3051"
	DEV_TYPE_NAME[AFG3081]	= "AFG-3081"

	#
	# Max Arb. frequencies.
	#
	MAX_F[AFG3051]	=  50e6		#  50 MHz
	MAX_F[AFG3081]	=  80e6		#  80 MHz

	#
	# Animation sequence
	#
	N_ANIM = 0
	ANIM[N_ANIM++] = "|"
	ANIM[N_ANIM++] = "/"
	ANIM[N_ANIM++] = "-"
	ANIM[N_ANIM++] = "\\"

	#
	# Output range
	#
	OUT_MAX = 32767
	OUT_MIN = -OUT_MAX

	#
	# Parameters and defaults
	#

	# Application name in messages
	if(MY_NAME == "")	{ MY_NAME = "gds-csv-to-afg" }

	# Default output format
	if(FORMAT == "")	{ FORMAT = CSV }

	# Total nbr of generated samples equals nbr of input samples
	if(MINIMIZE == "")	{ MINIMIZE = 1 }

	# No DC component adjustment
	if(DC == "")		{ DC = 0 }

	# No verbose messages
	if(VERBOSE == "")	{ VERBOSE = 0 }

	# Generator device type
	if(DEV_TYPE == "")	{ DEV_TYPE = AFG3051 }

	# Nbr of data in a DATA:DAC line
	if(STEPS == "")	{ STEPS = 8 }


	#
	# I/O Setup
	#
	FS = ","	# Good enough for reading GW Instek CSV
	OLD_ORS = ORS
	ORS = "\r\n"	# Instek uses DOS line endings

	#
	# Misc. constants
	#
	SAMP_DIV = 25		# GDS samples per div
	PI = 4 * atan2(1,1)
	MAX_P = 1024 * 1024

	#
	# Global data initialization
	#
	NData = 0
	Datmin = 3.40282347e+38
	Datmax = -Datmax
	ExitCode = 0

	#
	# Initial messages
	#
	info0("--- Converting GDS CSV to GW Instek AFG Data ---")
	info0("Configuration:")
	info0("   Device Type:             " DEV_TYPE_NAME[DEV_TYPE])
	info0("   Format:                  " FORMAT_NAME[FORMAT])
	info0("   DC adjustment:           " (DC ? "yes" : "no"))
	info0("   Minimum nbr. of samples: " (MINIMIZE ? "yes" : "no"))
}

###############################################################################
# Output generation
#
END {
	if(ExitCode) {
		exit ExitCode # Had a fatal error during data reading
	}

	if(!WDFlag) {
		die("No waveform data found in input. Not a GDS CSV file?")
	}

	if(NData < 2) {
		die("No enough waveform samples found in input.")
	}

	if(HoriScale == 0 && SampPeriod == 0) {
		die("No sampling interval information in input.")
	}

	if(VertScale == 0) {
		error("No vertical scaling information in input.")
	}

	if(NData != MemLen) {
		die("Number of samples found (" NData ") " 		\
		    "doesn't match expected Waveform Length (" MemLen ").")
	}

	if(MemLen > MAX_P) {
		die("Number of input samples (" MemLen ") exceeds number of "\
		       "possible samples in output waveform (" MAX_P ").")
	}

	if(SampPeriod == 0) {
		SampPeriod = HoriScale / SAMP_DIV
	}


	info0("Input Data:                ")
	info0("   Min. input value:       " Datmin " (" engNum(Datmin * VertScale / SAMP_DIV) VertUnits")")
	info0("   Max. input value:       " Datmax " (" engNum(Datmax * VertScale / SAMP_DIV) VertUnits")")
	info0("   Sampling Period:        " engNum(SampPeriod) "s")
	info0("   Sampling Rate:          " engNum(1/SampPeriod) "Hz")

	#
	# Interpolate to expand data over full 1M range
	#
	if(!MINIMIZE) {
		#
		# Interpolate sampling values so the whole
		# data set is filled.
		#
		info0("Interpolating ...")
		dataprime[0] = 0 # make sure dataprime is an array
		sinter(Data, MemLen, dataprime, MAX_P)
		MemLen = MAX_P
		datmini = Datmin
		datmaxi = Datmax
		for(i = 0; i < MAX_P; i++) {
			tmp = dataprime[i]
			Data[i] = tmp
			datmini = min(datmini, tmp)
			datmaxi = max(datmaxi, tmp)
		}
		info0("   Min. Interpolated Value  " datmini " (" engNum(datmini * VertScale / SAMP_DIV) VertUnits")")
		info0("   Max. Interpolated Value  " datmaxi " (" engNum(datmaxi * VertScale / SAMP_DIV) VertUnits")")
		Datmin = datmini
		Datmax = datmaxi
	}

	#
	# Scaling
	#
	if(DC) {
		offset = (Datmax + Datmin) / 2 
	} else {
		offset = 0
	}
	smin = Datmin - offset
	smax = Datmax - offset

	scaleint = scale = max(abs(smax), abs(smin))
	if(scale != 0) {
		scale = OUT_MAX / scale
	}

	info0("Adjusted and Scaled Data:  ")
	info0("   DC offset adjustment:   " offset " (" engNum(offset * VertScale / SAMP_DIV) VertUnits")")
	info0("   Adjusted minimum value: " smin " (" engNum(smin * VertScale / SAMP_DIV) VertUnits")")
	info0("   Adjusted maximum value: " smax " (" engNum(smax * VertScale / SAMP_DIV) VertUnits")")
	info0("   Scale Factor:           " scale " (" (scale * VertScale / SAMP_DIV) "/" VertUnits ")")

	for(i = 0; i < MemLen; i++) {
		Data[i] = int((Data[i] - offset) * scale)
	}

	if(FORMAT == CSV) {
		print "Start:,0"
		print "Length:," MemLen
		print "Sample Rate:," 1/SampPeriod
		for(i = 0; i < MemLen; i++) {
			print Data[i]
		}
	} else if(FORMAT == SCPI) {
		endl = ""
		for(i = 0; i < MemLen; i++) {
			if(!(i % STEPS)) {
				printf("%sDATA:DAC VOLATILE,%d", endl, i)
				endl = ORS
			}
			printf(",%d", Data[i])
		}
		print ""

		print "SOUR:FREQ " (1 / SampPeriod) / MemLen
		#print "SOUR:VOLT:UNIT VPP"
		print "SOUR:AMP " scaleint * 2 * VertScale / SAMP_DIV " VPP"
		print "SOUR:DCO " offset * VertScale / SAMP_DIV
		print "SOUR:FUNC USER"
	}

	ORS = "\n"
}
