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

    Utility for debugging tone detector configurations.  Allows user to
    configure tone detector from command line and test without having to
    compile any software.

    By default, it senses tones on port 1.  Or, if you disconnect the line
    from port 1, you can play a stored file back to the port (-w option),
    and sense tones in the stored file.
    
    Modifications:
    JK: 5/04/02 Added new argument to facilitate changing of SNR parameter 
    in the tone detector.
    BK: 11/02/03 Added -e flag so that it will use the environment variables
    for the tone detection.


         Voicetronix Voice Processing Board (VPB) Software

         Copyright (C) 1999-2008 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., 51 Franklin St, Fifth Floor, Boston,
         MA  02110-1301  USA

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

#include "vpbapi.h"
#include "kbhit.h"

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>


#define TOL 25    // +/- % this value tolerance in on/off time for pulsed tones


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;
} //}}}

static void init_tone_continuous(VPB_DETECT *cont)
{ //{{{
	// Continuous tone detector prototype (e.g. dial tone)

	cont->nstates        = 2;
	cont->tone_id        = VPB_DIAL;
	cont->ntones         = 1;
	cont->freq1          = 400;
	cont->bandwidth1     = 100;
	cont->freq2          = 0;
	cont->bandwidth2     = 0;
	cont->minlevel1      = -40;
	cont->minlevel2      = 0;
	cont->twist          = 0;
	cont->snr            = 10;
	cont->glitch         = 40;

	cont->stran[0].type  = VPB_RISING;
	cont->stran[0].tfire = 0;
	cont->stran[0].tmin  = 0;
	cont->stran[0].tmax  = 0;
	cont->stran[1].type  = VPB_TIMER;
	cont->stran[1].tfire = 2000;
	cont->stran[1].tmin  = 0;
	cont->stran[1].tmax  = 0;
} //}}}

static void init_tone_pulsed(VPB_DETECT *pulsed)
{ //{{{
	// Pulsed tone detector prototype (e.g. busy tone)

	pulsed->nstates        = 2;
	pulsed->tone_id        = VPB_BUSY;
	pulsed->ntones         = 1;
	pulsed->freq1          = 400;
	pulsed->bandwidth1     = 200;
	pulsed->freq2          = 0;
	pulsed->bandwidth2     = 0;
	pulsed->minlevel1      = -40;
	pulsed->minlevel2      = 0;
	pulsed->twist          = 0;
	pulsed->snr            = 10;
	pulsed->glitch         = 40;

	pulsed->stran[0].type  = VPB_RISING;
	pulsed->stran[0].tfire = 0;
	pulsed->stran[0].tmin  = 0;
	pulsed->stran[0].tmax  = 0;
	pulsed->stran[1].type  = VPB_FALLING;
	pulsed->stran[1].tfire = 0;
	pulsed->stran[1].tmin  = 200;
	pulsed->stran[1].tmax  = 300;

	// optional rising edge (if -off option used)
	pulsed->stran[2].type  = VPB_RISING;
	pulsed->stran[2].tfire = 0;
	pulsed->stran[2].tmin  = 200;
	pulsed->stran[2].tmax  = 300;
} //}}}

static void init_tone_degrunt(VPB_DETECT *cont)
{ //{{{
	// Continuous tone detector prototype (e.g. dial tone)

	cont->nstates        = 0;
	cont->tone_id        = VPB_GRUNT;
	cont->ntones         = 1;
	cont->freq1          = 400;
	cont->bandwidth1     = 100;
	cont->freq2          = 0;
	cont->bandwidth2     = 0;
	cont->minlevel1      = -40;
	cont->minlevel2      = 0;
	cont->twist          = 0;
	cont->snr            = 10;
	cont->glitch         = 40;

	cont->stran[0].type  = VPB_RISING;
	cont->stran[0].tfire = 0;
	cont->stran[0].tmin  = 0;
	cont->stran[0].tmax  = 0;
	cont->stran[1].type  = VPB_TIMER;
	cont->stran[1].tfire = 2000;
	cont->stran[1].tmin  = 0;
	cont->stran[1].tmax  = 0;
} //}}}

