mirror of https://github.com/lwvmobile/dsd-fme.git
470 lines
17 KiB
C
470 lines
17 KiB
C
/*-------------------------------------------------------------------------------
|
||
* p25_lcw.c
|
||
* P25p1 Link Control Word Decoding
|
||
*
|
||
* LWVMOBILE
|
||
* 2023-05 DSD-FME Florida Man Edition
|
||
*-----------------------------------------------------------------------------*/
|
||
|
||
#include "dsd.h"
|
||
|
||
//new p25_lcw function here -- TIA-102.AABF-D LCW Format Messages (if anybody wants to fill the rest out)
|
||
void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t irrecoverable_errors)
|
||
{
|
||
UNUSED(irrecoverable_errors);
|
||
|
||
uint8_t lc_format = (uint8_t)ConvertBitIntoBytes(&LCW_bits[0], 8); //format
|
||
uint8_t lc_opcode = (uint8_t)ConvertBitIntoBytes(&LCW_bits[2], 6); //opcode portion of format
|
||
uint8_t lc_mfid = (uint8_t)ConvertBitIntoBytes(&LCW_bits[8], 8); //mfid
|
||
uint8_t lc_svcopt = (uint8_t)ConvertBitIntoBytes(&LCW_bits[16], 8); //service options
|
||
uint8_t lc_pf = LCW_bits[0]; //protect flag
|
||
uint8_t lc_sf = LCW_bits[1]; //Implicit / Explicit MFID Format
|
||
UNUSED2(lc_opcode, lc_sf);
|
||
|
||
if (lc_pf == 1) //check the protect flag -- if set, its an encrypted lcw
|
||
{
|
||
fprintf (stderr, "%s",KRED);
|
||
fprintf (stderr, " LCW Protected ");
|
||
fprintf (stderr, "%s",KNRM);
|
||
}
|
||
|
||
if (lc_pf == 0) //not protected/encrypted lcw
|
||
{
|
||
|
||
if (opts->payload == 1) fprintf (stderr, " LCW");
|
||
|
||
//check to see if we need to run these as MFID 0 or 1 only (standard)
|
||
if (lc_mfid == 0 || lc_mfid == 1) //lc_mfid == 0
|
||
{
|
||
|
||
//check the service options on applicable formats
|
||
if (lc_format == 0x4A || lc_format == 0x46 || lc_format == 0x45
|
||
|| lc_format == 0x44 || lc_format == 0x03 || lc_format == 0x00)
|
||
{
|
||
|
||
if (lc_svcopt & 0x80) fprintf (stderr, " Emergency");
|
||
if (lc_svcopt & 0x40) fprintf (stderr, " Encrypted");
|
||
|
||
if (opts->payload == 1) //hide behind payload due to len
|
||
{
|
||
if (lc_svcopt & 0x20) fprintf (stderr, " Duplex");
|
||
if (lc_svcopt & 0x10) fprintf (stderr, " Packet");
|
||
else fprintf (stderr, " Circuit");
|
||
if (lc_svcopt & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||
fprintf (stderr, " Priority %d", lc_svcopt & 0x7); //call priority
|
||
}
|
||
}
|
||
|
||
if (lc_format == 0x00)
|
||
{
|
||
fprintf (stderr, " Group Voice Channel User");
|
||
uint8_t res = (uint8_t)ConvertBitIntoBytes(&LCW_bits[24], 7);
|
||
uint8_t explicit = LCW_bits[24]; //explicit source id required == 1, full SUID on seperate LCW
|
||
uint16_t group = (uint16_t)ConvertBitIntoBytes(&LCW_bits[32], 16);
|
||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||
fprintf (stderr, " - Group %d Source %d", group, source);
|
||
UNUSED2(res, explicit);
|
||
|
||
//don't set this when zero, annoying blink occurs in ncurses
|
||
if (group != 0) state->lasttg = group;
|
||
if (source != 0) state->lastsrc = source;
|
||
|
||
sprintf (state->call_string[0], " Group ");
|
||
if (lc_svcopt & 0x80) strcat (state->call_string[0], " Emergency ");
|
||
else if (lc_svcopt & 0x40) strcat (state->call_string[0], " Encrypted ");
|
||
else strcat (state->call_string[0], " ");
|
||
}
|
||
|
||
else if (lc_format == 0x03)
|
||
{
|
||
fprintf (stderr, " Unit to Unit Voice Channel User");
|
||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&LCW_bits[24], 24);
|
||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||
fprintf (stderr, " - Target %d Source %d", target, source);
|
||
|
||
//don't set this when zero, annoying blink occurs in ncurses
|
||
if (target != 0) state->lasttg = target;
|
||
if (source != 0) state->lastsrc = source;
|
||
|
||
sprintf (state->call_string[0], " Private ");
|
||
if (lc_svcopt & 0x80) strcat (state->call_string[0], " Emergency ");
|
||
else if (lc_svcopt & 0x40) strcat (state->call_string[0], " Encrypted ");
|
||
else strcat (state->call_string[0], " ");
|
||
|
||
}
|
||
|
||
//TODO: Allow Tuning from Call Grants either in LDU1 or TDULC? (TDMA to p1 fallback?)
|
||
//TODO: Allow TG Hold overrides here either in LDU1 or TDULC?
|
||
//NOTE: If we have an active TG hold, we really should't be here anyways
|
||
else if (lc_format == 0x42) //is this group only, or group and private?
|
||
{
|
||
fprintf (stderr, " Group Voice Channel Update - ");
|
||
uint16_t channel1 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[8], 16);
|
||
uint16_t group1 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[24], 16);
|
||
uint16_t channel2 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[40], 16);
|
||
uint16_t group2 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[56], 16);
|
||
|
||
if (channel1 && group1)
|
||
{
|
||
fprintf (stderr, "Ch: %04X TG: %d; ", channel1, group1);
|
||
sprintf (state->active_channel[0], "Active Ch: %04X TG: %d; ", channel1, group1);
|
||
state->last_active_time = time(NULL);
|
||
}
|
||
|
||
if (channel2 && group2 && group1 != group2)
|
||
{
|
||
fprintf (stderr, "Ch: %04X TG: %d; ", channel2, group2);
|
||
sprintf (state->active_channel[1], "Active Ch: %04X TG: %d; ", channel2, group2);
|
||
state->last_active_time = time(NULL);
|
||
}
|
||
|
||
}
|
||
|
||
else if (lc_format == 0x44)
|
||
{
|
||
fprintf (stderr, " Group Voice Channel Update – Explicit");
|
||
uint16_t group1 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[24], 16);
|
||
uint16_t channelt = (uint16_t)ConvertBitIntoBytes(&LCW_bits[40], 16);
|
||
uint16_t channelr = (uint16_t)ConvertBitIntoBytes(&LCW_bits[56], 16);
|
||
fprintf (stderr, "Ch: %04X TG: %d; ", channelt, group1);
|
||
UNUSED(channelr);
|
||
}
|
||
|
||
else if (lc_format == 0x45)
|
||
{
|
||
fprintf (stderr, " Unit to Unit Answer Request");
|
||
}
|
||
|
||
else if (lc_format == 0x46)
|
||
{
|
||
fprintf (stderr, " Telephone Interconnect Voice Channel User");
|
||
}
|
||
|
||
else if (lc_format == 0x47)
|
||
{
|
||
fprintf (stderr, " Telephone Interconnect Answer Request");
|
||
}
|
||
|
||
else if (lc_format == 0x49)
|
||
{
|
||
fprintf (stderr, " Souce ID Extension");
|
||
}
|
||
|
||
else if (lc_format == 0x4A)
|
||
{
|
||
fprintf (stderr, " Unit to Unit Voice Channel User – Extended");
|
||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&LCW_bits[16], 24);
|
||
uint32_t suid = (uint32_t)ConvertBitIntoBytes(&LCW_bits[40], 24);
|
||
fprintf (stderr, "TGT: %04X SUID: %d; ", target, suid);
|
||
}
|
||
|
||
else if (lc_format == 0x50)
|
||
{
|
||
fprintf (stderr, " Group Affiliation Query");
|
||
}
|
||
|
||
else if (lc_format == 0x51)
|
||
{
|
||
fprintf (stderr, " Unit Registration Command");
|
||
}
|
||
|
||
else if (lc_format == 0x52) //wonder if anybody uses this if its deleted/obsolete
|
||
{
|
||
fprintf (stderr, " Unit Authentication Command - OBSOLETE");
|
||
}
|
||
|
||
else if (lc_format == 0x53)
|
||
{
|
||
fprintf (stderr, " Status Query");
|
||
}
|
||
|
||
else if (lc_format == 0x54)
|
||
{
|
||
fprintf (stderr, " Status Update");
|
||
}
|
||
|
||
else if (lc_format == 0x55)
|
||
{
|
||
fprintf (stderr, " Status Update");
|
||
}
|
||
|
||
else if (lc_format == 0x56)
|
||
{
|
||
fprintf (stderr, " Call Alert");
|
||
}
|
||
|
||
|
||
else if (lc_format == 0x5A)
|
||
{
|
||
fprintf (stderr, " Status Update – Source ID Extension Required");
|
||
}
|
||
|
||
else if (lc_format == 0x5C)
|
||
{
|
||
fprintf (stderr, " Extended Function Command – Source ID Extension Required");
|
||
}
|
||
|
||
else if (lc_format == 0x60)
|
||
{
|
||
fprintf (stderr, " System Service Broadcast");
|
||
}
|
||
|
||
else if (lc_format == 0x61)
|
||
{
|
||
fprintf (stderr, " Secondary Control Channel Broadcast");
|
||
}
|
||
|
||
else if (lc_format == 0x62)
|
||
{
|
||
fprintf (stderr, " Adjacent Site Status Broadcast");
|
||
}
|
||
|
||
else if (lc_format == 0x63)
|
||
{
|
||
fprintf (stderr, " RFSS Status Broadcast");
|
||
}
|
||
|
||
else if (lc_format == 0x64)
|
||
{
|
||
fprintf (stderr, " Network Status Broadcast");
|
||
}
|
||
|
||
else if (lc_format == 0x65)
|
||
{
|
||
fprintf (stderr, " Protection Parameter Broadcast - OBSOLETE");
|
||
}
|
||
|
||
|
||
else if (lc_format == 0x66)
|
||
{
|
||
fprintf (stderr, " Secondary Control Channel Broadcast – Explicit (LCSCBX)");
|
||
}
|
||
|
||
else if (lc_format == 0x67) //explicit
|
||
{
|
||
fprintf (stderr, " Adjacent Site Status (LCASBX)");
|
||
uint8_t lra = (uint8_t)ConvertBitIntoBytes(&LCW_bits[8], 8);
|
||
uint16_t channelt = (uint16_t)ConvertBitIntoBytes(&LCW_bits[16], 16);
|
||
uint8_t rfssid = (uint8_t)ConvertBitIntoBytes(&LCW_bits[32], 8);
|
||
uint8_t siteid = (uint8_t)ConvertBitIntoBytes(&LCW_bits[40], 8);
|
||
uint16_t channelr = (uint16_t)ConvertBitIntoBytes(&LCW_bits[48], 16);
|
||
uint8_t cfva = (uint8_t)ConvertBitIntoBytes(&LCW_bits[64], 4);
|
||
fprintf (stderr, " - RFSS %d Site %d CH %04X", rfssid, siteid, channelt);
|
||
UNUSED2(lra, channelr);
|
||
|
||
//debug print only
|
||
// fprintf (stderr, "\n ");
|
||
// fprintf (stderr, " LRA [%02X] RFSS [%03d] SITE [%03d] CHAN-T [%04X] CHAN-R [%02X] CFVA [%X]\n ", lra, rfssid, siteid, channelt, channelr, cfva);
|
||
// if (cfva & 0x8) fprintf (stderr, " Conventional");
|
||
// if (cfva & 0x4) fprintf (stderr, " Failure Condition");
|
||
// if (cfva & 0x2) fprintf (stderr, " Up to Date (Correct)");
|
||
// else fprintf (stderr, " Last Known");
|
||
if (cfva & 0x1) fprintf (stderr, " - Connection Active");
|
||
// process_channel_to_freq (opts, state, channelt);
|
||
//end debug, way too much for a simple link control line
|
||
|
||
|
||
}
|
||
|
||
else if (lc_format == 0x68)
|
||
{
|
||
fprintf (stderr, " RFSS Status Broadcast – Explicit (LCRSBX)");
|
||
}
|
||
|
||
else if (lc_format == 0x69)
|
||
{
|
||
fprintf (stderr, " Network Status Broadcast – Explicit (LCNSBX)");
|
||
}
|
||
|
||
else if (lc_format == 0x6A)
|
||
{
|
||
fprintf (stderr, " Conventional Fallback");
|
||
}
|
||
|
||
else if (lc_format == 0x6B)
|
||
{
|
||
fprintf (stderr, " Message Update – Source ID Extension Required");
|
||
}
|
||
|
||
//tune back to CC here - save about 1-2 seconds
|
||
else if (lc_format == 0x4F) //# Call Termination/Cancellation
|
||
{
|
||
uint32_t tgt = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24); //can be individual, or all units (0xFFFFFF)
|
||
fprintf (stderr, " Call Termination; TGT: %d;", tgt);
|
||
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1)
|
||
{
|
||
|
||
//Will we need to check for a symbolrate change here, can a P25p2 TDMA-CC system
|
||
//revert back to a phase 1 traffic channel or carry a phase 1 traffic channel?
|
||
if (state->p25_cc_is_tdma == 1)
|
||
{
|
||
state->samplesPerSymbol = 8;
|
||
state->symbolCenter = 3;
|
||
opts->frame_p25p1 = 0; //turn it back off
|
||
}
|
||
|
||
//clear heuristics from current traffic channel
|
||
if (opts->frame_p25p1 == 1 && opts->use_heuristics == 1)
|
||
{
|
||
initialize_p25_heuristics(&state->p25_heuristics);
|
||
initialize_p25_heuristics(&state->inv_p25_heuristics);
|
||
}
|
||
|
||
//re-enable both slots (failsafe)
|
||
opts->slot1_on = 1;
|
||
opts->slot2_on = 1;
|
||
|
||
//rigctl
|
||
if (opts->use_rigctl == 1)
|
||
{
|
||
state->lasttg = 0;
|
||
state->lastsrc = 0;
|
||
state->payload_algid = 0;
|
||
state->payload_keyid = 0;
|
||
// state->payload_miP = 0;
|
||
//reset some strings
|
||
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||
sprintf (state->active_channel[0], "%s", "");
|
||
sprintf (state->active_channel[1], "%s", "");
|
||
opts->p25_is_tuned = 0;
|
||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
|
||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
|
||
}
|
||
//rtl
|
||
else if (opts->audio_in_type == 3)
|
||
{
|
||
#ifdef USE_RTLSDR
|
||
state->lasttg = 0;
|
||
state->lastsrc = 0;
|
||
state->payload_algid = 0;
|
||
state->payload_keyid = 0;
|
||
// state->payload_miP = 0;
|
||
//reset some strings
|
||
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||
sprintf (state->active_channel[0], "%s", "");
|
||
sprintf (state->active_channel[1], "%s", "");
|
||
opts->p25_is_tuned = 0;
|
||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
|
||
rtl_dev_tune (opts, state->p25_cc_freq);
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
|
||
else fprintf (stderr, " Unknown Format %02X MFID %02X SVC %02X", lc_format, lc_mfid, lc_svcopt);
|
||
}
|
||
|
||
//TODO: Look through all LCW format messages and move here if they don't use the MFID field
|
||
//just going to add/fix a few values I've observed for now, one issue with doing so may
|
||
//be where there is a reserved field that is also used as an MFID, i.e., Call Termination 0xF vs Moto Talker EOT 0xF with
|
||
//the reserved field showing the 0x90 for moto in it, and Harris 0xA vs Unit to Unit Voice Call 0xA
|
||
|
||
//TODO: Add Identification of Special SU Address values
|
||
//0 - No Unit
|
||
//1-0xFFFFFB - Assignable Units
|
||
//0xFFFFFC - FNE 16777212
|
||
//0xFFFFFD - System Default (FNE Calling Functions, Registration, Mobility)
|
||
//0xFFFFFE - Registration Default (registration transactions from SU)
|
||
//0xFFFFFF - All Units
|
||
|
||
//This lc_format doesn't use the MFID field
|
||
else if (lc_format == 0x42)
|
||
fprintf (stderr, " Conventional Fallback Indication");
|
||
|
||
//This lc_format doesn't use the MFID field
|
||
else if (lc_format == 0x57)
|
||
fprintf (stderr, " Extended Function Command");
|
||
|
||
//This lc_format doesn't use the MFID field
|
||
else if (lc_format == 0x58)
|
||
{
|
||
uint8_t iden = (uint8_t)ConvertBitIntoBytes(&LCW_bits[8], 4);
|
||
uint32_t base = (uint32_t)ConvertBitIntoBytes(&LCW_bits[40], 32);
|
||
fprintf (stderr, " Channel Identifier Update VU; Iden: %X; Base: %d;", iden, base*5);
|
||
}
|
||
|
||
//This lc_format doesn't use the MFID field
|
||
else if (lc_format == 0x59)
|
||
{
|
||
uint8_t iden = (uint8_t)ConvertBitIntoBytes(&LCW_bits[8], 4);
|
||
uint32_t base = (uint32_t)ConvertBitIntoBytes(&LCW_bits[40], 32);
|
||
fprintf (stderr, " Channel Identifier Update VU; Iden: %X; Base: %d;", iden, base*5);
|
||
}
|
||
|
||
//This lc_format doesn't use the MFID field
|
||
else if (lc_format == 0x63)
|
||
fprintf (stderr, " RFSS Status Broadcast");
|
||
|
||
//MFID 90 Embedded GPS
|
||
else if (lc_mfid == 0x90 && lc_opcode == 0x6)
|
||
{
|
||
fprintf (stderr, " MFID90 (Moto)");
|
||
apx_embedded_gps (opts, state, LCW_bits);
|
||
}
|
||
|
||
else if (lc_mfid == 0x90 && lc_opcode == 0x0)
|
||
{
|
||
//needed to fill this in, since tuning this on P1 will just leave the TG/SRC as zeroes
|
||
fprintf (stderr, " MFID90 (Moto) Group Regroup Channel User (LCGRGR)");
|
||
uint32_t sg = (uint32_t)ConvertBitIntoBytes(&LCW_bits[32], 16);
|
||
uint32_t src = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||
fprintf (stderr, " SG: %d; SRC: %d;", sg, src);
|
||
if (LCW_bits[16] == 1) fprintf (stderr, " Res;"); //res bit (octet 2)
|
||
if (LCW_bits[17] == 1) fprintf (stderr, " ENC;"); //P-bit (octet 2)
|
||
if (LCW_bits[31] == 1) fprintf (stderr, " EXT;"); //Full SUID next LC (external) (octet 3)
|
||
state->lasttg = sg;
|
||
state->lastsrc = src;
|
||
}
|
||
|
||
else if (lc_mfid == 0x90 && lc_opcode == 0x1)
|
||
{
|
||
fprintf (stderr, " MFID90 (Moto) Group Regroup Channel Update (LCGRGU)");
|
||
uint32_t sg = (uint32_t)ConvertBitIntoBytes(&LCW_bits[24], 16);
|
||
uint32_t ch = (uint32_t)ConvertBitIntoBytes(&LCW_bits[56], 16);
|
||
fprintf (stderr, " SG: %d; CH: %04X;", sg, ch);
|
||
if (LCW_bits[16] == 1) fprintf (stderr, " Res;"); //res bit (octet 2)
|
||
if (LCW_bits[17] == 1) fprintf (stderr, " ENC;"); //P-bit (octet 2)
|
||
}
|
||
|
||
else if (lc_mfid == 0x90 && lc_opcode == 0x3)
|
||
fprintf (stderr, " MFID90 (Moto) Group Regroup Add");
|
||
|
||
else if (lc_mfid == 0x90 && lc_opcode == 0x4)
|
||
fprintf (stderr, " MFID90 (Moto) Group Regroup Delete");
|
||
|
||
else if (lc_mfid == 0x90 && lc_opcode == 0xF)
|
||
{
|
||
uint32_t src = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||
fprintf (stderr, " MFID90 (Moto) Talker EOT; SRC: %d;", src);
|
||
}
|
||
|
||
//observed format value on Harris SNDCP data channel (Phase 2 CC to Phase 1 MPDU channel)
|
||
else if (lc_mfid == 0xA4 && lc_format == 0x0A)
|
||
{
|
||
//Could also just be a Unit to Unit Voice Channel User using the reserved field as the MFID?
|
||
//if it were similar to Unit to Unit though, the TGT and SRC values seem to be reversed
|
||
//this appears to be a data channel indicator, has a matching target and the FNE address in it
|
||
uint32_t src = (uint32_t)ConvertBitIntoBytes(&LCW_bits[24], 24);
|
||
uint32_t tgt = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||
fprintf (stderr, " MFIDA4 (Harris) Data Channel; SRC: %d; TGT: %d;", src, tgt);
|
||
}
|
||
|
||
//not a duplicate, this one will print if not MFID 0 or 1
|
||
else
|
||
{
|
||
fprintf (stderr, " Unknown Format %02X MFID %02X ", lc_format, lc_mfid);
|
||
if (lc_mfid == 0x90) fprintf (stderr, "(Moto)");
|
||
else if (lc_mfid == 0xA4) fprintf (stderr, "(Harris)");
|
||
}
|
||
}
|
||
|
||
//ending line break
|
||
fprintf (stderr, "\n");
|
||
|
||
}
|
||
|
||
|