/*******************************************************************************
 *	Tests.cpp	Signal Generator classes
 *			T.Barnaby,	BEAM Ltd,	2006-09-12
 *******************************************************************************
 */

#include <Tests.h>
#include <math.h>
#include <pthread.h>

BList<Test*>	testsAll;

BList<Test*>&	testsInit(){
	testsAll.append(new TestSine());
	testsAll.append(new TestSquare());
	testsAll.append(new TestBeam());
	testsAll.append(new TestBeam1());
	testsAll.append(new TestBeam2());
	testsAll.append(new TestBeam3());
	testsAll.append(new TestBeam4());
	testsAll.append(new TestMulti());
	
	return testsAll;
}




TestParam::TestParam(BString name, BString value){
	this->name = name;
	this->value = value;
}

BString TestParams::getValue(BString name){
	BIter	i;
	
	for(start(i); !isEnd(i); next(i)){
		if(get(i).name == name){
			return get(i).value;
		}
	}
	return "";
}

void TestParams::setValue(BString name, BString value){
	BIter	i;
	
	for(start(i); !isEnd(i); next(i)){
		if(get(i).name == name){
			get(i).value = value;
			return;
		}
	}
	BList<TestParam>::append(TestParam(name, value));
}

Test::Test(BString name){
	oname = name;
	osampleRate = 150000000;
	oamplitude = 1.0;
	ox = 0;
}

Test::~Test(){
}

BError Test::init(TestParams& params){
	BError	err;
	BString	s;
	
	oparams = params;
	
	if((s = params.getValue("sampleRate")) != "")
		osampleRate = atof(s);
	if((s = params.getValue("amplitude")) != "")
		oamplitude = atof(s);
	if((s = params.getValue("fileName")) != "")
		ofileName = s;
	if((s = params.getValue("fileType")) != "")
		ofileType = s;
	
	if(ofileName == "")
		return owgen.config(osampleRate, 8);
	else
		return ofile.open(ofileName, "w");
}

void Test::close(){
	if(ofileName == ""){
		owgen.close();
	}
	else {
		ofile.close();
	}
}

BString Test::name(){
	return oname;
}

BString Test::info(){
	return oinfo;
}

BError Test::run(){
	BError	err;

	return err;
}

BError Test::output(BSignalList& sigs){
	BError	err;
	BString	str;
	int	s;
	int	c = -1;
	BIter	i;
	int	in = 0;
	int	p = -1;

	if(ofileName == ""){
		return owgen.output(sigs);
	}
	else {
		if((str = oparams.getValue("channel")) != "")
			c = atoi(str);
		if((str = oparams.getValue("integer")) != "")
			in = atoi(str);
		if((str = oparams.getValue("period")) != "")
			p = atoi(str);
		
		if(ofileType == "txt"){
			if(c >= 0){
				for(sigs.start(i); !sigs.isEnd(i); sigs.next(i)){
					if((p >= 0) && (sigs[i].id != p))
						continue;
					for(s = 0; s < sigs[i].numSamples; s++){
						if(in){
							int16_t	d;

							d = int16_t(sigs[i].data[c][s] * 8191);
							ofile.printf("%d\n", d);
						}
						else {
							ofile.printf("%f\n", sigs[i].data[c][s]);
						}
					}
				}
			}
			else {
				for(sigs.start(i); !sigs.isEnd(i); sigs.next(i)){
					if((p >= 0) && (sigs[i].id != p))
						continue;
					for(s = 0; s < sigs[i].numSamples; s++){
						for(c = 0; c < 8; c++){
							if(in){
								int16_t	d;

								d = int16_t(sigs[i].data[c][s] * 8191);
								ofile.printf("%d ", d);
							}
							else {
								ofile.printf("%f ", sigs[i].data[c][s]);
							}
						}
						ofile.printf("\n");
					}
				}
			}
		}
		else {
			for(sigs.start(i); !sigs.isEnd(i); sigs.next(i)){
				if((p >= 0) && (sigs[i].id != p))
					continue;
				for(s = 0; s < sigs[i].numSamples; s++){
					uint32_t	v;
					uint16_t	ds;
					uint16_t	dx;
					uint16_t	dy;

					// Get 14bit value
					ds = int16_t(sigs[i].data[0][s] * 8191);
					dx = int16_t(sigs[i].data[1][s] * 8191);
					dy = int16_t(sigs[i].data[2][s] * 8191);

					v = (dy >> 4) << 22;
					v |= (dx >> 4) << 12;
					v |= (ds >> 3) << 1;
					if(sigs[i].data[4][s] > 0.5)
						v |= 0x01;
					ofile.write(&v, sizeof(v));

#ifdef ZAP	
					int16_t	t;
					
					t = (v >> 1) & 0x07FF;
					t |= (t & 0x400) ? 0xFE00 : 0; 
					printf("%d\n", t);
#endif
				}
			}
		}
		
		return err;
	}
}