static void set_tone_id(int argc, char *argv[], VPB_DETECT *tone)
{ //{{{
	int arg;

	if((arg = arg_exists(argc, argv, "-t")) != 0)
	{
		if(strcmp(argv[arg+1],"dial") == 0)
			tone->tone_id = VPB_DIAL;
		else if(strcmp(argv[arg+1],"busy") == 0)
			tone->tone_id = VPB_BUSY;
		else if(strcmp(argv[arg+1],"ringback") == 0)
			tone->tone_id = VPB_RINGBACK;
	}
} //}}}

static void fprintf_tone(char *file, VPB_DETECT *tone)
{ //{{{
	FILE *f = fopen(file, "wt");
	int  i;

	fprintf(f, "/* call me once for every open channel, just after vpb_open() */\n\n");
	fprintf(f, "void set_tone(int h) {\n");
	fprintf(f, "\tVPB_DETECT tone;\n\n");

	fprintf(f, "\ttone.nstates     = %d;\n", tone->nstates);
	fprintf(f, "\ttone.tone_id     = %d;\n", tone->tone_id);
	fprintf(f, "\ttone.ntones      = %d;\n", tone->ntones);
	fprintf(f, "\ttone.freq1       = %d;\n", tone->freq1);
	fprintf(f, "\ttone.bandwidth1  = %d;\n", tone->bandwidth1);
	fprintf(f, "\ttone.freq2       = %d;\n", tone->freq2);
	fprintf(f, "\ttone.bandwidth2  = %d;\n", tone->bandwidth2);
	fprintf(f, "\ttone.minlevel1   = %d;\n", tone->minlevel1);
	fprintf(f, "\ttone.minlevel2   = %d;\n", tone->minlevel2);
	fprintf(f, "\ttone.twist       = %d;\n", tone->twist);
	fprintf(f, "\ttone.snr         = %d;\n", tone->snr);
	fprintf(f, "\ttone.glitch      = %d;\n", tone->glitch);

	fprintf(f, "\n");
	for(i=0; i<tone->nstates; i++) {
		fprintf(f, "\ttone.stran[%d].type   = %d;\n",i,
			tone->stran[i].type);
		fprintf(f, "\ttone.stran[%d].tfire  = %d;\n",i,
			tone->stran[i].tfire);
		fprintf(f, "\ttone.stran[%d].tmin   = %d;\n",i,
			tone->stran[i].tmin);
		fprintf(f, "\ttone.stran[%d].tmax   = %d;\n",i,
			tone->stran[i].tmax);
	}

	fprintf(f, "\n");
	fprintf(f, "\tvpb_settonedet(h, &tone);");
	fprintf(f, "\n");
	fprintf(f, "}\n");

	fclose(f);
} //}}}


