/*
Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO

This file is part of QtSoundModem

QtSoundModem 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 3 of the License, or
(at your option) any later version.

QtSoundModem 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 QtSoundModem.  If not, see http://www.gnu.org/licenses

*/

// UZ7HO Soundmodem Port by John Wiseman G8BPQ

// UZ7HO Soundmodem Port

// Not Working 4psk100 FEC 

// Thoughts on Waterfall Display.

// Original used a 2048 sample FFT giving 5.859375 Hz bins. We plotted 1024 points, giving a 0 to 6000 specrum

// If we want say 300 to 3300 we need about half the bin size so twice the fft size. But should we also fit required range to window size?

// Unless we resize the most displayed bit of the screen in around 900 pixels. So each bin should be 3300 / 900 = 3.66667 Hz or a FFT size of around 3273

#include "QtSoundModem.h"
#include <qheaderview.h>
//#include <QDebug>
#include <QHostAddress>
#include <QAbstractSocket>
#include <QSettings>
#include <QPainter>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QMessageBox>
#include <QTimer>
#include <qevent.h>
#include <QStandardItemModel>
#include <QScrollBar>
#include <QFontDialog>
#include "UZ7HOStuff.h"


QImage *Constellation[4];
QImage *Waterfall = 0;
QLabel *DCDLabel[4];
QLineEdit *chanOffsetLabel[4];
QImage *DCDLed[4];

QImage *RXLevel;
QImage *RXLevel2;

QLabel *WaterfallCopy;

QLabel * RXLevelCopy;
QLabel * RXLevel2Copy;

QTextEdit * monWindowCopy;

extern workerThread *t;
extern QtSoundModem * w;
extern QCoreApplication * a;

QList<QSerialPortInfo> Ports = QSerialPortInfo::availablePorts();

void saveSettings();
void getSettings();
void DrawModemFreqRange();

extern "C" void CloseSound();
extern "C" void GetSoundDevices();
extern "C" char modes_name[modes_count][21];
extern "C" int speed[5];
extern "C" int KISSPort;
extern "C" short rx_freq[5];

extern "C" int CaptureCount;
extern "C" int PlaybackCount;

extern "C" int CaptureIndex;		// Card number
extern "C" int PlayBackIndex;

extern "C" char CaptureNames[16][256];
extern "C" char PlaybackNames[16][256];

extern "C" int SoundMode;
extern "C" int multiCore;

extern "C" int refreshModems;
int NeedPSKRefresh;

extern "C" int pnt_change[5];
extern "C" int needRSID[4];

extern "C" int needSetOffset[4];

extern "C" float MagOut[4096];
extern "C" float MaxMagOut;
extern "C" int MaxMagIndex;


extern "C" int using48000;			// Set if using 48K sample rate (ie RUH Modem active)
extern "C" int ReceiveSize;
extern "C" int SendSize;		// 100 mS for now

extern "C"
{ 
	int InitSound(BOOL Report);
	void soundMain();
	void MainLoop();
	void modulator(UCHAR snd_ch, int buf_size);
	void SampleSink(int LR, short Sample);
	void doCalib(int Port, int Act);
	int Freq_Change(int Chan, int Freq);
	void set_speed(int snd_ch, int Modem);
	void init_speed(int snd_ch);
	void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform);
	void dofft(short * in, float * outr, float * outi);
	void init_raduga();
	void DrawFreqTicks();
	void AGW_Report_Modem_Change(int port);
	char * strlop(char * buf, char delim);
	void sendRSID(int Chan, int dropTX);
	void RSIDinitfft();
	void il2p_init(int il2p_debug);
}

void make_graph_buf(float * buf, short tap, QPainter * bitmap);

int ModemA = 2;
int ModemB = 2;
int ModemC = 2;
int ModemD = 2;
int FreqA = 1500;
int FreqB = 1500;
int FreqC = 1500;
int FreqD = 1500;
int DCD = 50;

char CWIDCall[128] = "";
extern "C" char CWIDMark[32] = "";
int CWIDInterval = 0;
int CWIDLeft = 0;
int CWIDRight = 0;
int CWIDType = 1;			// on/off
bool afterTraffic = 0;
bool cwidtimerisActive = false;

int WaterfallMin = 00;
int WaterfallMax = 6000;

int Configuring = 0;
bool lockWaterfall = false;
bool inWaterfall = false;

extern "C" int NeedWaterfallHeaders;
extern "C" float BinSize;

extern "C" { int RSID_SABM[4]; }
extern "C" { int RSID_UI[4]; }
extern "C" { int RSID_SetModem[4]; }
extern "C" unsigned int pskStates[4];

int Closing = FALSE;				// Set to stop background thread

QRgb white = qRgb(255, 255, 255);
QRgb black = qRgb(0, 0, 0);

QRgb green = qRgb(0, 255, 0);
QRgb red = qRgb(255, 0, 0);
QRgb yellow = qRgb(255, 255, 0);
QRgb cyan = qRgb(0, 255, 255);

QRgb txText = qRgb(192, 0, 0);
QRgb rxText = qRgb(0, 0, 192);

bool darkTheme = true;
bool minimizeonStart = true;

// Indexed colour list from ARDOPC

#define WHITE 0
#define Tomato 1
#define Gold 2
#define Lime 3
#define Yellow 4
#define Orange 5
#define Khaki 6
#define Cyan 7
#define DeepSkyBlue 8
#define RoyalBlue 9
#define Navy 10
#define Black 11
#define Goldenrod 12
#define Fuchsia 13

QRgb vbColours[16] = { qRgb(255, 255, 255), qRgb(255, 99, 71), qRgb(255, 215, 0), qRgb(0, 255, 0),
						qRgb(255, 255, 0), qRgb(255, 165, 0), qRgb(240, 240, 140), qRgb(0, 255, 255),
						qRgb(0, 191, 255), qRgb(65, 105, 225), qRgb(0, 0, 128), qRgb(0, 0, 0),
						qRgb(218, 165, 32), qRgb(255, 0, 255) };

unsigned char  WaterfallLines[2][80][4096] = { 0 };
int NextWaterfallLine[2] = {0, 0};

unsigned int LastLevel = 255;
unsigned int LastBusy = 255;

extern "C" int UDPClientPort;
extern "C" int UDPServerPort;
extern "C" int TXPort;
extern char UDPHost[64];

QTimer *cwidtimer;
QWidget * mythis;


QSystemTrayIcon * trayIcon = nullptr;

int MintoTray = 1;

int RSID_WF = 0;				// Set to use RSID FFT for Waterfall. 

extern "C" void WriteDebugLog(char * Mess)
{
	qDebug() << Mess;
}

void QtSoundModem::doupdateDCD(int Chan, int State)
{
	DCDLabel[Chan]->setVisible(State);
}

extern "C" char * frame_monitor(string * frame, char * code, bool tx_stat);
extern "C" char * ShortDateTime();

extern "C" void mon_rsid(int snd_ch, char * RSID)
{
	int Len;
	char * Msg = (char *)malloc(1024);		// Cant pass local variable via signal/slot

	sprintf(Msg, "%d:%s [%s%c]", snd_ch + 1, RSID, ShortDateTime(), 'R');

	Len = strlen(Msg);

	if (Msg[Len - 1] != '\r')
	{
		Msg[Len++] = '\r';
		Msg[Len] = 0;
	}

	emit t->sendtoTrace(Msg, 0);
}

extern "C" void put_frame(int snd_ch, string * frame, char * code, int  tx, int excluded)
{
	UNUSED(excluded);

	int Len;
	char * Msg = (char *)malloc(1024);		// Cant pass local variable via signal/slot

	if (strcmp(code, "NON-AX25") == 0)
		sprintf(Msg, "%d: <NON-AX25 frame Len = %d [%s%c]\r", snd_ch, frame->Length, ShortDateTime(), 'R');
	else
		sprintf(Msg, "%d:%s", snd_ch + 1, frame_monitor(frame, code, tx));

	Len = strlen(Msg);

	if (Msg[Len - 1] != '\r')
	{
		Msg[Len++] = '\r';
		Msg[Len] = 0;
	}

	emit t->sendtoTrace(Msg, tx);
}

extern "C" void updateDCD(int Chan, bool State)
{
	emit t->updateDCD(Chan, State);
}

bool QtSoundModem::eventFilter(QObject* obj, QEvent *evt)
{
	UNUSED(obj);

	if (evt->type() == QEvent::Resize)
	{
		return QWidget::event(evt);
	}

	if (evt->type() == QEvent::WindowStateChange)
	{
		if (windowState().testFlag(Qt::WindowMinimized) == true)
			w_state = WIN_MINIMIZED;
		else
			w_state = WIN_MAXIMIZED;
	}
//	if (evt->type() == QGuiApplication::applicationStateChanged) - this is a sigma;
//	{
//		qDebug() << "App State changed =" << evt->type() << endl;
//	}

	return QWidget::event(evt);
}

void QtSoundModem::resizeEvent(QResizeEvent* event)
{
	QMainWindow::resizeEvent(event);

	QRect r = geometry();

	QRect r1 = ui.monWindow->geometry();
	QRect r2 = ui.centralWidget->geometry();

	int modemBoxHeight = 34;
	
	int Width = r.width();
	int Height = r.height() - 25;

	int monitorTop;
	int monitorHeight;
	int sessionTop;
	int sessionHeight = 0;
	int waterfallsTop;
	int waterfallsHeight = WaterfallImageHeight;

	ui.modeB->setVisible(soundChannel[1]);
	ui.centerB->setVisible(soundChannel[1]);
	ui.labelB->setVisible(soundChannel[1]);
	DCDLabel[1]->setVisible(soundChannel[1]);
	ui.RXOffsetB->setVisible(soundChannel[1]);

	ui.modeC->setVisible(soundChannel[2]);
	ui.centerC->setVisible(soundChannel[2]);
	ui.labelC->setVisible(soundChannel[2]);
	DCDLabel[2]->setVisible(soundChannel[2]);
	ui.RXOffsetC->setVisible(soundChannel[2]);

	ui.modeD->setVisible(soundChannel[3]);
	ui.centerD->setVisible(soundChannel[3]);
	ui.labelD->setVisible(soundChannel[3]);
	DCDLabel[3]->setVisible(soundChannel[3]);
	ui.RXOffsetD->setVisible(soundChannel[3]);

	if (soundChannel[2] || soundChannel[3])
		modemBoxHeight = 60;

	ui.Waterfall->setVisible(0);

	monitorTop = modemBoxHeight + 1;

	// Now have one waterfall label containing headers and waterfalls

		if (Firstwaterfall || Secondwaterfall)
			ui.Waterfall->setVisible(1);

	if (AGWServ)
	{
		sessionTable->setVisible(true);
		sessionHeight = 150;
	}
	else
	{
		sessionTable->setVisible(false);
	}

	// if only displaying one Waterfall, change height of waterfall area

	if (UsingBothChannels == 0  || (Firstwaterfall == 0) || (Secondwaterfall == 0))
	{
		waterfallsHeight /= 2;
	}

	if ((Firstwaterfall == 0) && (Secondwaterfall == 0))
		waterfallsHeight = 0;

	monitorHeight = Height - sessionHeight - waterfallsHeight - modemBoxHeight;
	waterfallsTop = Height - waterfallsHeight;
	sessionTop = Height - (sessionHeight + waterfallsHeight);

	ui.monWindow->setGeometry(QRect(0, monitorTop, Width, monitorHeight));

	if (AGWServ)
		sessionTable->setGeometry(QRect(0, sessionTop, Width, sessionHeight));

	if (waterfallsHeight)
		ui.Waterfall->setGeometry(QRect(0, waterfallsTop, Width, waterfallsHeight + 2));
}

QAction * setupMenuLine(QMenu * Menu, char * Label, QObject * parent, int State)
{
	QAction * Act = new QAction(Label, parent);
	Menu->addAction(Act);

	Act->setCheckable(true);
	if (State)
		Act->setChecked(true);

	parent->connect(Act, SIGNAL(triggered()), parent, SLOT(menuChecked()));

	return Act;
}

void QtSoundModem::menuChecked()
{
	QAction * Act = static_cast<QAction*>(QObject::sender());

	int state = Act->isChecked();

	if (Act == actWaterfall1)
		Firstwaterfall = state;

	else if (Act == actWaterfall2)
		Secondwaterfall = state;

	initWaterfall(Firstwaterfall | Secondwaterfall);

	saveSettings();
}

void QtSoundModem::initWaterfall(int state)
{
	if (state == 1)
	{
	//	if (ui.Waterfall)
	//	{
	//		delete ui.Waterfall;
	//		ui.Waterfall = new QLabel(ui.centralWidget);
	//	}
		WaterfallCopy = ui.Waterfall;

		Waterfall = new QImage(1024, WaterfallImageHeight + 2, QImage::Format_RGB32);

		NeedWaterfallHeaders = 1;

	}
	else
	{
		delete(Waterfall);
		Waterfall = 0;
	}

	QSize Size(800, 602);						// Not actually used, but Event constructor needs it

	QResizeEvent *event = new QResizeEvent(Size, Size);
	QApplication::sendEvent(this, event);
}

