/*
* QO100 Transceiver based on ADALM-PLUTO and Raspi/Odroid... SBCs
* ==================
* Author: DJ0ABR
*
*   (c) DJ0ABR
*   www.dj0abr.de
*
*   This program is free software; you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation; either version 2 of the License, or
*   (at your option) any later version.
*
*   This program 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 this program; if not, write to the Free Software
*   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* 
*/

#include "qo100trx.h"



char plutoid[100] = {"ip:192.168.20.25"};
//char plutoid[100];

char gui_ip[20] = {"127.127.0.1"};
int udprxsock = 0;
uint8_t audioloop = 0;
uint8_t rfloop = 0;
uint8_t ptt = 0;
uint8_t lastptt = 0;
uint8_t mute = 0;
char pbdevname[101] = {0};
char capdevname[101] = {0};
int newaudiodevs = 0;
int compressor = 0;
int gotPlutoID = 0;
int rxfilter = 3;
int txfilter = 3;
int rxmute = 0;
int refoffset = 0;
int resetqrgs = 0;
int beaconlock = 0;
int fftspeed = 0;
int audiohighpass = 0;
int micboost = 1;		// Multiplikator for TX audio
int agcvalue = 32000;	// should never exeed 16bit-signed (32768), buts need some reserve
int txpower = 0;
int recpb = 0;	// 0=idle, 1=rec, 2=pb
int sendtone = 0;
int extpttinput_enabled = 0;
int mute_enabled = 0;
int pttmode = 0;

// fifos to send/receive samples with pluto run thread
int RXfifo;
int TXfifo;
int FFTfifo;
int CWfifo;

int pbidx = -1, capidx = -1;

void udprxfunc(uint8_t *pdata, int len, struct sockaddr_in* sender)
{
	//printf("UDP command from GUI: %d: %d\n",pdata[0],pdata[1]);

	if(pdata[0] == 0)
	{
		// set RX offset
		uint32_t off = pdata[1];
		off <<= 8;
		off |= pdata[2];
		off <<= 8;
		off |= pdata[3];
		off <<= 8;
		off |= pdata[4];

		//printf("RX offset: %d\n",off);

		RXoffsetfreq = off;

		// set TX offset
		off = pdata[5];
		off <<= 8;
		off |= pdata[6];
		off <<= 8;
		off |= pdata[7];
		off <<= 8;
		off |= pdata[8];

		//printf("TX offset: %d\n",off);

		TXoffsetfreq = off;
	}

	if(pdata[0] == 3)
		audioloop = pdata[1];

	if(pdata[0] == 4)
	{
		ptt = pdata[1];
		if(ptt && lastptt == 0)
		{
			// switch to TX mode
			setSendtone(0);// never start with a test tone after pressing PTT
			io_fifo_clear(capidx);
			fifo_clear(TXfifo);
			set_ptt();
		}

		if(ptt==0 && lastptt)
		{
			// switch to TX mode
			release_ptt();
		}

		lastptt = ptt;
	}

	if(pdata[0] == 5)
	{
		rfloop = pdata[1];
		if(rfloop)
		{
			setRXfrequency((long long)TX_FREQ);
		}
		else
		{
			setRXfrequency((long long)RX_FREQ);
		}
	}

	if(pdata[0] == 7)
	{
		memcpy(pbdevname,pdata+1,100);
		pbdevname[99] = 0;
		memcpy(capdevname,pdata+1+100,100);
		capdevname[99] = 0;
		printf("get Audiodevs: <%s><%s>\n",pbdevname,capdevname);
		newaudiodevs = 1;
	}

	if(pdata[0] == 8)
	{
		uint32_t rxbaseqrg;
		rxbaseqrg = pdata[1];
		rxbaseqrg <<= 8;
		rxbaseqrg += pdata[2];
		rxbaseqrg <<= 8;
		rxbaseqrg += pdata[3];
		rxbaseqrg <<= 8;
		rxbaseqrg += pdata[4];

		uint32_t txbaseqrg;
		txbaseqrg = pdata[5];
		txbaseqrg <<= 8;
		txbaseqrg += pdata[6];
		txbaseqrg <<= 8;
		txbaseqrg += pdata[7];
		txbaseqrg <<= 8;
		txbaseqrg += pdata[8];

		RX_FREQ = (double)rxbaseqrg;
		TX_FREQ = (double)txbaseqrg;

		resetqrgs = 1;
	}

	if(pdata[0] == 9)
	{
		compressor = pdata[1];
		printf("compressor: %d\n",compressor);
	}

	if(pdata[0] == 10)
	{
		if(pdata[1] == 1)
		{
			// Pluto on local USB
			*plutoid = 0;
			printf("Pluto on local USB\n");
		}
		else
		{
			// Pluto on ETH
			memset(plutoid,0,sizeof(plutoid));
			strcpy(plutoid,"ip:");
			memcpy(plutoid+3,pdata+2,len-2);
			printf("Pluto on Ethernet IP: <%s>\n",plutoid);
		}
		gotPlutoID = 1;
	}

	if(pdata[0] == 11)
	{
		audiohighpass = pdata[1];
		micboost = pdata[2];
		//showbytestring("cmd11:",pdata,3,3);
	}

	if(pdata[0] == 12)
		rxfilter = pdata[1];

	if(pdata[0] == 13)
		txfilter = pdata[1];

	if(pdata[0] == 14)
		rxmute = pdata[1];

	if(pdata[0] == 15)
	{
		int val;
		val = pdata[1];
		val <<= 8;
		val += pdata[2];
		val <<= 8;
		val += pdata[3];
		val <<= 8;
		val += pdata[4];

		val -= 12000;
		refoffset = val;
		printf("refoffset: %d\n",refoffset);

		resetqrgs = 1;
	}

	if(pdata[0] == 16)
		beaconlock = pdata[1];

	if(pdata[0] == 17)
		fftspeed = pdata[1];

	if(pdata[0] == 19)
	{
		txpower = pdata[1];
		txpower = -txpower;	// convert received pos value into correct neg value
		if(txpower > 0) txpower = 0;
		if(txpower < -60) txpower = -60;
	}

	if(pdata[0] == 20)
		recpb = pdata[1];

	if(pdata[0] == 21)
	{
		int r = system("shutdown now");
        exit(r);
	}

	if(pdata[0] == 22)
	{
		// send test tone
		setSendtone(1-sendtone);
	}

	if(pdata[0] == 23)
	{
		pttmode = pdata[1];
	}
}

