From 3b9f9f5a3befb295da284379262323fad7d69c7a Mon Sep 17 00:00:00 2001 From: lwvmobile Date: Tue, 26 Mar 2024 17:33:34 -0400 Subject: [PATCH] Squashed commit of the following: commit a5c5be3b955a82cc72f1486ff87d7c1b4893295f Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Tue Mar 26 17:28:35 2024 -0400 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 commit c8d00846b13ff29122661e48eb6e6aba7fd50355 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Sun Mar 24 17:48:46 2024 -0400 EDACS: Display cached call source on Standard, tune group calls on assignment (#241) * Trim trailing whitespace * EDACS: Display cached call source on Standard The logic in `edacs-fme.c` didn't work, in retrospect, because it would have depended on getting back-to-back messages for the same call. In practice, that is unlikely even on an idle system as system state gets displayed. Instead, cache on the display end, for a "better" user experience. * EDACS: Pad channel freq to "000.000000 MHz" * EDACS: Process/tune group calls on assignment commit 8d432e8b3e94377c26712a3cc628c66b2a7bd042 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Sun Mar 24 12:29:22 2024 -0400 EDACS: ESK fixes, RE of standard channel assignments, cleanups (#238) * EDACS: Make i-calls very obvious on standard * EDACS: abstract target for Interconnect Channel Assignment * EDACS: little shortcut example updates * EDACS: Apply ESK to both FR1 and FR4 * EDACS: Group channel assignment by mode * EDACS: Merge MT-A 0/1 and 2/3 cases commit 0440dea1de70e5f0aaeff408770649efb1e2ba8c Author: lwvmobile Date: Sun Mar 24 02:47:09 2024 -0400 EDACS: Fix Call History For Standard Maybe; commit 208f188e559469539ec1a54fc26c8e21f38bb751 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Sat Mar 23 21:35:42 2024 -0400 EDACS: Speculative RE of MT-A value 0, pretty colours (#231) * EDACS: fix log formatting * EDACS: Speculative RE of MT-A value 0 * EDACS: CC message pretty colours * EDACS: little formatting * EDACS: eh make interconnect magenta commit 36f76a7e21ed609ccee143f81611ea84281d3e58 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Sat Mar 23 17:35:45 2024 -0400 EDACS: Fix copy/paste error (#230) I went through by hand and I'm confident this is the last of the `get_lcn_status_string()` errors. commit 4bc73d8c68df5966bbabf9ddbaa20540b9fe0d36 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Sat Mar 23 17:24:32 2024 -0400 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 commit 03507055a42e5f1c17b8e06df981b821249f5033 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Sat Mar 23 16:05:09 2024 -0400 EDACS: add EA messages / start mass Standard refactor (#226) * EDACS EA: refactor log messages for consistency * EDACS EA: channel assignment command (unknown data) * EDACS EA: status/message command * EDACS Standard: Refactor variable declarations * EDACS Standard: log raw payloads for debugging * EDACS: Fix typing * EDACS EA: update status/message CC command * EDACS: Log LCN status values * EDACS: String format * EDACS EA: channel assignment vs update * EDACS: fix syntax error * EDACS: change string for reserved LCN status commit fbe5fd20937abfea8c9dff8573730944cac2c09c Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Fri Mar 22 15:26:50 2024 -0400 EDACS EA: emergency calls, MT1 fixes, logging improvements (#221) * EDACS EA: Fix i-call MT1 after ESK changes * EDACS EA: Fix all-call ncurses display * EDACS EA: Make detailed log slightly more readable * EDACS EA: Improve colours in logs * EDACS EA: Space in logs for consistency * EDACS EA: Fix MT1 for analog group voice after ESK change * EDACS EA: add parsing for emergency calls commit 413e5f76a21526d9fcb59655d1940802857ed75e Merge: de9127c 388a57d Author: lwvmobile <59371473+lwvmobile@users.noreply.github.com> Date: Fri Mar 22 06:59:31 2024 -0400 Merge pull request #220 from lwvmobile/edacs_manual_modes Edacs manual modes commit 388a57d8fc08eec0c6951a1cb68f8b46e750c566 Author: lwvmobile Date: Fri Mar 22 05:28:11 2024 -0400 EDACS: Re-enable the -9 switch to maintain user compatability; Fix Missing Bracket on non color mode; commit 058099f1aaee48a1deacdb7ce607a8777dbbfd94 Author: lwvmobile Date: Fri Mar 22 03:24:57 2024 -0400 EDACS: Tweak Mode Toggle Display; Add Frame Dump on No Mode Set; commit 68aa6b018ebb672913ecfb20eb16005a6643d507 Author: lwvmobile Date: Fri Mar 22 02:19:38 2024 -0400 EDACS: Update Docs to Reflect Changes; commit c404f3c48c197dede8257ee602a58464a8cf2e9a Author: lwvmobile Date: Fri Mar 22 02:01:09 2024 -0400 EDACS: Ncurses Display Working Mode Tweaks; commit b2d1608eb71a3a8d6035662e3feb8ea450dce57c Author: lwvmobile Date: Fri Mar 22 01:08:28 2024 -0400 EDACS: Toggle Keys A and S for Mode Selection; commit a4553a0a0a2c5998b6bcf44c58d8e452cd71bc33 Author: lwvmobile Date: Fri Mar 22 00:45:37 2024 -0400 EDACS: Remove Auto Detect Code; Make CLI Switches; commit de9127cfb49a64cfb050822cf24c1c3d7fc13616 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Thu Mar 21 21:02:53 2024 -0400 EDACS: support for EA system all-calls (#214) * Refactor debug printing a bit * Clean up group call logic * Minor changes to debug printing * EDACS EA: support for system all-calls * EDACS: split out MT1 for TDMA call since we can't decode it anyway * Refactor identifying call mode (digital/analog) * Maintain consistent indentation for all MT1 * EDACS: Fix Compiler Error. Had an extra right bracket on line 383 --------- Co-authored-by: lwvmobile <59371473+lwvmobile@users.noreply.github.com> commit c85dd6039903254f5056d8b95083ca9eb5854188 Author: lwvmobile Date: Tue Mar 19 17:38:30 2024 -0400 EDACS: NET/STD LCN > 0 Check; commit bed766a86e5a99b98d8cb0b0f7e00e176c530e85 Author: lwvmobile Date: Tue Mar 19 09:34:10 2024 -0400 EDACS: STD/NET Don't Tune Unless CC LCN Set; commit 69f532a022119ef7dd27a34c5e201b04c606bcc4 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Mon Mar 18 11:41:58 2024 -0400 EDACS: EA I-Call decoding improvements (#205) * EDACS: formatting nit * EDACS: add name to credits * EDACS: Handle initial grant vs grant update for i-call * EDACS: consistency for group vs i-call * EDACS: file naming for i-call recordings * EDACS: EA refactor when MT1 is 0x1F * EDACS: check we know CC LCN before leaving it * EDACS: fix breaking change for Standard Revert mistake in f68367c4f3977ab7e4be5befc50df427da34fe68 * Fix patent number * EDACS: Fix Compiler Error 1; Fix Rogue Copy Paste * EDACS: Add MT1, MT2, and FR1 and FR4 Dumps on Payload; --------- Co-authored-by: lwvmobile <59371473+lwvmobile@users.noreply.github.com> commit a7de24c47b4b9ecb840d9e52f77381161453219a Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Mon Mar 18 07:47:34 2024 -0400 EDACS: fixes and improvements to EA analog and i-call parsing (#204) * Fix MT1 math given esk_mask value is 0xA0 * EDACS: Clean up formatting for I-Calls * EDACS: typo, fix the TGID for group calls Fix copy-pasta * EDACS: Fix MT2 for I-Calls Incorrectly copied decimal values as hex - obviously MT2 is only 4 bits so it cannot be greater than 15. * EDACS: check LCN is > 0 before trying to tune An EA CC sometimes includes commands that parse as LCN 0 - but otherwise valid - analog call voice grants for TG 1. Since LCN 0 is not valid, ensure that we don't try to tune to them while we try to figure out what they're about. * EDACS: Process "login" message on CC Log "login" messages sent on the CC. Verified behaviour against UniTrunker. commit 10eacd99ccbdb4c2ed8dccce426d9aa15b66f9b9 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Sun Mar 17 18:56:41 2024 -0400 EDACS: Clean up formatting for I-Calls (#203) * Fix MT1 math given esk_mask value is 0xA0 * EDACS: Clean up formatting for I-Calls commit ebe6e8fc659bc4c773dc09b272a0a4ba9431d7b1 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Sun Mar 17 18:02:12 2024 -0400 Fix MT1 math given esk_mask value is 0xA0 (#202) commit 8619612b76cbd4bb5c2595b13da79bc79f8ee76a Author: lwvmobile Date: Sat Mar 16 22:42:44 2024 -0400 EDACS: Setup Analog Calls and WAVs on EA and ICALL; commit 9ac1573f0bf384eb462ecd4645edefac0b3c4133 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Sat Mar 16 17:17:14 2024 -0400 EDACS: Add MT1 value / logging for analog group calls (#198) * Add MT1 value / logging for analog group calls * Respect user preference for tuning to group calls * Revert "Respect user preference for tuning to group calls" This reverts commit 83ac8feab7226ed4a751615866c311a59c8c6391. commit 0aa01a45efc9719c801e8171741f4c9a27ed7a69 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Sat Mar 16 17:16:52 2024 -0400 EDACS: Respect user preference for tuning to group calls (#201) commit f68367c4f3977ab7e4be5befc50df427da34fe68 Author: ilyacodes <33097525+ilyacodes@users.noreply.github.com> Date: Sat Mar 16 17:16:20 2024 -0400 EDACS: Add parsing for individual calls (#200) * EDACS: Add parsing for individual calls This change adds parsing for I-Calls on EA systems. Both analog and ProVoice modes are parsed and processed. See related issue for discussion. * Respect user preference for tuning to private calls * EDACS: Respect user preference for tuning to private calls * EDACS: Handle i-call target in ProVoice logging * EDACS: Show i-calls distinctly from group calls --- README.md | 2 +- examples/Example_Usage.md | 19 +- examples/trunking.sh | 18 +- include/dsd.h | 24 +- src/dsd_main.c | 157 +++- src/dsd_ncurses.c | 473 ++++++++---- src/edacs-fme.c | 1493 +++++++++++++++++++++++++++++++------ src/provoice.c | 13 +- 8 files changed, 1789 insertions(+), 410 deletions(-) diff --git a/README.md b/README.md index 60b8ceb..d9cc120 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ DSD-FME is an evolution of the original DSD project from 'DSD Author' using the base code of [szechyjs](https://github.com/szechyjs/dsd "szechyjs"), some code and ideas from [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve"), [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25") and [Osmocom OP25](https://gitea.osmocom.org/op25/op25 "Osmocom OP25"), along with other snippets of code, information, and inspirations from other projects including [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDRTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), [OK-DMRlib](https://github.com/OK-DMR/ok-dmrlib "OK-DMRlib"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"), Eric Cottrell, SP5WWP and others. Finally, this is all brought together with original code to extend the fuctionality and add new features including NCurses Terminal and Menu system, Pulse Audio, TCP Direct Link Audio, RIGCTL, Trunking Features, LRRP/GPS Mapping, P25 Phase 2, EDACS, YSF, M17, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users in mind, so please understand that this version may not compile, compile easily, or run correctly in other environments. -This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu, Forts, thewraithe2008, RayAir, Cretu, and others for the many hours of wav samples and information provided by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. I'd also like to thank mrscanner2008 for providing an additional remote where additional NXDN Type-C, 'Idas' Type-D, and XPT decoding and trunking could be sorted out. Thanks to volo-zyko for cleaning up a lot of code. Thank you everybody. +This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu, Forts, thewraithe2008, RayAir, Cretu, ilyacodes, and others for the many hours of wav samples and information provided by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. I'd also like to thank mrscanner2008 for providing an additional remote where additional NXDN Type-C, 'Idas' Type-D, and XPT decoding and trunking could be sorted out. Thanks to volo-zyko for cleaning up a lot of code. Thank you everybody. ![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/audio_work/dsd-fme2.png) diff --git a/examples/Example_Usage.md b/examples/Example_Usage.md index e68f868..88e6e3b 100644 --- a/examples/Example_Usage.md +++ b/examples/Example_Usage.md @@ -8,7 +8,11 @@ -fa Legacy Auto (not recommended) -fi NXDN48 -fn NXDN96 --fp EDACS/Provoice +-fp Provoice +-fh EDACS Standard / Network +-fH EDACS Standard / Network with ESK +-fe EDACS Extended Addresssing +-fE EDACS Extended Addressing with ESK -fm dPMR, also may need to use -xd if inverted dPMR. -f1 P25P1 -f2 P25P2 (may need to specify wacn/sys/nac manually if traffic channel) @@ -100,11 +104,11 @@ and in a second terminal tab, same folder, run EDACS Trunking (w/ channel map import) ---EDACS/PV Trunking using RIGCTL and TCP Direct Link Audio inside of SDR++ (Tested and Working on EDACS/EDACS-EA with Provoice, Analog Voice Monitoring and Per Call is Experimental) +--EDACS/PV Trunking using RIGCTL and TCP Direct Link Audio inside of SDR++ (see switches above for STD/NET, EA, and ESK modes) -`dsd-fme -i tcp -fp -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans` +`dsd-fme -i tcp -fE -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans` ---NXDN48 Trunking (standard band plan) with SDR++ (untested for frequency accuracy) +--NXDN48 Trunking (Direct Frequency Assignment) with SDR++ (untested for frequency accuracy) `dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans` @@ -176,9 +180,9 @@ M - toggle c4fm/qpsk 8/3 (phase 2 tdma control channel) R - start capturing symbol capture bin (date/time name file) r - stop capturing symbol capture bin spacebar - replay last symbol capture bin (captures must be stopped first) -s - stop playing symbol capture bin or wav input file +s - stop playing symbol capture bin or wav input file (lower s) P - start per call decoded wav files (Capital P) -p - stop per call decoded wav files (Lower p) +p - stop per call decoded wav files (lower p) t - toggle trunking (needs either rtl input, or rigctl connection) y - toggle scanner (needs either rtl input, or rigctl connection) 1 - Toggle Voice Synthesis in TDMA Slot 1 or FDMA Conventional Voice @@ -202,6 +206,9 @@ l - Hold TG in Slot 2 on TDMA Systems, or clear current hold C - Drop Call and Return to CC during trunking operation L - Manual Cycle Forward Channel Tuned when RIGCTL or using RTL input and channel csv loaded +S - Toggle between EDACS Standard/Network and Extended Addressing mode (Capital S) +A - Toggle EDACS ESK Mask (none vs A0) + ``` ## Sending Audio to a Icecast 2 Server via FFmpeg (Windows) diff --git a/examples/trunking.sh b/examples/trunking.sh index cb8152e..7053097 100644 --- a/examples/trunking.sh +++ b/examples/trunking.sh @@ -17,9 +17,21 @@ dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans #dsd-fme -fs -i rtl:0:450M:44:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.ans -#EDACS/EDACS-EA Digital Only -#dsd-fme -fp -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans -#dsd-fme -fp -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans +#EDACS Standard Network Analog and Digital +#dsd-fme -fh -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans +#dsd-fme -fh -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans + +#EDACS Standard Network Analog and Digital (with ESK) +#dsd-fme -fH -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans +#dsd-fme -fH -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans + +#EDACS Extended Address Analog and Digital +#dsd-fme -fe -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans +#dsd-fme -fe -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans + +#EDACS Extended Address Analog and Digital (with ESK) +#dsd-fme -fE -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans +#dsd-fme -fE -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans #NXDN48 Type-C or Type-D Trunking with Channel Map #dsd-fme -fi -i tcp -T -U 4532 -C nxdn_channel_map.csv -N 2> log.ans diff --git a/include/dsd.h b/include/dsd.h index 8354c33..fb91c09 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -210,8 +210,8 @@ typedef struct SF_INFO *audio_in_file_info; uint32_t rtlsdr_center_freq; - int rtlsdr_ppm_error; - int audio_in_type; + int rtlsdr_ppm_error; + int audio_in_type; char audio_out_dev[1024]; int audio_out_fd; int audio_out_fdR; //right channel audio for OSS hack @@ -367,7 +367,7 @@ typedef struct char group_in_file[1024]; char lcn_in_file[1024]; char chan_in_file[1024]; - char key_in_file[1024]; + char key_in_file[1024]; //end import filenames //reverse mute @@ -454,7 +454,7 @@ typedef struct short s_ru[160*6]; //single sample right short s_l4u[4][160*6]; //quad sample for up to a P25p2 4V short s_r4u[4][160*6]; //quad sample for up to a P25p2 4V - //end + //end int audio_out_idx; int audio_out_idx2; int audio_out_idxR; @@ -616,7 +616,7 @@ typedef struct char dmr_cach_fragment[4][17]; //unsure of size, will need to check/verify int dmr_cach_counter; //counter for dmr_cach_fragments 0-3; not sure if needed yet. - + //dmr talker alias new/fixed stuff uint8_t dmr_alias_format[2]; //per slot uint8_t dmr_alias_len[2]; //per slot @@ -695,13 +695,23 @@ typedef struct //edacs int ea_mode; - int esk_mode; + unsigned short esk_mask; unsigned long long int edacs_site_id; int edacs_lcn_count; //running tally of lcn's observed on edacs system 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 @@ -1188,7 +1198,7 @@ bool crc8_ok(uint8_t bits[], unsigned int len); //modified CRC functions for SB/RC uint8_t crc7(uint8_t bits[], unsigned int len); uint8_t crc3(uint8_t bits[], unsigned int len); -uint8_t crc4(uint8_t bits[], unsigned int len); +uint8_t crc4(uint8_t bits[], unsigned int len); //LFSR and LFSRP code courtesy of https://github.com/mattames/LFSR/ void LFSR(dsd_state * state); diff --git a/src/dsd_main.c b/src/dsd_main.c index 05df745..300f342 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -1047,8 +1047,8 @@ 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->esk_mode = -1; //same as above, but with esk or not - state->esk_mask = 0x0; //toggles from 0x0 to 0xA0 if esk mode enabled + state->edacs_vc_call_type = 0; + state->esk_mask = 0x0; //esk mask value state->edacs_site_id = 0; state->edacs_lcn_count = 0; state->edacs_cc_lcn = 0; @@ -1333,7 +1333,11 @@ usage () printf (" -fz Decode only M17*\n"); printf (" -fi Decode only NXDN48* (6.25 kHz) / IDAS*\n"); printf (" -fn Decode only NXDN96* (12.5 kHz)\n"); - printf (" -fp Decode only EDACS/ProVoice*\n"); + printf (" -fp Decode only ProVoice*\n"); + printf (" -fh Decode only EDACS Standard/ProVoice*\n"); + printf (" -fH Decode only EDACS Standard/ProVoice with ESK 0xA0*\n"); + printf (" -fe Decode only EDACS EA/ProVoice*\n"); + printf (" -fE Decode only EDACS EA/ProVoice with ESK 0xA0*\n"); printf (" -fm Decode only dPMR*\n"); printf (" -l Disable DMR, dPMR, and NXDN input filtering\n"); printf (" -u Unvoiced speech quality (default=3)\n"); @@ -1416,7 +1420,7 @@ usage () printf (" P25 - 12000; NXDN48 - 7000; NXDN96: 12000; DMR - 7000-12000; EDACS/PV - 12000-24000;\n"); //redo this, or check work, or whatever printf (" May vary based on system stregnth, etc.\n"); printf (" -t Set Trunking or Scan Speed VC/sync loss hangtime in seconds. (default = 1 second)\n"); - printf (" -9 Force Enable EDACS Standard or Networked Mode if Auto Detection Fails \n"); + // printf (" -9 Force Enable EDACS Standard or Networked Mode if Auto Detection Fails \n"); printf ("\n"); printf (" Trunking Example TCP: dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans\n"); printf (" Trunking Example RTL: dsd-fme -fs -i rtl:0:450M:26:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.ans\n"); @@ -1684,9 +1688,10 @@ main (int argc, char **argv) fprintf (stderr, "TG Hold set to %d \n", state.tg_hold); break; - case '9': //This is a temporary fix for RR issue until a permanent fix can be found + case '9': //Leaving Enabled to maintain backwards compatability state.ea_mode = 0; - fprintf (stderr,"Force Enabling EDACS Standard/Networked Mode Mode\n"); + state.esk_mask = 0; + fprintf (stderr,"Force Enabling EDACS Standard/Networked Mode Mode without ESK.\n"); break; //experimental audio monitoring @@ -2182,6 +2187,146 @@ main (int argc, char **argv) opts.rtl_bandwidth = 24; // opts.rtl_gain_value = 36; } + else if (optarg[0] == 'h') //standard / net w/o ESK + { + opts.frame_dstar = 0; + opts.frame_x2tdma = 0; + opts.frame_p25p1 = 0; + opts.frame_p25p2 = 0; + opts.frame_nxdn48 = 0; + opts.frame_nxdn96 = 0; + opts.frame_dmr = 0; + opts.frame_dpmr = 0; + opts.frame_provoice = 1; + state.ea_mode = 0; + state.esk_mask = 0; + opts.frame_ysf = 0; + opts.frame_m17 = 0; + state.samplesPerSymbol = 5; + state.symbolCenter = 2; + opts.mod_c4fm = 0; + opts.mod_qpsk = 0; + opts.mod_gfsk = 1; + state.rf_mod = 2; + opts.pulse_digi_rate_out = 8000; + opts.pulse_digi_out_channels = 1; + opts.dmr_stereo = 0; + opts.dmr_mono = 0; + state.dmr_stereo = 0; + // opts.setmod_bw = 12500; + sprintf (opts.output_name, "EDACS/PV"); + fprintf (stderr,"Setting symbol rate to 9600 / second\n"); + fprintf (stderr,"Decoding EDACS STD/NET and ProVoice frames.\n"); + fprintf (stderr,"EDACS Analog Voice Channels are Experimental.\n"); + //rtl specific tweaks + opts.rtl_bandwidth = 24; + // opts.rtl_gain_value = 36; + } + else if (optarg[0] == 'H') //standard / net w/ ESK + { + opts.frame_dstar = 0; + opts.frame_x2tdma = 0; + opts.frame_p25p1 = 0; + opts.frame_p25p2 = 0; + opts.frame_nxdn48 = 0; + opts.frame_nxdn96 = 0; + opts.frame_dmr = 0; + opts.frame_dpmr = 0; + opts.frame_provoice = 1; + state.ea_mode = 0; + state.esk_mask = 0xA0; + opts.frame_ysf = 0; + opts.frame_m17 = 0; + state.samplesPerSymbol = 5; + state.symbolCenter = 2; + opts.mod_c4fm = 0; + opts.mod_qpsk = 0; + opts.mod_gfsk = 1; + state.rf_mod = 2; + opts.pulse_digi_rate_out = 8000; + opts.pulse_digi_out_channels = 1; + opts.dmr_stereo = 0; + opts.dmr_mono = 0; + state.dmr_stereo = 0; + // opts.setmod_bw = 12500; + sprintf (opts.output_name, "EDACS/PV"); + fprintf (stderr,"Setting symbol rate to 9600 / second\n"); + fprintf (stderr,"Decoding EDACS STD/NET w/ ESK and ProVoice frames.\n"); + fprintf (stderr,"EDACS Analog Voice Channels are Experimental.\n"); + //rtl specific tweaks + opts.rtl_bandwidth = 24; + // opts.rtl_gain_value = 36; + } + else if (optarg[0] == 'e') //extended addressing w/o ESK + { + opts.frame_dstar = 0; + opts.frame_x2tdma = 0; + opts.frame_p25p1 = 0; + opts.frame_p25p2 = 0; + opts.frame_nxdn48 = 0; + opts.frame_nxdn96 = 0; + opts.frame_dmr = 0; + opts.frame_dpmr = 0; + opts.frame_provoice = 1; + state.ea_mode = 1; + state.esk_mask = 0; + opts.frame_ysf = 0; + opts.frame_m17 = 0; + state.samplesPerSymbol = 5; + state.symbolCenter = 2; + opts.mod_c4fm = 0; + opts.mod_qpsk = 0; + opts.mod_gfsk = 1; + state.rf_mod = 2; + opts.pulse_digi_rate_out = 8000; + opts.pulse_digi_out_channels = 1; + opts.dmr_stereo = 0; + opts.dmr_mono = 0; + state.dmr_stereo = 0; + // opts.setmod_bw = 12500; + sprintf (opts.output_name, "EDACS/PV"); + fprintf (stderr,"Setting symbol rate to 9600 / second\n"); + fprintf (stderr,"Decoding EDACS Extended Addressing and ProVoice frames.\n"); + fprintf (stderr,"EDACS Analog Voice Channels are Experimental.\n"); + //rtl specific tweaks + opts.rtl_bandwidth = 24; + // opts.rtl_gain_value = 36; + } + else if (optarg[0] == 'E') //extended addressing w/ ESK + { + opts.frame_dstar = 0; + opts.frame_x2tdma = 0; + opts.frame_p25p1 = 0; + opts.frame_p25p2 = 0; + opts.frame_nxdn48 = 0; + opts.frame_nxdn96 = 0; + opts.frame_dmr = 0; + opts.frame_dpmr = 0; + opts.frame_provoice = 1; + state.ea_mode = 1; + state.esk_mask = 0xA0; + opts.frame_ysf = 0; + opts.frame_m17 = 0; + state.samplesPerSymbol = 5; + state.symbolCenter = 2; + opts.mod_c4fm = 0; + opts.mod_qpsk = 0; + opts.mod_gfsk = 1; + state.rf_mod = 2; + opts.pulse_digi_rate_out = 8000; + opts.pulse_digi_out_channels = 1; + opts.dmr_stereo = 0; + opts.dmr_mono = 0; + state.dmr_stereo = 0; + // opts.setmod_bw = 12500; + sprintf (opts.output_name, "EDACS/PV"); + fprintf (stderr,"Setting symbol rate to 9600 / second\n"); + fprintf (stderr,"Decoding EDACS Extended Addressing w/ ESK and ProVoice frames.\n"); + fprintf (stderr,"EDACS Analog Voice Channels are Experimental.\n"); + //rtl specific tweaks + opts.rtl_bandwidth = 24; + // opts.rtl_gain_value = 36; + } else if (optarg[0] == '1') { opts.frame_dstar = 0; diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c index 4535aa2..a91dd1c 100644 --- a/src/dsd_ncurses.c +++ b/src/dsd_ncurses.c @@ -7,6 +7,9 @@ * * LWVMOBILE * 2022-08 DSD-FME Florida Man Edition + * + * ilyacodes + * 2024-03 EDACS-FME display improvements *-----------------------------------------------------------------------------*/ @@ -110,13 +113,13 @@ char * DMRBusrtTypes[32] = { "R34D ", "IDLE ", "R1_D ", - "ERR ", + "ERR ", "DUID ERR ", "R-S ERR ", "CRC ERR ", "NULL ", - "VOICE", - " ", + "VOICE", + " ", "INIT ", "INIT ", "PTT", //20 MAC @@ -188,7 +191,7 @@ void beeper (dsd_opts * opts, dsd_state * state, int lr) if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) pa_simple_write(opts->pulse_digi_dev_out, samp_s, 160*2, NULL); - + } else if (opts->audio_out_type == 8) //UDP Audio @@ -204,7 +207,7 @@ void beeper (dsd_opts * opts, dsd_state * state, int lr) if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) udp_socket_blaster (opts, state, 160*2, samp_s); - + } else if (opts->audio_out_type == 1) //STDOUT @@ -245,10 +248,10 @@ void beeper (dsd_opts * opts, dsd_state * state, int lr) samp_su[(i*6)+4] = outbuf[4]; samp_su[(i*6)+5] = outbuf[5]; } - + write (opts->audio_out_fd, samp_su, 960*2); } - + } } @@ -328,7 +331,7 @@ char *choicesc[] = { "Setup and Start RTL Input ", "Retune RTL Dongle ", "Toggle C4FM/QPSK (P2 TDMA CC)", - "Toggle C4FM/QPSK (P1 FDMA CC)", + "Toggle C4FM/QPSK (P1 FDMA CC)", "Start TCP Direct Link Audio", "Configure RIGCTL", "Stop All Decoded WAV Saving", @@ -415,7 +418,7 @@ void ncursesOpen (dsd_opts * opts, dsd_state * state) //this is primarily used to push a quick audio blip through OSS so it will show up in the mixer immediately // if (opts->audio_out_type == 2 || opts->audio_out_type == 5) // beeper (opts, state, 0); //causes crash in Cygwin when mixed input/output - + //terminate all values for (int i = 0; i < 10; i++) sprintf (alias_ch[i], "%s", ""); @@ -462,7 +465,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) closePulseOutput (opts); } - //close OSS output + //close OSS output if (opts->audio_out_type == 2 || opts->audio_out_type == 5) { close (opts->audio_out_fd); @@ -760,7 +763,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->rtl_dev_index = i; break; } - + } entry_win = newwin(17, WIDTH+20, starty+10, startx+10); @@ -861,7 +864,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) // if (opts->audio_out == 0) // { // opts->audio_out = 1; - // opts->audio_out_type = 0; + // opts->audio_out_type = 0; // // state->audio_out_buf_p = 0; // // state->audio_out_buf_pR = 0; // state->audio_out_idx = 0; @@ -933,7 +936,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->audio_in_type = 0; } else opts->audio_in_type = 5; - + } state->audio_smoothing = 0; //disable smoothing to prevent random crackling/buzzing @@ -1222,7 +1225,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) wscanw(entry_win, "%lld", &state->R); noecho(); if (state->R > 0x7FFF) state->R = 0x7FFF; - + state->keyloader = 0; //turn off keyloader } //toggle enforcement of basic privacy key over enc bit set on traffic @@ -1844,7 +1847,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) { openOSSOutput (opts); } - + if (opts->audio_in_type == 0) //reopen pulse input if it is the specified input method { @@ -1854,7 +1857,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) if (opts->audio_in_type == 3) //open rtl input if it is the specified input method { // ncursesPrinter (opts, state); //not sure why this was placed here originally, but causes a double free core dump when calling free(timestr) - #ifdef USE_RTLSDR + #ifdef USE_RTLSDR if (opts->rtl_started == 0) { opts->rtl_started = 1; //set here so ncurses terminal doesn't attempt to open it again @@ -1892,7 +1895,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (opts->audio_in_type != 1) //can't run getch/menu when using STDIN - { - timeout(0); // + timeout(0); // c = getch(); // } @@ -1908,7 +1911,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //Variable reset/set section //set lls sync types - if (state->synctype >= 0 && state->synctype < 39) + if (state->synctype >= 0 && state->synctype < 39) { lls = state->synctype; } @@ -2002,16 +2005,23 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //Edacs - ProVoice if ( (lls == 14 || lls == 15 || lls == 37 || lls == 38) && state->carrier == 1) { - - if (state->edacs_vc_lcn != -1) + + if (state->edacs_vc_lcn != -1) { call_matrix[state->edacs_vc_lcn][0] = lls; call_matrix[state->edacs_vc_lcn][1] = state->edacs_vc_lcn; - call_matrix[state->edacs_vc_lcn][2] = state->lasttg; - call_matrix[state->edacs_vc_lcn][3] = state->lastsrc; - call_matrix[state->edacs_vc_lcn][4] = 1; + 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 (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 || 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] = state->edacs_vc_call_type; call_matrix[state->edacs_vc_lcn][5] = time(NULL); - } + } } @@ -2074,7 +2084,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } } - + //TODO: Find better placement for these if ( strcmp(state->str50a, "") != 0 ) sprintf (alias_ch[9], "%s", state->str50a); @@ -2257,7 +2267,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // state->lastsrc = 0; // rd = 0; // tg = 0; - // } + // } // if (state->dmrburstR == 2 && state->dmr_end_alert[1] == 0) //if TLC and flag not tripped // { // beeper (opts, state, 1); @@ -2266,7 +2276,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // state->lastsrcR = 0; // rdR = 0; // tgR = 0; - // } + // } // } //Start Printing Section @@ -2276,7 +2286,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { printw ("------------------------------------------------------------------------------\n"); printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20231015)"); - printw ("------------------------------------------------------------------------------\n"); + printw ("------------------------------------------------------------------------------\n"); } #elif LIMAZULUTWEAKS if (opts->ncurses_compact == 1) @@ -2318,7 +2328,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) #elif ZDEV_BUILD if (i == 5) printw (" %s ", "AW "); if (i == 6) printw (" %s \n", GIT_TAG); - #else + #else if (i == 5) printw (" %s ", "AW "); if (i == 6) printw (" %s \n", GIT_TAG); #endif @@ -2383,7 +2393,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw (" SQ: %i;", opts->rtl_squelch_level); printw (" RMS: %04li;", opts->rtl_rms); printw (" BW: %i kHz;", opts->rtl_bandwidth); - printw (" FRQ: %i;", opts->rtlsdr_center_freq); + printw (" FRQ: %i;", opts->rtlsdr_center_freq); if (opts->rtl_udp_port != 0) printw ("\n| External RTL Tuning on UDP Port: %i", opts->rtl_udp_port); printw ("\n"); } @@ -2505,6 +2515,66 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) else printw (" - Black List Mode\n"); } #endif + //print additional information for EDACS modes and toggles + #ifdef PRETTY_COLORS + if (opts->p25_trunk == 1 && opts->frame_provoice == 1) + { + printw ("| \\--EDACS -"); + if (state->ea_mode == -1) + { + attron(COLOR_PAIR(2)); + printw (" Standard/Network"); + printw (" Extended Address"); + attron(COLOR_PAIR(4)); + } + else if (state->ea_mode == 0) + { + printw (" Standard/Network"); + attron(COLOR_PAIR(2)); + printw (" Extended Address"); + attron(COLOR_PAIR(4)); + } + else if (state->ea_mode == 1) + { + attron(COLOR_PAIR(2)); + printw (" Standard/Network"); + attron(COLOR_PAIR(4)); + printw (" Extended Address"); + } + printw (" Mode (S);"); + + printw(" ESK Mask: %02X", state->esk_mask); + printw (" (A); "); + + attron(COLOR_PAIR(4)); + printw ("\n"); + } + #else //set on to UPPER CASE, off to lower case + if (opts->p25_trunk == 1 && opts->frame_provoice == 1) + { + printw ("| \\--EDACS -"); + if (state->ea_mode == -1) + { + printw (" standard/network"); + printw (" extended address"); + } + else if (state->ea_mode == 0) + { + printw (" STANDARD/NETWORK"); + printw (" extended address"); + } + else if (state->ea_mode == 1) + { + printw (" standard/network"); + printw (" EXTENDED ADDRESS"); + } + printw (" Mode (S);"); + + printw(" ESK Mask: %02X", state->esk_mask); + printw (" (A) Toggle; "); + printw ("\n"); + } + #endif // if (opts->aggressive_framesync == 0) printw ("| Selective CRC ERR Bypass Enabled (RAS) \n"); if (state->M == 1) { @@ -2521,9 +2591,9 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (opts->scanner_mode == 1) { printw ("| Scan Mode: "); - if (state->lcn_freq_roll != 0) + if (state->lcn_freq_roll != 0) printw (" Frequency: %.06lf Mhz", (double)state->trunk_lcn_freq[state->lcn_freq_roll-1]/1000000); - printw (" Speed: %.02lf sec \n", opts->trunk_hangtime); //not sure values less than 1 make a difference, may be system/environment dependent + printw (" Speed: %.02lf sec \n", opts->trunk_hangtime); //not sure values less than 1 make a difference, may be system/environment dependent } if (opts->reverse_mute == 1) printw ("| Reverse Mute - Muting Unencrypted Voice\n"); @@ -2565,7 +2635,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } printw ("\n"); printw ("| In Level: [%02d%%] \n", level); - + if (opts->dmr_stereo == 0) { printw ("| Voice Error: [%X][%X]", state->errs&0xF, state->errs2&0xF); @@ -2588,7 +2658,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw ("------------------------------------------------------------------------------\n"); printw ("--Call Info-------------------------------------------------------------------\n"); - + //DSTAR if (lls == 6 || lls == 7 || lls == 18 || lls == 19) { @@ -2629,7 +2699,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw("RESERVED (%012llx) ", state->m17_dst); else printw("%s", state->m17_dst_str); - + printw ("\n"); printw ("| "); @@ -2639,7 +2709,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) else printw("%s", state->m17_src_str); - + printw ("\n"); printw ("| "); @@ -2666,7 +2736,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (state->m17_enc == 3) { printw (" Reserved Enc - Type: %d", state->m17_enc_st); - } + } printw ("\n"); @@ -2727,7 +2797,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // printw ("%c", state->ysf_txt[i][j]); // } // // printw (" "); //just a single space between each 'block' - // } + // } printw ("\n"); @@ -2750,7 +2820,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { printw (" - Frequency: %.06lf Mhz ", (double)state->p25_cc_freq/1000000); } - } + } else if (opts->p25_is_tuned == 1) { if (idas == 0) printw ("Monitoring RTCH Channel"); //Traffic Channel @@ -2763,8 +2833,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw ("\n"); } - - + + printw ("| "); // #ifdef LIMAZULUTWEAKS @@ -2862,10 +2932,10 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } //Active Trunking Channels (NXDN and IDAS) - if (1 == 1) //opts->p25_trunk + if (1 == 1) //opts->p25_trunk { printw ("\n"); - printw ("| "); + printw ("| "); //active channel display attron(COLOR_PAIR(4)); @@ -2883,8 +2953,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw("\n"); } - - //P25 and DMR BS/MS + + //P25 and DMR BS/MS if ( lls == 0 || lls == 1 || lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32 || lls == 33 || lls == 34 || lls == 35 || lls == 36) { @@ -2895,7 +2965,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // printw ("%s %s", state->dmr_branding, state->dmr_branding_sub); printw ("%s ", state->dmr_branding); printw ("%s", state->dmr_branding_sub); - printw ("%s", state->dmr_site_parms); //site id, net id, etc + printw ("%s", state->dmr_site_parms); //site id, net id, etc if (state->dmr_rest_channel > 0) { printw ("Rest LSN: %02d; ", state->dmr_rest_channel); @@ -2908,7 +2978,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { printw ("Freq: %.06lf MHz", (double)state->p25_cc_freq/1000000); } - + } else if (lls == 32 || lls == 33 || lls == 34) { @@ -2937,7 +3007,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw (" Phase 2 Invalid Parameters "); attron(COLOR_PAIR(3)); } - else + else { if (state->p25_cc_freq != 0) { @@ -3059,7 +3129,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //Not always correct, or correct at all, depends on context //this is already in the call_string anyways - + // if(state->dmrburstL == 16 && state->dmr_so == 0x40 && state->R == 0) //0100 0000 // { // attron(COLOR_PAIR(2)); @@ -3096,13 +3166,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //Embedded GPS (not LRRP) printw ("%s ", state->dmr_embedded_gps[0]); - + //Embedded Talker Alias Blocks for (int i = 0; i < 4; i++) { for (int j = 0; j < 7; j++) { - printw ("%s", state->dmr_alias_block_segment[0][i][j]); + printw ("%s", state->dmr_alias_block_segment[0][i][j]); } } @@ -3145,7 +3215,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //Slot 2 [1] -- Also Including DMR MS now to keep the display more 'uniform' in nature // if (lls < 30 || lls == 35 || lls == 36) - { + { printw ("| SLOT 2 - "); if (state->dmrburstR < 16 && state->carrier == 1 && state->lasttgR > 0 && state->lastsrcR > 0) { @@ -3161,10 +3231,10 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw ("%s | ", state->call_string[1]); printw ("%s ", DMRBusrtTypes[state->dmrburstR]); - if (opts->slot_preference == 0 && opts->audio_out_type == 5 && opts->audio_out == 1 && ( state->dmrburstL == 16 || state->dmrburstL == 21) && (state->dmrburstR == 16 || state->dmrburstR == 21) ) printw ("*M*"); + if (opts->slot_preference == 0 && opts->audio_out_type == 5 && opts->audio_out == 1 && ( state->dmrburstL == 16 || state->dmrburstL == 21) && (state->dmrburstR == 16 || state->dmrburstR == 21) ) printw ("*M*"); printw ("\n"); - + printw ("| V XTRA | "); //10 spaces if(state->dmrburstR == 16 && state->payload_algidR == 0 && (state->dmr_soR & 0xCF) == 0x40) //4F or CF mask? @@ -3285,7 +3355,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if(state->dmrburstR == 16 || state->dmrburstR == 21) //only during call { - + //Embedded GPS (not LRRP) attron(COLOR_PAIR(4)); printw ("%s ", state->dmr_embedded_gps[1]); @@ -3295,7 +3365,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { for (int j = 0; j < 7; j++) { - printw ("%s", state->dmr_alias_block_segment[1][i][j]); + printw ("%s", state->dmr_alias_block_segment[1][i][j]); } } @@ -3313,7 +3383,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) attron(COLOR_PAIR(4)); printw ("%s", state->dmr_lrrp_gps[1]); } - + //Group Name Labels from CSV import if (state->dmrburstR == 16 || state->dmrburstR > 19) { @@ -3401,7 +3471,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //EDACS and ProVoice if (lls == 14 || lls == 15 || lls == 37 || lls == 38) { - attroff (COLOR_PAIR(3)); //colors off for EDACS + attroff (COLOR_PAIR(3)); //colors off for EDACS if (state->edacs_site_id != 0) { if (opts->p25_is_tuned == 0) @@ -3412,8 +3482,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { printw ("| Monitoring Voice Channel - LCN [%02d]\n", state->edacs_tuned_lcn); //since we are tuned, keep updating the time so it doesn't disappear during call - call_matrix[state->edacs_tuned_lcn][5] = time(NULL); - } + call_matrix[state->edacs_tuned_lcn][5] = time(NULL); + } printw ("| SITE [%03lld][%02llX]", state->edacs_site_id, state->edacs_site_id); if (state->ea_mode == 1) @@ -3430,11 +3500,11 @@ 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; - printw ("| - LCN [%02d][%.06lf] MHz", i, (double)state->trunk_lcn_freq[i-1]/1000000); - + // 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][%010.06lf] MHz", i, (double)state->trunk_lcn_freq[i-1]/1000000); + //print Control Channel on LCN line with the current Control Channel if ( (i) == state->edacs_cc_lcn) { @@ -3442,12 +3512,82 @@ 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) + if ((i != state->edacs_cc_lcn) && time(NULL) - call_matrix[i][5] < 2) { - attron (COLOR_PAIR(3)); - if (state->ea_mode == 1) printw (" TG [%5lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] ); - else printw (" AFS [%03lld][%02d-%03d]", call_matrix[i][3], a, fs ); + print_call = 3; + attron (COLOR_PAIR(3)); + } + //print dying or dead calls in red for x seconds longer + 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) { + // Voice call + if ((call_matrix[i][4] & EDACS_IS_VOICE) != 0) + { + // System all-call + if ((call_matrix[i][4] & EDACS_IS_GROUP) != 0) + printw (" TGT [%8lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] ); + // I-Call + 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 + // Data call + printw (" TGT [ DATA ] SRC [%8lld] Data", call_matrix[i][3] ); + } + else + { + // Voice call + if ((call_matrix[i][4] & EDACS_IS_VOICE) != 0) + { + // Group call + 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++) { if (state->group_array[k].groupNumber == call_matrix[i][2] && call_matrix[i][2] != 0) @@ -3463,37 +3603,19 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) break; } } - attroff (COLOR_PAIR(3)); + + if (print_call == 3) + attroff (COLOR_PAIR(3)); + else if (print_call == 2) + attroff (COLOR_PAIR(2)); } - //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) ) - { - attron (COLOR_PAIR(2)); - if (state->ea_mode == 1) printw (" TG [%5lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] ); - else printw (" AFS [%03lld][%02d-%03d]", call_matrix[i][3], a, fs ); - 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(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) { - attron (COLOR_PAIR(3)); + attron (COLOR_PAIR(3)); } } @@ -3585,23 +3707,76 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //EDACS and ProVoice, outside of timestamp loop if (call_matrix[j][0] == 14 || call_matrix[j][0] == 15 || call_matrix[j][0] == 37 || call_matrix[j][0] == 38 ) { - if (call_matrix[j][3] != 0) + if (call_matrix[j][2] != 0) { printw ("| "); - printw ("%s ", getDateC(call_matrix[j][5]) ); + printw ("%s ", getDateC(call_matrix[j][5]) ); printw ("%s ", getTimeC(call_matrix[j][5]) ); printw ("LCN [%2lld] ", call_matrix[j][1]); if (state->ea_mode == 1) { - printw ("Group [%8lld] ", call_matrix[j][2]); - printw ("Source [%8lld] ", call_matrix[j][3]); + // Voice call + if ((call_matrix[j][4] & EDACS_IS_VOICE) != 0) + { + // Group call + 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 + 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 ); - } + // 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 + 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++) { @@ -3621,7 +3796,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) //end test printw ("\n"); } - + } } //end Call History //fence bottom @@ -3678,7 +3853,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) else if (opts->slot1_on == 0) { opts->slot1_on = 1; - if (opts->audio_out_type == 5) //OSS 48k/1 + if (opts->audio_out_type == 5) //OSS 48k/1 { opts->slot_preference = 0; //slot 1 // opts->slot2_on = 0; //turn off slot 2 @@ -3704,7 +3879,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) else if (opts->slot2_on == 0) { opts->slot2_on = 1; - if (opts->audio_out_type == 5) //OSS 48k/1 + if (opts->audio_out_type == 5) //OSS 48k/1 { opts->slot_preference = 1; //slot 2 // opts->slot1_on = 0; //turn off slot 1 @@ -3910,14 +4085,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { fprintf (stderr, "-T %s wav file directory does not exist\n", wav_file_directory); fprintf (stderr, "Creating directory %s to save decoded wav files\n", wav_file_directory); - mkdir(wav_file_directory, 0700); + mkdir(wav_file_directory, 0700); } opts->dmr_stereo_wav = 1; //catch all in case of no file name set, won't crash or something sprintf (opts->wav_out_file, "./%s/DSD-FME-T1.wav", opts->wav_out_dir); sprintf (opts->wav_out_fileR, "./%s/DSD-FME-T2.wav", opts->wav_out_dir); - openWavOutFileL (opts, state); - openWavOutFileR (opts, state); + openWavOutFileL (opts, state); + openWavOutFileR (opts, state); } //this one could cause issues, but seems okay @@ -3934,15 +4109,15 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // #ifdef AERO_BUILD //this might be okay on Aero as well, will need to look into and/or test - // + // #else if (c == 115) //'s' key, stop playing wav or symbol in files { if (opts->symbolfile != NULL) { - if (opts->audio_in_type == 4) + if (opts->audio_in_type == 4) { - fclose(opts->symbolfile); + fclose(opts->symbolfile); } } @@ -3956,12 +4131,12 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) opts->audio_in_type = 0; openPulseInput(opts); } - else opts->audio_in_type = 5; //exitflag = 1; - + else opts->audio_in_type = 5; //exitflag = 1; + } #endif - - //makes buzzing sound when locked out in new audio config and short, probably something to do with processaudio running or not running + + //makes buzzing sound when locked out in new audio config and short, probably something to do with processaudio running or not running if (state->lasttg != 0 && opts->frame_provoice != 1 && c == 33) //SHIFT+'1' key (exclamation point), lockout slot 1 or conventional tg from tuning/playback during session { state->group_array[state->group_tally].groupNumber = state->lasttg; @@ -4097,40 +4272,40 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) // if (c == 48) //'0' key, toggle upsampled audio smoothing // { // if (state->audio_smoothing == 1) state->audio_smoothing = 0; - // else state->audio_smoothing = 1; + // else state->audio_smoothing = 1; // } - if (opts->p25_trunk == 1 && c == 119) //'w' key, toggle white list/black list mode + if (opts->p25_trunk == 1 && c == 119) //'w' key, toggle white list/black list mode { if (opts->trunk_use_allow_list == 1) opts->trunk_use_allow_list = 0; - else opts->trunk_use_allow_list = 1; + else opts->trunk_use_allow_list = 1; } if (opts->p25_trunk == 1 && c == 117) //'u' key, toggle tune private calls { if (opts->trunk_tune_private_calls == 1) opts->trunk_tune_private_calls = 0; - else opts->trunk_tune_private_calls = 1; + else opts->trunk_tune_private_calls = 1; } if (opts->p25_trunk == 1 && c == 100) //'d' key, toggle tune data calls { if (opts->trunk_tune_data_calls == 1) opts->trunk_tune_data_calls = 0; - else opts->trunk_tune_data_calls = 1; + else opts->trunk_tune_data_calls = 1; } if (opts->p25_trunk == 1 && c == 101) //'e' key, toggle tune enc calls (P25 only on certain grants) { if (opts->trunk_tune_enc_calls == 1) opts->trunk_tune_enc_calls = 0; - else opts->trunk_tune_enc_calls = 1; + else opts->trunk_tune_enc_calls = 1; } if (opts->p25_trunk == 1 && c == 103) //'g' key, toggle tune group calls { if (opts->trunk_tune_group_calls == 1) opts->trunk_tune_group_calls = 0; - else opts->trunk_tune_group_calls = 1; + else opts->trunk_tune_group_calls = 1; } - if (c == 70) //'F' key - toggle agressive sync/crc failure/ras + if (c == 70) //'F' key - toggle agressive sync/crc failure/ras { if (opts->aggressive_framesync == 0) opts->aggressive_framesync = 1; else opts->aggressive_framesync = 0; @@ -4138,7 +4313,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (c == 68) //'D' key - Reset DMR Site Parms/Call Strings, etc. { - //dmr trunking/ncurses stuff + //dmr trunking/ncurses stuff state->dmr_rest_channel = -1; //init on -1 state->dmr_mfid = -1; // @@ -4200,7 +4375,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) if (opts->audio_in_type == 0) closePulseInput(opts); fprintf (stderr, "TCP Socket Connected Successfully.\n"); opts->audio_in_type = 8; - } + } } else fprintf (stderr, "TCP Socket Connection Error.\n"); } @@ -4272,7 +4447,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) state->symbolCenter = 3; } - fprintf (stderr, "\n User Activated Return to CC; \n "); + fprintf (stderr, "\n User Activated Return to CC; \n "); } @@ -4318,15 +4493,15 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { state->lcn_freq_roll++; //check roll again if greater than expected, then go back to zero - if (state->lcn_freq_roll >= state->lcn_freq_count) + if (state->lcn_freq_roll >= state->lcn_freq_count) { state->lcn_freq_roll = 0; //reset to zero - } + } } } //check that we have a non zero value first, then tune next frequency - if (state->trunk_lcn_freq[state->lcn_freq_roll] != 0) + if (state->trunk_lcn_freq[state->lcn_freq_roll] != 0) { //rigctl if (opts->use_rigctl == 1) @@ -4343,7 +4518,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) } fprintf (stderr, "\n User Activated Channel Cycle;"); - fprintf (stderr, " Tuning to Frequency: %.06lf MHz\n", + fprintf (stderr, " Tuning to Frequency: %.06lf MHz\n", (double)state->trunk_lcn_freq[state->lcn_freq_roll]/1000000); } @@ -4365,22 +4540,48 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) { state->samplesPerSymbol = 8; state->symbolCenter = 3; - } + } } - if (opts->m17encoder == 1 && c == 92) //'\' key - toggle M17 encoder Encode + TX + if (opts->m17encoder == 1 && c == 92) //'\' key - toggle M17 encoder Encode + TX { if (state->m17encoder_tx == 0) state->m17encoder_tx = 1; else state->m17encoder_tx = 0; //flag on the EOT marker to send last frame after toggling encoder to zero if (state->m17encoder_tx == 0) state->m17encoder_eot = 1; - + + } + + if(opts->frame_provoice == 1 && c == 65) //'A' Key, toggle ESK mask 0xA0 + { + if (state->esk_mask == 0) state->esk_mask = 0xA0; + else state->esk_mask = 0; + } + + if(opts->frame_provoice == 1 && c == 83) //'S' Key, toggle STD or EA mode and reset + { + if (state->ea_mode == -1) state->ea_mode = 0; + else if (state->ea_mode == 0) state->ea_mode = 1; + else state->ea_mode = 0; + + //reset -- test to make sure these don't do weird things when reset + state->edacs_site_id = 0; + state->edacs_lcn_count = 0; + 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; + state->lastsrc = 0; + } //anything with an entry box will need the inputs and outputs stopped first - //so probably just write a function to handle c input, and when c = certain values + //so probably just write a function to handle c input, and when c = certain values //needing an entry box, then stop all of those //allocated memory pointer needs to be free'd each time diff --git a/src/edacs-fme.c b/src/edacs-fme.c index 0670f94..e84c5dd 100644 --- a/src/edacs-fme.c +++ b/src/edacs-fme.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------------- - * EDACS-FME + * EDACS-FME * A program for decoding EDACS (ported to DSD-FME) * https://github.com/lwvmobile/edacs-fm * @@ -15,6 +15,9 @@ * * LWVMOBILE * 2023-11 Version EDACS-FM Florida Man Edition + * + * ilyacodes + * 2024-03 rewrite EDACS standard parsing to spec, add reverse-engineered EA messages *-----------------------------------------------------------------------------*/ #include "dsd.h" @@ -50,6 +53,22 @@ char * getTimeE(void) //get pretty hhmmss timestamp return curr; } +char* get_lcn_status_string(int lcn) +{ + if (lcn == 26 || lcn == 27) + return "[Reserved LCN Status]"; + if (lcn == 28) + return "[Convert To Callee]"; + else if (lcn == 29) + return "[Call Queued]"; + else if (lcn == 30) + return "[System Busy]"; + else if (lcn == 31) + return "[Call Denied]"; + else + return ""; +} + void openWavOutFile48k (dsd_opts * opts, dsd_state * state) { UNUSED(state); @@ -69,7 +88,7 @@ void openWavOutFile48k (dsd_opts * opts, dsd_state * state) //generic rms type function long int gen_rms(short *samples, int len, int step) { - + int i; long int rms; long p, t, s; @@ -165,7 +184,7 @@ void analog_dc_block_filter(short * input, int len) avg = sum / len; avg = (avg + dc_avg * 9) / 10; for (i=0; i < len; i++) - input[i] -= (short)avg; + input[i] -= (short)avg; dc_avg = avg; } @@ -183,7 +202,7 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn short analog1[960]; short analog2[960]; short analog3[960]; - short sample = 0; + short sample = 0; state->last_cc_sync_time = time(NULL); state->last_vc_sync_time = time(NULL); @@ -198,7 +217,7 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn fprintf (stderr, "\n"); while (!exitflag && count > 0) - { + { //this will only work on 48k/1 short output if (opts->audio_in_type == 0) { @@ -224,7 +243,7 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn } //NOTE: The core dumps observed previously were due to SDR++ Remote Server connection dropping due to Internet/Other issues - //and unlike in the main livescanner loop where it just hangs, this loop will cause a core dump. The observed issue + //and unlike in the main livescanner loop where it just hangs, this loop will cause a core dump. The observed issue //has not occurred when using SDR++ on local hardware, just the remote server software over the Internet. //NOTE: The fix below does not apply to above observed issue, as the TCP connection will not drop, there will just @@ -272,7 +291,7 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn } analog3[i] = sample; } - + //this rms will only work properly (for now) with squelch enabled in SDR++ rms = gen_rms(analog3, 960, 1); } @@ -371,8 +390,15 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn else fprintf(stderr, "%s", KRED); fprintf (stderr, " Analog RMS: %04ld SQL: %ld", rms, sql); - if (afs != 0) + if (state->ea_mode == 0) + { fprintf (stderr, " AFS [%03d] [%02d-%03d] LCN [%02d]", afs, afs >> 7, afs & 0x7F, lcn); + } + else + { + if (afs == -1) fprintf (stderr, " TGT [ SYSTEM ] LCN [%02d] All-Call", lcn); + else fprintf (stderr, " TGT [%08d] LCN [%02d]", afs, lcn); + } //debug, view hit counter // fprintf (stderr, " CNT: %d; ", count); @@ -401,7 +427,7 @@ void edacs_analog(dsd_opts * opts, dsd_state * state, int afs, unsigned char lcn fprintf (stderr, "%s", KNRM); if (count > 0) fprintf (stderr, "\n"); - + } } @@ -411,7 +437,7 @@ void edacs(dsd_opts * opts, dsd_state * state) unsigned long long int fr_1 = 0xFFFFFFFFFF; //40-bit frames unsigned long long int fr_2 = 0; //each is a 40 bit frame that repeats 3 times unsigned long long int fr_3 = 0; //two messages per frame - unsigned long long int fr_4 = 0xFFFFFFFFFF; + unsigned long long int fr_4 = 0xFFFFFFFFFF; unsigned long long int fr_5 = 0; unsigned long long int fr_6 = 0; @@ -421,17 +447,13 @@ void edacs(dsd_opts * opts, dsd_state * state) unsigned long long int fr_4m = 0xFFFFFFF; //28-bit 7X message portion to pass to bch handler unsigned long long int fr_4t = 0xFFFFFFFFFF; //40 bit return from BCH with poly attached - unsigned char command = 0xFF; - unsigned char mt1 = 0x1F; - unsigned char mt2 = 0xF; - unsigned char lcn = 0; - //commands; may not use these anymore unsigned int vcmd = 0xEE; //voice command variable unsigned int idcmd = 0xFD; unsigned int peercmd = 0xF88; //using for EA detection test unsigned int netcmd = 0xF3; //using for Networked Test UNUSED2(vcmd, idcmd); + UNUSED2(peercmd, netcmd); char * timestr; //add timestr here, so we can assign it and also free it to prevent memory leak timestr = getTimeE(); @@ -451,18 +473,18 @@ void edacs(dsd_opts * opts, dsd_state * state) { //only fr_1 and fr4 are going to matter fr_1 = fr_1 << 1; - fr_1 = fr_1 | edacs_bit[i]; + fr_1 = fr_1 | edacs_bit[i]; fr_2 = fr_2 << 1; fr_2 = fr_2 | edacs_bit[i+40]; fr_3 = fr_3 << 1; fr_3 = fr_3 | edacs_bit[i+80]; - - fr_4 = fr_4 << 1; - fr_4 = fr_4 | edacs_bit[i+120]; + + fr_4 = fr_4 << 1; + fr_4 = fr_4 | edacs_bit[i+120]; fr_5 = fr_5 << 1; fr_5 = fr_5 | edacs_bit[i+160]; fr_6 = fr_6 << 1; - fr_6 = fr_6 | edacs_bit[i+200]; + fr_6 = fr_6 | edacs_bit[i+200]; } @@ -486,136 +508,252 @@ void edacs(dsd_opts * opts, dsd_state * state) else //BCH Pass, continue from here. { - //ESK on/off detection, I honestly don't remember the logic for this anymore, but it works fine - if ( (((fr_1t & 0xF000000000) >> 36) != 0xB) && (((fr_1t & 0xF000000000) >> 36) != 0x1) && (((fr_1t & 0xFF00000000) >> 32) != 0xF3) ) - { - //experimenting with values here, not too high, and not too low - if ( (((fr_1t & 0xF000000000) >> 36) <= 0x8 )) - { - state->esk_mask = 0xA0; - } - //ideal value would be 5, but some other values exist that don't allow it - if ( (((fr_1t & 0xF000000000) >> 36) > 0x8 ) ) - { - state->esk_mask = 0x0; - } - } + //Auto Detection Modes Have Been Removed due to reliability issues, + //users will now need to manually specify these options: + /* + -fh Decode only EDACS Standard/ProVoice*\n"); + -fH Decode only EDACS Standard/ProVoice with ESK 0xA0*\n"); + -fe Decode only EDACS EA/ProVoice*\n"); + -fE Decode only EDACS EA/ProVoice with ESK 0xA0*\n"); - //Standard/Networked Auto Detection - //if (command == netcmd) //netcmd is F3 Standard/Networked (I think) - if ( (fr_1t >> 32 == netcmd) || (fr_1t >> 32 == (netcmd ^ 0xA0)) ) - { - state->ea_mode = 0; //disable extended addressing mode - } + (A) key toggles mode; (S) key toggles mask value in ncurses + */ - //EA Auto detection //peercmd is 0xF88 peer site relay 0xFF80000000 >> 28 - if (fr_1t >> 28 == peercmd || fr_1t >> 28 == (peercmd ^ 0xA00) ) - { - state->ea_mode = 1; //enable extended addressing mode - } + //TODO: Consider re-adding the auto code to make a suggestion to users + //as to which mode to proceed in? - //Start Extended Addressing Mode + //Color scheme: + // - KRED - critical information (emergency, failsoft, etc) + // - KYEL - system data + // - KGRN - voice group calls + // - KCYN - voice individual calls + // - KMAG - voice other calls (interconnect, all-call, etc) + // - KBLU - subscriber data + // - KWHT - unknown/reserved + + //Account for ESK, if any + unsigned long long int fr_esk_mask = ((unsigned long long int)state->esk_mask) << 32; + fr_1t = fr_1t ^ fr_esk_mask; + fr_4t = fr_4t ^ fr_esk_mask; + + //Start Extended Addressing Mode if (state->ea_mode == 1) { - command = ((fr_1t & 0xFF00000000) >> 32) ^ state->esk_mask; - mt1 = (command & 0xF8) >> 3; - mt2 = (fr_1t & 0x780000000) >> 31; - lcn = (fr_1t & 0x3E0000000) >> 29; //only valid during calls, status + unsigned char mt1 = (fr_1t & 0xF800000000) >> 35; + unsigned char mt2 = (fr_1t & 0x780000000) >> 31; - //Site ID + 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 - if (mt1 == 0x1F && mt2 == 0xA) + unsigned char lcn = 0; + + //Add raw payloads and MT1/MT2 for easy debug + if (opts->payload == 1) { - site_id = ((fr_1 & 0x1F000) >> 12) | ((fr_1 & 0x1F000000) >> 19); - fprintf (stderr, "%s", KYEL); - fprintf (stderr, " Site ID [%02llX][%03lld] Extended Addressing", site_id, site_id); + fprintf (stderr, " FR_1 [%010llX]", fr_1t); + fprintf (stderr, " FR_4 [%010llX]", fr_4t); + fprintf (stderr, " (MT1: %02X", mt1); + // MT2 is meaningless if MT1 is not 0x1F + if (mt1 == 0x1F) + fprintf (stderr, "; MT2: %X) ", mt2); + else + fprintf (stderr, ") "); + } + + //MT1 of 0x1F indicates to use MT2 for the opcode. See US patent US7546135B2, Figure 2b. + if (mt1 == 0x1F) + { + + //Test Call (not seen in the wild, see US patent US7546135B2, Figure 2b) + if (mt2 == 0x0) + { + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Initiate Test Call"); + fprintf (stderr, "%s", KNRM); + } + //Adjacent Sites + else if (mt2 == 0x1) + { + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Adjacent Site"); + if ( ((fr_1t & 0xFF000) >> 12) > 0 ) + { + int adj = (fr_1t & 0xFF000) >> 12; + int adj_l = (fr_1t & 0x1F000000) >> 24; + fprintf (stderr, " :: Site ID [%02X][%03d] on CC LCN [%02d]%s", adj, adj, adj_l, get_lcn_status_string(lcn)); + } + fprintf (stderr, "%s", KNRM); + } + //Status/Message + else if (mt2 == 0x4) + { + int status = (fr_1t & 0xFF000) >> 12; + int source = (fr_4t & 0xFFFFF000) >> 12; + fprintf (stderr, "%s", KBLU); + if (status == 248) fprintf (stderr, " Status Request :: Target [%08d]", source); + else fprintf (stderr, " Message Acknowledgement :: Status [%03d] Source [%08d]", status, source); + fprintf (stderr, "%s", KNRM); + } + //Control Channel LCN + else if (mt2 == 0x8) + { + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Control Channel"); + if (((fr_4t >> 12) & 0x1F) != 0) + { + state->edacs_cc_lcn = ((fr_4t >> 12) & 0x1F); + if (state->edacs_cc_lcn > state->edacs_lcn_count && lcn < 26) //26, or 27. shouldn't matter don't think cc lcn will give a status lcn val + { + state->edacs_lcn_count = state->edacs_cc_lcn; + } + fprintf (stderr, " :: LCN [%d]%s", state->edacs_cc_lcn, get_lcn_status_string(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 + } + } + fprintf (stderr, "%s", KNRM); + } + //Site ID + else if (mt2 == 0xA) + { + site_id = ((fr_1 & 0x1F000) >> 12) | ((fr_1 & 0x1F000000) >> 19); + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Extended Addressing :: Site ID [%02llX][%03lld]", site_id, site_id); + fprintf (stderr, "%s", KNRM); + state->edacs_site_id = site_id; + } + //disabling kick command, data looks like its just FFFF, no actual values, can't verify accuracy + // else if (mt2 == 0xB) //KICK LISTING for EA?? Unverified, but probably observed in Unitrunker way back when. + // { + // int kicked = (fr_4t & 0xFFFFF000) >> 12; + // fprintf (stderr, " FR_1 [%010llX]", fr_1t); + // fprintf (stderr, " FR_3 [%010llX]", fr_3); + // fprintf (stderr, " FR_4 [%010llX]", fr_4t); + // fprintf (stderr, " FR_6 [%010llX]", fr_6); + // fprintf (stderr, " %08d REG/DEREG/AUTH?", kicked); + // } + //Patch Groups + else if (mt2 == 0xC) + { + int patch_site = ((fr_4t & 0xFF00000000) >> 32); //is site info valid, 0 for all sites? else patch only good on site listed? + int sourcep = ((fr_1t & 0xFFFF000) >> 12); + int targetp = ((fr_4t & 0xFFFF000) >> 12); + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Patch :: Site [%d] Source [%d] Target [%d] ", patch_site, sourcep, targetp); + fprintf (stderr, "%s", KNRM); + } + //Serial Number Request (not seen in the wild, see US patent 20030190923, Figure 2b) + else if (mt2 == 0xD) + { + fprintf (stderr, "%s", KYEL); + fprintf (stderr, " Serial Number Request"); + fprintf (stderr, "%s", KNRM); + } + else + { + fprintf (stderr, "%s", KWHT); + fprintf (stderr, " Unknown Command"); + 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); + } + } + + } + //TDMA Group Grant Update (never observed, unknown if ever used on any EDACS system) + else if (mt1 == 0x1) + { + 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", KNRM); - state->edacs_site_id = site_id; } - //Patch Groups - else if (mt1 == 0x1F && mt2 == 0xC) + //Data Group Grant Update + else if (mt1 == 0x2) { - int patch_site = ((fr_4t & 0xFF00000000) >> 32); //is site info valid, 0 for all sites? else patch only good on site listed? - int sourcep = ((fr_1t & 0xFFFF000) >> 12); - int targetp = ((fr_4t & 0xFFFF000) >> 12); - fprintf (stderr, " Patch -- Site [%d] Source [%d] Target [%d] ", patch_site, sourcep, targetp); + 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", KNRM); } - //Adjacent Sites - else if (mt1 == 0x1F && mt2 == 0x1) - { - fprintf (stderr, " Adjacent Site"); - if ( ((fr_1t & 0xFF000) >> 12) > 0 ) - { - int adj = (fr_1t & 0xFF000) >> 12; - int adj_l = (fr_1t & 0x1F000000) >> 24; - fprintf (stderr, " [%02X][%03d] on CC LCN [%02d]", adj, adj, adj_l); - } - } - //Control Channel LCN - else if (mt1 == 0x1F && mt2 == 0x8) - { - fprintf (stderr, " Control Channel LCN"); - if (((fr_4t >> 12) & 0x1F) != 0) - { - state->edacs_cc_lcn = ((fr_4t >> 12) & 0x1F); - if (state->edacs_cc_lcn > state->edacs_lcn_count && lcn < 26) //26, or 27. shouldn't matter don't think cc lcn will give a status lcn val - { - state->edacs_lcn_count = state->edacs_cc_lcn; - } - fprintf (stderr, " [%d]", 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 - } - } - } - //disabling kick command, data looks like its just FFFF, no actual values, can't verify accuracy - // else if (mt1 == 0x1F && mt2 == 0xB) //KICK LISTING for EA?? Unverified, but probably observed in Unitrunker way back when. - // { - // int kicked = (fr_4t & 0xFFFFF000) >> 12; - // fprintf (stderr, " FR_1 [%010llX]", fr_1t); - // fprintf (stderr, " FR_3 [%010llX]", fr_3); - // fprintf (stderr, " FR_4 [%010llX]", fr_4t); - // fprintf (stderr, " FR_6 [%010llX]", fr_6); - // fprintf (stderr, " Kick Command?"); - // } //Voice Call Grant Update - // mt1 0x3 is Digital group voice call, 0x2 Group Data Channel, 0x1 TDMA call - else if (mt1 >= 0x1 && mt1 <= 0x3) + // MT1 value determines the type of group call: + // - 0x03 digital group voice (ProVoice, standard on SLERS EA) + // - 0x06 analog group voice + else if (mt1 == 0x3 || mt1 == 0x6) { + lcn = (fr_1t & 0x3E0000000) >> 29; + //LCNs greater than 26 are considered status values, "Busy, Queue, Deny, etc" - if (lcn > state->edacs_lcn_count && lcn < 26) + if (lcn > state->edacs_lcn_count && lcn < 26) { state->edacs_lcn_count = lcn; } + int is_digital = (mt1 == 0x3) ? 1 : 0; + int is_update = (fr_1t & 0x10000000) >> 28; + int is_emergency = (fr_4t & 0x100000000) >> 32; + int is_tx_trunking = (fr_4t & 0x200000000) >> 33; int group = (fr_1t & 0xFFFF000) >> 12; int source = (fr_4t & 0xFFFFF000) >> 12; - if (group != 0) state->lasttg = group; - if (source != 0) state->lastsrc = source; + + //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; + + //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); - fprintf (stderr, " Group [%05d] Source [%08d] LCN[%02d]", group, source, lcn); + if (is_digital == 0) fprintf (stderr, " Analog Group Call"); + 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: + // - emergency calls - usually message trunking + // - normal calls - usually transmission trunking + if (is_tx_trunking == 0) fprintf (stderr, " [Message Trunking]"); + if (is_emergency == 1) + { + fprintf (stderr, "%s", KRED); + fprintf (stderr, " [EMERGENCY]"); + } + fprintf (stderr, "%s", KNRM); char mode[8]; //allow, block, digital enc sprintf (mode, "%s", ""); @@ -638,33 +776,30 @@ void edacs(dsd_opts * opts, dsd_state * state) if (state->tg_hold != 0 && state->tg_hold != group) sprintf (mode, "%s", "B"); if (state->tg_hold != 0 && state->tg_hold == group) sprintf (mode, "%s", "A"); - if (mt1 == 0x1) fprintf (stderr, " TDMA Call"); //never observed, wonder if any EDACS systems ever carried a TDMA signal (X2-TDMA?) - if (mt1 == 0x2) fprintf (stderr, " Group Data Call"); //Never Seen this one before - if (mt1 == 0x3) fprintf (stderr, " Digital Call"); //ProVoice, this is what we always get on SLERS EA - fprintf (stderr, "%s", KNRM); - //this is working now with the new import setup - if (opts->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block + 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 < 26 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise) + 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, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl) 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 TG %d SRC %d.wav", getDateE(), timestr, state->edacs_site_id, group, source); - openWavOutFile (opts, state); - // openWavOutFile48k (opts, state); //debug for testing analog wav only + if (is_digital == 1) + openWavOutFile (opts, state); + else + openWavOutFile48k (opts, state); } - + //do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well if (opts->use_rigctl == 1) { - 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 the lcn index starts at zero + 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 the lcn index starts at zero state->edacs_tuned_lcn = lcn; opts->p25_is_tuned = 1; - //debug testing (since I don't have EDACS standard w/ Analog nearby) - // edacs_analog(opts, state, group, lcn); + if (is_digital == 0) + edacs_analog(opts, state, group, lcn); } if (opts->audio_in_type == 3) //rtl dongle @@ -673,91 +808,304 @@ void edacs(dsd_opts * opts, dsd_state * state) rtl_dev_tune (opts, state->trunk_lcn_freq[lcn-1]); state->edacs_tuned_lcn = lcn; opts->p25_is_tuned = 1; - //debug testing (since I don't have EDACS standard w/ Analog nearby) - // edacs_analog(opts, state, group, lcn); + if (is_digital == 0) + edacs_analog(opts, state, group, lcn); #endif } } - + } } - else //print frames for debug/analysis + //I-Call Grant Update + else if (mt1 == 0x10) { - fprintf (stderr, " FR_1 [%010llX]", fr_1t); - fprintf (stderr, " FR_4 [%010llX]", fr_4t); + lcn = (fr_4t & 0x1F00000000) >> 32; + + //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; + } + + int is_digital = (fr_1t & 0x200000000) >> 33; + int is_update = (fr_1t & 0x100000000) >> 32; + int target = (fr_1t & 0xFFFFF000) >> 12; + int source = (fr_4t & 0xFFFFF000) >> 12; + + //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"); + else fprintf (stderr, " Digital I-Call"); + if (is_update == 0) fprintf (stderr, " Assignment"); + else fprintf (stderr, " Update"); + + fprintf (stderr, " :: Target [%08d] Source [%08d] LCN [%02d]%s", target, source, lcn, get_lcn_status_string(lcn)); + fprintf (stderr, "%s", KNRM); + + char mode[8]; //allow, block, digital enc + sprintf (mode, "%s", ""); + + //if we are using allow/whitelist mode, then write 'B' to mode for block - no allow/whitelist support for i-calls + if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B"); + + //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, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl) + 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 %d SRC %d I-Call.wav", getDateE(), timestr, state->edacs_site_id, target, source); + if (is_digital == 1) + openWavOutFile (opts, state); + else + openWavOutFile48k (opts, state); + } + + //do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well + if (opts->use_rigctl == 1) + { + 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 the lcn 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 + } + + } + } + } + //Channel assignment (unknown reason, just know it assigns an LCN in the expected order; believed related to data) + else if (mt1 == 0x12) + { + lcn = (fr_4t & 0x1F00000000) >> 32; + int source = (fr_4t & 0xFFFFF000) >> 12; + 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) + { + lcn = (fr_1t & 0x3E0000000) >> 29; + + //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; + } + + int is_digital = (fr_1t & 0x10000000) >> 28; + int is_update = (fr_1t & 0x8000000) >> 27; + int source = (fr_4t & 0xFFFFF000) >> 12; + + //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"); + else fprintf (stderr, " Digital System All-Call"); + if (is_update == 0) fprintf (stderr, " Assignment"); + else fprintf (stderr, " Update"); + + fprintf (stderr, " :: Source [%08d] LCN [%02d]%s", source, lcn, get_lcn_status_string(lcn)); + fprintf (stderr, "%s", KNRM); + + 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"); + + //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, should probably check as well to make sure we have a valid trunking method active (rigctl, rtl) + 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 %d All-Call.wav", getDateE(), timestr, state->edacs_site_id, source); + if (is_digital == 1) + openWavOutFile (opts, state); + else + openWavOutFile48k (opts, state); + } + + //do condition here, in future, will allow us to use tuning methods as well, or rtl_udp as well + if (opts->use_rigctl == 1) + { + 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 the lcn index starts at zero + state->edacs_tuned_lcn = lcn; + opts->p25_is_tuned = 1; + if (is_digital == 0) + edacs_analog(opts, state, -1, 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, -1, lcn); + #endif + } + + } + + } + } + //Login + else if (mt1 == 0x19) + { + int group = (fr_1t & 0xFFFF000) >> 12; + int source = (fr_4t & 0xFFFFF000) >> 12; + fprintf (stderr, "%s", KBLU); + fprintf (stderr, " Login :: Group [%05d] Source [%08d]", group, source); + fprintf (stderr, "%s", KNRM); + } + //Unknown command + else + { + fprintf (stderr, "%s", KWHT); fprintf (stderr, " Unknown Command"); + 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); + } } } //Start Standard or Networked Mode else if (state->ea_mode == 0) { - //standard or networked - command = ((fr_1t & 0xFF00000000) >> 32) ^ state->esk_mask; - 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; - //site ID and CC LCN - if (command == 0xFD) + state->edacs_vc_call_type = 0; + + //Add raw payloads and MT-A/MT-B/MT-D for easy debug + if (opts->payload == 1) { - int site_id = (fr_1t & 0xFF000) >> 12; - int cc_lcn = (fr_1t & 0x1F000000) >> 24; - - fprintf (stderr, "%s", KYEL); - fprintf (stderr, " Site ID [%02X][%03d] on CC LCN [%02d] Standard/Networked", site_id, site_id, cc_lcn); - 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) + fprintf (stderr, " FR_1 [%010llX]", fr_1t); + fprintf (stderr, " FR_4 [%010llX]", fr_4t); + fprintf (stderr, " (MT-A: %X", mt_a); + // MT-B is meaningless if MT-A is not 0x7 + if (mt_a == 0x7) { - 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) + fprintf (stderr, "; MT-B: %X", mt_b); + // MT-D is meaningless if MT-B is not 0x7 + if (mt_b == 0x7) { - lcnfreq = GetCurrentFreq (opts->rigctl_sockfd); - if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq; + fprintf (stderr, "; MT-D: %02X) ", mt_d); } - //if using rtl input, we can ask for the current frequency tuned - if (opts->audio_in_type == 3) + else { - lcnfreq = (long int)opts->rtlsdr_center_freq; - if (lcnfreq != 0) state->trunk_lcn_freq[state->edacs_cc_lcn-1] = lcnfreq; + fprintf (stderr, ") "); } } - - //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) + else { - state->p25_cc_freq = state->trunk_lcn_freq[state->edacs_cc_lcn-1]; //index starts at zero, lcn's locally here start at 1 + fprintf (stderr, ") "); } - } - //voice call assignment - else if (command == 0xEE || command == 0xEF) - { + //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. - if (lcn > state->edacs_lcn_count && lcn < 26) + //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) + //Emergency Voice Group Channel Assignment (6.2.4.2) + if (mt_a == 0x0 || mt_a == 0x1 || mt_a == 0x2 || mt_a == 0x3) + { + int is_digital = (mt_a == 0x2 || mt_a == 0x3) ? 1 : 0; + int is_emergency = (mt_a == 0x1 || 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", KGRN); + fprintf (stderr, " Voice Group Channel Assignment ::"); + if (is_digital == 0) fprintf (stderr, " Analog"); + else fprintf (stderr, " Digital"); + fprintf (stderr, " 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); + + //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; - //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); + + //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", ""); @@ -766,67 +1114,49 @@ void edacs(dsd_opts * opts, dsd_state * state) //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"); + //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 == afs) + if (state->group_array[i].groupNumber == group) { - fprintf (stderr, " [%s]", state->group_array[i].groupName); strcpy (mode, state->group_array[i].groupMode); break; } } - - //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"); + //TG hold on EDACS Standard/Net -- block non-matching target, allow matching group + if (state->tg_hold != 0 && state->tg_hold != group) sprintf (mode, "%s", "B"); + if (state->tg_hold != 0 && state->tg_hold == group) 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) - //analog working now with edacs_analog - if (command == 0xEE) - fprintf (stderr, " Analog"); - - //Digital Call (ProVoice, hopefully, and not Aegis in 2022) - 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->p25_trunk == 1 && (strcmp(mode, "DE") != 0) && (strcmp(mode, "B") != 0) ) //DE is digital encrypted, B is block + 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 < 26 && state->trunk_lcn_freq[lcn-1] != 0) //don't tune if zero (not loaded or otherwise) + 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 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 + sprintf (opts->wav_out_file, "./WAV/%s %s EDACS Site %lld TG %04d SRC %05d.wav", getDateE(), timestr, state->edacs_site_id, group, lid); + 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 && 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); + 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 (command == 0xEE) edacs_analog(opts, state, afs, lcn); + if (is_digital == 0) edacs_analog(opts, state, group, lcn); } if (opts->audio_in_type == 3) //rtl dongle @@ -835,33 +1165,700 @@ void edacs(dsd_opts * opts, dsd_state * state) 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); + if (is_digital == 0) edacs_analog(opts, state, group, lcn); #endif } } - } + } + //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; + + //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]", 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) + { + int group = (fr_1t & 0x1FFC000000) >> 26; + int lid = (fr_1t & 0x3FFF000) >> 12; + + fprintf (stderr, "%s", KBLU); + fprintf (stderr, " Login Acknowledgement :: Group [%04d] LID [%05d]", group, lid); fprintf (stderr, "%s", KNRM); } - //Network Command, Not sure of how to handle, but just show frames - else if (command == 0xF3) + //Use MT-B + else if (mt_a == 0x7) { - fprintf (stderr, " FR_1 [%010llX]", fr_1t); - fprintf (stderr, " FR_4 [%010llX]", fr_4t); - fprintf (stderr, " Network Command"); - } + //Status Request / Message Acknowledge (6.2.4.5) + if (mt_b == 0x0) + { + int status = (fr_1t & 0x3FC000000) >> 26; + int lid = (fr_1t & 0x3FFF000) >> 12; - else //print frames for debug/analysis + fprintf (stderr, "%s", KBLU); + 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); + } + //Interconnect Channel Assignment (6.2.4.6) + else if (mt_b == 0x1) + { + 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; + + //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 (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); + + //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 = (is_individual == 0) ? (fr_1t & 0x2000000) >> 25 : 0; + int group = (fr_1t & 0x7FF000) >> 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; + + //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; + + if (is_individual == 0) + { + fprintf (stderr, "%s", KGRN); + fprintf (stderr, " Voice Group Channel Update ::"); + } + else + { + fprintf (stderr, "%s", KCYN); + 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, " 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) + { + 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; + } + + //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; + + //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", ""); + + //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; + + 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) + //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 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 ::"); + 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); + + //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) + { + int is_drop = (fr_1t & 0x80000000) >> 31; + int lcn = (fr_1t & 0x7C000000) >> 26; + int lid = (fr_1t & 0x3FFF000) >> 12; + + 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", KBLU); + 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(cc_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) + //Analog and digital flag extrapolated from reverse engineering of other messages + else if (mt_d == 0x0F) + { + int lcn = (fr_1t & 0x1F000000) >> 24; + 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) >> 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 (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); + + //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) + { + 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", KWHT); + 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, "%s", KWHT); + 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) + { + fprintf (stderr, " ::"); + fprintf (stderr, " FR_1 [%010llX]", fr_1t); + fprintf (stderr, " FR_4 [%010llX]", fr_4t); + } + } + } + //Reserved command (MT-A) + else { - fprintf (stderr, " FR_1 [%010llX]", fr_1t); - fprintf (stderr, " FR_4 [%010llX]", fr_4t); - fprintf (stderr, " Unknown Command"); + fprintf (stderr, "%s", KWHT); + fprintf (stderr, " Reserved Command (MT-A)"); + 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); + } } } //end Standard or Networked - //supply user warning to use -9 switch if decoding doesn't start shortly - else fprintf (stderr, " Net/EA Auto Detect; Use -9 CLI Switch For Standard;"); + //let users know they need to select an operational mode with the switches below + else + { + fprintf (stderr, " Detected: Use -fh, -fH, -fe, or -fE for std, esk, ea, or ea-esk;"); + fprintf (stderr, "\n"); + fprintf (stderr, " FR_1 [%010llX]", fr_1t); + fprintf (stderr, " FR_4 [%010llX]", fr_4t); + } } @@ -883,13 +1880,13 @@ void eot_cc(dsd_opts * opts, dsd_state * state) //jump back to CC here if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1) { - + //rigctl if (opts->use_rigctl == 1) { state->lasttg = 0; state->lastsrc = 0; - state->payload_algid = 0; + state->payload_algid = 0; state->payload_keyid = 0; state->payload_miP = 0; //reset some strings @@ -900,7 +1897,7 @@ void eot_cc(dsd_opts * opts, dsd_state * state) 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); + SetFreq(opts->rigctl_sockfd, state->p25_cc_freq); } //rtl @@ -926,4 +1923,4 @@ void eot_cc(dsd_opts * opts, dsd_state * state) } } - \ No newline at end of file + diff --git a/src/provoice.c b/src/provoice.c index b24f4e6..c16166d 100644 --- a/src/provoice.c +++ b/src/provoice.c @@ -27,12 +27,19 @@ void processProVoice (dsd_opts * opts, dsd_state * state) fprintf (stderr," VOICE"); - //print group and source values if EA trunked + //print group/target and source values if EA trunked if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->ea_mode == 1) { fprintf (stderr, "%s", KGRN); - fprintf (stderr, " Site: %lld Group: %d Source: %d LCN: %d ", - state->edacs_site_id, state->lasttg, state->lastsrc, state->edacs_tuned_lcn); + if (state->lasttg > 100000) { + // I-Call + fprintf (stderr, " Site: %lld Target: %d Source: %d LCN: %d ", + state->edacs_site_id, state->lasttg - 100000, state->lastsrc, state->edacs_tuned_lcn); + } else { + // Group call + fprintf (stderr, " Site: %lld Group: %d Source: %d LCN: %d ", + state->edacs_site_id, state->lasttg, state->lastsrc, state->edacs_tuned_lcn); + } fprintf (stderr, "%s", KNRM); } //print afs value if standard/networked trunked