int main(int argc, char *argv[])
{ //{{{
	if(argc == 1) {
		printf("\nusage (continuous tone, e.g. dial tone): \n");
		printf("%s -c CentreFreq(Hz) Bandwidth(Hz) FireTime(ms)"
		       " [--card num] [--port num]"
		       " [-w WaveFile]"
		       " [-l LogFile LogSecs] [-t dial|busy|ringback]"
		       " [-d CentreFreq2(Hz) Bandwidth2(Hz)]"
		       " [-q Twist]"
		       " [-n Signal to Noise Ratio (dB)]"
		       " [-s Cfunction.c]\n", argv[0]);
		printf("\nusage (pulsed tone, e.g. busy tone): \n");
		printf("%s -p CentreFreq(Hz) Bandwidth(Hz) OnTime(ms)"
		       " [-off OffTime(ms)]"
		       " [--card num] [--port num]"
		       " [-w WaveFile] [-t dial|busy|ringback]"
		       " [-d CentreFreq2(Hz) Bandwidth2(Hz)]"
		       " [-q Twist]"
		       " [-n Signal to Noise Ratio (dB)]"
		       " [-l LogFile LogSecs] [-s Cfunction.c]\n", argv[0]);
		printf("\nusage (Tone details from the environment): \n");
		printf("%s -e [--card num] [--port num] [ -w WaveFile ] \n"
		       " [-l LogFile LogSecs] [-s Cfunction.c]\n", argv[0]);
		printf("\n");
		exit(EXIT_FAILURE);
	}

	int cardnum = 0;
	int portnum = 0;
	int arg;

	if((arg = arg_exists(argc,argv,"--card")) != 0) cardnum = atoi(argv[arg+1]);
	if((arg = arg_exists(argc,argv,"--port")) != 0) portnum = atoi(argv[arg+1]);

	int h         = vpb_open(cardnum, portnum);
	int port_type = vpb_get_port_type(h);

	if(port_type == VPB_FXO) {
		if( vpb_get_card_type(h) == VPB_OPCI ) {
			printf("ERROR: The tonedebug utility cannot operate with the\n");
			printf("       OpenPCI FXO ports in its present form, as they\n");
			printf("       will not echo sufficient signal.  Please select\n");
			printf("       an FXS port (with the --port option) instead.\n\n");
			exit(EXIT_FAILURE);
		}
		vpb_sethook_sync(h,VPB_OFFHOOK);
	}

	VPB_DETECT  tone;

	if((arg = arg_exists(argc, argv, "-c")) != 0) {
		init_tone_continuous(&tone);
		tone.freq1 = atoi(argv[arg+1]);
		assert(tone.freq1 != 0);
		tone.bandwidth1 = atoi(argv[arg+2]);
		assert(tone.bandwidth1!= 0);
		tone.stran[1].tfire = atoi(argv[arg+3]);
		assert(tone.stran[1].tfire != 0);
	}

	// single tone pulsed
	if((arg = arg_exists(argc, argv, "-p")) != 0) {
		init_tone_pulsed(&tone);
		tone.freq1 = atoi(argv[arg+1]);
		assert(tone.freq1 != 0);
		tone.bandwidth1 = atoi(argv[arg+2]);
		assert(tone.bandwidth1!= 0);

		int tol = (int)((float)(atoi(argv[arg+3]))*(float)TOL/100.0);
		tone.stran[1].tmin = atoi(argv[arg+3]) - tol;
		tone.stran[1].tmax = atoi(argv[arg+3]) + tol;

		// optional off-time
		if ((arg = arg_exists(argc, argv, "-off")) != 0) {
			tone.nstates = 3;
			tone.stran[2].tmin = atoi(argv[arg+1]) - tol;
			tone.stran[2].tmax = atoi(argv[arg+1]) + tol;
		}
	}

	// optional dual tone
	if((arg = arg_exists(argc, argv, "-d")) != 0) {
		tone.ntones     = 2;
		tone.freq2      = atoi(argv[arg+1]);
		tone.bandwidth2 = atoi(argv[arg+2]);
		tone.minlevel2  = -40;
		tone.twist      = 20;
		if((arg = arg_exists(argc, argv, "-q")) != 0)
			tone.twist = atoi(argv[arg+1]);
	}
	// JK: 05/04/02
	// optional signal to noise ratio setting
	if((arg = arg_exists(argc, argv, "-n")) != 0)
		tone.snr = atoi(argv[arg+1]);

	if((arg = arg_exists(argc, argv, "-e")) != 0) {
		printf("Using Environment...\n");
	} else {
		set_tone_id(argc, argv, &tone);
		vpb_settonedet(h, &tone);
	}

	if((arg = arg_exists(argc, argv, "-s")) != 0)
		fprintf_tone(argv[arg+1], &tone);

	if((arg = arg_exists(argc, argv, "-g")) != 0) {
		init_tone_degrunt(&tone);
		vpb_deltonedet(h, &tone);
	}

	if((arg = arg_exists(argc, argv, "-w")) != 0) {
		if(port_type == VPB_FXO) {
			printf("Note: -w option used for testing tone detector based\n"
			       "on wave files echoed from tx->rx side of the hybrid,\n"
			       "ensure no phone line is connected to board %d, port %d\n",
			       cardnum, portnum);
		}
		vpb_play_file_async(h, argv[arg+1], 0);
	}

	if((arg = arg_exists(argc, argv, "-l")) != 0)
		vpb_debug_tonedet(h, tone.tone_id, argv[arg+1], atoi(argv[arg+2]));


	while( !kbhit() ) {
		VPB_EVENT   e;
		char        s[VPB_MAX_STR];

		// Time out occasionally here to check if the user hit a key.
		if(vpb_get_event_sync(&e,500) != VPB_OK) continue;

		vpb_translate_event(&e, s);
		printf("%s",s);
	}

	if(port_type == VPB_FXO)
		vpb_sethook_sync(h,VPB_ONHOOK);

	vpb_close(h);

	return EXIT_SUCCESS;
} //}}}