QRect PSKRect = { 100,100,100,100 };

QDialog * constellationDialog;
QLabel * constellationLabel[4];
QLabel * QualLabel[4];

// Local copies

QLabel *RXOffsetLabel;
QSlider *RXOffset;

QFont Font;

extern "C" void CheckPSKWindows()
{
	NeedPSKRefresh = 1;
}
void DoPSKWindows()
{
	// Display Constellation for PSK Window;

	int NextX = 0;
	int i;

	for (i = 0; i < 4; i++)
	{
		if (soundChannel[i] && pskStates[i])
		{
			constellationLabel[i]->setGeometry(QRect(NextX, 19, 121, 121));
			QualLabel[i]->setGeometry(QRect(1 + NextX, 1, 120, 15));
			constellationLabel[i]->setVisible(1);
			QualLabel[i]->setVisible(1);

			NextX += 122;
		}
		else
		{
			constellationLabel[i]->setVisible(0);
			QualLabel[i]->setVisible(0);
		}
	}
	constellationDialog->resize(NextX, 140);
}

QTimer *wftimer;

QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent)
{
	QString family;
	int csize;
	QFont::Weight weight;

	ui.setupUi(this);

	mythis = this;

	getSettings();

	QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat);

	family = mysettings.value("FontFamily", "Courier New").toString();
	csize = mysettings.value("PointSize", 0).toInt();
	weight = (QFont::Weight)mysettings.value("Weight", 50).toInt();

	Font = QFont(family);
	Font.setPointSize(csize);
	Font.setWeight(weight);

	QApplication::setFont(Font);

	constellationDialog = new QDialog(nullptr, Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
	constellationDialog->resize(488, 140);
	constellationDialog->setGeometry(PSKRect);

	QFont f("Arial", 8, QFont::Normal);

	for (int i = 0; i < 4; i++)
	{
		char Text[16];
		sprintf(Text, "Chan %c", i + 'A');

		constellationLabel[i] = new QLabel(constellationDialog);
		constellationDialog->setWindowTitle("PSK Constellations");

		QualLabel[i] = new QLabel(constellationDialog);
		QualLabel[i]->setText(Text);
		QualLabel[i]->setFont(f);

		Constellation[i] = new QImage(121, 121, QImage::Format_RGB32);
		Constellation[i]->fill(black);
		constellationLabel[i]->setPixmap(QPixmap::fromImage(*Constellation[i]));
	}
	constellationDialog->show();

	if (MintoTray)
	{
		char popUp[256];
		sprintf(popUp, "QtSoundModem %d %d", AGWPort, KISSPort);
		trayIcon = new QSystemTrayIcon(QIcon(":/QtSoundModem/soundmodem.ico"), this);
		trayIcon->setToolTip(popUp);
		trayIcon->show();
		
		connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(TrayActivated(QSystemTrayIcon::ActivationReason)));
	}

	using48000 = 0;			// Set if using 48K sample rate (ie RUH Modem active)
	ReceiveSize = 512;
	SendSize = 1024;		// 100 mS for now

	for (int i = 0; i < 4; i++)
	{
		if (soundChannel[i] && (speed[i] == SPEED_RUH48 || speed[i] == SPEED_RUH96))
		{
			using48000 = 1;			// Set if using 48K sample rate (ie RUH Modem active)
			ReceiveSize = 2048;
			SendSize = 4096;		// 100 mS for now
		}
	}

	float FFTCalc = 12000.0f / ((WaterfallMax - WaterfallMin) / 900.0f);

	FFTSize = FFTCalc + 0.4999;

	if (FFTSize > 8191)
		FFTSize = 8190;

	if (FFTSize & 1)		// odd
		FFTSize--;

	BinSize = 12000.0 / FFTSize;

	restoreGeometry(mysettings.value("geometry").toByteArray());
	restoreState(mysettings.value("windowState").toByteArray());

	sessionTable = new QTableWidget(ui.centralWidget);

	sessionTable->verticalHeader()->setVisible(FALSE);
	sessionTable->verticalHeader()->setDefaultSectionSize(20);
	sessionTable->horizontalHeader()->setDefaultSectionSize(68);
	sessionTable->setRowCount(1);
	sessionTable->setColumnCount(12);
	m_TableHeader << "MyCall" << "DestCall" << "Status" << "Sent pkts" << "Sent Bytes" << "Rcvd pkts" << "Rcvd bytes" << "Rcvd FC" << "FEC corr" << "CPS TX" << "CPS RX" << "Direction";

	mysetstyle();

	sessionTable->setHorizontalHeaderLabels(m_TableHeader);
	sessionTable->setColumnWidth(0, 80);
	sessionTable->setColumnWidth(1, 80);
	sessionTable->setColumnWidth(4, 76);
	sessionTable->setColumnWidth(5, 76);
	sessionTable->setColumnWidth(6, 80);
	sessionTable->setColumnWidth(11, 72);

	for (int i = 0; i < modes_count; i++)
	{
		ui.modeA->addItem(modes_name[i]);
		ui.modeB->addItem(modes_name[i]);
		ui.modeC->addItem(modes_name[i]);
		ui.modeD->addItem(modes_name[i]);
	}

	// Set up Menus

	setupMenu = ui.menuBar->addMenu(tr("Settings"));

	actDevices = new QAction("Setup Devices", this);
	setupMenu->addAction(actDevices);

	connect(actDevices, SIGNAL(triggered()), this, SLOT(clickedSlot()));
	actDevices->setObjectName("actDevices");
	actModems = new QAction("Setup Modems", this);
	actModems->setObjectName("actModems");
	setupMenu->addAction(actModems);

	connect(actModems, SIGNAL(triggered()), this, SLOT(clickedSlot()));

	actFont = new QAction("Setup Font", this);
	actFont->setObjectName("actFont");
	setupMenu->addAction(actFont);

	connect(actFont, SIGNAL(triggered()), this, SLOT(clickedSlot()));

	actMintoTray = setupMenu->addAction("Minimize to Tray", this, SLOT(MinimizetoTray()));
	actMintoTray->setCheckable(1);
	actMintoTray->setChecked(MintoTray);

	viewMenu = ui.menuBar->addMenu(tr("&View"));

	actWaterfall1 = setupMenuLine(viewMenu, (char *)"First waterfall", this, Firstwaterfall);
	actWaterfall2 = setupMenuLine(viewMenu, (char *)"Second Waterfall", this, Secondwaterfall);

	actCalib = ui.menuBar->addAction("&Calibration");
	connect(actCalib, SIGNAL(triggered()), this, SLOT(doCalibrate()));

	actRestartWF = ui.menuBar->addAction("Restart Waterfall");
	connect(actRestartWF, SIGNAL(triggered()), this, SLOT(doRestartWF()));

	actAbout = ui.menuBar->addAction("&About");
	connect(actAbout, SIGNAL(triggered()), this, SLOT(doAbout()));

	RXLevel = new QImage(150, 10, QImage::Format_RGB32);
	RXLevel->fill(white);
	ui.RXLevel->setPixmap(QPixmap::fromImage(*RXLevel));
	RXLevelCopy = ui.RXLevel;

	RXLevel2 = new QImage(150, 10, QImage::Format_RGB32);
	RXLevel2->fill(white);
	ui.RXLevel2->setPixmap(QPixmap::fromImage(*RXLevel2));
	RXLevel2Copy = ui.RXLevel2;

	DCDLabel[0] = new QLabel(this);
	DCDLabel[0]->setObjectName(QString::fromUtf8("DCDLedA"));
	DCDLabel[0]->setGeometry(QRect(280, 31, 12, 12));
	DCDLabel[0]->setVisible(TRUE);

	DCDLabel[1] = new QLabel(this);
	DCDLabel[1]->setObjectName(QString::fromUtf8("DCDLedB"));
	DCDLabel[1]->setGeometry(QRect(575, 31, 12, 12));
	DCDLabel[1]->setVisible(TRUE);

	DCDLabel[2] = new QLabel(this);
	DCDLabel[2]->setObjectName(QString::fromUtf8("DCDLedC"));
	DCDLabel[2]->setGeometry(QRect(280, 61, 12, 12));
	DCDLabel[2]->setVisible(FALSE);

	DCDLabel[3] = new QLabel(this);
	DCDLabel[3]->setObjectName(QString::fromUtf8("DCDLedD"));
	DCDLabel[3]->setGeometry(QRect(575, 61, 12, 12));
	DCDLabel[3]->setVisible(FALSE);
	
	DCDLed[0] = new QImage(12, 12, QImage::Format_RGB32);
	DCDLed[1] = new QImage(12, 12, QImage::Format_RGB32);
	DCDLed[2] = new QImage(12, 12, QImage::Format_RGB32);
	DCDLed[3] = new QImage(12, 12, QImage::Format_RGB32);

	DCDLed[0]->fill(red);
	DCDLed[1]->fill(red);
	DCDLed[2]->fill(red);
	DCDLed[3]->fill(red);

	DCDLabel[0]->setPixmap(QPixmap::fromImage(*DCDLed[0]));
	DCDLabel[1]->setPixmap(QPixmap::fromImage(*DCDLed[1]));
	DCDLabel[2]->setPixmap(QPixmap::fromImage(*DCDLed[2]));
	DCDLabel[3]->setPixmap(QPixmap::fromImage(*DCDLed[3]));

	chanOffsetLabel[0] = ui.RXOffsetA;
	chanOffsetLabel[1] = ui.RXOffsetB;
	chanOffsetLabel[2] = ui.RXOffsetC;
	chanOffsetLabel[3] = ui.RXOffsetD;

	WaterfallCopy = ui.Waterfall;

	initWaterfall(Firstwaterfall | Secondwaterfall);

	monWindowCopy = ui.monWindow;

	ui.monWindow->document()->setMaximumBlockCount(10000);

//	connect(ui.monWindow, SIGNAL(selectionChanged()), this, SLOT(onTEselectionChanged()));

	connect(ui.modeA, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int)));
	connect(ui.modeB, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int)));
	connect(ui.modeC, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int)));
	connect(ui.modeD, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int)));

	ModemA = speed[0];
	ModemB = speed[1];
	ModemC = speed[2];
	ModemD = speed[3];

	ui.modeA->setCurrentIndex(speed[0]);
	ui.modeB->setCurrentIndex(speed[1]);
	ui.modeC->setCurrentIndex(speed[2]);
	ui.modeD->setCurrentIndex(speed[3]);

	ModemA = ui.modeA->currentIndex();

	ui.centerA->setValue(rx_freq[0]);
	ui.centerB->setValue(rx_freq[1]);
	ui.centerC->setValue(rx_freq[2]);
	ui.centerD->setValue(rx_freq[3]);

	connect(ui.centerA, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
	connect(ui.centerB, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
	connect(ui.centerC, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));
	connect(ui.centerD, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));

	ui.DCDSlider->setValue(dcd_threshold);

	char valChar[32];
	sprintf(valChar, "RX Offset %d", rxOffset);
	ui.RXOffsetLabel->setText(valChar);
	ui.RXOffset->setValue(rxOffset);

	RXOffsetLabel = ui.RXOffsetLabel;
	RXOffset = ui.RXOffset;

	connect(ui.DCDSlider, SIGNAL(sliderMoved(int)), this, SLOT(clickedSlotI(int)));
	connect(ui.RXOffset, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int)));

	QObject::connect(t, SIGNAL(sendtoTrace(char *, int)), this, SLOT(sendtoTrace(char *, int)), Qt::QueuedConnection);
	QObject::connect(t, SIGNAL(updateDCD(int, int)), this, SLOT(doupdateDCD(int, int)), Qt::QueuedConnection);

	QObject::connect(t, SIGNAL(startCWIDTimer()), this, SLOT(startCWIDTimerSlot()), Qt::QueuedConnection);

	connect(ui.RXOffsetA, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
	connect(ui.RXOffsetB, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
	connect(ui.RXOffsetC, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
	connect(ui.RXOffsetD, SIGNAL(returnPressed()), this, SLOT(returnPressed()));

	QTimer *timer = new QTimer(this);
	connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot()));
	timer->start(100);

	wftimer = new QTimer(this);
	connect(wftimer, SIGNAL(timeout()), this, SLOT(doRestartWF()));
	wftimer->start(1000 * 300);

	cwidtimer = new QTimer(this);
	connect(cwidtimer, SIGNAL(timeout()), this, SLOT(CWIDTimer()));


	if (CWIDInterval && afterTraffic == false)
		cwidtimer->start(CWIDInterval * 60000);

	if (RSID_SetModem[0])
	{
		RSID_WF = 1;
		RSIDinitfft();
	}
//	il2p_init(1);

	QTimer::singleShot(200, this, &QtSoundModem::updateFont);

}

void QtSoundModem::updateFont()
{
	QApplication::setFont(Font);
}

void QtSoundModem::MinimizetoTray()
{
	MintoTray = actMintoTray->isChecked();
	saveSettings();
	QMessageBox::about(this, tr("QtSoundModem"),
	tr("Program must be restarted to change Minimize mode"));
}


void QtSoundModem::TrayActivated(QSystemTrayIcon::ActivationReason reason)
{
	if (reason == 3)
	{
		showNormal();
		w->setWindowState((w->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
	} 
}

extern "C" void sendCWID(char * strID, BOOL blnPlay, int Chan);

extern "C" void checkforCWID()
{
	emit(t->startCWIDTimer());
};

extern "C" void QtSoundModem::startCWIDTimerSlot()
{
	if (CWIDInterval && afterTraffic == 1 && cwidtimerisActive == false)
	{
		cwidtimerisActive = true;
		QTimer::singleShot(CWIDInterval * 60000, this, &QtSoundModem::CWIDTimer);
	}
}

void QtSoundModem::CWIDTimer()
{
	cwidtimerisActive = false;
	sendCWID(CWIDCall, CWIDType, 0);
	calib_mode[0] = 4;
}

void extSetOffset(int chan)
{
	char valChar[32];
	sprintf(valChar, "%d", chanOffset[chan]);
	chanOffsetLabel[chan]->setText(valChar);

	NeedWaterfallHeaders = true;

	pnt_change[0] = 1;
	pnt_change[1] = 1;
	pnt_change[2] = 1;
	pnt_change[3] = 1;

	return;
}
	
void QtSoundModem::MyTimerSlot()
{
	// 100 mS Timer Event

	for (int i = 0; i < 4; i++)
	{

		if (needSetOffset[i])
		{
			needSetOffset[i] = 0;
			extSetOffset(i);						// Update GUI
		}
	}

	if (refreshModems)
	{
		refreshModems = 0;

		ui.modeA->setCurrentIndex(speed[0]);
		ui.modeB->setCurrentIndex(speed[1]);
		ui.modeC->setCurrentIndex(speed[2]);
		ui.modeD->setCurrentIndex(speed[3]);
		ui.centerA->setValue(rx_freq[0]);
		ui.centerB->setValue(rx_freq[1]);
		ui.centerC->setValue(rx_freq[2]);
		ui.centerD->setValue(rx_freq[3]);
	}
	if (NeedPSKRefresh)
	{
		NeedPSKRefresh = 0;
		DoPSKWindows();
	}

	if (NeedWaterfallHeaders)
	{
		NeedWaterfallHeaders = 0;

		if (Waterfall)
		{
			Waterfall->fill(black);
			DrawModemFreqRange();
			DrawFreqTicks();
		}
	}

	show_grid();
}

void QtSoundModem::returnPressed()
{
	char Name[32];
	int Chan;
	QString val;
	
	strcpy(Name, sender()->objectName().toUtf8());

	Chan = Name[8] - 'A';

	val = chanOffsetLabel[Chan]->text();

	chanOffset[Chan] = val.toInt();
	needSetOffset[Chan] = 1;				// Update GUI


}

void CheckforChanges(int Mode, int OldMode)
{
	int old48000 = using48000;

	if (OldMode != Mode && Mode == 15)
	{
		QMessageBox msgBox;

		msgBox.setText("Warning!!\nARDOP Packet is NOT the same as ARDOP\n"
			"It is an experimental mode for sending ax.25 frames using ARDOP packet formats\n");

		msgBox.setStandardButtons(QMessageBox::Ok);

		msgBox.exec();
	}

	// See if need to switch beween 12000 and 48000

	using48000 = 0;			// Set if using 48K sample rate (ie RUH Modem active)
	ReceiveSize = 512;
	SendSize = 1024;		// 100 mS for now

	for (int i = 0; i < 4; i++)
	{
		if (soundChannel[i] && (speed[i] == SPEED_RUH48 || speed[i] == SPEED_RUH96))
		{
			using48000 = 1;			// Set if using 48K sample rate (ie RUH Modem active)
			ReceiveSize = 2048;
			SendSize = 4096;		// 100 mS for now
		}
	}

	if (using48000 != old48000)
	{
		InitSound(1);
	}
}


void QtSoundModem::clickedSlotI(int i)
{
	char Name[32];

	strcpy(Name, sender()->objectName().toUtf8());

	if (strcmp(Name, "modeA") == 0)
	{
		int OldModem = ModemA;
		ModemA = ui.modeA->currentIndex();
		set_speed(0, ModemA);
		CheckforChanges(ModemA, OldModem);
		saveSettings();
		AGW_Report_Modem_Change(0);
		return;
	}

	if (strcmp(Name, "modeB") == 0)
	{
		int OldModem = ModemB;
		ModemB = ui.modeB->currentIndex();
		set_speed(1, ModemB);
		CheckforChanges(ModemB, OldModem);
		saveSettings();
		AGW_Report_Modem_Change(1);
		return;
	}

	if (strcmp(Name, "modeC") == 0)
	{
		int OldModem = ModemC;
		ModemC = ui.modeC->currentIndex();
		set_speed(2, ModemC);
		CheckforChanges(ModemC, OldModem);
		saveSettings();
		AGW_Report_Modem_Change(2);
		return;
	}

	if (strcmp(Name, "modeD") == 0)
	{
		int OldModem = ModemD;
		ModemD = ui.modeD->currentIndex();
		set_speed(3, ModemD);
		CheckforChanges(ModemD, OldModem);
		saveSettings();
		AGW_Report_Modem_Change(3);
		return;
	}

	if (strcmp(Name, "centerA") == 0)
	{
		if (i > 299)
		{
			QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
			ui.centerA->setValue(Freq_Change(0, i));
			settings->setValue("Modem/RXFreq1", ui.centerA->value());
			AGW_Report_Modem_Change(0);

		}
		return;
	}

	if (strcmp(Name, "centerB") == 0)
	{
		if (i > 299)
		{
			QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
			ui.centerB->setValue(Freq_Change(1, i));
			settings->setValue("Modem/RXFreq2", ui.centerB->value());
			AGW_Report_Modem_Change(1);
		}
		return;
	}

	if (strcmp(Name, "centerC") == 0)
	{
		if (i > 299)
		{
			QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
			ui.centerC->setValue(Freq_Change(2, i));
			settings->setValue("Modem/RXFreq3", ui.centerC->value());
			AGW_Report_Modem_Change(2);
		}
		return;
	}

	if (strcmp(Name, "centerD") == 0)
	{
		if (i > 299)
		{
			QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat);
			ui.centerD->setValue(Freq_Change(3, i));
			settings->setValue("Modem/RXFreq4", ui.centerD->value());
			AGW_Report_Modem_Change(3);
		}
		return;
	}

	if (strcmp(Name, "DCDSlider") == 0)
	{
		dcd_threshold = i;
		saveSettings();
		return;
	}
	
	if (strcmp(Name, "RXOffset") == 0)
	{
		char valChar[32];
		rxOffset = i;
		sprintf(valChar, "RX Offset %d",rxOffset);
		ui.RXOffsetLabel->setText(valChar);

		NeedWaterfallHeaders = true;

		pnt_change[0] = 1;
		pnt_change[1] = 1;
		pnt_change[2] = 1;
		pnt_change[3] = 1;

		saveSettings();
		return;
	}

	QMessageBox msgBox;
	msgBox.setWindowTitle("MessageBox Title");
	msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName());
	msgBox.exec();
}


void QtSoundModem::clickedSlot()
{
	char Name[32];

	strcpy(Name, sender()->objectName().toUtf8());

	if (strcmp(Name, "actDevices") == 0)
	{
		doDevices();
		return;
	}

	if (strcmp(Name, "actModems") == 0)
	{
		doModems();
		return;
	}

	if (strcmp(Name, "showBPF_A") == 0)
	{
		doFilter(0, 0);
		return;
	}

	if (strcmp(Name, "showTXBPF_A") == 0)
	{
		doFilter(0, 1);
		return;
	}

	if (strcmp(Name, "showLPF_A") == 0)
	{
		doFilter(0, 2);
		return;
	}
	

	if (strcmp(Name, "showBPF_B") == 0)
	{
		doFilter(1, 0);
		return;
	}

	if (strcmp(Name, "showTXBPF_B") == 0)
	{
		doFilter(1, 1);
		return;
	}

	if (strcmp(Name, "showLPF_B") == 0)
	{
		doFilter(1, 2);
		return;
	}

	if (strcmp(Name, "Low_A") == 0)
	{
		handleButton(0, 1);
		return;
	}

	if (strcmp(Name, "High_A") == 0)
	{
		handleButton(0, 2);
		return;
	}

	if (strcmp(Name, "Both_A") == 0)
	{
		handleButton(0, 3);
		return;
	}

	if (strcmp(Name, "Stop_A") == 0)
	{
		handleButton(0, 0);
		return;
	}


	if (strcmp(Name, "Low_B") == 0)
	{
		handleButton(1, 1);
		return;
	}

	if (strcmp(Name, "High_B") == 0)
	{
		handleButton(1, 2);
		return;
	}

	if (strcmp(Name, "Both_B") == 0)
	{
		handleButton(1, 3);
		return;
	}

	if (strcmp(Name, "Stop_B") == 0)
	{
		handleButton(1, 0);
		return;
	}

	if (strcmp(Name, "Low_C") == 0)
	{
		handleButton(2, 1);
		return;
	}

	if (strcmp(Name, "High_C") == 0)
	{
		handleButton(2, 2);
		return;
	}

	if (strcmp(Name, "Both_C") == 0)
	{
		handleButton(2, 3);
		return;
	}

	if (strcmp(Name, "Stop_C") == 0)
	{
		handleButton(2, 0);
		return;
	}

	if (strcmp(Name, "Low_D") == 0)
	{
		handleButton(3, 1);
		return;
	}

	if (strcmp(Name, "High_D") == 0)
	{
		handleButton(3, 2);
		return;
	}

	if (strcmp(Name, "Both_D") == 0)
	{
		handleButton(3, 3);
		return;
	}

	if (strcmp(Name, "Stop_D") == 0)
	{
		handleButton(3, 0);
		return;
	}

	if (strcmp(Name, "actFont") == 0)
	{
		bool ok;
		Font = QFontDialog::getFont(&ok, QFont(Font, this));

		if (ok)
		{
			// the user clicked OK and font is set to the font the user selected
			QApplication::setFont(Font);
			sessionTable->horizontalHeader()->setFont(Font); 

			saveSettings();
		}
		else
		{
			// the user canceled the dialog; font is set to the initial
			// value, in this case Helvetica [Cronyx], 10

//			QApplication::setFont(Font);

		}
		return;
	}

	QMessageBox msgBox;
	msgBox.setWindowTitle("MessageBox Title");
	msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName());
	msgBox.exec();
}

Ui_ModemDialog * Dlg;

QDialog * modemUI;
QDialog * deviceUI;

