Detect DV Fast Data on a per-frame basis

This commit adds a maybeFixupVoiceFrame() function that is used by
both the RF and Net code to manage FEC regeneration and DTMF blanking
in voice frames.

The presence of Fast Data is discovered by reading the mini-header in
every second data frame.  If found, FEC regeneration and DTMF blanking
are disabled for the current and next voice frames.

An exception is voice frames that have a sync frame instead of a data
frame.  This commit always disables FEC regeneration and DTMF blanking
for these frames.  A later commit will add support for these frames by
setting aside the voice frame until the next data frame can be read.

This commit also includes a number of debugging statements that will
be removed in a later commit.
This commit is contained in:
Tim Stewart 2020-11-28 15:53:54 -05:00
parent 8656aaedaa
commit 8874d1262b
5 changed files with 119 additions and 29 deletions

View file

@ -23,6 +23,7 @@
#include <functional>
const unsigned int MAX_SYNC_BIT_ERRORS = 2U;
const unsigned int FAST_DATA_BEEP_GRACE_FRAMES = 6U;
bool CallsignCompare(const std::string& arg, const unsigned char* my)
{
@ -83,7 +84,15 @@ m_minRSSI(0U),
m_aveRSSI(0U),
m_rssiCount(0U),
m_enabled(true),
m_fp(NULL)
m_fp(NULL),
m_rfVoiceSyncData(NULL),
m_rfVoiceSyncDataLen(0U),
m_netVoiceSyncData(NULL),
m_netVoiceSyncDataLen(0U),
m_rfNextFrameIsFastData(false),
m_netNextFrameIsFastData(false),
m_rfSkipDTMFBlankingFrames(0U),
m_netSkipDTMFBlankingFrames(0U)
{
assert(display != NULL);
assert(rssiMapper != NULL);
@ -92,6 +101,8 @@ m_fp(NULL)
m_gateway = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH];
m_lastFrame = new unsigned char[DSTAR_FRAME_LENGTH_BYTES + 1U];
m_rfVoiceSyncData = new unsigned char[MODEM_DATA_LEN];
m_netVoiceSyncData = new unsigned char[MODEM_DATA_LEN];
std::string call = callsign;
call.resize(DSTAR_LONG_CALLSIGN_LENGTH - 1U, ' ');
@ -116,6 +127,56 @@ CDStarControl::~CDStarControl()
delete[] m_callsign;
delete[] m_gateway;
delete[] m_lastFrame;
delete[] m_rfVoiceSyncData;
delete[] m_netVoiceSyncData;
}
unsigned int CDStarControl::maybeFixupVoiceFrame(
unsigned char* data,
unsigned int len,
unsigned int offset,
const char* log_prefix,
unsigned char n,
bool blank_dtmf,
unsigned char* voice_sync_data,
unsigned int* voice_sync_data_len,
bool* next_frame_is_fast_data,
unsigned int* skip_dtmf_blanking_frames
)
{
unsigned int errors = 0U;
unsigned char mini_header = data[offset + 9U] ^ DSTAR_SCRAMBLER_BYTES[0U];
unsigned char mini_header_type = mini_header & DSTAR_SLOW_DATA_TYPE_MASK;
if (n == 0U) {
LogMessage("%s frame %u: FEC regeneration disabled for first frame", log_prefix, n);
} else if ((n % 2U != 0U) &&
((mini_header_type == DSTAR_SLOW_DATA_TYPE_FASTDATA01) ||
(mini_header_type == DSTAR_SLOW_DATA_TYPE_FASTDATA16))) {
*next_frame_is_fast_data = true;
if (blank_dtmf)
*skip_dtmf_blanking_frames = FAST_DATA_BEEP_GRACE_FRAMES;
LogMessage("%s frame %u: found fast data", log_prefix, n);
} else if (*next_frame_is_fast_data == true) {
*next_frame_is_fast_data = false;
if (blank_dtmf)
*skip_dtmf_blanking_frames = FAST_DATA_BEEP_GRACE_FRAMES;
LogMessage("%s frame %u: found fast data (cont.)", log_prefix, n);
} else {
errors = m_fec.regenerateDStar(data + offset);
LogMessage("%s frame %u: *** REGENERATING FEC ***", log_prefix, n);
if (blank_dtmf && (*skip_dtmf_blanking_frames > 0U)) {
(*skip_dtmf_blanking_frames)--;
LogMessage("%s frame %u: *** Not BLANKING DTMF (left to skip: %u) ***",
log_prefix, n, *skip_dtmf_blanking_frames);
} else if (blank_dtmf && (*skip_dtmf_blanking_frames == 0U)) {
LogMessage("%s frame %u: *** BLANKING DTMF ***", log_prefix, n);
blankDTMF(data + offset);
}
}
return errors;
}
bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
@ -323,6 +384,9 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
if (m_duplex)
writeQueueEOTRF();
m_rfNextFrameIsFastData = false;
m_rfSkipDTMFBlankingFrames = 0U;
unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH];
unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH];
unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH];
@ -353,17 +417,6 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
return false;
} else if (m_rfState == RS_RF_AUDIO) {
unsigned int errors = 0U;
if (!m_rfHeader.isDataPacket()) {
errors = m_fec.regenerateDStar(data + 1U);
m_display->writeDStarBER(float(errors) / 0.48F);
LogDebug("D-Star, audio sequence no. %u, errs: %u/48 (%.1f%%)", m_rfN, errors, float(errors) / 0.48F);
m_rfErrs += errors;
}
m_rfBits += 48U;
m_rfFrames++;
// The sync is regenerated by the modem so can do exact match
if (::memcmp(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES) == 0)
m_rfN = 0U;
@ -374,13 +427,23 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_display->writeDStarRSSI(m_rssi);
}
unsigned int errors = 0U;
if (!m_rfHeader.isDataPacket()) {
errors = maybeFixupVoiceFrame(data, len, 1U, "RF", m_rfN, m_duplex, m_rfVoiceSyncData, &m_rfVoiceSyncDataLen,
&m_rfNextFrameIsFastData, &m_rfSkipDTMFBlankingFrames);
m_display->writeDStarBER(float(errors) / 0.48F);
LogDebug("D-Star, audio sequence no. %u, errs: %u/48 (%.1f%%)", m_rfN, errors, float(errors) / 0.48F);
m_rfErrs += errors;
}
m_rfBits += 48U;
m_rfFrames++;
if (m_net)
writeNetworkDataRF(data, errors, false);
if (m_duplex) {
blankDTMF(data + 1U);
if (m_duplex)
writeQueueDataRF(data);
}
m_rfN = (m_rfN + 1U) % 21U;
} else if (m_rfState == RS_RF_LATE_ENTRY) {
@ -492,7 +555,8 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
unsigned int errors = 0U;
if (!m_rfHeader.isDataPacket()) {
errors = m_fec.regenerateDStar(data + 1U);
errors = maybeFixupVoiceFrame(data, len, 1U, "RF", m_rfN, m_duplex, m_rfVoiceSyncData, &m_rfVoiceSyncDataLen,
&m_rfNextFrameIsFastData, &m_rfSkipDTMFBlankingFrames);
LogDebug("D-Star, audio sequence no. %u, errs: %u/48 (%.1f%%)", m_rfN, errors, float(errors) / 0.48F);
m_rfErrs += errors;
}
@ -502,10 +566,8 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
if (m_net)
writeNetworkDataRF(data, errors, false);
if (m_duplex) {
blankDTMF(data + 1U);
if (m_duplex)
writeQueueDataRF(data);
}
m_rfState = RS_RF_AUDIO;
@ -670,6 +732,9 @@ void CDStarControl::writeNetwork()
writeFile(data + 1U, length - 1U);
closeFile();
#endif
m_netNextFrameIsFastData = false;
m_netSkipDTMFBlankingFrames = 0U;
unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH];
unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH];
unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH];
@ -690,9 +755,8 @@ void CDStarControl::writeNetwork()
unsigned int errors = 0U;
if (!m_netHeader.isDataPacket())
errors = m_fec.regenerateDStar(data + 2U);
blankDTMF(data + 2U);
errors = maybeFixupVoiceFrame(data, length, 2U, "Net", n, true, m_netVoiceSyncData, &m_netVoiceSyncDataLen,
&m_netNextFrameIsFastData, &m_netSkipDTMFBlankingFrames);
data[1U] = TAG_DATA;

