More M17 MMDVM support

pull/3/head
Doug McLain 3 years ago
parent b6bff5c93a
commit 514c03655d

@ -965,7 +965,7 @@ void DMRCodec::process_rx_data()
emit update_output_level(m_audio->level()); emit update_output_level(m_audio->level());
} }
} }
else if ( ((m_modeinfo.stream_state == STREAM_END) || (m_modeinfo.stream_state == STREAM_LOST)) && (m_rxmodemq.size() > 50) ){ else if ( ((m_modeinfo.stream_state == STREAM_END) || (m_modeinfo.stream_state == STREAM_LOST)) && (m_rxmodemq.size() < 50) ){
m_rxtimer->stop(); m_rxtimer->stop();
m_audio->stop_playback(); m_audio->stop_playback();
m_rxwatchdog = 0; m_rxwatchdog = 0;

@ -26,7 +26,7 @@
#define M17CHARACTERS " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/." #define M17CHARACTERS " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/."
//#define DEBUG #define DEBUG
const uint8_t SCRAMBLER[] = { const uint8_t SCRAMBLER[] = {
0x00U, 0x00U, 0xD6U, 0xB5U, 0xE2U, 0x30U, 0x82U, 0xFFU, 0x84U, 0x62U, 0xBAU, 0x4EU, 0x96U, 0x90U, 0xD8U, 0x98U, 0xDDU, 0x00U, 0x00U, 0xD6U, 0xB5U, 0xE2U, 0x30U, 0x82U, 0xFFU, 0x84U, 0x62U, 0xBAU, 0x4EU, 0x96U, 0x90U, 0xD8U, 0x98U, 0xDDU,
@ -409,6 +409,7 @@ void M17Codec::send_modem_data(QByteArray d)
if(m_modeinfo.stream_state == STREAM_NEW){ if(m_modeinfo.stream_state == STREAM_NEW){
::memcpy(lsf, &d.data()[6], M17_LSF_LENGTH_BYTES); ::memcpy(lsf, &d.data()[6], M17_LSF_LENGTH_BYTES);
encodeCRC16(lsf, M17_LSF_LENGTH_BYTES);
::memcpy(txframe, M17_LINK_SETUP_SYNC_BYTES, 2); ::memcpy(txframe, M17_LINK_SETUP_SYNC_BYTES, 2);
conv.encodeLinkSetup(lsf, txframe + M17_SYNC_LENGTH_BYTES); conv.encodeLinkSetup(lsf, txframe + M17_SYNC_LENGTH_BYTES);
interleave(txframe, tmp); interleave(txframe, tmp);
@ -464,13 +465,28 @@ void M17Codec::process_modem_data(QByteArray d)
{ {
QByteArray txframe; QByteArray txframe;
static uint16_t txstreamid = 0; static uint16_t txstreamid = 0;
static uint8_t lsf[M17_LSF_LENGTH_BYTES]; static uint8_t lsf[M17_LSF_LENGTH_BYTES] = {0};
static uint8_t lsfchunks[M17_LSF_LENGTH_BYTES] = {0};
static bool validlsf = false;
CM17Convolution conv; CM17Convolution conv;
uint8_t tmp[M17_FRAME_LENGTH_BYTES]; uint8_t tmp[M17_FRAME_LENGTH_BYTES];
if(d.size() < 3){ if(d.size() < 3){
return; return;
} }
if( (d.data()[2] == MMDVM_M17_LINK_SETUP) &&
(((uint8_t)d.data()[4] != M17_LINK_SETUP_SYNC_BYTES[0]) || ((uint8_t)d.data()[5] != M17_LINK_SETUP_SYNC_BYTES[1]))){
qDebug() << "LSF with no sync bytes" << (d.data()[2] == MMDVM_M17_LINK_SETUP) << ((uint8_t)d.data()[4] != M17_LINK_SETUP_SYNC_BYTES[0]) << ((uint8_t)d.data()[5] != M17_LINK_SETUP_SYNC_BYTES[1]);
return;
}
if( (d.data()[2] == MMDVM_M17_STREAM) &&
(((uint8_t)d.data()[4] != M17_STREAM_SYNC_BYTES[0]) || ((uint8_t)d.data()[5] != M17_STREAM_SYNC_BYTES[1]))){
qDebug() << "stream frame with no sync bytes" << (d.data()[2] == MMDVM_M17_STREAM) << ((uint8_t)d.data()[4] != M17_STREAM_SYNC_BYTES[0]) << ((uint8_t)d.data()[5] != M17_STREAM_SYNC_BYTES[1]);
return;
}
uint8_t *p = (uint8_t *)d.data(); uint8_t *p = (uint8_t *)d.data();
if((d.data()[2] == MMDVM_M17_LINK_SETUP) || (d.data()[2] == MMDVM_M17_STREAM)){ if((d.data()[2] == MMDVM_M17_LINK_SETUP) || (d.data()[2] == MMDVM_M17_STREAM)){
@ -483,18 +499,23 @@ void M17Codec::process_modem_data(QByteArray d)
txstreamid = 0; txstreamid = 0;
if(m_modeinfo.host == "MMDVM_DIRECT"){ if(m_modeinfo.host == "MMDVM_DIRECT"){
m_modeinfo.streamid = 0; m_modeinfo.streamid = 0;
m_modeinfo.dst.clear();
m_modeinfo.src.clear();
m_modeinfo.stream_state = STREAM_END; m_modeinfo.stream_state = STREAM_END;
::memset(lsf, 0, M17_LSF_LENGTH_BYTES);
::memset(lsfchunks, 0, M17_LSF_LENGTH_BYTES);
validlsf = false;
} }
qDebug() << "End of M17 stream";
} }
else if(d.data()[2] == MMDVM_M17_LINK_SETUP){ else if(d.data()[2] == MMDVM_M17_LINK_SETUP){
::memset(lsf, 0x00U, M17_LSF_LENGTH_BYTES); ::memset(lsf, 0x00U, M17_LSF_LENGTH_BYTES);
conv.decodeLinkSetup(p + M17_SYNC_LENGTH_BYTES, lsf); uint32_t ber = conv.decodeLinkSetup(p + M17_SYNC_LENGTH_BYTES, lsf);
bool valid = checkCRC16(lsf, M17_LSF_LENGTH_BYTES); validlsf = checkCRC16(lsf, M17_LSF_LENGTH_BYTES);
txstreamid = static_cast<uint16_t>((::rand() & 0xFFFF)); txstreamid = static_cast<uint16_t>((::rand() & 0xFFFF));
qDebug() << "LSF valid == " << valid; qDebug() << "M17 LSF received valid == " << validlsf << "ber: " << ber;
if(m_modeinfo.host == "MMDVM_DIRECT"){ if(validlsf && (m_modeinfo.host == "MMDVM_DIRECT")){
uint8_t cs[10]; uint8_t cs[10];
::memcpy(cs, lsf, 6); ::memcpy(cs, lsf, 6);
decode_callsign(cs); decode_callsign(cs);
@ -506,49 +527,90 @@ void M17Codec::process_modem_data(QByteArray d)
} }
else if(d.data()[2] == MMDVM_M17_STREAM){ else if(d.data()[2] == MMDVM_M17_STREAM){
uint8_t frame[M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES]; uint8_t frame[M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES];
conv.decodeData(p + M17_SYNC_LENGTH_BYTES + M17_LICH_FRAGMENT_FEC_LENGTH_BYTES, frame); uint32_t ber = conv.decodeData(p + M17_SYNC_LENGTH_BYTES + M17_LICH_FRAGMENT_FEC_LENGTH_BYTES, frame);
//uint16_t fn = (frame[0U] << 8) + (frame[1U] << 0); uint16_t fn = (frame[0U] << 8) + (frame[1U] << 0);
uint8_t netframe[M17_LSF_LENGTH_BYTES + M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES]; uint8_t netframe[M17_LSF_LENGTH_BYTES + M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES];
::memcpy(netframe, lsf, M17_LSF_LENGTH_BYTES); ::memcpy(netframe, lsf, M17_LSF_LENGTH_BYTES);
::memcpy(netframe + M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES, frame, M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES); ::memcpy(netframe + M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES, frame, M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES);
netframe[M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + 0U] &= 0x7FU; netframe[M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + 0U] &= 0x7FU;
uint32_t lich1, lich2, lich3, lich4;
bool valid1 = CGolay24128::decode24128(p + M17_SYNC_LENGTH_BYTES + 0U, lich1);
bool valid2 = CGolay24128::decode24128(p + M17_SYNC_LENGTH_BYTES + 3U, lich2);
bool valid3 = CGolay24128::decode24128(p + M17_SYNC_LENGTH_BYTES + 6U, lich3);
bool valid4 = CGolay24128::decode24128(p + M17_SYNC_LENGTH_BYTES + 9U, lich4);
if (valid1 && valid2 && valid3 && valid4) {
uint8_t lich[M17_LICH_FRAGMENT_LENGTH_BYTES];
combineFragmentLICH(lich1, lich2, lich3, lich4, lich);
uint32_t n = (lich4 >> 5) & 0x07U;
::memcpy(lsfchunks + (n * M17_LSF_FRAGMENT_LENGTH_BYTES), lich, M17_LSF_FRAGMENT_LENGTH_BYTES);
bool valid = checkCRC16(lsfchunks, M17_LSF_LENGTH_BYTES);
qDebug() << "lich valid == " << valid << " lich n == " << n;
if (valid) {
::memcpy(lsf, lsfchunks, M17_LSF_LENGTH_BYTES);
::memset(lsfchunks, 0, M17_LSF_LENGTH_BYTES);
validlsf = valid;
}
if(!validlsf){
qDebug() << "No LSF yet...";
return;
}
}
if(m_modeinfo.host == "MMDVM_DIRECT"){ if(m_modeinfo.host == "MMDVM_DIRECT"){
if( !m_tx && (m_modeinfo.streamid == 0) ){ if( !m_tx && (m_modeinfo.streamid == 0) ){
if(txstreamid == 0){ if(txstreamid == 0){
qDebug() << "No header, late entry..."; qDebug() << "No header, late entry...";
uint8_t cs[10];
::memcpy(cs, lsf, 6);
decode_callsign(cs);
m_modeinfo.dst = QString((char *)cs);
::memcpy(cs, lsf+6, 6);
decode_callsign(cs);
m_modeinfo.src = QString((char *)cs);
txstreamid = static_cast<uint16_t>((::rand() & 0xFFFF)); txstreamid = static_cast<uint16_t>((::rand() & 0xFFFF));
} }
m_modeinfo.stream_state = STREAM_NEW;
m_modeinfo.streamid = txstreamid; m_modeinfo.streamid = txstreamid;
m_audio->start_playback(); m_modeinfo.ts = QDateTime::currentMSecsSinceEpoch();
qDebug() << "New RF stream from " << m_modeinfo.src << " to " << m_modeinfo.dst << " id == " << QString::number(m_modeinfo.streamid, 16) << "FN == " << fn << " ber == " << ber;
if((netframe[13] & 0x06U) == 0x04U){ m_audio->start_playback();
m_modeinfo.type = 1;//"3200 Voice";
set_mode(true);
}
else{
m_modeinfo.type = 0;//"1600 V/D";
set_mode(false);
}
if(!m_rxtimer->isActive()){ if(!m_rxtimer->isActive()){
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
m_rxtimer->start(m_modeinfo.type ? m_rxtimerint : 32); m_rxtimer->start(m_rxtimerint);
#else #else
m_rxtimer->start(m_modeinfo.type ? m_rxtimerint : m_rxtimerint*2); m_rxtimer->start(m_rxtimerint);
#endif #endif
} }
m_modeinfo.stream_state = STREAM_NEW;
m_modeinfo.ts = QDateTime::currentMSecsSinceEpoch();
qDebug() << "New RF stream from " << m_modeinfo.src << " to " << m_modeinfo.dst << " id == " << QString::number(m_modeinfo.streamid, 16);
} }
else{ else{
m_modeinfo.stream_state = STREAMING; m_modeinfo.stream_state = STREAMING;
} }
qDebug() << "RF streaming from " << m_modeinfo.src << " to " << m_modeinfo.dst << " id == " << QString::number(m_modeinfo.streamid, 16) << "FN == " << fn << " ber == " << ber << " type == " << netframe[13];
if((netframe[13] & 0x06U) == 0x04U){
m_modeinfo.type = 1;//"3200 Voice";
set_mode(true);
}
else{
m_modeinfo.type = 0;//"1600 V/D";
set_mode(false);
}
m_modeinfo.frame_number = (netframe[28] << 8) | (netframe[29] & 0xff); m_modeinfo.frame_number = (netframe[28] << 8) | (netframe[29] & 0xff);
m_rxwatchdog = 0; m_rxwatchdog = 0;
int s = 8; int s = 8;
if(get_mode()){ if(get_mode()){
s = 16; s = 16;
@ -557,9 +619,15 @@ void M17Codec::process_modem_data(QByteArray d)
for(int i = 0; i < s; ++i){ for(int i = 0; i < s; ++i){
m_rxcodecq.append(netframe[30+i]); m_rxcodecq.append(netframe[30+i]);
} }
emit update(m_modeinfo); emit update(m_modeinfo);
} }
else{ else{
if(txstreamid == 0){
qDebug() << "No header for netframe";
txstreamid = static_cast<uint16_t>((::rand() & 0xFFFF));
}
uint8_t dst[10]; uint8_t dst[10];
memset(dst, ' ', 9); memset(dst, ' ', 9);
memcpy(dst, m_hostname.toLocal8Bit(), m_hostname.size()); memcpy(dst, m_hostname.toLocal8Bit(), m_hostname.size());
@ -649,11 +717,13 @@ void M17Codec::transmit()
txframe.clear(); txframe.clear();
emit update_output_level(m_audio->level()); emit update_output_level(m_audio->level());
int r = get_mode() ? 0x05 : 0x07; int r = get_mode() ? 0x05 : 0x07;
if(m_tx){ if(m_tx){
if(txstreamid == 0){ if(txstreamid == 0){
txstreamid = static_cast<uint16_t>((::rand() & 0xFFFF)); txstreamid = static_cast<uint16_t>((::rand() & 0xFFFF));
//std::cerr << "txstreamid == " << txstreamid << std::endl; //std::cerr << "txstreamid == " << txstreamid << std::endl;
if(!m_rxtimer->isActive() && (m_modeinfo.host == "MMDVM_DIRECT")){ if(!m_rxtimer->isActive() && (m_modeinfo.host == "MMDVM_DIRECT")){
m_rxmodemq.clear();
m_modeinfo.stream_state = STREAM_NEW; m_modeinfo.stream_state = STREAM_NEW;
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
m_rxtimer->start(m_modeinfo.type ? m_rxtimerint : 32); m_rxtimer->start(m_modeinfo.type ? m_rxtimerint : 32);
@ -671,6 +741,7 @@ void M17Codec::transmit()
uint8_t src[10]; uint8_t src[10];
uint8_t dst[10]; uint8_t dst[10];
uint8_t lsf[30];
memset(dst, ' ', 9); memset(dst, ' ', 9);
memcpy(dst, m_hostname.toLocal8Bit(), m_hostname.size()); memcpy(dst, m_hostname.toLocal8Bit(), m_hostname.size());
dst[8] = m_module; dst[8] = m_module;
@ -691,13 +762,20 @@ void M17Codec::transmit()
txframe.append((char *)dst, 6); txframe.append((char *)dst, 6);
txframe.append((char *)src, 6); txframe.append((char *)src, 6);
txframe.append('\x00'); txframe.append('\x00');
txframe.append(r); // Frame type voice only txframe.append(r);
txframe.append(14, 0x00); //Blank nonce txframe.append(14, 0x00); //Blank nonce
txframe.append((char)(tx_cnt >> 8)); txframe.append((char)(tx_cnt >> 8));
txframe.append((char)tx_cnt & 0xff); txframe.append((char)tx_cnt & 0xff);
txframe.append((char *)c2, 16); txframe.append((char *)c2, 16);
txframe.append(2, 0x00); //txframe.append(2, 0x00);
for(int i = 0; i < 28; ++i){
lsf[i] = txframe.data()[6+i];
}
encodeCRC16(lsf, M17_LSF_LENGTH_BYTES);
txframe.append(lsf[28]);
txframe.append(lsf[29]);
//QString ss = QString("%1").arg(txstreamid, 4, 16, QChar('0')); //QString ss = QString("%1").arg(txstreamid, 4, 16, QChar('0'));
//QString n = QString("TX %1").arg(tx_cnt, 4, 16, QChar('0')); //QString n = QString("TX %1").arg(tx_cnt, 4, 16, QChar('0'));
@ -716,13 +794,14 @@ void M17Codec::transmit()
m_modeinfo.frame_number = tx_cnt; m_modeinfo.frame_number = tx_cnt;
m_modeinfo.streamid = txstreamid; m_modeinfo.streamid = txstreamid;
emit update(m_modeinfo); emit update(m_modeinfo);
#ifdef DEBUG
fprintf(stderr, "SEND:%d: ", txframe.size()); fprintf(stderr, "SEND:%d: ", txframe.size());
for(int i = 0; i < txframe.size(); ++i){ for(int i = 0; i < txframe.size(); ++i){
fprintf(stderr, "%02x ", (uint8_t)txframe.data()[i]); fprintf(stderr, "%02x ", (uint8_t)txframe.data()[i]);
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");
fflush(stderr); fflush(stderr);
#endif
} }
else{ else{
const uint8_t quiet3200[] = { 0x00, 0x01, 0x43, 0x09, 0xe4, 0x9c, 0x08, 0x21 }; const uint8_t quiet3200[] = { 0x00, 0x01, 0x43, 0x09, 0xe4, 0x9c, 0x08, 0x21 };
@ -782,12 +861,14 @@ void M17Codec::transmit()
m_modeinfo.frame_number = tx_cnt; m_modeinfo.frame_number = tx_cnt;
m_modeinfo.streamid = txstreamid; m_modeinfo.streamid = txstreamid;
emit update(m_modeinfo); emit update(m_modeinfo);
#ifdef DEBUG
fprintf(stderr, "LAST:%d: ", txframe.size()); fprintf(stderr, "LAST:%d: ", txframe.size());
for(int i = 0; i < txframe.size(); ++i){ for(int i = 0; i < txframe.size(); ++i){
fprintf(stderr, "%02x ", (uint8_t)txframe.data()[i]); fprintf(stderr, "%02x ", (uint8_t)txframe.data()[i]);
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");
fflush(stderr); fflush(stderr);
#endif
} }
} }
@ -827,12 +908,13 @@ void M17Codec::process_rx_data()
m_audio->write(pcm, s); m_audio->write(pcm, s);
emit update_output_level(m_audio->level()); emit update_output_level(m_audio->level());
} }
else if ( (m_modeinfo.stream_state == STREAM_END) || (m_modeinfo.stream_state == STREAM_LOST) ){ else if ( ((m_modeinfo.stream_state == STREAM_END) || (m_modeinfo.stream_state == STREAM_LOST)) && (m_rxmodemq.size() < 50) ){
m_rxtimer->stop(); m_rxtimer->stop();
m_audio->stop_playback(); m_audio->stop_playback();
m_rxwatchdog = 0; m_rxwatchdog = 0;
m_modeinfo.streamid = 0; m_modeinfo.streamid = 0;
m_rxcodecq.clear(); m_rxcodecq.clear();
m_rxmodemq.clear();
qDebug() << "M17 playback stopped"; qDebug() << "M17 playback stopped";
return; return;
} }

@ -218,7 +218,7 @@ void SerialModem::process_modem()
} }
} }
else if(m_serialdata.size() >= m_serialdata[1]){ else if(m_serialdata.size() >= s){
for(int i = 0; i < s; ++i){ for(int i = 0; i < s; ++i){
out.append(m_serialdata.dequeue()); out.append(m_serialdata.dequeue());
} }

Loading…
Cancel
Save