void QtSoundModem::doModems()
{
	Dlg = new(Ui_ModemDialog);

	QDialog UI;
	char valChar[10];

	Dlg->setupUi(&UI);

	modemUI = &UI;
	deviceUI = 0;

	myResize *resize = new myResize();

	UI.installEventFilter(resize);

	sprintf(valChar, "%d", bpf[0]);
	Dlg->BPFWidthA->setText(valChar);
	sprintf(valChar, "%d", bpf[1]);
	Dlg->BPFWidthB->setText(valChar);
	sprintf(valChar, "%d", bpf[2]);
	Dlg->BPFWidthC->setText(valChar);
	sprintf(valChar, "%d", bpf[3]);
	Dlg->BPFWidthD->setText(valChar);

	sprintf(valChar, "%d", txbpf[0]);
	Dlg->TXBPFWidthA->setText(valChar);
	sprintf(valChar, "%d", txbpf[1]);
	Dlg->TXBPFWidthB->setText(valChar);
	sprintf(valChar, "%d", txbpf[2]);
	Dlg->TXBPFWidthC->setText(valChar);
	sprintf(valChar, "%d", txbpf[3]);
	Dlg->TXBPFWidthD->setText(valChar);

	sprintf(valChar, "%d", lpf[0]);
	Dlg->LPFWidthA->setText(valChar);
	sprintf(valChar, "%d", lpf[1]);
	Dlg->LPFWidthB->setText(valChar);
	sprintf(valChar, "%d", lpf[2]);
	Dlg->LPFWidthC->setText(valChar);
	sprintf(valChar, "%d", lpf[4]);
	Dlg->LPFWidthD->setText(valChar);

	sprintf(valChar, "%d", BPF_tap[0]);
	Dlg->BPFTapsA->setText(valChar);
	sprintf(valChar, "%d", BPF_tap[1]);
	Dlg->BPFTapsB->setText(valChar);
	sprintf(valChar, "%d", BPF_tap[2]);
	Dlg->BPFTapsC->setText(valChar);
	sprintf(valChar, "%d", BPF_tap[3]);
	Dlg->BPFTapsD->setText(valChar);

	sprintf(valChar, "%d", LPF_tap[0]);
	Dlg->LPFTapsA->setText(valChar);
	sprintf(valChar, "%d", LPF_tap[1]);
	Dlg->LPFTapsB->setText(valChar);
	sprintf(valChar, "%d", LPF_tap[2]);
	Dlg->LPFTapsC->setText(valChar);
	sprintf(valChar, "%d", LPF_tap[3]);
	Dlg->LPFTapsD->setText(valChar);

	Dlg->preEmphAllA->setChecked(emph_all[0]);

	if (emph_all[0])
		Dlg->preEmphA->setDisabled(TRUE);
	else
		Dlg->preEmphA->setCurrentIndex(emph_db[0]);

	Dlg->preEmphAllB->setChecked(emph_all[1]);

	if (emph_all[1])
		Dlg->preEmphB->setDisabled(TRUE);
	else
		Dlg->preEmphB->setCurrentIndex(emph_db[1]);

	Dlg->preEmphAllC->setChecked(emph_all[2]);

	if (emph_all[2])
		Dlg->preEmphC->setDisabled(TRUE);
	else
		Dlg->preEmphC->setCurrentIndex(emph_db[2]);

	Dlg->preEmphAllD->setChecked(emph_all[3]);

	if (emph_all[3])
		Dlg->preEmphD->setDisabled(TRUE);
	else
		Dlg->preEmphD->setCurrentIndex(emph_db[3]);


	Dlg->nonAX25A->setChecked(NonAX25[0]);
	Dlg->nonAX25B->setChecked(NonAX25[1]);
	Dlg->nonAX25C->setChecked(NonAX25[2]);
	Dlg->nonAX25D->setChecked(NonAX25[3]);

	Dlg->KISSOptA->setChecked(KISS_opt[0]);
	Dlg->KISSOptB->setChecked(KISS_opt[1]);
	Dlg->KISSOptC->setChecked(KISS_opt[2]);
	Dlg->KISSOptD->setChecked(KISS_opt[3]);

	sprintf(valChar, "%d", maxframe[0]);
	Dlg->MaxFrameA->setText(valChar);
	sprintf(valChar, "%d", maxframe[1]);
	Dlg->MaxFrameB->setText(valChar);
	sprintf(valChar, "%d", maxframe[2]);
	Dlg->MaxFrameC->setText(valChar);
	sprintf(valChar, "%d", maxframe[3]);
	Dlg->MaxFrameD->setText(valChar);

	sprintf(valChar, "%d", txdelay[0]);
	Dlg->TXDelayA->setText(valChar);
	sprintf(valChar, "%d", txdelay[1]);
	Dlg->TXDelayB->setText(valChar);
	sprintf(valChar, "%d", txdelay[2]);
	Dlg->TXDelayC->setText(valChar);
	sprintf(valChar, "%d", txdelay[3]);
	Dlg->TXDelayD->setText(valChar);


	sprintf(valChar, "%d", txtail[0]);
	Dlg->TXTailA->setText(valChar);
	sprintf(valChar, "%d", txtail[1]);
	Dlg->TXTailB->setText(valChar);
	sprintf(valChar, "%d", txtail[2]);
	Dlg->TXTailC->setText(valChar);
	sprintf(valChar, "%d", txtail[3]);
	Dlg->TXTailD->setText(valChar);

	Dlg->FrackA->setText(QString::number(frack_time[0]));
	Dlg->FrackB->setText(QString::number(frack_time[1]));
	Dlg->FrackC->setText(QString::number(frack_time[2]));
	Dlg->FrackD->setText(QString::number(frack_time[3]));

	Dlg->RetriesA->setText(QString::number(fracks[0]));
	Dlg->RetriesB->setText(QString::number(fracks[1]));
	Dlg->RetriesC->setText(QString::number(fracks[2]));
	Dlg->RetriesD->setText(QString::number(fracks[3]));

	sprintf(valChar, "%d", RCVR[0]);
	Dlg->AddRXA->setText(valChar);
	sprintf(valChar, "%d", RCVR[1]);
	Dlg->AddRXB->setText(valChar);
	sprintf(valChar, "%d", RCVR[2]);
	Dlg->AddRXC->setText(valChar);
	sprintf(valChar, "%d", RCVR[3]);
	Dlg->AddRXD->setText(valChar);

	sprintf(valChar, "%d", rcvr_offset[0]);
	Dlg->RXShiftA->setText(valChar);

	sprintf(valChar, "%d", rcvr_offset[1]);
	Dlg->RXShiftB->setText(valChar);

	sprintf(valChar, "%d", rcvr_offset[2]);
	Dlg->RXShiftC->setText(valChar);
	sprintf(valChar, "%d", rcvr_offset[3]);
	Dlg->RXShiftD->setText(valChar);

	//	speed[1]
	//	speed[2];

	Dlg->recoverBitA->setCurrentIndex(recovery[0]);
	Dlg->recoverBitB->setCurrentIndex(recovery[1]);
	Dlg->recoverBitC->setCurrentIndex(recovery[2]);
	Dlg->recoverBitD->setCurrentIndex(recovery[3]);

	Dlg->fx25ModeA->setCurrentIndex(fx25_mode[0]);
	Dlg->fx25ModeB->setCurrentIndex(fx25_mode[1]);
	Dlg->fx25ModeC->setCurrentIndex(fx25_mode[2]);
	Dlg->fx25ModeD->setCurrentIndex(fx25_mode[3]);

	Dlg->IL2PModeA->setCurrentIndex(il2p_mode[0]);
	Dlg->IL2PModeB->setCurrentIndex(il2p_mode[1]);
	Dlg->IL2PModeC->setCurrentIndex(il2p_mode[2]);
	Dlg->IL2PModeD->setCurrentIndex(il2p_mode[3]);

	Dlg->CRC_A->setChecked(il2p_crc[0]);
	Dlg->CRC_B->setChecked(il2p_crc[1]);
	Dlg->CRC_C->setChecked(il2p_crc[2]);
	Dlg->CRC_D->setChecked(il2p_crc[3]);

	Dlg->CWIDCall->setText(CWIDCall);
	Dlg->CWIDInterval->setText(QString::number(CWIDInterval));
	Dlg->CWIDMark->setText(CWIDMark);

	if (CWIDType)
		Dlg->radioButton_2->setChecked(1);
	else
		Dlg->CWIDType->setChecked(1);

	Dlg->afterTraffic->setChecked(afterTraffic);

	Dlg->RSIDSABM_A->setChecked(RSID_SABM[0]);
	Dlg->RSIDSABM_B->setChecked(RSID_SABM[1]);
	Dlg->RSIDSABM_C->setChecked(RSID_SABM[2]);
	Dlg->RSIDSABM_D->setChecked(RSID_SABM[3]);

	Dlg->RSIDUI_A->setChecked(RSID_UI[0]);
	Dlg->RSIDUI_B->setChecked(RSID_UI[1]);
	Dlg->RSIDUI_C->setChecked(RSID_UI[2]);
	Dlg->RSIDUI_D->setChecked(RSID_UI[3]);

	Dlg->DigiCallsA->setText(MyDigiCall[0]);
	Dlg->DigiCallsB->setText(MyDigiCall[1]);
	Dlg->DigiCallsC->setText(MyDigiCall[2]);
	Dlg->DigiCallsD->setText(MyDigiCall[3]);

	Dlg->RSID_1_SETMODEM->setChecked(RSID_SetModem[0]);
	Dlg->RSID_2_SETMODEM->setChecked(RSID_SetModem[1]);
	Dlg->RSID_3_SETMODEM->setChecked(RSID_SetModem[2]);
	Dlg->RSID_4_SETMODEM->setChecked(RSID_SetModem[3]);
	
	connect(Dlg->showBPF_A, SIGNAL(released()), this, SLOT(clickedSlot()));
	connect(Dlg->showTXBPF_A, SIGNAL(released()), this, SLOT(clickedSlot()));
	connect(Dlg->showLPF_A, SIGNAL(released()), this, SLOT(clickedSlot()));

	connect(Dlg->showBPF_B, SIGNAL(released()), this, SLOT(clickedSlot()));
	connect(Dlg->showTXBPF_B, SIGNAL(released()), this, SLOT(clickedSlot()));
	connect(Dlg->showLPF_B, SIGNAL(released()), this, SLOT(clickedSlot()));

	connect(Dlg->showBPF_C, SIGNAL(released()), this, SLOT(clickedSlot()));
	connect(Dlg->showTXBPF_C, SIGNAL(released()), this, SLOT(clickedSlot()));
	connect(Dlg->showLPF_C, SIGNAL(released()), this, SLOT(clickedSlot()));

	connect(Dlg->showBPF_D, SIGNAL(released()), this, SLOT(clickedSlot()));
	connect(Dlg->showTXBPF_D, SIGNAL(released()), this, SLOT(clickedSlot()));
	connect(Dlg->showLPF_D, SIGNAL(released()), this, SLOT(clickedSlot()));

	connect(Dlg->okButton, SIGNAL(clicked()), this, SLOT(modemaccept()));
	connect(Dlg->modemSave, SIGNAL(clicked()), this, SLOT(modemSave()));
	connect(Dlg->cancelButton, SIGNAL(clicked()), this, SLOT(modemreject()));

	connect(Dlg->SendRSID_1, SIGNAL(clicked()), this, SLOT(doRSIDA()));
	connect(Dlg->SendRSID_2, SIGNAL(clicked()), this, SLOT(doRSIDB()));
	connect(Dlg->SendRSID_3, SIGNAL(clicked()), this, SLOT(doRSIDC()));
	connect(Dlg->SendRSID_4, SIGNAL(clicked()), this, SLOT(doRSIDD()));

	connect(Dlg->preEmphAllA, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllAChanged(int)));
	connect(Dlg->preEmphAllB, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllBChanged(int)));
	connect(Dlg->preEmphAllC, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllCChanged(int)));
	connect(Dlg->preEmphAllD, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllDChanged(int)));

	UI.exec();
}

void QtSoundModem::preEmphAllAChanged(int state)
{
	Dlg->preEmphA->setDisabled(state);
}

void QtSoundModem::preEmphAllBChanged(int state)
{
	Dlg->preEmphB->setDisabled(state);
}

void QtSoundModem::preEmphAllCChanged(int state)
{
	Dlg->preEmphC->setDisabled(state);
}

void QtSoundModem::preEmphAllDChanged(int state)
{
	Dlg->preEmphD->setDisabled(state);
}

extern "C" void get_exclude_list(char * line, TStringList * list);

void QtSoundModem::modemaccept()
{
	modemSave();

	AGW_Report_Modem_Change(0);
	AGW_Report_Modem_Change(1);
	AGW_Report_Modem_Change(2);
	AGW_Report_Modem_Change(3);

	delete(Dlg);
	saveSettings();

	modemUI->accept();

}

void QtSoundModem::modemSave()
{
	QVariant Q;
	
	emph_all[0] = Dlg->preEmphAllA->isChecked();
	emph_db[0] = Dlg->preEmphA->currentIndex();

	emph_all[1] = Dlg->preEmphAllB->isChecked();
	emph_db[1] = Dlg->preEmphB->currentIndex();

	emph_all[2] = Dlg->preEmphAllC->isChecked();
	emph_db[2] = Dlg->preEmphC->currentIndex();

	emph_all[3] = Dlg->preEmphAllD->isChecked();
	emph_db[3] = Dlg->preEmphD->currentIndex();

	NonAX25[0] = Dlg->nonAX25A->isChecked();
	NonAX25[1] = Dlg->nonAX25B->isChecked();
	NonAX25[2] = Dlg->nonAX25C->isChecked();
	NonAX25[3] = Dlg->nonAX25D->isChecked();

	KISS_opt[0] = Dlg->KISSOptA->isChecked();
	KISS_opt[1] = Dlg->KISSOptB->isChecked();
	KISS_opt[2] = Dlg->KISSOptC->isChecked();
	KISS_opt[3] = Dlg->KISSOptD->isChecked();

	if (emph_db[0] < 0 || emph_db[0] > nr_emph)
		emph_db[0] = 0;

	if (emph_db[1] < 0 || emph_db[1] > nr_emph)
		emph_db[1] = 0;

	if (emph_db[2] < 0 || emph_db[2] > nr_emph)
		emph_db[2] = 0;

	if (emph_db[3] < 0 || emph_db[3] > nr_emph)
		emph_db[3] = 0;

	Q = Dlg->TXDelayA->text();
	txdelay[0] = Q.toInt();

	Q = Dlg->TXDelayB->text();
	txdelay[1] = Q.toInt();
	
	Q = Dlg->TXDelayC->text();
	txdelay[2] = Q.toInt();

	Q = Dlg->TXDelayD->text();
	txdelay[3] = Q.toInt();

	Q = Dlg->MaxFrameA->text();
	maxframe[0] = Q.toInt();

	Q = Dlg->MaxFrameB->text();
	maxframe[1] = Q.toInt();

	Q = Dlg->MaxFrameC->text();
	maxframe[2] = Q.toInt();

	Q = Dlg->MaxFrameD->text();
	maxframe[3] = Q.toInt();

	if (maxframe[0] == 0 || maxframe[0] > 7) maxframe[0] = 3;
	if (maxframe[1] == 0 || maxframe[1] > 7) maxframe[1] = 3;
	if (maxframe[2] == 0 || maxframe[2] > 7) maxframe[2] = 3;
	if (maxframe[3] == 0 || maxframe[3] > 7) maxframe[3] = 3;

	Q = Dlg->TXTailA->text();
	txtail[0] = Q.toInt();

	Q = Dlg->TXTailB->text();
	txtail[1] = Q.toInt();

	Q = Dlg->TXTailC->text();
	txtail[2] = Q.toInt();

	txtail[3] = Dlg->TXTailD->text().toInt();

	frack_time[0] = Dlg->FrackA->text().toInt();
	frack_time[1] = Dlg->FrackB->text().toInt();
	frack_time[2] = Dlg->FrackC->text().toInt();
	frack_time[3] = Dlg->FrackD->text().toInt();

	fracks[0] = Dlg->RetriesA->text().toInt();
	fracks[1] = Dlg->RetriesB->text().toInt();
	fracks[2] = Dlg->RetriesC->text().toInt();
	fracks[3] = Dlg->RetriesD->text().toInt();

	Q = Dlg->AddRXA->text();
	RCVR[0] = Q.toInt();

	Q = Dlg->AddRXB->text();
	RCVR[1] = Q.toInt();

	Q = Dlg->AddRXC->text();
	RCVR[2] = Q.toInt();

	Q = Dlg->AddRXD->text();
	RCVR[3] = Q.toInt();

	Q = Dlg->RXShiftA->text();
	rcvr_offset[0] = Q.toInt();

	Q = Dlg->RXShiftB->text();
	rcvr_offset[1] = Q.toInt();

	Q = Dlg->RXShiftC->text();
	rcvr_offset[2] = Q.toInt();

	Q = Dlg->RXShiftD->text();
	rcvr_offset[3] = Q.toInt();

	fx25_mode[0] = Dlg->fx25ModeA->currentIndex();
	fx25_mode[1] = Dlg->fx25ModeB->currentIndex();
	fx25_mode[2] = Dlg->fx25ModeC->currentIndex();
	fx25_mode[3] = Dlg->fx25ModeD->currentIndex();

	il2p_mode[0] = Dlg->IL2PModeA->currentIndex();
	il2p_mode[1] = Dlg->IL2PModeB->currentIndex();
	il2p_mode[2] = Dlg->IL2PModeC->currentIndex();
	il2p_mode[3] = Dlg->IL2PModeD->currentIndex();

	il2p_crc[0] = Dlg->CRC_A->isChecked();
	il2p_crc[1] = Dlg->CRC_B->isChecked();
	il2p_crc[2] = Dlg->CRC_C->isChecked();
	il2p_crc[3] = Dlg->CRC_D->isChecked();

	recovery[0] = Dlg->recoverBitA->currentIndex();
	recovery[1] = Dlg->recoverBitB->currentIndex();
	recovery[2] = Dlg->recoverBitC->currentIndex();
	recovery[3] = Dlg->recoverBitD->currentIndex();


	strcpy(CWIDCall, Dlg->CWIDCall->text().toUtf8().toUpper());
	strcpy(CWIDMark, Dlg->CWIDMark->text().toUtf8().toUpper());
	CWIDInterval = Dlg->CWIDInterval->text().toInt();
	CWIDType = Dlg->radioButton_2->isChecked();

	afterTraffic = Dlg->afterTraffic->isChecked();

	if (CWIDInterval && afterTraffic == false)
		cwidtimer->start(CWIDInterval * 60000);
	else
		cwidtimer->stop();


	RSID_SABM[0] = Dlg->RSIDSABM_A->isChecked();
	RSID_SABM[1] = Dlg->RSIDSABM_B->isChecked();
	RSID_SABM[2] = Dlg->RSIDSABM_C->isChecked();
	RSID_SABM[3] = Dlg->RSIDSABM_D->isChecked();

	RSID_UI[0] = Dlg->RSIDUI_A->isChecked();
	RSID_UI[1] = Dlg->RSIDUI_B->isChecked();
	RSID_UI[2] = Dlg->RSIDUI_C->isChecked();
	RSID_UI[3] = Dlg->RSIDUI_D->isChecked();

	RSID_SetModem[0] = Dlg->RSID_1_SETMODEM->isChecked();
	RSID_SetModem[1] = Dlg->RSID_2_SETMODEM->isChecked();
	RSID_SetModem[2] = Dlg->RSID_3_SETMODEM->isChecked();
	RSID_SetModem[3] = Dlg->RSID_4_SETMODEM->isChecked();

	Q = Dlg->DigiCallsA->text();
	strcpy(MyDigiCall[0], Q.toString().toUtf8().toUpper());

	Q = Dlg->DigiCallsB->text();
	strcpy(MyDigiCall[1], Q.toString().toUtf8().toUpper());

	Q = Dlg->DigiCallsC->text();
	strcpy(MyDigiCall[2], Q.toString().toUtf8().toUpper());

	Q = Dlg->DigiCallsD->text();
	strcpy(MyDigiCall[3], Q.toString().toUtf8().toUpper());

	int i;

	for (i = 0; i < 4; i++)
	{
		initTStringList(&list_digi_callsigns[i]);
		get_exclude_list(MyDigiCall[i], &list_digi_callsigns[i]);
	}

/*
	Q = Dlg->LPFWidthA->text();
	lpf[0] = Q.toInt();

	Q = Dlg->LPFWidthB->text();
	lpf[1] = Q.toInt();

	Q = Dlg->LPFWidthC->text();
	lpf[2] = Q.toInt();

	Q = Dlg->LPFWidthD->text();
	lpf[3] = Q.toInt();
*/

}

void QtSoundModem::modemreject()
{
	delete(Dlg);
	modemUI->reject();
}

void QtSoundModem::doRSIDA()
{
	needRSID[0] = 1;
}

void QtSoundModem::doRSIDB()
{
	needRSID[1] = 1;
}

void QtSoundModem::doRSIDC()
{
	needRSID[2] = 1;
}

void QtSoundModem::doRSIDD()
{
	needRSID[3] = 1;
}




void QtSoundModem::doFilter(int Chan, int Filter)
{
	Ui_Dialog Dev;
	QImage * bitmap;

	QDialog UI;

	Dev.setupUi(&UI);

	bitmap = new QImage(642, 312, QImage::Format_RGB32);

	bitmap->fill(qRgb(255, 255, 255));

	QPainter qPainter(bitmap);
	qPainter.setBrush(Qt::NoBrush);
	qPainter.setPen(Qt::black);

	if (Filter == 0)
		make_graph_buf(DET[0][0].BPF_core[Chan], BPF_tap[Chan], &qPainter);
	else if (Filter == 1)
		make_graph_buf(tx_BPF_core[Chan], tx_BPF_tap[Chan], &qPainter);
	else
		make_graph_buf(LPF_core[Chan], LPF_tap[Chan], &qPainter);

	qPainter.end();
	Dev.label->setPixmap(QPixmap::fromImage(*bitmap));

	UI.exec();

}

Ui_devicesDialog * Dev;

char NewPTTPort[80];

int newSoundMode = 0;
int oldSoundMode = 0;

void QtSoundModem::SoundModeChanged(bool State)
{
	UNUSED(State);

	// Mustn't change SoundMode until dialog is accepted

	if (Dev->UDP->isChecked())
		newSoundMode = 3;
	else if (Dev->PULSE->isChecked())
		newSoundMode = 2;
	else
		newSoundMode = Dev->OSS->isChecked();

}

void QtSoundModem::DualPTTChanged(bool State)
{
	UNUSED(State);

	// Forse Evaluation of Cat Port setting

	PTTPortChanged(0);
}

void QtSoundModem::CATChanged(bool State)
{
	UNUSED(State);
	PTTPortChanged(0);
}

void QtSoundModem::PTTPortChanged(int Selected)
{
	UNUSED(Selected);

	QVariant Q = Dev->PTTPort->currentText();
	strcpy(NewPTTPort, Q.toString().toUtf8());

	Dev->RTSDTR->setVisible(false);
	Dev->CAT->setVisible(false);

	Dev->PTTOnLab->setVisible(false);
	Dev->PTTOn->setVisible(false);
	Dev->PTTOff->setVisible(false);
	Dev->PTTOffLab->setVisible(false);
	Dev->CATLabel->setVisible(false);
	Dev->CATSpeed->setVisible(false);

	Dev->GPIOLab->setVisible(false);
	Dev->GPIOLeft->setVisible(false);
	Dev->GPIORight->setVisible(false);
	Dev->GPIOLab2->setVisible(false);

	Dev->CM108Label->setVisible(false);
	Dev->VIDPID->setVisible(false);

	if (strcmp(NewPTTPort, "None") == 0)
	{
	}
	else if (strcmp(NewPTTPort, "GPIO") == 0)
	{
		Dev->GPIOLab->setVisible(true);
		Dev->GPIOLeft->setVisible(true);
		if (Dev->DualPTT->isChecked())
		{
			Dev->GPIORight->setVisible(true);
			Dev->GPIOLab2->setVisible(true);
		}
	}

	else if (strcmp(NewPTTPort, "CM108") == 0)
	{
		Dev->CM108Label->setVisible(true);
//#ifdef __ARM_ARCHX
		Dev->CM108Label->setText("CM108 Device");
//#else
//		Dev->CM108Label->setText("CM108 VID/PID");
//#endif
		Dev->VIDPID->setText(CM108Addr);
		Dev->VIDPID->setVisible(true);
	}
	else if (strcmp(NewPTTPort, "HAMLIB") == 0)
	{
		Dev->CM108Label->setVisible(true);
		Dev->CM108Label->setText("rigctrld Port");
		Dev->VIDPID->setText(QString::number(HamLibPort));
		Dev->VIDPID->setVisible(true);
		Dev->PTTOnLab->setText("rigctrld Host");
		Dev->PTTOnLab->setVisible(true);
		Dev->PTTOn->setText(HamLibHost);
		Dev->PTTOn->setVisible(true);
	}
	else
	{
		Dev->RTSDTR->setVisible(true);
		Dev->CAT->setVisible(true);

		if (Dev->CAT->isChecked())
		{
			Dev->PTTOnLab->setVisible(true);
			Dev->PTTOnLab->setText("PTT On String");
			Dev->PTTOn->setText(PTTOnString);
			Dev->PTTOn->setVisible(true);
			Dev->PTTOff->setVisible(true);
			Dev->PTTOff->setText(PTTOffString);
			Dev->PTTOffLab->setVisible(true);
			Dev->CATLabel->setVisible(true);
			Dev->CATSpeed->setVisible(true);
		}
	}
}

bool myResize::eventFilter(QObject *obj, QEvent *event)
{
	if (event->type() == QEvent::Resize)
	{
		QResizeEvent *resizeEvent = static_cast<QResizeEvent *>(event);
		QSize size = resizeEvent->size();
		int h = size.height();
		int w = size.width();

		if (obj == deviceUI)
			Dev->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10));
		else
			Dlg->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10));

		return true;
	}
	return QObject::eventFilter(obj, event);
}