View file

@ -98,6 +98,28 @@ private:
unsigned int m_rssiCount;
bool m_enabled;
FILE* m_fp;
unsigned char* m_rfVoiceSyncData;
unsigned int m_rfVoiceSyncDataLen;
unsigned char* m_netVoiceSyncData;
unsigned int m_netVoiceSyncDataLen;
bool m_rfNextFrameIsFastData;
bool m_netNextFrameIsFastData;
unsigned int m_rfSkipDTMFBlankingFrames;
unsigned int m_netSkipDTMFBlankingFrames;
unsigned int maybeFixupVoiceFrame(
unsigned char* data,
unsigned int len,
unsigned int offset,
const char* log_prefix,
unsigned char n,
bool blank_dtmf,
unsigned char* voice_sync_data,
unsigned int* voice_sync_data_len,
bool* next_frame_is_fast_data,
unsigned int* skip_dtmf_blanking_frames
);
void writeNetwork();

View file

@ -42,12 +42,14 @@ const unsigned int DSTAR_DATA_FRAME_LENGTH_BYTES = 3U;
const unsigned int DSTAR_LONG_CALLSIGN_LENGTH = 8U;
const unsigned int DSTAR_SHORT_CALLSIGN_LENGTH = 4U;
const unsigned char DSTAR_SLOW_DATA_TYPE_MASK = 0xF0U;
const unsigned char DSTAR_SLOW_DATA_TYPE_GPSDATA = 0x30U;
const unsigned char DSTAR_SLOW_DATA_TYPE_TEXT = 0x40U;
const unsigned char DSTAR_SLOW_DATA_TYPE_HEADER = 0x50U;
const unsigned char DSTAR_SLOW_DATA_TYPE_SQUELCH = 0xC0U;
const unsigned char DSTAR_SLOW_DATA_LENGTH_MASK = 0x0FU;
const unsigned char DSTAR_SLOW_DATA_TYPE_MASK = 0xF0U;
const unsigned char DSTAR_SLOW_DATA_TYPE_GPSDATA = 0x30U;
const unsigned char DSTAR_SLOW_DATA_TYPE_TEXT = 0x40U;
const unsigned char DSTAR_SLOW_DATA_TYPE_HEADER = 0x50U;
const unsigned char DSTAR_SLOW_DATA_TYPE_FASTDATA01 = 0x80U;
const unsigned char DSTAR_SLOW_DATA_TYPE_FASTDATA16 = 0x90U;
const unsigned char DSTAR_SLOW_DATA_TYPE_SQUELCH = 0xC0U;
const unsigned char DSTAR_SLOW_DATA_LENGTH_MASK = 0x0FU;
// Data Frames are always scrambled using the first three bytes of
// DSTAR_SCRAMBLER_BYTES, and Voice Frames are scrambled with all nine

View file

@ -39,6 +39,8 @@ const unsigned char TAG_DATA = 0x01U;
const unsigned char TAG_LOST = 0x02U;
const unsigned char TAG_EOT = 0x03U;
const unsigned int MODEM_DATA_LEN = 220U;
enum HW_TYPE {
HWT_MMDVM,
HWT_DVMEGA,

View file

@ -668,7 +668,7 @@ int CMMDVMHost::run()
m_ump->setCD(cd);
}
unsigned char data[220U];
unsigned char data[MODEM_DATA_LEN];
unsigned int len;
bool ret;