void close_program()
{
    printf("\nCtrl-C pressed\n");
    keeprunning = 0;
	sleep(1);
	close_liquid();
	close_liquid_modulator();
	close_fft();
	close_gpio();
}

int main ()
{
	printf("=== QO100 Transceiver Pluto-Driver, by DJ0ABR ===\n");

	// linux error number see:
	// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno-base.h
	/*char s1[100];
	iio_strerror(-19, s1, 99);
	printf("<%s>\n",s1);
	exit(0);*/

	char url[512];

	// check if we are connected to a router
	sprintf(url,"ip route");
	char *pr = runProgram(url, sizeof(url)-1);
	//printf("<%s>\n",pr);
	if(strstr(pr,"default via"))
	{
		// we are on a router, check github for updates
		sprintf(url,"wget --no-check-certificate --no-cache --no-cookies --no-http-keep-alive -O version.txt https://raw.githubusercontent.com/dj0abr/QO100_Transceiver/main/version.txt?cachekiller=%d",rand());
		int sres = system(url);
		if(sres < 0)
		{
			printf("error %d when reading actual serial number\n",sres);
		}
	}

    install_signal_handler(close_program);

	// Install FIFOs
	RXfifo = create_fifo(200, PLUTOBUFSIZE*4);
	TXfifo = create_fifo(200, PLUTOBUFSIZE*4);
	FFTfifo = create_fifo(200, PLUTOBUFSIZE*4);
	CWfifo = create_fifo(10,48*4);

	// start audio (soundcard) and get connected devices
	if(kmaudio_init() == -1)
	{
		printf("NO AUDIO device\n");
		exit(0);
	}
	kmaudio_getDeviceList();

	// UDP receiver for commands from GUI
	UdpRxInit(&udprxsock, 40821, udprxfunc , &keeprunning);

	// send audio devices to GUI
	int len;
	uint8_t *s = io_getAudioDevicelist(&len);
	uint8_t ub[len+1+2];
	ub[0] = 4; // ID for sound device string
	ub[1] = ((uint16_t)DRIVER_SERIAL) >> 8;		// driver serial number
	ub[2] = ((uint16_t)DRIVER_SERIAL) & 0xff;
	memcpy(ub+3,s,len);
	sendUDP(gui_ip, GUI_UDPPORT, ub, len+1+2);

	// wait for initial configuration from GUI
	// the GUI sends now:
	// selected Audio Device (udp code 7)
	// Base QRGs (udp code 8)
	// Pluto Address (udp code 10)
	int to=0;
	while(gotPlutoID == 0)
	{
		usleep(100000);
		if(++to >= 50)
		{
			printf("no config from GUI, exit program\n");
			exit(0);
		}
	}
	printf("initial config OK, continue\n");

	// find plutoid
	if(*plutoid == 'i')
	{
		strcpy(pluto_context_name,plutoid);
	}
	else
	{
		// automatically search a pluto connected via USB
		int res = pluto_get_USB(plutoid);
        if(!res)
        {
            printf("no Pluto found on USB, exit program\n");
            exit(0);
        }
	}

	//  Pluto found, now initialize it
	printf("Pluto IP/USB ID      : <%s>\n",pluto_context_name);
	pluto_setup();
	
 	// init FFT
	init_fft();

	// init DSP demodulator
	init_liquid();

	// init DSP modulator
	init_liquid_modulator();
	
	// pluto RX/TX is now running, samples are available in the fifos
	// start the SSB receiver and transmitter
	init_rx();
	init_tx();

	// init rotary encoders on RPI
	init_rotencoder();

	release_ptt();

	printf("initialisation finished. Enter normal operation (press Ctrl+C to cancel)\n*** main-PID:%ld ***\n",syscall(SYS_gettid));
	while(keeprunning)
	{
		// main loop
		// time-uncritical jobs are done here
		
		if(newaudiodevs)
		{
			if(*pbdevname && *capdevname)
			{
				// audio device names available
				printf("close streams\n");
				if(pbidx!=-1) close_stream(pbidx);
				if(capidx!=-1) close_stream(capidx);
				printf("init streams\n");

				pbidx = kmaudio_startPlayback(pbdevname, AUDIOSAMPRATE);
				if(pbidx == -1)
					printf("NO AUDIO play device: <%s>\n",pbdevname);

				capidx = kmaudio_startCapture(capdevname, AUDIOSAMPRATE);
				if(capidx == -1)
					printf("NO AUDIO record device: <%s>\n",capdevname);

				newaudiodevs = 0;
			}
		}

		if(resetqrgs)
		{
			resetqrgs = 0;

			// set pluto tuner frequency if changed by user
			setRXfrequency((long long)RX_FREQ);
			setTXfrequency((long long)TX_FREQ);
		}

		setTXpower();

		static int ms10 = 0;
		if(++ms10 >= 100)
		{
			ms10 = 0;
			int rotfreq = getEncSteps(0);
			if(rotfreq != 0)
			{
				uint8_t rxoff[2];
				rxoff[0] = 7;
				rxoff[1] = (uint8_t)(rotfreq+128);
				sendUDP(gui_ip, GUI_UDPPORT, rxoff, 2);
			}
		}

		int rotvol = getEncSteps(1);
		if(rotvol != 0)
		{
			rotvol = -rotvol;

			rxvolume += (float)rotvol / 50.0f;
			if(rxvolume < 0.02f) rxvolume = 0.02f;
			// max volume is limited just before sending to sound driver
		}

		int nptt = getPort("p");
		if(nptt == 1) extpttinput_enabled = 1;
		if(extpttinput_enabled)
		{
			nptt = test_ptt_gpio();
			if(pttmode == 1)
			{
				if(nptt == 3) ptt = 1;
				if(nptt == 2) ptt = 0;
			}
			else
				if(nptt == 3) ptt = 1-ptt;
		}

		int nmute = getPort("m");
		if(nmute == 1) mute_enabled = 1;
		if(mute_enabled)
		{
			nmute = test_mute_gpio();
			if(nmute == 3)
			{
				if(mute) mute = 0;
				else mute = 1;
			}
		}

		// switch PTT output according to variable "ptt"
		if(ptt != lastptt)
		{
			if(ptt)
			{
				// switch to TX mode
				setSendtone(0);// never start with a test tone after pressing PTT
				io_fifo_clear(capidx);
				fifo_clear(TXfifo);
				set_ptt();
			}

			if(!ptt) release_ptt();	// switch to RX mode

			lastptt = ptt;
		}

		//printf("%d\n",getPort("c"));

		usleep(1000);	// do NOT change, used for some simple timings
	}
 
	return 0;
}

void setSendtone(int onoff)
{
	sendtone = onoff;

	uint8_t st[2];
	st[0] = 10;
	st[1] = (uint8_t)sendtone;
	sendUDP(gui_ip, GUI_UDPPORT, st, 2);
}