TestSine::TestSine() : Test("Sine") {
	oinfo = "Outputs a sine wave on channel 0";
	ofreq = 10e6;
}

TestSine::~TestSine(){
}

BError TestSine::run(){
	BError			err;
	int			numSamples = int(64 * osampleRate / ofreq);
	BSignal			sig(0, numSamples, 0, 0);
	SigGenSine		sig1;

	sig1.config(osampleRate, ofreq, oamplitude);

	sig1.generate(sig.data[0], numSamples);
	osigList.append(sig);
	if(err = output(osigList))
		printf("Output Error: %s\n", err.getString().retStr());

	pthread_testcancel();

	return err;
}


TestSquare::TestSquare() : Test("Square") {
	oinfo = "Outputs a square wave, 0 to 1v, at 10MHz on channel 0";
	ofreq = 10e6;
}

TestSquare::~TestSquare(){
}

BError TestSquare::run(){
	BError			err;
	int			numSamples = int(64 * osampleRate / ofreq);
	BSignal			sig(0, numSamples, 0, 0);
	SigGenSquare		sig1;

	sig1.config(osampleRate, ofreq, oamplitude/2, oamplitude/2);

	sig1.generate(sig.data[0], numSamples);
	osigList.append(sig);
	if(err = output(osigList))
		printf("Output Error: %s\n", err.getString().retStr());

	pthread_testcancel();

	return err;
}


TestBeam::TestBeam(BString name) : Test(name) {
	if(name == ""){
		oname = "Beam";
		oinfo = "Outputs a repetitive TMS Cycle with a beam with short timings";
		oinfo += " Has 4 particle bunches at harmonic 8.";
		ofref = 437000;
	}
}

TestBeam::~TestBeam(){
}

BError TestBeam::init(TestParams& params){
	BError	err;
	int	ns = 0;
	BString	s;

	if(err = Test::init(params))
		return err;

	if((s = params.getValue("fref")) != "")
		ofref = atof(s);

	if(ofileName == ""){
		// Set ns to be a multiple of 15 for sysclock and 8 samples for the AWG
		ns = 8 * 15 * int(round((osampleRate / (8 * 15)) / ofref));

		ofref = osampleRate / ns;
		osysClock = osampleRate / int(osampleRate / 10e6);
		onumSamples = ns;
	}
	else {
		ns = int(round(osampleRate / ofref));
		// Round ns to 2 for PUPE TestData
		if(ns & 1)
			ns++;
		ofref = osampleRate / ns;
		osysClock = ns * int(10e6 / ns);
		onumSamples = ns;
	}

#ifdef ZAP
	// Set ns to be a multiple of 15 for sysclock and 8 samples for the AWG
	ns = 8 * 15 * int(round((osampleRate / (8 * 15)) / ofref));

	ofref = osampleRate / ns;
	osysClock = osampleRate / int(osampleRate / 10e6);
	onumSamples = ns;
#endif

#ifdef ZAP	
	printf("Samplerate: %f\n", osampleRate);
	printf("NumSamplesPerLoop: %d\n", ns);
	printf("SysClock: %f Ratio: %f\n", osysClock, osampleRate/osysClock);
	printf("Fref: %f Ratio: %f\n", ofref, osampleRate/ofref);
#endif
	
	return err;
}

uint32_t TestBeam::numLoops(double fref, int ms){
	return uint32_t(ms * osampleRate / (onumSamples* 1000));
}

int TestBeam::addCycleNone(int numRepeat, int next){
	int		numSamples = onumSamples;
	BSignal		sig(osigList.number(), numSamples, numRepeat, next);
	
	osigList.append(sig);

	return 0;
}

int TestBeam::addCycleBlank(int numRepeat, int next){
	int		numSamples = onumSamples;
	BSignal		sig(osigList.number(), numSamples, numRepeat, next);
	SigGenSquare	sig3;
	
	sig3.config(osampleRate, osysClock, 0.5, 0.5);			// SYSTEM_CLOCK
	sig3.generate(sig.data[3], numSamples);
	
	osigList.append(sig);

	return 0;
}

