EDACS: Standard display rewrite, parsing/RE fixes, tune i-calls and all-calls (#246)

* EDACS EA: Fix reversed TDMA/data calls

* EDACS: deduplicate ncurses printing logic

* EDACS: pass call state via new state struct field

* EDACS: RE some differences from Standard spec

* EDACS: show state for data calls

* EDACS: tune individual calls on Standard

* EDACS: tune all-calls on Standard

* EDACS: properly identify emergency on channel updates

* EDACS: fix build break

* EDACS: fix conditions for refreshing the display source

* EDACS EA: Identify calls as voice for display

* EDACS: voice channel update emergency refactor

* EDACS: Show interconnect calls in call log

* EDACS EA: show unknown data channel assignments in log

* EDACS: check LCN is not 0 before assigning it

* EDACS: C operator precedence is dumb

* Initialize edacs_vc_call_type

* use j and not i in call_matrix for call history;

---------

Co-authored-by: lwvmobile <lwvmobile@gmail.com>
This commit is contained in:
ilyacodes 2024-03-26 17:28:35 -04:00 committed by GitHub
parent c8d00846b1
commit a5c5be3b95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 389 additions and 117 deletions

View File

@ -702,6 +702,16 @@ typedef struct
int edacs_cc_lcn; //current lcn for the edacs control channel
int edacs_vc_lcn; //current lcn for any active vc (not the one we are tuned/tuning to)
int edacs_tuned_lcn; //the vc we are currently tuned to...above variable is for updating all in the matrix
int edacs_vc_call_type; //the type of call on the given VC - see defines below
//flags for EDACS call type
#define EDACS_IS_VOICE 0x01
#define EDACS_IS_DIGITAL 0x02
#define EDACS_IS_EMERGENCY 0x04
#define EDACS_IS_GROUP 0x08
#define EDACS_IS_INDIVIDUAL 0x10
#define EDACS_IS_ALL_CALL 0x20
#define EDACS_IS_INTERCONNECT 0x40
//trunking group and lcn freq list
long int trunk_lcn_freq[26]; //max number on an EDACS system, should be enough on DMR too hopefully

View File

@ -1047,7 +1047,7 @@ initState (dsd_state * state)
//edacs - may need to make these user configurable instead for stability on non-ea systems
state->ea_mode = -1; //init on -1, 0 is standard, 1 is ea
state->edacs_vc_call_type = 0;
state->esk_mask = 0x0; //esk mask value
state->edacs_site_id = 0;
state->edacs_lcn_count = 0;

View File

@ -2012,13 +2012,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
call_matrix[state->edacs_vc_lcn][1] = state->edacs_vc_lcn;
call_matrix[state->edacs_vc_lcn][2] = state->lasttg;
//EDACS standard does not provide source LIDs on channel update messages; instead, for the sake of display, let's
//assume the prior source for a given LCN is still accurate, unless we have an updated one provided.
//assume the prior source for a given LCN is still accurate, unless we have an updated one provided (or the call
//type has changed under us).
//
//If you MUST have perfectly-accurate source LIDs, look at the logged CC messages yourself - incorrect source LIDs
//may be displayed if we miss an initial call channel assignment.
if (state->ea_mode == 1 || state->lastsrc != 0)
if (state->ea_mode == 1 || (state->lastsrc != 0 || call_matrix[state->edacs_vc_lcn][4] != state->edacs_vc_call_type))
call_matrix[state->edacs_vc_lcn][3] = state->lastsrc;
call_matrix[state->edacs_vc_lcn][4] = 1;
call_matrix[state->edacs_vc_lcn][4] = state->edacs_vc_call_type;
call_matrix[state->edacs_vc_lcn][5] = time(NULL);
}
@ -3511,76 +3512,81 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw (" Control Channel");
attroff (COLOR_PAIR(1));
}
int print_call = 0;
//print active calls on corresponding LCN line
if ((i != state->edacs_cc_lcn) && time(NULL) - call_matrix[i][5] < 2)
{
print_call = 3;
attron (COLOR_PAIR(3));
if (state->ea_mode == 1) {
if (call_matrix[i][2] + 1 == 0)
// System all-call
printw (" TGT [ SYSTEM ] SRC [%8lld] All-Call", call_matrix[i][3] );
else if (call_matrix[i][2] > 100000)
// I-Call
printw (" TGT [%8lld] SRC [%8lld] I-Call", call_matrix[i][2] - 100000, call_matrix[i][3] );
else
// Group call
printw (" TGT [%8lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] );
}
else
{
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][ UNIT ] 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++)
{
if (state->group_array[k].groupNumber == call_matrix[i][2] && call_matrix[i][2] != 0)
{
printw (" [%s]", state->group_array[k].groupName);
printw ("[%s]", state->group_array[k].groupMode);
break;
}
else if (state->group_array[k].groupNumber == call_matrix[i][3] && call_matrix[i][3] != 0)
{
printw (" [%s]", state->group_array[k].groupName);
printw ("[%s]", state->group_array[k].groupMode);
break;
}
}
attroff (COLOR_PAIR(3));
}
//print dying or dead calls in red for x seconds longer
if ( (i != state->edacs_cc_lcn) && (time(NULL) - call_matrix[i][5] >= 2) && (time(NULL) - call_matrix[i][5] < 5) )
else if ( (i != state->edacs_cc_lcn) && (time(NULL) - call_matrix[i][5] >= 2) && (time(NULL) - call_matrix[i][5] < 5) )
{
print_call = 2;
attron (COLOR_PAIR(2));
}
if (print_call != 0)
{
if (state->ea_mode == 1) {
if (call_matrix[i][2] + 1 == 0)
// Voice call
if ((call_matrix[i][4] & EDACS_IS_VOICE) != 0)
{
// System all-call
printw (" TGT [ SYSTEM ] SRC [%8lld] All-Call", call_matrix[i][3] );
else if (call_matrix[i][2] > 100000)
if ((call_matrix[i][4] & EDACS_IS_GROUP) != 0)
printw (" TGT [%8lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] );
// I-Call
printw (" TGT [%8lld] SRC [%8lld] I-Call", call_matrix[i][2] - 100000, call_matrix[i][3] );
else if ((call_matrix[i][4] & EDACS_IS_INDIVIDUAL) != 0)
printw (" TGT [%8lld] SRC [%8lld] I-Call", call_matrix[i][2], call_matrix[i][3] );
// System all-call
else if ((call_matrix[i][4] & EDACS_IS_ALL_CALL) != 0)
printw (" TGT [ SYSTEM ] SRC [%8lld] All-Call", call_matrix[i][3] );
// Interconnect call
else if ((call_matrix[i][4] & EDACS_IS_INTERCONNECT) != 0)
printw (" TGT [ SYSTEM ] SRC [%8lld] Interconnect", call_matrix[i][3] );
// Unknown call
else
printw (" Unknown call type" );
// Call flags
if ((call_matrix[i][4] & EDACS_IS_DIGITAL) == 0) printw (" [Ana]");
else printw (" [Dig]");
if ((call_matrix[i][4] & EDACS_IS_EMERGENCY) != 0) printw ("[EM]");
}
else
// Group call
printw (" TGT [%8lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] );
// Data call
printw (" TGT [ DATA ] SRC [%8lld] Data", call_matrix[i][3] );
}
else
{
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][ UNIT ] SRC [%5lld] I-Call", call_matrix[i][2] - 10000, call_matrix[i][3] );
else
// Voice call
if ((call_matrix[i][4] & EDACS_IS_VOICE) != 0)
{
// Group call
printw (" TGT [%6lld][%02d-%03d] SRC [%5lld]", call_matrix[i][2], a, fs, call_matrix[i][3] );
if ((call_matrix[i][4] & EDACS_IS_GROUP) != 0)
printw (" TGT [%6lld][%02d-%03d] SRC [%5lld]", call_matrix[i][2], a, fs, call_matrix[i][3] );
// I-Call
else if ((call_matrix[i][4] & EDACS_IS_INDIVIDUAL) != 0)
printw (" TGT [%6lld][ UNIT ] SRC [%5lld] I-Call", call_matrix[i][2], call_matrix[i][3] );
// System all-call
else if ((call_matrix[i][4] & EDACS_IS_ALL_CALL) != 0)
printw (" TGT [ SYSTEM ] SRC [%5lld] All-Call", call_matrix[i][3] );
// Interconnect call
else if ((call_matrix[i][4] & EDACS_IS_INTERCONNECT) != 0)
printw (" TGT [ SYSTEM ] SRC [%5lld] Interconnect", call_matrix[i][3] );
// Unknown call
else
printw (" Unknown call type" );
// Call flags
if ((call_matrix[i][4] & EDACS_IS_DIGITAL) == 0) printw (" [Ana]");
else printw (" [Dig]");
if ((call_matrix[i][4] & EDACS_IS_EMERGENCY) != 0) printw ("[EM]");
}
else
// Data call
printw (" TGT [ DATA ] SRC [%8lld] Data", call_matrix[i][3] );
}
for (int k = 0; k < state->group_tally; k++)
{
@ -3597,9 +3603,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
break;
}
}
attroff (COLOR_PAIR(2));
if (print_call == 3)
attroff (COLOR_PAIR(3));
else if (print_call == 2)
attroff (COLOR_PAIR(2));
}
if (i == state->edacs_tuned_lcn && opts->p25_is_tuned == 1) printw (" **T**"); //asterisk which lcn is opened
if (i == state->edacs_tuned_lcn && opts->p25_is_tuned == 1) printw (" **T**"); //asterisk which lcn is tuned
printw ("\n");
}
if (state->carrier == 1)
@ -3704,30 +3715,67 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
printw ("LCN [%2lld] ", call_matrix[j][1]);
if (state->ea_mode == 1)
{
if (call_matrix[j][2] + 1 == 0)
// System all-call
printw ("Target [ SYSTEM ] Source [%8lld] All-Call", call_matrix[j][3]);
else if (call_matrix[j][2] > 100000)
// I-Call
printw ("Target [%8lld] Source [%8lld] I-Call", call_matrix[j][2] - 100000, call_matrix[j][3]);
else
// Voice call
if ((call_matrix[j][4] & EDACS_IS_VOICE) != 0)
{
// Group call
printw ("Target [%8lld] Source [%8lld]", call_matrix[j][2], call_matrix[j][3]);
if ((call_matrix[j][4] & EDACS_IS_GROUP) != 0)
printw ("Target [%8lld] Source [%8lld]", call_matrix[j][2], call_matrix[j][3]);
// I-Call
else if ((call_matrix[j][4] & EDACS_IS_INDIVIDUAL) != 0)
printw ("Target [%8lld] Source [%8lld] I-Call", call_matrix[j][2], call_matrix[j][3]);
// System all-call
else if ((call_matrix[j][4] & EDACS_IS_ALL_CALL) != 0)
printw ("Target [ SYSTEM ] Source [%8lld] All-Call", call_matrix[j][3]);
// Interconnect
else if ((call_matrix[j][4] & EDACS_IS_INTERCONNECT) != 0)
printw ("Target [ SYSTEM ] Source [%8lld] Interconnect", call_matrix[j][3]);
// Unknown call
else
printw ("Unknown call type");
// Call flags
if ((call_matrix[j][4] & EDACS_IS_DIGITAL) == 0) printw (" [Ana]");
else printw (" [Dig]");
if ((call_matrix[j][4] & EDACS_IS_EMERGENCY) != 0) printw ("[EM]");
}
else
// Data call
printw ("Target [ DATA ] Source [%8lld] Data", call_matrix[j][3]);
}
else
{
// 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][ UNIT ] Source [%5lld] I-Call", call_matrix[j][2] - 10000, call_matrix[j][3]);
else
// Voice call
if ((call_matrix[j][4] & EDACS_IS_VOICE) != 0)
{
// Compute 4:4:3 AFS for display purposes only
int a = (call_matrix[j][2] >> 7) & 0xF;
int fs = call_matrix[j][2] & 0x7F;
// Group call
printw ("Target [%6lld][%02d-%03d] Source [%5lld]", call_matrix[j][2], a, fs, call_matrix[j][3]);
if ((call_matrix[j][4] & EDACS_IS_GROUP) != 0)
printw ("Target [%6lld][%02d-%03d] Source [%5lld]", call_matrix[j][2], a, fs, call_matrix[j][3]);
// I-Call
else if ((call_matrix[j][4] & EDACS_IS_INDIVIDUAL) != 0)
printw ("Target [%6lld][ UNIT ] Source [%5lld] I-Call", call_matrix[j][2], call_matrix[j][3]);
// System all-call
else if ((call_matrix[j][4] & EDACS_IS_ALL_CALL) != 0)
printw ("Target [ SYSTEM ] Source [%5lld] All-Call", call_matrix[j][3]);
// Interconnect
else if ((call_matrix[j][4] & EDACS_IS_INTERCONNECT) != 0)
printw ("Target [ SYSTEM ] Source [%5lld] Interconnect", call_matrix[j][3]);
// Unknown call
else
printw ("Unknown call type");
// Call flags
if ((call_matrix[j][4] & EDACS_IS_DIGITAL) == 0) printw (" [Ana]");
else printw (" [Dig]");
if ((call_matrix[j][4] & EDACS_IS_EMERGENCY) != 0) printw ("[EM]");
}
else
// Data call
printw ("Target [ DATA ] Source [%5lld] Data", call_matrix[j][3]);
}
//test
for (int k = 0; k < state->group_tally; k++)
@ -4524,6 +4572,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
state->edacs_cc_lcn = 0;
state->edacs_vc_lcn = 0;
state->edacs_tuned_lcn = -1;
state->edacs_vc_call_type = 0;
state->p25_cc_freq = 0;
opts->p25_is_tuned = 0;
state->lasttg = 0;

