mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-09 15:27:22 -05:00
EDACS: Rewrite Standard/Networked parsing per TIA/EIA TSB 69.3 (#229)
* EDACS EA: refactor log messages for consistency * EDACS: Log LCN status values * EDACS EA: channel assignment vs update * EDACS: Implement TIA/EIA TSB 69.3 * EDACS: clean up Site ID message * EDACS: Actually process call for Channel Update * EDACS: Update display for Standard calls * EDACS: Format of Voice Group Channel Assignment * EDACS Standard: Log LCN status values * EDACS: Rebase and fix some issues * EDACS: Fix copy/paste error
This commit is contained in:
@@ -3490,9 +3490,9 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
for (i = 1; i <= state->edacs_lcn_count; i++)
|
||||
{
|
||||
//shim 443 afs in here for display purposes
|
||||
int a = (call_matrix[i][3] >> 7) & 0xF;
|
||||
int fs = call_matrix[i][3] & 0x7F;
|
||||
// Compute 4:4:3 AFS for display purposes only
|
||||
int a = (call_matrix[i][2] >> 7) & 0xF;
|
||||
int fs = call_matrix[i][2] & 0x7F;
|
||||
printw ("| - LCN [%02d][%.06lf] MHz", i, (double)state->trunk_lcn_freq[i-1]/1000000);
|
||||
|
||||
//print Control Channel on LCN line with the current Control Channel
|
||||
@@ -3519,7 +3519,15 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
else
|
||||
{
|
||||
printw (" AFS [%03lld][%02d-%03d]", call_matrix[i][3], a, fs );
|
||||
if (call_matrix[i][2] + 1 == 0)
|
||||
// System all-call
|
||||
printw (" TGT [SYSTEM][SYSTEM] SRC [%5lld] All-Call", call_matrix[i][3] );
|
||||
else if (call_matrix[i][2] > 10000)
|
||||
// I-Call
|
||||
printw (" TGT [%6lld][------] SRC [%5lld] I-Call", call_matrix[i][2] - 10000, call_matrix[i][3] );
|
||||
else
|
||||
// Group call
|
||||
printw (" TGT [%6lld][%02d-%03d] SRC [%5lld]", call_matrix[i][2], a, fs, call_matrix[i][3] );
|
||||
}
|
||||
for (int k = 0; k < state->group_tally; k++)
|
||||
{
|
||||
@@ -3555,7 +3563,15 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
else
|
||||
{
|
||||
printw (" AFS [%03lld][%02d-%03d]", call_matrix[i][3], a, fs );
|
||||
if (call_matrix[i][2] + 1 == 0)
|
||||
// System all-call
|
||||
printw (" TGT [SYSTEM][SYSTEM] SRC [%5lld] All-Call", call_matrix[i][3] );
|
||||
else if (call_matrix[i][2] > 10000)
|
||||
// I-Call
|
||||
printw (" TGT [%6lld][------] SRC [%5lld] I-Call", call_matrix[i][2] - 10000, call_matrix[i][3] );
|
||||
else
|
||||
// Group call
|
||||
printw (" TGT [%6lld][%02d-%03d] SRC [%5lld]", call_matrix[i][2], a, fs, call_matrix[i][3] );
|
||||
}
|
||||
for (int k = 0; k < state->group_tally; k++)
|
||||
{
|
||||
@@ -3691,9 +3707,18 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
else
|
||||
{
|
||||
int a = (call_matrix[j][3] >> 7) & 0xF;
|
||||
int fs = call_matrix[j][3] & 0x7F;
|
||||
printw (" AFS [%03lld][%02d-%03d]", call_matrix[j][3], a, fs );
|
||||
// Compute 4:4:3 AFS for display purposes only
|
||||
int a = (call_matrix[j][2] >> 7) & 0xF;
|
||||
int fs = call_matrix[j][2] & 0x7F;
|
||||
if (call_matrix[j][2] + 1 == 0)
|
||||
// System all-call
|
||||
printw ("Target [SYSTEM][SYSTEM] Source [%5lld] All-Call", call_matrix[j][3]);
|
||||
else if (call_matrix[j][2] > 10000)
|
||||
// I-Call
|
||||
printw ("Target [%6lld][------] Source [%5lld] I-Call", call_matrix[j][2] - 10000, call_matrix[j][3]);
|
||||
else
|
||||
// Group call
|
||||
printw ("Target [%6lld][%02d-%03d] Source [%5lld]", call_matrix[j][2], a, fs, call_matrix[j][3]);
|
||||
}
|
||||
//test
|
||||
for (int k = 0; k < state->group_tally; k++)
|
||||
|
||||
647
src/edacs-fme.c
647
src/edacs-fme.c
@@ -722,7 +722,6 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
else fprintf (stderr, " Digital Group Call");
|
||||
if (is_update == 0) fprintf (stderr, " Assignment");
|
||||
else fprintf (stderr, " Update");
|
||||
|
||||
fprintf (stderr, " :: Group [%05d] Source [%08d] LCN [%02d]%s", group, source, lcn, get_lcn_status_string(lcn));
|
||||
|
||||
//Trunking mode is correlated to (but not guaranteed to match) the type of call:
|
||||
@@ -983,10 +982,6 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
//Start Standard or Networked Mode
|
||||
else if (state->ea_mode == 0)
|
||||
{
|
||||
//TODO: migrate away from these legacy variables
|
||||
unsigned char command = (fr_1t & 0xFF00000000) >> 32;
|
||||
unsigned char lcn = (fr_1t & 0xF8000000) >> 27;
|
||||
|
||||
unsigned char mt_a = (fr_1t & 0xE000000000) >> 37;
|
||||
unsigned char mt_b = (fr_1t & 0x1C00000000) >> 34;
|
||||
unsigned char mt_d = (fr_1t & 0x3E0000000) >> 29;
|
||||
@@ -1017,161 +1012,541 @@ void edacs(dsd_opts * opts, dsd_state * state)
|
||||
}
|
||||
}
|
||||
|
||||
//site ID and CC LCN
|
||||
if (command == 0xFD)
|
||||
//The following is heavily based on TIA/EIA Telecommunications System Bulletin 69.3, "Enhanced Digital Access
|
||||
//Communications System (EDACS) Digital Air Interface for Channel Access, Modulation, Messages, and Formats",
|
||||
//April 1998. Where real world systems are found to diverge from this bulletin, please note the basis for the
|
||||
//deviation.
|
||||
|
||||
//Voice Group Channel Assignment (6.2.4.1)
|
||||
//Emergency Voice Group Channel Assignment (6.2.4.2)
|
||||
if (mt_a == 0x2 || mt_a == 0x3)
|
||||
{
|
||||
int site_id = (fr_1t & 0xFF000) >> 12;
|
||||
int cc_lcn = (fr_1t & 0x1F000000) >> 24;
|
||||
int is_emergency = (mt_a == 0x3) ? 1 : 0;
|
||||
int lid = ((fr_1t & 0x1FC0000000) >> 23) | ((fr_4t & 0xFE0000000) >> 29);
|
||||
int lcn = (fr_1t & 0x1F000000) >> 24;
|
||||
int is_tx_trunk = (fr_1t & 0x800000) >> 23;
|
||||
int group = (fr_1t & 0x7FF000) >> 12;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Site ID [%02X][%03d] on CC LCN [%02d] Standard/Networked", site_id, site_id, cc_lcn);
|
||||
fprintf (stderr, " Voice Group Channel Assignment :: Group [%04d] LID [%05d] LCN [%02d]%s", group, lid, lcn, get_lcn_status_string(lcn));
|
||||
if (is_tx_trunk == 0) fprintf (stderr, " [message trunking]");
|
||||
if (is_emergency == 1)
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " EMERGENCY");
|
||||
}
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
state->edacs_site_id = site_id;
|
||||
state->edacs_cc_lcn = cc_lcn;
|
||||
|
||||
if (state->edacs_cc_lcn > state->edacs_lcn_count && lcn < 26)
|
||||
{
|
||||
state->edacs_lcn_count = state->edacs_cc_lcn;
|
||||
}
|
||||
|
||||
//check for control channel lcn frequency if not provided in channel map or in the lcn list
|
||||
if (state->trunk_lcn_freq[state->edacs_cc_lcn-1] == 0)
|
||||
{
|
||||
long int lcnfreq = 0;
|
||||
//if using rigctl, we can ask for the currrent frequency
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
lcnfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||
if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq;
|
||||
}
|
||||
//if using rtl input, we can ask for the current frequency tuned
|
||||
if (opts->audio_in_type == 3)
|
||||
{
|
||||
lcnfreq = (long int)opts->rtlsdr_center_freq;
|
||||
if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq;
|
||||
}
|
||||
}
|
||||
|
||||
//set trunking cc here so we know where to come back to
|
||||
if (opts->p25_trunk == 1 && state->trunk_lcn_freq[state->edacs_cc_lcn-1] != 0)
|
||||
{
|
||||
state->p25_cc_freq = state->trunk_lcn_freq[state->edacs_cc_lcn-1]; //index starts at zero, lcn's locally here start at 1
|
||||
}
|
||||
|
||||
// TODO: Actually process the call
|
||||
}
|
||||
|
||||
//voice call assignment
|
||||
else if (command == 0xEE || command == 0xEF)
|
||||
//Data Call Channel Assignment (6.2.4.3)
|
||||
else if (mt_a == 0x5)
|
||||
{
|
||||
int is_individual_call = (fr_1t & 0x1000000000) >> 36;
|
||||
int is_from_lid = (fr_1t & 0x800000000) >> 35;
|
||||
int port = ((fr_1t & 0x700000000) >> 29) | ((fr_4t & 0x700000000) >> 32);
|
||||
int lcn = (fr_1t & 0xF8000000) >> 27;
|
||||
int is_individual_id = (fr_1t & 0x4000000) >> 26;
|
||||
int lid = (fr_1t & 0x3FFF000) >> 12;
|
||||
int group = (fr_1t & 0x7FF000) >> 12;
|
||||
|
||||
if (lcn > state->edacs_lcn_count && lcn < 26)
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Data Call Channel Assignment :: Type");
|
||||
if (is_individual_call == 1) fprintf (stderr, " [Individual]");
|
||||
else fprintf (stderr, " [Group]");
|
||||
if (is_individual_id == 1) fprintf (stderr, " LID [%05d]", lid);
|
||||
else fprintf (stderr, " Group [%04d]", group);
|
||||
if (is_from_lid == 1) fprintf (stderr, " -->");
|
||||
else fprintf (stderr, " <--");
|
||||
fprintf (stderr, " Port [%02d] LCN [%02d]%s", port, lcn, get_lcn_status_string(lcn));
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Login Acknowledge (6.2.4.4)
|
||||
else if (mt_a == 0x6)
|
||||
{
|
||||
int group = (fr_1t & 0x1FFC000000) >> 26;
|
||||
int lid = (fr_1t & 0x3FFF000) >> 12;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Login Acknowledgement :: Group [%04d] LID [%05d]", group, lid);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Use MT-B
|
||||
else if (mt_a == 0x7)
|
||||
{
|
||||
//Status Request / Message Acknowledge (6.2.4.5)
|
||||
if (mt_b == 0x0)
|
||||
{
|
||||
state->edacs_lcn_count = lcn;
|
||||
int status = (fr_1t & 0x3FC000000) >> 26;
|
||||
int lid = (fr_1t & 0x3FFF000) >> 12;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
if (status == 248) fprintf (stderr, " Status Request :: LID [%05d]", lid);
|
||||
else fprintf (stderr, " Message Acknowledgement :: Status [%03d] LID [%05d]", status, lid);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
state->edacs_vc_lcn = lcn;
|
||||
//just going to use the default 4-4-3 A-FS scheme, making it user configurable would be a pain
|
||||
int afs = (fr_1t & 0x7FF000) >> 12;
|
||||
int a = afs >> 7;
|
||||
int fs = afs & 0x7F;
|
||||
int status = (fr_1t & 0xF00000000) >> 32;
|
||||
UNUSED(status);
|
||||
if (afs > 0) state->lastsrc = afs;
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, " AFS [%03d] [%02d-%03d] LCN [%02d]", afs, a, fs, lcn);
|
||||
|
||||
char mode[8]; //allow, block, digital enc
|
||||
sprintf (mode, "%s", "");
|
||||
|
||||
//if we are using allow/whitelist mode, then write 'B' to mode for block
|
||||
//comparison below will look for an 'A' to write to mode if it is allowed
|
||||
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
||||
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
//Interconnect Channel Assignment (6.2.4.6)
|
||||
else if (mt_b == 0x1)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == afs)
|
||||
int mt_c = (fr_1t & 0x300000000) >> 32;
|
||||
int lcn = (fr_1t & 0xF8000000) >> 27;
|
||||
int is_individual_id = (fr_1t & 0x4000000) >> 26;
|
||||
int lid = (fr_1t & 0x3FFF000) >> 12;
|
||||
int group = (fr_1t & 0x7FF000) >> 12;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Interconnect Channel Assignment :: Type");
|
||||
if (mt_c == 0x2) fprintf (stderr, " [Voice]");
|
||||
else fprintf (stderr, " [Reserved]");
|
||||
if (is_individual_id == 1) fprintf (stderr, " LID [%05d]", lid);
|
||||
else fprintf (stderr, " Group [%04d]", group);
|
||||
fprintf (stderr, " LCN [%02d]%s", lcn, get_lcn_status_string(lcn));
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
// TODO: Actually process the call
|
||||
}
|
||||
//Channel Updates (6.2.4.7)
|
||||
else if (mt_b == 0x3)
|
||||
{
|
||||
int mt_c = (fr_1t & 0x300000000) >> 32;
|
||||
int lcn = (fr_1t & 0xF8000000) >> 27;
|
||||
int is_individual = (fr_1t & 0x4000000) >> 26;
|
||||
int is_emergency = (fr_1t & 0x2000000) >> 25;
|
||||
int group = (fr_1t & 0x7FF000) >> 12;
|
||||
int lid = ((fr_1t & 0x1FC0000) >> 11) | ((fr_4t & 0x7F000) >> 12);
|
||||
|
||||
//Abstract away to a target, and be sure to check whether it's an individual call later
|
||||
int target = (is_individual == 0) ? group : lid;
|
||||
|
||||
//Technically only MT-C 0x1 and 0x3 are defined in TSB 69.3 - using and extrapolating on legacy code
|
||||
int is_tx_trunk = (mt_c == 2 || mt_c == 3) ? 1 : 0;
|
||||
int is_digital = (mt_c == 1 || mt_c == 3) ? 1 : 0;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
if (is_individual == 0) fprintf (stderr, " Voice Group Channel Update ::");
|
||||
else fprintf (stderr, " Voice Individual Channel Update ::");
|
||||
if (is_digital == 0) fprintf (stderr, " Analog");
|
||||
else fprintf (stderr, " Digital");
|
||||
if (is_individual == 0) fprintf (stderr, " Group [%04d]", target);
|
||||
else fprintf (stderr, " LID [%05d]", target);
|
||||
fprintf (stderr, " LCN [%02d]%s", lcn, get_lcn_status_string(lcn));
|
||||
if (is_tx_trunk == 0) fprintf (stderr, " [message trunking]");
|
||||
if (is_emergency == 1)
|
||||
{
|
||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||
strcpy (mode, state->group_array[i].groupMode);
|
||||
break;
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " EMERGENCY");
|
||||
}
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc)
|
||||
if (lcn > state->edacs_lcn_count && lcn < 26)
|
||||
{
|
||||
state->edacs_lcn_count = lcn;
|
||||
}
|
||||
state->edacs_vc_lcn = lcn;
|
||||
|
||||
//If we have the same target already in progress, we can keep the source ID since we know it (and don't get
|
||||
//it in this CC message). But if we have a different target, we should clear out to a source ID of 0.
|
||||
if (is_individual == 0)
|
||||
{
|
||||
if (state->lasttg != target) {
|
||||
state->lasttg = target;
|
||||
state->lastsrc = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Use IDs > 10000 to represent i-call targets to differentiate from TGs
|
||||
if (state->lasttg != target + 10000) {
|
||||
state->lasttg = target + 10000;
|
||||
state->lastsrc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
char mode[8]; //allow, block, digital enc
|
||||
sprintf (mode, "%s", "");
|
||||
|
||||
//if we are using allow/whitelist mode, then write 'B' to mode for block
|
||||
//comparison below will look for an 'A' to write to mode if it is allowed
|
||||
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
||||
|
||||
//Individual calls always remain blocked if in allow/whitelist mode
|
||||
if (is_individual == 0)
|
||||
{
|
||||
//Get group mode for calls that are in the allow/whitelist
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == target)
|
||||
{
|
||||
strcpy (mode, state->group_array[i].groupMode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//TG hold on EDACS Standard/Net -- block non-matching target, allow matching group
|
||||
if (state->tg_hold != 0 && state->tg_hold != target) sprintf (mode, "%s", "B");
|
||||
if (state->tg_hold != 0 && state->tg_hold == target) sprintf (mode, "%s", "A");
|
||||
}
|
||||
|
||||
//NOTE: Restructured below so that analog and digital are handled the same, just that when
|
||||
//its analog, it will now start edacs_analog which will while loop analog samples until
|
||||
//signal level drops (RMS, or a dotting sequence is detected)
|
||||
|
||||
//this is working now with the new import setup
|
||||
if (((is_individual == 0 && opts->trunk_tune_group_calls == 1) || (is_individual == 1 && opts->trunk_tune_private_calls == 1)) &&
|
||||
opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block
|
||||
{
|
||||
if (lcn > 0 && lcn < 26 && state->edacs_cc_lcn != 0 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise)
|
||||
{
|
||||
//openwav file and do per call right here
|
||||
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
|
||||
{
|
||||
if (is_individual == 0) sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TG %04d SRC %05d.wav", getDateE(), timestr, state->edacs_site_id, target, state->lastsrc);
|
||||
else sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TGT %05d SRC %05d I-Call.wav", getDateE(), timestr, state->edacs_site_id, target, state->lastsrc);
|
||||
if (is_digital == 0) openWavOutFile48k (opts, state); //analog at 48k
|
||||
else openWavOutFile (opts, state); //digital
|
||||
}
|
||||
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
//only set bandwidth IF we have an original one to fall back to (experimental, but requires user to set the -B 12000 or -B 24000 value manually)
|
||||
if (opts->setmod_bw != 0)
|
||||
{
|
||||
if (is_digital == 0) SetModulation(opts->rigctl_sockfd, 7000); //narrower bandwidth, but has issues with dotting sequence
|
||||
else SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||
}
|
||||
|
||||
SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[lcn-1]); //minus one because our index starts at zero
|
||||
state->edacs_tuned_lcn = lcn;
|
||||
opts->p25_is_tuned = 1;
|
||||
if (is_digital == 0) edacs_analog(opts, state, target, lcn);
|
||||
}
|
||||
|
||||
if (opts->audio_in_type == 3) //rtl dongle
|
||||
{
|
||||
#ifdef USE_RTLSDR
|
||||
rtl_dev_tune (opts, state->trunk_lcn_freq[lcn-1]);
|
||||
state->edacs_tuned_lcn = lcn;
|
||||
opts->p25_is_tuned = 1;
|
||||
if (is_digital == 0) edacs_analog(opts, state, target, lcn);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//System Assigned ID (6.2.4.8)
|
||||
else if (mt_b == 0x4)
|
||||
{
|
||||
int sgid = (fr_1t & 0x3FF800000) >> 23;
|
||||
int group = (fr_1t & 0x7FF000) >> 12;
|
||||
|
||||
//TG hold on EDACS Standard/Net -- block non-matching target, allow matching afs (decimal)
|
||||
if (state->tg_hold != 0 && state->tg_hold != afs) sprintf (mode, "%s", "B");
|
||||
if (state->tg_hold != 0 && state->tg_hold == afs) sprintf (mode, "%s", "A");
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " System Assigned ID :: SGID [%04d] Group [%04d]", sgid, group);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Individual Call Channel Assignment (6.2.4.9)
|
||||
else if (mt_b == 0x5)
|
||||
{
|
||||
int is_tx_trunk = (fr_1t & 0x200000000) >> 33;
|
||||
int lcn = (fr_1t & 0xF8000000) >> 27;
|
||||
int call_type = (fr_1t & 0x4000000) >> 26;
|
||||
int target = (fr_1t & 0x3FFF000) >> 12;
|
||||
int source = (fr_4t & 0x3FFF000) >> 12;
|
||||
|
||||
//NOTE: Restructured below so that analog and digital are handled the same, just that when
|
||||
//its analog, it will now start edacs_analog which will while loop analog samples until
|
||||
//signal level drops (RMS, or a dotting sequence is detected)
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Individual Call Channel Assignment :: Type");
|
||||
if (call_type == 1) fprintf (stderr, " [Voice]");
|
||||
else fprintf (stderr, " [Reserved]");
|
||||
fprintf (stderr, " Caller [%05d] Callee [%05d] LCN [%02d]%s", source, target, lcn, get_lcn_status_string(lcn));
|
||||
if (is_tx_trunk == 0) fprintf (stderr, " [message trunking]");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//analog working now with edacs_analog
|
||||
if (command == 0xEE)
|
||||
fprintf (stderr, " Analog");
|
||||
// TODO: Actually process the call
|
||||
}
|
||||
//Console Unkey / Drop (6.2.4.10)
|
||||
else if (mt_b == 0x6)
|
||||
{
|
||||
int is_drop = (fr_1t & 0x80000000) >> 31;
|
||||
int lcn = (fr_1t & 0x7C000000) >> 26;
|
||||
int lid = (fr_1t & 0x3FFF000) >> 12;
|
||||
|
||||
//Digital Call (ProVoice, hopefully, and not Aegis in 2022)
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Console ");
|
||||
if (is_drop == 0) fprintf (stderr, " Unkey");
|
||||
else fprintf (stderr, " Drop");
|
||||
fprintf (stderr, " :: LID [%05d] LCN [%02d]%s", lid, lcn, get_lcn_status_string(lcn));
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Use MT-D
|
||||
else if (mt_b == 0x7)
|
||||
{
|
||||
//Cancel Dynamic Regroup (6.2.4.11)
|
||||
if (mt_d == 0x00)
|
||||
{
|
||||
int knob = (fr_1t & 0x1C000000) >> 26;
|
||||
int lid = (fr_1t & 0x3FFF000) >> 12;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Cancel Dynamic Regroup :: LID [%05d] Knob position [%1d]", lid, knob + 1);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Adjacent Site Control Channel (6.2.4.12)
|
||||
else if (mt_d == 0x01)
|
||||
{
|
||||
int adj_cc_lcn = (fr_1t & 0x1F000000) >> 24;
|
||||
int adj_site_index = (fr_1t & 0xE00000) >> 21;
|
||||
int adj_site_id = (fr_1t & 0x1F0000) >> 16;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Adjacent Site Control Channel :: Site ID [%02X][%03d] Index [%1d] LCN [%02d]%s", adj_site_id, adj_site_id, adj_site_index, adj_cc_lcn, get_lcn_status_string(adj_cc_lcn));
|
||||
if (adj_site_id == 0 && adj_site_index == 0) fprintf (stderr, " [Adjacency Table Reset]");
|
||||
else if (adj_site_id != 0 && adj_site_index == 0) fprintf (stderr, " [Priority System Definition]");
|
||||
else if (adj_site_id == 0 && adj_site_index != 0) fprintf (stderr, " [Adjacencies Table Length Definition]");
|
||||
else fprintf (stderr, " [Adjacent System Definition]");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Extended Site Options (6.2.4.13)
|
||||
else if (mt_d == 0x02)
|
||||
{
|
||||
int msg_num = (fr_1t & 0xE000000) >> 25;
|
||||
int data = (fr_1t & 0x1FFF000) >> 12;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Extended Site Options :: Message Num [%1d] Data [%04X]", msg_num, data);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//System Dynamic Regroup Plan Bitmap (6.2.4.14)
|
||||
else if (mt_d == 0x04)
|
||||
{
|
||||
int bank = (fr_1t & 0x10000000) >> 28;
|
||||
int resident = (fr_1t & 0xFF00000) >> 20;
|
||||
int active = (fr_1t & 0xFF000) >> 12;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " System Dynamic Regroup Plan Bitmap :: Plan Bank [%1d] Resident [", bank);
|
||||
|
||||
int plan = bank * 8;
|
||||
int first = 1;
|
||||
while (resident != 0) {
|
||||
if (resident & 0x1 == 1) {
|
||||
if (first == 1)
|
||||
{
|
||||
first = 0;
|
||||
fprintf (stderr, "%d", plan);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, ", %d", plan);
|
||||
}
|
||||
}
|
||||
resident >>= 1;
|
||||
plan++;
|
||||
}
|
||||
|
||||
fprintf (stderr, "] Active [");
|
||||
|
||||
plan = bank * 8;
|
||||
first = 1;
|
||||
while (active != 0) {
|
||||
if (active & 0x1 == 1) {
|
||||
if (first == 1)
|
||||
{
|
||||
first = 0;
|
||||
fprintf (stderr, "%d", plan);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, ", %d", plan);
|
||||
}
|
||||
}
|
||||
active >>= 1;
|
||||
plan++;
|
||||
}
|
||||
|
||||
fprintf (stderr, "]");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Assignment to Auxiliary Control Channel (6.2.4.15)
|
||||
else if (mt_d == 0x05)
|
||||
{
|
||||
int aux_cc_lcn = (fr_1t & 0x1F000000) >> 24;
|
||||
int group = (fr_1t & 0x7FF000) >> 12;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Assignment to Auxiliary CC :: Group [%04d] Aux CC LCN [%02d]%s", group, aux_cc_lcn, get_lcn_status_string(aux_cc_lcn));
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Initiate Test Call Command (6.2.4.16)
|
||||
else if (mt_d == 0x06)
|
||||
{
|
||||
int cc_lcn = (fr_1t & 0x1F000000) >> 24;
|
||||
int wc_lcn = (fr_1t & 0xF80000) >> 19;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Initiate Test Call Command :: CC LCN [%02d] WC LCN [%02d]", cc_lcn, wc_lcn);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Unit Enable / Disable (6.2.4.17)
|
||||
else if (mt_d == 0x07)
|
||||
{
|
||||
int qualifier = (fr_1t & 0xC000000) >> 26;
|
||||
int target = (fr_1t & 0x3FFF000) >> 12;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Unit Enable/Disable ::");
|
||||
if (qualifier == 0x0) fprintf (stderr, " [Temporary Disable]");
|
||||
else if (qualifier == 0x1) fprintf (stderr, " [Corrupt Personality]");
|
||||
else if (qualifier == 0x2) fprintf (stderr, " [Revoke Logical ID]");
|
||||
else fprintf (stderr, " [Re-enable Unit]");
|
||||
fprintf (stderr, " LID [%05d]", target);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Site ID (6.2.4.18)
|
||||
else if (mt_d == 0x08 || mt_d == 0x09 || mt_d == 0x0A || mt_d == 0x0B)
|
||||
{
|
||||
int cc_lcn = (fr_1t & 0x1F000000) >> 24;
|
||||
int priority = (fr_1t & 0xE00000) >> 21;
|
||||
int is_scat = (fr_1t & 0x80000) >> 19;
|
||||
int is_failsoft = (fr_1t & 0x40000) >> 18;
|
||||
int is_auxiliary = (fr_1t & 0x20000) >> 17;
|
||||
int site_id = (fr_1t & 0x1F000) >> 12;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Standard/Networked :: Site ID [%02X][%03d] Priority [%1d] CC LCN [%02d]%s", site_id, site_id, priority, cc_lcn, get_lcn_status_string(lcn));
|
||||
if (is_failsoft == 1)
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " [FAILSOFT]");
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
}
|
||||
if (is_scat == 1) fprintf (stderr, " [SCAT]");
|
||||
if (is_auxiliary == 1) fprintf (stderr, " [Auxiliary]");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//Store our site ID
|
||||
state->edacs_site_id = site_id;
|
||||
|
||||
//LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc)
|
||||
if (state->edacs_cc_lcn > state->edacs_lcn_count && cc_lcn < 26)
|
||||
{
|
||||
state->edacs_lcn_count = state->edacs_cc_lcn;
|
||||
}
|
||||
|
||||
//If this is only an auxiliary CC, keep searching for the primary CC
|
||||
if (is_auxiliary == 0)
|
||||
{
|
||||
//Store our CC LCN
|
||||
state->edacs_cc_lcn = cc_lcn;
|
||||
|
||||
//Check for control channel LCN frequency if not provided in channel map or in the LCN list
|
||||
if (state->trunk_lcn_freq[state->edacs_cc_lcn - 1] == 0)
|
||||
{
|
||||
//If using rigctl, we can ask for the currrent frequency
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
long int lcnfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||
if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn - 1] = lcnfreq;
|
||||
}
|
||||
|
||||
//If using rtl input, we can ask for the current frequency tuned
|
||||
if (opts->audio_in_type == 3)
|
||||
{
|
||||
long int lcnfreq = (long int)opts->rtlsdr_center_freq;
|
||||
if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn - 1] = lcnfreq;
|
||||
}
|
||||
}
|
||||
|
||||
//Set trunking CC here so we know where to come back to
|
||||
if (opts->p25_trunk == 1 && state->trunk_lcn_freq[state->edacs_cc_lcn - 1] != 0)
|
||||
{
|
||||
//Index starts at zero, LCNs locally here start at 1
|
||||
state->p25_cc_freq = state->trunk_lcn_freq[state->edacs_cc_lcn - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
//System All-Call (6.2.4.19)
|
||||
else if (mt_d == 0x0F)
|
||||
{
|
||||
int lcn = (fr_1t & 0x1F000000) >> 24;
|
||||
int qualifier = (fr_1t & 0x800000) >> 23;
|
||||
int is_update = (fr_1t & 0x400000) >> 22;
|
||||
int is_tx_trunk = (fr_1t & 0x200000) >> 21;
|
||||
int lid = ((fr_1t & 0x7F000) >> 5) | ((fr_4t & 0x7F000) >> 12);
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " System All-Call Channel");
|
||||
if (is_update == 0) fprintf (stderr, " Assignment");
|
||||
else fprintf (stderr, " Update");
|
||||
fprintf (stderr, " ::");
|
||||
if (qualifier == 1) fprintf (stderr, " [Voice]");
|
||||
else fprintf (stderr, " [Reserved]");
|
||||
fprintf (stderr, " LID [%05d] LCN [%02d]%s", lid, lcn, get_lcn_status_string(lcn));
|
||||
if (is_tx_trunk == 0) fprintf (stderr, " [message trunking]");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
// TODO: Actually process the call
|
||||
}
|
||||
//Dynamic Regrouping (6.2.4.20)
|
||||
else if (mt_d == 0x10)
|
||||
{
|
||||
int fleet_bits = (fr_1t & 0x1C000000) >> 26;
|
||||
int lid = (fr_1t & 0x3FFF000) >> 12;
|
||||
int plan = (fr_4t & 0x1E0000000) >> 29;
|
||||
int type = (fr_4t & 0x18000000) >> 27;
|
||||
int knob = (fr_4t & 0x7000000) >> 24;
|
||||
int group = (fr_4t & 0x7FF000) >> 12;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Dynamic Regrouping :: Plan [%02d] Knob position [%1d] LID [%05d] Group [%04d] Fleet bits [%1d]", plan, knob + 1, lid, group, fleet_bits);
|
||||
if (type == 0) fprintf (stderr, " [Forced select, no deselect]");
|
||||
else if (type == 1) fprintf (stderr, " [Forced select, optional deselect]");
|
||||
else if (type == 2) fprintf (stderr, " [Reserved]");
|
||||
else fprintf (stderr, " [Optional select]");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Reserved command (MT-D)
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KMAG);
|
||||
fprintf (stderr, " Reserved Command (MT-D)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
// Only print the payload if we haven't already printed it
|
||||
if (opts->payload != 1)
|
||||
{
|
||||
fprintf (stderr, " ::");
|
||||
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Reserved command (MT-B)
|
||||
else
|
||||
fprintf (stderr, " Digital");
|
||||
|
||||
//skip analog calls on RTL Input on ARM devices due to High CPU usage from RTL RMS function
|
||||
// #ifdef __arm__
|
||||
// if (command == 0xEE && opts->audio_in_type == 3)
|
||||
// goto ENDPV;
|
||||
// #endif
|
||||
|
||||
//this is working now with the new import setup
|
||||
if (opts->trunk_tune_group_calls == 1 && opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block
|
||||
{
|
||||
if (lcn > 0 && lcn < 26 && state->edacs_cc_lcn != 0 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise)
|
||||
fprintf (stderr, "%s", KMAG);
|
||||
fprintf (stderr, " Reserved Command (MT-B)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
// Only print the payload if we haven't already printed it
|
||||
if (opts->payload != 1)
|
||||
{
|
||||
//openwav file and do per call right here
|
||||
if (opts->dmr_stereo_wav == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3))
|
||||
{
|
||||
sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld AFS %02d-%03d - %03d.wav", getDateE(), timestr, state->edacs_site_id, a, fs, afs);
|
||||
if (command == 0xEF) openWavOutFile (opts, state); //digital
|
||||
if (command == 0xEE) openWavOutFile48k (opts, state); //analog at 48k
|
||||
}
|
||||
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
//only set bandwidth IF we have an original one to fall back to (experimental, but requires user to set the -B 12000 or -B 24000 value manually)
|
||||
if (opts->setmod_bw != 0 && command == 0xEE) SetModulation(opts->rigctl_sockfd, 7000); //narrower bandwidth, but has issues with dotting sequence
|
||||
else 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_lcn_freq[lcn-1]); //minus one because our index starts at zero
|
||||
state->edacs_tuned_lcn = lcn;
|
||||
opts->p25_is_tuned = 1;
|
||||
if (command == 0xEE) edacs_analog(opts, state, afs, lcn);
|
||||
}
|
||||
|
||||
if (opts->audio_in_type == 3) //rtl dongle
|
||||
{
|
||||
#ifdef USE_RTLSDR
|
||||
rtl_dev_tune (opts, state->trunk_lcn_freq[lcn-1]);
|
||||
state->edacs_tuned_lcn = lcn;
|
||||
opts->p25_is_tuned = 1;
|
||||
if (command == 0xEE) edacs_analog(opts, state, afs, lcn);
|
||||
#endif
|
||||
}
|
||||
fprintf (stderr, " ::");
|
||||
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//Reserved command (MT-A)
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KMAG);
|
||||
fprintf (stderr, " Reserved Command (MT-A)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
//Network Command, Not sure of how to handle, but just show frames
|
||||
else if (command == 0xF3)
|
||||
{
|
||||
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||
fprintf (stderr, " Network Command");
|
||||
}
|
||||
|
||||
else //print frames for debug/analysis
|
||||
{
|
||||
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||
fprintf (stderr, " Unknown Command");
|
||||
// Only print the payload if we haven't already printed it
|
||||
if (opts->payload != 1)
|
||||
{
|
||||
fprintf (stderr, " ::");
|
||||
fprintf (stderr, " FR_1 [%010llX]", fr_1t);
|
||||
fprintf (stderr, " FR_4 [%010llX]", fr_4t);
|
||||
}
|
||||
}
|
||||
|
||||
} //end Standard or Networked
|
||||
|
||||
Reference in New Issue
Block a user