/* Copyright (C) 2019-2021 Doug McLain This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #ifndef Q_OS_ANDROID #include #endif #include #include "serialambe.h" #define ENDLINE "\n" //#define DEBUG #define AMBE3000_START_BYTE 0x61 #define AMBE3000_TYPE_CONFIG 0x00 #define AMBE3000_TYPE_CHANNEL 0x01 #define AMBE3000_TYPE_SPEECH 0x02 #define AMBE3000_PKT_RATEP 0x0a #define AMBE3000_PKT_INIT 0x0b #define AMBE3000_PKT_RATEP 0x0a #define AMBE3000_PKT_PRODID 0x30 #define AMBE3000_PKT_VERSTRING 0x31 #define AMBE3000_PKT_READY 0x39 #define AMBE3000_PKT_RESET 0x33 #define AMBE3000_PKT_PARITYMODE 0x3f const uint8_t AMBEP251_4400_2800[17] = {AMBE3000_START_BYTE, 0x00, 0x0d, AMBE3000_TYPE_CONFIG, AMBE3000_PKT_RATEP, 0x05U, 0x58U, 0x08U, 0x6BU, 0x10U, 0x30U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x90U}; //DVSI P25 USB Dongle FEC const uint8_t AMBE2000_2400_1200[17] = {AMBE3000_START_BYTE, 0x00, 0x0d, AMBE3000_TYPE_CONFIG, AMBE3000_PKT_RATEP, 0x01U, 0x30U, 0x07U, 0x63U, 0x40U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x48U}; const uint8_t AMBE3000_2450_1150[17] = {AMBE3000_START_BYTE, 0x00, 0x0d, AMBE3000_TYPE_CONFIG, AMBE3000_PKT_RATEP, 0x04U, 0x31U, 0x07U, 0x54U, 0x24U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x6fU, 0x48U}; const uint8_t AMBE3000_2450_0000[17] = {AMBE3000_START_BYTE, 0x00, 0x0d, AMBE3000_TYPE_CONFIG, AMBE3000_PKT_RATEP, 0x04U, 0x31U, 0x07U, 0x54U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x70U, 0x31U}; const uint8_t AMBE3000_PARITY_DISABLE[8] = {AMBE3000_START_BYTE, 0x00, 0x04, AMBE3000_TYPE_CONFIG, AMBE3000_PKT_PARITYMODE, 0x00, 0x2f, 0x14}; const uint8_t AMBE3000_PRODID[5] = {AMBE3000_START_BYTE, 0x00, 0x01, AMBE3000_TYPE_CONFIG, AMBE3000_PKT_PRODID}; const uint8_t AMBE3000_VERSION[5] = {AMBE3000_START_BYTE, 0x00, 0x01, AMBE3000_TYPE_CONFIG, AMBE3000_PKT_VERSTRING}; //const uint8_t AMBE2020[48] = {0x13, 0xec, 0x00, 0x00, 0x10, 0x30, 0x00, 0x01, 0x00, 0x00, 0x42, 0x30, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; //const uint8_t AMBE2020[4] = {0x04, 0x20, 0x01, 0x00}; const uint8_t AMBE2020[5] = {0x05, 0x00, 0x18, 0x00, 0x01}; SerialAMBE::SerialAMBE(QString protocol) : m_protocol(protocol), m_decode_gain(1.0) { } SerialAMBE::~SerialAMBE() { m_serial->close(); } QMap SerialAMBE::discover_devices() { QMap devlist; const QString blankString = "N/A"; QString out; #ifndef Q_OS_ANDROID const auto serialPortInfos = QSerialPortInfo::availablePorts(); if(serialPortInfos.count()){ for(const QSerialPortInfo &serialPortInfo : serialPortInfos) { out = "Port: " + serialPortInfo.portName() + ENDLINE + "Location: " + serialPortInfo.systemLocation() + ENDLINE + "Description: " + (!serialPortInfo.description().isEmpty() ? serialPortInfo.description() : blankString) + ENDLINE + "Manufacturer: " + (!serialPortInfo.manufacturer().isEmpty() ? serialPortInfo.manufacturer() : blankString) + ENDLINE + "Serial number: " + (!serialPortInfo.serialNumber().isEmpty() ? serialPortInfo.serialNumber() : blankString) + ENDLINE + "Vendor Identifier: " + (serialPortInfo.hasVendorIdentifier() ? QByteArray::number(serialPortInfo.vendorIdentifier(), 16) : blankString) + ENDLINE + "Product Identifier: " + (serialPortInfo.hasProductIdentifier() ? QByteArray::number(serialPortInfo.productIdentifier(), 16) : blankString) + ENDLINE; //fprintf(stderr, "%s", out.toStdString().c_str());fflush(stderr); devlist[serialPortInfo.systemLocation()] = serialPortInfo.description() + ":" + serialPortInfo.systemLocation(); } } #else QStringList list = AndroidSerialPort::GetInstance().discover_devices(); for ( const auto& i : list ){ devlist[i] = i; } #endif return devlist; } void SerialAMBE::connect_to_serial(QString p) { const QString blankString = "N/A"; int br = 460800; if((m_protocol != "P25") && (m_protocol != "M17") && (p != "")){ #ifndef Q_OS_ANDROID m_serial = new QSerialPort; QSerialPortInfo info(*m_serial); QString out = "Port: " + info.portName() + ENDLINE + "Location: " + info.systemLocation() + ENDLINE + "Description: " + (!info.description().isEmpty() ? info.description() : blankString) + ENDLINE + "Manufacturer: " + (!info.manufacturer().isEmpty() ? info.manufacturer() : blankString) + ENDLINE + "Serial number: " + (!info.serialNumber().isEmpty() ? info.serialNumber() : blankString) + ENDLINE + "Vendor Identifier: " + (info.hasVendorIdentifier() ? QByteArray::number(info.vendorIdentifier(), 16) : blankString) + ENDLINE + "Product Identifier: " + (info.hasProductIdentifier() ? QByteArray::number(info.productIdentifier(), 16) : blankString) + ENDLINE; fprintf(stderr, "%s", out.toStdString().c_str());fflush(stderr); m_description = info.description(); if(m_description == "DV Dongle"){ br = 230400; } #else m_serial = &AndroidSerialPort::GetInstance(); connect(m_serial, SIGNAL(device_ready()), this, SLOT(config_ambe())); //connect(m_serial, SIGNAL(device_denied()), this, SLOT(config_ambe())); #endif m_serial->setPortName(p); m_serial->setBaudRate(br); m_serial->setDataBits(QSerialPort::Data8); m_serial->setStopBits(QSerialPort::OneStop); m_serial->setParity(QSerialPort::NoParity); if (m_serial->open(QIODevice::ReadWrite)) { #ifndef Q_OS_ANDROID connect(m_serial, &QSerialPort::readyRead, this, &SerialAMBE::process_serial); config_ambe(); #else connect(m_serial, SIGNAL(data_received(QByteArray)), this, SLOT(receive_serial(QByteArray))); #endif } else{ qDebug() << "Error: Failed to open device."; } } } void SerialAMBE::config_ambe() { QByteArray a; a.clear(); if(m_description != "DV Dongle"){ m_serial->setFlowControl(QSerialPort::HardwareControl); m_serial->setRequestToSend(true); a.append(reinterpret_cast(AMBE3000_PARITY_DISABLE), sizeof(AMBE3000_PARITY_DISABLE)); m_serial->write(a); QThread::msleep(100); a.clear(); a.append(reinterpret_cast(AMBE3000_PRODID), sizeof(AMBE3000_PRODID)); m_serial->write(a); QThread::msleep(100); a.clear(); a.append(reinterpret_cast(AMBE3000_VERSION), sizeof(AMBE3000_VERSION)); m_serial->write(a); QThread::msleep(100); a.clear(); } if(m_protocol == "DMR"){ a.append(reinterpret_cast(AMBE3000_2450_1150), sizeof(AMBE3000_2450_1150)); packet_size = 9; } else if( (m_protocol == "YSF") || (m_protocol == "NXDN") ){ a.append(reinterpret_cast(AMBE3000_2450_0000), sizeof(AMBE3000_2450_0000)); packet_size = 7; } else if(m_protocol == "P25"){ a.append(reinterpret_cast(AMBEP251_4400_2800), sizeof(AMBEP251_4400_2800)); } else if(m_description != "DV Dongle"){ //D-Star with AMBE3000 a.append(reinterpret_cast(AMBE2000_2400_1200), sizeof(AMBE2000_2400_1200)); packet_size = 9; } else{ a.append(reinterpret_cast(AMBE2020), sizeof(AMBE2020)); packet_size = 9; } m_serial->write(a); #ifdef DEBUG fprintf(stderr, "SENDHW %d:%d:", a.size(), m_serialdata.size()); for(int i = 0; i < a.size(); ++i){ //if((d.data()[i] == 0x61) && (data.data()[i+1] == 0x01) && (data.data()[i+2] == 0x42) && (data.data()[i+3] == 0x02)){ // i+= 6; //} fprintf(stderr, "%02x ", (unsigned char)a.data()[i]); } fprintf(stderr, "\n"); fflush(stderr); #endif emit ambedev_ready(); } void SerialAMBE::receive_serial(QByteArray d) { for(int i = 0; i < d.size(); i++){ m_serialdata.append(d[i]); } if(m_description == "DV Dongle"){ process_serial_2020(); } else{ process_serial_3000(); } } void SerialAMBE::process_serial() { QByteArray d = m_serial->readAll(); for(int i = 0; i < d.size(); i++){ m_serialdata.append(d[i]); } #ifdef DEBUG fprintf(stderr, "AMBEHW %d:%d:", d.size(), m_serialdata.size()); for(int i = 0; i < d.size(); ++i){ //if((d.data()[i] == 0x61) && (data.data()[i+1] == 0x01) && (data.data()[i+2] == 0x42) && (data.data()[i+3] == 0x02)){ // i+= 6; //} fprintf(stderr, "%02x ", (unsigned char)d.data()[i]); } fprintf(stderr, "\n"); fflush(stderr); #endif if(m_description == "DV Dongle"){ process_serial_2020(); } else{ process_serial_3000(); } } void SerialAMBE::decode(uint8_t *ambe) { if(m_description == "DV Dongle"){ decode_2020(ambe); } else{ decode_3000(ambe); } } void SerialAMBE::encode(int16_t *audio) { uint8_t packet[327] = {AMBE3000_START_BYTE, 0x01, 0x43, AMBE3000_TYPE_SPEECH, 0x40, 0x00, 0xa0}; for(int i = 0; i < 160; ++i){ packet [(i*2)+7] = (audio[i] >> 8) & 0xff; packet [(i*2)+8] = audio[i] & 0xff; } m_serial->write((char *)packet, 327); #ifdef DEBUG fprintf(stderr, "SENDHW: "); for(int i = 0; i < 326; ++i){ //if((d.data()[i] == 0x61) && (data.data()[i+1] == 0x01) && (data.data()[i+2] == 0x42) && (data.data()[i+3] == 0x02)){ // i+= 6; //} fprintf(stderr, "%02x ", packet[i]); } fprintf(stderr, "\n"); fflush(stderr); #endif } void SerialAMBE::decode_2020(uint8_t *ambe) { uint8_t packet[50] = {0x32, 0xa0, 0xec, 0x13, 0x00, 0x00, 0x30, 0x10, 0x01, 0x00, 0x00, 0x00, 0x30, 0x42, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t pcm[322]; memset(pcm, 0, 322); pcm[0] = 0x42; pcm[1] = 0x81; memcpy(packet+24, ambe, packet_size); m_serial->write((char *)packet, 50); m_serial->write((char *)pcm, 322); } void SerialAMBE::decode_3000(uint8_t *ambe) { uint8_t packet[15] = {AMBE3000_START_BYTE, 0x00, 0x0b, AMBE3000_TYPE_CHANNEL, 0x01, 0x48}; if( packet_size == 7 ){ packet[2] = 0x09; packet[5] = 0x31; } memcpy(packet+6, ambe, packet_size); m_serial->write((char *)packet, (6 + packet_size)); } void SerialAMBE::process_serial_2020() { if( (m_serialdata.size() > 321) && ((uint8_t)m_serialdata[0] == 0x42) && ((uint8_t)m_serialdata[1] == 0x81) ) { emit data_ready(); } if( (m_serialdata.size() > 49) && ((uint8_t)m_serialdata[0] == 0x32) && ((uint8_t)m_serialdata[1] == 0xa0) && ((uint8_t)m_serialdata[0] == 0xec) && ((uint8_t)m_serialdata[1] == 0x13) ) { emit data_ready(); } } void SerialAMBE::process_serial_3000() { if(m_serialdata.size() < 3){ return; } while( (m_serialdata.size() > 3) && (m_serialdata[0] == AMBE3000_START_BYTE) && (m_serialdata[3] == 0x00) && (m_serialdata.size() >= m_serialdata[2]) ) { switch(m_serialdata[4]){ case AMBE3000_PKT_PARITYMODE: if(!m_serialdata[5]){ qDebug() << "AMBE3000 Parity disabled"; } else{ qDebug() << "ERROR: AMBE3000 Parity not disabled"; } break; case AMBE3000_PKT_PRODID: m_ambeprodid.clear(); for(int i = 0; i < (m_serialdata[2] - 2); ++i){ m_ambeprodid.append(m_serialdata[5+i]); } qDebug() << "PRODID == " << m_ambeprodid; break; case AMBE3000_PKT_VERSTRING: m_ambeverstring.clear(); for(int i = 0; i < (m_serialdata[2] - 2); ++i){ m_ambeverstring.append(m_serialdata[5+i]); } qDebug() << "VERSTRING == " << m_ambeverstring; break; case AMBE3000_PKT_RATEP: if(!m_serialdata[5]){ qDebug() << "AMBE3000 Rate set"; emit connected(true); } else{ qDebug() << "ERROR: AMBE3000 Rate not set"; emit connected(false); } break; default: break; } do { m_serialdata.dequeue(); } while(m_serialdata.size() && m_serialdata[0] != AMBE3000_START_BYTE); } if( (m_serialdata.size() >= (6 + packet_size)) && (m_serialdata[0] == AMBE3000_START_BYTE) && (m_serialdata[3] == AMBE3000_TYPE_CHANNEL) ) { emit data_ready(); } } bool SerialAMBE::get_ambe(uint8_t *ambe) { bool r = false; if(m_serialdata.isEmpty()){ return r; } if( (m_serialdata.size() > 3) && (m_serialdata[0] == AMBE3000_START_BYTE) && (m_serialdata[3] != AMBE3000_TYPE_CHANNEL) ) { do { m_serialdata.dequeue(); } while(m_serialdata.size() && m_serialdata[0] != AMBE3000_START_BYTE); } if( (m_serialdata.size() >= (6 + packet_size)) && (m_serialdata[0] == AMBE3000_START_BYTE) && (m_serialdata[3] == AMBE3000_TYPE_CHANNEL) ) { for(int i = 0; i < 6; ++i){ m_serialdata.dequeue(); } for(int i = 0; i < packet_size; i++){ ambe[i] = m_serialdata.dequeue(); } r = true; } return r; } bool SerialAMBE::get_audio(int16_t *audio) { bool r = false; if(m_serialdata.isEmpty()){ return r; } uint8_t header[] = {AMBE3000_START_BYTE, 0x01, 0x42, AMBE3000_TYPE_SPEECH, 0x00, 0xA0}; /* if( (m_serialdata.size() > 3) && (m_serialdata[0] == 0x61) && (m_serialdata[3] != 0x02) ) { do { m_serialdata.dequeue(); } while(m_serialdata.size() && m_serialdata[0] != 0x61); } //qDebug() << "get_audio() m_serialdata.size() == " << m_serialdata.size(); fprintf(stderr, "AMBEHW %d:%d:", m_serialdata.size(), m_serialdata.size()); for(int i = 0; i < m_serialdata.size(); ++i){ //if((d.data()[i] == 0x61) && (data.data()[i+1] == 0x01) && (data.data()[i+2] == 0x42) && (data.data()[i+3] == 0x02)){ // i+= 6; //} fprintf(stderr, "%02x ", (unsigned char)m_serialdata[i]); } fprintf(stderr, "\n"); fflush(stderr); */ if(m_serialdata.size() >= 326){ if ( ((uint8_t)m_serialdata[0] == header[0]) && ((uint8_t)m_serialdata[1] == header[1]) && ((uint8_t)m_serialdata[2] == header[2]) && ((uint8_t)m_serialdata[3] == header[3]) && ((uint8_t)m_serialdata[4] == header[4]) && ((uint8_t)m_serialdata[5] == header[5])){ m_serialdata.dequeue(); m_serialdata.dequeue(); m_serialdata.dequeue(); m_serialdata.dequeue(); m_serialdata.dequeue(); m_serialdata.dequeue(); for(int i = 0; i < 160; i++){ //Byte swap BE to LE audio[i] = ((m_serialdata.dequeue() << 8) & 0xff00) | (m_serialdata.dequeue() & 0xff); audio[i] = (qreal)audio[i] * m_decode_gain; } r = true; } else{ while( (m_serialdata.size() > 5) && ( ((uint8_t)m_serialdata[0] != header[0]) || ((uint8_t)m_serialdata[1] != header[1]) || ((uint8_t)m_serialdata[2] != header[2]) || ((uint8_t)m_serialdata[3] != header[3]) || ((uint8_t)m_serialdata[4] != header[4]) || ((uint8_t)m_serialdata[5] != header[5]))){ m_serialdata.dequeue(); } } } return r; } void SerialAMBE::clear_queue() { m_serialdata.clear(); }