int TestBeam::addCycleStart(int numRepeat, int next){
	int		numSamples = onumSamples;
	BSignal		sig(osigList.number(), numSamples, numRepeat, next);
	SigGenSquare	sig3;
	SigGenPulse	sig7;
	BString		s;
	int		cycleStartPhase = 0;
	double		cycleStartTime;

	if((s = oparams.getValue("cycleStartPhase")) != "")
		cycleStartPhase = s.retInt();

	cycleStartTime = cycleStartPhase / (ofref * 360);
//	printf("CycleStartPhase: %d %g\n", cycleStartPhase, cycleStartTime);

	sig3.config(osampleRate, osysClock, 0.5, 0.5);			// SYSTEM_CLOCK
	sig7.config(osampleRate, ofref, 1.0, 1e-6, cycleStartTime);	// START_CYCLE
	
	sig3.generate(sig.data[3], numSamples);
	sig7.generate(sig.data[7], numSamples);

	osigList.append(sig);

	return 0;
}

int TestBeam::addCycleStartMulti(int numRepeat, int next){
	int		numSamples = onumSamples;
	BSignal		sig(osigList.number(), numSamples, numRepeat, next);
	SigGenPulse	sig0;
	SigGenPulse	sig1;
	SigGenPulse	sig2;
	SigGenPulse	sig3;
	BString		s;
	int		cycleStartPhase = 0;
	double		cycleStartTime;

	if((s = oparams.getValue("cycleStartPhase")) != "")
		cycleStartPhase = s.retInt();

	cycleStartTime = cycleStartPhase / (ofref * 360);
//	printf("CycleStartPhase: %d %g\n", cycleStartPhase, cycleStartTime);

	sig0.config(osampleRate, ofref, 1.0, 1e-6, cycleStartTime);	// START_CYCLE
	sig1.config(osampleRate, ofref, 1.0, 1e-6, cycleStartTime);	// START_CYCLE
	sig2.config(osampleRate, ofref, 1.0, 1e-6, cycleStartTime);	// START_CYCLE
	sig3.config(osampleRate, ofref, 1.0, 1e-6, cycleStartTime);	// START_CYCLE
	
	sig0.generate(sig.data[4], numSamples);
	sig1.generate(sig.data[5], numSamples);
	sig2.generate(sig.data[6], numSamples);
	sig3.generate(sig.data[7], numSamples);

	osigList.append(sig);

	return 0;
}

int TestBeam::addCyclePreInjection(int numRepeat, int next){
	int		numSamples = onumSamples;
	BSignal		sig(osigList.number(), numSamples, numRepeat, next);
	SigGenSquare	sig3;
	SigGenSquare	sig4;

	sig3.config(osampleRate, osysClock, 0.5, 0.5);			// SYSTEM_CLOCK
	sig4.config(osampleRate, ofref, 0.5, 0.5);			// FREF
	
	sig3.generate(sig.data[3], numSamples);
	sig4.generate(sig.data[4], numSamples);

	osigList.append(sig);

	return 0;
}
int TestBeam::addCycleInjection(int numRepeat, int next){
	int		numSamples = onumSamples;
	BSignal		sig(osigList.number(), numSamples, numRepeat, next);
	SigGenSquare	sig3;
	SigGenSquare	sig4;
	SigGenPulse	sig5;

	sig3.config(osampleRate, osysClock, 0.5, 0.5);			// SYSTEM_CLOCK
	sig4.config(osampleRate, ofref, 0.5, 0.5);			// FREF
	sig5.config(osampleRate, ofref, 1.0, 1e-6);			// INJECTION
	
	sig3.generate(sig.data[3], numSamples);
	sig4.generate(sig.data[4], numSamples);
	sig5.generate(sig.data[5], numSamples);

	osigList.append(sig);

	return 0;
}

