/*---------------------------------------------------------------------------*\

	FILE....: measgain.cpp
	TYPE....: C++ Program
	AUTHOR..: David Rowe
	DATE....: 23/9/01

	Test program to measure the end-end gain through the VPB ports.

\*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*\

         Voicetronix Voice Processing Board (VPB) Software

         Copyright (C) 1999-2001 Voicetronix www.voicetronix.com.au

         This library is free software; you can redistribute it and/or
         modify it under the terms of the GNU Lesser General Public
         License as published by the Free Software Foundation; either
         version 2.1 of the License, or (at your option) any later version.

         This library 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
         Lesser General Public License for more details.

         You should have received a copy of the GNU Lesser General Public
         License along with this library; if not, write to the Free Software
         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
	 USA

\*---------------------------------------------------------------------------*/

#include "mess.h"
#include "comm.h"
#include "config.h"
#include "dspfifo.h"
#include "timer.h"
#include "timer.h"
#include "wobbly.h"
#include "translate.h"
#include "mapdev.h"
#include "vpbapi.h"

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define	N	        160	// size of processing frame
#define OFF_HK_DELAY    500     // number of ms to pause after off-hook
#define MNSAM            40      // number of frames to sample
#define NCONV           20      // number of frames to wait for convergence
#define AMP             4000    // peak amplitude of test signal
#define NFREQS          7       // number of test frequencies 
#define FS              8000.0  // sampling frequency

float freqs[] = {300.0, 500.0, 1000.0, 1500.0, 2000.0, 2500.0, 3000.0};

void vpb_set_codec_reg(int chdev,    // channel handle
		       unsigned short addr,  // 8-bit address of codec register
		       unsigned short data   // 8-bit data to write to register
		      );

void play_set_hw_gain(int handle, float gain, Comm *c);
void record_set_hw_gain(int handle, float gain, Comm *c);

void scan_gain(int htx, int hrx);
float measure_gain(int htx, int hrx, float f);
int arg_exists(int argc, char *argv[], char *arg);

// headers for functions internal to driver
int vpb_echo_canc_disable();
int vpb_echo_canc_enable();

int kbhit();

static int arg_exists(int argc, char *argv[], const char *arg)
{ //{{{
	for(int i = 0; i < argc; ++i)
		if(strcmp(argv[i],arg) == 0) return i;

	return 0;
} //}}}

int main(int argc, char *argv[])
{
	int       arg, htx, hrx;
	int       txport, rxport, on_hook;
	float     playg, recg;
	VPB_EVENT e;
	char      s[MAX_STR];
	char      *number;

	// scan command line arguments

	// help
	if (arg_exists(argc, argv, "-h") || arg_exists(argc, argv, "--help")) {
		printf("-bal [192..223]        Set codec hybrid balance\n");
		printf("-txport [0...] number  VPB tx port and num to dial\n");
		printf("-rxport [0...]         VPB rx port\n");
		printf("-tg [-12.0..12.0]      Set codec tx (play) gain\n");
		printf("-rg [-12.0..12.0]      Set codec rx (record) gain\n");
		exit(0);
	}

	// determine two ports to test
	arg = arg_exists(argc, argv, "-txport");
	assert(arg != 0);
	txport = atoi(argv[arg+1]);
	assert((txport >= 0) && (txport <= 3));
	number = argv[arg+2];

	arg = arg_exists(argc, argv, "-rxport");
	assert(arg != 0);
	rxport = atoi(argv[arg+1]);
	assert((rxport >= 0) && (rxport <= 3));

	// initialise 

	try {
		
		htx = vpb_open(0,txport);
		hrx = vpb_open(0,rxport);

		// option to set codec record gain
		if ((arg = arg_exists(argc, argv, "-rg")) != 0) {
			recg = atof(argv[arg+1]);
			assert((recg >= -12.0) && (recg <= 12.0));
			vpb_record_set_hw_gain(hrx, recg);
		}

		// option to set codec play gain
		if ((arg = arg_exists(argc, argv, "-pg")) != 0) {
			playg = atof(argv[arg+1]);
			assert((playg >= -12.0) && (playg <= 12.0));
			vpb_play_set_hw_gain(htx, playg);
		}

		// OK, dial.......
		vpb_sethook_sync(htx, VPB_OFFHOOK);
		sleep(2);  // give it enough time to get a dialtone
		printf("OK, dialing %s down port %d\n",number,txport);
		vpb_dial_sync(htx, number);

		// wait for ring and take off hook

		printf("Waiting for rings on port %d\n", rxport);
		on_hook = 1;
		do {
			while(vpb_get_event_async(&e) == VPB_OK) {
				vpb_translate_event(&e, s);
				//printf(s);

				// take channels off-hook on ring
				if (e.type == VPB_RING) {
					vpb_sethook_sync(e.handle,VPB_OFFHOOK);
					on_hook = 0;
				}
			}
			vpb_sleep(100);
		} while(on_hook);

		scan_gain(htx, hrx);

		vpb_sethook_sync(htx, VPB_ONHOOK);
		vpb_sethook_sync(hrx, VPB_ONHOOK);
		vpb_close(htx);
		vpb_close(hrx);

	}	// try ...

	catch (Wobbly w) {
		char	s[MAX_STR];

		w.translate(s);

		if (w.file[0] != 0)
			printf("exception caught: %s, %s, line = %d\n"
			       ,s, w.file, w.line);
		else
			printf("exception caught: %s\n",s);

		printf("Press any key to exit....\n");
	}

	return 0;
}

// measure gain across a range of frequencies

void scan_gain(int htx, int hrx) {
	float gain, av_gain;
	int i;

	vpb_play_buf_start(htx, VPB_LINEAR);
	vpb_record_buf_start(hrx, VPB_LINEAR);

	av_gain = 0.0;
	for(i=0; i<NFREQS; i++) {
		gain = measure_gain(htx, hrx, freqs[i]);
		av_gain += gain;
	}
	av_gain /= NFREQS;

	// clean up and finish
	vpb_play_buf_finish(htx);
	vpb_record_buf_finish(hrx);

	printf("\nAverage gain = %5.2fdB\n", av_gain);
}

// measure gain at a single frequency

float measure_gain(int htx, int hrx, float f) {
	int		i,j;
	char		s[MAX_STR];
	float           sam;
        short           bufrd[N], bufwr[N];
	float           tx_pwr, rx_pwr;
	float           gain;
	float           pi = 4.0*atan(1.0);
	int             ne, nr;
	VPB_EVENT       e;

	tx_pwr = rx_pwr = 0.0;
	ne = nr = 0;

	for(i=0; i<MNSAM; i++) {

		while(vpb_get_event_async(&e) == VPB_OK) {
			vpb_translate_event(&e, s);
			mprintf(s);
		}

		// generate a buffer of sine samples and output

		for(j=0; j<N; j++) {

			sam = cos(2*pi*(f/FS)*(i*N+j));

			sam *= AMP;
			bufwr[j] = (short)sam;
			if (i >= NCONV) {
				tx_pwr += sam*sam;
				nr++;
			}
		}

		vpb_play_buf_sync(htx, (char*)bufwr, sizeof(short)*N);

		// wait for buffer of rx samples

		vpb_record_buf_sync(hrx, (char*)bufrd, sizeof(short)*N);

		for(j=0; j<N; j++) {
			sam = bufrd[j];
			if (i >= NCONV) {
				rx_pwr += sam*sam;
				ne++;
			}
		}
	}

	// print results
	gain = 10*log10(rx_pwr/tx_pwr);
	printf("%fHz, %5.2fdB\n", f, gain);
	return gain;
}

