Hytera XPT WIP #110
This commit is contained in:
parent
ce75acb41a
commit
ba0e422f23
|
|
@ -143,7 +143,7 @@ NXDN Trunking Update: NXDN DFA (Direct Frequency Assignment) has been coded from
|
||||||
|
|
||||||
Channel Map and Group CSV Note: Leave the top line of the channel_map.csv and group.csv as the label, do not delete the line, if no line is there, dsd_import skips the first line so it will not import the first channel or first group in those files if there is something there that isn't a label.
|
Channel Map and Group CSV Note: Leave the top line of the channel_map.csv and group.csv as the label, do not delete the line, if no line is there, dsd_import skips the first line so it will not import the first channel or first group in those files if there is something there that isn't a label.
|
||||||
|
|
||||||
Hytera XPT: Experimental Code has been added for XPT system slco/flco/csbk decoding and trunking. The setup will be similar to Capacity Plus trunking in the csv file, but add a channel 0 and frequency value as the 'home' repeater. This is based off of my best understanding that the 'home' repeater is the starting point of all XPT calls, and that calls only expand outwards to the next free repeater the busier the system gets. I cannot guarantee that XPT will 'trunk' correctly, it may in fact stay on the home repeater most times since theoretically, if my understanding is correct, all calls start there and there should be a voice call there before there is one on a free repeater. To properly decode an XPT system, having multiple instances of DSD-FME passively listening to each repeater channel may be a more complete method than attempting to trunk it directly.
|
Hytera XPT: Experimental Code has been added for XPT system slco/flco/csbk decoding and trunking. The setup will be similar to Capacity Plus trunking in the csv file. This is based off of my best understanding that the 'home' repeater LCN is the starting point of all XPT calls, and that calls only expand outwards to the next free repeater LCN the busier the system gets. I cannot guarantee that XPT will 'trunk' correctly, it may in fact stay on the home repeater most times since theoretically, if my understanding is correct, all calls start there and there should be a voice call there before there is one on a free repeater. To properly decode an XPT system, having multiple instances of DSD-FME passively listening to each repeater channel may be a more complete method than attempting to trunk it directly.
|
||||||
|
|
||||||
## NCurses Keyboard Shortcuts ##
|
## NCurses Keyboard Shortcuts ##
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
LSN Number(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||||
1,858606250
|
1,858606250
|
||||||
2,858606250
|
2,858606250
|
||||||
3,859606250
|
3,859606250
|
||||||
|
|
|
||||||
|
|
|
@ -34,6 +34,25 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
|
||||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[16], 24); //destination llid
|
uint32_t target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[16], 24); //destination llid
|
||||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[40], 24); //source llid
|
uint32_t source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[40], 24); //source llid
|
||||||
|
|
||||||
|
//extra tgt/src handling for XPT
|
||||||
|
uint8_t target_hash[24];
|
||||||
|
uint8_t tg_hash = 0;
|
||||||
|
uint8_t is_xpt = 0;
|
||||||
|
//set flag if XPT
|
||||||
|
if (strcmp (state->dmr_branding_sub, "XPT ") == 0) is_xpt = 1;
|
||||||
|
|
||||||
|
//truncate tgt/src to 16-bit values if XPT
|
||||||
|
if (is_xpt == 1)
|
||||||
|
{
|
||||||
|
target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[24], 16); //destination llid
|
||||||
|
source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 16); //source llid
|
||||||
|
if (gi == 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 16; i++) target_hash[i] = dheader_bits[24+i];
|
||||||
|
tg_hash = crc8 (target_hash, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//store source for dmr pdu packet handling (lrrp) when not available in completed message
|
//store source for dmr pdu packet handling (lrrp) when not available in completed message
|
||||||
if (dpf != 15) state->dmr_lrrp_source[slot] = source;
|
if (dpf != 15) state->dmr_lrrp_source[slot] = source;
|
||||||
|
|
||||||
|
|
@ -112,6 +131,9 @@ void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t
|
||||||
if (a == 1 && dpf != 15) fprintf(stderr, "- Response Requested ");
|
if (a == 1 && dpf != 15) fprintf(stderr, "- Response Requested ");
|
||||||
if (dpf != 15) fprintf (stderr, "- Source: %d Target: %d ", source, target);
|
if (dpf != 15) fprintf (stderr, "- Source: %d Target: %d ", source, target);
|
||||||
|
|
||||||
|
//include the hash value if this is an XPT and if its IND data
|
||||||
|
if (dpf != 15 && is_xpt == 1 && gi == 0) fprintf (stderr, "Hash: %d ", tg_hash);
|
||||||
|
|
||||||
//sap string handling
|
//sap string handling
|
||||||
if (dpf == 15) sap = p_sap;
|
if (dpf == 15) sap = p_sap;
|
||||||
|
|
||||||
|
|
|
||||||
157
src/dmr_csbk.c
157
src/dmr_csbk.c
|
|
@ -615,13 +615,35 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
||||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
||||||
|
|
||||||
|
uint8_t target_hash[24];
|
||||||
|
uint8_t tg_hash = 0;
|
||||||
|
|
||||||
if (gi == 0) fprintf (stderr, "Individual ");
|
if (gi == 0) fprintf (stderr, "Individual ");
|
||||||
else fprintf (stderr, "Group ");
|
else fprintf (stderr, "Group ");
|
||||||
|
|
||||||
if (content == 0) fprintf (stderr, "CSBK - ");
|
if (content == 0) fprintf (stderr, "CSBK - ");
|
||||||
else fprintf (stderr, "Data - ");
|
else fprintf (stderr, "Data - ");
|
||||||
|
|
||||||
fprintf (stderr, "Target [%d] - Source [%d] ", target, source);
|
// check to see if this is XPT
|
||||||
|
if (strcmp (state->dmr_branding_sub, "XPT ") == 0)
|
||||||
|
{
|
||||||
|
//get 16-bit truncated target and source ids
|
||||||
|
target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 16); //40, or 32?
|
||||||
|
source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[64], 16); //56, or 64?
|
||||||
|
|
||||||
|
//check to see if this is indiv data (private) then we will need to report the hash value as well
|
||||||
|
if (gi == 0)
|
||||||
|
{
|
||||||
|
//the crc8 hash is the value represented in the Hytera XPT Site Status CSBK when dealing with indiv data
|
||||||
|
for (int i = 0; i < 16; i++) target_hash[i] = cs_pdu_bits[40+i];
|
||||||
|
tg_hash = crc8 (target_hash, 16);
|
||||||
|
fprintf (stderr, "Source: %d - Target: %d - Hash: %d ", source, target, tg_hash);
|
||||||
|
}
|
||||||
|
else fprintf (stderr, "Source: %d - Target: %d ", source, target);
|
||||||
|
|
||||||
|
}
|
||||||
|
else fprintf (stderr, "Source: %d - Target: %d ", source, target);
|
||||||
|
|
||||||
}
|
}
|
||||||
//end tier 2 csbks
|
//end tier 2 csbks
|
||||||
|
|
||||||
|
|
@ -780,7 +802,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
pch[i] = state->cap_plus_csbk_bits[i+48+(group_tally*8)];
|
pch[i] = state->cap_plus_csbk_bits[i+48+(group_tally*8)];
|
||||||
if (pch[i] == 1)
|
if (pch[i] == 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr, " Ch%d:", i+1);
|
fprintf (stderr, " LSN %d:", i+1);
|
||||||
private_target = (uint16_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[56+(k*16)+(group_tally*8)], 16);
|
private_target = (uint16_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[56+(k*16)+(group_tally*8)], 16);
|
||||||
fprintf (stderr, " TGT %d;", private_target);
|
fprintf (stderr, " TGT %d;", private_target);
|
||||||
if (opts->trunk_tune_data_calls == 1) t_tg[i] = private_target; //set here for tuning allow/block
|
if (opts->trunk_tune_data_calls == 1) t_tg[i] = private_target; //set here for tuning allow/block
|
||||||
|
|
@ -799,7 +821,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
k = 0;
|
k = 0;
|
||||||
for (i = 0; i < 8; i++)
|
for (i = 0; i < 8; i++)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "Ch%d: ", i+1);
|
fprintf (stderr, "LSN %d: ", i+1);
|
||||||
if (ch[i] == 1) //group voice channels
|
if (ch[i] == 1) //group voice channels
|
||||||
{
|
{
|
||||||
tg = (uint8_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[k*8+32], 8);
|
tg = (uint8_t)ConvertBitIntoBytes(&state->cap_plus_csbk_bits[k*8+32], 8);
|
||||||
|
|
@ -1034,7 +1056,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
//Hytera XPT
|
//Hytera XPT
|
||||||
if (csbk_fid == 0x68)
|
if (csbk_fid == 0x68)
|
||||||
{
|
{
|
||||||
//XPT Site Status -- Very Very Experimental, WIP, Don't be surprised by bad decodes or broken trunking
|
//XPT Site Status -- Experimental, WIP, Don't be surprised by bad decodes or broken trunking
|
||||||
if (csbk_o == 0x0A)
|
if (csbk_o == 0x0A)
|
||||||
{
|
{
|
||||||
//initial line break
|
//initial line break
|
||||||
|
|
@ -1053,17 +1075,19 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
uint8_t xpt_free = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 4); //free repeater channel
|
uint8_t xpt_free = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 4); //free repeater channel
|
||||||
uint8_t xpt_bank = 0;
|
uint8_t xpt_bank = 0;
|
||||||
|
|
||||||
|
//xpt_bank needs testing on busier systems/samples still to verify accuracy
|
||||||
if (xpt_seq) xpt_bank = xpt_seq*6;
|
if (xpt_seq) xpt_bank = xpt_seq*6;
|
||||||
|
|
||||||
//get 2-bit status values for each 6 channels (timeslots)
|
//get 2-bit status values for each 6 channels (timeslots)
|
||||||
for (i = 0; i < 6; i++) xpt_ch[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[20+(i*2)], 2);
|
for (i = 0; i < 6; i++) xpt_ch[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[20+(i*2)], 2);
|
||||||
|
|
||||||
fprintf (stderr, " Hytera XPT Site Status - Free RPT: %d SN: %d\n ", xpt_free, xpt_seq);
|
fprintf (stderr, " Hytera XPT Site Status - Free LCN: %d SN: %d \n ", xpt_free, xpt_seq);
|
||||||
|
|
||||||
//Print List of Channels and Activity
|
//Print List of Channels and Activity
|
||||||
for (i = 0; i < 6; i++)
|
for (i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "Ch%02d: ", i+xpt_bank+1); //(xpt_seq*6)
|
//LSN value here is logical slot, in flco, we get the logical channel (which is the repeater, does not include the slot value)
|
||||||
|
fprintf (stderr, "LSN %02d: ", i+xpt_bank+1); //swap out xpt_bank for all (xpt_seq*6) to simplify things
|
||||||
tg = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[i*8+32], 8);
|
tg = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[i*8+32], 8);
|
||||||
fprintf (stderr, "ST-%X", xpt_ch[i]); //status bits value 0,1,2, or 3
|
fprintf (stderr, "ST-%X", xpt_ch[i]); //status bits value 0,1,2, or 3
|
||||||
if (tg != 0) fprintf (stderr, " %03d ", tg);
|
if (tg != 0) fprintf (stderr, " %03d ", tg);
|
||||||
|
|
@ -1077,14 +1101,17 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
|
|
||||||
if (i == 2) fprintf (stderr, "\n ");
|
if (i == 2) fprintf (stderr, "\n ");
|
||||||
|
|
||||||
|
|
||||||
//add values to trunking tg/channel potentials
|
//add values to trunking tg/channel potentials
|
||||||
if (tg != 0) t_tg[i+(xpt_seq*6)] = tg;
|
if (tg != 0) t_tg[i+xpt_bank] = tg;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//debug ncurses terminal display
|
||||||
|
// sprintf (state->dmr_lrrp_gps[0], "XPT SN %d LSN: %02d TG: %03d; LSN: %02d TG: %03d; LSN: %02d TG: %03d;", xpt_seq, xpt_bank+1, t_tg[0], xpt_bank+2, t_tg[1], xpt_bank+3, t_tg[2]);
|
||||||
|
// sprintf (state->dmr_lrrp_gps[1], "XPT SN %d LSN: %02d TG: %03d; LSN: %02d TG: %03d; LSN: %02d TG: %03d;", xpt_seq, xpt_bank+4, t_tg[3], xpt_bank+5, t_tg[4], xpt_bank+6, t_tg[5]);
|
||||||
|
|
||||||
//add string for ncurses terminal display
|
//add string for ncurses terminal display
|
||||||
sprintf (state->dmr_site_parms, "Free RPT - %d ", xpt_free);
|
sprintf (state->dmr_site_parms, "Free LCN - %d ", xpt_free);
|
||||||
|
|
||||||
//assign to cc freq to follow during no sync
|
//assign to cc freq to follow during no sync
|
||||||
long int ccfreq = 0;
|
long int ccfreq = 0;
|
||||||
|
|
@ -1116,7 +1143,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
if (opts->trunk_tune_group_calls == 0) goto SKIPXPT;
|
if (opts->trunk_tune_group_calls == 0) goto SKIPXPT;
|
||||||
|
|
||||||
//don't tune if vc on the current channel
|
//don't tune if vc on the current channel
|
||||||
if ( (time(NULL) - state->last_vc_sync_time > 2) )
|
if ( (time(NULL) - state->last_vc_sync_time) > 2 ) //parenthesis error fixed
|
||||||
{
|
{
|
||||||
for (j = 0; j < 6; j++) //go through the channels stored looking for active ones to tune to
|
for (j = 0; j < 6; j++) //go through the channels stored looking for active ones to tune to
|
||||||
{
|
{
|
||||||
|
|
@ -1131,7 +1158,7 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
//and hope it doesn't clash with other normal TG values
|
//and hope it doesn't clash with other normal TG values
|
||||||
for (int i = 0; i < state->group_tally; i++)
|
for (int i = 0; i < state->group_tally; i++)
|
||||||
{
|
{
|
||||||
if (state->group_array[i].groupNumber == t_tg[j+(xpt_seq*6)])
|
if (state->group_array[i].groupNumber == t_tg[j+xpt_bank])
|
||||||
{
|
{
|
||||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||||
strcpy (mode, state->group_array[i].groupMode);
|
strcpy (mode, state->group_array[i].groupMode);
|
||||||
|
|
@ -1139,16 +1166,22 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
//without priority, this will tune the first one it finds (if group isn't blocked)
|
//without priority, this will tune the first one it finds (if group isn't blocked)
|
||||||
if (t_tg[j+(xpt_seq*6)+1] != 0 && state->p25_cc_freq != 0 && opts->p25_trunk == 1 && (strcmp(mode, "B") != 0) && (strcmp(mode, "DE") != 0))
|
if (t_tg[j+xpt_bank] != 0 && state->p25_cc_freq != 0 && opts->p25_trunk == 1 && (strcmp(mode, "B") != 0) && (strcmp(mode, "DE") != 0))
|
||||||
{
|
{
|
||||||
if (state->trunk_chan_map[j+(xpt_seq*6)+1] != 0) //if we have a valid frequency
|
//debug print for tuning verification
|
||||||
|
fprintf (stderr, "\n LSN/TG to tune to: %d - %d", j+xpt_bank+1, t_tg[j+xpt_bank]);
|
||||||
|
|
||||||
|
if (state->trunk_chan_map[j+xpt_bank+1] != 0) //if we have a valid frequency
|
||||||
{
|
{
|
||||||
//RIGCTL
|
//RIGCTL
|
||||||
if (opts->use_rigctl == 1)
|
if (opts->use_rigctl == 1)
|
||||||
{
|
{
|
||||||
|
//debug
|
||||||
|
fprintf (stderr, " - Freq: %ld", state->trunk_chan_map[j+xpt_bank+1]);
|
||||||
|
|
||||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
SetFreq(opts->rigctl_sockfd, state->trunk_chan_map[j+(xpt_seq*6)+1]);
|
SetFreq(opts->rigctl_sockfd, state->trunk_chan_map[j+xpt_bank+1]);
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+(xpt_seq*6)+1];
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+xpt_bank+1];
|
||||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||||
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away
|
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away
|
||||||
j = 11; //break loop
|
j = 11; //break loop
|
||||||
|
|
@ -1157,8 +1190,11 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
//rtl_udp
|
//rtl_udp
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
rtl_udp_tune (opts, state, state->trunk_chan_map[j+(xpt_seq*6)+1]);
|
//debug
|
||||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+(xpt_seq*6)+1];
|
fprintf (stderr, " - Freq: %ld", state->trunk_chan_map[j+xpt_bank+1]);
|
||||||
|
|
||||||
|
rtl_udp_tune (opts, state, state->trunk_chan_map[j+xpt_bank+1]);
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+xpt_bank+1];
|
||||||
opts->p25_is_tuned = 1;
|
opts->p25_is_tuned = 1;
|
||||||
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away
|
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away
|
||||||
j = 11; //break loop
|
j = 11; //break loop
|
||||||
|
|
@ -1174,13 +1210,10 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
|
|
||||||
sprintf (state->dmr_branding_sub, "XPT ");
|
sprintf (state->dmr_branding_sub, "XPT ");
|
||||||
|
|
||||||
//Trunking Note: My understanding is that XPT calls always start on the 'home' repeater, and then expand
|
//Notes: I had a few issues to fix in this CSBK, but it does appear that this is trunking on at least one system now,
|
||||||
//outward to the free repeaters, so its probably easiest for a user to load the frequencies in a csv file
|
//albeit a very quiet system that makes it difficult to know for certain if every aspect is working correctly
|
||||||
//and let FME scan through them as opposed to actively trunking, it will spend most of the time on the home repeater
|
|
||||||
//and if calls occur on the home repeater, it'll be too busy with those to go to the free repeater
|
|
||||||
//DSD-FME doesn't have any sort of TG priority, so its first come first served, just block or allow
|
|
||||||
|
|
||||||
//Additional Notes: I've set XPT to set a CC frequency to whichever frequency its tuned to currently and getting
|
//Notes: I've set XPT to set a CC frequency to whichever frequency its tuned to currently and getting
|
||||||
//this particular CSBK, if the status portion does work correctly, then it shouldn't matter which frequency it is on
|
//this particular CSBK, if the status portion does work correctly, then it shouldn't matter which frequency it is on
|
||||||
//as long as this CSBK comes in and we can tune to other repeater lcns if they have activity and the frequency mapping
|
//as long as this CSBK comes in and we can tune to other repeater lcns if they have activity and the frequency mapping
|
||||||
//is correct in the csv file, assuming the current frequency doesn't have voice activity
|
//is correct in the csv file, assuming the current frequency doesn't have voice activity
|
||||||
|
|
@ -1195,54 +1228,62 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
|
||||||
fprintf (stderr, " Hytera XPT CSBK 0x0B ");
|
|
||||||
|
|
||||||
//wraithe's theory -- seems pretty good to me
|
//wraithe's theory -- seems pretty good to me
|
||||||
//still only a theory though until can be verified
|
//still only a theory though until can be verified
|
||||||
//by having a system with precisely known variables
|
//by having a system with precisely known variables
|
||||||
|
|
||||||
//new samples show this doesn't always quite work, there seems to be
|
|
||||||
//more than one data set transmitted in this CSBK
|
|
||||||
//one set works, the other doesn't fit the pattern
|
|
||||||
|
|
||||||
// Site ID [5 bits]
|
// Site ID [5 bits]
|
||||||
// Unknown_3 [3 bits]
|
// Unknown_3 [3 bits]
|
||||||
// Free Repeater on site [4 bits]
|
// Free Repeater on site [4 bits]
|
||||||
// Unknown_4 [4 bits]
|
// Unknown_4 [4 bits]
|
||||||
|
|
||||||
// 11:44:58 Sync: +DMR slot1 [slot2] | Color Code=01 | CSBK
|
|
||||||
// Hytera XPT CSBK 0x0B
|
|
||||||
// DMR PDU Payload [0B][68][10][20][18][10][20][10][28][20][EF][DA]
|
|
||||||
|
|
||||||
// repeat x3 (for total of 4 sites)
|
// repeat x3 (for total of 4 sites)
|
||||||
|
|
||||||
// int i;
|
//new samples show this doesn't always quite work, there seems to be
|
||||||
// uint8_t xpt_site_id[4];
|
//more than one data set transmitted in this CSBK, when the first two bits aren't 0
|
||||||
// uint8_t xpt_site_rp[4];
|
//one set works, the other doesn't fit the pattern
|
||||||
// uint8_t xpt_site_u1[4];
|
|
||||||
// uint8_t xpt_site_u2[4];
|
|
||||||
|
|
||||||
// for (i = 0; i < 4; i++)
|
// 18:55:56 Sync: +DMR slot1 [slot2] | Color Code=01 | CSBK
|
||||||
// {
|
// Hytera XPT Site Id: 1 - Free LCN: 2
|
||||||
// xpt_site_id[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16+(i*16)], 5);
|
// XPT Adj Site(s): Site:4 Free:1; Site:2 Free:1; Site:5 Free:1;
|
||||||
// xpt_site_u1[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[21+(i*16)], 3);
|
// DMR PDU Payload [0B][68][08][20][20][10][10][10][28][10][F4][C0]
|
||||||
// xpt_site_rp[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[24+(i*16)], 4);
|
// 18:55:56 Sync: +DMR [slot1] slot2 | Color Code=01 | CSBK
|
||||||
// xpt_site_u2[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[28+(i*16)], 4);
|
// Hytera XPT CSBK 0x0B - SN: 1
|
||||||
// }
|
// DMR PDU Payload [4B][68][54][F0][00][00][00][00][00][00][E9][8F] <--site ID: 10 Free: 15 doesn't make sense compared to other data here
|
||||||
|
|
||||||
// fprintf (stderr, " Hytera XPT Site Id: %d - Free RPT: %d", xpt_site_id[0], xpt_site_rp[0]);
|
int i;
|
||||||
// // fprintf (stderr, " RS1: %d RS2: %d", xpt_site_u1[0], xpt_site_u2[1]); //debug
|
uint8_t xpt_site_id[4];
|
||||||
|
uint8_t xpt_site_rp[4];
|
||||||
|
uint8_t xpt_site_u1[4];
|
||||||
|
uint8_t xpt_site_u2[4];
|
||||||
|
|
||||||
// fprintf (stderr, "\n");
|
uint8_t xpt_sn = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[0], 2); //this is not the protect flag and reserved flag, could be a seq number of fl?
|
||||||
// fprintf (stderr, " XPT Adj Site(s): ");
|
uint8_t adj_flag = cs_pdu_bits[1]; //this is NOT the TS bit like in other XPT PDUs
|
||||||
// for (i = 1; i < 4; i++)
|
|
||||||
// {
|
for (i = 0; i < 4; i++)
|
||||||
// if (xpt_site_id[i] != 0)
|
{
|
||||||
// {
|
xpt_site_id[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16+(i*16)], 5);
|
||||||
// fprintf (stderr, "%d (%d) ", xpt_site_id[i], xpt_site_rp[i]);
|
xpt_site_u1[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[21+(i*16)], 3);
|
||||||
// // fprintf (stderr, "RS1: %d RS2: %d - ", xpt_site_u1[i], xpt_site_u2[i]); //debug
|
xpt_site_rp[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[24+(i*16)], 4);
|
||||||
// }
|
xpt_site_u2[i] = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[28+(i*16)], 4);
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
if (xpt_sn == 0) //for now, only run on 0, 1 could be appended data, but I don't think its adj_site related; could be other info;
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Hytera XPT Site Id: %d - Free LCN: %d", xpt_site_id[0], xpt_site_rp[0]);
|
||||||
|
// fprintf (stderr, " RS1: %d RS2: %d", xpt_site_u1[0], xpt_site_u2[1]); //debug
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " XPT Adj Site(s): ");
|
||||||
|
for (i = 1; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (xpt_site_id[i] != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Site:%d Free:%d; ", xpt_site_id[i], xpt_site_rp[i]);
|
||||||
|
// fprintf (stderr, "RS1: %d RS2: %d - ", xpt_site_u1[i], xpt_site_u2[i]); //debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else fprintf (stderr, " Hytera XPT CSBK 0x0B - SN: %d", xpt_sn);
|
||||||
|
|
||||||
sprintf (state->dmr_branding_sub, "XPT ");
|
sprintf (state->dmr_branding_sub, "XPT ");
|
||||||
|
|
||||||
|
|
|
||||||
100
src/dmr_flco.c
100
src/dmr_flco.c
|
|
@ -172,7 +172,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||||
|
|
||||||
//look for any Hytera XPT system, adjust TG to 16-bit allocation
|
//look for any Hytera XPT system, adjust TG to 16-bit allocation
|
||||||
//Groups use only 8 out of 16, but 16 always seems to be allocated
|
//Groups use only 8 out of 16, but 16 always seems to be allocated
|
||||||
//private calls use 16-bit target values hased to 8-bit in the site status csbk
|
//private calls use 16-bit target values hashed to 8-bit in the site status csbk
|
||||||
//the TLC preceeds the VLC for a 'handshake' call setup in XPT
|
//the TLC preceeds the VLC for a 'handshake' call setup in XPT
|
||||||
|
|
||||||
//truncate if XPT is set
|
//truncate if XPT is set
|
||||||
|
|
@ -186,11 +186,12 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||||
tg_hash = crc8 (target_hash, 16);
|
tg_hash = crc8 (target_hash, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
//XPT Call 'Grant' Setup Occurs in TLC (TermLC Handshake) with a flco 0x09
|
//XPT Call 'Grant/Alert' Setup Occurs in TLC (TermLC 'Handshake') with a flco 0x09
|
||||||
if (fid == 0x68 && flco == 0x09)
|
if (fid == 0x68 && flco == 0x09)
|
||||||
{
|
{
|
||||||
//The CSBK always uses an 8-bit TG; The White Papers (user manuals) say 8-bit TG and 16-bit SRC addressing
|
//The CSBK always uses an 8-bit TG/TGT; The White Papers (user manuals) say 8-bit TG and 16-bit SRC addressing
|
||||||
//It seems that private calls and indiv data calls use a hash of their 8 bit tgt values in the CSBK
|
//private calls and indiv data calls use a hash of their 8 bit tgt values in the CSBK
|
||||||
|
xpt_int = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 4); //This is consistent with an LCN value, not an LSN value
|
||||||
xpt_free = (uint8_t)ConvertBitIntoBytes(&lc_bits[24], 4); //24 and 4 on 0x09
|
xpt_free = (uint8_t)ConvertBitIntoBytes(&lc_bits[24], 4); //24 and 4 on 0x09
|
||||||
xpt_hand = (uint8_t)ConvertBitIntoBytes(&lc_bits[28], 4); //handshake kind: 0 - ordinary; 1-2 Interrupts; 3-15 reserved;
|
xpt_hand = (uint8_t)ConvertBitIntoBytes(&lc_bits[28], 4); //handshake kind: 0 - ordinary; 1-2 Interrupts; 3-15 reserved;
|
||||||
|
|
||||||
|
|
@ -198,86 +199,55 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||||
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16); //16-bit allocation
|
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16); //16-bit allocation
|
||||||
|
|
||||||
//the bits that are left behind
|
//the bits that are left behind
|
||||||
xpt_res_a = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 8); //Where the SVC bits would usually be
|
xpt_res_a = (uint8_t)ConvertBitIntoBytes(&lc_bits[20], 4); //after the xpt_int channel, before the free repeater channel
|
||||||
xpt_res_b = (uint8_t)ConvertBitIntoBytes(&lc_bits[48], 8); //where the first 8 bits of the SRC would be
|
xpt_res_b = (uint8_t)ConvertBitIntoBytes(&lc_bits[48], 8); //where the first 8 bits of the SRC would be
|
||||||
xpt_res_c = (uint8_t)ConvertBitIntoBytes(&lc_bits[72], 8); //some unknown 8 bit value after the SRC
|
xpt_res_c = (uint8_t)ConvertBitIntoBytes(&lc_bits[72], 8); //some unknown 8 bit value after the SRC
|
||||||
|
|
||||||
//speculation: 16,4 may be the current repeater channel this call will occur on? or the channel to interrupt?, could also be 8 bits?
|
|
||||||
xpt_int = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 4); //not sure about this value for this FLCO
|
|
||||||
|
|
||||||
//the crc8 hash is the value represented in the CSBK when dealing with private calls
|
//the crc8 hash is the value represented in the CSBK when dealing with private calls
|
||||||
for (int i = 0; i < 16; i++) target_hash[i] = lc_bits[32+i];
|
for (int i = 0; i < 16; i++) target_hash[i] = lc_bits[32+i];
|
||||||
tg_hash = crc8 (target_hash, 16); //This is a different poly, but using as placeholder
|
tg_hash = crc8 (target_hash, 16);
|
||||||
|
|
||||||
fprintf (stderr, "%s \n", KGRN);
|
fprintf (stderr, "%s \n", KGRN);
|
||||||
fprintf (stderr, " SLOT %d ", state->currentslot+1);
|
fprintf (stderr, " SLOT %d ", state->currentslot+1);
|
||||||
fprintf(stderr, "TGT=%u SRC=%u ", target, source);
|
|
||||||
|
|
||||||
// Here are some of the RIDs with hash address seen in that sample:
|
|
||||||
// 930 = 88
|
|
||||||
// 9317 = 198
|
|
||||||
// 10002 = 187
|
|
||||||
|
|
||||||
if (opts->payload == 1) fprintf(stderr, "HASH=%d ", tg_hash);
|
|
||||||
// if (opts->payload == 1) fprintf(stderr, "HSK [%X] ", xpt_hand);
|
|
||||||
|
|
||||||
// if (opts->payload == 1) fprintf(stderr, "CH=%d ", xpt_int);
|
|
||||||
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X ", flco, fid);
|
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X ", flco, fid);
|
||||||
|
fprintf (stderr, "TGT=%u SRC=%u ", target, source);
|
||||||
// if (opts->payload == 1) fprintf(stderr, "RS [%02X][%02X][%02X] ", xpt_res_a, xpt_res_b, xpt_res_c);
|
|
||||||
|
|
||||||
fprintf (stderr, "Hytera XPT ");
|
fprintf (stderr, "Hytera XPT ");
|
||||||
if (reserved == 1) fprintf (stderr, "Group "); //according to observation
|
if (reserved == 1) fprintf (stderr, "Group "); //according to observation
|
||||||
else fprintf (stderr, "Private "); //according to observation
|
else fprintf (stderr, "Private "); //according to observation
|
||||||
fprintf (stderr, "Call Alert "); //sounds more reasonable than 'protect'
|
fprintf (stderr, "Call Alert "); //Alert or Grant
|
||||||
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
//reorganized all the 'extra' data to a second line and added extra verbosity
|
||||||
fprintf (stderr, "F-Rpt %d", xpt_free);
|
if (opts->payload == 1) fprintf (stderr, "\n ");
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "%s", KYEL);
|
||||||
|
//only display the hashed tgt value if its a private call and not a group call
|
||||||
|
if (reserved == 0 && opts->payload == 1) fprintf(stderr, "TGT Hash=%d; ", tg_hash);
|
||||||
|
if (opts->payload == 1) fprintf(stderr, "HSK=%X; ", xpt_hand);
|
||||||
|
//extra verbosity on handshake type
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Handshake - ");
|
||||||
|
if (xpt_hand == 0) fprintf (stderr, "Ordinary; ");
|
||||||
|
else if (xpt_hand == 1) fprintf (stderr, "Callback/Alarm Interrupt; ");
|
||||||
|
else if (xpt_hand == 2) fprintf (stderr, "Release Channel Interrupt; ");
|
||||||
|
else fprintf (stderr, "Reserved; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
//logical repeater channel, not the logical slot value in the CSBK
|
||||||
|
if (opts->payload == 1) fprintf(stderr, "Call on LCN %d; ", xpt_int); //LCN channel call or 'interrupt' will occur on
|
||||||
|
// if (opts->payload == 1) fprintf(stderr, "RS A[%01X]B[%02X]C[%02X]; ", xpt_res_a, xpt_res_b, xpt_res_c); //leftover bits
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "Free LCN %d; ", xpt_free); //current free repeater LCN channel
|
||||||
fprintf (stderr, "%s ", KNRM);
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
|
||||||
//add string for ncurses terminal display
|
//add string for ncurses terminal display
|
||||||
sprintf (state->dmr_site_parms, "Free RPT - %d ", xpt_free);
|
sprintf (state->dmr_site_parms, "Free LCN - %d ", xpt_free);
|
||||||
|
|
||||||
is_xpt = 1;
|
is_xpt = 1;
|
||||||
goto END_FLCO;
|
goto END_FLCO;
|
||||||
}
|
}
|
||||||
|
|
||||||
//XPT 'Handshake' -- not observed as of yet on any samples
|
//Hytera XPT 'Others' -- moved the patent opcodes here as well for now
|
||||||
if ( fid == 0x68 && (flco == 0x2E || flco == 0x2F) )
|
if ( fid == 0x68 && (flco == 0x13 || flco == 0x2E || flco == 0x2F) )
|
||||||
{
|
|
||||||
|
|
||||||
//all below is according to the patent, but never observed on any samples I have
|
|
||||||
target = (uint32_t)ConvertBitIntoBytes(&lc_bits[24], 24); //24-bit values according to patent
|
|
||||||
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 24); //24-bit values according to patent
|
|
||||||
|
|
||||||
xpt_free = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 4); //16 and 4 on 2E and 2F
|
|
||||||
xpt_hand = (uint8_t)ConvertBitIntoBytes(&lc_bits[20], 4); //handshake kind: 0 - ordinary; 1-2 Interrupts; 3-15 reserved;
|
|
||||||
xpt_int = (uint8_t)ConvertBitIntoBytes(&lc_bits[24], 8); //Interrupt Designated Channel (No idea?)
|
|
||||||
|
|
||||||
fprintf (stderr, "%s \n", KGRN);
|
|
||||||
fprintf (stderr, " SLOT %d ", state->currentslot+1);
|
|
||||||
fprintf(stderr, "TGT=%u SRC=%u ", target, source);
|
|
||||||
|
|
||||||
if (opts->payload == 1) fprintf(stderr, "HASH=%d ", tg_hash);
|
|
||||||
// if (opts->payload == 1) fprintf(stderr, "HSK=[%X] ", xpt_hand);
|
|
||||||
|
|
||||||
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X ", flco, fid);
|
|
||||||
fprintf (stderr, "Hytera XPT ");
|
|
||||||
if (reserved == 1) fprintf (stderr, "Group ");
|
|
||||||
else fprintf (stderr, "Private ");
|
|
||||||
|
|
||||||
fprintf (stderr, "Call ");
|
|
||||||
if (flco == 0x2E) fprintf (stderr, "Request ");
|
|
||||||
if (flco == 0x2F) fprintf (stderr, "Response ");
|
|
||||||
|
|
||||||
fprintf (stderr, "%s ", KNRM);
|
|
||||||
is_xpt = 1;
|
|
||||||
goto END_FLCO;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Hytera XPT "something" -- seen on Embedded
|
|
||||||
if (fid == 0x68 && flco == 0x13)
|
|
||||||
{
|
{
|
||||||
if (type == 1) fprintf (stderr, "%s \n", KCYN);
|
if (type == 1) fprintf (stderr, "%s \n", KCYN);
|
||||||
if (type == 2) fprintf (stderr, "%s \n", KCYN);
|
if (type == 2) fprintf (stderr, "%s \n", KCYN);
|
||||||
|
|
@ -905,13 +875,13 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
||||||
else if (slco == 0x08)
|
else if (slco == 0x08)
|
||||||
{
|
{
|
||||||
//The Priority Repeater and Priority Hash values stem from SDRTrunk, but I've never seen these values not be zeroes
|
//The Priority Repeater and Priority Hash values stem from SDRTrunk, but I've never seen these values not be zeroes
|
||||||
// fprintf (stderr, " SLCO Hytera XPT - Free RPT %d - PRI RPT %d - PRI HASH: %02X", xpt_free, xpt_pri, xpt_hash);
|
fprintf (stderr, " SLCO Hytera XPT - Free LCN %d - PRI LCN %d - PRI HASH: %02X", xpt_free, xpt_pri, xpt_hash);
|
||||||
//NOTE: on really busy systems, this free repeater assignment can lag due to the 4 TS requirment to get SLC
|
//NOTE: on really busy systems, this free repeater assignment can lag due to the 4 TS requirment to get SLC
|
||||||
fprintf (stderr, " SLCO Hytera XPT - Free RPT %d ", xpt_free);
|
// fprintf (stderr, " SLCO Hytera XPT - Free LCN %d ", xpt_free);
|
||||||
sprintf (state->dmr_branding_sub, "XPT ");
|
sprintf (state->dmr_branding_sub, "XPT ");
|
||||||
|
|
||||||
//add string for ncurses terminal display
|
//add string for ncurses terminal display
|
||||||
sprintf (state->dmr_site_parms, "Free RPT - %d ", xpt_free);
|
sprintf (state->dmr_site_parms, "Free LCN - %d ", xpt_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
else fprintf (stderr, " SLCO Unknown - %d ", slco);
|
else fprintf (stderr, " SLCO Unknown - %d ", slco);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue