You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

789 lines
15 KiB
C++

#include "ckeystonecomm.h"
#ifdef QT_DEBUG
//#define OUTPUT_DEBUG
#endif
#ifdef Q_OS_WIN
#include <windows.h>
#endif
#include <QDebug>
#include <QThread>
#define HEAD 0xFE
#define END 0xFD
#define MSC_DATA_LENGTH (100 * 1024)
#define MAX_SEGMENT_SIZE 8191
#define MAX_IMAGE_SIZE 100 * 1024 // bytes, ETSI TS 101 499
#define MAX_NAME_LENGTH 100
#define EXPECTED_IMAGE_LENGTH 1000
// Set GPIO function
#define DATA_IN 0
#define DATA_OUT 1
#define MUTE 2
#define I2S_DATA_IN 3
#define I2S_LRCLK_IN 4
#define I2S_SSCLK_IN 5
#define I2S_BCLK_IN 6
#define I2S_DATA_OUT 7
#define I2S_LRCLK_OUT 8
#define I2S_SSCLK_OUT 9
#define I2S_BCLK_OUT 10
#define SPDIF_OUT 11
#define SPI_DO 12
#define SPI_CLK 13
#define SPI_CS 14
#define SPI_DI 15
// Driving strength(0~3)
#define DRIVE_2MA 0
#define DRIVE_4MA 1
#define DRIVE_6MA 2
#define DRIVE_8MA 3
#define ANTENNA_RELAY_PIN 11
cKeyStoneCOMM::cKeyStoneCOMM(const QString& commPort, QObject *parent) :
QObject{parent},
m_commPort{commPort},
m_serialPort{nullptr},
m_serialReadError{false},
m_hardMute{false},
m_initialized{false},
m_totalProgram{0},
m_mutePrevVolume{0}
{
}
cKeyStoneCOMM::~cKeyStoneCOMM()
{
close();
}
bool cKeyStoneCOMM::open(bool _hardMute)
{
close();
m_hardMute = _hardMute;
m_serialPort = new QSerialPort(m_commPort, this);
if(!m_serialPort)
return(false);
m_serialPort->setBaudRate(QSerialPort::Baud115200);
m_serialPort->setDataBits(QSerialPort::Data8);
m_serialPort->setStopBits(QSerialPort::OneStop);
m_serialPort->setParity(QSerialPort::NoParity);
m_serialPort->setFlowControl(QSerialPort::NoFlowControl);
if(!m_serialPort->open(QIODevice::ReadWrite))
{
qDebug() << "open failed: " << m_serialPort->errorString();
close();
return(false);
}
#ifdef OUTPUT_DEBUG
qDebug() << "open succeeded.";
#endif
m_serialPort->setBaudRate(QSerialPort::Baud115200);
m_serialPort->setDataBits(QSerialPort::Data8);
m_serialPort->setStopBits(QSerialPort::OneStop);
m_serialPort->setParity(QSerialPort::NoParity);
m_serialPort->setFlowControl(QSerialPort::NoFlowControl);
#ifdef OUTPUT_DEBUG
qDebug() << "*** Baud Rate: " << m_serialPort->baudRate();
qDebug() << "*** Data Bits: " << m_serialPort->dataBits();
qDebug() << "*** Stop Bits: " << m_serialPort->stopBits();
qDebug() << "*** Parity Bits: " << m_serialPort->parity();
qDebug() << "*** Flow Control: " << m_serialPort->flowControl();
#endif
connect(m_serialPort, &QSerialPort::readyRead, this, &cKeyStoneCOMM::serialReadReady);
connect(m_serialPort, &QSerialPort::errorOccurred, this, &cKeyStoneCOMM::serialReadError);
if(m_hardMute)
hardMute();
else
hardUnmute();
QThread::msleep(800);
if(!isSysReady())
{
for(int i = 0;i < 2;i++)
{
if(hardResetRadio())
{
for(int x = 0;x < 2;x++)
{
if(isSysReady())
{
// Enable Pin 11 for antenna switching
GPIO_SetFunction(ANTENNA_RELAY_PIN, DATA_OUT, DRIVE_8MA);
m_totalProgram = getTotalProgram();
m_initialized = true;
qDebug() << "Initialized. Current programs: " << m_totalProgram;
return(true);
}
}
}
}
return(false);
}
else
{
// Enable Pin 11 for antenna switching
GPIO_SetFunction(ANTENNA_RELAY_PIN, DATA_OUT, DRIVE_8MA);
m_totalProgram = getTotalProgram();
}
m_initialized = true;
#ifdef OUTPUT_DEBUG
qDebug() << "Initialized. Current programs: " << m_totalProgram;
#endif
return(true);
}
void cKeyStoneCOMM::close()
{
if(m_serialPort)
{
if(m_serialPort->isOpen())
{
stopStream();
m_serialPort->close();
}
delete m_serialPort;
m_serialPort = nullptr;
}
m_initialized = false;
}
int16_t cKeyStoneCOMM::channelCount()
{
return(m_totalProgram);
}
bool cKeyStoneCOMM::setVolume(int8_t volume)
{
uint16_t dwBytes;
unsigned char output[] = {HEAD,0x01,0x22,0x01,0x00,0x01,0x00,END};
unsigned char input[8] = {0};
if((volume < 0) || (volume > 16))
return(false); // Volume out of range
output[6] = volume;
if(volume == 0)
if(m_hardMute)
hardMute();
if(!writeSerialBytes(output, 8, &dwBytes))
return(false);
if(dwBytes != 8)
return(false);
// need to check the return
if(!readSerialBytes(input, &dwBytes))
return(false);
m_currentVolume = volume;
return(true);
}
bool cKeyStoneCOMM::startStream(STREAM_MODE mode, uint16_t channel)
{
uint16_t dwBytes;
unsigned char input[8] = {0};
unsigned char output[] = {HEAD,0x01,0x00,0x01,0x00,0x05,0x00,0x00,0x00,0x00,0x00,END};
unsigned char strChannel[4];
if(mode == 0)
if(m_hardMute)
hardMute();
if((mode < 0) || (mode > 1))
return(false);
output[6] = mode;
if(mode == 0)
{
//DAB GPIO 11 set to LOW for antenna switching
GPIO_SetLevel(ANTENNA_RELAY_PIN, 0);
output[10] = (unsigned char)channel; // WARNING - This is not very safe, TODO
}
else
{
//FM GPIO 11 set to HIGH for antenna switching
GPIO_SetLevel(ANTENNA_RELAY_PIN, 1);
if(channel < 20000)
{
memcpy(&strChannel[0], &channel, sizeof(long));
output[7] = strChannel[3];
output[8] = strChannel[2];
output[9] = strChannel[1];
output[10] = strChannel[0];
}
else
return(false);
}
if(!writeSerialBytes(output, 12, &dwBytes))
return(false);
if(dwBytes != 12)
return(false);
// need to check the return
if(!readSerialBytes(input, &dwBytes))
return(false);
return(true);
}
bool cKeyStoneCOMM::stopStream()
{
uint16_t dwBytes;
unsigned char output[] = {HEAD,0x01,0x02,0x01,0x00,0x00,END};
unsigned char input[8] = {0};
if(m_hardMute)
hardMute();
if(!writeSerialBytes(output, 7, &dwBytes))
return(false);
if(dwBytes != 7)
return(false);
if(!readSerialBytes(input, &dwBytes))
return(false);
if(goodHeader(input, dwBytes))
{
if(input[2] == 0x01)
return(true);
else
return(false);
}
else
return(false);
}
bool cKeyStoneCOMM::nextStream()
{
long channel = 0;
char currentMode = playMode();
if(currentMode == STREAM_MODE_DAB)
{
channel = playIndex();
if(channel < m_totalProgram-1)
channel++;
else
channel = 0;
startStream(STREAM_MODE_DAB, channel);
return(true);
}
if(currentMode == STREAM_MODE_FM)
{
//HERWIG FMSearch(1);
return(true);
}
return(false);
}
bool cKeyStoneCOMM::prevStream()
{
long channel = 0;
char currentMode = playMode();
if(currentMode == STREAM_MODE_DAB)
{
channel = playIndex();
if(channel > 0)
channel--;
else
channel = m_totalProgram-1;
startStream(STREAM_MODE_DAB, channel);
return(true);
}
if(currentMode == STREAM_MODE_FM)
{
//HERWIG FMSearch(0);
return(true);
}
return(false);
}
cKeyStoneCOMM::STREAM_MODE cKeyStoneCOMM::playMode()
{
uint16_t dwBytes;
unsigned char input[8] = {0};
unsigned char output[] = {HEAD,0x01,0x11,0x01,0x00,0x00,END};
if(!writeSerialBytes(output, 7, &dwBytes))
return(STREAM_MODE_NONE);
if(dwBytes != 7)
return(STREAM_MODE_NONE);
if(!readSerialBytes(input, &dwBytes))
return(STREAM_MODE_NONE);
if(!goodHeader(input, dwBytes))
return(STREAM_MODE_NONE);
else
{
if((input[1] == 0x01) && (input[2] == 0x11))
return((STREAM_MODE)input[6]);
else
return(STREAM_MODE_NONE);
}
}
int16_t cKeyStoneCOMM::playIndex()
{
uint16_t dwBytes;
unsigned char output[] = {HEAD,0x01,0x12,0x01,0x00,0x00,END};
unsigned char input[11] = {0};
if(!writeSerialBytes(output, 7, &dwBytes))
return(-1);
if(!readSerialBytes(input, &dwBytes))
return(-1);
else
{
if(goodHeader(input, dwBytes))
{
long channel = (0xFF & input[6]) << 24 | (0xFF & input[7]) << 16 | (0xFF & input[8]) << 8 | (0xFF & input[9]);
return(channel);
}
else
return(-1);
}
}
int8_t cKeyStoneCOMM::volumePlus()
{
char currentVolume = volume();
if(currentVolume < 16)
currentVolume++;
if(setVolume(currentVolume))
return(currentVolume);
else
return(-1);
}
int8_t cKeyStoneCOMM::volumeMinus()
{
char currentVolume = volume();
if(currentVolume > 0)
currentVolume--;
if(setVolume(currentVolume))
return(currentVolume);
else
return(-1);
}
void cKeyStoneCOMM::volumeMute()
{
char currentVolume = volume();
if(currentVolume == 0)
setVolume(m_mutePrevVolume);
else
{
m_mutePrevVolume = currentVolume;
setVolume(0);
}
}
int8_t cKeyStoneCOMM::volume()
{
uint16_t dwBytes;
unsigned char output[] = {HEAD,0x01,0x23,0x01,0x00,0x00,END};
unsigned char input[8] = {0};
if(!writeSerialBytes(output, 7, &dwBytes))
return(-1);
if(dwBytes != 7)
return(-1);
if(!readSerialBytes(input, &dwBytes))
return(-1);
else
{
if(goodHeader(input, dwBytes))
{
m_currentVolume = input[6];
return(input[6]);
}
else
return(-1);
}
}
int8_t cKeyStoneCOMM::signalStrength()
{
int biterror;
return(getSignalStrength(&biterror));
}
bool cKeyStoneCOMM::hardMute()
{
m_hardMute = true;
#ifdef Q_OS_WIN
if(!::EscapeCommFunction(m_serialPort->handle(), CLRRTS))
return(false);
if(!::EscapeCommFunction(m_serialPort->handle(), CLRDTR))
return(false);
#elif defined Q_OS_LINUX
int status;
ioctl(m_serialPort->handle, TIOCMGET, &status);
status &= ~TIOCM_RTS;
ioctl(m_serialPort->handle, TIOCMSET, &status);
#endif
QThread::msleep(350);
return(true);
}
bool cKeyStoneCOMM::hardUnmute()
{
QThread::msleep(850);
#ifdef Q_OS_WIN
if(!::EscapeCommFunction(m_serialPort->handle(), SETRTS))
return(false);
if(!::EscapeCommFunction(m_serialPort->handle(), CLRDTR))
return(false);
#elif defined Q_OS_LINUX
int status;
ioctl(m_serialPort->handle(), TIOCMGET, &status);
status |= TIOCM_RTS;
ioctl(m_serialPort->handle(), TIOCMSET, &status);
#endif
m_hardMute = false;
return(true);
}
bool cKeyStoneCOMM::hardResetRadio()
{
#ifdef Q_OS_WIN
if(EscapeCommFunction(m_serialPort->handle(), SETRTS))
{
EscapeCommFunction(m_serialPort->handle(), SETDTR);
Sleep(100);
if (EscapeCommFunction(m_serialPort->handle(), SETRTS))
{
EscapeCommFunction(m_serialPort->handle(), CLRDTR);
QThread::msleep(1000);
return(true);
}
else
return(false);
}
else
return(false);
#elif defined Q_OS_LINUX
int status;
ioctl(m_serialPort->handle(), TIOCMGET, &status);
status |= TIOCM_DTR; // set DTR pin low to reset module
ioctl(m_serialPort->handle(), TIOCMSET, &status);
usleep(100000);
status &= ~TIOCM_DTR; // set DTR pin HIGH to disable RESET
ioctl(m_serialPort->handle(), TIOCMSET, &status);
usleep(1000000); // delay 1 sec to wait for module to ready
#endif
}
bool cKeyStoneCOMM::isSysReady()
{
uint16_t dwBytes;
unsigned char input[8] = {0};
unsigned char output[] = {HEAD,0x00,0x00,0x01,0x00,0x00,END};
if(m_serialPort && m_serialPort->isOpen())
{
if(!writeSerialBytes(output, 7, &dwBytes))
return(false);
if(!readSerialBytes(input, &dwBytes))
return(false);
if(dwBytes < 7)
return(false);
if(goodHeader(input, dwBytes))
{
if(input[2] == 0x01)
return(true);
else
return(false);
}
else
return(false);
}
else
return(false);
}
bool cKeyStoneCOMM::readSerialBytes(unsigned char* buffer, uint16_t* bytesreadreturn)
{
m_readData.clear();
m_cntReadData = 0;
m_serialReadError = false;
while(m_serialPort->waitForReadyRead(100));
if(m_serialReadError)
{
*bytesreadreturn = 0;
qDebug() << "error";
return(false);
}
*bytesreadreturn = m_readData.count();
std::memcpy(buffer, m_readData.data(), *bytesreadreturn);
return(true);
}
bool cKeyStoneCOMM::writeSerialBytes(unsigned char* buffer, uint16_t nNumberOfBytesToWrite, uint16_t* lpNumberOfBytesWritten)
{
int i;
i = m_serialPort->write(reinterpret_cast<char*>(buffer), nNumberOfBytesToWrite);
if(i != nNumberOfBytesToWrite)
return(false);
*lpNumberOfBytesWritten = i;
return(true);
}
bool cKeyStoneCOMM::goodHeader(unsigned char* input, uint16_t dwBytes)
{
if((input[0] == HEAD) && (input[dwBytes-1] == END))
return(true);
else
return(false);
}
bool cKeyStoneCOMM::GPIO_SetLevel(char pin, char level)
{
uint16_t dwBytes;
unsigned char input[7] = { 0 };
unsigned char output[] = { HEAD,0x08,0x01,0x00,0x00,0x02,0x00,0x00,END };
output[6] = pin;
output[7] = level;
if (!writeSerialBytes(output, 9, &dwBytes))
return(false);
if(dwBytes != 9)
return(false);
if(!readSerialBytes(input, &dwBytes))
return(false);
if(!goodHeader(input, dwBytes))
return(false);
else
{
if((input[1] == 0x00) && (input[2] == 0x01))
return(true);
else
return(false);
}
}
bool cKeyStoneCOMM::GPIO_GetLevel(char pin, char* level)
{
uint16_t dwBytes;
unsigned char input[8] = { 0 };
unsigned char output[] = { HEAD,0x08,0x02,0x00,0x00,0x01,0x00,END };
output[6] = pin;
if(!writeSerialBytes(output, 9, &dwBytes))
return(false);
if(dwBytes != 9)
return(false);
if(!readSerialBytes(input, &dwBytes))
return(false);
if(!goodHeader(input, dwBytes))
return(false);
else
{
if((input[1] == 0x08) && (input[2] == 0x02))
{
*level = input[6];
return(true);
}
else
return(false);
}
}
bool cKeyStoneCOMM::GPIO_SetFunction(char pin, char pinfunction, char drive)
{
uint16_t dwBytes;
unsigned char input[7] = { 0 };
unsigned char output[] = { HEAD,0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x00,END };
output[6] = pin;
output[7] = pinfunction;
output[8] = drive;
if(!writeSerialBytes(output, 10, &dwBytes))
return(false);
if(dwBytes != 10)
return(false);
if(!readSerialBytes(input, &dwBytes))
return(false);
if(!goodHeader(input, dwBytes))
return(false);
else
{
if((input[1] == 0x00) && (input[2] == 0x01))
{
return(true);
}
else
return(false);
}
}
int16_t cKeyStoneCOMM::getTotalProgram()
{
uint16_t dwBytes;
unsigned char output[] = {HEAD,0x01,0x13,0x01,0x00,0x00,END};
unsigned char input[11] = {0};
if(!writeSerialBytes(output, 7, &dwBytes))
return(0);
if(!readSerialBytes(input, &dwBytes))
return(0);
else
{
if(goodHeader(input, dwBytes))
{
if(input[1] == 0x01)
{
uint16_t total = (0xFF & input[6]) << 24 | (0xFF & input[7]) << 16 | (0xFF & input[8]) << 8 |(0xFF & input[9]);
m_totalProgram = total;
return(total);
}
else
return(0);
}
else
return(0);
}
}
int8_t cKeyStoneCOMM::getSignalStrength(int* biterror)
{
uint16_t dwBytes;
char dabStrength = 0;
unsigned char input[10] = {0};
unsigned char output[] = {HEAD,0x01,0x15,0x01,0x00,0x00,END};
double temp;
if(!writeSerialBytes(output, 7, &dwBytes))
return(-1);
if(dwBytes != 7)
return(-1);
if(!readSerialBytes(input, &dwBytes))
return(-1);
if(dwBytes < 8)
return(-1);
if(goodHeader(input, dwBytes))
{
if(input[5] == 0x01)
{
*biterror = 0;
return(input[6]); //FM Strength
}
if(input[5] == 0x03)
{
if(input[6] > 0)
{
// calculate percentage and return bit error
*biterror = (0xFF & input[7]) << 8 |(0xFF & input[8]);
temp = 100.00/18.00;
temp = temp * input[6];
dabStrength = (char)temp;
return(dabStrength);
}
else
return(0);
}
return(-1);
}
else
return(-1);
}
void cKeyStoneCOMM::serialReadReady()
{
m_readData.append(m_serialPort->readAll());
}
//void cKeyStoneCOMM::handleTimeout()
//{
//}
void cKeyStoneCOMM::serialReadError(QSerialPort::SerialPortError error)
{
if(error == QSerialPort::ReadError)
{
m_serialReadError = true;
m_serialPortError = error;
#ifdef OUTPUT_DEBUG
qDebug() << error;
#endif
}
}