void CDMRSlot::clock(unsigned int ms) { m_timeoutTimer.clock(ms); m_playoutTimer.clock(ms); if (m_playoutTimer.isRunning() && m_playoutTimer.hasExpired()) { m_playoutTimer.stop(); while (!m_networkQueue.isEmpty()) { unsigned char len = 0U; m_networkQueue.getData(&len, 1U); unsigned char buffer[100U]; m_networkQueue.getData(buffer, len); m_radioQueue.addData(&len, 1U); m_radioQueue.addData(buffer, len); } } if (m_state == SS_RELAYING_NETWORK) { m_networkWatchdog.clock(ms); if (m_networkWatchdog.hasExpired()) { LogMessage("DMR Slot %u, network watchdog has expired", m_slotNo); writeEndOfTransmission(); #if defined(DUMP_DMR) closeFile(); #endif } } }
void CDStarControl::clock(unsigned int ms) { if (m_network != NULL) writeNetwork(); m_ackTimer.clock(ms); if (m_ackTimer.isRunning() && m_ackTimer.hasExpired()) { sendAck(); m_ackTimer.stop(); } m_holdoffTimer.clock(ms); if (m_holdoffTimer.isRunning() && m_holdoffTimer.hasExpired()) m_holdoffTimer.stop(); m_timeoutTimer.clock(ms); if (m_state == RS_RELAYING_NETWORK_AUDIO) { m_networkWatchdog.clock(ms); if (m_networkWatchdog.hasExpired()) { // We're received the header haven't we? m_frames += 1U; if (m_bits == 0U) m_bits = 1U; LogMessage("D-Star, network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%", float(m_frames) / 50.0F, (m_lost * 100U) / m_frames, float(m_errs * 100U) / float(m_bits)); m_timeoutTimer.stop(); writeEndOfTransmission(); #if defined(DUMP_DSTAR) closeFile(); #endif } } if (m_state == RS_RELAYING_NETWORK_AUDIO) { m_packetTimer.clock(ms); if (m_packetTimer.isRunning() && m_packetTimer.hasExpired()) { unsigned int frames = m_elapsed.elapsed() / DSTAR_FRAME_TIME; if (frames > m_frames) { unsigned int count = frames - m_frames; if (count > 3U) { LogMessage("D-Star, lost audio for 300ms filling in, %u %u", frames, m_frames); insertSilence(count - 1U); } } m_packetTimer.start(); } } }
void CDMRSlot::clock(unsigned int ms) { m_timeoutTimer.clock(ms); if (m_state == RS_RELAYING_NETWORK_AUDIO || m_state == RS_RELAYING_NETWORK_DATA) { m_networkWatchdog.clock(ms); if (m_networkWatchdog.hasExpired()) { // We've received the voice header haven't we? m_frames += 1U; if (m_state == RS_RELAYING_NETWORK_AUDIO) LogMessage("DMR Slot %u, network watchdog has expired, %u%% packet loss, BER: %u%%", m_slotNo, (m_lost * 100U) / m_frames, (m_errs * 100U) / m_bits); else LogMessage("DMR Slot %u, network watchdog has expired", m_slotNo); writeEndOfTransmission(); #if defined(DUMP_DMR) closeFile(); #endif } } if (m_state == RS_RELAYING_NETWORK_AUDIO) { m_packetTimer.clock(ms); if (m_packetTimer.hasExpired()) { unsigned int frames = m_elapsed.elapsed() / DMR_SLOT_TIME; if (frames > m_frames) { unsigned int count = frames - m_frames; if (count > 3U) insertSilence(m_seqNo + count - 1U); } m_packetTimer.start(); } } }
void CDMRSlot::writeModem(unsigned char *data) { if (data[0U] == TAG_LOST && m_state == SS_RELAYING_RF) { LogMessage("DMR Slot %u, transmission lost", m_slotNo); writeEndOfTransmission(); return; } if (data[0U] == TAG_LOST && m_state == SS_LATE_ENTRY) { m_state = SS_LISTENING; return; } if (m_state == SS_RELAYING_NETWORK) return; bool dataSync = (data[1U] & DMR_SYNC_DATA) == DMR_SYNC_DATA; bool audioSync = (data[1U] & DMR_SYNC_AUDIO) == DMR_SYNC_AUDIO; if (dataSync) { CSlotType slotType; slotType.putData(data + 2U); unsigned char colorCode = slotType.getColorCode(); unsigned char dataType = slotType.getDataType(); if (colorCode != m_colorCode) return; if (dataType == DT_VOICE_LC_HEADER) { if (m_state != SS_RELAYING_RF) { CFullLC fullLC; m_lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER); if (m_lc == NULL) { LogMessage("DMR Slot %u: unable to decode the LC", m_slotNo); return; } // Regenerate the LC fullLC.encode(*m_lc, data + 2U, DT_VOICE_LC_HEADER); // Regenerate the Slot Type slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; m_n = 0U; m_networkWatchdog.stop(); m_timeoutTimer.start(); m_seqNo = 0U; for (unsigned i = 0U; i < 3U; i++) { writeNetwork(data, DT_VOICE_LC_HEADER); writeRadioQueue(data); } m_state = SS_RELAYING_RF; setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); LogMessage("DMR Slot %u, received RF header from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); } } else if (dataType == DT_VOICE_PI_HEADER) { if (m_state == SS_RELAYING_RF) { // Regenerate the Slot Type slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; m_n = 0U; writeNetwork(data, DT_VOICE_PI_HEADER); writeRadioQueue(data); LogMessage("DMR Slot %u, received PI header", m_slotNo); } else { // Should save the PI header for after we have a valid LC } } else { // Ignore wakeup CSBKs if (dataType == DT_CSBK) { CCSBK csbk(data + 2U); CSBKO csbko = csbk.getCSBKO(); if (csbko == CSBKO_BSDWNACT) return; } if (m_state == SS_RELAYING_RF) { unsigned char end[DMR_FRAME_LENGTH_BYTES + 2U]; // Generate the LC CFullLC fullLC; fullLC.encode(*m_lc, end + 2U, DT_TERMINATOR_WITH_LC); // Generate the Slot Type CSlotType slotType; slotType.setColorCode(m_colorCode); slotType.setDataType(DT_TERMINATOR_WITH_LC); slotType.getData(end + 2U); // Set the Data Sync to be from the BS CDMRSync sync; sync.addSync(end + 2U, DST_BS_DATA); end[0U] = TAG_EOT; end[1U] = 0x00U; writeNetwork(end, DT_TERMINATOR_WITH_LC); writeRadioQueue(end); LogMessage("DMR Slot %u, received RF end of transmission", m_slotNo); // 480ms of idle to space things out for (unsigned int i = 0U; i < 8U; i++) writeRadioQueue(m_idle); writeEndOfTransmission(); if (dataType == DT_TERMINATOR_WITH_LC) return; } // Regenerate the Slot Type slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; writeNetwork(data, dataType); writeRadioQueue(data); } } else if (audioSync) { if (m_state == SS_RELAYING_RF) { // Convert the Audio Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_AUDIO); unsigned char fid = m_lc->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) ; // AMBE FEC data[0U] = TAG_DATA; data[1U] = 0x00U; m_n = 0U; writeRadioQueue(data); writeNetwork(data, DT_VOICE_SYNC); } else if (m_state == SS_LISTENING) { m_state = SS_LATE_ENTRY; } } else { if (m_state == SS_RELAYING_RF) { // Regenerate the EMB CEMB emb; emb.putData(data + 2U); emb.setColorCode(m_colorCode); emb.getData(data + 2U); unsigned char fid = m_lc->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) ; // AMBE FEC data[0U] = TAG_DATA; data[1U] = 0x00U; m_n++; writeRadioQueue(data); writeNetwork(data, DT_VOICE); } else if (m_state == SS_LATE_ENTRY) { // If we haven't received an LC yet, then be strict on the color code CEMB emb; emb.putData(data + 2U); unsigned char colorCode = emb.getColorCode(); if (colorCode != m_colorCode) return; m_lc = m_embeddedLC.addData(data + 2U, emb.getLCSS()); if (m_lc != NULL) { // Create a dummy start frame to replace the received frame unsigned char start[DMR_FRAME_LENGTH_BYTES + 2U]; CDMRSync sync; sync.addSync(start + 2U, DST_BS_DATA); CFullLC fullLC; fullLC.encode(*m_lc, start + 2U, DT_VOICE_LC_HEADER); CSlotType slotType; slotType.setColorCode(m_colorCode); slotType.setDataType(DT_VOICE_LC_HEADER); slotType.getData(start + 2U); start[0U] = TAG_DATA; start[1U] = 0x00U; m_n = 0U; m_networkWatchdog.stop(); m_timeoutTimer.start(); m_seqNo = 0U; for (unsigned int i = 0U; i < 3U; i++) { writeNetwork(start, DT_VOICE_LC_HEADER); writeRadioQueue(start); } // Send the original audio frame out unsigned char fid = m_lc->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) ; // AMBE FEC data[0U] = TAG_DATA; data[1U] = 0x00U; m_n++; writeRadioQueue(data); writeNetwork(data, DT_VOICE); m_state = SS_RELAYING_RF; setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); LogMessage("DMR Slot %u, received RF late entry from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); } } } }
void CDMRSlot::writeNetwork(const CDMRData& dmrData) { if (m_state == SS_RELAYING_RF || m_state == SS_LATE_ENTRY) return; m_networkWatchdog.start(); unsigned char dataType = dmrData.getDataType(); unsigned char data[DMR_FRAME_LENGTH_BYTES + 2U]; dmrData.getData(data + 2U); if (dataType == DT_VOICE_LC_HEADER) { if (m_state != SS_RELAYING_NETWORK) { CFullLC fullLC; m_lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER); if (m_lc == NULL) { LogMessage("DMR Slot %u, bad LC received from the network", m_slotNo); return; } // Regenerate the LC fullLC.encode(*m_lc, data + 2U, DT_VOICE_LC_HEADER); // Regenerate the Slot Type CSlotType slotType; slotType.setColorCode(m_colorCode); slotType.setDataType(DT_VOICE_LC_HEADER); slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; m_playoutTimer.start(); m_timeoutTimer.start(); for (unsigned int i = 0U; i < 3U; i++) writeNetworkQueue(data); m_state = SS_RELAYING_NETWORK; setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); #if defined(DUMP_DMR) openFile(); writeFile(data); #endif LogMessage("DMR Slot %u, received network header from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); } } else if (dataType == DT_VOICE_PI_HEADER) { if (m_state != SS_RELAYING_NETWORK) return; // Regenerate the Slot Type CSlotType slotType; slotType.setColorCode(m_colorCode); slotType.setDataType(DT_VOICE_PI_HEADER); slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; writeNetworkQueue(data); #if defined(DUMP_DMR) writeFile(data); #endif } else if (dataType == DT_TERMINATOR_WITH_LC) { if (m_state != SS_RELAYING_NETWORK) return; // Regenerate the LC CFullLC fullLC; fullLC.encode(*m_lc, data + 2U, DT_TERMINATOR_WITH_LC); // Regenerate the Slot Type CSlotType slotType; slotType.setColorCode(m_colorCode); slotType.setDataType(DT_TERMINATOR_WITH_LC); slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_EOT; data[1U] = 0x00U; writeNetworkQueue(data); writeEndOfTransmission(); #if defined(DUMP_DMR) writeFile(data); closeFile(); #endif LogMessage("DMR Slot %u, received network end of transmission", m_slotNo); } else if (dataType == DT_VOICE_SYNC) { if (m_state != SS_RELAYING_NETWORK) return; // Convert the Audio Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_AUDIO); unsigned char fid = m_lc->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) ; // AMBE FEC data[0U] = TAG_DATA; data[1U] = 0x00U; writeNetworkQueue(data); #if defined(DUMP_DMR) writeFile(data); #endif } else if (dataType == DT_VOICE) { if (m_state != SS_RELAYING_NETWORK) return; unsigned char fid = m_lc->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) ; // AMBE FEC // Change the color code in the EMB CEMB emb; emb.putData(data + 2U); emb.setColorCode(m_colorCode); emb.getData(data + 2U); data[0U] = TAG_DATA; data[1U] = 0x00U; writeNetworkQueue(data); #if defined(DUMP_DMR) writeFile(data); #endif } else { if (m_state != SS_RELAYING_NETWORK) return; // Change the Color Code of the Slot Type CSlotType slotType; slotType.putData(data + 2U); slotType.setColorCode(m_colorCode); slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; writeNetworkQueue(data); #if defined(DUMP_DMR) writeFile(data); #endif } }
bool CDStarControl::writeModem(unsigned char *data) { unsigned char type = data[0U]; if (type == TAG_LOST && m_state == RS_RELAYING_RF_AUDIO) { if (m_bits == 0U) m_bits = 1U; LogMessage("D-Star, transmission lost, %.1f seconds, BER: %.1f%%", float(m_frames) / 50.0F, float(m_errs * 100U) / float(m_bits)); m_ackTimer.start(); writeEndOfTransmission(); return false; } if (type == TAG_LOST) { if (m_state == RS_LATE_ENTRY) m_state = RS_LISTENING; return false; } if (type == TAG_HEADER) { CDStarHeader header(data + 1U); // Is this a transmission destined for a repeater? if (!header.isRepeater()) { LogMessage("D-Star, non repeater RF header received"); return false; } unsigned char callsign[DSTAR_LONG_CALLSIGN_LENGTH]; header.getRPTCall1(callsign); // Is it for us? if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) { LogMessage("D-Star, received RF header for wrong repeater - %8.8s", callsign); return false; } unsigned char gateway[DSTAR_LONG_CALLSIGN_LENGTH]; header.getRPTCall2(gateway); unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; header.getMyCall1(my1); unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH]; header.getMyCall2(my2); unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH]; header.getYourCall(your); m_net = ::memcmp(gateway, m_gateway, DSTAR_LONG_CALLSIGN_LENGTH) == 0; if (m_state == RS_LISTENING) { // Only start the timeout if not already running if (!m_timeoutTimer.isRunning()) { m_timeoutTimer.start(); m_bits = 1U; m_errs = 0U; } m_header = header; m_networkWatchdog.stop(); m_holdoffTimer.stop(); m_ackTimer.stop(); m_frames = 1U; m_lost = 0U; m_n = 0U; if (m_duplex) { // Modify the header header.setRepeater(false); header.setRPTCall1(m_callsign); header.setRPTCall2(m_callsign); header.get(data + 1U); writeQueueHeader(data); } if (m_net) { // Modify the header header.setRepeater(false); header.setRPTCall1(m_callsign); header.setRPTCall2(m_gateway); header.get(data + 1U); for (unsigned i = 0U; i < 3U; i++) writeNetworkHeader(data, false); } m_state = RS_RELAYING_RF_AUDIO; m_display->writeDStar(std::string((char*)my1, 8U), std::string((char*)my2, 4U)); LogMessage("D-Star, received RF header from %8.8s/%4.4s to %8.8s", my1, my2, your); } else if (m_state == RS_RELAYING_NETWORK_AUDIO) { if (m_net) { for (unsigned i = 0U; i < 3U; i++) writeNetworkHeader(data, true); } LogMessage("D-Star, received RF busy header from %8.8s/%4.4s to %8.8s", my1, my2, your); return false; } } else if (type == TAG_EOT) { if (m_state == RS_RELAYING_RF_AUDIO) { if (m_net) { for (unsigned int i = 0U; i < 2U; i++) writeNetworkData(DSTAR_END_PATTERN_BYTES, 0U, true, false); } if (m_duplex) { for (unsigned int i = 0U; i < 3U; i++) writeQueueData(DSTAR_END_PATTERN_BYTES); } m_ackTimer.start(); if (m_bits == 0U) m_bits = 1U; LogMessage("D-Star, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_frames) / 50.0F, float(m_errs * 100U) / float(m_bits)); writeEndOfTransmission(); } else if (m_state == RS_RELAYING_NETWORK_AUDIO) { if (m_net) { for (unsigned int i = 0U; i < 2U; i++) writeNetworkData(DSTAR_END_PATTERN_BYTES, 0U, true, true); } } return false; } else if (type == TAG_DATA) { if (m_state == RS_LISTENING) { // 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_slowData.start(); m_state = RS_LATE_ENTRY; } return false; } else if (m_state == RS_RELAYING_RF_AUDIO) { unsigned int errors = m_fec.regenerateDStar(data + 1U); m_errs += errors; m_bits += 48U; m_frames++; // 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_n = 0U; // Regenerate the sync if (m_n == 0U) ::memcpy(data + DSTAR_VOICE_FRAME_LENGTH_BYTES + 1U, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES); m_n = (m_n + 1U) % 21U; if (m_net) writeNetworkData(data, errors, false, false); if (m_duplex) { blankDTMF(data + 1U); writeQueueData(data); } } else if (m_state == RS_RELAYING_NETWORK_AUDIO) { m_fec.regenerateDStar(data + 1U); if (m_net) writeNetworkData(data, 0U, false, true); return false; } else if (m_state == RS_LATE_ENTRY) { // 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_slowData.reset(); return false; } CDStarHeader* header = m_slowData.add(data + 1U); if (header == NULL) return false; // Is this a transmission destined for a repeater? if (!header->isRepeater()) { LogMessage("D-Star, non repeater RF late entry header received"); return false; } unsigned char callsign[DSTAR_LONG_CALLSIGN_LENGTH]; header->getRPTCall1(callsign); // Is it for us? if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) { LogMessage("D-Star, received RF header for wrong repeater - %8.8s", callsign); return false; } unsigned char gateway[DSTAR_LONG_CALLSIGN_LENGTH]; header->getRPTCall2(gateway); unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; header->getMyCall1(my1); unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH]; header->getMyCall2(my2); unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH]; header->getYourCall(your); m_net = ::memcmp(gateway, m_gateway, DSTAR_LONG_CALLSIGN_LENGTH) == 0; // Only reset the timeout if the timeout is not running if (!m_timeoutTimer.isRunning()) { m_timeoutTimer.start(); m_bits = 1U; m_errs = 0U; } // Create a dummy start frame to replace the received frame m_networkWatchdog.stop(); m_ackTimer.stop(); m_header = *header; m_frames = 1U; m_lost = 0U; m_n = 1U; if (m_duplex) { unsigned char start[DSTAR_HEADER_LENGTH_BYTES + 1U]; start[0U] = TAG_HEADER; // Modify the header header->setRepeater(false); header->setRPTCall1(m_callsign); header->setRPTCall2(m_callsign); header->get(start + 1U); writeQueueHeader(start); } if (m_net) { unsigned char start[DSTAR_HEADER_LENGTH_BYTES + 1U]; start[0U] = TAG_HEADER; // Modify the header header->setRepeater(false); header->setRPTCall1(m_gateway); header->setRPTCall2(m_callsign); header->get(start + 1U); for (unsigned int i = 0U; i < 3U; i++) writeNetworkHeader(start, false); } delete header; unsigned int errors = m_fec.regenerateDMR(data + 1U); m_errs += errors; m_bits += 48U; if (m_net) writeNetworkData(data, errors, false, false); if (m_duplex) { blankDTMF(data + 1U); writeQueueData(data); } m_state = RS_RELAYING_RF_AUDIO; m_display->writeDStar(std::string((char*)my1, 8U), std::string((char*)my2, 4U)); LogMessage("D-Star, received RF late entry from %8.8s/%4.4s to %8.8s", my1, my2, your); } } else { CUtils::dump("D-Star, unknown data from modem", data, DSTAR_FRAME_LENGTH_BYTES + 1U); } return true; }
void CDStarControl::writeNetwork() { assert(m_network != NULL); unsigned char data[DSTAR_HEADER_LENGTH_BYTES + 2U]; unsigned int length = m_network->read(data, DSTAR_HEADER_LENGTH_BYTES + 2U); if (length == 0U) return; if (m_state == RS_RELAYING_RF_AUDIO || m_state == RS_LATE_ENTRY) return; m_networkWatchdog.start(); unsigned char type = data[0U]; if (type == TAG_HEADER) { if (m_state != RS_LISTENING) return; CDStarHeader header(data + 1U); unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; header.getMyCall1(my1); unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH]; header.getMyCall2(my2); unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH]; header.getYourCall(your); m_header = header; m_timeoutTimer.start(); m_elapsed.start(); m_ackTimer.stop(); m_frames = 0U; m_lost = 0U; m_n = 0U; m_bits = 1U; m_errs = 0U; writeQueueHeader(data); #if defined(DUMP_DSTAR) openFile(); writeFile(data + 1U, length - 1U); #endif m_state = RS_RELAYING_NETWORK_AUDIO; m_display->writeDStar(std::string((char*)my1, 8U), std::string((char*)my2, 4U)); LogMessage("D-Star, received network header from %8.8s/%4.4s to %8.8s", my1, my2, your); } else if (type == TAG_EOT) { if (m_state != RS_RELAYING_NETWORK_AUDIO) return; m_timeoutTimer.stop(); data[1U] = TAG_EOT; for (unsigned int i = 0U; i < 3U; i++) writeQueueData(data + 1U); #if defined(DUMP_DSTAR) writeFile(data + 1U, length - 1U); closeFile(); #endif // We've received the header and EOT haven't we? m_frames += 2U; if (m_bits == 0U) m_bits = 1U; LogMessage("D-Star, received network end of transmission, %.1f seconds, %u%% packet loss, BER: %.1f%%", float(m_frames) / 50.0F, (m_lost * 100U) / m_frames, float(m_errs * 100U) / float(m_bits)); writeEndOfTransmission(); } else if (type == TAG_DATA) { if (m_state != RS_RELAYING_NETWORK_AUDIO) return; unsigned char n = data[1U]; insertSilence(data + 2U, n); m_errs += m_fec.regenerateDStar(data + 2U); m_bits += 48U; blankDTMF(data + 2U); // Regenerate the sync if (n == 0U) ::memcpy(data + DSTAR_VOICE_FRAME_LENGTH_BYTES + 2U, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES); m_n = n; m_packetTimer.start(); m_frames++; data[1U] = TAG_DATA; #if defined(DUMP_DSTAR) writeFile(data + 1U, length - 1U); #endif writeQueueData(data + 1U); } else { CUtils::dump("D-Star, unknown data from network", data, DSTAR_FRAME_LENGTH_BYTES + 1U); } }
void CDMRSlot::writeModem(unsigned char *data) { if (data[0U] == TAG_LOST && m_state == RS_RELAYING_RF_AUDIO) { LogMessage("DMR Slot %u, transmission lost, BER: %u%%", m_slotNo, (m_errs * 100U) / m_bits); writeEndOfTransmission(); return; } if (data[0U] == TAG_LOST && m_state == RS_RELAYING_RF_DATA) { LogMessage("DMR Slot %u, transmission lost", m_slotNo); writeEndOfTransmission(); return; } if (data[0U] == TAG_LOST && m_state == RS_LATE_ENTRY) { m_state = RS_LISTENING; return; } if (m_state == RS_RELAYING_NETWORK_AUDIO || m_state == RS_RELAYING_NETWORK_DATA) return; bool dataSync = (data[1U] & DMR_SYNC_DATA) == DMR_SYNC_DATA; bool audioSync = (data[1U] & DMR_SYNC_AUDIO) == DMR_SYNC_AUDIO; if (dataSync) { CSlotType slotType; slotType.putData(data + 2U); unsigned char dataType = slotType.getDataType(); if (dataType == DT_VOICE_LC_HEADER) { if (m_state == RS_RELAYING_RF_AUDIO) return; CFullLC fullLC; m_lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER); if (m_lc == NULL) { LogMessage("DMR Slot %u: unable to decode the LC", m_slotNo); return; } // Regenerate the Slot Type slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; m_networkWatchdog.stop(); m_timeoutTimer.start(); m_seqNo = 0U; m_n = 0U; m_bits = 1U; m_errs = 0U; // Put a small delay into starting retransmission writeQueue(m_idle); for (unsigned i = 0U; i < 3U; i++) { writeNetwork(data, DT_VOICE_LC_HEADER); writeQueue(data); } m_state = RS_RELAYING_RF_AUDIO; setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); LogMessage("DMR Slot %u, received RF voice header from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); } else if (dataType == DT_VOICE_PI_HEADER) { if (m_state != RS_RELAYING_RF_AUDIO) return; // Regenerate the Slot Type slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; m_n = 0U; writeNetwork(data, DT_VOICE_PI_HEADER); writeQueue(data); } else if (dataType == DT_TERMINATOR_WITH_LC) { if (m_state != RS_RELAYING_RF_AUDIO) return; // Regenerate the Slot Type slotType.getData(data + 2U); // Set the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_EOT; data[1U] = 0x00U; writeNetwork(data, DT_TERMINATOR_WITH_LC); writeQueue(data); LogMessage("DMR Slot %u, received RF end of voice transmission, BER: %u%%", m_slotNo, (m_errs * 100U) / m_bits); // 480ms of idle to space things out for (unsigned int i = 0U; i < 8U; i++) writeQueue(m_idle); writeEndOfTransmission(); } else if (dataType == DT_DATA_HEADER) { if (m_state == RS_RELAYING_RF_DATA) return; // Regenerate the Slot Type slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; m_networkWatchdog.stop(); m_seqNo = 0U; m_n = 0U; // Put a small delay into starting retransmission writeQueue(m_idle); for (unsigned i = 0U; i < 3U; i++) { writeNetwork(data, DT_DATA_HEADER); writeQueue(data); } m_state = RS_RELAYING_RF_DATA; // setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); // m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); // LogMessage("DMR Slot %u, received RF data header from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); LogMessage("DMR Slot %u, received RF data header", m_slotNo); } else { // Regenerate the Slot Type slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; writeNetwork(data, dataType); writeQueue(data); } } else if (audioSync) { if (m_state == RS_RELAYING_RF_AUDIO) { // Convert the Audio Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_AUDIO); unsigned char fid = m_lc->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) m_errs += m_fec.regenerateDMR(data + 2U); m_bits += 216U; data[0U] = TAG_DATA; data[1U] = 0x00U; m_n = 0U; writeQueue(data); writeNetwork(data, DT_VOICE_SYNC); } else if (m_state == RS_LISTENING) { m_state = RS_LATE_ENTRY; } } else { CEMB emb; emb.putData(data + 2U); if (m_state == RS_RELAYING_RF_AUDIO) { // Regenerate the EMB emb.setColorCode(m_colorCode); emb.getData(data + 2U); unsigned char fid = m_lc->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) m_errs += m_fec.regenerateDMR(data + 2U); m_bits += 216U; data[0U] = TAG_DATA; data[1U] = 0x00U; m_n++; writeQueue(data); writeNetwork(data, DT_VOICE); } else if (m_state == RS_LATE_ENTRY) { // If we haven't received an LC yet, then be strict on the color code unsigned char colorCode = emb.getColorCode(); if (colorCode != m_colorCode) return; m_lc = m_embeddedLC.addData(data + 2U, emb.getLCSS()); if (m_lc != NULL) { // Create a dummy start frame to replace the received frame unsigned char start[DMR_FRAME_LENGTH_BYTES + 2U]; CDMRSync sync; sync.addSync(start + 2U, DST_BS_DATA); CFullLC fullLC; fullLC.encode(*m_lc, start + 2U, DT_VOICE_LC_HEADER); CSlotType slotType; slotType.setColorCode(m_colorCode); slotType.setDataType(DT_VOICE_LC_HEADER); slotType.getData(start + 2U); start[0U] = TAG_DATA; start[1U] = 0x00U; m_networkWatchdog.stop(); m_timeoutTimer.start(); m_seqNo = 0U; m_n = 0U; m_bits = 1U; m_errs = 0U; for (unsigned int i = 0U; i < 3U; i++) { writeNetwork(start, DT_VOICE_LC_HEADER); writeQueue(start); } // Regenerate the EMB emb.getData(data + 2U); // Send the original audio frame out unsigned char fid = m_lc->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) m_errs += m_fec.regenerateDMR(data + 2U); m_bits += 216U; data[0U] = TAG_DATA; data[1U] = 0x00U; m_n++; writeQueue(data); writeNetwork(data, DT_VOICE); m_state = RS_RELAYING_RF_AUDIO; setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); LogMessage("DMR Slot %u, received RF late entry from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); } } } }
void CDMRSlot::writeNetwork(const CDMRData& dmrData) { if (m_state == RS_RELAYING_RF_AUDIO || m_state == RS_RELAYING_RF_DATA || m_state == RS_LATE_ENTRY) return; m_networkWatchdog.start(); unsigned char dataType = dmrData.getDataType(); unsigned char data[DMR_FRAME_LENGTH_BYTES + 2U]; dmrData.getData(data + 2U); if (dataType == DT_VOICE_LC_HEADER) { if (m_state == RS_RELAYING_NETWORK_AUDIO) return; CFullLC fullLC; m_lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER); if (m_lc == NULL) { LogMessage("DMR Slot %u, bad LC received from the network", m_slotNo); return; } // Regenerate the Slot Type CSlotType slotType; slotType.setColorCode(m_colorCode); slotType.setDataType(DT_VOICE_LC_HEADER); slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; m_timeoutTimer.start(); m_frames = 0U; m_bits = 1U; m_errs = 0U; // 540ms of idle to give breathing space for lost frames for (unsigned int i = 0U; i < 9U; i++) writeQueue(m_idle); for (unsigned int i = 0U; i < 3U; i++) writeQueue(data); m_state = RS_RELAYING_NETWORK_AUDIO; setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); #if defined(DUMP_DMR) openFile(); writeFile(data); #endif LogMessage("DMR Slot %u, received network voice header from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); } else if (dataType == DT_VOICE_PI_HEADER) { if (m_state != RS_RELAYING_NETWORK_AUDIO) return; // Regenerate the Slot Type CSlotType slotType; slotType.setColorCode(m_colorCode); slotType.setDataType(DT_VOICE_PI_HEADER); slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; writeQueue(data); #if defined(DUMP_DMR) writeFile(data); #endif } else if (dataType == DT_TERMINATOR_WITH_LC) { if (m_state != RS_RELAYING_NETWORK_AUDIO) return; // Regenerate the Slot Type CSlotType slotType; slotType.setColorCode(m_colorCode); slotType.setDataType(DT_TERMINATOR_WITH_LC); slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_EOT; data[1U] = 0x00U; writeQueue(data); writeEndOfTransmission(); #if defined(DUMP_DMR) writeFile(data); closeFile(); #endif // We've received the voice header and terminator haven't we? m_frames += 2U; LogMessage("DMR Slot %u, received network end of voice transmission, %u%% packet loss, BER: %u%%", m_slotNo, (m_lost * 100U) / m_frames, (m_errs * 100U) / m_bits); } else if (dataType == DT_DATA_HEADER) { if (m_state == RS_RELAYING_NETWORK_DATA) return; // Regenerate the Slot Type CSlotType slotType; slotType.setColorCode(m_colorCode); slotType.setDataType(DT_DATA_HEADER); slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; // Put a small delay into starting transmission writeQueue(m_idle); writeQueue(m_idle); for (unsigned i = 0U; i < 3U; i++) writeQueue(data); m_state = RS_RELAYING_NETWORK_DATA; // setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); // m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); // LogMessage("DMR Slot %u, received network data header from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); LogMessage("DMR Slot %u, received network data header", m_slotNo); } else if (dataType == DT_VOICE_SYNC) { if (m_state != RS_RELAYING_NETWORK_AUDIO) return; // Initialise the lost packet data if (m_frames == 0U) { m_seqNo = dmrData.getSeqNo(); m_n = dmrData.getN(); m_elapsed.start(); m_lost = 0U; } else { insertSilence(dmrData.getSeqNo()); } // Convert the Audio Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_AUDIO); unsigned char fid = m_lc->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) m_errs += m_fec.regenerateDMR(data + 2U); m_bits += 216U; data[0U] = TAG_DATA; data[1U] = 0x00U; writeQueue(data); m_packetTimer.start(); m_frames++; // Save details in case we need to infill data m_seqNo = dmrData.getSeqNo(); m_n = dmrData.getN(); ::memcpy(m_lastFrame, data, DMR_FRAME_LENGTH_BYTES + 2U); #if defined(DUMP_DMR) writeFile(data); #endif } else if (dataType == DT_VOICE) { if (m_state != RS_RELAYING_NETWORK_AUDIO) return; // Initialise the lost packet data if (m_frames == 0U) { m_seqNo = dmrData.getSeqNo(); m_n = dmrData.getN(); m_elapsed.start(); m_lost = 0U; } else { insertSilence(dmrData.getSeqNo()); } unsigned char fid = m_lc->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) m_errs += m_fec.regenerateDMR(data + 2U); m_bits += 216U; // Change the color code in the EMB CEMB emb; emb.putData(data + 2U); emb.setColorCode(m_colorCode); emb.getData(data + 2U); data[0U] = TAG_DATA; data[1U] = 0x00U; writeQueue(data); m_packetTimer.start(); m_frames++; // Save details in case we need to infill data m_seqNo = dmrData.getSeqNo(); m_n = dmrData.getN(); ::memcpy(m_lastFrame, data, DMR_FRAME_LENGTH_BYTES + 2U); #if defined(DUMP_DMR) writeFile(data); #endif } else { // Change the Color Code of the Slot Type CSlotType slotType; slotType.putData(data + 2U); slotType.setColorCode(m_colorCode); slotType.getData(data + 2U); // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); data[0U] = TAG_DATA; data[1U] = 0x00U; writeQueue(data); #if defined(DUMP_DMR) writeFile(data); #endif } }