void QtSoundModem::doDevices()
{
	char valChar[10];

	Dev = new(Ui_devicesDialog);

	QDialog UI;

	int i;

	Dev->setupUi(&UI);

	deviceUI = &UI;
	modemUI = 0;

	myResize *resize = new myResize();

	UI.installEventFilter(resize);

	newSoundMode = SoundMode;
	oldSoundMode = SoundMode;

#ifdef WIN32
	Dev->ALSA->setText("WaveOut");
	Dev->OSS->setVisible(0);
	Dev->PULSE->setVisible(0);
#endif

	if (SoundMode == 0)
		Dev->ALSA->setChecked(1);
	else if (SoundMode == 1)
		Dev->OSS->setChecked(1);
	else if (SoundMode == 2)
		Dev->PULSE->setChecked(1);
	else if (SoundMode == 2)
		Dev->UDP->setChecked(1);

	connect(Dev->ALSA, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool)));
	connect(Dev->OSS, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool)));
	connect(Dev->PULSE, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool)));
	connect(Dev->UDP, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool)));

	for (i = 0; i < PlaybackCount; i++)
		Dev->outputDevice->addItem(&PlaybackNames[i][0]);

	i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains);


	if (i == -1)
	{
		// Add device to list

		Dev->outputDevice->addItem(PlaybackDevice);
		i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains);
	}

	Dev->outputDevice->setCurrentIndex(i);

	for (i = 0; i < CaptureCount; i++)
		Dev->inputDevice->addItem(&CaptureNames[i][0]);

	i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains);

	if (i == -1)
	{
		// Add device to list

		Dev->inputDevice->addItem(CaptureDevice);
		i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains);
	}
	Dev->inputDevice->setCurrentIndex(i);

	Dev->Modem_1_Chan->setCurrentIndex(soundChannel[0]);
	Dev->Modem_2_Chan->setCurrentIndex(soundChannel[1]);
	Dev->Modem_3_Chan->setCurrentIndex(soundChannel[2]);
	Dev->Modem_4_Chan->setCurrentIndex(soundChannel[3]);

	// Disable "None" option in first modem

	QStandardItemModel *model = dynamic_cast<QStandardItemModel *>(Dev->Modem_1_Chan->model());
	QStandardItem * item = model->item(0, 0);
	item->setEnabled(false);

	Dev->singleChannelOutput->setChecked(SCO);
	Dev->colourWaterfall->setChecked(raduga);

	sprintf(valChar, "%d", KISSPort);
	Dev->KISSPort->setText(valChar);
	Dev->KISSEnabled->setChecked(KISSServ);

	sprintf(valChar, "%d", AGWPort);
	Dev->AGWPort->setText(valChar);
	Dev->AGWEnabled->setChecked(AGWServ);

	Dev->PTTOn->setText(PTTOnString);
	Dev->PTTOff->setText(PTTOffString);

	sprintf(valChar, "%d", PTTBAUD);
	Dev->CATSpeed->setText(valChar);

	sprintf(valChar, "%d", UDPClientPort);
	Dev->UDPPort->setText(valChar);
	Dev->UDPTXHost->setText(UDPHost);

	if (UDPServerPort != TXPort)
		sprintf(valChar, "%d/%d", UDPServerPort, TXPort);
	else
		sprintf(valChar, "%d", UDPServerPort);

	Dev->UDPTXPort->setText(valChar);

	Dev->UDPEnabled->setChecked(UDPServ);

	sprintf(valChar, "%d", pttGPIOPin);
	Dev->GPIOLeft->setText(valChar);
	sprintf(valChar, "%d", pttGPIOPinR);
	Dev->GPIORight->setText(valChar);

	Dev->VIDPID->setText(CM108Addr);

	QStringList items;

	connect(Dev->CAT, SIGNAL(toggled(bool)), this, SLOT(CATChanged(bool)));
	connect(Dev->DualPTT, SIGNAL(toggled(bool)), this, SLOT(DualPTTChanged(bool)));
	connect(Dev->PTTPort, SIGNAL(currentIndexChanged(int)), this, SLOT(PTTPortChanged(int)));

	if (PTTMode == PTTCAT)
		Dev->CAT->setChecked(true);
	else
		Dev->RTSDTR->setChecked(true);

	for (const QSerialPortInfo &info : Ports)
	{
		items.append(info.portName());
	}

	items.sort();

	Dev->PTTPort->addItem("None");
	Dev->PTTPort->addItem("CM108");

	//#ifdef __ARM_ARCH

	Dev->PTTPort->addItem("GPIO");

	//#endif

	Dev->PTTPort->addItem("HAMLIB");

	for (const QString &info : items)
	{
		Dev->PTTPort->addItem(info);
	}

	// If we are using a user specifed device add it

	i = Dev->PTTPort->findText(PTTPort, Qt::MatchFixedString);

	if (i == -1)
	{
		// Add our device to list

		Dev->PTTPort->insertItem(0, PTTPort);
		i = Dev->PTTPort->findText(PTTPort, Qt::MatchContains);
	}

	Dev->PTTPort->setCurrentIndex(i);

	PTTPortChanged(0);				// Force reevaluation

	Dev->txRotation->setChecked(TX_rotate);
	Dev->DualPTT->setChecked(DualPTT);

	Dev->multiCore->setChecked(multiCore);
	Dev->darkTheme->setChecked(darkTheme);

	Dev->WaterfallMin->setCurrentIndex(Dev->WaterfallMin->findText(QString::number(WaterfallMin), Qt::MatchFixedString));
	Dev->WaterfallMax->setCurrentIndex(Dev->WaterfallMax->findText(QString::number(WaterfallMax), Qt::MatchFixedString));

	QObject::connect(Dev->okButton, SIGNAL(clicked()), this, SLOT(deviceaccept()));
	QObject::connect(Dev->cancelButton, SIGNAL(clicked()), this, SLOT(devicereject()));

	UI.exec();

}

void QtSoundModem::mysetstyle()
{
	if (darkTheme)
	{
		qApp->setStyleSheet(
			"QWidget {color: white; background-color: black}"
			"QTabBar::tab {color: rgb(127, 127, 127); background-color: black}"
			"QTabBar::tab::selected {color: white}"
			"QPushButton {border-style: outset; border-width: 2px; border-color: rgb(127, 127, 127)}"
			"QPushButton::default {border-style: outset; border-width: 2px; border-color: white}");

		sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(40, 40, 40) }");

		txText = qRgb(255, 127, 127);
		rxText = qRgb(173, 216, 230);
	}
	else
	{
		qApp->setStyleSheet("");

		sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(224, 224, 224) }");

		txText = qRgb(192, 0, 0);
		rxText = qRgb(0, 0, 192);
	}
}

void QtSoundModem::deviceaccept()
{
	QVariant Q = Dev->inputDevice->currentText();
	int cardChanged = 0;
	char portString[32];
	int newMax;
	int newMin;

	if (Dev->UDP->isChecked())
	{
		// cant have server and slave

		if (Dev->UDPEnabled->isChecked())
		{
			QMessageBox::about(this, tr("QtSoundModem"),
				tr("Can't have UDP sound source and UDP server at same time"));
			return;
		}
	}

	if (oldSoundMode != newSoundMode)
	{
		QMessageBox msgBox;

		msgBox.setText("QtSoundModem must restart to change Sound Mode.\n"
			"Program will close if you hit Ok\n"
			"You will need to reselect audio devices after restarting");

		msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);

		int i = msgBox.exec();

		if (i == QMessageBox::Ok)
		{			
			SoundMode = newSoundMode;
			saveSettings();

			Closing = 1;
			return;
		}

		if (oldSoundMode == 0)
			Dev->ALSA->setChecked(1);
		else if (oldSoundMode == 1)
			Dev->OSS->setChecked(1);
		else if (oldSoundMode == 2)
			Dev->PULSE->setChecked(1);
		else if (oldSoundMode == 3)
			Dev->UDP->setChecked(1);

		QMessageBox::about(this, tr("Info"),
			tr("<p align = 'center'>Changes not saved</p>"));

		return;

	}

	if (strcmp(CaptureDevice, Q.toString().toUtf8()) != 0)
	{
		strcpy(CaptureDevice, Q.toString().toUtf8());
		cardChanged = 1;
	}

	CaptureIndex = Dev->inputDevice->currentIndex();

	Q = Dev->outputDevice->currentText();

	if (strcmp(PlaybackDevice, Q.toString().toUtf8()) != 0)
	{
		strcpy(PlaybackDevice, Q.toString().toUtf8());
		cardChanged = 1;
	}

	PlayBackIndex = Dev->outputDevice->currentIndex();

	soundChannel[0] = Dev->Modem_1_Chan->currentIndex();
	soundChannel[1] = Dev->Modem_2_Chan->currentIndex();
	soundChannel[2] = Dev->Modem_3_Chan->currentIndex();
	soundChannel[3] = Dev->Modem_4_Chan->currentIndex();

	UsingLeft = 0;
	UsingRight = 0;
	UsingBothChannels = 0;

	for (int i = 0; i < 4; i++)
	{
		if (soundChannel[i] == LEFT)
		{
			UsingLeft = 1;
			modemtoSoundLR[i] = 0;
		}
		else if (soundChannel[i] == RIGHT)
		{
			UsingRight = 1;
			modemtoSoundLR[i] = 1;
		}
	}

	if (UsingLeft && UsingRight)
		UsingBothChannels = 1;

	SCO = Dev->singleChannelOutput->isChecked();
	raduga = Dev->colourWaterfall->isChecked();
	AGWServ = Dev->AGWEnabled->isChecked();
	KISSServ = Dev->KISSEnabled->isChecked();

	Q = Dev->KISSPort->text();
	KISSPort = Q.toInt();

	Q = Dev->AGWPort->text();
	AGWPort = Q.toInt();

	Q = Dev->PTTPort->currentText();
	
	char temp[256];

	strcpy(temp, Q.toString().toUtf8());

	if (strlen(temp))
		strcpy(PTTPort, temp);

	DualPTT = Dev->DualPTT->isChecked();
	TX_rotate = Dev->txRotation->isChecked();
	multiCore = Dev->multiCore->isChecked();
	darkTheme = Dev->darkTheme->isChecked();
	mysetstyle();

	if (Dev->CAT->isChecked())
		PTTMode = PTTCAT;
	else
		PTTMode = PTTRTS;

	Q = Dev->PTTOn->text();
	strcpy(PTTOnString, Q.toString().toUtf8());
	Q = Dev->PTTOff->text();
	strcpy(PTTOffString, Q.toString().toUtf8());

	Q = Dev->CATSpeed->text();
	PTTBAUD = Q.toInt();

	Q = Dev->UDPPort->text();
	UDPClientPort = Q.toInt();


	Q = Dev->UDPTXPort->text();
	strcpy(portString, Q.toString().toUtf8());
	UDPServerPort = atoi(portString);

	if (strchr(portString, '/'))
	{
		char * ptr = strlop(portString, '/');
		TXPort = atoi(ptr);
	}
	else
		TXPort = UDPServerPort;

	Q = Dev->UDPTXHost->text();
	strcpy(UDPHost, Q.toString().toUtf8());

	UDPServ = Dev->UDPEnabled->isChecked();

	Q = Dev->GPIOLeft->text();
	pttGPIOPin = Q.toInt();

	Q = Dev->GPIORight->text();
	pttGPIOPinR = Q.toInt();

	Q = Dev->VIDPID->text();

	if (strcmp(PTTPort, "CM108") == 0)
		strcpy(CM108Addr, Q.toString().toUtf8());
	else if (strcmp(PTTPort, "HAMLIB") == 0)
	{
		HamLibPort = Q.toInt();
		Q = Dev->PTTOn->text();
		strcpy(HamLibHost, Q.toString().toUtf8());
	}

	Q = Dev->WaterfallMax->currentText();
	newMax = Q.toInt();

	Q = Dev->WaterfallMin->currentText();
	newMin = Q.toInt();

	if (newMax != WaterfallMax || newMin != WaterfallMin)
	{
		QMessageBox msgBox;

		msgBox.setText("QtSoundModem must restart to change Waterfall range. Program will close if you hit Ok\n"
		"It may take up to 30 seconds for the program to start for the first time after changing settings");

		msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);

		int i = msgBox.exec();

		if (i == QMessageBox::Ok)
		{
			Configuring = 1;			// Stop Waterfall

			WaterfallMax = newMax;
			WaterfallMin = newMin;
			saveSettings();
			Closing = 1;
			return;
		}
	}


	ClosePTTPort();
	OpenPTTPort();

	NeedWaterfallHeaders = true;

	delete(Dev);
	saveSettings();
	deviceUI->accept();

	if (cardChanged)
	{
		InitSound(1);
	}

	// Reset title and tooltip in case ports changed 

	char Title[128];
	sprintf(Title, "QtSoundModem Version %s Ports %d%s/%d%s", VersionString, AGWPort, AGWServ ? "*": "", KISSPort, KISSServ ? "*" : "");
	w->setWindowTitle(Title);

	sprintf(Title, "QtSoundModem %d %d", AGWPort, KISSPort);
	if (trayIcon)
		trayIcon->setToolTip(Title);

	QSize newSize(this->size());
	QSize oldSize(this->size());

	QResizeEvent *myResizeEvent = new QResizeEvent(newSize, oldSize);

	QCoreApplication::postEvent(this, myResizeEvent);  
}

void QtSoundModem::devicereject()
{	
	delete(Dev);
	deviceUI->reject();
}

void QtSoundModem::handleButton(int Port, int Type)
{
	// interlock calib with CWID

	if (calib_mode[0] == 4)		// CWID
		return;
	
	doCalib(Port, Type);
}



