void repeaters_store_voice_frame_to_echo_buf(repeater_t *repeater, ipscpacket_t *ipscpacket) { repeater_echo_buf_t *new_echo_buf_entry; dmrpacket_payload_voice_bits_t *voice_bits; if (repeater == NULL || ipscpacket == NULL) return; new_echo_buf_entry = (repeater_echo_buf_t *)malloc(sizeof(repeater_echo_buf_t)); if (new_echo_buf_entry == NULL) { console_log(" error: can't allocate memory for new echo buffer entry\n"); return; } console_log(LOGLEVEL_REPEATERS LOGLEVEL_DEBUG "repeaters [%s]: storing ts%u voice frame to echo buf\n", repeaters_get_display_string_for_ip(&repeater->ipaddr), ipscpacket->timeslot); voice_bits = dmrpacket_extract_voice_bits(&ipscpacket->payload_bits); base_bitstobytes(voice_bits->raw.bits, sizeof(dmrpacket_payload_voice_bits_t), new_echo_buf_entry->voice_bytes.bytes, sizeof(dmrpacket_payload_voice_bits_t)/8); new_echo_buf_entry->next = NULL; if (repeater->slot[ipscpacket->timeslot-1].echo_buf_last_entry == NULL) { repeater->slot[ipscpacket->timeslot-1].echo_buf_last_entry = repeater->slot[ipscpacket->timeslot-1].echo_buf_first_entry = new_echo_buf_entry; } else { // Putting the new entry to the end of the linked list. repeater->slot[ipscpacket->timeslot-1].echo_buf_last_entry->next = new_echo_buf_entry; repeater->slot[ipscpacket->timeslot-1].echo_buf_last_entry = new_echo_buf_entry; } }
dmrpacket_sync_type_t dmrpacket_get_sync_type(dmrpacket_payload_sync_bits_t *sync_bits) { // See DMR AI spec. page 89. static uint8_t sync_bs_sourced_voice[6] = { 0x75, 0x5F, 0xD7, 0xDF, 0x75, 0xF7 }; static uint8_t sync_bs_sourced_data[6] = { 0xDF, 0xF5, 0x7D, 0x75, 0xDF, 0x5D }; static uint8_t sync_ms_sourced_voice[6] = { 0x7F, 0x7D, 0x5D, 0xD5, 0x7D, 0xFD }; static uint8_t sync_ms_sourced_data[6] = { 0xD5, 0xD7, 0xF7, 0x7F, 0xD7, 0x57 }; static uint8_t sync_ms_sourced_rc[6] = { 0x77, 0xD5, 0x5F, 0x7D, 0xFD, 0x77 }; static uint8_t sync_direct_voice_ts1[6] = { 0x5D, 0x57, 0x7F, 0x77, 0x57, 0xFF }; static uint8_t sync_direct_data_ts1[6] = { 0xF7, 0xFD, 0xD5, 0xDD, 0xFD, 0x55 }; static uint8_t sync_direct_voice_ts2[6] = { 0x7D, 0xFF, 0xD5, 0xF5, 0x5D, 0x5F }; static uint8_t sync_direct_data_ts2[6] = { 0xD7, 0x55, 0x7F, 0x5F, 0xF7, 0xF5 }; uint8_t sync_bytes[6]; base_bitstobytes(sync_bits->bits, sizeof(sync_bits->bits), sync_bytes, sizeof(sync_bytes)); if (memcmp(sync_bytes, sync_bs_sourced_voice, sizeof(sync_bytes)) == 0) return DMRPACKET_SYNC_TYPE_BS_SOURCED_VOICE; else if (memcmp(sync_bytes, sync_bs_sourced_data, sizeof(sync_bytes)) == 0) return DMRPACKET_SYNC_TYPE_BS_SOURCED_DATA; else if (memcmp(sync_bytes, sync_ms_sourced_voice, sizeof(sync_bytes)) == 0) return DMRPACKET_SYNC_TYPE_MS_SOURCED_VOICE; else if (memcmp(sync_bytes, sync_ms_sourced_data, sizeof(sync_bytes)) == 0) return DMRPACKET_SYNC_TYPE_MS_SOURCED_DATA; else if (memcmp(sync_bytes, sync_ms_sourced_rc, sizeof(sync_bytes)) == 0) return DMRPACKET_SYNC_TYPE_MS_SOURCED_RC; else if (memcmp(sync_bytes, sync_direct_voice_ts1, sizeof(sync_bytes)) == 0) return DMRPACKET_SYNC_TYPE_DIRECT_VOICE_TS1; else if (memcmp(sync_bytes, sync_direct_data_ts1, sizeof(sync_bytes)) == 0) return DMRPACKET_SYNC_TYPE_DIRECT_DATA_TS1; else if (memcmp(sync_bytes, sync_direct_voice_ts2, sizeof(sync_bytes)) == 0) return DMRPACKET_SYNC_TYPE_DIRECT_VOICE_TS2; else if (memcmp(sync_bytes, sync_direct_data_ts2, sizeof(sync_bytes)) == 0) return DMRPACKET_SYNC_TYPE_DIRECT_DATA_TS2; else return DMRPACKET_SYNC_TYPE_UNKNOWN; }
// See DMR AI. spec. page 67. and DMR services spec. page 53. dmrpacket_csbk_t *dmrpacket_csbk_decode(bptc_196_96_data_bits_t *data_bits) { static dmrpacket_csbk_t csbk; uint8_t i; uint16_t calculated_crc = 0; uint16_t crc; uint8_t bytes[12]; if (data_bits == NULL) return NULL; console_log(LOGLEVEL_DMRLC " decoding csbk:\n"); base_bitstobytes(data_bits->bits, sizeof(bptc_196_96_data_bits_t), bytes, sizeof(bytes)); for (i = 0; i < 10; i++) crc_calc_crc16_ccitt(&calculated_crc, bytes[i]); crc_calc_crc16_ccitt_finish(&calculated_crc); // Inverting according to the inversion polynomial. calculated_crc = ~calculated_crc; // Applying CRC mask, see DMR AI spec. page 143. calculated_crc ^= 0xa5a5; crc = bytes[10] << 8 | bytes[11]; console_log(LOGLEVEL_DMRLC " crc: %.4x calculated: %.4x (%s)\n", crc, calculated_crc, calculated_crc != crc ? "error" : "ok"); if (calculated_crc != crc) return NULL; if (bytes[0] & 0b01000000) { console_log(LOGLEVEL_DMRLC " error: protect flag is not 0\n"); return NULL; } if (bytes[1] != 0) { console_log(LOGLEVEL_DMRLC " error: feature set id is not 0\n"); return NULL; } csbk.last_block = (bytes[0] & 0b10000000) > 0; console_log(LOGLEVEL_DMRLC " last block: %u\n", csbk.last_block); csbk.dst_id = bytes[4] << 16 | bytes[5] << 8 | bytes[6]; console_log(LOGLEVEL_DMRLC " dst id: %u\n", csbk.dst_id); csbk.src_id = bytes[7] << 16 | bytes[8] << 8 | bytes[9]; console_log(LOGLEVEL_DMRLC " src id: %u\n", csbk.src_id); csbk.csbko = bytes[0] & 0b111111; console_log(LOGLEVEL_DMRLC " csbko: %s (%.2x)\n", dmrpacket_csbk_get_readable_csbko(csbk.csbko), csbk.csbko); switch (csbk.csbko) { case DMRPACKET_CSBKO_BS_OUTBOUND_ACTIVATION: // No important params to parse. break; case DMRPACKET_CSBKO_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST: csbk.data.unit_to_unit_voice_service_request.service_options = bytes[2]; console_log(LOGLEVEL_DMRLC " service options: 0x%.2x\n", csbk.data.unit_to_unit_voice_service_request.service_options); break; case DMRPACKET_CSBKO_UNIT_TO_UNIT_VOICE_SERVICE_ANSWER_RESPONSE: csbk.data.unit_to_unit_voice_service_answer_response.service_options = bytes[2]; console_log(LOGLEVEL_DMRLC " service options: 0x%.2x\n", csbk.data.unit_to_unit_voice_service_answer_response.service_options); csbk.data.unit_to_unit_voice_service_answer_response.answer_response = bytes[3]; console_log(LOGLEVEL_DMRLC " answer response: 0x%.2x\n", csbk.data.unit_to_unit_voice_service_answer_response.answer_response); break; case DMRPACKET_CSBKO_NEGATIVE_ACKNOWLEDGE_RESPONSE: csbk.data.negative_acknowledge_response.source_type = ((bytes[2] & 0b01000000) > 0); console_log(LOGLEVEL_DMRLC " source type: %u", csbk.data.negative_acknowledge_response.source_type); csbk.data.negative_acknowledge_response.service_type = bytes[2] & 0b111111; console_log(LOGLEVEL_DMRLC " service type: 0x%.2x\n", csbk.data.negative_acknowledge_response.service_type); csbk.data.negative_acknowledge_response.reason_code = bytes[3]; console_log(LOGLEVEL_DMRLC " reason code: 0x%.2x\n", csbk.data.negative_acknowledge_response.reason_code); break; case DMRPACKET_CSBKO_PREAMBLE: csbk.data.preamble.data_follows = ((bytes[2] & 0b10000000) > 0); console_log(LOGLEVEL_DMRLC " data follows: %u\n", csbk.data.preamble.data_follows); csbk.data.preamble.dst_is_group = ((bytes[2] & 0b01000000) > 0); console_log(LOGLEVEL_DMRLC " dst is group: %u\n", csbk.data.preamble.dst_is_group); csbk.data.preamble.csbk_blocks_to_follow = bytes[3]; console_log(LOGLEVEL_DMRLC " blocks to follow: %u\n", csbk.data.preamble.csbk_blocks_to_follow); break; default: console_log(LOGLEVEL_DMRLC " unknown csbko\n"); return NULL; } return &csbk; }