int TestBeam::addCycleBeam(int numRepeat, int next, int harmonic, int numBunches, int hchange, double reduce){
	int		numSamples = onumSamples;
	BSignal		sig(osigList.number(), numSamples, numRepeat, next);
	SigGenBeam	sig0;
	SigGenBeam	sig1;
	SigGenBeam	sig2;
	SigGenSquare	sig3;
	SigGenSquare	sig4;
	SigGenPulse	sig6;
	int		bunchSet = 0;
	int		i;
	
	for(i = 0; i < numBunches; i++){
		bunchSet |= (1 << (harmonic/2 - numBunches/2 + i));
	}

//	sig0.config(osampleRate, ofref, harmonic, bunchSet, reduce, 1, oamplitude);
	sig0.config(osampleRate, ofref, harmonic, bunchSet, reduce, 0, oamplitude);
	sig1.config(osampleRate, ofref, harmonic, bunchSet, reduce, 0, 0.5 * oamplitude);		// DeltaX
	sig2.config(osampleRate, ofref, harmonic, bunchSet, reduce, 0, -0.25 * oamplitude);		// DeltaY
	sig3.config(osampleRate, osysClock, 0.5, 0.5);			// SYSTEM_CLOCK
	sig4.config(osampleRate, ofref, 0.5, 0.5);			// FREF
	sig6.config(osampleRate, ofref, 1.0, 1e-6);			// HCHANGE
	
	sig0.generate(sig.data[0], numSamples);
	sig1.generate(sig.data[1], numSamples);
	sig2.generate(sig.data[2], numSamples);
	sig3.generate(sig.data[3], numSamples);
	sig4.generate(sig.data[4], numSamples);
	if(hchange)
		sig6.generate(sig.data[6], numSamples);

	osigList.append(sig);

	return 0;
}

int TestBeam::addCycleStop(int numRepeat, int next){
	int		numSamples = onumSamples;
	BSignal		sig(osigList.number(), numSamples, numRepeat, next);
	SigGenSquare	sig3;
	SigGenPulse	sig8;

	sig3.config(osampleRate, osysClock, 0.5, 0.5);			// SYSTEM_CLOCK
	sig8.config(osampleRate, ofref, 1.0, 1e-6);			// STOP_CYCLE
	
	sig3.generate(sig.data[3], numSamples);
	sig8.generate(sig.data[8], numSamples);

	osigList.append(sig);

	return 0;
}


BError TestBeam::run(){
	BError	err;
	int	n = 1;

	printf("SampleRate: %f\n", osampleRate);	
	printf("SysClock: %f\n", osysClock);
	printf("Fref: %f\n", ofref);
	printf("NumSamples: %u\n", onumSamples);

	addCycleBlank(0, n++);
	addCycleStart(0, n++);
	addCyclePreInjection(2, n++);
	addCycleInjection(0, n++);
	addCycleBeam(4, n++, 8, 4, 0, 0.0);
	addCycleStop(0, 0);

	if(err = output(osigList))
		printf("Output Error: %s\n", err.getString().retStr());

	pthread_testcancel();

	return err;
}


TestBeam1::TestBeam1() : TestBeam("Beam1") {
	oinfo = "Outputs a repetitive TMS Cycle with a beam.";
	oinfo += " Has 4 particle bunches at harmonic 8.";
	ofref = 437000;
}

BError TestBeam1::run(){
	BError	err;
	int	n = 1;

	printf("SampleRate: %f\n", osampleRate);	
	printf("SysClock: %f\n", osysClock);
	printf("Fref: %f\n", ofref);
	
	addCycleBlank(0, n++);
	addCycleStart(0, n++);
	addCyclePreInjection(numLoops(ofref, 150), n++);
	addCycleInjection(0, n++);
	addCycleBeam(numLoops(ofref, 600), n++, 8, 4, 0, 0.0);
	addCycleStop(0, n++);
	addCycleBlank(numLoops(ofref, 400), n++);
	addCycleBlank(0, 0);

	if(err = output(osigList))
		printf("Output Error: %s\n", err.getString().retStr());

	pthread_testcancel();

	return err;
}

TestBeam2::TestBeam2() : TestBeam("Beam2") {
	oinfo = "Outputs a repetitive TMS Cycle with a beam.";
	oinfo += " Has 4 particle bunches at harmonic 8 moving to 4 particle bunches at harmonic 16.";
	ofref = 437000;
}

BError TestBeam2::run(){
	BError	err;
	int	n = 1;

	printf("SampleRate: %f\n", osampleRate);	
	printf("SysClock: %f\n", osysClock);
	printf("Fref: %f\n", ofref);
	
	addCycleBlank(0, n++);
	addCycleStart(0, n++);
	addCyclePreInjection(numLoops(ofref, 150), n++);
	addCycleInjection(0, n++);
	addCycleBeam(numLoops(ofref, 100), n++, 8, 4, 0, 0.0);
	addCycleBeam(0, n++, 8, 4, 1, 0.0);
	addCycleBeam(numLoops(ofref, 100), n++, 16, 4, 0, 0.0);
	addCycleBeam(numLoops(ofref, 400), n++, 16, 4, 0, 0.0);
	addCycleStop(0, n++);
	addCycleBlank(numLoops(ofref, 400), n++);
	addCycleBlank(0, 0);

	if(err = output(osigList))
		printf("Output Error: %s\n", err.getString().retStr());

	pthread_testcancel();

	return err;
}


TestBeam3::TestBeam3() : TestBeam("Beam3") {
	oinfo = "Outputs a repetitive TMS Cycle with a beam. With Fref = 437000";
	oinfo += " Has 4 particle bunches at harmonic 8 moving to 8 particle bunches at harmonic 16.";
	oinfo += " The 4 bunches have amplitudes reducing from the first to last bunch within an orbit.";
	oinfo += " The DeltaX set is at 0.2 amplitude.";
	oinfo += " The DeltaY set is at -0.1 amplitude.";
	oinfo += " Period 4 is set at harmonic 8, Period 6 is set at harmonic 16.";
	ofref = 437000;
}

BError TestBeam3::run(){
	BError	err;
	int	n = 1;
	double	reduce = 0.1;

	printf("SampleRate: %f\n", osampleRate);	
	printf("SysClock: %f\n", osysClock);
	printf("Fref: %f\n", ofref);
	
	addCycleBlank(0, n++);
	addCycleStart(0, n++);
	addCyclePreInjection(numLoops(ofref, 150), n++);
	addCycleInjection(0, n++);
	addCycleBeam(numLoops(ofref, 300), n++, 8, 4, 0, reduce);
	addCycleBeam(0, n++, 8, 4, 1, reduce);
	addCycleBeam(numLoops(ofref, 100), n++, 16, 8, 0, reduce);
	addCycleBeam(numLoops(ofref, 400), n++, 16, 8, 0, reduce);
	addCycleStop(0, n++);
	addCycleBlank(numLoops(ofref, 400), n++);
	addCycleBlank(0, 0);

	if(err = output(osigList))
		printf("Output Error: %s\n", err.getString().retStr());

	pthread_testcancel();

	return err;
}

TestBeam4::TestBeam4() : TestBeam("Beam4") {
	oinfo = "Outputs a repetitive TMS Cycle with a beam. With Fref = 437000";
	oinfo += " Has 4 particle bunches at harmonic 8 moving to 8 particle bunches at harmonic 16.";
	oinfo += " The 4 bunches have amplitudes reducing from the first to last bunch within an orbit.";
	oinfo += " The first set of bunches after injection are full size.";
	oinfo += " The DeltaX set is at 0.2 amplitude.";
	oinfo += " The DeltaY set is at -0.1 amplitude.";
	oinfo += " Period 4 is set at harmonic 8, Period 6 is set at harmonic 16.";
	ofref = 437000;
}

BError TestBeam4::run(){
	BError	err;
	int	n = 1;
	double	reduce = 0.1;

	printf("SampleRate: %f\n", osampleRate);	
	printf("SysClock: %f\n", osysClock);
	printf("Fref: %f\n", ofref);
	
	addCycleBlank(0, n++);
	addCycleStart(0, n++);
	addCyclePreInjection(numLoops(ofref, 150), n++);
	addCycleInjection(0, n++);
	addCycleBeam(1, n++, 8, 4, 0, 0);
	addCycleBeam(numLoops(ofref, 300), n++, 8, 4, 0, reduce);
	addCycleBeam(0, n++, 8, 4, 1, reduce);
	addCycleBeam(numLoops(ofref, 100), n++, 16, 8, 0, reduce);
	addCycleBeam(numLoops(ofref, 400), n++, 16, 8, 0, reduce);
	addCycleStop(0, n++);
	addCycleBlank(numLoops(ofref, 400), n++);
	addCycleBlank(0, 0);

	if(err = output(osigList))
		printf("Output Error: %s\n", err.getString().retStr());

	pthread_testcancel();

	return err;
}

TestMulti::TestMulti() : TestBeam("Multi") {
	oinfo = "Outputs a repetitive TMS CYCLE_START timing pulse on the first four channels.";
	ofref = 437000;
}

BError TestMulti::run(){
	BError	err;
	int	n = 1;

	printf("SampleRate: %f\n", osampleRate);	
	printf("SysClock: %f\n", osysClock);
	printf("Fref: %f\n", ofref);
	
	addCycleStartMulti(0, n++);
	addCycleNone(numLoops(ofref, 1200), n++);

	if(err = output(osigList))
		printf("Output Error: %s\n", err.getString().retStr());

	pthread_testcancel();

	return err;
}