View File

@ -542,6 +542,8 @@ void edacs(dsd_opts * opts, dsd_state * state)
unsigned char mt1 = (fr_1t & 0xF800000000) >> 35;
unsigned char mt2 = (fr_1t & 0x780000000) >> 31;
state->edacs_vc_call_type = 0;
//TODO: initialize where they are actually used
unsigned long long int site_id = 0; //we probably could just make this an int as well as the state variables
unsigned char lcn = 0;
@ -690,8 +692,8 @@ void edacs(dsd_opts * opts, dsd_state * state)
lcn = (fr_1t & 0x3E0000000) >> 29;
int group = (fr_1t & 0xFFFF000) >> 12;
int source = (fr_4t & 0xFFFFF000) >> 12;
fprintf (stderr, "%s", KBLU);
fprintf (stderr, " Data Group Call :: Group [%05d] Source [%08d] LCN [%02d]%s", group, source, lcn, get_lcn_status_string(lcn));
fprintf (stderr, "%s", KGRN);
fprintf (stderr, " TDMA Group Call :: Group [%05d] Source [%08d] LCN [%02d]%s", group, source, lcn, get_lcn_status_string(lcn));
fprintf (stderr, "%s", KNRM);
}
//Data Group Grant Update
@ -700,8 +702,8 @@ void edacs(dsd_opts * opts, dsd_state * state)
lcn = (fr_1t & 0x3E0000000) >> 29;
int group = (fr_1t & 0xFFFF000) >> 12;
int source = (fr_4t & 0xFFFFF000) >> 12;
fprintf (stderr, "%s", KGRN);
fprintf (stderr, " TDMA Group Call :: Group [%05d] Source [%08d] LCN [%02d]%s", group, source, lcn, get_lcn_status_string(lcn));
fprintf (stderr, "%s", KBLU);
fprintf (stderr, " Data Group Call :: Group [%05d] Source [%08d] LCN [%02d]%s", group, source, lcn, get_lcn_status_string(lcn));
fprintf (stderr, "%s", KNRM);
}
//Voice Call Grant Update
@ -724,9 +726,16 @@ void edacs(dsd_opts * opts, dsd_state * state)
int is_tx_trunking = (fr_4t & 0x200000000) >> 33;
int group = (fr_1t & 0xFFFF000) >> 12;
int source = (fr_4t & 0xFFFFF000) >> 12;
//Call info for state
if (lcn != 0) state->edacs_vc_lcn = lcn;
state->lasttg = group; // 0 is a valid TG, it's the all-call for agency 0
if (source != 0) state->lastsrc = source;
if (lcn != 0) state->edacs_vc_lcn = lcn;
//Call type for state
state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_GROUP;
if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL;
if (is_emergency == 1) state->edacs_vc_call_type |= EDACS_IS_EMERGENCY;
fprintf (stderr, "%s", KGRN);
if (is_digital == 0) fprintf (stderr, " Analog Group Call");
@ -823,9 +832,15 @@ void edacs(dsd_opts * opts, dsd_state * state)
int is_update = (fr_1t & 0x100000000) >> 32;
int target = (fr_1t & 0xFFFFF000) >> 12;
int source = (fr_4t & 0xFFFFF000) >> 12;
if (target != 0) state->lasttg = target + 100000; //Use IDs > 100000 to represent i-call targets to differentiate from TGs
if (source != 0) state->lastsrc = source;
//Call info for state
if (lcn != 0) state->edacs_vc_lcn = lcn;
if (target != 0) state->lasttg = target;
if (source != 0) state->lastsrc = source;
//Call type for state
state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_INDIVIDUAL;
if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL;
fprintf (stderr, "%s", KCYN);
if (is_digital == 0) fprintf (stderr, " Analog I-Call");
@ -890,6 +905,19 @@ void edacs(dsd_opts * opts, dsd_state * state)
fprintf (stderr, "%s", KBLU);
fprintf (stderr, " Channel Assignment (Unknown Data) :: Source [%08d] LCN [%02d]%s", source, lcn, get_lcn_status_string(lcn));
fprintf (stderr, "%s", KNRM);
//LCNs greater than 26 are considered status values, "Busy, Queue, Deny, etc"
if (lcn > state->edacs_lcn_count && lcn < 26)
{
state->edacs_lcn_count = lcn;
}
//Call info for state
if (lcn != 0) state->edacs_vc_lcn = lcn;
if (source != 0) state->lastsrc = source;
//Call type for state
state->edacs_vc_call_type = EDACS_IS_INDIVIDUAL;
}
//System All-Call Grant Update
else if (mt1 == 0x16)
@ -905,9 +933,15 @@ void edacs(dsd_opts * opts, dsd_state * state)
int is_digital = (fr_1t & 0x10000000) >> 28;
int is_update = (fr_1t & 0x8000000) >> 27;
int source = (fr_4t & 0xFFFFF000) >> 12;
state->lasttg = -1; // represent system all-call as TG -1 to differentiate from real TGs
if (source != 0) state->lastsrc = source;
//Call info for state
if (lcn != 0) state->edacs_vc_lcn = lcn;
state->lasttg = 0;
if (source != 0) state->lastsrc = source;
//Call type for state
state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_ALL_CALL;
if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL;
fprintf (stderr, "%s", KMAG);
if (is_digital == 0) fprintf (stderr, " Analog System All-Call");
@ -997,6 +1031,8 @@ void edacs(dsd_opts * opts, dsd_state * state)
unsigned char mt_b = (fr_1t & 0x1C00000000) >> 34;
unsigned char mt_d = (fr_1t & 0x3E0000000) >> 29;
state->edacs_vc_call_type = 0;
//Add raw payloads and MT-A/MT-B/MT-D for easy debug
if (opts->payload == 1)
{
@ -1028,7 +1064,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
//April 1998. Where real world systems are found to diverge from this bulletin, please note the basis for the
//deviation.
//MT-A 0 and 1 as analog/digital mode indicator reverse engineered from Quebec STM and San Antonio/Bexar Co
//MT-A 0 and 1 as analog/digital mode indicator reverse engineered from Montreal STM and San Antonio/Bexar Co
//systems; occurs immediately prior to Voice Group Channel Update.
//
//Voice Group Channel Assignment (6.2.4.1)
@ -1060,9 +1096,16 @@ void edacs(dsd_opts * opts, dsd_state * state)
{
state->edacs_lcn_count = lcn;
}
state->edacs_vc_lcn = lcn;
state->lasttg = group;
state->lastsrc = lid;
//Call info for state
if (lcn != 0) state->edacs_vc_lcn = lcn;
state->lasttg = group;
state->lastsrc = lid;
//Call type for state
state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_GROUP;
if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL;
if (is_emergency == 1) state->edacs_vc_call_type |= EDACS_IS_EMERGENCY;
char mode[8]; //allow, block, digital enc
sprintf (mode, "%s", "");
@ -1139,16 +1182,34 @@ void edacs(dsd_opts * opts, dsd_state * state)
int lid = (fr_1t & 0x3FFF000) >> 12;
int group = (fr_1t & 0x7FF000) >> 12;
//Abstract away to a target, and be sure to check whether it's an individual call later
int target = (is_individual_id == 0) ? group : lid;
fprintf (stderr, "%s", KBLU);
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_individual_id == 1) fprintf (stderr, " LID [%05d]", target);
else fprintf (stderr, " Group [%04d]", target);
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);
//LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc)
if (lcn > state->edacs_lcn_count && lcn < 26)
{
state->edacs_lcn_count = lcn;
}
//Call info for state
if (lcn != 0) state->edacs_vc_lcn = lcn;
state->lasttg = target;
state->lastsrc = 0;
//Call type for state
if (is_individual_call == 0) state->edacs_vc_call_type = EDACS_IS_GROUP;
else state->edacs_vc_call_type = EDACS_IS_INDIVIDUAL;
}
//Login Acknowledge (6.2.4.4)
else if (mt_a == 0x6)
@ -1186,26 +1247,44 @@ void edacs(dsd_opts * opts, dsd_state * state)
//Abstract away to a target, and be sure to check whether it's an individual call later
int target = (is_individual_id == 0) ? group : lid;
//Technically only MT-C 0x2 is defined in TSB 69.3 - using and extrapolating on legacy code
int is_digital = (mt_c == 2 || mt_c == 3) ? 1 : 0;
fprintf (stderr, "%s", KMAG);
fprintf (stderr, " Interconnect Channel Assignment :: Type");
if (mt_c == 0x2) fprintf (stderr, " [Voice]");
else fprintf (stderr, " [Reserved]");
if (is_digital == 0) fprintf (stderr, " Analog");
else fprintf (stderr, " Digital");
if (is_individual_id == 1) fprintf (stderr, " LID [%05d]", target);
else fprintf (stderr, " Group [%04d]", target);
fprintf (stderr, " LCN [%02d]%s", lcn, get_lcn_status_string(lcn));
fprintf (stderr, "%s", KNRM);
// TODO: Actually process the call
//LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc)
if (lcn > state->edacs_lcn_count && lcn < 26)
{
state->edacs_lcn_count = lcn;
}
//Call info for state
if (lcn != 0) state->edacs_vc_lcn = lcn;
state->lasttg = 0;
state->lastsrc = target;
//Call type for state
state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_INTERCONNECT;
if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL;
}
//Channel Updates (6.2.4.7)
//Source/caller being present in individual call channel updates reverse engineered from Montreal STM system
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 is_emergency = (is_individual == 0) ? (fr_1t & 0x2000000) >> 25 : 0;
int group = (fr_1t & 0x7FF000) >> 12;
int lid = ((fr_1t & 0x1FC0000) >> 11) | ((fr_4t & 0x7F000) >> 12);
int lid = (fr_1t & 0x3FFF000) >> 12;
int source = (fr_4t & 0x3FFF000) >> 12; //Source only present in individual calls
//Abstract away to a target, and be sure to check whether it's an individual call later
int target = (is_individual == 0) ? group : lid;
@ -1227,7 +1306,7 @@ void edacs(dsd_opts * opts, dsd_state * state)
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);
else fprintf (stderr, " Callee [%05d] Caller [%05d]", target, source);
fprintf (stderr, " LCN [%02d]%s", lcn, get_lcn_status_string(lcn));
if (is_tx_trunk == 0) fprintf (stderr, " [Message Trunking]");
if (is_emergency == 1)
@ -1242,14 +1321,19 @@ void edacs(dsd_opts * opts, dsd_state * state)
{
state->edacs_lcn_count = lcn;
}
state->edacs_vc_lcn = lcn;
//Use IDs > 10000 to represent i-call targets to differentiate from TGs
if (is_individual == 0) state->lasttg = target;
else state->lasttg = target + 10000;
//Call info for state
if (lcn != 0) state->edacs_vc_lcn = lcn;
state->lasttg = target;
//Alas, EDACS standard does not provide a source LID on channel updates - try to work around this on the display end instead
state->lastsrc = 0;
//Alas, EDACS standard does not provide a source LID on channel updates - try to work around this on the display end instead
state->lastsrc = 0;
//Call type for state
state->edacs_vc_call_type = EDACS_IS_VOICE;
if (is_individual == 0) state->edacs_vc_call_type |= EDACS_IS_GROUP;
else state->edacs_vc_call_type |= EDACS_IS_INDIVIDUAL;
if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL;
if (is_emergency == 1) state->edacs_vc_call_type |= EDACS_IS_EMERGENCY;
char mode[8]; //allow, block, digital enc
sprintf (mode, "%s", "");
@ -1332,23 +1416,88 @@ void edacs(dsd_opts * opts, dsd_state * state)
fprintf (stderr, "%s", KNRM);
}
//Individual Call Channel Assignment (6.2.4.9)
//Analog and digital flag reverse engineered from Montreal STM system
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 is_digital = (fr_1t & 0x4000000) >> 26;
int target = (fr_1t & 0x3FFF000) >> 12;
int source = (fr_4t & 0x3FFF000) >> 12;
fprintf (stderr, "%s", KCYN);
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));
fprintf (stderr, " Individual Call Channel Assignment ::");
if (is_digital == 0) fprintf (stderr, " Analog");
else fprintf (stderr, " Digital");
fprintf (stderr, " Callee [%05d] Caller [%05d] LCN [%02d]%s", target, source, lcn, get_lcn_status_string(lcn));
if (is_tx_trunk == 0) fprintf (stderr, " [Message Trunking]");
fprintf (stderr, "%s", KNRM);
// TODO: Actually process the call
//LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc)
if (lcn > state->edacs_lcn_count && lcn < 26)
{
state->edacs_lcn_count = lcn;
}
//Call info for state
if (lcn != 0) state->edacs_vc_lcn = lcn;
state->lasttg = target;
state->lastsrc = source;
//Call type for state
state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_INDIVIDUAL;
if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL;
char mode[8]; //allow, block, digital enc
sprintf (mode, "%s", "");
//if we are using allow/whitelist mode, then write 'B' to mode for block
//Individual calls always remain blocked if in allow/whitelist mode
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
//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 ((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))
{
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
}
}
}
}
//Console Unkey / Drop (6.2.4.10)
else if (mt_b == 0x6)
@ -1552,26 +1701,90 @@ void edacs(dsd_opts * opts, dsd_state * state)
}
}
//System All-Call (6.2.4.19)
//Analog and digital flag extrapolated from reverse engineering of other messages
else if (mt_d == 0x0F)
{
int lcn = (fr_1t & 0x1F000000) >> 24;
int qualifier = (fr_1t & 0x800000) >> 23;
int is_digital = (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);
int lid = ((fr_1t & 0x7F000) >> 12) | ((fr_4t & 0xFE000) >> 6);
fprintf (stderr, "%s", KMAG);
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]");
if (is_digital == 0) fprintf (stderr, " Analog");
else fprintf (stderr, " Digital");
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
//LCNs >= 26 are reserved to indicate status (queued, busy, denied, etc)
if (lcn > state->edacs_lcn_count && lcn < 26)
{
state->edacs_lcn_count = lcn;
}
//Call info for state
if (lcn != 0) state->edacs_vc_lcn = lcn;
state->lasttg = 0;
state->lastsrc = lid;
//Call type for state
state->edacs_vc_call_type = EDACS_IS_VOICE | EDACS_IS_ALL_CALL;
if (is_digital == 1) state->edacs_vc_call_type |= EDACS_IS_DIGITAL;
char mode[8]; //allow, block, digital enc
sprintf (mode, "%s", "");
//if we are using allow/whitelist mode, then write 'A' to mode for allow - always allow all-calls by default
if (opts->trunk_use_allow_list == 1) 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 ((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)
{
//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 SRC %05d All-Call.wav", getDateE(), timestr, state->edacs_site_id, 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, 0, 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, 0, lcn);
#endif
}
}
}
}
//Dynamic Regrouping (6.2.4.20)
else if (mt_d == 0x10)