void QtSoundModem::doRestartWF()
{
	if (inWaterfall)
	{
		// in waterfall update thread

		wftimer->start(5000);
		return;
	}
		
	lockWaterfall = true;

	if (Firstwaterfall | Secondwaterfall)
	{
		initWaterfall(0);
		initWaterfall(1);
	}

	delete(RXLevel);
	delete(ui.RXLevel);
	ui.RXLevel = new QLabel(ui.centralWidget);
	RXLevelCopy = ui.RXLevel;
	ui.RXLevel->setGeometry(QRect(780, 14, 150, 11));
	ui.RXLevel->setFrameShape(QFrame::Box);
	ui.RXLevel->setFrameShadow(QFrame::Sunken);

	delete(RXLevel2);
	delete(ui.RXLevel2);

	ui.RXLevel2 = new QLabel(ui.centralWidget);
	RXLevel2Copy = ui.RXLevel2;

	ui.RXLevel2->setGeometry(QRect(780, 23, 150, 11));
	ui.RXLevel2->setFrameShape(QFrame::Box);
	ui.RXLevel2->setFrameShadow(QFrame::Sunken);

	RXLevel = new QImage(150, 10, QImage::Format_RGB32);
	RXLevel->fill(cyan);

	RXLevel2 = new QImage(150, 10, QImage::Format_RGB32);
	RXLevel2->fill(white);

	ui.RXLevel->setVisible(1);
	ui.RXLevel2->setVisible(1);

	lockWaterfall = false;
}


void QtSoundModem::doAbout()
{
	QMessageBox::about(this, tr("About"),
		tr("G8BPQ's port of UZ7HO's Soundmodem\n\nCopyright (C) 2019-2020 Andrei Kopanchuk UZ7HO"));
}

void QtSoundModem::doCalibrate()
{
	Ui_calDialog Calibrate;
	{
		QDialog UI;
		Calibrate.setupUi(&UI);

		connect(Calibrate.Low_A, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.Both_A, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.Stop_A, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.Low_B, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.High_B, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.Both_B, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.Stop_B, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.Low_C, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.High_C, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.Both_C, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.Stop_C, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.Low_D, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.High_D, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.Both_D, SIGNAL(released()), this, SLOT(clickedSlot()));
		connect(Calibrate.Stop_D, SIGNAL(released()), this, SLOT(clickedSlot()));

		/*
		
		connect(Calibrate.Low_A, &QPushButton::released, this, [=] { handleButton(0, 1); });
		connect(Calibrate.High_A, &QPushButton::released, this, [=] { handleButton(0, 2); });
		connect(Calibrate.Both_A, &QPushButton::released, this, [=] { handleButton(0, 3); });
		connect(Calibrate.Stop_A, &QPushButton::released, this, [=] { handleButton(0, 0); });
		connect(Calibrate.Low_B, &QPushButton::released, this, [=] { handleButton(1, 1); });
		connect(Calibrate.High_B, &QPushButton::released, this, [=] { handleButton(1, 2); });
		connect(Calibrate.Both_B, &QPushButton::released, this, [=] { handleButton(1, 3); });
		connect(Calibrate.Stop_B, &QPushButton::released, this, [=] { handleButton(1, 0); });

//		connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(handleButton(1, 2)));
*/
		UI.exec();
	}
}

void QtSoundModem::RefreshSpectrum(unsigned char * Data)
{
	int i;

	// Last 4 bytes are level busy and Tuning lines

	Waterfall->fill(Black);

	if (Data[206] != LastLevel)
	{
		LastLevel = Data[206];
//		RefreshLevel(LastLevel);
	}

	if (Data[207] != LastBusy)
	{
		LastBusy = Data[207];
//		Busy->setVisible(LastBusy);
	}

	for (i = 0; i < 205; i++)
	{
		int val = Data[0];

		if (val > 63)
			val = 63;

		Waterfall->setPixel(i, val, Yellow);
		if (val < 62)
			Waterfall->setPixel(i, val + 1, Gold);
		Data++;
	}

	ui.Waterfall->setPixmap(QPixmap::fromImage(*Waterfall));

}

void RefreshLevel(unsigned int Level, unsigned int LevelR)
{
	// Redraw the RX Level Bar Graph

	unsigned int  x, y;

	for (x = 0; x < 150; x++)
	{
		for (y = 0; y < 10; y++)
		{
			if (x < Level)
			{
				if (Level < 50)
					RXLevel->setPixel(x, y, yellow);
				else if (Level > 135)
					RXLevel->setPixel(x, y, red);
				else
					RXLevel->setPixel(x, y, green);
			}
			else
				RXLevel->setPixel(x, y, white);
		}
	}
	RXLevelCopy->setPixmap(QPixmap::fromImage(*RXLevel));

	for (x = 0; x < 150; x++)
	{
		for (y = 0; y < 10; y++)
		{
			if (x < LevelR)
			{
				if (LevelR < 50)
					RXLevel2->setPixel(x, y, yellow);
				else if (LevelR > 135)
					RXLevel2->setPixel(x, y, red);
				else
					RXLevel2->setPixel(x, y, green);
			}
			else
				RXLevel2->setPixel(x, y, white);
		}
	}
	RXLevel2Copy->setPixmap(QPixmap::fromImage(*RXLevel2));
}

extern "C" unsigned char CurrentLevel;
extern "C" unsigned char CurrentLevelR;

void QtSoundModem::RefreshWaterfall(int snd_ch, unsigned char * Data)
{
	int j;
	unsigned char * Line;
	int len = Waterfall->bytesPerLine();
	int TopLine = NextWaterfallLine[snd_ch];

	// Write line to cyclic buffer then draw starting with the line just written

	// Length is 208 bytes, including Level and Busy flags

	memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Data, 206);
	if (NextWaterfallLine[snd_ch] > 63)
		NextWaterfallLine[snd_ch] = 0;

	for (j = 63; j > 0; j--)
	{
		Line = Waterfall->scanLine(j);
		memcpy(Line, &WaterfallLines[snd_ch][TopLine++][0], len);
		if (TopLine > 63)
			TopLine = 0;
	}

	ui.Waterfall->setPixmap(QPixmap::fromImage(*Waterfall));
}

void QtSoundModem::sendtoTrace(char * Msg, int tx)
{
	const QTextCursor old_cursor = monWindowCopy->textCursor();
	const int old_scrollbar_value = monWindowCopy->verticalScrollBar()->value();
	const bool is_scrolled_down = old_scrollbar_value == monWindowCopy->verticalScrollBar()->maximum();

	// Move the cursor to the end of the document.
	monWindowCopy->moveCursor(QTextCursor::End);

	// Insert the text at the position of the cursor (which is the end of the document).

	if (tx)
		monWindowCopy->setTextColor(txText);
	else
		monWindowCopy->setTextColor(rxText);

	monWindowCopy->textCursor().insertText(Msg);

	if (old_cursor.hasSelection() || !is_scrolled_down)
	{
		// The user has selected text or scrolled away from the bottom: maintain position.
		monWindowCopy->setTextCursor(old_cursor);
		monWindowCopy->verticalScrollBar()->setValue(old_scrollbar_value);
	}
	else
	{
		// The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom.
		monWindowCopy->moveCursor(QTextCursor::End);
		monWindowCopy->verticalScrollBar()->setValue(monWindowCopy->verticalScrollBar()->maximum());
	}

	free(Msg);
}


// I think this does the waterfall

typedef struct TRGBQ_t
{
	Byte b, g, r, re;

} TRGBWQ;

typedef struct tagRECT
{
	int    left;
	int    top;
	int    right;
	int    bottom;
} RECT;

unsigned int RGBWF[256] ;


extern "C" void init_raduga()
{
	Byte offset[6] = {0, 51, 102, 153, 204};
	Byte i, n;

	for (n = 0; n < 52; n++)
	{
		i = n * 5;

		RGBWF[n + offset[0]] = qRgb(0, 0, i);
		RGBWF[n + offset[1]] = qRgb(0, i, 255);
		RGBWF[n + offset[2]] = qRgb(0, 255, 255 - i);
		RGBWF[n + offset[3]] = qRgb(1, 255, 0);
		RGBWF[n + offset[4]] = qRgb(255, 255 - 1, 0);
	}
}

extern "C" int nonGUIMode;


// This draws the Frequency Scale on Waterfall

extern "C" void DrawFreqTicks()
{
	if (nonGUIMode)
		return;

	// Draw Frequency Markers on waterfall header(s);

	int x, i;
	char Textxx[20];
	QImage * bm = Waterfall;

	QPainter qPainter(bm);
	qPainter.setBrush(Qt::black);
	qPainter.setPen(Qt::white);

	int Chan;

#ifdef WIN32
		int Top = 3;
#else
		int Top = 4;
#endif
		int Base = 0;

		for (Chan = 0; Chan < 2; Chan++)
		{
			if (Chan == 1 || ((UsingBothChannels == 0) && (UsingRight == 1)))
				sprintf(Textxx, "Right");
			else
				sprintf(Textxx, "Left");

			qPainter.drawText(2, Top, 100, 20, 0, Textxx);

			// We drew markers every 100 Hz or 100 / binsize pixels

			int Markers = ((WaterfallMax - WaterfallMin) / 100) + 5;			// Number of Markers to draw
			int Freq = WaterfallMin;
			float PixelsPerMarker = 100.0 / BinSize;

			for (i = 0; i < Markers; i++)
			{
				x = round(PixelsPerMarker * i);
				if (x < 1025)
				{
					if ((Freq % 500) == 0)
						qPainter.drawLine(x, Base + 22, x, Base + 15);
					else
						qPainter.drawLine(x, Base + 22, x, Base + 18);

					if ((Freq % 500) == 0)
					{
						sprintf(Textxx, "%d", Freq);

						if (x < 924)
							qPainter.drawText(x - 12, Top, 100, 20, 0, Textxx);
					}
				}
				Freq += 100;
			}

			if (UsingBothChannels == 0)
				break;

			Top += WaterfallTotalPixels;
			Base = WaterfallTotalPixels;
		}

}

// These draws the frequency Markers on the Waterfall

void DrawModemFreqRange()
{
	if (nonGUIMode)
		return;

	// Draws the modem freq bars on waterfall header(s)


	int x1, x2, k, pos1, pos2, pos3;
	QImage * bm = Waterfall;

	QPainter qPainter(bm);
	qPainter.setBrush(Qt::NoBrush);
	qPainter.setPen(Qt::white);

	int Chan;
	int LRtoDisplay = LEFT;
	int top = 0;

	for (Chan = 0; Chan < 2; Chan++)
	{
		if (Chan == 1 || ((UsingBothChannels == 0) && (UsingRight == 1)))
			LRtoDisplay = RIGHT;

		//	bm->fill(black);

	//	qPainter.fillRect(top, 23, 1024, 10, Qt::black);

		// We drew markers every 100 Hz or 100 / binsize pixels

		float PixelsPerHz = 1.0 / BinSize;
		k = 26 + top;

		// draw all enabled ports on the ports on this soundcard

		// First Modem is always on the first waterfall
		// If second is enabled it is on the first unless different
		//		channel from first

		for (int i = 0; i < 4; i++)
		{
			if (soundChannel[i] != LRtoDisplay)
					continue;

			pos1 = roundf((((rxOffset + chanOffset[i] + rx_freq[i]) - 0.5*rx_shift[i]) - WaterfallMin) * PixelsPerHz) - 5;
			pos2 = roundf((((rxOffset + chanOffset[i] + rx_freq[i]) + 0.5*rx_shift[i]) - WaterfallMin) * PixelsPerHz) - 5;
			pos3 = roundf(((rxOffset + chanOffset[i] + rx_freq[i])) - WaterfallMin * PixelsPerHz);
			x1 = pos1 + 5;
			x2 = pos2 + 5;

			qPainter.setPen(Qt::white);
			qPainter.drawLine(x1, k, x2, k);
			qPainter.drawLine(x1, k - 3, x1, k + 3);
			qPainter.drawLine(x2, k - 3, x2, k + 3);
			//		qPainter.drawLine(pos3, k - 3, pos3, k + 3);

			if (rxOffset || chanOffset[i])
			{
				// Draw TX posn if rxOffset used

				pos3 = roundf((rx_freq[i] - WaterfallMin) * PixelsPerHz);
				qPainter.setPen(Qt::magenta);
				qPainter.drawLine(pos3, k - 3, pos3, k + 3);
				qPainter.drawLine(pos3, k - 3, pos3, k + 3);
				qPainter.drawLine(pos3 - 2, k - 3, pos3 + 2, k - 3);
			}

			k += 3;
		}
		if (UsingBothChannels == 0)
			break;

		LRtoDisplay = RIGHT;
		top = WaterfallTotalPixels;
	}
}


void doWaterfallThread(void * param);

extern "C" void doWaterfall(int snd_ch)
{
	if (nonGUIMode)
		return;

	if (Closing)
		return;

	if (lockWaterfall)
		return;

//	if (multiCore)			// Run modems in separate threads
//		_beginthread(doWaterfallThread, 0, xx);
//	else
		doWaterfallThread((void *)(size_t)snd_ch);

}

extern "C" void displayWaterfall()
{
	// if we are using both channels but only want right need to extract correct half of Image

	if (Waterfall == nullptr)
		return;

	if (UsingBothChannels && (Firstwaterfall == 0))
		WaterfallCopy->setAlignment(Qt::AlignBottom | Qt::AlignLeft);
	else
		WaterfallCopy->setAlignment(Qt::AlignTop | Qt::AlignLeft);

	WaterfallCopy->setPixmap(QPixmap::fromImage(*Waterfall));
}

extern "C" float aFFTAmpl[1024];
extern "C" void SMUpdateBusyDetector(int LR, float * Real, float *Imag);

void doWaterfallThread(void * param)
{
	int snd_ch = (int)(size_t)param;

	if (lockWaterfall)
		return;

	if (Configuring)
		return;

	inWaterfall = true;					// don't allow restart waterfall

	if (snd_ch == 1 && UsingLeft == 0)	// Only using right
		snd_ch = 0;						// Samples are in first buffer

	QImage * bm = Waterfall;

	int  i;
	single  mag;
	UCHAR * p;
	UCHAR Line[4096] = "";			// 4 bytes per pixel

	int lineLen, Start, End;
	word  hFFTSize;
	Byte  n;
	float RealOut[8192] = { 0 };
	float ImagOut[8192];


	RefreshLevel(CurrentLevel, CurrentLevelR);	// Signal Level

	hFFTSize = FFTSize / 2;


	// I think an FFT should produce n/2 bins, each of Samp/n Hz
	// Looks like my code only works with n a power of 2

	// So can use 1024 or 4096. 1024 gives 512 bins of 11.71875 and a 512 pixel 
	// display (is this enough?)

	Start = (WaterfallMin / BinSize);		// First and last bins to process
	End = (WaterfallMax / BinSize);

	if (0)	//RSID_WF
	{
		// Use the Magnitudes in float aFFTAmpl[RSID_FFT_SIZE];

		for (i = 0; i < hFFTSize; i++)
		{
			mag = aFFTAmpl[i];

			mag *= 0.00000042f;

			if (mag < 0.00001f)
				mag = 0.00001f;

			if (mag > 1.0f)
				mag = 1.0f;

			mag = 22 * log2f(mag) + 255;

			if (mag < 0)
				mag = 0;

			fft_disp[snd_ch][i] = round(mag);
		}
	}
	else
	{
		dofft(&fft_buf[snd_ch][0], RealOut, ImagOut);

		//	FourierTransform(1024, &fft_buf[snd_ch][0], RealOut, ImagOut, 0);

		for (i = Start; i < End; i++)
		{
			//mag: = ComplexMag(fft_d[i])*0.00000042;

	//		mag = sqrtf(powf(RealOut[i], 2) + powf(ImagOut[i], 2)) * 0.00000042f;

			mag = powf(RealOut[i], 2);
			mag += powf(ImagOut[i], 2);
			mag = sqrtf(mag);
			mag *= 0.00000042f;


			if (mag > MaxMagOut)
			{
				MaxMagOut = mag;
				MaxMagIndex = i;
			}

			if (mag < 0.00001f)
				mag = 0.00001f;

			if (mag > 1.0f)
				mag = 1.0f;

			mag = 22 * log2f(mag) + 255;

			if (mag < 0)
				mag = 0;

			MagOut[i] = mag;					// for Freq Guess
			fft_disp[snd_ch][i] = round(mag);
		}
	}

	SMUpdateBusyDetector(snd_ch, RealOut, ImagOut);



	// we always do fft so we can get centre freq and do busy detect. But only update waterfall if on display

	if (bm == 0)
	{
		inWaterfall = false;
		return;
	}
	if ((Firstwaterfall == 0 && snd_ch == 0) || (Secondwaterfall == 0 && snd_ch == 1))
	{
		inWaterfall = false;
		return;
	}

	p = Line;
	lineLen = 4096;

	if (raduga == DISP_MONO)
	{
		for (i = Start; i < End; i++)
		{
			n = fft_disp[snd_ch][i];
			*(p++) = n;					// all colours the same
			*(p++) = n;
			*(p++) = n;
			p++;
		}
	}
	else
	{
		for (i = Start; i < End; i++)
		{
			n = fft_disp[snd_ch][i];
			memcpy(p, &RGBWF[n], 4);
			p += 4;
		}
	}

	// Scroll



	int TopLine = NextWaterfallLine[snd_ch];
	int TopScanLine = WaterfallHeaderPixels;

	if (snd_ch)
		TopScanLine += WaterfallTotalPixels;

	// Write line to cyclic buffer then draw starting with the line just written

	memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Line, 4096);
	if (NextWaterfallLine[snd_ch] > 79)
		NextWaterfallLine[snd_ch] = 0;
	
	// Sanity check

	int WFMaxLine = bm->height();


	if ((79 + TopScanLine) >= bm->height())
	{
		printf("Invalid WFMaxLine %d \n", bm->height());
			exit(1);
	}


	for (int j = 79; j > 0; j--)
	{
		p = bm->scanLine(j + TopScanLine);
		if (p == nullptr)
		{
			printf("Invalid WF Pointer \n");
			exit(1);
		}
		memcpy(p, &WaterfallLines[snd_ch][TopLine][0], lineLen);
		TopLine++;
		if (TopLine > 79)
			TopLine = 0;
	}

	inWaterfall = false;
}


void QtSoundModem::changeEvent(QEvent* e)
{
	if (e->type() == QEvent::WindowStateChange)
	{
		QWindowStateChangeEvent* ev = static_cast<QWindowStateChangeEvent*>(e);

		qDebug() << windowState();

		if (!(ev->oldState() & Qt::WindowMinimized) && windowState() & Qt::WindowMinimized)
		{
			if (trayIcon)
				setVisible(false);
		}
//		if (!(ev->oldState() != Qt::WindowNoState) && windowState() == Qt::WindowNoState)
//		{
//			QMessageBox::information(this, "", "Window has been restored");
//		}

	}
	QWidget::changeEvent(e);
}

#include <QCloseEvent>

void QtSoundModem::closeEvent(QCloseEvent *event)
{
	UNUSED(event);

	QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat);
	mysettings.setValue("geometry", QWidget::saveGeometry());
	mysettings.setValue("windowState", saveState());

	Closing = TRUE;
	qDebug() << "Closing";

	QThread::msleep(100);
}

	
QtSoundModem::~QtSoundModem()
{
	qDebug() << "Saving Settings";
		
	QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat);
	mysettings.setValue("geometry", saveGeometry());
	mysettings.setValue("windowState", saveState());
	
	saveSettings();	
	Closing = TRUE;
	qDebug() << "Closing";

	QThread::msleep(100);
}

extern "C" void QSleep(int ms)
{
	QThread::msleep(ms);
}

int upd_time = 30;

void QtSoundModem::show_grid()
{
	// This refeshes the session list

	int  snd_ch, i, num_rows, row_idx;
	QTableWidgetItem *item;
	const char * msg;

	int  speed_tx, speed_rx;

	if (grid_time < 10)
	{
		grid_time++;
		return;
	}

	grid_time = 0;

	//label7.Caption = inttostr(stat_r_mem); mem_arq

	num_rows = 0;
	row_idx = 0;

	for (snd_ch = 0; snd_ch < 4; snd_ch++)
	{
		for (i = 0; i < port_num; i++)
		{
			if (AX25Port[snd_ch][i].status != STAT_NO_LINK)
				num_rows++;
		}
	}

	if (num_rows == 0)
	{
		sessionTable->clearContents();
		sessionTable->setRowCount(0);
		sessionTable->setRowCount(1);
	}
	else
		sessionTable->setRowCount(num_rows);


	for (snd_ch = 0; snd_ch < 4; snd_ch++)
	{
		for (i = 0; i < port_num; i++)
		{
			if (AX25Port[snd_ch][i].status != STAT_NO_LINK)
			{
				switch (AX25Port[snd_ch][i].status)
				{
				case STAT_NO_LINK:

					msg = "No link";
					break;

				case STAT_LINK:

					msg = "Link";
					break;

				case STAT_CHK_LINK:

					msg = "Chk link";
					break;

				case STAT_WAIT_ANS:

					msg = "Wait ack";
					break;

				case STAT_TRY_LINK:

					msg = "Try link";
					break;

				case STAT_TRY_UNLINK:

					msg = "Try unlink";
				}


				item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].mycall);
				sessionTable->setItem(row_idx, 0, item);

				item = new QTableWidgetItem(AX25Port[snd_ch][i].kind);
				sessionTable->setItem(row_idx, 11, item);

				item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].corrcall);
				sessionTable->setItem(row_idx, 1, item);

				item = new QTableWidgetItem(msg);
				sessionTable->setItem(row_idx, 2, item);

				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_pkt));
				sessionTable->setItem(row_idx, 3, item);

				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_byte));
				sessionTable->setItem(row_idx, 4, item);

				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_pkt));
				sessionTable->setItem(row_idx, 5, item);

				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_byte));
				sessionTable->setItem(row_idx, 6, item);

				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_fc));
				sessionTable->setItem(row_idx, 7, item);

				item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_fec_count));
				sessionTable->setItem(row_idx, 8, item);

				if (grid_timer != upd_time)
					grid_timer++;
				else
				{
					grid_timer = 0;
					speed_tx = round(abs(AX25Port[snd_ch][i].info.stat_s_byte - AX25Port[snd_ch][i].info.stat_l_s_byte) / upd_time);
					speed_rx = round(abs(AX25Port[snd_ch][i].info.stat_r_byte - AX25Port[snd_ch][i].info.stat_l_r_byte) / upd_time);

					item = new QTableWidgetItem(QString::number(speed_tx));
					sessionTable->setItem(row_idx, 9, item);

					item = new QTableWidgetItem(QString::number(speed_rx));
					sessionTable->setItem(row_idx, 10, item);

					AX25Port[snd_ch][i].info.stat_l_r_byte = AX25Port[snd_ch][i].info.stat_r_byte;
					AX25Port[snd_ch][i].info.stat_l_s_byte = AX25Port[snd_ch][i].info.stat_s_byte;	
				}

				row_idx++;
			}
		}
	}
}

// "Copy on Select" Code

void QtSoundModem::onTEselectionChanged()
{
	QTextEdit * x = static_cast<QTextEdit*>(QObject::sender());
	x->copy();
}

#define ConstellationHeight 121
#define ConstellationWidth 121
#define PLOTRADIUS 60

#define MAX(x, y) ((x) > (y) ? (x) : (y))

extern "C" int SMUpdatePhaseConstellation(int chan, float * Phases, float * Mags, int intPSKPhase, int Count)
{
	// Subroutine to update bmpConstellation plot for PSK modes...
	// Skip plotting and calculations of intPSKPhase(0) as this is a reference phase (9/30/2014)

	float dblPhaseError;
	float dblPhaseErrorSum = 0;
	float intP = 0;
	float dblRad = 0;
	float dblAvgRad = 0;
	float dbPhaseStep;
	float MagMax = 0;
	float dblPlotRotation = 0;

	int i, intQuality;

	int intX, intY;
	int yCenter = (ConstellationHeight - 2) / 2;
	int xCenter = (ConstellationWidth - 2) / 2;

	if (Count == 0)
		return 0;

	if (nonGUIMode == 0)
	{
		Constellation[chan]->fill(black);

		for (i = 0; i < 120; i++)
		{
			Constellation[chan]->setPixel(xCenter, i, cyan);
			Constellation[chan]->setPixel(i, xCenter, cyan);
		}
	}
	dbPhaseStep = 2 * M_PI / intPSKPhase;

	for (i = 1; i < Count; i++)  // Don't plot the first phase (reference)
	{
		MagMax = MAX(MagMax, Mags[i]); // find the max magnitude to auto scale
		dblAvgRad += Mags[i];
	}

	dblAvgRad = dblAvgRad / Count; // the average radius

	for (i = 0; i < Count; i++)
	{
		dblRad = PLOTRADIUS * Mags[i] / MagMax; //  scale the radius dblRad based on intMag
		intP = round((Phases[i]) / dbPhaseStep);

		// compute the Phase error

		dblPhaseError = fabsf(Phases[i] - intP * dbPhaseStep); // always positive and < .5 *  dblPhaseStep
		dblPhaseErrorSum += dblPhaseError;

		if (nonGUIMode == 0)
		{
			intX = xCenter + dblRad * cosf(dblPlotRotation + Phases[i]);
			intY = yCenter + dblRad * sinf(dblPlotRotation + Phases[i]);

			if (intX > 0 && intY > 0)
				if (intX != xCenter && intY != yCenter)
					Constellation[chan]->setPixel(intX, intY, yellow);
		}
	}

	dblAvgRad = dblAvgRad / Count; // the average radius

	intQuality = MAX(0, ((100 - 200 * (dblPhaseErrorSum / (Count)) / dbPhaseStep))); // ignore radius error for (PSK) but include for QAM

	if (nonGUIMode == 0)
	{
		char QualText[64];
		sprintf(QualText, "Chan %c Qual = %d", chan + 'A', intQuality);
		QualLabel[chan]->setText(QualText);
		constellationLabel[chan]->setPixmap(QPixmap::fromImage(*Constellation[chan]));
	}
	return intQuality;
}



