450 Commits
v1.3 ... 446

Author SHA1 Message Date
lwvmobile
3086b7852e MSData Bug Fix; Disable DMR Data Superframe Debug; 2022-10-20 11:38:50 -04:00
lwvmobile
8f6796d6f6 Filter Tests and Tweaks 2022-10-19 23:01:59 -04:00
lwvmobile
e9b1d76a98 Frame Sync OCD 2022-10-19 22:22:23 -04:00
lwvmobile
afba623b20 Fix More Compiler Warning 2022-10-19 22:19:00 -04:00
lwvmobile
9b92855b4e DMR CACH and Burst ERR Tweaking; 2022-10-17 21:07:58 -04:00
lwvmobile
4138f92917 DMR Burst Type FEC Fix; 2022-10-17 17:16:22 -04:00
lwvmobile
0bb9dd760c Enable DMR Direct Mode Voice; No Data; 2022-10-16 14:10:16 -04:00
lwvmobile
8b4f91ed8b char * pEnd build bugfix; 2022-10-13 14:18:05 -04:00
lwvmobile
a8fd2c7834 10/32/64 Key Entry at CLI; Unmute Condition Check; 2022-10-13 11:40:15 -04:00
lwvmobile
8670b99d69 DMR Stereo Internal Sync Tweaks; OCD Tweaks; 2022-10-09 07:32:48 -04:00
lwvmobile
6bd37821a9 NXDN96 Scrambler Support; Force Key Priority CLI; 2022-10-08 07:08:23 -04:00
lwvmobile
215594a2a8 P25 PDU Tweaks; 2022-10-08 07:07:45 -04:00
lwvmobile
3f31d4ffe0 GFSK Symbol Capture Bin Quick Fix 2022-10-05 13:29:00 -04:00
lwvmobile
4a83c1beae Permit MAC_SIGNAL on bad CRC with -F option 2022-10-05 12:51:33 -04:00
lwvmobile
67a78d8be0 Symbol Capture Read Tweaks and Fixes 2022-10-05 12:07:00 -04:00
lwvmobile
14ba90a33f TSBK to vPDU bug fix 2022-10-02 08:07:25 -04:00
lwvmobile
66fd3fd7bf Per Call Wrong Tone Selection Bug Fix 2022-10-02 05:58:49 -04:00
lwvmobile
d1ffcda819 Update Readme; NCurses MAC_SIGNAL Fix 2022-10-01 04:52:23 -04:00
lwvmobile
f81575335e Phase 2 Fixes and Tweaks (ISCH and Disc Tap) 2022-10-01 04:18:22 -04:00
lwvmobile
163db6b872 MFIDA4 Console Tweak 2022-09-30 12:43:16 -04:00
lwvmobile
e6927d8115 P25 Additions, Tweaks, Testing 2022-09-30 11:52:03 -04:00
lwvmobile
3a19bf5583 Update Readme p2 2022-09-28 17:09:14 -04:00
lwvmobile
181a6a5695 Update Readme 2022-09-28 17:04:24 -04:00
lwvmobile
0d2244fac0 Don't Log Null PDUs 2022-09-28 16:56:11 -04:00
lwvmobile
bd7d43de79 Help Clarification 2022-09-27 18:42:11 -04:00
lwvmobile
c37400bf81 DMR Stereo Standalone Option 2022-09-27 18:39:35 -04:00
lwvmobile
bab139e2e4 P25 Restructuring 2022-09-27 17:21:51 -04:00
lwvmobile
6714761d8e Mute Audio Bug Fix; Don't process audio when mute 2022-09-26 19:54:03 -04:00
lwvmobile
467992ceff Clear Audio Buffer on Mute Toggle 2022-09-26 19:27:23 -04:00
lwvmobile
cb84f7469e Mute Audio Option (-o null) 2022-09-26 19:13:49 -04:00
lwvmobile
ad4013d7d2 Test Push from VSCodium 2022-09-23 12:45:07 -04:00
lwvmobile
5b0e40e758 test commit from vscodium 2022-09-23 12:32:36 -04:00
lwvmobile
05a9268aef Merge pull request #85 from lwvmobile/callalert
Call Alert; Call History Bug Fix; Minor Tweaks
2022-09-22 21:13:04 -04:00
lwvmobile
f3d2dbb4dd Call Alert; Call History Bug Fix; Minor Tweaks
Call Alert (-a or toggle on/off in ncurses)
--beep sound on radio id change when using ncurses terminal (tied to call history array)
---need to copy tone8.wav and tone24.wav files to /usr/share/ directory and chmod 777 them.
---updated all auto install scripts to copy and chmod as appropriate
----iz4tow, need to update windows tutorials for the wav files, else they won't play

Tweaks/Bug Fix to Call History (specifically DMR) to only set src ids on voice sync, and not on data sync

Manually Set P2 Parameters at CLI (-X BEE00ABC123)
-- use -X (capital X) and then enter WACN/SYSID/CC altogether 
--- feature already available in ncurses terminal menu

Update Linux Install Instructions and auto installers to copy/paste wav files into /usr/share/ directory

Other Minor Tweaks
2022-09-22 20:46:47 -04:00
lwvmobile
370d6be28e NXDN/NCurses/CLI Per Call Bug Fix
NXDN/NCurses/CLI Per Call Bug Fix
2022-09-20 15:46:17 -04:00
lwvmobile
3224f0af37 Merge pull request #84 from lwvmobile/lwvmobile-patch-1
P2 Console Output Change; Disable YSF Sync
2022-09-20 14:09:22 -04:00
lwvmobile
5a73bcac8b P2 Console Output Change; Disable YSF Sync
P2 Console Output Change; 
--change up color coding slightly
Disable YSF Sync
--frame_ysf variable was never initialized, so some systems would false sync on YSF (YSF never finished, no handling yet)
2022-09-20 14:08:06 -04:00
lwvmobile
af76c8de0d Merge pull request #83 from lwvmobile/phase2
Phase 2 Audio Decoding
2022-09-20 00:00:54 -04:00
lwvmobile
a3ecbe7414 OCD Spacing Issue
OCD Spacing Issue
2022-09-19 23:25:37 -04:00
lwvmobile
4e04056f4b Phase 2 More PDU decodes
A few extra PDU decodes for Secondary Control Channel and Voice Grant Update
2022-09-19 23:08:44 -04:00
lwvmobile
d8cf47b225 Checkdown for Valid P2 Parameters
Checkdown for Valid P2 Parameters
--if invalid parameters specified by user for P2 WACN, SYSID, and CC (too large) then treat same as missing parameters and skip processing P2 frames that require scrambling.
2022-09-19 17:33:16 -04:00
lwvmobile
57d4c7afbe Minor Tweaks
Minor Tweaks
--ncurses will truncate manually entered P2 parameters set if they are larger than expected.
--muting/key tweak
2022-09-19 17:13:58 -04:00
lwvmobile
cb5c160fbf Experimental Symbol Capture Bin Input Throttle
Experimental Symbol Capture Bin Input Throttle
--throttle read speed when reading capture bin files that are still being written to by OP25, quick easy piggyback (-n).
Readme tweaks
Minor tweaks
2022-09-19 15:48:14 -04:00
lwvmobile
5390a52313 Restore Ncurses Menu Option
Restore Ncurses Menu Option
2022-09-17 13:25:57 -04:00
lwvmobile
1d78e000da Fix DMR Voice Burst Bug; Update Screenshot
Fix DMR Voice Burst Bug
--was gathering bits to bytes wrong, and it broke DMR ProcessVoiceBurstSync

Update Screenshot to reflect current look and feel of FME
2022-09-17 13:24:28 -04:00
lwvmobile
90700e7d28 Phase 2 Audio Decoding
Phase 2 Audio Decoding Implemented

XDMA decoder class implemented and set to default (-ft)
--decoder class includes P25 1, 2, DMR BS/MS, and X2-TDMA
-- -fr -T option reworked, -T now for Per Call, use -ft (default)

Force Privacy Key
--force privacy key use over system FID and SVC options.

Per call now works on XDMA decoding and NXDN decoding

Limited Phase 1 TSBK and Phase 2 LCCH handling (for Network Status Broadcast)
--Need to get WACN, SYSID, and CC values for Phase 2 descrambling

Fix most of the compiler warnings
2022-09-17 11:44:45 -04:00
lwvmobile
0e9c2fc3dc Additional Sanity Check
Additional Sanity Check
--perform sanity check on DMRvcL and DMRvcR to prevent out of bounds array issue that could lead to a crash in some environments with bad signal
2022-08-29 19:49:34 -04:00
lwvmobile
4d24e93905 small example fix
remove the -pu option, no longer need to unmute audio for a payload on it
2022-08-28 20:08:00 -04:00
lwvmobile
1505103c18 RTL PPM Input Fix
RTL Input Fix
--PPM error wasn't being set by command line -P switch. Had disabled for testing, forgot to re-enable.
2022-08-28 00:48:53 -04:00
lwvmobile
861ca3cb54 Merge pull request #82 from lwvmobile/SerpenteraII
Add More Options
2022-08-27 23:17:07 -04:00
lwvmobile
c228207588 Additional ifdef rtl rule
--add rule to prevent tuning box from opening if no rtl support.
2022-08-27 21:31:59 -04:00
lwvmobile
09f92579ee Banner Update
Banner Update; Minor Tweaks?
2022-08-27 20:29:13 -04:00
lwvmobile
a277707a4d Entry Box Width Fix 2022-08-26 22:05:23 -04:00
lwvmobile
41a236d244 Nomenclature Fix
Nomenclature Fix
2022-08-26 22:01:39 -04:00
lwvmobile
837bfac5f5 Add More Options
Serpen'tera' Privacy now supports 10/32/64 character modes (have to use ncurses terminal to enter 32 or 64 char values)
NXDN Scrambler LFSR
Odd and End Terminal Tweaks
2022-08-26 21:54:54 -04:00
lwvmobile
2f05bfacfa DMR Voice LC Header Fix
DMR Voice LC Header Fix
--Fix Voice LC Header to assign SRC and TGT values, mask if Cap+ system detected to prevent erroneous values
--Optimization for FID and SVC options, don't reset on carrier signal drop or TLC
2022-08-23 23:31:34 -04:00
lwvmobile
f61c59ccc0 Merge pull request #81 from lwvmobile/serpentera
DMR Additions
2022-08-23 17:01:19 -04:00
lwvmobile
a682d9e882 DMR Stereo Slot 2 Fix
DMR Stereo Slot 2 Fix
--DMRA Privacy Key not applied correctly in Slot 2 Stereo Mode
2022-08-22 17:57:05 -04:00
lwvmobile
88b3145cfd OCD Nomenclature #3
--final final on nomenclature, P to Pr
2022-08-22 16:57:44 -04:00
lwvmobile
7199848f16 OCD Line Spacing and Nomenclature
OCD Line Spacing and Nomenclature
--fixed a few alignment issues to my liking
--final on nomenclature for privacy modes
2022-08-22 16:55:00 -04:00
lwvmobile
b7bb59dbba Additional Console Print Fixes
Additional Console Print Fixes
--missed a few places where we needed to have info printing
2022-08-22 02:34:29 -04:00
lwvmobile
eb9152fb4e More Nomenclature Fixes
--maybe I can finally settle on this naming scheme
2022-08-22 01:49:56 -04:00
lwvmobile
2ec4250e5b Disable setting state src and tgt on VLC Header
Disable setting state src and tgt on VLC Header
--bugfix for ncurses terminal specifically to prevent the Voice LC Header setting the src and tgt ids; 
--some TIII systems seem to have different values for that than what is in the TLC and Voice Burst.
--console printout can still show erroneous VLC header radio src ids that don't match the TLC or Voice Burst.
2022-08-22 00:56:01 -04:00
lwvmobile
092da7b6d0 Nomenclature Fix
Nomenclature Fix
2022-08-21 23:55:16 -04:00
lwvmobile
df6d7a6609 DMR Additions
--Easier to use key entry for privacy modes
---Serpen'tera' 10 mode now available
--Slightly improved dmr dibit buffering/inverted dibit buffering
--Tweaks to console and ncurses output regarding FID, SVC, and privacy key values
--fix for RTL optional support (think I have all the ifdef covered now)
--TGT, SRC, FID, SVC, now set on Voice LC Header (not sure why I wasn't before)
--other misc tweaks that I can't recall
2022-08-21 23:45:00 -04:00
lwvmobile
7f34ff9340 LRRP Additional Shim
--Forgot a Shim Flag from the KrisMar set
2022-08-14 23:08:01 -04:00
lwvmobile
12e4b4cd6f Merge pull request #78 from lwvmobile/LRRPPatch5
LRRP Patches and More Options
2022-08-14 22:51:42 -04:00
lwvmobile
6189ec95fa LRRP Patches and More Options
LRRP Patches and LRRP Filename Options
--shim/rework some rules for KrisMar samples
--allow filename to be customized in ncurses terminal
2022-08-14 22:43:58 -04:00
lwvmobile
0f6140a30a Merge pull request #77 from lwvmobile/mutingtweaks
Muting Enc Tweaks; LRRP getDateL tweak;
2022-08-13 19:09:08 -04:00
lwvmobile
5cb14c356b Add files via upload 2022-08-13 19:07:53 -04:00
lwvmobile
44cdfd912d Muting Enc Tweaks; LRRP getDateL tweak;
Muting Enc Tweaks
--Tweak when to mute and unmute encrypted traffic, and when to write wav and mbe files when enc or not
--If any DMR traffic appears muted that shouldn't be, simply toggle the mute in ncurses menu
LRRP getDateL tweak
--expand array from 99 to 120; seems to be usable in both Linux Desktop and Cygwin this way, most tests required.
2022-08-13 19:03:35 -04:00
lwvmobile
ec4c4637df LRRP Patch 4
Revert getDateL array size; 200 fixes issue in Cygwin, but breaks in Linux builds; (priority is Linux builds) 
--Will look into a different solution that works on both ends.
2022-08-13 13:55:13 -04:00
lwvmobile
e5a7f8c41a LRRP Patch 3
Expand Array on LRRP getDateL function to get properly formatted datestamp in lrrp.txt
2022-08-13 12:47:03 -04:00
lwvmobile
03fb413750 Merge pull request #76 from lwvmobile/LRRPpatch2
LRRP Patch 2
2022-08-13 12:35:52 -04:00
lwvmobile
f22d61df62 LRRP Patch 2
Attempt to Patch/Mitigate Corrupt Date Stamp in lrrp.txt file when using certain environments (cygwin)
2022-08-13 12:35:03 -04:00
lwvmobile
d20de53f91 Merge pull request #75 from lwvmobile/lrrp-patch
LRRP Patch
2022-08-12 15:31:47 -04:00
lwvmobile
3ba7a96bbd LRRP Patch
LRRP Patch - Additional Flags for Lat/Lon
2022-08-12 15:31:18 -04:00
lwvmobile
3e36e5e78d Merge pull request #72 from lwvmobile/lwvmobile-patch-2
Fix Readme; Init Variable
2022-08-12 10:45:17 -04:00
lwvmobile
83edd7ff2b Fix Readme; Init Variable
Fix Reame Grammar Error (double text)
Init Variable that might cause the broken/experimental source audio monitor to open in some distros/cygwin.
2022-08-12 10:43:10 -04:00
lwvmobile
416edcbd59 Merge pull request #69 from lwvmobile/ncursesmenu
NCurses Menu
2022-08-11 19:21:15 -04:00
lwvmobile
65ce60f6b5 Update Readme
Embed Youtube video link to first image...hopefully.
2022-08-11 15:53:29 -04:00
lwvmobile
e461335b2e Update CMakeLists.txt
Make PulseAudio a requirement
2022-08-11 13:34:03 -04:00
lwvmobile
b9223e5207 Mute NXDN and P25 ENC
Mute NXDN and P25 ENC by default, but still print frame payloads.
2022-08-11 10:17:32 -04:00
lwvmobile
805fdd7583 Proof Reading; Tweaks;
Proof Read and tweak a few minor things. Add a few #ifdef USE_RTLSDR to make sure it still compiles when not using the rtl support.
2022-08-11 09:11:06 -04:00
lwvmobile
2b847c2270 Encrypted DMR muted by default now
Encrypted DMR muted by default now; Can be toggled in Terminal; Will still dump payload on request. Fix Bug in DMR Stereo 'muddy sound' caused by appending audio errs to both left and right channel each pass instead of only when using left or right.

--will not playback audio or write wav files or mbe files when muted

Fix errs and errsR to only append/set in MBE when on that slot, not always
2022-08-10 23:39:19 -04:00
lwvmobile
6043b14c13 Update Readme
Update Readme; Minor Bug Fix. Update dmr_so and dmr_soR in the Voice LC Header.
2022-08-10 20:03:26 -04:00
lwvmobile
11b353089b Data Header and Data Bursts Cleanup
Cleanup the code in the dmr_sync.c file, fix minor code errors.
Include the dsd-fme-qgis-map file for opening with QGIS and importing LRRP data dumped to ~/lrrp.txt
Update the Readme file.
2022-08-10 18:44:56 -04:00
lwvmobile
b39d5d8401 Accidentally included again
Deleted again
2022-08-09 18:45:21 -04:00
lwvmobile
5d44425b10 Cleanup Straggler Code and Language
Cleanup Straggler Code and Language
--only dropped one F-Bomb this time
2022-08-09 18:43:44 -04:00
lwvmobile
f45f4fe413 NCurses Menu
--NCurses Menu (no more tedious CLI options)
--Save/Read Capture Bin (WIP!)
--Read OP25 Capture Bin (WIP!)
--Per Call WAV saving on DMR Stereo
--Revampled Data Header and 1/2, 3/4 Data LRRP
--Dump LRRP Data to text file, import into QGIS
--Included QGIS Map File with layers set up
--DMR CACH and Burst FEC 
--No more Port Audio or OSS...ever. 
--Switch to 8Khz/1Ch for most decode types due to crisper sound
---DMR Stereo remains at 24/2, will look into revampint upsampe for cleaner sound
2022-08-09 18:10:13 -04:00
lwvmobile
10ed2eac5a Merge pull request #67 from lwvmobile/summercleanup
Summer Cleanup
2022-06-20 08:47:58 -04:00
lwvmobile
222cd706bf Update README.md 2022-06-19 11:44:01 -04:00
lwvmobile
18714523c7 Update Screenshot 2022-06-19 10:21:19 -04:00
lwvmobile
6c89c6260b Update README.md 2022-06-19 10:20:30 -04:00
lwvmobile
48006c28c0 Additional Info
Add info regarding download-and-install.sh vs download-and-install-nodeps.sh and when to use each one, and additional info on the transition from dsd to dsd-fme executable, and info on manually removing the old dsd executable if needed.
2022-06-19 09:54:40 -04:00
lwvmobile
634eba522f Create download-and-install-nodeps.sh
This version is to download and install if you already have dependencies installed, saving time over the initial download-and-install.sh which is only for new/clean builds without deps installed.
2022-06-19 09:37:55 -04:00
lwvmobile
81588b3201 Additional Accredation
Giving credit to where credit is due. Trying to make sure I get all my sources listed as best as I can.
2022-06-19 08:19:40 -04:00
lwvmobile
1ca0f8a7d5 Update README.md
Tweak the Auto Install script to use download-and-install.sh
Minor Info Tweak; Thorough Comb Through for Accuracy
2022-06-19 08:13:33 -04:00
lwvmobile
7133ad9dae Update Readme
Update Readme to let current users know to that the executable name was changed from dsd to dsd-fme.
2022-06-17 08:31:22 -04:00
lwvmobile
caf21d2f0c Add info for headless installs
Add info for headless installs
2022-06-16 13:16:28 -04:00
lwvmobile
2b0a85f99f Change Permission to allow execute
Change Permission to allow execute
2022-06-16 06:27:34 -04:00
lwvmobile
3a3f717236 Create install.sh 2022-06-16 06:17:20 -04:00
lwvmobile
b4c7c60427 Rename install.sh to download-and-install.sh 2022-06-16 06:15:03 -04:00
lwvmobile
0f2dfe4dca Change Examples
Change Examples to reflect executable name change from dsd to dsd-fme
2022-06-16 06:10:26 -04:00
lwvmobile
f0cc508bf9 change executable name to dsd-fme
rename executable from dsd to dsd-fme
2022-06-16 05:58:38 -04:00
lwvmobile
5b46fb8ab3 Remove Building TESTS
Remove Building TESTS
Remove PortAudio
2022-06-16 05:54:32 -04:00
lwvmobile
1a87f81f08 Delete debian directory 2022-06-16 05:53:32 -04:00
lwvmobile
af6a5b39de Delete FindLibPortAudio.cmake 2022-06-16 05:51:03 -04:00
lwvmobile
2c839f7fd2 Delete .github/workflows directory 2022-06-16 05:50:02 -04:00
lwvmobile
af0c0a4418 Delete .gitignore 2022-06-16 05:49:49 -04:00
lwvmobile
c495c90ef9 Delete CONTRIBUTING.md 2022-06-16 05:49:25 -04:00
lwvmobile
f8be80318b Delete CHANGELOG 2022-06-16 05:49:09 -04:00
lwvmobile
dbea859331 Delete test directory 2022-06-16 05:48:57 -04:00
lwvmobile
2463fe22f7 Delete .travis.yml 2022-06-16 05:48:15 -04:00
lwvmobile
dc0c78e14d Summer Cleanup
Summer Cleanup
--delete unused files, work on cmake,
2022-06-16 05:46:58 -04:00
lwvmobile
a2ac82c470 Merge pull request #66 from lwvmobile/dmrstereopatching
DMR Stereo Audio and Timing Tweaks
2022-06-15 04:59:18 -04:00
lwvmobile
c402dfff96 Code Tweak and Clean Up More DEV Code
Tweaks to when to run LFSR and remove unused code
2022-06-12 13:10:26 -04:00
lwvmobile
3f586d9c98 Restore Line Break 2
Restore Line Break 2
2022-06-12 12:30:05 -04:00
lwvmobile
4ab74a6bf0 Restore Line Break Printing
Restore Line Break Printing and Clean Out some DEV code
2022-06-12 12:14:54 -04:00
lwvmobile
d89507ebde DMR Stereo Audio and Timing Tweaks
--Tweak Naming of DMR Stereo output streams, giving them unique names so that the pulse audio server will remember their audio levels and left and right channel configurations on subsequent start ups.

--Tweak timing of events inside of the DMR BS Stereo Method
2022-06-12 11:58:03 -04:00
lwvmobile
806fe955cb Fix Minor Ncurses Regression
Fix Minor Ncurses Regression
2022-06-09 20:06:22 -04:00
lwvmobile
c82e028032 Merge pull request #65 from lwvmobile/pulsetesting
Code Cleanup and Testing
2022-06-09 20:02:30 -04:00
lwvmobile
357f59b290 Minor Code Tweaks
Minor Code Tweaks
2022-06-09 16:58:45 -04:00
lwvmobile
cbd3d98151 More Code Testing and Cleanup
More Code Testing and Cleanup
--disable some debug printouts
2022-06-08 22:42:06 -04:00
lwvmobile
5a39fe65db Code Cleanup and Testing
Code Cleanup and Testing
--Minor Tweaks and Clean Up
2022-06-07 23:01:52 -04:00
lwvmobile
37c61aa5cb Merge pull request #63 from lwvmobile/pulsecleaning
Code Cleanup and Tweaks
2022-05-30 17:10:26 -04:00
lwvmobile
74d08ffaa6 Update README.md
Add link to RR Forum Main Post about DSD-FME
2022-05-30 17:09:32 -04:00
lwvmobile
171f372a4a Really Fix Compiler Warning
Change the correct X to llX this time
2022-05-30 03:35:42 -04:00
lwvmobile
3924c70386 Fix Compiler Warning
the usual %X to %llX warnings I forget to deal with
2022-05-30 03:32:37 -04:00
lwvmobile
656d7b2add Disable VC6 sync print
Disable VC6 sync print
2022-05-30 03:24:19 -04:00
lwvmobile
66ec027bd2 Code Cleanup and Tweaks
Code Cleanup and Tweaks
--properly accredit LFSR
--change name from LSFR to LFSR (oops)
--minor code tweaks and OCD Spacing
2022-05-30 03:19:21 -04:00
lwvmobile
6ad98fbf69 Merge pull request #62 from lwvmobile/dmrstereopatch
DMR Stereo Patches
2022-05-20 14:43:32 -04:00
lwvmobile
9b4e790095 Update Readme
Update Readme to reflect change in usage 
-- no more '-d ./MBE/' required unless user wants to save MBE files as well.
-- other small clarifications and updates
2022-05-20 14:36:47 -04:00
lwvmobile
e69451b0d7 Seperate MBE Payload Printing from MBE Saving
Seperate MBE Payload Printing from MBE Saving
--seperate the MBE Payload Printing functions from MBE Saving Function
--no longer need to use -d ./MBE/ to print a MBE Payload
--is currently using -Z for payload printing, may further seperate, make new user switch in future
2022-05-19 22:16:44 -04:00
lwvmobile
1d30d5ec32 Fix Uninitialized Values
audio_in_type and audio_out_type were not initialized
--this caused issues with Raspberry Pi OS 64-bit, presumably others as well by not opening the audio output stream.
2022-05-19 20:05:39 -04:00
lwvmobile
710e76d0ab Stupid Compiler Fix
Stupid Compiler Fix
2022-05-19 17:38:39 -04:00
lwvmobile
994e728bfb DMR Stereo Payload Printing
DMR Stereo Payload Printing
--new method to print voice payloads
--dump DMR Stereo payloads without needing to use -d ./MBE/
--rework how DMR Stereo voice is handled and add extra features to it
--general code cleanup
2022-05-19 17:26:45 -04:00
lwvmobile
2fd250b357 Merge pull request #61 from lwvmobile/lwvmobile-dmrstereo-patch
DMR Stereo Inversion Handling
2022-05-14 09:14:53 -04:00
lwvmobile
ed68f41f0e Revert STDIN Audio Output
Revert STDIN Audio Output
--Revert back to 8K/1 for normal due to occasional crackling that resulted from upscaling audio to 48k when using stdin, mbe, and rtl.
--Still need to investigate the root cause of this issue.
2022-05-13 16:22:35 -04:00
lwvmobile
16e84ae104 Fix Segfault on MBE Payload directory not existing
Fix Segfault on MBE Payload directory not existing
--code will check for existence of save directory, and create it if needed to prevent a segfault.
--other minor tweak to DMR slots
2022-05-13 13:18:44 -04:00
lwvmobile
4f1ecda2ad Forgot Something
Forgot Something
--I don't even remember now
2022-05-12 22:03:09 -04:00
lwvmobile
685c8aebe4 Fix Slot0/2 Thing
Fix Slot0/2 Thing
2022-05-12 21:38:55 -04:00
lwvmobile
6de3e6c9f3 DMR Remove SLOTS from MS Mode
DMR Remove SLOTS from MS Mode
--other little tweaks/inversion tweaks
2022-05-12 21:31:26 -04:00
lwvmobile
a848f9c0fe More DMR Inversion fixes
More DMR Inversion fixes
--fix some issues regarding when to invert dibits
---still have random issue of frame syncing to voice type instead of data type immediately after voice ends in TDMA stereo when inverted, guessing the dibit buffer is storing non-inverted dibits causing this?
--code will need a round of cleanup once fully tested and going good inverted
2022-05-12 16:54:45 -04:00
lwvmobile
7d6c3dc67c DMR Stereo Inversion Handling
DMR Stereo Inversion Handling
--Fix frame sync not forwarding inverted signal to DMR Stereo methods
--Other Misc Bug Fixes
2022-05-12 11:25:41 -04:00
lwvmobile
2263ddae63 Merge pull request #59 from lwvmobile/dmrstereo
DMR Stereo TDMA
2022-05-07 08:15:13 -04:00
lwvmobile
f8b9d47c6d Minor Framesync Print with DMR
Minor Framesync Print with DMR
--Minor Framesync Print Fix for DMR
2022-05-06 11:48:37 -04:00
lwvmobile
064cc21b2b Update README.md
Clarification/Cleanup/Spelling/Grammar
2022-05-05 15:46:01 -04:00
lwvmobile
56da871ce2 Update README.md
Update Readme to include information for DMR Stereo, new screenshots, and other updates.
2022-05-05 13:28:53 -04:00
lwvmobile
ddc2a52a33 Update Screenshots
Update Screenshots
2022-05-05 13:06:15 -04:00
lwvmobile
ec9c220ad7 Fix Upsampling Bug
Fix Upsampling Bug
--Change logic to only upsample audio when out sample rate is greater than 8k.
--Revert RTL and MBE output sample rate to 8k/1 due to random crackling
--RTL will still shift to 24k/2 for DMR TDMA stereo, may produce noticeable crackling, unknown as of now, unable to test.
2022-05-04 10:28:35 -04:00
lwvmobile
02a27111d3 Fix Upsampling
Fix Upsampling
--upsample audio on all input types to the proper output rate required for mono and stereo output.
--Allows for 48000/1 or 24000/2 output when using RTL or STDIN input
--MBE file playback still at 8000 due to random crackling when upscaled
--WAV file still recorded at 8000 mono
--MBE file saving and WAV saving disabled when using the -T Stereo TDMA option until both can be reworked.
2022-05-03 15:48:15 -04:00
lwvmobile
4c3a3e0959 Reconfigure STDIN and RTL for TDMA Stereo
Reconfigure STDIN and RTL for TDMA Stereo
--adjust configurations and rates to allow two channel audio output when using -T option with RTL and STDIN
--minor LRRP print mistake
2022-05-03 13:49:44 -04:00
lwvmobile
ea40e3e3b7 More NCurses Tweaks
More NCurses Tweaks
--Clarify extra call and data information lines
--Add Data SRC and TGT to NCurses for LRRP and other things
--OCD nitpicks
2022-05-03 07:54:45 -04:00
lwvmobile
ab873d3821 NCurses Updates
Update NCurses to work with DMR BS TDMA Stereo, DMR MS, dPMR, D-STAR
--reorganize the code and clean up the mess
Minor Tweaks and Code Cleanup
2022-05-03 06:54:08 -04:00
lwvmobile
3cd1eefad8 DMR Stereo TDMA
DMR Stereo TDMA
--play voice in both TDMA slots with the -T option (dsd -fr -T)
--Improved MS voice (mostly working) and data handling (hit or miss)
--pretty pretty colors on the console log output
--rate 3/4 data decoding partially working
2022-04-30 04:43:03 -04:00
lwvmobile
8b48328431 Merge pull request #57 from lwvmobile/pulse-dpmr
dPMR Smooth Sync and Playback
2022-04-08 20:49:12 -04:00
lwvmobile
2c842f5115 ncurses tweak take 2
ncurses tweak take 2
2022-04-08 20:38:32 -04:00
lwvmobile
4ad5a8f9dd revert ncurses tweak (again)
revert ncurses tweak (again)
2022-04-08 20:22:00 -04:00
lwvmobile
33cbad7618 ncurses tweak
ncurses tweak
2022-04-08 20:12:06 -04:00
lwvmobile
504ee431b5 Update dmr_process_voice.c 2022-04-08 19:59:05 -04:00
lwvmobile
53fca195ba Delete trellis.h 2022-04-08 16:27:05 -04:00
lwvmobile
12dbd90509 dPMR Smooth Sync and Playback
dPMR Smooth Sync and Playback
--fix sync and playback issues with dPMR
General Code Cleanup
2022-04-08 16:22:08 -04:00
lwvmobile
2339cee016 Merge pull request #56 from lwvmobile/pulse-bug-fix
bug fixes
2022-04-04 16:43:40 -04:00
lwvmobile
4c44a47c61 ncurses bug fix 2
ncurses bug fix 2
2022-04-04 16:39:22 -04:00
lwvmobile
11d4b2b330 ncurses bugfix
ncurses bugfix
2022-04-04 16:32:53 -04:00
lwvmobile
cb57478fcc compiler warning fix
compiler warning fix
2022-04-04 16:27:07 -04:00
lwvmobile
672e5cbd44 bug fixes
bug fixes and tweaks
2022-04-04 16:12:19 -04:00
lwvmobile
c1eb511855 Merge pull request #53 from lwvmobile/pulse-raw2
Fix Raw Audio Monitoring
2022-04-03 06:21:25 -04:00
lwvmobile
2ccc27c03b Fix Minor Ncurses DMR Regression
Fix Minor Ncurses DMR Regression
2022-04-02 19:17:55 -04:00
lwvmobile
43c1c9823b Remove dmr_branding from ncurses printer
Remove dmr_branding from ncurses printer
--unreliable at the moment
2022-04-02 17:15:59 -04:00
lwvmobile
a3530c58a0 Last Second DMR Print Fix
Fix Voice Header Payload
2022-04-02 16:12:30 -04:00
lwvmobile
48ddba2ab8 Last Second NXDN Print Cleanup 2022-04-02 16:06:55 -04:00
lwvmobile
9b554dfd93 Additional NXDN STDERR Printing Cleanup 2022-04-02 15:58:21 -04:00
lwvmobile
edf465d03c More Code Cleanup 2022-04-02 15:38:01 -04:00
lwvmobile
e7a477dc88 Delete dmr_encryption.c 2022-04-02 14:28:12 -04:00
lwvmobile
7c461844ad Fix Raw Audio Monitoring
Fix Raw Audio Monitoring
--Still crashes Pulse Audio server on Ubuntu/Linux Mint
--PULSE_LATENCY_MSEC=1000 or high value mitigates problem for brief moment, but still crashes
---Issue with Latency, or maybe writing garbage values to pa_simple_write
General Code Cleanup
Tweak NCURSES Printer
Tweak STDERR Printouts
2022-04-02 14:24:47 -04:00
lwvmobile
ab29dd8e6a Merge pull request #48 from lwvmobile/DMR12
DMR 12 Rate Data LRRP
2022-03-26 07:28:02 -04:00
lwvmobile
800fc3101e Fix swapped inverted_dmr to inverted_dpmr
Fix swapped inverted_dmr to inverted_dpmr in the dmr_voice.c file.
-accidentally swapped inverted_dmr to inverted_dpmr while working on dPMR.
2022-03-26 00:10:49 -04:00
lwvmobile
13ec6855f8 DMR 12 Rate Updates
DMR 12 Rate Updates
Other Tweaks to P25 Printing
More experimenting with 34 Rate Trellis Code (busted)
2022-03-26 00:03:14 -04:00
lwvmobile
6a649376fc DMR 12
Fix Compiler Warnings
2022-03-24 08:31:30 -04:00
lwvmobile
1ee3dba499 DMR 12 Rate Data LRRP
DMR 12 Rate Data LRRP Decoding
2022-03-24 07:54:09 -04:00
lwvmobile
94487fd1c0 Merge pull request #47 from lwvmobile/pulsedmr
Staging for DMR Improvements
2022-03-13 21:12:34 -04:00
lwvmobile
4af78289b2 Last Minute NCURSES Tweak
Last Minute NCURSES Tweak
2022-03-13 20:44:48 -04:00
lwvmobile
5ee669aa18 Fix Compiler Warning, minor tweaks
Fix Compiler Warning, minor tweaks to NCurses view and information logging
2022-03-13 20:32:32 -04:00
lwvmobile
6afb0d718f Update Information for README
Update Information for README
2022-03-13 20:30:29 -04:00
lwvmobile
ea1f0bdf2f Update Screenshot
Update Screenshot
2022-03-13 19:49:13 -04:00
lwvmobile
184b8b6a39 Enable D-Star, Pulse Tweaks, NCurses Tweaks, Misc
Enable D-Star, Pulse Tweaks, NCurses Tweaks, Misc
2022-03-13 19:13:09 -04:00
lwvmobile
0fc9380b00 DMR PI Header, NCurses Tweaks, Fun Stuff
DMR PI Header, NCurses Tweaks, Fun Stuff
--Add PI Header handler, print DMR ALG, KEY, and MI values
--NCurses updates to reflect new DMR and NXDN info available
--Other Fun Stuff a.k.a. Stuff I might have broken or can't remember what I did now
2022-03-12 00:03:21 -05:00
lwvmobile
9f330e7902 NXDN Ncurses Improvements
NXDN Ncurses Improvements
-show TG, call types, alg, keys, etc on NXDN systems
2022-03-09 18:10:32 -05:00
lwvmobile
d756ab6574 DMR, dPMR, Pulse Audio Tinkering
DMR, dPMR, Pulse Audio Tinkering
2022-03-08 16:57:26 -05:00
lwvmobile
beb96f73db Fix MI to llX
Fix MI to llX
2022-03-06 22:48:44 -05:00
lwvmobile
f053e53e76 More DMR Staging, Fix MI to Hex in P25
More DMR Staging, Fix MI to Hex in P25
-switch back to 7 octets on AMB payloads
--will break compatibility with 6 octets
-Print MI values as Hex now, not as binary strings
2022-03-06 22:38:29 -05:00
lwvmobile
c994f00e5d Fix issue on amb file playback
Fix issue on amb file playback
-switching from 7 back to 6, otherwise older files may break when playing back 
-still needs testing on 6 to 6, and 7 to 6 reads
2022-03-06 18:42:50 -05:00
lwvmobile
5834914e91 Staging for DMR Improvements
Staging for DMR Improvements
2022-03-06 18:30:29 -05:00
lwvmobile
2b71652b23 Merge pull request #46 from lwvmobile/lwvmobile-patch-p
Tweak Ncurses; Tweak Modulations
2022-02-28 17:06:34 -05:00
lwvmobile
d44c050ca4 Tweak Ncurses; Tweak Modulations
Tweak Ncurses:
-update NXDN call log values; fix ran and initial rd value
Tweak Modulations:
-set correct modulation types for P25 and DMR systems
2022-02-28 16:51:17 -05:00
lwvmobile
1e9ea14a51 Merge pull request #43 from lwvmobile/lwvmobile-patch-1
More ALG,KEY,MI 'fixes'
2022-02-26 08:00:21 -05:00
lwvmobile
f6fdbfa922 More ALG,KEY,MI 'fixes'
Only assign and print when err == 0
2022-02-26 07:59:24 -05:00
lwvmobile
7a341fa1c9 Merge pull request #41 from lwvmobile/lwvmobile-patch-1
Fix Alg,Key,MI
2022-02-25 19:43:36 -05:00
lwvmobile
786f306eac Fix Alg,Key,MI
Wasn't always printing
2022-02-25 19:41:31 -05:00
lwvmobile
b61b77e773 Merge pull request #39 from lwvmobile/lwvmobile-patch-3
Fix to MBE filenames for IMBE types
2022-02-25 15:27:21 -05:00
lwvmobile
bba8579192 Fix to MBE filenames for IMBE types
Fix MBE filenames, no nac/tg on ProVoice type
2022-02-25 15:27:03 -05:00
lwvmobile
aca0adc4c5 Merge pull request #38 from lwvmobile/lwvmobile-patch-2
Append WAV Files
2022-02-25 15:17:27 -05:00
lwvmobile
b996d95b1c Append WAV Files
Append to WAV files as opposed to overwriting them each time
Minor Tweaks to NCURSES terminal in regards to NXDN, update call log on systems with RAN of 0
2022-02-25 15:09:06 -05:00
lwvmobile
71be900630 Merge pull request #37 from lwvmobile/lwvmobile-patch-1
MI Values
2022-02-25 08:34:00 -05:00
lwvmobile
5530d2ac9c MI Values
MI Values
2022-02-25 08:31:54 -05:00
lwvmobile
a64c4187e5 Merge pull request #36 from lwvmobile/lwvmobile-patch-1
P25P1 Information
2022-02-24 20:20:05 -05:00
lwvmobile
568a8521c0 Informational Print to Main
fprintf Unmute P25 Encrypted Audio
2022-02-24 20:19:11 -05:00
lwvmobile
ab186da2f3 P25P1 Information
P25P1 Information
-added fprintf and printw for algid, keyid, mfid, errs, etc
2022-02-24 20:05:32 -05:00
lwvmobile
11d72c39d3 Merge pull request #35 from lwvmobile/lwvmobile-patch-1
Tweaks to Instal Script and Readme
2022-02-22 18:37:06 -05:00
lwvmobile
8669f6c309 remove ITPP from auto building
users will need to manually build ITPP if they cannot download and install it from their repo.
2022-02-22 18:07:40 -05:00
lwvmobile
fdaeae1307 Update README.md 2022-02-22 18:03:50 -05:00
lwvmobile
010ff8cb60 Tweaks to Instal Script and Readme 2022-02-22 17:53:45 -05:00
lwvmobile
12c7379d2a Add ncurses example to readme
Add ncurses example to readme
2022-02-22 11:01:21 -05:00
lwvmobile
ae462d74a5 Update Screenshot 2
Update Screenshot 2
2022-02-22 10:54:21 -05:00
lwvmobile
aa2600cdc2 Update Screenshot
Update Screenshot
2022-02-22 10:53:23 -05:00
lwvmobile
69034b7c2f Update Screenshot
Update Screenshot
-show ncurses terminal
2022-02-22 10:24:12 -05:00
lwvmobile
49ea9f7f52 Merge pull request #33 from lwvmobile/lwvmobile-patch-1
Little Tweaks
2022-02-22 08:50:52 -05:00
lwvmobile
670e4e7bec Little Tweaks
Little Tweaks
2022-02-22 08:49:46 -05:00
lwvmobile
5b4d624bb2 Merge pull request #32 from lwvmobile/lwvmobile-patch-cmake
Lwvmobile patch cmake
2022-02-21 13:47:57 -05:00
lwvmobile
1a4440f1ab Add files via upload 2022-02-21 13:17:44 -05:00
lwvmobile
c917c61b85 Fix CMAKE set
Fix CMAKE set Curses to Ncurses, need wide true
2022-02-21 13:11:07 -05:00
lwvmobile
a239cd94fe Delete nxdn_voice.c.bkp3 2022-02-21 12:57:44 -05:00
lwvmobile
a6c83a7fbb Delete nxdn_data.c.bkp2 2022-02-21 12:57:36 -05:00
lwvmobile
c7e52ab4fe Delete nxdn_data.c.bkp 2022-02-21 12:57:28 -05:00
lwvmobile
df14508778 Delete dsd_frame.c.bkp 2022-02-21 12:57:20 -05:00
lwvmobile
178b76f092 Delete 222FindCURSES.cmake.old 2022-02-21 12:57:04 -05:00
lwvmobile
36fbbfd4d7 Delete FindCURSES.cmake.bkp 2022-02-21 12:56:57 -05:00
lwvmobile
d1903b1574 Delete FindCURSES.cmake 2022-02-21 12:56:48 -05:00
lwvmobile
fea03a0242 CMake Changes, NCURSES Tweaks
Remove NCurses FindCurses.cmake file, change lines in Cmakelists
Tweak and Fix a few NCurses Terminal tidbits
2022-02-21 12:55:20 -05:00
lwvmobile
2ed2dc36aa Minor Tweak
Add lines to instruct user to hit CTRL+C again on exit, and also to run reset in terminal to clean up any mess.
2022-02-20 17:03:47 -05:00
lwvmobile
2223183805 Fix CmakeLists.txt
Had to change PulseAudio back to PULSEAUDIO
Arch complains about it in all caps, but Mint refuses to build without it in all caps.
2022-02-20 16:50:04 -05:00
lwvmobile
25affc5b54 Merge pull request #31 from lwvmobile/ncursesandnxdn
NCurses and NXDN Updates
2022-02-20 16:31:04 -05:00
lwvmobile
ad13d06acc Delete nxdn_voice.c.bkp3 2022-02-20 16:25:18 -05:00
lwvmobile
291f9ed963 Delete nxdn_data.c.bkp2 2022-02-20 16:25:03 -05:00
lwvmobile
455a7a9329 Delete nxdn_data.c.bkp 2022-02-20 16:24:55 -05:00
lwvmobile
7e9eadaea5 Delete dsd_frame.c.bkp 2022-02-20 16:24:45 -05:00
lwvmobile
387f2253b1 More NCURSES Tweaking
More NCURSES Tweaking
-tweak array, seems to update smoother now, immediate
-more concise, cleaner names for sync types
-array updates on DMR, P25P1, NXDN, PV types only currenlty (no dstar etc yet)
More P25 resetState tweaking/testing
Other under the hood tweaking
2022-02-20 15:41:28 -05:00
lwvmobile
af401a7e5b More NCURSES tweaking
More NCURSES tweaking
resetState memory leak mitigation
2022-02-19 19:44:56 -05:00
lwvmobile
4404c1afed remove printw carrier
remove printw carrier
only had that in as a quick test, forgot to disable it
2022-02-19 10:47:52 -05:00
lwvmobile
417a48cc71 resetState and Minor Ncurses Printer tweak
resetState and Minor Ncurses Printer tweak
don't run with -n yet, resets state, using to test to fix when input type changes, but won't decode properly
2022-02-19 10:42:55 -05:00
lwvmobile
9acff308b9 OCD change
OCD change to how rtl info is presented in ncurses window
2022-02-19 06:35:39 -05:00
lwvmobile
48c9ae4b28 disable FM_banner
disable FM_banner on ncurses terminal, still can't figure out ncursesw support in cmake
2022-02-19 06:20:19 -05:00
lwvmobile
c3c4956ebb set(CURSES_NEED_WIDE TRUE)
set(CURSES_NEED_WIDE TRUE)
2022-02-19 06:13:07 -05:00
lwvmobile
9751eb165f set(CURSES_NEED_WIDE TRUE)
set(CURSES_NEED_WIDE TRUE)
2022-02-19 06:09:35 -05:00
lwvmobile
409321eb43 NCurses wide support and cmake
NCurses wide support and cmake
2022-02-19 05:55:13 -05:00
lwvmobile
6f2682d667 NCurses and NXDN Updates
NCurses Terminal in pretty good shape, good enough for testing
NXDN decodes RAN and RID now (from LEH)
2022-02-18 19:05:14 -05:00
lwvmobile
46196cf283 Merge pull request #30 from lwvmobile/lwvmobile-patch-nxdn-mod2
NXDN48/96 Modulation Fix
2022-02-15 11:04:50 -05:00
lwvmobile
80526be3ed NXDN48/96 Modulation Fix
Change NXDN to C4FM Modulation and change RF_mod type.
Not sure if it actually affects performance, but its the correct type.
Decoding relies more on samplesPerSymbol than anything.
2022-02-15 11:00:03 -05:00
lwvmobile
d26d45eb7e Merge pull request #28 from lwvmobile/lwvmobile-patch-stdin-fix
fix STDIN audio rates
2022-02-14 15:01:29 -05:00
lwvmobile
36f522922d fix STDIN audio rates
fix STDIN audio rates
made a boo boo with pulse audio rates tinkering, had to fix rates for STDIN so that decoded audio played out at the correct rate
2022-02-14 14:59:45 -05:00
lwvmobile
d70cab2499 Update README.md 2022-02-13 19:11:06 -05:00
lwvmobile
6583fbe8f7 OCD tweak
OCD tweak
2022-02-13 17:54:09 -05:00
lwvmobile
b3383cd543 Add Virtual Sink script
Add Virtual Sink script, making it even easier to get going
2022-02-13 17:53:05 -05:00
lwvmobile
9596301aee Merge pull request #26 from lwvmobile/lwvmobile-patch-readme
Lwvmobile patch readme
2022-02-13 17:49:10 -05:00
lwvmobile
fbbd4285f5 wget install.sh
wget isntall.sh, make it easy
2022-02-13 17:47:04 -05:00
lwvmobile
97bc736f86 Update README.md 2022-02-13 17:41:03 -05:00
lwvmobile
460b8999c3 Delete Screenshot_216.png 2022-02-13 17:39:09 -05:00
lwvmobile
88d7a59afe Delete Screenshot_214.png 2022-02-13 17:39:00 -05:00
lwvmobile
6be364c6ce New Screenshot with uniform name
New Screenshot with uniform name
Make new screenshots uploaded seemless
2022-02-13 17:38:47 -05:00
lwvmobile
3d83c0b06c Update README.md 2022-02-13 17:36:04 -05:00
lwvmobile
3b9185fc40 Update README.md 2022-02-13 17:34:43 -05:00
lwvmobile
5897131f05 Merge pull request #25 from lwvmobile/lwvmobile-patch-1
Fix Naughty Merge
2022-02-13 15:36:43 -05:00
lwvmobile
c7a83c8c51 Fix Naughty Merge
Fix Naughty Merge
Didn't know resolving issue on a test pull I wouldn't commit would change the file.
2022-02-13 15:36:06 -05:00
lwvmobile
0eda6f5cf1 Merge branch 'master' into pulseaudio 2022-02-13 14:38:47 -05:00
lwvmobile
718d049927 Merge pull request #23 from lwvmobile/lwvmobile-patch-under-the-hood
Add Github Build Version
2022-02-13 14:35:33 -05:00
lwvmobile
e0c2e740d2 Add Github Build Version
Add Github Build Version
Work on Pulse Audio System (Testing 2 Channel Stereo Output)
Work on NCurses Terminal (Still Work to Do)
Include install.sh and rebuild.sh scripts
2022-02-13 14:31:15 -05:00
lwvmobile
aab0208605 Merge pull request #22 from lwvmobile/lwvmobile-patch-1
Show Github Build Version
2022-02-12 16:23:13 -05:00
lwvmobile
d037d2e0ad quick link fix
quick link fix
2022-02-12 15:58:50 -05:00
lwvmobile
dd8e48789f Add hyperlink to bottom of banner
Add hyperlink to bottom of banner
2022-02-12 15:56:03 -05:00
lwvmobile
d37ccf2883 Show Github Build Version 2022-02-12 15:50:11 -05:00
lwvmobile
6fb16cbd95 Update README.md 2022-02-11 19:39:36 -05:00
lwvmobile
7a3e1a67da Merge branch 'master' into pulseaudio 2022-02-11 19:32:21 -05:00
lwvmobile
0f5c019d44 Merge pull request #21 from lwvmobile/lwvmobile-patch-ncurses-initial
Initial NCURSES Support
2022-02-11 19:29:56 -05:00
lwvmobile
9092259ce1 Initial NCURSES Support
Initial NCURSES Support

Its in there, and can be called with -N, but it doesn't display much yet aside from program name, mbelib, and time.
Just want to upload it so I can test it vs autobuild with cmake requiring ncurses libraries again
2022-02-11 19:25:05 -05:00
lwvmobile
079ed7e8cc Merge pull request #20 from lwvmobile/lwvmobile-patch-NXDN-bug-fix
NXDN Detection Bug
2022-02-11 18:44:13 -05:00
lwvmobile
3bb8a5608f NXDN Detection Bug
Had to fix line 644 with this
Had wrong detection type

else if ((strncmperr (synctest18, INV_NXDN_BS_VOICE_SYNC, 18, 1) == 0) || (strncmperr (synctest18, INV_NXDN_MS_VOICE_SYNC, 18, 1) == 0)) //supposed to be voice, not data?
2022-02-11 18:40:04 -05:00
lwvmobile
142fb0c60e Merge pull request #19 from lwvmobile/lwvmobile-patch-nxdn-inv-detection-fix
NXDN bug fix
2022-02-11 18:34:10 -05:00
lwvmobile
d088d09328 NXDN bug fix
Had to fix line 651 with correct detection type

else if ((strncmperr (synctest18, INV_NXDN_BS_VOICE_SYNC, 18, 1) == 0) || (strncmperr (synctest18, INV_NXDN_MS_VOICE_SYNC, 18, 1) == 0))
2022-02-11 18:32:28 -05:00
lwvmobile
baca1be1b3 Simple Quick Installer
Simple Quick Installer
Should work on any Ubuntu/Debian/Raspberry Pi OS/ based system.
2022-02-10 19:57:57 -05:00
lwvmobile
47c43f9c9c Update README.md 2022-02-10 19:30:05 -05:00
lwvmobile
b1dcbeb459 Update README.md 2022-02-10 19:14:33 -05:00
lwvmobile
ceb98222b6 Update README.md 2022-02-10 19:09:30 -05:00
lwvmobile
34338ce2b1 Update README.md 2022-02-10 18:02:39 -05:00
lwvmobile
3ef8c846c6 Merge pull request #18 from lwvmobile/lwvmobile-patch-1
Direct all prints to STDERR
2022-02-10 17:08:32 -05:00
lwvmobile
325bff80d0 Print to STDERR
Print to STDERR
Take 3
2022-02-10 17:04:47 -05:00
lwvmobile
fcf680a138 Direct all prints to STDERR
Direct all prints to STDERR
Take 2
2022-02-10 16:56:37 -05:00
lwvmobile
05b9e29724 Update README.md 2022-02-10 16:43:07 -05:00
lwvmobile
fcfe06e1b2 Update README.md 2022-02-08 19:22:39 -05:00
lwvmobile
7e4a537035 Forgot to upload this 2022-02-08 18:46:03 -05:00
lwvmobile
62bf2d91ca Add files via upload
try try again
2022-02-08 18:38:09 -05:00
lwvmobile
003b956d2c Merge pull request #16 from lwvmobile/lwvmobile-patch-pulse-stdin-etc
Fix STDIN, file playback, and WAV file creation
2022-02-08 18:37:33 -05:00
lwvmobile
c408a57198 Fix STDIN, file playback, and WAV file creation
Fix STDIN and file playback
set rate out to 8000 when calling for any of these methods
2022-02-08 18:35:56 -05:00
lwvmobile
9f0e0adee8 Delete dsd.h.bkp 2022-02-08 13:11:07 -05:00
lwvmobile
b23705b7b6 Delete dsd_audio.c.bkp 2022-02-08 13:10:45 -05:00
lwvmobile
eaec87ac44 Merge pull request #15 from lwvmobile/lwvmobile-patch-pulse1
More Pulse Audio Tinkering
2022-02-08 13:06:20 -05:00
lwvmobile
4f012390c0 More Pulse Audio Tinkering
More Pulse Audio Tinkering
Had to disable Monitor Source Input
(Was working in Arch, but with some latency/buffer issues, makes LM pulse server crash/restart?)
2022-02-08 13:04:29 -05:00
lwvmobile
d125d12132 Update README.md 2022-02-08 12:11:28 -05:00
lwvmobile
617b5d8072 Add files via upload 2022-02-08 12:08:43 -05:00
lwvmobile
4b81636947 Add pulseaudio stuff
add pulseaudio stuff
2022-02-08 00:46:49 -05:00
lwvmobile
4c8f0f3c50 Merge pull request #12 from lwvmobile/lwvmobile-patch-pulse
PulseAudio
2022-02-08 00:33:30 -05:00
lwvmobile
130afdc278 PulseAudio
Swap Pulse Audio in for all instance where OSS is used
Lots of other potentially things breaking
2022-02-08 00:26:14 -05:00
lwvmobile
a4aab9a56d Merge pull request #11 from lwvmobile/lwvmobile-patch-3
Fix Monitor Source Audio Issue
2022-02-05 14:42:56 -05:00
lwvmobile
2b8ce4b248 Fix Monitor Source Audio Issue
Fix Monitor Source Audio Issue
Forgot to add 'now' variable to all sync types, would attempt to play source audio and decoded audio at same time on most types.
2022-02-05 14:37:27 -05:00
lwvmobile
be165c8c77 Update README.md 2022-02-05 13:49:35 -05:00
lwvmobile
46fa8b5c60 Update README.md 2022-02-05 13:45:31 -05:00
lwvmobile
628b9c30c2 Add files via upload 2022-02-05 13:45:17 -05:00
lwvmobile
13a453193a Update README.md 2022-02-05 13:38:37 -05:00
lwvmobile
b58b2b578a Merge pull request #10 from lwvmobile/lwvmobile-patch-2
Add RTL VFO bandwidth options
2022-02-05 13:36:05 -05:00
lwvmobile
b66cac75d2 Add RTL VFO bandwidth options
Add RTL VFO bandwidth options
-Y 8 for 8 kHz VFO
-Y 24 for 24 kHz VFO

Should work at any interval where 48 cleanly divides by a number
2022-02-05 13:30:44 -05:00
lwvmobile
6573521363 Update README.md 2022-02-05 13:27:48 -05:00
lwvmobile
f5ed83289f Update README.md 2022-02-05 13:26:45 -05:00
lwvmobile
2859ad3927 Update README.md 2022-02-05 11:07:07 -05:00
lwvmobile
aaa8e5c3f1 Update README.md 2022-02-05 10:41:29 -05:00
lwvmobile
f6673b6f59 Update README.md 2022-02-05 10:39:58 -05:00
lwvmobile
f3dac806a8 Update README.md 2022-02-05 10:31:46 -05:00
lwvmobile
92db25f56d Add Screenshot
Add Screenshot for README
2022-02-05 10:28:05 -05:00
lwvmobile
4e60c61eb0 Update README.md 2022-02-05 10:22:19 -05:00
lwvmobile
fd9f9126d7 Merge pull request #9 from lwvmobile/lwvmobile-patch-1
Remove NXDN from -fa option
2022-02-05 09:54:02 -05:00
lwvmobile
7e6b3adf9d Remove NXDN from -fa option
Removed NXDN from -fa option so it doesn't cause a bunch of random false positives
Clean up a few little things, hopefully nothing too major.
2022-02-05 09:44:26 -05:00
lwvmobile
e0bddd6699 Merge pull request #8 from lwvmobile/lwvmobile-patch-1
Source Audio Monitoring (WIP)
2022-02-04 18:16:08 -05:00
lwvmobile
d98040895d Source Audio Monitoring (WIP)
Add Source Audio Monitoring - But its a Work in Progress
Seriously, though, depending on which setting you have on, it sounds terrible.

Also, make ncurses no longer a requirement for the time being. Still want to work that into this version eventually.
2022-02-04 18:01:33 -05:00
lwvmobile
5a8b6f1d4a Update README.md 2022-02-01 22:38:43 -05:00
lwvmobile
4ca81a5dc2 Merge pull request #7 from lwvmobile/lwvmobile-patch-1
Add RTL option -U to specify udp port
2022-02-01 22:30:56 -05:00
lwvmobile
7222e71c08 Fix example usage to include all current options 2022-02-01 22:29:49 -05:00
lwvmobile
cb9e6c632c Add RTL option -U to specify udp port
Add RTL option -U to specify udp port for remote control
2022-02-01 22:23:49 -05:00
lwvmobile
005d356859 Merge pull request #6 from lwvmobile/lwvmobile-patch-1
Added a few more RTL options
2022-02-01 21:41:01 -05:00
lwvmobile
7a94be9292 Added a few more RTL options
Added RTL options for Squelch (-L) and Sample 'Volume' Multiplier -V
Hopefully, I didn't break anything else in the process
2022-02-01 21:37:09 -05:00
lwvmobile
9ae874449f Merge pull request #4 from lwvmobile/lwvmobile-patch-1
NXDN48 Voice Decoding is Good Now
2022-02-01 00:03:39 -05:00
lwvmobile
1e95db0977 NXDN48 Voice Decoding is Good Now
NXDN48 Voice Decoding is Good Now

Just use squelch if using SDR++ or GQRX to feed it. RTL-SDR input mode and NXDN still makes tons of false positives, and doesn't work well at all. May just be a bad signal issue but still think the RTLSDR input uses wrong center freq/tuned freq/"optimizations", need to get back to that some time, maybe also implement squelch settings on rtl_fm mode.

Thanks to LouisErigHerve for his 'Improved NXDN Detection' update on his version, implemented it and works SO MUCH BETTER!

May still disable the if ((state->lastsynctype == 9) || (state->lastsynctype == 17)) condition in dsd_frame_sync.c if I think it leads to more consistent decoding vs false positives. USE SQUELCH!
2022-01-31 23:54:14 -05:00
lwvmobile
93ccc668d7 add quick usage example 2022-01-29 22:57:21 -05:00
lwvmobile
defa6455d6 Update README.md 2022-01-29 22:43:54 -05:00
lwvmobile
7c5fef56e9 Merge pull request #2 from lwvmobile/rtl-sdr
Attempt to make the rtl-sdr branch the main branch
2022-01-29 22:42:00 -05:00
lwvmobile
6ed5e0bcab fix things so stupid checker will pass 2022-01-29 22:34:05 -05:00
lwvmobile
31157b72ab Update codeql-analysis.yml
add libncurses5 and libncurses5-dev
2022-01-29 22:30:54 -05:00
lwvmobile
0cb68d7c53 Merge branch 'master' into rtl-sdr 2022-01-29 22:25:38 -05:00
lwvmobile
b67a3bb6de Update README.md 2022-01-29 22:17:19 -05:00
lwvmobile
d0086a5f96 Update README.md
Add warnings to README file
2022-01-29 22:11:28 -05:00
lwvmobile
2b84518caf Merge pull request #1 from lwvmobile/lwvmobile-patch-1
Add More RTL Options, UDP Remote
2022-01-29 22:06:49 -05:00
lwvmobile
60593dcce9 Clean up some naughty language
Clean up some naughty language
2022-01-29 22:05:15 -05:00
lwvmobile
fad831d1aa Delete FindNCURSES.cmake
Didn't mean to include this file
2022-01-29 22:03:42 -05:00
lwvmobile
3c11d72652 Delete old ASCII art
Delete old ASCII art and printer
2022-01-29 21:59:55 -05:00
lwvmobile
97a17df9d1 Delete todolist
Didn't mean to upload the todolist
2022-01-29 21:58:03 -05:00
lwvmobile
2523383b14 Add More RTL Options, UDP Remote
Add Gain and Device Index Number for RTL, add UDP remote to control frequency and squelch of RTL. Other Random things.
2022-01-29 21:53:35 -05:00
Jared Szechy
a50f3b714f Install deps 2021-12-13 16:38:44 -05:00
Jared Szechy
df3f58b05e Create codeql-analysis.yml 2021-12-13 16:31:03 -05:00
Yinette Hodge
2debc3e5f1 Fixing multiple definitions of exitflag (szechyjs#104) (#105)
Thanks to N0YKG for the fix! I am just making the PR :)
2021-10-21 13:47:46 -04:00
Jared Szechy
f383d165fd Add ppm_error option 2021-01-21 23:14:04 -05:00
Jared Szechy
5324d53c47 Add center freq cli opt 2021-01-17 19:37:00 -05:00
Jared Szechy
f0f067510f Require threads 2021-01-16 22:53:08 -05:00
Jared Szechy
2bdcc9cc65 Update deps 2021-01-16 22:33:27 -05:00
Jared Szechy
0b0754c1d5 It works 2021-01-16 21:43:23 -05:00
Jared Szechy
502607a608 Start on rtlsdr 2021-01-16 18:29:20 -05:00
ra1nb0w
290a2204ba cmake: fix portaudio linking issue (#103) 2020-12-22 16:38:51 -05:00
Jared Szechy
f175834e45 Update travis 2018-07-01 10:07:39 -04:00
Jared Szechy
98f9e36291 Cleanup cmake find modules 2018-07-01 09:55:06 -04:00
Jared Szechy
5f8aedb864 Don't use default tar-ignore, we need the .git dir for version 2018-06-29 00:02:54 -04:00
Jared Szechy
b674f277d9 Remove samples submodule 2018-06-28 23:33:16 -04:00
Jared Szechy
386cf93eb1 Add debian packaging 2018-06-28 23:03:12 -04:00
Jared Szechy
7ee04e58c5 Use the new travis containers 2015-08-06 12:54:09 -04:00
Jared Szechy
9eaaa323fe More cleanup 2015-08-06 12:34:56 -04:00
Jared Szechy
6605925162 Some much needed organization 2015-08-06 12:25:30 -04:00
Jared Szechy
6fbda52998 Update CONTRIBUTING.md 2015-08-06 11:44:38 -04:00
Jared Szechy
8e60a7b6a8 Add contributing file 2015-08-06 11:43:44 -04:00
Jared Szechy
e18cbf730e Fix link 2015-08-06 11:31:14 -04:00
Jared Szechy
8db618f83d Tweaks to README 2015-08-06 11:29:36 -04:00
Jared Szechy
60807e09ab Merge pull request #48 from dreinhold/master
Fixed 12.5KHz NXDN poor performance due to the 6.25KHz filter being used...
2014-10-08 14:52:46 -04:00
LinuxSheeple-E
c55f8b71fd Fixed 12.5KHz NXDN poor performance due to the 6.25KHz filter being used. 2014-10-05 19:12:35 -07:00
Jared Szechy
cc1fb3b33c Merge pull request #45 from midnightmagic/libsndfile_help_output
-h now lists the new '-' option (woops)
2014-09-14 21:10:32 -04:00
midnightmagic
cfcbf6a8da -h now lists the new '-' option (woops) 2014-09-14 14:15:53 -07:00
Jared Szechy
dd5c11acb3 Merge pull request #44 from midnightmagic/libsndfile_stdin_pipe_support
libsndfile can be used to open a UNIX-style pipe
2014-09-14 17:02:07 -04:00
midnightmagic
fd998e2bf7 libsndfile can be used to open a UNIX-style pipe on many platforms
In specific, it can be used to read in data almost directly from Gqrx
Currently, this will not function correctly using -i /dev/stdin on OS X
because stdin is not seekable, and it is not actually a file; also, for
ongoing raw data, there are no hints for libsndfile to identify the
sample.

So, we can use the UNIX-y '-' to do libsndfile-approved fd handle open.

Tested with the dsd-samples wav after converting it to correct sampletype
(stereo) -> sox p25_raw_unencrypted_edf.wav -c 2 ~/gqrx_last_recorded_filename.wav
.. and then pressing 'play' in the Gqrx interface while running:

socat udp-listen:7355 - | dsd -i - -o pa:2

.. in the background. And it decoded it nicely as many times as I played it,
which was 5 or 10.
2014-09-11 03:32:22 -07:00
Jared Szechy
6a9aa076b8 Add option to display available portaudio devices. 2014-07-07 20:21:39 -04:00
Jared Szechy
45eb53f245 Merge pull request #38 from jpwichern/master
Portaudio support
2014-06-09 15:50:52 -04:00
Jiri Wichern
74fa117850 Tweaking the delay a bit... 0,050 s for output seems too little; it results in choppy audio and ultimately, a heap of errors. 0,100 seems ok for the output buffer. But the input buffer needs to be much larger else it overflows. Also, an extra stop stream before terminating to make sure all audio from the audio buffer is played. 2014-06-07 20:49:35 +02:00
jpwichern
5dd236ec96 Update dsd_audio.c
Small oops
2014-06-06 17:26:50 +02:00
jpwichern
55b190ef49 Update dsd.h, dsd_audio.c, dsd_main.c and dsd_symbol.c
Adding portaudio support. Implementing it as a new audio_..._type in dsd.h.

Update CMakeLists.txt and create FindLibPortAudio.cmake

CMake portaudio check
2014-06-05 21:54:38 +02:00
Jared Szechy
df3c0dcab4 Merge pull request #36 from robotastic/master
Fixes a crash if an irrecoverable error happens
2014-05-27 19:19:36 -04:00
robotastic
d077cd8316 Fixes a crash if an irrecoverable error happens
If an irrecoverable error happens, you should start a new sequence
since the previous dibit was not set
2014-05-26 22:30:45 -04:00
Jared Szechy
05caba143b Merge pull request #35 from dreinhold/master
Allow the mbe file move to work in windows based systems.
2014-05-26 17:56:39 -04:00
Dylan Reinhold
f2767707ef Allow the mbe file move to work in windows based systems. 2014-05-26 10:37:15 -07:00
Jared Szechy
4e338dede9 Fixed file output directory.
- This fix is very minimal, as a result output directory arguments
    must have a trailing slash.

Fixes #34
2014-05-26 12:38:07 -04:00
Jared Szechy
f207eba8f9 Use svg travis-ci badge 2014-05-15 22:06:41 -04:00
Jared Szechy
3e9dc91e81 Merge pull request #30 from EdFuentetaja/master
P25 "heuristiscs" module
2014-05-10 21:28:18 -04:00
Jared Szechy
c39b0e6e43 Add travis-ci badge to README 2014-05-10 21:10:47 -04:00
Jared Szechy
a9995c695a Add sudo to install 2014-05-10 21:07:00 -04:00
Jared Szechy
f062fe0ef3 Forgot directory on cmakes 2014-05-10 21:04:57 -04:00
Jared Szechy
5f30a6aad6 Make travis_deps.sh executable 2014-05-10 21:03:04 -04:00
Jared Szechy
e0c574c470 Fix package names 2014-05-10 21:01:31 -04:00
Jared Szechy
68c471d639 First cut at enabling travis-ci 2014-05-10 20:55:31 -04:00
EdFuentetaja
1c05c6da6e Merge branch 'master' of https://github.com/EdFuentetaja/dsd.git 2014-05-11 08:43:54 +08:00
EdFuentetaja
8f61ac63a3 Minor fix in comments. 2014-05-11 08:43:06 +08:00
EdFuentetaja
840dbac8b0 Minor fix in comments. 2014-05-11 08:34:40 +08:00
EdFuentetaja
a0c7d16905 Merge branch 'master' of https://github.com/szechyjs/dsd.git
Conflicts:
	dsd.h
	test/reedsolomon-test.cpp
2014-05-11 08:21:20 +08:00
EdFuentetaja
9655ab0bd7 Merge branch 'master' of https://github.com/EdFuentetaja/dsd.git 2014-05-11 08:16:37 +08:00
EdFuentetaja
9e6b3b7978 Included error correction on P25 TDULC.
Added a new "heuristics" module for P25 that improves the estimation of the symbol (dibit) that corresponds to a given analog signal value.
Added a BER (bit error reate) estimation for P25.
2014-05-11 08:14:29 +08:00
EdFuentetaja
d8181ec79e Fixed some compilation warnings and added a few comments. 2014-05-11 08:14:29 +08:00
EdFuentetaja
f8cd8d715f Added error correction to the metadata of LDU1 and LDU2.
Refactored implementation of p25p1_ldu1.c and p25p1_ldu2.c. There are less duplicities now and hopefully is easier to read and maintain.
Added a unit test for the Hamming algorithm.
2014-05-11 08:14:28 +08:00
Jared Szechy
20c55d073b Update samples 2014-05-11 08:14:28 +08:00
Jared Szechy
1e854adb84 Use gtest for golay24 and reed-solomon tests 2014-05-11 08:14:27 +08:00
Jared Szechy
6ab62d006a Add itpp as a requirement in readme 2014-05-11 08:14:27 +08:00
EdFuentetaja
9d0fe07931 Included error correction on P25 TDULC.
Added a new "heuristics" module for P25 that improves the estimation of the symbol (dibit) that corresponds to a given analog signal value.
Added a BER (bit error reate) estimation for P25.
2014-05-11 02:23:24 +08:00
EdFuentetaja
b45e563e54 Fixed some compilation warnings and added a few comments. 2014-05-06 23:57:15 +08:00
Jared Szechy
ad0c849dc4 Merge pull request #26 from waffle-iron/master
waffle.io Badge
2014-04-26 10:59:00 -04:00
Making GitHub Delicious.
3229737e6c add waffle.io badge 2014-04-26 08:57:22 -06:00
Jared Szechy
596451eba0 Fix compile errors from #23 2014-04-22 19:46:01 -04:00
Jared Szechy
c23ff4fe0c Merge pull request #23 from EdFuentetaja/master
Error correction on the metadata fields of P25 LDU1 and LDU2
2014-04-22 19:23:07 -04:00
Jared Szechy
134f2e8e21 Move bulk of README to the Wiki 2014-04-22 19:21:43 -04:00
EdFuentetaja
01a2b6959a Added error correction to the metadata of LDU1 and LDU2.
Refactored implementation of p25p1_ldu1.c and p25p1_ldu2.c. There are less duplicities now and hopefully is easier to read and maintain.
Added a unit test for the Hamming algorithm.
2014-03-31 23:37:08 +08:00
EdFuentetaja
7299b78c3b Merge branch 'master' of https://github.com/szechyjs/dsd.git 2014-03-31 21:31:30 +08:00
EdFuentetaja
9b35ef5c83 Added a few comments, fixed ortography and some other cosmetic changes. 2014-03-31 21:31:00 +08:00
Jared Szechy
75972cb024 Update samples 2014-03-12 01:26:16 -04:00
Jared Szechy
461031d3f1 Use gtest for golay24 and reed-solomon tests 2014-03-11 22:29:15 -04:00
Jared Szechy
772eb7f810 Merge pull request #22 from EdFuentetaja/master
Added support for error correction in P25 HDU data.
2014-03-11 21:34:25 -04:00
EdFuentetaja
3f4ff87078 Added support for error correction in P25 HDU data.
Both Golay(24,12,8) and Reed-Solomon(63,47,17) are now implemented.
2014-03-11 22:41:59 +08:00
Jared Szechy
1553973718 Add itpp as a requirement in readme 2014-03-01 18:13:47 -05:00
Jared Szechy
95c3d8fd27 Merge pull request #20 from EdFuentetaja/master
Added decoding and error correction on the NID fields: NAC and DUID.
2014-03-01 15:01:24 -05:00
EdFuentetaja
d7b36f32c0 Parity errors in NIC are now ignored.
Clean up the code to make it easier to understand. Now the logic to error correct the NIC makes more sense.
2014-03-01 20:35:46 +08:00
EdFuentetaja
9b9cb78ddf Added decoding and error correction on the NID fields: NAC and DUID. Making use of the IT++ library for BCH encoding/decoding. 2014-02-02 14:45:19 +08:00
Jared Szechy
e40e89ae81 Merge pull request #15 from dreinhold/add_git_sha_version
Add git tag / SHA1 as a build string
2013-12-19 10:09:02 -08:00
Dylan Reinhold
2f6214fe72 Add git tag / SHA1 as a build string
Every build from commit 20271c6 (Apr 10 2013) is versioned as 1.6.0.
Add a build version after the version number
Digital Speech Decoder 1.6.0 (build:v1.6.0-20-g16110bd)

The build string uses git describe to list the tag closest to head,
and the number of commits past that tag, the a litteral g and ending
with the current SHA1
2013-12-19 08:49:49 -08:00
Jared Szechy
271a7e459a Change current work to v1.7.0-dev 2013-12-19 10:11:38 -05:00
Jared Szechy
e3dde6f7f9 Merge commit '20271c6df57dacbaed3f2255a8230f44f58bd3ae'
Apparently when 1.6.0 was release it was not properly merged. This re-add the disable p25 encryption muting flag.

Conflicts:
	README
	dsd.h
	dsd_main.c
	dsd_symbol.c
	p25p1_ldu1.c
	p25p1_ldu2.c
2013-12-19 09:57:23 -05:00
Jared Szechy
16110bd251 Update README 2013-12-18 16:38:26 -05:00
Jared Szechy
945f93d678 Add samples as a subrepo 2013-12-18 15:58:06 -05:00
Jared Szechy
2630e98128 Remove samples dir, will replace with a submodule for those that want to use it 2013-12-18 15:50:14 -05:00
Jared Szechy
855fdd64e8 Add missing -r option from help command. Also add cmake a s a requirement in the README. 2013-12-18 15:10:15 -05:00
Jared Szechy
158ae7c520 Update README 2013-12-17 03:05:10 -05:00
Jared Szechy
7bd0a10c65 Change CMake to properly locate required libraries 2013-12-17 02:04:22 -05:00
Jared Szechy
e0376bc009 Add samples directory 2013-12-17 01:22:43 -05:00
Jared Szechy
25765ef4d0 Merge pull request #9 from balr0g/filtering-fixes
Filtering fixes
2013-12-16 19:19:22 -08:00
Jared Szechy
9965f8a36d Merge pull request #8 from balr0g/readme-fixes
Updated README for dstar
2013-12-16 19:18:10 -08:00
balr0g
4763717b59 Improvements to CLI input parsing and filtering 2013-12-16 14:50:20 -05:00
balr0g
8c8dea4142 Updated README 2013-12-16 13:04:44 -05:00
Jared Szechy
ac816f9207 Merge pull request #7 from balr0g/dstar
Initial DSTAR AMBE voice decoding support
2013-12-16 10:01:26 -08:00
balr0g
040ea148b0 Initial working DSTAR voice support 2013-12-16 13:00:18 -05:00
Jared Szechy
27960ae4be Merge pull request #6 from balr0g/libsndfile-support
Support for wav input/output using libsndfile
2013-12-16 09:54:14 -08:00
Jared Szechy
5fb79e9baa Merge pull request #5 from balr0g/dmr-ambe+2-rename
Fixes for ambe3600x2250 -> ambe3600x2450 rename
2013-12-15 13:55:19 -08:00
balr0g
de3e5e7f60 Use libsndfile for wav output as well 2013-12-12 20:09:08 -05:00
balr0g
e3bafe96e9 Use libsndfile for wav file input 2013-12-12 19:50:07 -05:00
balr0g
f978a8759e Fixes for ambe3600x2250 -> ambe3600x2450 rename 2013-12-12 09:58:08 -05:00
Jared Szechy
260ec82ea1 Merge pull request #1 from balr0g/cmake-build-fixes
Convert build system to use cmake and fix for Mac OS X build
2013-07-26 10:07:48 -07:00
balr0g
e40c32d8ad Convert build system to use cmake and fix for Mac OS X build 2013-07-07 17:55:42 -04:00
Alex Mirea
5d147c9f04 version 1.6.0
See
http://forums.radioreference.com/digital-voice-decoding-software/256111-
dsd-1-6-closed-fork-open-source.html#post1884783
2013-06-19 18:39:48 -04:00
Jared Szechy
3683f6c631 Fix linking of mbe 2013-06-19 18:30:53 -04:00
Alex Mirea
20271c6df5 new options to control filters and P25 encrypted signals
I have added the -l and -pu options.
2013-04-10 09:05:56 +03:00
Alex Mirea
fa73feded2 version 1.6.0
See
http://forums.radioreference.com/digital-voice-decoding-software/256111-
dsd-1-6-closed-fork-open-source.html#post1884783
2013-04-09 22:50:45 +03:00
Jared Szechy
46b9b01baf add ignore file 2011-08-21 04:01:34 -04:00
Jared Szechy
b1024fba95 version 1.4.1 2011-08-21 03:36:53 -04:00
139 changed files with 41168 additions and 6405 deletions

View File

@@ -1,37 +0,0 @@
1.3 New features:
Decodes voice from ProVoice signals (requires -fp option)
algid and kid are now shown in hex notation
Fixed bugs:
auto output gain now has faster rise time
MoTDMA is now correctly labeled X2-TDMA
1.2 New features:
Decodes voice for DMR standard (including MOTOTRBO)
Full metadata (src, talkgroup, lcinfo, mfid, lcformat, mi, algid, keyid) for X2-TDMA
TDMA slot identification for X2-TDMA/DMR/MOTOTRBO
Identifies non-voice frame types for X2-TDMA/DMR/MOTOTRBO
Frame only (no voice yet) support for 9600 baud NXDN
Auto leveling audio output gain (default) and -g option for fixed gain
GFSK modulation optimizations and improved C4FM/GFSK/QPSK auto detect
Fixed bugs:
nac was showing wrong ID
p25 metadata (lcinfo, mfid, lfcormat, mi, keyid, algid, lsd) are now
printed out in the correct bit order (MSB -> LSB), was reversed.
fixed serveral bugs in dsd_upsample.c, with improved quality
1.1 New features:
Scanner control options to allow scan resume during certain TDULC
Improved upsampling function audio quality
Greatly improved handling of X2-TDMA frames
Much faster QPSK decision point tracking by default
Fixed bugs:
playMbeFiles was not output to .wav file when -w was given
Now correctly detects/handles Mot and P25 talkgroup formats
PDU frames were not detected
1.0.3 Fixed buggy C4FM/QPSK auto detection and added -A option to adjust it.
1.0 Initial release

68
CMakeLists.txt Normal file
View File

@@ -0,0 +1,68 @@
project(dsd-fme)
cmake_minimum_required(VERSION 2.8.11)
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
#Set curses to ncurses, and wide true for ascii
set(CURSES_NEED_NCURSES TRUE)
set(CURSES_NEED_WIDE TRUE)
include(git_revision)
git_describe(GIT_TAG)
find_package(LibSndFile REQUIRED)
find_package(MBE REQUIRED)
find_package(ITPP REQUIRED)
find_package(RTLSDR)
find_package(Curses REQUIRED)
find_package(PULSEAUDIO REQUIRED)
include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${ITPP_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIR})
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${CURSES_LIBRARY})
if(RTLSDR_FOUND)
find_package(Threads)
include_directories(SYSTEM ${RTLSDR_INCLUDE_DIRS})
list(APPEND LIBS ${RTLSDR_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
add_definitions(-DUSE_RTLSDR)
endif(RTLSDR_FOUND)
FILE(GLOB SRCS src/*.c src/*.cpp)
FILE(GLOB HEADERS include/*.h include/*.hpp)
if(NOT RTLSDR_FOUND)
list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_fm.cpp)
endif(NOT RTLSDR_FOUND)
configure_file("src/git_ver.c.in" "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c" @ONLY)
list(APPEND SRCS "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c")
include_directories("${PROJECT_SOURCE_DIR}/include")
ADD_EXECUTABLE(dsd-fme ${SRCS} ${HEADERS})
TARGET_LINK_LIBRARIES(dsd-fme ${LIBS})
include(GNUInstallDirs)
install(TARGETS dsd-fme DESTINATION ${CMAKE_INSTALL_BINDIR})
# man page
find_program(HELP2MAN_FOUND help2man)
if (HELP2MAN_FOUND)
add_custom_command(TARGET dsd-fme POST_BUILD
COMMAND help2man
ARGS -n "Digital Speech Decoder"
--version-string=${GIT_TAG}
-o ${CMAKE_CURRENT_BINARY_DIR}/dsd-fme.1
--no-info
$<TARGET_FILE:dsd-fme>
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dsd-fme.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
endif()
# uninstall target
configure_file(
"cmake/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)

View File

@@ -1,3 +1,18 @@
dstar_header.c/h, descramble.h, and fcs.h are under the following license:
dstar_header.c/h and fcs.h: Copyright (C) 2010 by Kristoff Bonne, ON1ARF
descramble.h: Copyright (C) 2011 by Jonathan Naylor, G4KLX
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
All other code is under the following license:
Copyright (C) 2010 DSD Author
GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)

View File

@@ -1,30 +1,30 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.10
mQENBEufusYBCADN1SNkRkQzdLM02MCjen8KQmfuA38MrQIV0dZLpPJcn1+hsvZZ
KWvpUiWCNw1ClGRsJAT5cjonndi1PV7tpzQz+CyMqSP5OwR40eTJBoZhl74hdmU8
QvcKkJ32khSsOXMo60NHk5iIMDELzOGqq57NHhbunj6NtYog2mR5+WT37JLCvF/q
dvYl2KkRwEHD76/b/O1CyheiiqNMGBb13zPN0qO/PRlENSgViLcDh9qVj4ETNNS6
zMlP4D4pa07iED4Ua31wiXI04ReznnmvMqzQb8uFbg7d98F3bZpsbNURbmsFHMhj
c9qw65nzaMa3OHfZ3Qwl4XRlNqEzZrclIZADABEBAAG0DW1iZWxpYiBBdXRob3KJ
ATgEEwECACIFAkufusYCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEE+Y
6GPqXv4sI/4H/RsumInT5GtDdBTgxt3lERqc5GF8tI5CAQX9DUMMkKxQ306Fgb4M
ISYdepgL6BlbtSyMdKpKsDLScNA6YXER4gMU1nhhyBulqj41kAfsVbG8hiTBy5Ri
FTo36HrC//Zj1MlaTCB8Rl6B6IftAHIb1U+Gda3WnLDe+R5Pym/76TqeYx23PnJl
V+qfrkjbNyOb2bttyko2iccm9DSq/I1OF0Ufz3P60ssUwHW/b2S6WOXRxiZIq0sz
8pAweUFCxIMWo+XYlSpRtwpebfx+JoHJKdW1cSCgY9jwVsOZO1zxc8P+jr3ByiIR
ZPd0PfBt/AHuC6Hmw9nC12e49+4qsHkj50S5AQ0ES5+6xgEIALf60WjqHm552FIX
KqmG0TIrXpCsYoTWzDdO5j0Ro6fmg9U48BqDH5aS4YRvuyPE7q6DGYEnXF5EIPtU
FhUk4FMmez5eMg91UW4Qjqx8M3reoBROYocM5Nfbc9w7St8+3oRS6rUBIh/B6o4O
kEjkoW3R7O3uFpC53Oi9IVhx/joWxhi430RrUTig5CQzuPFTAHd3xJVE6Pv44nsY
/PsOhBLu2Z3kRkvjcGbsGDlAl2bDEobzQdsqXDD8gJkF5W4IeBKvRC54SE0FSm57
G1gadhZ7vT4DWQZvYYKXR+AE7FTcopoAFHdapYokOGLHCguabyeZ5q7+s7J4nddL
p5yq8ikAEQEAAYkBHwQYAQIACQUCS5+6xgIbDAAKCRBPmOhj6l7+LPi6B/sEkDkm
Qs/aTz9HDANBAbTuDZTY4WBbkPdFos3xxC2Z/TH5m4bfHEkvNk5WD+j+vYB3exAc
uBi7ZiqibRpap4kiAiKKCRXOyvmEmrjKB5wOWbRpkdTeEdPcpsQ4dDDw1ZUKwzjN
1jI/9GOgq49nAGdlU59f3IdPut6oQNq9vrtanTq2xHVN/rOhV9GLE6gLX1YWkpIH
zYSUheGPsnv4sWRsQDTB7xmF+6RZzjmauKNwsGFscX1TenOWtUbYb9qFCwvNMOSk
51sEaD6PK6wWTXMqQlgjaoGqh1ZaePgT0QTh1W/bvOD0fL5NEToJl2h/lBSvO+ez
R9B++dt5JX9BMxAH
=puA7
mQENBEufuqsBCADAtWFE1qE3xqJE4xggUn6id0fVulM7y+rSH1VPxo7Ps9duc/O9
VegEx8+N5KphDROS4RgHxMiS1O8Qy5Hpq4gEp6RvLNj3s+0DMwqRZoA0tBCkNmvF
K7sF+GncrOu/NZkDIZ8emN9NWWeWynWJvuM2H5HQA9yCq+YTFae5sgyr3APC3xh4
OkTuVMcclGTJdVrISlNBDpo/AZLJ/nV0SgITpiZVsI4RSNiQPP1kX+2fIDEAwtxN
3HIDyegDoX36cOItsb05zHkLxoUoZnWoxMvV3rwnqXg5cr6PWfiwgqWd0avl1sg6
abpR6MKV68OCpTkqMrDIXJJrM2sTz+ZB2bBtABEBAAG0CkRTRCBBdXRob3KJATgE
EwECACIFAkufuqsCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEPqiY10/
HX/Q11UH/0ZutGQWLZyZD5ZqVdBeYEf6IKhKhaC2vD/+5zUJ2cIgT5xJVTPCKN7X
sygBANJc9GyPRYRvQecdcTHR5B+wUKCPeibv9iQuU3v7Uqb5edXEh2b4jOMWNvHJ
l3lqBdUefuQdbc2xXCPgn+GkyexXEBsVF3hCzWV9r4bCpY57iWUYZZT0wAbN81hz
jPKCCRmF+gZUUufxyhUGq/+eAKeUll6lgG5Ms2YKOlUMphfMav45qgWZHj1DASOV
wPsiIVUaC6+GtHTTzHYRL05ydxxduX+yyKDO1emCE4fx1n3jdwToHLRrC1ui+AJM
Q1s+bp+bGmMYaYaKc/JtNHcN8mbWyCK5AQ0ES5+6qwEIAJvPrnv2QO5Be3FRsnYL
qbA1h/Lj0SjnuimaiNOzCYFIYtusLpyWjwWUnOEXdw5FoExqUStOHJ557SDG/zFB
qy+DsNC9ncSRZ5U2tUQUtt50m2lpYvoy/sVf3oN+IsyZ5hZpiAwrxlyMC/aoGR3C
6ZlLkZ42azNCmEmoBniIxQ+XwhwkLMRsOnqLoFyXB9CcDFIXeEhZkVWZT0B5+20f
yGSBDJ6VcIAASoaIyvSwU9l/DT7D/s0J8WnZQZmRdrsZ3Ikj0Sv/4D2MJnCVJTSw
DHq5Sf7DmUq/WD0iWRLEXWmPt9w3WxP4imhtmIpICxCDoeQUTkqFpOTf29sVcpPt
838AEQEAAYkBHwQYAQIACQUCS5+6qwIbDAAKCRD6omNdPx1/0O8ECADAcPORjGFl
RnIrsgiVMp82GQ7hnZZxktU1WVVx8EsPTT1DD79nIoeKI+UcenzuOGyTX7DGcy+P
rWntUbt2ZygPTbP5Wu6zd845y9EzjxQ5q0vDF4oAefQptwqGDVeO/KBv9cZLbnih
VEjQELgC/rVB7nd5p83EL5vb8qL/Eiu65stxwZ3QOT9pp2bsAPk6LJWWnVqJroKI
Pc6KWG7n6mMJPbhFmE8Ld2lcNlSKQwLHIxDocL2GAqh5S5hJeGg2oLMFpy0g2Ron
643w0zPfpKC15TlnkJDCIgtRlHovrs42Qkypz9y3e8yOL9O0RLYIKbuKw4mNR3cT
OdeV+TjfiZ5I
=2oe6
-----END PGP PUBLIC KEY BLOCK-----

109
Makefile
View File

@@ -1,109 +0,0 @@
# Copyright (C) 2010 DSD Author
# GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
CC = gcc
CFLAGS = -O2
INCLUDES = -I. -I/usr/local/include -I/usr/include
LIBS = -L/usr/local/lib -lm -lmbe
INSTALL=install
AR=ar
RANLIB=ranlib
DEST_BASE=/usr/local
DEST_INC=${DEST_BASE}/include
DEST_LIB=${DEST_BASE}/lib
DEST_BIN=${DEST_BASE}/bin
OBJS = dsd_main.o dsd_symbol.o dsd_dibit.o dsd_frame_sync.o dsd_file.o dsd_audio.o dsd_serial.o dsd_frame.o dsd_mbe.o dsd_upsample.o p25p1_hdu.o p25p1_ldu1.o p25p1_ldu2.o p25p1_tdulc.o p25_lcw.o x2tdma_voice.o x2tdma_data.o dstar.o nxdn96.o dmr_voice.o dmr_data.o provoice.o
all: dsd
build: all
dsd_main.o: dsd_main.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_main.c -o dsd_main.o
dsd_symbol.o: dsd_symbol.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_symbol.c -o dsd_symbol.o
dsd_dibit.o: dsd_dibit.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_dibit.c -o dsd_dibit.o
dsd_frame_sync.o: dsd_frame_sync.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_frame_sync.c -o dsd_frame_sync.o
dsd_file.o: dsd_file.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_file.c -o dsd_file.o
dsd_audio.o: dsd_audio.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_audio.c -o dsd_audio.o
dsd_serial.o: dsd_serial.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_serial.c -o dsd_serial.o
dsd_frame.o: dsd_frame.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_frame.c -o dsd_frame.o
dsd_mbe.o: dsd_mbe.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_mbe.c -o dsd_mbe.o
dsd_upsample.o: dsd_upsample.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_upsample.c -o dsd_upsample.o
p25p1_hdu.o: p25p1_hdu.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c p25p1_hdu.c -o p25p1_hdu.o
p25p1_ldu1.o: p25p1_ldu1.c p25p1_const.h dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c p25p1_ldu1.c -o p25p1_ldu1.o
p25p1_ldu2.o: p25p1_ldu2.c p25p1_const.h dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c p25p1_ldu2.c -o p25p1_ldu2.o
p25p1_tdulc.o: p25p1_tdulc.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c p25p1_tdulc.c -o p25p1_tdulc.o
p25_lcw.o: p25_lcw.c dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c p25_lcw.c -o p25_lcw.o
x2tdma_voice.o: x2tdma_voice.c x2tdma_const.h dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c x2tdma_voice.c -o x2tdma_voice.o
x2tdma_data.o: x2tdma_data.c x2tdma_const.h dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c x2tdma_data.c -o x2tdma_data.o
dstar.o: dstar.c dstar_const.h dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dstar.c -o dstar.o
nxdn96.o: nxdn96.c nxdn96_const.h dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c nxdn96.c -o nxdn96.o
dmr_voice.o: dmr_voice.c dmr_const.h dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dmr_voice.c -o dmr_voice.o
dmr_data.o: dmr_data.c dmr_const.h dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c dmr_data.c -o dmr_data.o
provoice.o: provoice.c provoice_const.h dsd.h config.h
$(CC) $(CFLAGS) $(INCLUDES) -c provoice.c -o provoice.o
dsd: $(OBJS) dsd.h config.h
$(CC) $(CFLAGS) $(LIBS) -o dsd $(OBJS)
clean:
rm -f *.o
rm -f dsd
install: dsd
$(INSTALL) dsd $(DEST_BIN)
chgrp uucp $(DEST_BIN)/dsd
chmod g+s $(DEST_BIN)/dsd

520
README
View File

@@ -1,520 +0,0 @@
Digital Speech Decoder 1.3
Copyright (C) 2010 DSD Author
GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
DSD is able to decode several digital voice formats from discriminator
tap audio and synthesize the decoded speech. Speech
synthesis requires mbelib, which is a separate package. DSD 1.3
requires mbelib 1.1 or later.
Supported formats in version 1.3:
P25 Phase 1 Widely deployed radio standard used in public safety
and amateur radio.
Support includes decoding and synthesis of speech,
display of all link control info, and the ability to save
and replay .imb data files
ProVoice EDACS Digital voice format used by public safety and
amateur radio.
Support includes decoding and synthesis of speech and
the ability to save and replay .imb data files.
Note: not enabled by default, use -fp to enable.
X2-TDMA Two slot TDMA system currently being deployed by several
public safety organizations. Based on the DMR
standard with extensions for P25 style signaling.
Support includes decoding and synthesis of speech,
display of all link control info, and the ability to save
and replay .amb data files
DMR/MOTOTRBO "Digital Mobile Radio" Eurpoean two slot TDMA standard.
MOTOTRBO is a popular implementation of this standard.
Support includes decoding and synthesis of speech and
the ability to save and replay .amb data files.
Development (no speech) support only:
NXDN (9600) Digital radio standard used by NEXEDGE and IDAS brands.
Development support only. DSD recognizes frames and can
extract the voice bits but speech is not yet decoded.
The issue is likely either an unusual interleave pattern
or a pseudo-random bit mask initialized by a "color code"
Note: not enabled by default, use -fn to enable.
D-STAR Amateur radio digital voice standard
Development support only. DSD recognized frames and can
extract the voice bits but speech is not yet decoded.
D-STAR likely uses a version of AMBE not yet supported by
mbelib. The voice bit interleave pattern also needs to be
determined.
Note: not enabled by default, use -fn to enable.
Unsupported formats in version 1.3 considered for future development:
P25 Phase 2 This is not yet a published standard. Full support is
expected once the standard is published and there are
systems operating to test against. Phase 2 will use
a vocoder supported by mbelib.
OpenSKY It is possible that the four slot version uses a vocoder
supported by mbelib. The two slot version does not.
NXDN (4800) This should be supportable as soon as we have .wav files
or access to a radio programmed in this format.
Supported demodulation optimizations in version 1.3:
C4FM Continuous envelope 2 or 4 level FSK with relatively
sharp transitions between symbols. Used by most P25
systems.
Optimizations include calibrating decision points only
during sync, 4/10 sample window per symbol, and symbol
edge timing calibration.
GFSK Continuous envelope 2 or 4 level FSK with a narrower
Gaussian/"raised cosine" filter that affects transitions
between symbols. Used by DMR/MOTOTRBO, NXDN and many
others. Noisy C4FM signals may be detected as GFSK
but this is ok, the optimization changes will help with
noisy signals.
Optimizations are similar to C4FM except symbol transitions
are only kept out of the middle 4 samples and only the
middle two samples are used.
QPSK Quadrature Phase Shift Keying (and variants) used in
some P25 systems and all known X2-TDMA systems. May be
advertised under the marketing term "LSM"
Optimizations include continuous decision point
calibration, using middle two samples, and using the
symbol midpoint "spike" for symbol timing.
Installation
DSD should easily compile on any Linux or *BSD system with gcc.
Just untar and run "make" or "make install". There are some debugging/
development options in config.h that normal users will want to leave
disabled as they can severely impact performance.
Operation
There are two main operating modes, "Live scanner" and "Play files"
Usage: dsd [options] Live scanner mode
Live Scanner mode takes 48KHz/16 bit mono audio samples from a
sound card input and decodes speech in real time. Options are provided
for controling information display and saving mbe data files.
The synthesized speech can be output to a soundcard and/or a
.wav file.
Usage: dsd [options] -r <files> Read/Play saved mbe data from file(s)
Play files mode reads mbe data from files specified on the command
line (including wildcards) and synthesizes speech from those files.
The synthesized speech can be output to a soundcard and/or a
.wav file. The -r command line options is used to activate Play files
mode.
Display modes
There are two main display modes in Live scanner mode. "Errorbars"
and "Datascope".
Errorbars mode output for P25 Phase 1 looks like this:
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 0 tg: 32464 TDULC
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 0 tg: 32464 TDULC
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 0 tg: 32464 TDULC
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 0 tg: 32464 TDULC
Sync: -p25p1 mod: C4FM input: 38% nac: 5C2 src: 0 tg: 32464 TDU
Sync: -p25p1 mod: C4FM input: 38% nac: 5C2 src: 0 tg: 32464 HDU
Sync: -p25p1 mod: C4FM input: 42% nac: 5C2 src: 0 tg: 32464 LDU1 e:
Sync: (-p25p1) mod: C4FM input: 39% nac: 5C2 src: 52610 tg: 32464 (LDU2) e:
Sync: -p25p1 mod: C4FM input: 38% nac: 5C2 src: 52610 tg: 32464 LDU1 e:
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 52610 tg: 32464 LDU2 e:
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 52610 tg: 32464 LDU1 e:
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 52610 tg: 32464 LDU2 e:
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 52610 tg: 32464 LDU1 e:
"Sync" indicates the frame type detected and whether the polarity is
positive or negative. DSD automatically detects and handles either
polarity except for DMR/MOTOTRBO/X2-TDMA which unfortunatley use both
sync polarities.
Most combinations of transmitter, receiver and soundcard show netagive
(-) polarity for X2-TDMA signals and (+) polarity for DMR/MOTOTRBO so
those are the defaults.
********
You may need to use the -x option to select non-inverted polarity if
you are not getting usable X2-TDMA/MOTOTRBO/DMR speech. As they use both
normal and inverted sync it is not possible to detect polariy
automatically.
********
"mod" indicates the current demodulation optimizations.
"input" indicates the audio input level. QPSK signals tend to appear
much "wider" than C4FM from a discriminator tap so it is important
to set your input gain using a QPSK signal if you plan to montir them.
It is not necessary nor desirable to get to 100%, in fact your sound
card may max out below 100%. It is best to use the Datascope mode for
setting input gain (see below). Typical values with good results are
40% for C4FM and 66% for QPSK.
"nac" is the P25 Phase 1 Network Access Code. This is a 12 bit field
in each P25 Phase 1 header. It should not be confused with the 16
bit System ID used in non-p25 trunking control channels.
"src" is the radio id of the trasmitting subscriber unit.
"tg" is the talkgroup derived from link control information.
"HDU/LDU1/LDU2/TDU/TDULC" are P25 Phase 1 frame types, referred to as
frame subtype within DSD.
"e:" is the beginning of the errorbars display. Each "=" indicates a
detected error within the voice data. "R" and "M" indicat that a voice
frame was repeated or muted due to excessive errors.
Values in parentheses () indicate an assumption (soft decision) was
made based on the previous frame.
Errorbars mode output for X2-TDMA looks like this:
Sync: -X2-TDMA mod: QPSK input: 59% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
Sync: -X2-TDMA mod: QPSK input: 47% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
Sync: -X2-TDMA mod: QPSK input: 43% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
Sync: (-X2-TDMA) mod: QPSK input: 28% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
DMR/MOTOTRBO display is similar except it does not yet show source
and talkgroup information.
As of version 1.2 DSD shows which specific TDMA slots are active (with
capital SLOT letters) and which slot is currently being monitored (with
square brackets []. Noisy/degraded signals will affect the accuracy
of this display.
The frame subtypes (Voice/LC etc) are shown based on the DMR standard
types.
Datascope mode output looks like this:
Demod mode: C4FM Nac: 8C3
Frame Type: P25 Phase 1 Talkgroup: 16528
Frame Subtype: LDU1 Source: 0
TDMA activity: slot0 slot1 Voice errors:
+----------------------------------------------------------------+
| # ^ !| ^ # |
| * | * |
| * | * |
| * | * * |
| * * | * * |
| * * | ** * |
| * ** | ** * |
| ** ** | ** * |
| ** ** | ** * |
| ** ** | ** * |
+----------------------------------------------------------------+
C4FM Example
Demod mode: C4FM Nac: 126
Frame Type: P25 Phase 1 Talkgroup: 25283
Frame Subtype: LDU2 Source: 0
TDMA activity: slot0 slot1 Voice errors:
+----------------------------------------------------------------+
| # ^ ! ^ # |
| * | |
| * | |
| ** | |
| ** | * |
| * ** | * * |
| ** ** | * * |
| *** ** | ** * |
| *** ** | *** * |
| *** **** | **** * * |
+----------------------------------------------------------------+
QPSK Example
At the top is various information about the signal, similar to the
information provided in Errorbars mode. The large box is similar to
a spectrum analyzer viewing the channel bandwidth.
The horizontal axis is the input audio level, minimum on the left and
maximum on the right. The vertical axis is the number of samples
seend at each audio level.
The "*" symbols represent the number of audio
samples that were at each level during the aggregation period.
(default = 36 symbols) The -S options controls the aggregation period
as well as the QPSK tracking symbol buffer, so changing that will affect
QPSK performance as well as the Datascope display.
As you can see from the figures above, clean C4FM signals tend to have
four very sharply defined audio levels. The datascope pattern also
tends to be faily stable with minor shifts left and right as the
receiver tries to frequency track any DC offset.
QPSK signals on the other hand tend to appear much broader (and artifact
of how they are distored by FM PLL discriminators). They also tend
to vary wildly in width and centering. This is especially true when
monitoring simulcast systems. Muliple QPSK signals interfere much more
dramatically with an FM discriminator than C4FM signals.
*******
For this reason it is important to isolate your receiver to one
transmitter tower, _especially_ for QPSK signals.
*******
The "#" symbols indicate the detected min/max values that are used
to calibrate the symbol decision points. These are indicated by
"!" for the center decision point and "^" for the mid decision points.
Display Options
There are several options to control the type and quantity of
information displayed in Errorbars mode:
-e Show Frame Info and errorbars (default)
-pe Show p25 encryption sync bits
-pl Show p25 link control bits
-ps Show p25 status bits and low speed data
-pt Show p25 talkgroup info
-q Don't show Frame Info/errorbars
-s Datascope (disables other display options)
-t Show symbol timing during sync
-v <num> Frame information Verbosity
-z <num> Frame rate for datascope
Most of these options are self explanitory. Symbol timing is a noisy
option that allows you to view the quality of the frame sync samples
and accuracy of the symbol timing adjustments.
Symbol Timing display looks like this:
Symbol Timing:
----------
----------
----------
----------
----------
-+++++++++ 1
+---------- 0
----------
++++++++++ 0
++++++++++
---------- 0
----------
++++++++++ 0
++++++++++
++++++++++
++++++++++
---------- 0
++++++++++ 0
---------- 0
++++++++++ 0
++++++++++
++++++++++
++++++++++
++++++++++
C4FM example
Symbol Timing:
+---------
----------
----------
----------
-----X---- 5
--+++O++++- 4
----------
----X----- 4
++++O++--- 4
--++O++++- 4
----X----- 4
----------
++++O+++-- 4
-+++O+++-- 4
--++O+++-- 4
--++O+++-- 4
----------
++++O++++- 4
----------
++++O+++-- 4
-+++O++++- 4
-+++O+++++ 4
-+++O++--- 4
--++O+++-- 4
QPSK example
Symbol timing is only displayed for symbols during the frame sync
period. Each horizontal line represents the 10 audio samples for each
symbol. "-" indicates an audio sample below the center reference level
and "+" represents a sample above center. "X" indicates a low spike
below a reference threshold (reference minimum for C4FM and 80%
of reference minimum for QPSK). "O" represents a high spike above
the high reference threshold. The numbers to the right indicate which
sample position the targeted transition occurred (+/- for C4FM or
spike high/low for QPSK). The number of audio samples for the next
symbol are adjusted to get this value closer to the target (0 for
C4FM and 4 for QPSK). This shows how DSD maintains accurate symbol
timing. Symbol timing adjustments are only made during sync, which
is the only time reliable transitions can be observed.
In both examples above the symbol timing was off by one sample at
the beginning of the frame sync period and was adjusted. Generally
if you see any spike values "X/O" in C4FM mode, or lots of them in
QPSK mode it indicates noise on the input signal.
Input/Output Options
-i <device> Audio input device (default is /dev/audio)
-o <device> Audio output device (default is /dev/audio)
-d <dir> Create mbe data files, use this directory
-g <num> Audio output gain (default = 0 = auto)
-n Do not send synthesized speech to audio output device
-w <file> Output synthesized speech to a .wav file
The audio in device can be a sound card OR a .wav file if the file
is in the exact format 48k/16bits/mono/pcm. Audio in should be an
unfilterd discriminator tap signal.
The audio out device should be a sound card (use the -w options to
output to a .wav file).
If the audio in device is the same as the audio out device, the
synthesized speech has to be upsampled to the 48k sample rate required
for input. A fast upsample function is provided but still leaves some
artifacts.
********
The best sound and minimum cpu usage is achieved with separate sound
cards for input and output
********
if you specify different input/output devices DSD will use 8k as the
output sample rate and the lack of resampling results in much better
audio as well as lowe cpu consumption.
If you are using onboard "AC97" sound device you may find that DSD uses
much more cpu than expected, in some cases more than is available.
This is because many AC97 sound devices are designed to rely on CPU
processing power instead of hardware. You may also find that 8k sample
rate output is upsampled in the driver using a very basic algorithim
resulting in severe distortion. The solution is to use a real hardware
sound device (pci card, usb device etc).
As of version 1.2 DSD now automatically levels the output audio. This
greately improves readability and eliminates the painful effects of
noise bursts. You can specify a fixed audio output gain with the -g
option.
Scanner control options:
-B <num> Serial port baud rate (default=115200)
-C <device> Serial port for scanner control (default=/dev/ttyUSB0)
-R <num> Resume scan after <num> TDULC frames or any PDU
On some P25 systems Packet Data Units (PDU) are sent on the same
frequencies used for voice traffic. If done constantly this can
be a severe hinderance to scanning the system in conventional
mode. The -R option enables sending a "resume scan" command to
a scanner connected to a serial port. Use -B and -C to set the baud
rate and serial port device if necessary.
Decoder options
-fa Auto-detect frame type (default)
-f1 Decode only P25 Phase 1
-fd Decode only D-STAR* (no audio)
-fn Decode only NXDN96* (no audio)
-fp Decode only ProVoice*
-fr Decode only DMR/MOTOTRBO
-fx Decode only X2-TDMA
-ma Auto-select modulation optimizations (default)
-mc Use only C4FM modulation optimizations
-mg Use only GFSK modulation optimizations
-mq Use only QPSK modulation optimizations
-u <num> Unvoiced speech quality (default=3)
-xx Expect non-inverted X2-TDMA signal
-xr Expect inverted DMR/MOTOTRBO signal
NXDN and D-STAR are not enabled by default as they are for development
testing only and have shorter frame sync patterns that are prone to
false triggering.
MBE speech synthesis is broken down into two main types of sounds,
"Voiced" and "Unvoiced". Voiced speech bands are synthesized with
a single sine wave centered in the frequency band with the appropriate
phase and amplitude.
Unvoiced speech is supposed to be generated with a noise source, 256
point DFT a number of band filters, followed by a 256 point inverse DFT.
For computational simplicity mbelib uses a different method. For each
unvoiced speech band, a number of sine waves are generated, each with a
different random initial phase. The number of waves used per band is
controlled by the -u option. A setting of 4 would approximate the
performance of the 256 point DFT method as the maximum number of voice
bands is 56, and very low frequencies are not synthesized. Values less
than 3 have a noticable lack of unvoiced speech and/or artifacts. The
defualt of 3 provides good speech quality with reasonable cpu use.
Increasing the quality above the default rapidly consumes more CPU for
increasingly diminishing returns.
Advanced decoder options
-A <num> QPSK modulation auto detection threshold (default=26)
-S <num> Symbol buffer size for QPSK decision point tracking
(default=36)
-M <num> Min/Max buffer size for QPSK decision point tracking
(default=15)
Encryption
********
Decryption of speech is NOT supported, even if you lawfully posess the
encryption keys. Decryption support will not be added in the future as
the authors wish to steer as far away from the legal issues associated
with encryption as possible.
********
We realize that there are many legitemate and lawful uses of decryption
software including system/interoperability testing and lawful monitoring.
This software is distributed under a liberal BSD license so there is
nothing to stop others from supplying patches, forking this project or
incorporating it into a commercial product and adding decryption support.
There is support for displaying the encryption sync bits transmitted in
the clear on P25 Phase 1 systems. These bits do not allow for the
decryption of signals without the secret encryption keys. The
encryption sync bits are useful for determining whether a signal is
encrypted vs merely noisy or degraded. As the encryption sync bits
typically include long strings of zeros when a transmission is not
encrypted they can also be used to visually estimate bit error rates.

264
README.md Normal file
View File

@@ -0,0 +1,264 @@
# Digital Speech Decoder - Florida Man Edition
This version of DSD is a flavor blend of [szechyjs](https://github.com/szechyjs/dsd "szechyjs") RTL branch and some of my own additions, along with portions of DMR and NXDN code from the [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve") branch. This code also borrows snippets, inspiration, and ideas from other open source works including [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25"), [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"). 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 and hrh17 for the many hours of wav samples submitted by them.
## 2022.10.01 Update ##
Very Experimental (Mostly Just Sampling Speed and using built in QPSK demodulator) for Phase 2 CQPSK 6000 sps input over disc tap inputs (SDR++, GQRX, WAV files). Promising results, but some errors in decoding as expected. I've had some pretty good luck using SDR++ to listen to and decode Duke Energy TDMA Control Channel through the virtual sink. Experimental CQPSK can be toggled in Ncurses Menu and also via command line switch `-f2 -m2` but keep in mind this will only work with Phase 2 RF channels using CQPSK, and not a combined decoding.
Also, added some new iden_up PDUs and other odd and end P25 PDU's (still far from complete). Ncurses Terminal and Console output will now show relevant frequencies for CC and Voice channels if iden_up commands are present.
DSD-FME, and The Florida Man Labs will be taking a hiatus period from straigth development of this project. Currently, I feel we are at a bit of a stand still as far as what can be done with the project without new demodulation, dibit buffer, audio, and input methods. DSD-FME will continue to receive maintenance updates for simple bug fixes and also any major bug issues that cause crashing, etc., but I am reluctant to continue developing with the current underlying issues mentioned above. I am going to go into a period of hiatus with this project and do lots of experimentation, learning, and testing to see what viable options are out there or need to be created from scratch. I feel like I've learned a lot, achieved a lot, made tons of mistakes, learned from a few of them, but ultimately improved on the DSD project. DSD-FME will continue to receive replies and support in any opened issues but will currently only extend to help in setting up and using the software, on top of regular minor maintenance and any crash related issues.
## 2022.09.28 Mini Update ##
Back by popular demand, I've re-implemented DMR Stereo as a standalone decoder. DMR Stereo is also still available in XDMA decoding for P25 1, 2, and DMR BS/MS Simplex as well.
DMR Stereo `dsd-fme -fs`
XDMA `dsd-fme -ft`
It can also be selected in the NCurses Terminal Menu. Sadly I had to remove the X2-TDMA decoder option in the menu, I'm sure nobody will miss it.
## 2022.09.17 Update ##
P25 Phase 2 Audio decoding has been implemented into DSD-FME. Currently, Phase 2 will only work from OP25 symbol capture bin files, or from wav files/SDR applications/RTL input that are sourced from FSK4 sources. CQPSK (LSM/H-D8PSK) will not work from audio sources. With the addition of Phases 2 audio, a new default decoder class has been implemented, XDMA, which is P25 Phase 1, Phase 2, DMR BS and MS (DMR Stereo). Furthermore, very limited Phase 1 TSBK support and Phase 2 FACCH/SACCH/LCCH has been worked in just to get Network Status Broadcasts, which can fill in the required P2 parameters for WACN, SYSID, and CC for Phase 2 frame de-scrambling, and active voice call information.
With the implementation of the XDMA decoder class (default decoder), the CLI switches for DMR Stereo has been repurposed. -T option will now open a per call wav file saving when used with NCurses Terminal, otherwise it will write wav files to slot 1 and slot 2 wav files.
Anybody using `dsd-fme -fr -T` ...... should now just use `dsd-fme -ft` instead!
To see the up to date CLI options, please look at the help options with the -h CLI option.
## 2022.08.12 Update ##
A new menu system has been introduced in the NCURSES Terminal. Also includes support for LRRP to text file type of choice and reading OP25 symbol capture bin files.
[![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/pulseaudio/dsd-fme.png)](https://www.youtube.com/watch?v=TqTfAfaPJ4s "DSD-FME Update 2022.08.12")
To get started with the new menu system, simply launch with:
`dsd-fme -N 2> log.txt`
## 2022.06.20 Update - Current Users Please Read!! ##
The executable for this project has been changed from `dsd` to `dsd-fme` so after pulling or cloning the lastest version, make sure to call the software with `dsd-fme`.
## 2022.05.05 Update ##
DMR Stereo method added for listening to voice audio in both TDMA channels/slots simultaneously.
### Example Usage and Note!
`dsd-fme` or `./dsd-fme` may need to be used in your usage, depending on if you are running it from the build folder, or if you have run make install. You will need to tweak the examples to your particular usage. Users can always check `dsd-fme -h` for help and all command line switches to use.
`dsd-fme` is all you need to run for pulse input, pulse output, and auto detect for DMR BS/MS, P25 (1 and 2) and X2-TDMA decoding. To use other decoding methods which cannot be auto detected, please use the following command line switches. Make sure to route audio into and out of DSD-FME using pavucontrol and virtual sinks as needed.
```
-ft XDMA decoder class (P25 1 and 2, DMR BS/MS, X2-TDMA)
-fs DMR Stereo BS/MS Simplex
-fa Legacy Auto
-fi NXDN48
-fn NXDN96
-fp ProVoice
-fm dPMR, also may need to use -xd if inverted dPMR.
-fr DMR, also may need to use -xr if inverted DMR.
-f1 P25P1
-fx X2-TDMA
```
## Example Usage - New XDMA Decoder, Ncurses Terminal, Pulse Input/Output, and Log Console to file
`dsd-fme -ft -N 2> voice.log`
and in a second terminal tab, same folder, run
`tail -n 40 -f voice.log`
Then you can open up your pavucontrol "Pulse Audio Volume Control" or "Volume Control" application and route input into DSD-FME from any application and DSD-FME output to the left and right speakers respectively. (unlock the channel in the application stream and adjust left and right independently)
## Example Usage - RTL2832 Dongle Input
`dsd-fme -fi -i rtl -c 154.9875M -P -2 -D 1 -G 36 -L 70 -U 6021 -Y 12`
```
-i rtl to use rtl_fm (default is -i pulse for pulse audio)
-o pulse to set output to pulse audio (excluded, is default anyways)
-c Set frequency
-P set PPM error (default = 0)
-D set device index number (default = 0; first device detected)
-G set device gain (0-49) (default = 0 Auto Gain)
-L set rtl squelch to 70 (default = 0; off)
-U set UDP port for rtl_fm remote control (default = 6020)
-Y 12 set rtl VFO bandwidth in kHz, (default = 12)(6, 8, 12, 16, 24, 48)
```
## Example STDIN UDP from GQRX or SDR++, output to Pulse Audio, and append decoded audio to wav file
Be sure to first start UDP output sink in GQRX or SDR++ and set VFO appropriately
`socat stdio udp-listen:7355 | dsd-fme -fi -i - -w nxdn.wav`
## Example Usage - Extra Information for Academic Study and Logging
`dsd-fme -Z -N 2>> voice.log`
and in a second terminal tab, same folder, run
`tail -n 40 -f voice.log`
![DSD-FME](https://github.com/lwvmobile/dsd-fme/blob/pulseaudio/dsd-fme2.png)
## Roadmap
The Current list of objectives include:
1. New demodulators and input method(s) (considering liquid-dsp).
2. Graphical User Interface (may need to migrate to new project).
3. Lots of experimentation.
## How to clone, check out, and build this branch
### Ubuntu 22.04/20.04/LM20/Debian Bullseye or Newer:
Using the included download-and-install.sh should make for a simple and painless clone, build, and install on newer Debian/Ubuntu/Mint/Pi systems. Simply acquire or copy the script, and run it. Update: Ubuntu 22.04 and RPi Bullseye 64-bit has been tested working with the installer script and functions appropriately.
If you need all dependencies build and installed first (only on Debian/Ubuntu/Mint/Pi), run:
```
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/pulseaudio/download-and-install.sh
chmod +x download-and-install.sh
./download-and-install.sh
```
If you have dependencies already installed (i.e. need a fresh clean install on a system with DSD-FME already or using system other than Debian/Ubuntu, etc), please run this instead:
```
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/pulseaudio/download-and-install-nodeps.sh
chmod +x download-and-install-nodeps.sh
./download-and-install-nodeps.sh
```
### Ubuntu 18.04/LM19/Buster Note:
The above install.sh should now function on older system types. You can elect to manually follow the steps down below. Do NOT Manually build and install ITPP 4.3.1 on older systems, it is currently not wanting to build on Ubuntu 18.04 and Linux Mint 19. Install it from the repository instead.
## Manual Install
First, install dependency packages. This guide will assume you are using Debian/Ubuntu based distros. Check your package manager for equivalent packages if different.
```
sudo apt update
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev
```
## Headless
If running headless, swap out pavucontrol for pulsemixer, and also install pulseaudio as well. Attempting to install pavucontrol in a headless environment may attempt to install a minimal desktop environment. Note: Default behavior of pulseaudio in a headless environment may be to be muted, so check by opening pulsemixer and unmuting and routing audio appropriately.
```
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev pulsemixer pulseaudio
```
### Build and Install ITPP - ONLY IF NOT IN REPO!!
```
wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
tar xjf itpp*
#if you can't cd into this folder, double check folder name first
cd itpp-4.3.1
mkdir build
cd build
cmake ..
make -j `nproc`
sudo make install
sudo ldconfig
cd ..
cd ..
```
MBELib is considered a requirement in this build. You must read this notice prior to continuing. [MBElib Patent Notice](https://github.com/lwvmobile/mbelib#readme "MBElib Patent Notice") This version of MBELib is 1.3.1 and prints to STDERR, using the stock 1.3.0 MBElib may cause problems with print alignments if paired with this version of DSD-FME, or cause issues with printing over the screen in future builds.
```
git clone https://github.com/lwvmobile/mbelib
cd mbelib
mkdir build
cd build
cmake ..
make -j `nproc`
sudo make install
sudo ldconfig
cd ..
cd ..
```
Finish by running these steps to clone and build DSD-FME w/ pulseaudio support.
```
git clone https://github.com/lwvmobile/dsd-fme
cd dsd-fme
git branch -a
git checkout remotes/origin/pulseaudio
git checkout -b pulseaudio
git branch -a #double check to see if you are on pulseaudio branch
sudo cp tone8.wav /usr/share/
sudo cp tone24.wav /usr/share/
sudo chmod 777 /usr/share/tone8.wav
sudo chmod 777 /usr/share/tone24.wav
mkdir build
cd build
cmake ..
make -j `nproc`
##only run make install if you don't have another version already installed##
sudo make install
sudo ldconfig
```
Optional 'Virtual Sinks' for routing audio from SDR++ or GQRX, Media Players, etc. into DSD-FME
You may wish to direct sound into DSD-FME via Virtual Sinks. You may set up a Virtual Sink or two on your machine for routing audio in and out of applications to other applications using the following command, and opening up pavucontrol "PulseAudio Volume Control" in the menu (or `pulsemixer` in headless mode) to change line out of application to virtual sink, and line in of DSD-FME to monitor of virtual sink. This command will not persist past a reboot, so you will need to invoke them each time you reboot, or search for how to add this to your conf files for persistency if desired.
```
pacmd load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink
pacmd load-module module-null-sink sink_name=virtual_sink2 sink_properties=device.description=Virtual_Sink2
```
Already have this branch, and just want to pull the latest build? You can run the rebuild.sh file in the dsd-fme folder, or manually do the pull with the commands:
```
##Open your clone folder##
git pull https://github.com/lwvmobile/dsd-fme pulseaudio
##cd into your build folder##
cd build
cmake ..
make -j `nproc`
sudo make install
sudo ldconfig
```
If the call alert wav files aren't playing, then make sure to run the following in the dsd-fme folder to copy the wav files to the /usr/share/ folder and give them adequate permission to be accessed.
```
sudo cp tone8.wav /usr/share/
sudo cp tone24.wav /usr/share/
sudo chmod 777 /usr/share/tone8.wav
sudo chmod 777 /usr/share/tone24.wav
```
# Join the Conversation
Want to help drive the direction of this project and read more about the latest updates and releases to DSD-FME? Then join the conversation on the 'unofficial official' [DSD-FME](https://forums.radioreference.com/threads/dsd-fme.438137/ "DSD-FME") Forum Thread on the Radio Reference Forums.
## License
Copyright (C) 2010 DSD Author
GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

15
cmake/FindITPP.cmake Normal file
View File

@@ -0,0 +1,15 @@
# - Try to find ITPP
# Once done this will define
#
# ITPP_FOUND - System has ITPP
# ITPP_INCLUDE_DIR - The ITPP include directory
# ITPP_LIBRARY - The library needed to use ITPP
#
find_path(ITPP_INCLUDE_DIR itpp/itcomm.h)
set(ITPP_NAMES ${ITPP_NAMES} itpp libitpp libitpp.dll)
FIND_LIBRARY(ITPP_LIBRARY NAMES ${ITPP_NAMES})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ITPP DEFAULT_MSG ITPP_LIBRARY ITPP_INCLUDE_DIR)

View File

@@ -0,0 +1,16 @@
# - Try to find Libsndfile
# Once done this will define
#
# LIBSNDFILE_FOUND - System has LIBSNDFILE
# LIBSNDFILE_INCLUDE_DIR - The SNDFILE include directory
# LIBSNDFILE_LIBRARY - The library needed to use SNDFILE
#
find_path(LIBSNDFILE_INCLUDE_DIR sndfile.h)
SET(LIBSNDFILE_NAMES ${LIBSNDFILE_NAMES} sndfile libsndfile)
FIND_LIBRARY(LIBSNDFILE_LIBRARY NAMES ${LIBSNDFILE_NAMES})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LibSndFile DEFAULT_MSG LIBSNDFILE_LIBRARY
LIBSNDFILE_INCLUDE_DIR)

15
cmake/FindMBE.cmake Normal file
View File

@@ -0,0 +1,15 @@
# - Try to find MBE
# Once done this will define
#
# MBE_FOUND - System has MBE
# MBE_INCLUDE_DIR - The MBE include directory
# MBE_LIBRARY - The library needed to use MBE
#
find_path(MBE_INCLUDE_DIR mbelib.h)
set(MBE_NAMES ${MBE_NAMES} mbe libmbe)
find_library(MBE_LIBRARY NAMES ${MBE_NAMES})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MBE DEFAULT_MSG MBE_LIBRARY MBE_INCLUDE_DIR)

View File

@@ -0,0 +1,68 @@
#.rst:
# FindPulseAudio
# --------------
# Finds the PulseAudio library
#
# This will define the following variables::
#
# PULSEAUDIO_FOUND - system has the PulseAudio library
# PULSEAUDIO_INCLUDE_DIRS - the PulseAudio include directory
# PULSEAUDIO_LIBRARIES - the libraries needed to use PulseAudio
# PULSEAUDIO_DEFINITIONS - the definitions needed to use PulseAudio
#
# and the following imported targets::
#
# PulseAudio::PulseAudio - The PulseAudio library
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_PULSEAUDIO libpulse>=11.0.0 QUIET)
pkg_check_modules(PC_PULSEAUDIO_MAINLOOP libpulse-mainloop-glib>=11.0.0 QUIET)
pkg_check_modules(PC_PULSEAUDIO_SIMPLE libpulse-simple>=11.0.0 QUIET)
endif()
find_path(PULSEAUDIO_INCLUDE_DIR NAMES pulse/pulseaudio.h pulse/simple.h
PATHS ${PC_PULSEAUDIO_INCLUDEDIR} ${PC_PULSEAUDIO_INCLUDE_DIRS})
find_library(PULSEAUDIO_LIBRARY NAMES pulse libpulse
PATHS ${PC_PULSEAUDIO_LIBDIR} ${PC_PULSEAUDIO_LIBRARY_DIRS})
find_library(PULSEAUDIO_SIMPLE_LIBRARY NAMES pulse-simple libpulse-simple
PATHS ${PC_PULSEAUDIO_LIBDIR} ${PC_PULSEAUDIO_LIBRARY_DIRS})
find_library(PULSEAUDIO_MAINLOOP_LIBRARY NAMES pulse-mainloop pulse-mainloop-glib libpulse-mainloop-glib
PATHS ${PC_PULSEAUDIO_LIBDIR} ${PC_PULSEAUDIO_LIBRARY_DIRS})
if(PC_PULSEAUDIO_VERSION)
set(PULSEAUDIO_VERSION_STRING ${PC_PULSEAUDIO_VERSION})
elseif(PULSEAUDIO_INCLUDE_DIR AND EXISTS "${PULSEAUDIO_INCLUDE_DIR}/pulse/version.h")
file(STRINGS "${PULSEAUDIO_INCLUDE_DIR}/pulse/version.h" pulseaudio_version_str REGEX "^#define[\t ]+pa_get_headers_version\\(\\)[\t ]+\\(\".*\"\\).*")
string(REGEX REPLACE "^#define[\t ]+pa_get_headers_version\\(\\)[\t ]+\\(\"([^\"]+)\"\\).*" "\\1" PULSEAUDIO_VERSION_STRING "${pulseaudio_version_str}")
unset(pulseaudio_version_str)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PulseAudio
REQUIRED_VARS PULSEAUDIO_LIBRARY PULSEAUDIO_MAINLOOP_LIBRARY PULSEAUDIO_SIMPLE_LIBRARY PULSEAUDIO_INCLUDE_DIR
VERSION_VAR PULSEAUDIO_VERSION_STRING)
if(PULSEAUDIO_FOUND)
set(PULSEAUDIO_INCLUDE_DIRS ${PULSEAUDIO_INCLUDE_DIR})
set(PULSEAUDIO_LIBRARIES ${PULSEAUDIO_LIBRARY} ${PULSEAUDIO_MAINLOOP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY})
set(PULSEAUDIO_DEFINITIONS -DHAS_PULSEAUDIO=1)
if(NOT TARGET PulseAudio::PulseAudioMainloop)
add_library(PulseAudio::PulseAudioMainloop UNKNOWN IMPORTED)
set_target_properties(PulseAudio::PulseAudioMainloop PROPERTIES
IMPORTED_LOCATION "${PULSEAUDIO_MAINLOOP_LIBRARY}")
endif()
if(NOT TARGET PulseAudio::PulseAudio)
add_library(PulseAudio::PulseAudio UNKNOWN IMPORTED)
set_target_properties(PulseAudio::PulseAudio PROPERTIES
IMPORTED_LOCATION "${PULSEAUDIO_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${PULSEAUDIO_INCLUDE_DIR}"
INTERFACE_COMPILE_DEFINITIONS HAVE_LIBPULSE=1
INTERFACE_LINK_LIBRARIES PulseAudio::PulseAudioMainloop)
endif()
endif()
mark_as_advanced(PULSEAUDIO_INCLUDE_DIR PULSEAUDIO_LIBRARY PULSEAUDIO_MAINLOOP_LIBRARY PULSEAUDIO_SIMPLE_LIBRARY)

29
cmake/FindRTLSDR.cmake Normal file
View File

@@ -0,0 +1,29 @@
# - Try to find rtlsdr - the hardware driver for the realtek chip in the dvb receivers
# Once done this will define
# RTLSDR_FOUND - System has rtlsdr
# RTLSDR_LIBRARIES - The rtlsdr libraries
# RTLSDR_INCLUDE_DIRS - The rtlsdr include directories
# RTLSDR_LIB_DIRS - The rtlsdr library directories
if (NOT RTLSDR_FOUND)
find_package(PkgConfig)
pkg_check_modules(RTLSDR_PKG librtlsdr)
set(RTLSDR_DEFINITIONS ${PC_RTLSDR_CFLAGS_OTHER})
find_path(RTLSDR_INCLUDE_DIR
NAMES rtl-sdr.h
HINTS ${RTLSDR_PKG_INCLUDE_DIRS})
find_library(RTLSDR_LIBRARY
NAMES rtlsdr
HINTS ${RTLSDR_PKG_LIBRARY_DIRS})
set(RTLSDR_LIBRARIES ${RTLSDR_LIBRARY})
set(RTLSDR_INCLUDE_DIRS ${RTLSDR_INCLUDE_DIR})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(RTLSDR DEFAULT_MSG RTLSDR_LIBRARY RTLSDR_INCLUDE_DIR)
mark_as_advanced(RTLSDR_INCLUDE_DIR RTLSDR_LIBRARY)
endif (NOT RTLSDR_FOUND)

View File

@@ -0,0 +1,23 @@
if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
cmake_policy(SET CMP0007 OLD)
list(REVERSE files)
foreach (file ${files})
message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
if (EXISTS "$ENV{DESTDIR}${file}")
execute_process(
COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}"
OUTPUT_VARIABLE rm_out
RESULT_VARIABLE rm_retval
)
if(NOT ${rm_retval} EQUAL 0)
message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
endif (NOT ${rm_retval} EQUAL 0)
else (EXISTS "$ENV{DESTDIR}${file}")
message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
endif (EXISTS "$ENV{DESTDIR}${file}")
endforeach(file)

130
cmake/git_revision.cmake Normal file
View File

@@ -0,0 +1,130 @@
# - Returns a version string from Git
#
# These functions force a re-configure on each git commit so that you can
# trust the values of the variables in your build system.
#
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
#
# Returns the refspec and sha hash of the current head revision
#
# git_describe(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the source tree, and adjusting
# the output so that it tests false if an error occurs.
#
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe --exact-match on the source tree,
# and adjusting the output so that it tests false if there was no exact
# matching tag.
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if(__get_git_revision_description)
return()
endif()
set(__get_git_revision_description YES)
# We must run the following at "include" time, not at function call time,
# to find the path to this module rather than the path to a calling list file
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
function(get_git_head_revision _refspecvar _hashvar)
set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
# We have reached the root directory, we are not in git
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
return()
endif()
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
endwhile()
# check if this is a submodule
if(NOT IS_DIRECTORY ${GIT_DIR})
file(READ ${GIT_DIR} submodule)
string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
endif()
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
if(NOT EXISTS "${GIT_DATA}")
file(MAKE_DIRECTORY "${GIT_DATA}")
endif()
if(NOT EXISTS "${GIT_DIR}/HEAD")
return()
endif()
set(HEAD_FILE "${GIT_DATA}/HEAD")
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
configure_file("cmake/git_revision.cmake.in"
"${GIT_DATA}/grabRef.cmake"
@ONLY)
include("${GIT_DATA}/grabRef.cmake")
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
endfunction()
function(git_describe _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
return()
endif()
# TODO sanitize
#if((${ARGN}" MATCHES "&&") OR
# (ARGN MATCHES "||") OR
# (ARGN MATCHES "\\;"))
# message("Please report the following error to the project!")
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
#endif()
#message(STATUS "Arguments to execute_process: ${ARGN}")
execute_process(COMMAND
"${GIT_EXECUTABLE}"
describe
${hash}
${ARGN}
WORKING_DIRECTORY
"${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var} "${out}" PARENT_SCOPE)
endfunction()
function(git_get_exact_tag _var)
git_describe(out --exact-match ${ARGN})
set(${_var} "${out}" PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,38 @@
#
# Internal file for GetGitRevisionDescription.cmake
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
set(HEAD_HASH)
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
if(HEAD_CONTENTS MATCHES "ref")
# named branch
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}")
configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
set(HEAD_HASH "${HEAD_REF}")
endif()
else()
# detached HEAD
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
endif()
if(NOT HEAD_HASH)
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
endif()

3
configure vendored
View File

@@ -1,3 +0,0 @@
#!/bin/sh
echo "There is no configure script, just run make"

View File

@@ -1,251 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
void
processDMRdata (dsd_opts * opts, dsd_state * state)
{
int i, dibit;
int *dibit_p;
char sync[25];
char syncdata[25];
char cachdata[13];
char cc[5];
char bursttype[5];
#ifdef DMR_DUMP
int k;
char syncbits[49];
char cachbits[25];
#endif
cc[4] = 0;
bursttype[4] = 0;
dibit_p = state->dibit_buf_p - 90;
// CACH
for (i = 0; i < 12; i++)
{
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
cachdata[i] = dibit;
if (i == 2)
{
state->currentslot = (1 & (dibit >> 1)); // bit 1
if (state->currentslot == 0)
{
state->slot0light[0] = '[';
state->slot0light[6] = ']';
state->slot1light[0] = ' ';
state->slot1light[6] = ' ';
}
else
{
state->slot1light[0] = '[';
state->slot1light[6] = ']';
state->slot0light[0] = ' ';
state->slot0light[6] = ' ';
}
}
}
cachdata[12] = 0;
#ifdef DMR_DUMP
k = 0;
for (i = 0; i < 12; i++)
{
dibit = cachdata[i];
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
cachbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
cachbits[24] = 0;
printf ("%s ", cachbits);
#endif
// current slot
dibit_p += 49;
// slot type
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
cc[0] = (1 & (dibit >> 1)) + 48; // bit 1
cc[1] = (1 & dibit) + 48; // bit 0
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
cc[2] = (1 & (dibit >> 1)) + 48; // bit 1
cc[3] = (1 & dibit) + 48; // bit 0
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
bursttype[0] = (1 & (dibit >> 1)) + 48; // bit 1
bursttype[1] = (1 & dibit) + 48; // bit 0
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
bursttype[2] = (1 & (dibit >> 1)) + 48; // bit 1
bursttype[3] = (1 & dibit) + 48; // bit 0
// parity bit
dibit_p++;
if (strcmp (bursttype, "0000") == 0)
{
sprintf (state->fsubtype, " PI Header ");
}
else if (strcmp (bursttype, "0001") == 0)
{
sprintf (state->fsubtype, " VOICE Header ");
}
else if (strcmp (bursttype, "0010") == 0)
{
sprintf (state->fsubtype, " TLC ");
}
else if (strcmp (bursttype, "0011") == 0)
{
sprintf (state->fsubtype, " CSBK ");
}
else if (strcmp (bursttype, "0100") == 0)
{
sprintf (state->fsubtype, " MBC Header ");
}
else if (strcmp (bursttype, "0101") == 0)
{
sprintf (state->fsubtype, " MBC ");
}
else if (strcmp (bursttype, "0110") == 0)
{
sprintf (state->fsubtype, " DATA Header ");
}
else if (strcmp (bursttype, "0111") == 0)
{
sprintf (state->fsubtype, " RATE 1/2 DATA");
}
else if (strcmp (bursttype, "1000") == 0)
{
sprintf (state->fsubtype, " RATE 3/4 DATA");
}
else if (strcmp (bursttype, "1001") == 0)
{
sprintf (state->fsubtype, " Slot idle ");
}
else if (strcmp (bursttype, "1010") == 0)
{
sprintf (state->fsubtype, " Rate 1 DATA ");
}
else
{
sprintf (state->fsubtype, " ");
}
// signaling data or sync
for (i = 0; i < 24; i++)
{
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
syncdata[i] = dibit;
sync[i] = (dibit | 1) + 48;
}
sync[24] = 0;
syncdata[24] = 0;
#ifdef DMR_DUMP
k = 0;
for (i = 0; i < 24; i++)
{
dibit = syncdata[i];
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
syncbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
syncbits[48] = 0;
printf ("%s ", syncbits);
#endif
if (strcmp (sync, DMR_DATA_SYNC) == 0)
{
if (state->currentslot == 0)
{
sprintf (state->slot0light, "[slot0]");
}
else
{
sprintf (state->slot1light, "[slot1]");
}
}
else if (strcmp (sync, DMR_VOICE_SYNC) == 0)
{
if (state->currentslot == 0)
{
sprintf (state->slot0light, "[SLOT0]");
}
else
{
sprintf (state->slot1light, "[SLOT1]");
}
}
if (opts->errorbars == 1)
{
printf ("%s %s ", state->slot0light, state->slot1light);
}
// current slot second half, cach, next slot 1st half
skipDibit (opts, state, 120);
if (opts->errorbars == 1)
{
if (strcmp (state->fsubtype, " ") == 0)
{
printf (" Unknown burst type: %s\n", bursttype);
}
else
{
printf ("%s\n", state->fsubtype);
}
}
}

View File

@@ -1,376 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
#include "dmr_const.h"
void
processDMRvoice (dsd_opts * opts, dsd_state * state)
{
// extracts AMBE frames from DMR frame
int i, j, dibit;
int *dibit_p;
char ambe_fr[4][24];
char ambe_fr2[4][24];
char ambe_fr3[4][24];
const int *w, *x, *y, *z;
char sync[25];
char syncdata[25];
char cachdata[13];
int mutecurrentslot;
#ifdef DMR_DUMP
int k;
char syncbits[49];
char cachbits[25];
#endif
mutecurrentslot = 0;
dibit_p = state->dibit_buf_p - 144;
for (j = 0; j < 6; j++)
{
// 2nd half of previous slot
for (i = 0; i < 54; i++)
{
if (j > 0)
{
dibit = getDibit (opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
}
// CACH
for (i = 0; i < 12; i++)
{
if (j > 0)
{
dibit = getDibit (opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
cachdata[i] = dibit;
if (i == 2)
{
state->currentslot = (1 & (dibit >> 1)); // bit 1
if (state->currentslot == 0)
{
state->slot0light[0] = '[';
state->slot0light[6] = ']';
state->slot1light[0] = ' ';
state->slot1light[6] = ' ';
}
else
{
state->slot1light[0] = '[';
state->slot1light[6] = ']';
state->slot0light[0] = ' ';
state->slot0light[6] = ' ';
}
}
}
cachdata[12] = 0;
#ifdef DMR_DUMP
k = 0;
for (i = 0; i < 12; i++)
{
dibit = cachdata[i];
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
cachbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
cachbits[24] = 0;
printf ("%s ", cachbits);
#endif
// current slot frame 1
w = rW;
x = rX;
y = rY;
z = rZ;
for (i = 0; i < 36; i++)
{
if (j > 0)
{
dibit = getDibit (opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
// current slot frame 2 first half
w = rW;
x = rX;
y = rY;
z = rZ;
for (i = 0; i < 18; i++)
{
if (j > 0)
{
dibit = getDibit (opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
// signaling data or sync
for (i = 0; i < 24; i++)
{
if (j > 0)
{
dibit = getDibit (opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
syncdata[i] = dibit;
sync[i] = (dibit | 1) + 48;
}
sync[24] = 0;
syncdata[24] = 0;
if (strcmp (sync, DMR_DATA_SYNC) == 0)
{
mutecurrentslot = 1;
if (state->currentslot == 0)
{
sprintf (state->slot0light, "[slot0]");
}
else
{
sprintf (state->slot1light, "[slot1]");
}
}
else if (strcmp (sync, DMR_VOICE_SYNC) == 0)
{
mutecurrentslot = 0;
if (state->currentslot == 0)
{
sprintf (state->slot0light, "[SLOT0]");
}
else
{
sprintf (state->slot1light, "[SLOT1]");
}
}
if ((j == 0) && (opts->errorbars == 1))
{
printf ("%s %s VOICE e:", state->slot0light, state->slot1light);
}
#ifdef DMR_DUMP
k = 0;
for (i = 0; i < 24; i++)
{
dibit = syncdata[i];
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
syncbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
syncbits[48] = 0;
printf ("%s ", syncbits);
#endif
// current slot frame 2 second half
for (i = 0; i < 18; i++)
{
dibit = getDibit (opts, state);
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
if (mutecurrentslot == 0)
{
if (state->firstframe == 1)
{ // we don't know if anything received before the first sync after no carrier is valid
state->firstframe = 0;
}
else
{
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
}
}
// current slot frame 3
w = rW;
x = rX;
y = rY;
z = rZ;
for (i = 0; i < 36; i++)
{
dibit = getDibit (opts, state);
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
if (mutecurrentslot == 0)
{
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
// CACH
for (i = 0; i < 12; i++)
{
dibit = getDibit (opts, state);
cachdata[i] = dibit;
}
cachdata[12] = 0;
#ifdef DMR_DUMP
k = 0;
for (i = 0; i < 12; i++)
{
dibit = cachdata[i];
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
cachbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
cachbits[24] = 0;
printf ("%s ", cachbits);
#endif
// next slot
skipDibit (opts, state, 54);
// signaling data or sync
for (i = 0; i < 24; i++)
{
dibit = getDibit (opts, state);
syncdata[i] = dibit;
sync[i] = (dibit | 1) + 48;
}
sync[24] = 0;
syncdata[24] = 0;
if (strcmp (sync, DMR_DATA_SYNC) == 0)
{
if (state->currentslot == 0)
{
sprintf (state->slot1light, " slot1 ");
}
else
{
sprintf (state->slot0light, " slot0 ");
}
}
else if (strcmp (sync, DMR_VOICE_SYNC) == 0)
{
if (state->currentslot == 0)
{
sprintf (state->slot1light, " SLOT1 ");
}
else
{
sprintf (state->slot0light, " SLOT0 ");
}
}
#ifdef DMR_DUMP
k = 0;
for (i = 0; i < 24; i++)
{
dibit = syncdata[i];
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
syncbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
syncbits[48] = 0;
printf ("%s ", syncbits);
#endif
if (j == 5)
{
// 2nd half next slot
skipDibit (opts, state, 54);
// CACH
skipDibit (opts, state, 12);
// first half current slot
skipDibit (opts, state, 54);
}
}
if (opts->errorbars == 1)
{
printf ("\n");
}
}

View File

@@ -0,0 +1,26 @@
#! /bin/bash
clear
echo DSD-FME Digital Speech Decoder - Florida Man Edition Auto Installer
echo This will clone, build, and install DSD-FME only.
echo If you need dependencies installed, please run download-and-install.sh instead.
echo
read -p "Press enter to continue"
git clone https://github.com/lwvmobile/dsd-fme
cd dsd-fme
git branch -a
git checkout remotes/origin/pulseaudio
git checkout -b pulseaudio
git branch -a #double check to see if you are on pulseaudio branch
sudo cp tone8.wav /usr/share/
sudo cp tone24.wav /usr/share/
sudo chmod 777 /usr/share/tone8.wav
sudo chmod 777 /usr/share/tone24.wav
mkdir build
cd build
cmake ..
make -j `nproc`
##only run make install if you don't have another version already installed##
sudo make install
sudo ldconfig

70
download-and-install.sh Normal file
View File

@@ -0,0 +1,70 @@
#! /bin/bash
clear
echo DSD-FME Digital Speech Decoder - Florida Man Edition Auto Installer
echo MBELib is considered a requirement on this build.
echo You must view this notice prior to continuing.
echo The Patent Notice can be found at the site below.
echo https://github.com/lwvmobile/mbelib#readme
echo Please confirm that you have viewed the patent notice by entering y below.
echo
echo y/N
read ANSWER
Y='y'
if [[ $Y == $ANSWER ]]; then
sudo apt update
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev
echo ITPP Manual Build and Install has been temporarily removed from this script due to issues with older versions of Ubuntu.
echo Please check to see that libitpp-dev has successfully been downloaded and installed from the repo.
read -p "Press enter to continue"
#wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
#tar xjf itpp*
#if you can't cd into this folder, double check folder name first
#cd itpp-4.3.1
#mkdir build
#cd build
#cmake ..
#make -j `nproc`
#sudo make install
#sudo ldconfig
#cd ..
#cd ..
git clone https://github.com/lwvmobile/mbelib
cd mbelib
mkdir build
cd build
cmake ..
make -j `nproc`
sudo make install
sudo ldconfig
cd ..
cd ..
git clone https://github.com/lwvmobile/dsd-fme
cd dsd-fme
git branch -a
git checkout remotes/origin/pulseaudio
git checkout -b pulseaudio
git branch -a #double check to see if you are on pulseaudio branch
sudo cp tone8.wav /usr/share/
sudo cp tone24.wav /usr/share/
sudo chmod 777 /usr/share/tone8.wav
sudo chmod 777 /usr/share/tone24.wav
mkdir build
cd build
cmake ..
make -j `nproc`
##only run make install if you don't have another version already installed##
sudo make install
sudo ldconfig
else
echo
echo Sorry, you cannot build DSD-FME without acknowledging the Patent Notice.
fi

BIN
dsd-fme-qgis-map.qgz Normal file

Binary file not shown.

BIN
dsd-fme.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

BIN
dsd-fme2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

218
dsd.h
View File

@@ -1,218 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define __USE_XOPEN
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef SOLARIS
#include <sys/audioio.h>
#endif
#ifdef BSD
#include <sys/soundcard.h>
#endif
#include <math.h>
#include <mbelib.h>
typedef struct
{
int onesymbol;
char mbe_in_file[1024];
FILE *mbe_in_f;
int errorbars;
int datascope;
int symboltiming;
int verbose;
int p25enc;
int p25lc;
int p25status;
int p25tg;
int scoperate;
char audio_in_dev[1024];
int audio_in_fd;
char audio_out_dev[1024];
int audio_out_fd;
int split;
int playoffset;
char mbe_out_dir[1024];
char mbe_out_file[1024];
FILE *mbe_out_f;
float audio_gain;
int audio_out;
char wav_out_file[1024];
FILE *wav_out_f;
int wav_out_fd;
int serial_baud;
char serial_dev[1024];
int serial_fd;
int resume;
int frame_dstar;
int frame_x2tdma;
int frame_p25p1;
int frame_nxdn;
int frame_dmr;
int frame_provoice;
int mod_c4fm;
int mod_qpsk;
int mod_gfsk;
int uvquality;
int inverted_x2tdma;
int inverted_dmr;
int mod_threshold;
int ssize;
int msize;
int playfiles;
int delay;
} dsd_opts;
typedef struct
{
int *dibit_buf;
int *dibit_buf_p;
int repeat;
short *audio_out_buf;
short *audio_out_buf_p;
float *audio_out_float_buf;
float *audio_out_float_buf_p;
float audio_out_temp_buf[160];
float *audio_out_temp_buf_p;
int audio_out_idx;
int audio_out_idx2;
int center;
int jitter;
int synctype;
int min;
int max;
int lmid;
int umid;
int minref;
int maxref;
int lastsample;
int sbuf[128];
int sidx;
int maxbuf[1024];
int minbuf[1024];
int midx;
char err_str[64];
char fsubtype[16];
char ftype[16];
int symbolcnt;
int rf_mod;
int numflips;
int lastsynctype;
int lastp25type;
int offset;
int carrier;
char tg[25][16];
int tgcount;
int lasttg;
int lastsrc;
int nac;
int errs;
int errs2;
int mbe_file_type;
int optind;
int numtdulc;
int firstframe;
char slot0light[8];
char slot1light[8];
float aout_gain;
float aout_max_buf[200];
float *aout_max_buf_p;
int aout_max_buf_idx;
int samplesPerSymbol;
int symbolCenter;
char algid[9];
char keyid[17];
int currentslot;
mbe_parms *cur_mp;
mbe_parms *prev_mp;
mbe_parms *prev_mp_enhanced;
} dsd_state;
/*
* Frame sync patterns
*/
#define INV_FRAME_SYNC "333331331133111131311111"
#define FRAME_SYNC "111113113311333313133333"
#define X2TDMA_VOICE_SYNC "113131333331313331113311"
#define X2TDMA_DATA_SYNC "331313111113131113331133"
#define DSTAR_SYNC "313131313133131113313111"
#define INV_DSTAR_SYNC "131313131311313331131333"
#define INV_NXDN96_SYNC "313133113131111333"
#define NXDN96_SYNC "131311331313333111"
#define DMR_DATA_SYNC "313333111331131131331131"
#define DMR_VOICE_SYNC "131111333113313313113313"
#define INV_PROVOICE_SYNC "31313111333133133311331133113311"
#define PROVOICE_SYNC "13131333111311311133113311331133"
/*
* function prototypes
*/
void processDMRdata (dsd_opts * opts, dsd_state * state);
void processDMRvoice (dsd_opts * opts, dsd_state * state);
void processAudio (dsd_opts * opts, dsd_state * state);
void writeSynthesizedVoice (dsd_opts * opts, dsd_state * state);
void playSynthesizedVoice (dsd_opts * opts, dsd_state * state);
void openAudioOutDevice (dsd_opts * opts, int speed);
void openAudioInDevice (dsd_opts * opts);
int getDibit (dsd_opts * opts, dsd_state * state);
void skipDibit (dsd_opts * opts, dsd_state * state, int count);
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
void saveAmbe2250Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
int readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
int readAmbe2250Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
void openMbeInFile (dsd_opts * opts, dsd_state * state);
void closeMbeOutFile (dsd_opts * opts, dsd_state * state);
void openMbeOutFile (dsd_opts * opts, dsd_state * state);
void openWavOutFile (dsd_opts * opts, dsd_state * state);
void printFrameInfo (dsd_opts * opts, dsd_state * state);
void processFrame (dsd_opts * opts, dsd_state * state);
void printFrameSync (dsd_opts * opts, dsd_state * state, char *frametype, int offset, char *modulation);
int getFrameSync (dsd_opts * opts, dsd_state * state);
int comp (const void *a, const void *b);
void noCarrier (dsd_opts * opts, dsd_state * state);
void initOpts (dsd_opts * opts);
void initState (dsd_state * state);
void usage ();
void liveScanner (dsd_opts * opts, dsd_state * state);
int main (int argc, char **argv);
void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv);
void processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24]);
void openSerial (dsd_opts * opts, dsd_state * state);
void resumeScan (dsd_opts * opts, dsd_state * state);
int getSymbol (dsd_opts * opts, dsd_state * state, int have_sync);
void upsample (dsd_state * state, float invalue);
void processDSTAR (dsd_opts * opts, dsd_state * state);
void processNXDN96 (dsd_opts * opts, dsd_state * state);
void processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo);
void processHDU (dsd_opts * opts, dsd_state * state);
void processLDU1 (dsd_opts * opts, dsd_state * state);
void processLDU2 (dsd_opts * opts, dsd_state * state);
void processTDULC (dsd_opts * opts, dsd_state * state);
void processProVoice (dsd_opts * opts, dsd_state * state);
void processX2TDMAdata (dsd_opts * opts, dsd_state * state);
void processX2TDMAvoice (dsd_opts * opts, dsd_state * state);

View File

@@ -1,365 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
void
processAudio (dsd_opts * opts, dsd_state * state)
{
int i, n;
float aout_abs, max, gainfactor, gaindelta, maxbuf;
if (opts->audio_gain == (float) 0)
{
// detect max level
max = 0;
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
for (n = 0; n < 160; n++)
{
aout_abs = fabsf (*state->audio_out_temp_buf_p);
if (aout_abs > max)
{
max = aout_abs;
}
state->audio_out_temp_buf_p++;
}
*state->aout_max_buf_p = max;
state->aout_max_buf_p++;
state->aout_max_buf_idx++;
if (state->aout_max_buf_idx > 74)
{
state->aout_max_buf_idx = 0;
state->aout_max_buf_p = state->aout_max_buf;
}
// lookup max history
for (i = 0; i < 75; i++)
{
maxbuf = state->aout_max_buf[i];
if (maxbuf > max)
{
max = maxbuf;
}
}
// determine optimal gain level
if (max > (float) 0)
{
gainfactor = ((float) 30000 / max);
}
else
{
gainfactor = (float) 50;
}
if (gainfactor < state->aout_gain)
{
state->aout_gain = gainfactor;
gaindelta = (float) 0;
}
else
{
if (gainfactor > (float) 50)
{
gainfactor = (float) 50;
}
gaindelta = gainfactor - state->aout_gain;
if (gaindelta > ((float) 0.05 * state->aout_gain))
{
gaindelta = ((float) 0.05 * state->aout_gain);
}
}
gaindelta /= (float) 160;
}
else
{
gaindelta = (float) 0;
}
// adjust output gain
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
for (n = 0; n < 160; n++)
{
*state->audio_out_temp_buf_p = (state->aout_gain + ((float) n * gaindelta)) * (*state->audio_out_temp_buf_p);
state->audio_out_temp_buf_p++;
}
state->aout_gain += ((float) 160 * gaindelta);
// copy audio datat to output buffer and upsample if necessary
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
if (opts->split == 0)
{
for (n = 0; n < 160; n++)
{
upsample (state, *state->audio_out_temp_buf_p);
state->audio_out_temp_buf_p++;
state->audio_out_float_buf_p += 6;
state->audio_out_idx += 6;
state->audio_out_idx2 += 6;
}
state->audio_out_float_buf_p -= (960 + opts->playoffset);
// copy to output (short) buffer
for (n = 0; n < 960; n++)
{
if (*state->audio_out_float_buf_p > (float) 32760)
{
*state->audio_out_float_buf_p = (float) 32760;
}
else if (*state->audio_out_float_buf_p < (float) -32760)
{
*state->audio_out_float_buf_p = (float) -32760;
}
*state->audio_out_buf_p = (short) *state->audio_out_float_buf_p;
state->audio_out_buf_p++;
state->audio_out_float_buf_p++;
}
state->audio_out_float_buf_p += opts->playoffset;
}
else
{
for (n = 0; n < 160; n++)
{
if (*state->audio_out_temp_buf_p > (float) 32760)
{
*state->audio_out_temp_buf_p = (float) 32760;
}
else if (*state->audio_out_temp_buf_p < (float) -32760)
{
*state->audio_out_temp_buf_p = (float) -32760;
}
*state->audio_out_buf_p = (short) *state->audio_out_temp_buf_p;
state->audio_out_buf_p++;
state->audio_out_temp_buf_p++;
state->audio_out_idx++;
state->audio_out_idx2++;
}
}
}
void
writeSynthesizedVoice (dsd_opts * opts, dsd_state * state)
{
int n;
short aout_buf[160];
short *aout_buf_p;
aout_buf_p = aout_buf;
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
for (n = 0; n < 160; n++)
{
if (*state->audio_out_temp_buf_p > (float) 32760)
{
*state->audio_out_temp_buf_p = (float) 32760;
}
else if (*state->audio_out_temp_buf_p < (float) -32760)
{
*state->audio_out_temp_buf_p = (float) -32760;
}
*aout_buf_p = (short) *state->audio_out_temp_buf_p;
aout_buf_p++;
state->audio_out_temp_buf_p++;
}
write (opts->wav_out_fd, aout_buf, 320);
fflush (opts->wav_out_f);
}
void
playSynthesizedVoice (dsd_opts * opts, dsd_state * state)
{
if (state->audio_out_idx > opts->delay)
{
// output synthesized speech to sound card
write (opts->audio_out_fd, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2));
state->audio_out_idx = 0;
}
if (state->audio_out_idx2 >= 800000)
{
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
state->audio_out_buf_p = state->audio_out_buf + 100;
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
memset (state->audio_out_buf, 0, 100 * sizeof (short));
state->audio_out_idx2 = 0;
}
}
void
openAudioOutDevice (dsd_opts * opts, int speed)
{
#ifdef SOLARIS
sample_info_t aset, aget;
opts->audio_out_fd = open (opts->audio_out_dev, O_WRONLY);
if (opts->audio_out_fd == -1)
{
printf ("Error, couldn't open %s\n", opts->audio_out_dev);
exit (1);
}
// get current
ioctl (opts->audio_out_fd, AUDIO_GETINFO, &aset);
aset.record.sample_rate = speed;
aset.play.sample_rate = speed;
aset.record.channels = 1;
aset.play.channels = 1;
aset.record.precision = 16;
aset.play.precision = 16;
aset.record.encoding = AUDIO_ENCODING_LINEAR;
aset.play.encoding = AUDIO_ENCODING_LINEAR;
if (ioctl (opts->audio_out_fd, AUDIO_SETINFO, &aset) == -1)
{
printf ("Error setting sample device parameters\n");
exit (1);
}
#endif
#ifdef BSD
int fmt;
opts->audio_out_fd = open (opts->audio_out_dev, O_WRONLY);
if (opts->audio_out_fd == -1)
{
printf ("Error, couldn't open %s\n", opts->audio_out_dev);
opts->audio_out = 0;
}
fmt = 0;
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0)
{
printf ("ioctl reset error \n");
}
fmt = speed;
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SPEED, &fmt) < 0)
{
printf ("ioctl speed error \n");
}
fmt = 0;
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_STEREO, &fmt) < 0)
{
printf ("ioctl stereo error \n");
}
fmt = AFMT_S16_LE;
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SETFMT, &fmt) < 0)
{
printf ("ioctl setfmt error \n");
}
#endif
printf ("Audio Out Device: %s\n", opts->audio_out_dev);
}
void
openAudioInDevice (dsd_opts * opts)
{
#ifdef SOLARIS
sample_info_t aset, aget;
int rgain;
rgain = 64;
if (opts->split == 1)
{
opts->audio_in_fd = open (opts->audio_in_dev, O_RDONLY);
}
else
{
opts->audio_in_fd = open (opts->audio_in_dev, O_RDWR);
}
if (opts->audio_in_fd == -1)
{
printf ("Error, couldn't open /dev/audio\n");
}
// get current
ioctl (opts->audio_in_fd, AUDIO_GETINFO, &aset);
aset.record.sample_rate = 48000;
aset.play.sample_rate = 48000;
aset.record.channels = 1;
aset.play.channels = 1;
aset.record.precision = 16;
aset.play.precision = 16;
aset.record.encoding = AUDIO_ENCODING_LINEAR;
aset.play.encoding = AUDIO_ENCODING_LINEAR;
aset.record.port = AUDIO_LINE_IN;
aset.record.gain = rgain;
if (ioctl (opts->audio_in_fd, AUDIO_SETINFO, &aset) == -1)
{
printf ("Error setting sample device parameters\n");
exit (1);
}
#endif
#ifdef BSD
int fmt;
if (opts->split == 1)
{
opts->audio_in_fd = open (opts->audio_in_dev, O_RDONLY);
}
else
{
opts->audio_in_fd = open (opts->audio_in_dev, O_RDWR);
}
if (opts->audio_in_fd == -1)
{
printf ("Error, couldn't open %s\n", opts->audio_in_dev);
opts->audio_out = 0;
}
fmt = 0;
if (ioctl (opts->audio_in_fd, SNDCTL_DSP_RESET) < 0)
{
printf ("ioctl reset error \n");
}
fmt = 48000;
if (ioctl (opts->audio_in_fd, SNDCTL_DSP_SPEED, &fmt) < 0)
{
printf ("ioctl speed error \n");
}
fmt = 0;
if (ioctl (opts->audio_in_fd, SNDCTL_DSP_STEREO, &fmt) < 0)
{
printf ("ioctl stereo error \n");
}
fmt = AFMT_S16_LE;
if (ioctl (opts->audio_in_fd, SNDCTL_DSP_SETFMT, &fmt) < 0)
{
printf ("ioctl setfmt error \n");
}
#endif
if (opts->split == 1)
{
printf ("Audio In Device: %s\n", opts->audio_in_dev);
}
else
{
printf ("Audio In/Out Device: %s\n", opts->audio_in_dev);
}
}

View File

@@ -1,290 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
int
getDibit (dsd_opts * opts, dsd_state * state)
{
// returns one dibit value
int i, j, o, symbol;
int sbuf2[128];
int spectrum[64];
char modulation[8];
int lmin, lmax, lsum;
state->numflips = 0;
symbol = getSymbol (opts, state, 1);
state->sbuf[state->sidx] = symbol;
for (i = 0; i < opts->ssize; i++)
{
sbuf2[i] = state->sbuf[i];
}
qsort (sbuf2, opts->ssize, sizeof (int), comp);
// continuous update of min/max in rf_mod=1 (QPSK) mode
// in c4fm min/max must only be updated during sync
if (state->rf_mod == 1)
{
lmin = (sbuf2[0] + sbuf2[1]) / 2;
lmax = (sbuf2[(opts->ssize - 1)] + sbuf2[(opts->ssize - 2)]) / 2;
state->minbuf[state->midx] = lmin;
state->maxbuf[state->midx] = lmax;
if (state->midx == (opts->msize - 1))
{
state->midx = 0;
}
else
{
state->midx++;
}
lsum = 0;
for (i = 0; i < opts->msize; i++)
{
lsum += state->minbuf[i];
}
state->min = lsum / opts->msize;
lsum = 0;
for (i = 0; i < opts->msize; i++)
{
lsum += state->maxbuf[i];
}
state->max = lsum / opts->msize;
state->center = ((state->max) + (state->min)) / 2;
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
state->maxref = ((state->max) * 0.80);
state->minref = ((state->min) * 0.80);
}
else
{
state->maxref = state->max;
state->minref = state->min;
}
if (state->sidx == (opts->ssize - 1))
{
state->sidx = 0;
if (opts->datascope == 1)
{
if (state->rf_mod == 0)
{
sprintf (modulation, "C4FM");
}
else if (state->rf_mod == 1)
{
sprintf (modulation, "QPSK");
}
else if (state->rf_mod == 2)
{
sprintf (modulation, "GFSK");
}
for (i = 0; i < 64; i++)
{
spectrum[i] = 0;
}
for (i = 0; i < opts->ssize; i++)
{
o = (sbuf2[i] + 32768) / 1024;
spectrum[o]++;
}
if (state->symbolcnt > (4800 / opts->scoperate))
{
state->symbolcnt = 0;
printf ("\n");
printf ("Demod mode: %s Nac: %4X\n", modulation, state->nac);
printf ("Frame Type: %s Talkgroup: %7i\n", state->ftype, state->lasttg);
printf ("Frame Subtype: %s Source: %12i\n", state->fsubtype, state->lastsrc);
printf ("TDMA activity: %s %s Voice errors: %s\n", state->slot0light, state->slot1light, state->err_str);
printf ("+----------------------------------------------------------------+\n");
for (i = 0; i < 10; i++)
{
printf ("|");
for (j = 0; j < 64; j++)
{
if (i == 0)
{
if ((j == ((state->min) + 32768) / 1024) || (j == ((state->max) + 32768) / 1024))
{
printf ("#");
}
else if ((j == ((state->lmid) + 32768) / 1024) || (j == ((state->umid) + 32768) / 1024))
{
printf ("^");
}
else if (j == (state->center + 32768) / 1024)
{
printf ("!");
}
else
{
if (j == 32)
{
printf ("|");
}
else
{
printf (" ");
}
}
}
else
{
if (spectrum[j] > 9 - i)
{
printf ("*");
}
else
{
if (j == 32)
{
printf ("|");
}
else
{
printf (" ");
}
}
}
}
printf ("|\n");
}
printf ("+----------------------------------------------------------------+\n");
}
}
}
else
{
state->sidx++;
}
// determine dibit state
if ((state->synctype == 6) || (state->synctype == 14))
{
if (symbol > state->center)
{
*state->dibit_buf_p = 1;
state->dibit_buf_p++;
return (0);
}
else
{
*state->dibit_buf_p = 3;
state->dibit_buf_p++;
return (1);
}
}
else if ((state->synctype == 7) || (state->synctype == 15))
{
if (symbol > state->center)
{
*state->dibit_buf_p = 1;
state->dibit_buf_p++;
return (1);
}
else
{
*state->dibit_buf_p = 3;
state->dibit_buf_p++;
return (0);
}
}
else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) || (state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13))
{
if (symbol > state->center)
{
if (symbol > state->umid)
{
*state->dibit_buf_p = 1; // store non-inverted values in dibit_buf
state->dibit_buf_p++;
return (3);
}
else
{
*state->dibit_buf_p = 0;
state->dibit_buf_p++;
return (2);
}
}
else
{
if (symbol < state->lmid)
{
*state->dibit_buf_p = 3;
state->dibit_buf_p++;
return (1);
}
else
{
*state->dibit_buf_p = 2;
state->dibit_buf_p++;
return (0);
}
}
}
else
{
if (symbol > state->center)
{
if (symbol > state->umid)
{
*state->dibit_buf_p = 1;
state->dibit_buf_p++;
return (1);
}
else
{
*state->dibit_buf_p = 0;
state->dibit_buf_p++;
return (0);
}
}
else
{
if (symbol < state->lmid)
{
*state->dibit_buf_p = 3;
state->dibit_buf_p++;
return (3);
}
else
{
*state->dibit_buf_p = 2;
state->dibit_buf_p++;
return (2);
}
}
}
}
void
skipDibit (dsd_opts * opts, dsd_state * state, int count)
{
short sample;
int i;
for (i = 0; i < (count); i++)
{
sample = getDibit (opts, state);
}
}

View File

@@ -1,331 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
void
saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
{
int i, j, k;
unsigned char b;
unsigned char err;
err = (unsigned char) state->errs2;
fputc (err, opts->mbe_out_f);
k = 0;
for (i = 0; i < 11; i++)
{
b = 0;
for (j = 0; j < 8; j++)
{
b = b << 1;
b = b + imbe_d[k];
k++;
}
fputc (b, opts->mbe_out_f);
}
fflush (opts->mbe_out_f);
}
void
saveAmbe2250Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
{
int i, j, k;
unsigned char b;
unsigned char err;
err = (unsigned char) state->errs2;
fputc (err, opts->mbe_out_f);
k = 0;
for (i = 0; i < 6; i++)
{
b = 0;
for (j = 0; j < 8; j++)
{
b = b << 1;
b = b + ambe_d[k];
k++;
}
fputc (b, opts->mbe_out_f);
}
b = ambe_d[48];
fputc (b, opts->mbe_out_f);
fflush (opts->mbe_out_f);
}
int
readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
{
int i, j, k;
unsigned char b;
state->errs2 = fgetc (opts->mbe_in_f);
state->errs = state->errs2;
k = 0;
for (i = 0; i < 11; i++)
{
b = fgetc (opts->mbe_in_f);
if (feof (opts->mbe_in_f))
{
return (1);
}
for (j = 0; j < 8; j++)
{
imbe_d[k] = (b & 128) >> 7;
b = b << 1;
b = b & 255;
k++;
}
}
return (0);
}
int
readAmbe2250Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
{
int i, j, k;
unsigned char b;
state->errs2 = fgetc (opts->mbe_in_f);
state->errs = state->errs2;
k = 0;
for (i = 0; i < 6; i++)
{
b = fgetc (opts->mbe_in_f);
if (feof (opts->mbe_in_f))
{
return (1);
}
for (j = 0; j < 8; j++)
{
ambe_d[k] = (b & 128) >> 7;
b = b << 1;
b = b & 255;
k++;
}
}
b = fgetc (opts->mbe_in_f);
ambe_d[48] = b;
return (0);
}
void
openMbeInFile (dsd_opts * opts, dsd_state * state)
{
char cookie[5];
opts->mbe_in_f = fopen (opts->mbe_in_file, "ro");
if (opts->mbe_in_f == NULL)
{
printf ("Error: could not open %s\n", opts->mbe_in_file);
}
// read cookie
cookie[0] = fgetc (opts->mbe_in_f);
cookie[1] = fgetc (opts->mbe_in_f);
cookie[2] = fgetc (opts->mbe_in_f);
cookie[3] = fgetc (opts->mbe_in_f);
cookie[4] = 0;
if (strstr (cookie, ".amb") != NULL)
{
state->mbe_file_type = 1;
}
else if (strstr (cookie, ".imb") != NULL)
{
state->mbe_file_type = 0;
}
else
{
state->mbe_file_type = -1;
printf ("Error - unrecognized file type\n");
}
}
void
closeMbeOutFile (dsd_opts * opts, dsd_state * state)
{
char shell[255], newfilename[64], ext[5], datestr[32];
char tgid[17];
int sum, i, j;
int talkgroup;
struct tm timep;
if (opts->mbe_out_f != NULL)
{
if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
{
sprintf (ext, ".imb");
strptime (opts->mbe_out_file, "%s.imb", &timep);
}
else
{
sprintf (ext, ".amb");
strptime (opts->mbe_out_file, "%s.amb", &timep);
}
for (i = 0; i < 17; i++)
{
tgid[i] = 0;
}
if (state->tgcount > 0)
{
for (i = 0; i < 16; i++)
{
sum = 0;
for (j = 0; j < state->tgcount; j++)
{
sum = sum + state->tg[j][i] - 48;
}
tgid[i] = (char) (((float) sum / (float) state->tgcount) + 48.5);
}
talkgroup = (int) strtol (tgid, NULL, 2);
}
else
{
talkgroup = 0;
}
fflush (opts->mbe_out_f);
fclose (opts->mbe_out_f);
opts->mbe_out_f = NULL;
strftime (datestr, 31, "%Y-%m-%d-%H%M%S", &timep);
sprintf (newfilename, "nac%X-%s-tg%i%s", state->nac, datestr, talkgroup, ext);
sprintf (shell, "mv %s %s", opts->mbe_out_file, newfilename);
system (shell);
}
}
void
openMbeOutFile (dsd_opts * opts, dsd_state * state)
{
struct timeval tv;
int i, j;
char ext[5];
if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
{
sprintf (ext, ".imb");
}
else
{
sprintf (ext, ".amb");
}
// reset talkgroup id buffer
for (i = 0; i < 12; i++)
{
for (j = 0; j < 25; j++)
{
state->tg[j][i] = 0;
}
}
state->tgcount = 0;
gettimeofday (&tv, NULL);
sprintf (opts->mbe_out_file, "%i%s", (int) tv.tv_sec, ext);
opts->mbe_out_f = fopen (opts->mbe_out_file, "w");
if (opts->mbe_out_f == NULL)
{
printf ("Error, couldn't open %s\n", opts->mbe_out_file);
}
// write magic
fprintf (opts->mbe_out_f, "%s", ext);
fflush (opts->mbe_out_f);
}
void
openWavOutFile (dsd_opts * opts, dsd_state * state)
{
opts->wav_out_f = fopen (opts->wav_out_file, "w");
if (opts->wav_out_f == NULL)
{
printf ("Error - could not open wav output file %s\n", opts->wav_out_file);
return;
}
opts->wav_out_fd = fileno (opts->wav_out_f);
fprintf (opts->wav_out_f, "RIFF");
// total length
fputc (0, opts->wav_out_f);
fputc (0, opts->wav_out_f);
fputc (0, opts->wav_out_f);
fputc (0, opts->wav_out_f);
fprintf (opts->wav_out_f, "WAVE");
fprintf (opts->wav_out_f, "fmt ");
// length of format chunk
fputc (16, opts->wav_out_f);
fputc (0, opts->wav_out_f);
fputc (0, opts->wav_out_f);
fputc (0, opts->wav_out_f);
// always 0x1
fputc (1, opts->wav_out_f);
fputc (0, opts->wav_out_f);
// channels
fputc (1, opts->wav_out_f);
fputc (0, opts->wav_out_f);
// sample rate
fputc (64, opts->wav_out_f);
fputc (31, opts->wav_out_f);
fputc (0, opts->wav_out_f);
fputc (0, opts->wav_out_f);
// bytes per second
fputc (176, opts->wav_out_f);
fputc (62, opts->wav_out_f);
fputc (0, opts->wav_out_f);
fputc (0, opts->wav_out_f);
// bytes/sample
fputc (2, opts->wav_out_f);
fputc (0, opts->wav_out_f);
// bits/sample
fputc (16, opts->wav_out_f);
fputc (0, opts->wav_out_f);
// data chunk header
fprintf (opts->wav_out_f, "data");
// length of data
fputc (0, opts->wav_out_f);
fputc (0, opts->wav_out_f);
fputc (0, opts->wav_out_f);
fputc (127, opts->wav_out_f);
fflush (opts->wav_out_f);
}

View File

@@ -1,421 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
void
printFrameInfo (dsd_opts * opts, dsd_state * state)
{
int level;
level = (int) (((float) state->max / (float) 32768) * (float) 100);
if (opts->verbose > 0)
{
printf ("input: %2i%% ", level);
}
if (state->nac != 0)
{
printf ("nac: %4X ", state->nac);
}
if (opts->verbose > 1)
{
printf ("src: %8i ", state->lastsrc);
}
printf ("tg: %5i ", state->lasttg);
}
void
processFrame (dsd_opts * opts, dsd_state * state)
{
int i, j, dibit;
char duid[3];
char nac[13];
int level;
duid[2] = 0;
j = 0;
if (state->rf_mod == 1)
{
state->maxref = (state->max * 0.80);
state->minref = (state->min * 0.80);
}
else
{
state->maxref = state->max;
state->minref = state->min;
}
if ((state->synctype == 8) || (state->synctype == 9))
{
state->nac = 0;
state->lastsrc = 0;
state->lasttg = 0;
if (opts->errorbars == 1)
{
if (opts->verbose > 0)
{
level = (int) (((float) state->max / (float) 32768) * (float) 100);
printf ("input: %2i%% ", level);
}
}
state->nac = 0;
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
openMbeOutFile (opts, state);
}
sprintf (state->fsubtype, " VOICE ");
processNXDN96 (opts, state);
return;
}
else if ((state->synctype == 6) || (state->synctype == 7))
{
state->nac = 0;
state->lastsrc = 0;
state->lasttg = 0;
if (opts->errorbars == 1)
{
if (opts->verbose > 0)
{
level = (int) (((float) state->max / (float) 32768) * (float) 100);
printf ("input: %2i%% ", level);
}
}
state->nac = 0;
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
openMbeOutFile (opts, state);
}
sprintf (state->fsubtype, " VOICE ");
processDSTAR (opts, state);
return;
}
else if ((state->synctype >= 10) && (state->synctype <= 13))
{
state->nac = 0;
state->lastsrc = 0;
state->lasttg = 0;
if (opts->errorbars == 1)
{
if (opts->verbose > 0)
{
level = (int) (((float) state->max / (float) 32768) * (float) 100);
printf ("input: %2i%% ", level);
}
}
if ((state->synctype == 11) || (state->synctype == 12))
{
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
openMbeOutFile (opts, state);
}
sprintf (state->fsubtype, " VOICE ");
processDMRvoice (opts, state);
}
else
{
closeMbeOutFile (opts, state);
state->err_str[0] = 0;
processDMRdata (opts, state);
}
return;
}
else if ((state->synctype >= 2) && (state->synctype <= 5))
{
state->nac = 0;
state->lastsrc = 0;
state->lasttg = 0;
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
}
if ((state->synctype == 3) || (state->synctype == 4))
{
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
openMbeOutFile (opts, state);
}
sprintf (state->fsubtype, " VOICE ");
processX2TDMAvoice (opts, state);
}
else
{
closeMbeOutFile (opts, state);
state->err_str[0] = 0;
processX2TDMAdata (opts, state);
}
return;
}
else if ((state->synctype == 14) || (state->synctype == 15))
{
state->nac = 0;
state->lastsrc = 0;
state->lasttg = 0;
if (opts->errorbars == 1)
{
if (opts->verbose > 0)
{
level = (int) (((float) state->max / (float) 32768) * (float) 100);
printf ("input: %2i%% ", level);
}
}
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
openMbeOutFile (opts, state);
}
sprintf (state->fsubtype, " VOICE ");
processProVoice (opts, state);
return;
}
else
{
j = 0;
for (i = 0; i < 6; i++)
{
dibit = getDibit (opts, state);
nac[j] = (1 & (dibit >> 1)) + 48; // bit 1
j++;
nac[j] = (1 & dibit) + 48; // bit 0
j++;
}
nac[12] = 0;
state->nac = strtol (nac, NULL, 2);
for (i = 0; i < 2; i++)
{
duid[i] = getDibit (opts, state) + 48;
}
}
if (strcmp (duid, "00") == 0)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf (" HDU\n");
}
state->cur_mp->repeat = 0;
if (opts->mbe_out_dir[0] != 0)
{
closeMbeOutFile (opts, state);
openMbeOutFile (opts, state);
}
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
state->lastp25type = 2;
sprintf (state->fsubtype, " HDU ");
processHDU (opts, state);
}
else if (strcmp (duid, "11") == 0)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf (" LDU1 ");
}
if (opts->mbe_out_dir[0] != 0)
{
if (opts->mbe_out_f == NULL)
{
openMbeOutFile (opts, state);
}
}
state->lastp25type = 1;
sprintf (state->fsubtype, " LDU1 ");
state->numtdulc = 0;
processLDU1 (opts, state);
}
else if (strcmp (duid, "22") == 0)
{
if (state->lastp25type != 1)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf (" Ignoring LDU2 not preceeded by LDU1\n");
}
state->lastp25type = 0;
sprintf (state->fsubtype, " ");
}
else
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf (" LDU2 ");
}
if (opts->mbe_out_dir[0] != 0)
{
if (opts->mbe_out_f == NULL)
{
openMbeOutFile (opts, state);
}
}
state->lastp25type = 2;
sprintf (state->fsubtype, " LDU2 ");
state->numtdulc = 0;
processLDU2 (opts, state);
}
}
else if (strcmp (duid, "33") == 0)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf (" TDULC\n");
}
state->cur_mp->repeat = 0;
if (opts->mbe_out_dir[0] != 0)
{
closeMbeOutFile (opts, state);
}
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
state->tgcount = 0;
state->lastp25type = 0;
sprintf (state->fsubtype, " TDULC ");
state->numtdulc++;
if ((opts->resume > 0) && (state->numtdulc > opts->resume))
{
resumeScan (opts, state);
}
processTDULC (opts, state);
state->err_str[0] = 0;
}
else if (strcmp (duid, "03") == 0)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf (" TDU\n");
}
state->cur_mp->repeat = 0;
if (opts->mbe_out_dir[0] != 0)
{
closeMbeOutFile (opts, state);
}
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
state->tgcount = 0;
state->lastp25type = 0;
state->err_str[0] = 0;
sprintf (state->fsubtype, " TDU ");
skipDibit (opts, state, 40);
}
else if (strcmp (duid, "13") == 0)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf (" TSDU\n");
}
state->lastp25type = 3;
sprintf (state->fsubtype, " TSDU ");
skipDibit (opts, state, 328);
}
else if (strcmp (duid, "30") == 0)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf (" PDU\n");
if (opts->resume > 0)
{
resumeScan (opts, state);
}
}
if (opts->mbe_out_dir[0] != 0)
{
if (opts->mbe_out_f == NULL)
{
openMbeOutFile (opts, state);
}
}
state->lastp25type = 4;
sprintf (state->fsubtype, " PDU ");
}
// try to guess based on previous frame if unknown type
else if (state->lastp25type == 1)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf ("(LDU2) ");
}
if (opts->mbe_out_dir[0] != 0)
{
if (opts->mbe_out_f == NULL)
{
openMbeOutFile (opts, state);
}
}
state->lastp25type = 0;
sprintf (state->fsubtype, "(LDU2) ");
state->numtdulc = 0;
processLDU2 (opts, state);
}
else if (state->lastp25type == 2)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf ("(LDU1) ");
}
if (opts->mbe_out_dir[0] != 0)
{
if (opts->mbe_out_f == NULL)
{
openMbeOutFile (opts, state);
}
}
state->lastp25type = 0;
sprintf (state->fsubtype, "(LDU1) ");
state->numtdulc = 0;
processLDU1 (opts, state);
}
else if (state->lastp25type == 3)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf (" (TSDU)\n");
}
state->lastp25type = 0;
sprintf (state->fsubtype, "(TSDU) ");
skipDibit (opts, state, 328);
}
else if (state->lastp25type == 4)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf (" (PDU)\n");
}
state->lastp25type = 0;
}
else
{
state->lastp25type = 0;
sprintf (state->fsubtype, " ");
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
printf (" duid:%s *Unknown DUID*\n", duid);
}
}
}

View File

@@ -1,691 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
void
printFrameSync (dsd_opts * opts, dsd_state * state, char *frametype, int offset, char *modulation)
{
if (opts->verbose > 0)
{
printf ("Sync: %s ", frametype);
}
if (opts->verbose > 2)
{
printf ("o: %4i ", offset);
}
if (opts->verbose > 1)
{
printf ("mod: %s ", modulation);
}
if (opts->verbose > 2)
{
printf ("g: %f ", state->aout_gain);
}
}
int
getFrameSync (dsd_opts * opts, dsd_state * state)
{
/* detects frame sync and returns frame type
* 0 = +p25p1
* 1 = -p25p1
* 2 = +X2-TDMA (non inverted signal data frame)
* 3 = +X2-TDMA (inverted signal voice frame)
* 4 = -X2-TDMA (non inverted signal voice frame)
* 5 = -X2-TDMA (inverted signal data frame)
* 6 = +D-STAR
* 7 = -D-STAR
* 8 = +NXDN96
* 9 = -NXDN96
* 10 = +DMR (non inverted singlan data frame)
* 11 = -DMR (inverted signal voice frame)
* 12 = +DMR (non inverted signal voice frame)
* 13 = -DMR (inverted signal data frame)
* 14 = +ProVoice
* 15 = -ProVoice
*/
int i, j, t, o, dibit, sync, symbol, synctest_pos, lastt;
char synctest[25];
char synctest18[19];
char synctest32[33];
char modulation[8];
char *synctest_p;
char synctest_buf[10240];
int lmin, lmax, lidx;
int lbuf[24], lbuf2[24];
int lsum;
char spectrum[64];
// detect frame sync
t = 0;
synctest[24] = 0;
synctest18[18] = 0;
synctest32[32] = 0;
synctest_pos = 0;
synctest_p = synctest_buf;
sync = 0;
lmin = 0;
lmax = 0;
lidx = 0;
lastt = 0;
state->numflips = 0;
if ((opts->symboltiming == 1) && (state->carrier == 1))
{
printf ("\nSymbol Timing:\n");
}
while (sync == 0)
{
t++;
symbol = getSymbol (opts, state, 0);
lbuf[lidx] = symbol;
state->sbuf[state->sidx] = symbol;
if (lidx == 23)
{
lidx = 0;
}
else
{
lidx++;
}
if (state->sidx == (opts->ssize - 1))
{
state->sidx = 0;
}
else
{
state->sidx++;
}
if (lastt == 23)
{
lastt = 0;
if (state->numflips > opts->mod_threshold)
{
if (opts->mod_qpsk == 1)
{
state->rf_mod = 1;
}
}
else if (state->numflips > 18)
{
if (opts->mod_gfsk == 1)
{
state->rf_mod = 2;
}
}
else
{
if (opts->mod_c4fm == 1)
{
state->rf_mod = 0;
}
}
state->numflips = 0;
}
else
{
lastt++;
}
//determine dibit state
if (symbol > 0)
{
*state->dibit_buf_p = 1;
state->dibit_buf_p++;
dibit = 49;
}
else
{
*state->dibit_buf_p = 3;
state->dibit_buf_p++;
dibit = 51;
}
/*
printf("%c", dibit);
if (t == 768)
{
printf("\n");
t=0;
}
*/
*synctest_p = dibit;
if (t >= 24)
{
for (i = 0; i < 24; i++)
{
lbuf2[i] = lbuf[i];
}
qsort (lbuf2, 24, sizeof (int), comp);
lmin = (lbuf2[2] + lbuf2[3] + lbuf2[4]) / 3;
lmax = (lbuf2[21] + lbuf2[20] + lbuf2[19]) / 3;
if (state->rf_mod == 1)
{
state->minbuf[state->midx] = lmin;
state->maxbuf[state->midx] = lmax;
if (state->midx == (opts->msize - 1))
{
state->midx = 0;
}
else
{
state->midx++;
}
lsum = 0;
for (i = 0; i < opts->msize; i++)
{
lsum += state->minbuf[i];
}
state->min = lsum / opts->msize;
lsum = 0;
for (i = 0; i < opts->msize; i++)
{
lsum += state->maxbuf[i];
}
state->max = lsum / opts->msize;
state->center = ((state->max) + (state->min)) / 2;
state->maxref = ((state->max) * 0.80);
state->minref = ((state->min) * 0.80);
}
else
{
state->maxref = state->max;
state->minref = state->min;
}
if (state->rf_mod == 0)
{
sprintf (modulation, "C4FM");
}
else if (state->rf_mod == 1)
{
sprintf (modulation, "QPSK");
}
else if (state->rf_mod == 2)
{
sprintf (modulation, "GFSK");
}
if (opts->datascope == 1)
{
if (lidx == 0)
{
for (i = 0; i < 64; i++)
{
spectrum[i] = 0;
}
for (i = 0; i < 24; i++)
{
o = (lbuf2[i] + 32768) / 1024;
spectrum[o]++;
}
if (state->symbolcnt > (4800 / opts->scoperate))
{
state->symbolcnt = 0;
printf ("\n");
printf ("Demod mode: %s Nac: %4X\n", modulation, state->nac);
printf ("Frame Type: %s Talkgroup: %7i\n", state->ftype, state->lasttg);
printf ("Frame Subtype: %s Source: %12i\n", state->fsubtype, state->lastsrc);
printf ("TDMA activity: %s %s Voice errors: %s\n", state->slot0light, state->slot1light, state->err_str);
printf ("+----------------------------------------------------------------+\n");
for (i = 0; i < 10; i++)
{
printf ("|");
for (j = 0; j < 64; j++)
{
if (i == 0)
{
if ((j == ((state->min) + 32768) / 1024) || (j == ((state->max) + 32768) / 1024))
{
printf ("#");
}
else if (j == (state->center + 32768) / 1024)
{
printf ("!");
}
else
{
if (j == 32)
{
printf ("|");
}
else
{
printf (" ");
}
}
}
else
{
if (spectrum[j] > 9 - i)
{
printf ("*");
}
else
{
if (j == 32)
{
printf ("|");
}
else
{
printf (" ");
}
}
}
}
printf ("|\n");
}
printf ("+----------------------------------------------------------------+\n");
}
}
}
strncpy (synctest, (synctest_p - 23), 24);
if (opts->frame_p25p1 == 1)
{
if (strcmp (synctest, FRAME_SYNC) == 0)
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, " P25 Phase 1 ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " +p25p1 ", synctest_pos + 1, modulation);
}
state->lastsynctype = 0;
return (0);
}
if (strcmp (synctest, INV_FRAME_SYNC) == 0)
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, " P25 Phase 1 ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " -p25p1 ", synctest_pos + 1, modulation);
}
state->lastsynctype = 1;
return (1);
}
}
if (opts->frame_x2tdma == 1)
{
if (strcmp (synctest, X2TDMA_DATA_SYNC) == 0)
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + (lmax)) / 2;
state->min = ((state->min) + (lmin)) / 2;
if (opts->inverted_x2tdma == 0)
{
// data frame
sprintf (state->ftype, " X2-TDMA ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " +X2-TDMA ", synctest_pos + 1, modulation);
}
state->lastsynctype = 2;
return (2);
}
else
{
// inverted voice frame
sprintf (state->ftype, " X2-TDMA ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " -X2-TDMA ", synctest_pos + 1, modulation);
}
if (state->lastsynctype != 3)
{
state->firstframe = 1;
}
state->lastsynctype = 3;
return (3);
}
}
if (strcmp (synctest, X2TDMA_VOICE_SYNC) == 0)
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (opts->inverted_x2tdma == 0)
{
// voice frame
sprintf (state->ftype, " X2-TDMA ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " +X2-TDMA ", synctest_pos + 1, modulation);
}
if (state->lastsynctype != 4)
{
state->firstframe = 1;
}
state->lastsynctype = 4;
return (4);
}
else
{
// inverted data frame
sprintf (state->ftype, " X2-TDMA ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " -X2-TDMA ", synctest_pos + 1, modulation);
}
state->lastsynctype = 5;
return (5);
}
}
}
if (opts->frame_dmr == 1)
{
if (strcmp (synctest, DMR_DATA_SYNC) == 0)
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + (lmax)) / 2;
state->min = ((state->min) + (lmin)) / 2;
if (opts->inverted_dmr == 0)
{
// data frame
sprintf (state->ftype, " DMR ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " +DMR ", synctest_pos + 1, modulation);
}
state->lastsynctype = 10;
return (10);
}
else
{
// inverted voice frame
sprintf (state->ftype, " DMR ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " -DMR ", synctest_pos + 1, modulation);
}
if (state->lastsynctype != 11)
{
state->firstframe = 1;
}
state->lastsynctype = 11;
return (11);
}
}
if (strcmp (synctest, DMR_VOICE_SYNC) == 0)
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
if (opts->inverted_dmr == 0)
{
// voice frame
sprintf (state->ftype, " DMR ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " +DMR ", synctest_pos + 1, modulation);
}
if (state->lastsynctype != 12)
{
state->firstframe = 1;
}
state->lastsynctype = 12;
return (12);
}
else
{
// inverted data frame
sprintf (state->ftype, " DMR ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " -DMR ", synctest_pos + 1, modulation);
}
state->lastsynctype = 13;
return (13);
}
}
}
if (opts->frame_provoice == 1)
{
strncpy (synctest32, (synctest_p - 31), 32);
if (strcmp (synctest32, PROVOICE_SYNC) == 0)
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, " ProVoice ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " -ProVoice ", synctest_pos + 1, modulation);
}
state->lastsynctype = 14;
return (14);
}
else if (strcmp (synctest32, INV_PROVOICE_SYNC) == 0)
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, " ProVoice ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " -ProVoice ", synctest_pos + 1, modulation);
}
state->lastsynctype = 15;
return (15);
}
}
if (opts->frame_nxdn == 1)
{
strncpy (synctest18, (synctest_p - 17), 18);
if (strcmp (synctest18, NXDN96_SYNC) == 0)
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, " NXDN96 ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " +NXDN96 ", synctest_pos + 1, modulation);
}
state->lastsynctype = 8;
return (8);
}
if (strcmp (synctest18, INV_NXDN96_SYNC) == 0)
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, " NXDN96 ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " -NXDN96 ", synctest_pos + 1, modulation);
}
state->lastsynctype = 9;
return (9);
}
}
if (opts->frame_dstar == 1)
{
if (strcmp (synctest, DSTAR_SYNC) == 0)
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, " D-STAR ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " +D-STAR ", synctest_pos + 1, modulation);
}
state->lastsynctype = 6;
return (6);
}
if (strcmp (synctest, INV_DSTAR_SYNC) == 0)
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, " D-STAR ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, " -D-STAR ", synctest_pos + 1, modulation);
}
state->lastsynctype = 7;
return (7);
}
}
if ((t == 24) && (state->lastsynctype != -1))
{
if ((state->lastsynctype == 0) && ((state->lastp25type == 1) || (state->lastp25type == 2)))
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + (lmax)) / 2;
state->min = ((state->min) + (lmin)) / 2;
sprintf (state->ftype, "(P25 Phase 1)");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, "(+p25p1) ", synctest_pos + 1, modulation);
}
state->lastsynctype = -1;
return (0);
}
else if ((state->lastsynctype == 1) && ((state->lastp25type == 1) || (state->lastp25type == 2)))
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, "(P25 Phase 1)");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, "(-p25p1) ", synctest_pos + 1, modulation);
}
state->lastsynctype = -1;
return (1);
}
else if ((state->lastsynctype == 3) && (strcmp (synctest, X2TDMA_VOICE_SYNC) != 0))
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, "(X2-TDMA) ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, "(-X2-TDMA) ", synctest_pos + 1, modulation);
}
state->lastsynctype = -1;
return (3);
}
else if ((state->lastsynctype == 4) && (strcmp (synctest, X2TDMA_DATA_SYNC) != 0))
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, "(X2-TDMA) ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, "(+X2-TDMA) ", synctest_pos + 1, modulation);
}
state->lastsynctype = -1;
return (4);
}
else if ((state->lastsynctype == 11) && (strcmp (synctest, DMR_VOICE_SYNC) != 0))
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, "(DMR) ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, "(-DMR) ", synctest_pos + 1, modulation);
}
state->lastsynctype = -1;
return (11);
}
else if ((state->lastsynctype == 12) && (strcmp (synctest, DMR_DATA_SYNC) != 0))
{
state->carrier = 1;
state->offset = synctest_pos;
state->max = ((state->max) + lmax) / 2;
state->min = ((state->min) + lmin) / 2;
sprintf (state->ftype, "(DMR) ");
if (opts->errorbars == 1)
{
printFrameSync (opts, state, "(+DMR) ", synctest_pos + 1, modulation);
}
state->lastsynctype = -1;
return (12);
}
}
}
if (synctest_pos < 10200)
{
synctest_pos++;
synctest_p++;
}
else
{
// buffer reset
synctest_pos = 0;
synctest_p = synctest_buf;
noCarrier (opts, state);
}
if (state->carrier == 1)
{
if (synctest_pos >= 1800)
{
if (opts->errorbars == 1)
{
if (opts->verbose > 1)
{
printf ("Sync: no sync\n");
}
}
noCarrier (opts, state);
return (-1);
}
}
}
return (-1);
}

View File

@@ -1,635 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#define _MAIN
#include "dsd.h"
#include "p25p1_const.h"
#include "x2tdma_const.h"
#include "dstar_const.h"
#include "nxdn96_const.h"
#include "dmr_const.h"
#include "provoice_const.h"
int
comp (const void *a, const void *b)
{
if (*((const int *) a) == *((const int *) b))
return 0;
else if (*((const int *) a) < *((const int *) b))
return -1;
else
return 1;
}
void
noCarrier (dsd_opts * opts, dsd_state * state)
{
state->dibit_buf_p = state->dibit_buf + 200;
memset (state->dibit_buf, 0, sizeof (int) * 200);
if (opts->mbe_out_f != NULL)
{
closeMbeOutFile (opts, state);
}
state->jitter = -1;
state->lastsynctype = -1;
state->carrier = 0;
state->max = 15000;
state->min = -15000;
state->center = 0;
state->err_str[0] = 0;
sprintf (state->fsubtype, " ");
sprintf (state->ftype, " ");
state->lasttg = 0;
state->lastsrc = 0;
state->lastp25type = 0;
state->repeat = 0;
state->nac = 0;
state->numtdulc = 0;
sprintf (state->slot0light, " slot0 ");
sprintf (state->slot1light, " slot1 ");
state->firstframe = 0;
if (opts->audio_gain == (float) 0)
{
state->aout_gain = 25;
}
memset (state->aout_max_buf, 0, sizeof (float) * 200);
state->aout_max_buf_p = state->aout_max_buf;
state->aout_max_buf_idx = 0;
sprintf (state->algid, "________");
sprintf (state->keyid, "________________");
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
}
void
initOpts (dsd_opts * opts)
{
opts->onesymbol = 10;
opts->mbe_in_file[0] = 0;
opts->mbe_in_f = NULL;
opts->errorbars = 1;
opts->datascope = 0;
opts->symboltiming = 0;
opts->verbose = 2;
opts->p25enc = 0;
opts->p25lc = 0;
opts->p25status = 0;
opts->p25tg = 0;
opts->scoperate = 15;
sprintf (opts->audio_in_dev, "/dev/audio");
opts->audio_in_fd = -1;
sprintf (opts->audio_out_dev, "/dev/audio");
opts->audio_out_fd = -1;
opts->split = 0;
opts->playoffset = 0;
opts->mbe_out_dir[0] = 0;
opts->mbe_out_file[0] = 0;
opts->mbe_out_f = NULL;
opts->audio_gain = 0;
opts->audio_out = 1;
opts->wav_out_file[0] = 0;
opts->wav_out_f = NULL;
opts->wav_out_fd = -1;
opts->serial_baud = 115200;
sprintf (opts->serial_dev, "/dev/ttyUSB0");
opts->resume = 0;
opts->frame_dstar = 0;
opts->frame_x2tdma = 1;
opts->frame_p25p1 = 1;
opts->frame_nxdn = 0;
opts->frame_dmr = 1;
opts->frame_provoice = 0;
opts->mod_c4fm = 1;
opts->mod_qpsk = 1;
opts->mod_gfsk = 1;
opts->uvquality = 3;
opts->inverted_x2tdma = 1; // most transmitter + scanner + sound card combinations show inverted signals for this
opts->inverted_dmr = 0; // most transmitter + scanner + sound card combinations show non-inverted signals for this
opts->mod_threshold = 26;
opts->ssize = 36;
opts->msize = 15;
opts->playfiles = 0;
opts->delay = 0;
}
void
initState (dsd_state * state)
{
int i, j;
state->dibit_buf = malloc (sizeof (int) * 1000000);
state->dibit_buf_p = state->dibit_buf + 200;
memset (state->dibit_buf, 0, sizeof (int) * 200);
state->repeat = 0;
state->audio_out_buf = malloc (sizeof (short) * 1000000);
memset (state->audio_out_buf, 0, 100 * sizeof (short));
state->audio_out_buf_p = state->audio_out_buf + 100;
state->audio_out_float_buf = malloc (sizeof (float) * 1000000);
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
state->audio_out_idx = 0;
state->audio_out_idx2 = 0;
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
state->center = 0;
state->jitter = -1;
state->synctype = -1;
state->min = -15000;
state->max = 15000;
state->lmid = 0;
state->umid = 0;
state->minref = -12000;
state->maxref = 12000;
state->lastsample = 0;
for (i = 0; i < 128; i++)
{
state->sbuf[i] = 0;
}
state->sidx = 0;
for (i = 0; i < 1024; i++)
{
state->maxbuf[i] = 15000;
}
for (i = 0; i < 1024; i++)
{
state->minbuf[i] = -15000;
}
state->midx = 0;
state->err_str[0] = 0;
sprintf (state->fsubtype, " ");
sprintf (state->ftype, " ");
state->symbolcnt = 0;
state->rf_mod = 0;
state->numflips = 0;
state->lastsynctype = -1;
state->lastp25type = 0;
state->offset = 0;
state->carrier = 0;
for (j = 0; i < 25; j++)
{
for (i = 0; j < 16; i++)
{
state->tg[i][j] = 0;
}
}
state->tgcount = 0;
state->lasttg = 0;
state->lastsrc = 0;
state->nac = 0;
state->errs = 0;
state->errs2 = 0;
state->mbe_file_type = -1;
state->optind = 0;
state->numtdulc = 0;
state->firstframe = 0;
sprintf (state->slot0light, " slot0 ");
sprintf (state->slot1light, " slot1 ");
state->aout_gain = 25;
memset (state->aout_max_buf, 0, sizeof (float) * 200);
state->aout_max_buf_p = state->aout_max_buf;
state->aout_max_buf_idx = 0;
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
sprintf (state->algid, "________");
sprintf (state->keyid, "________________");
state->currentslot = 0;
state->cur_mp = malloc (sizeof (mbe_parms));
state->prev_mp = malloc (sizeof (mbe_parms));
state->prev_mp_enhanced = malloc (sizeof (mbe_parms));
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
}
void
usage ()
{
printf ("Digital Speech Decoder 1.3\n");
printf ("\n");
printf ("Usage:\n");
printf (" dsd [options] Live scanner mode\n");
printf (" dsd [options] -r <files> Read/Play saved mbe data from file(s)\n");
printf (" dsd -h Show help\n");
printf ("\n");
printf ("Display Options:\n");
printf (" -e Show Frame Info and errorbars (default)\n");
printf (" -pe Show p25 encryption sync bits\n");
printf (" -pl Show p25 link control bits\n");
printf (" -ps Show p25 status bits and low speed data\n");
printf (" -pt Show p25 talkgroup info\n");
printf (" -q Don't show Frame Info/errorbars\n");
printf (" -s Datascope (disables other display options)\n");
printf (" -t Show symbol timing during sync\n");
printf (" -v <num> Frame information Verbosity\n");
printf (" -z <num> Frame rate for datascope\n");
printf ("\n");
printf ("Input/Output options:\n");
printf (" -i <device> Audio input device (default is /dev/audio)\n");
printf (" -o <device> Audio output device (default is /dev/audio)\n");
printf (" -d <dir> Create mbe data files, use this directory\n");
printf (" -g <num> Audio output gain (default = 0 = auto)\n");
printf (" -n Do not send synthesized speech to audio output device\n");
printf (" -w <file> Output synthesized speech to a .wav file\n");
printf ("\n");
printf ("Scanner control options:\n");
printf (" -B <num> Serial port baud rate (default=115200)\n");
printf (" -C <device> Serial port for scanner control (default=/dev/ttyUSB0)\n");
printf (" -R <num> Resume scan after <num> TDULC frames or any PDU\n");
printf ("\n");
printf ("Decoder options:\n");
printf (" -fa Auto-detect frame type (default)\n");
printf (" -f1 Decode only P25 Phase 1\n");
printf (" -fd Decode only D-STAR* (no audio)\n");
printf (" -fn Decode only NXDN96* (no audio)\n");
printf (" -fp Decode only ProVoice*\n");
printf (" -fr Decode only DMR/MOTOTRBO\n");
printf (" -fx Decode only X2-TDMA\n");
printf (" -ma Auto-select modulation optimizations (default)\n");
printf (" -mc Use only C4FM modulation optimizations\n");
printf (" -mg Use only GFSK modulation optimizations\n");
printf (" -mq Use only QPSK modulation optimizations\n");
printf (" -u <num> Unvoiced speech quality (default=3)\n");
printf (" -xx Expect non-inverted X2-TDMA signal\n");
printf (" -xr Expect inverted DMR/MOTOTRBO signal\n");
printf ("\n");
printf (" * denotes frame types that cannot be auto-detected.\n");
printf ("\n");
printf ("Advanced decoder options:\n");
printf (" -A <num> QPSK modulation auto detection threshold (default=26)\n");
printf (" -S <num> Symbol buffer size for QPSK decision point tracking\n");
printf (" (default=36)\n");
printf (" -M <num> Min/Max buffer size for QPSK decision point tracking\n");
printf (" (default=15)\n");
exit (0);
}
void
liveScanner (dsd_opts * opts, dsd_state * state)
{
while (1)
{
noCarrier (opts, state);
state->synctype = getFrameSync (opts, state);
// recalibrate center/umid/lmid
state->center = ((state->max) + (state->min)) / 2;
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
while (state->synctype != -1)
{
processFrame (opts, state);
state->synctype = getFrameSync (opts, state);
// recalibrate center/umid/lmid
state->center = ((state->max) + (state->min)) / 2;
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
}
}
}
int
main (int argc, char **argv)
{
int c;
extern char *optarg;
extern int optind, opterr, optopt;
dsd_opts opts;
dsd_state state;
initOpts (&opts);
initState (&state);
while ((c = getopt (argc, argv, "hep:qstv:z:i:o:d:g:nw:B:C:R:f:m:u:x:A:S:M:r")) != -1)
{
opterr = 0;
switch (c)
{
case 'h':
usage ();
exit (0);
case 'e':
opts.errorbars = 1;
opts.datascope = 0;
break;
case 'p':
if (optarg[0] == 'e')
{
opts.p25enc = 1;
}
else if (optarg[0] == 'l')
{
opts.p25lc = 1;
}
else if (optarg[0] == 's')
{
opts.p25status = 1;
}
else if (optarg[0] == 't')
{
opts.p25tg = 1;
}
break;
case 'q':
opts.errorbars = 0;
opts.verbose = 0;
break;
case 's':
opts.errorbars = 0;
opts.p25enc = 0;
opts.p25lc = 0;
opts.p25status = 0;
opts.p25tg = 0;
opts.datascope = 1;
opts.symboltiming = 0;
break;
case 't':
opts.symboltiming = 1;
opts.errorbars = 1;
opts.datascope = 0;
break;
case 'v':
sscanf (optarg, "%d", &opts.verbose);
break;
case 'z':
sscanf (optarg, "%d", &opts.scoperate);
opts.errorbars = 0;
opts.p25enc = 0;
opts.p25lc = 0;
opts.p25status = 0;
opts.p25tg = 0;
opts.datascope = 1;
opts.symboltiming = 0;
printf ("Setting datascope frame rate to %i frame per second.\n", opts.scoperate);
break;
case 'i':
sscanf (optarg, "%s", opts.audio_in_dev);
break;
case 'o':
sscanf (optarg, "%s", opts.audio_out_dev);
break;
case 'd':
sscanf (optarg, "%s", opts.mbe_out_dir);
printf ("Writing mbe data files to directory %s\n", opts.mbe_out_dir);
break;
case 'g':
sscanf (optarg, "%f", &opts.audio_gain);
if (opts.audio_gain <= (float) 0)
{
opts.audio_gain = (float) 0;
printf ("Enabling audio out auto-gain\n");
}
else
{
printf ("Setting audio out gain to %f\n", opts.audio_gain);
state.aout_gain = opts.audio_gain;
}
break;
case 'n':
opts.audio_out = 0;
printf ("Disabling audio output to soundcard.\n");
break;
case 'w':
sscanf (optarg, "%s", opts.wav_out_file);
printf ("Writing audio to file %s\n", opts.wav_out_file);
openWavOutFile (&opts, &state);
break;
case 'B':
sscanf (optarg, "%d", &opts.serial_baud);
break;
case 'C':
sscanf (optarg, "%s", opts.serial_dev);
break;
case 'R':
sscanf (optarg, "%d", &opts.resume);
printf ("Enabling scan resume after %i TDULC frames\n", opts.resume);
break;
case 'f':
if (optarg[0] == 'a')
{
opts.frame_dstar = 0;
opts.frame_x2tdma = 1;
opts.frame_p25p1 = 1;
opts.frame_nxdn = 0;
opts.frame_dmr = 1;
opts.frame_provoice = 0;
}
else if (optarg[0] == 'd')
{
opts.frame_dstar = 1;
opts.frame_x2tdma = 0;
opts.frame_p25p1 = 0;
opts.frame_nxdn = 0;
opts.frame_dmr = 0;
opts.frame_provoice = 0;
printf ("Decoding only D-STAR frames.\n");
}
else if (optarg[0] == 'x')
{
opts.frame_dstar = 0;
opts.frame_x2tdma = 1;
opts.frame_p25p1 = 0;
opts.frame_nxdn = 0;
opts.frame_dmr = 0;
opts.frame_provoice = 0;
printf ("Decoding only X2-TDMA frames.\n");
}
else if (optarg[0] == 'p')
{
opts.frame_dstar = 0;
opts.frame_x2tdma = 0;
opts.frame_p25p1 = 0;
opts.frame_nxdn = 0;
opts.frame_dmr = 0;
opts.frame_provoice = 1;
state.samplesPerSymbol = 5;
state.symbolCenter = 2;
opts.mod_c4fm = 0;
opts.mod_qpsk = 0;
opts.mod_gfsk = 1;
state.rf_mod = 2;
printf ("Enabling only GFSK modulation optimizations.\n");
printf ("Decoding only ProVoice frames.\n");
}
else if (optarg[0] == '1')
{
opts.frame_dstar = 0;
opts.frame_x2tdma = 0;
opts.frame_p25p1 = 1;
opts.frame_nxdn = 0;
opts.frame_dmr = 0;
opts.frame_provoice = 0;
printf ("Decoding only P25 Phase 1 frames.\n");
}
else if (optarg[0] == 'n')
{
opts.frame_dstar = 0;
opts.frame_x2tdma = 0;
opts.frame_p25p1 = 0;
opts.frame_nxdn = 1;
opts.frame_dmr = 0;
opts.frame_provoice = 0;
printf ("Decoding only NXDN 9600 baud frames.\n");
}
else if (optarg[0] == 'r')
{
opts.frame_dstar = 0;
opts.frame_x2tdma = 0;
opts.frame_p25p1 = 0;
opts.frame_nxdn = 0;
opts.frame_dmr = 1;
opts.frame_provoice = 0;
printf ("Decoding only DMR/MOTOTRBO frames.\n");
}
break;
case 'm':
if (optarg[0] == 'a')
{
opts.mod_c4fm = 1;
opts.mod_qpsk = 1;
opts.mod_gfsk = 1;
state.rf_mod = 0;
}
else if (optarg[0] == 'c')
{
opts.mod_c4fm = 1;
opts.mod_qpsk = 0;
opts.mod_gfsk = 0;
state.rf_mod = 0;
printf ("Enabling only C4FM modulation optimizations.\n");
}
else if (optarg[0] == 'g')
{
opts.mod_c4fm = 0;
opts.mod_qpsk = 0;
opts.mod_gfsk = 1;
state.rf_mod = 2;
printf ("Enabling only GFSK modulation optimizations.\n");
}
else if (optarg[0] == 'q')
{
opts.mod_c4fm = 0;
opts.mod_qpsk = 1;
opts.mod_gfsk = 0;
state.rf_mod = 1;
printf ("Enabling only QPSK modulation optimizations.\n");
}
break;
case 'u':
sscanf (optarg, "%i", &opts.uvquality);
if (opts.uvquality < 1)
{
opts.uvquality = 1;
}
else if (opts.uvquality > 64)
{
opts.uvquality = 64;
}
printf ("Setting unvoice speech quality to %i waves per band.\n", opts.uvquality);
break;
case 'x':
if (optarg[0] == 'x')
{
opts.inverted_x2tdma = 0;
printf ("Expecting non-inverted X2-TDMA signals.\n");
}
else if (optarg[0] == 'r')
{
opts.inverted_dmr = 1;
printf ("Expecting inverted DMR/MOTOTRBO signals.\n");
}
break;
case 'A':
sscanf (optarg, "%i", &opts.mod_threshold);
printf ("Setting C4FM/QPSK auto detection threshold to %i\n", opts.mod_threshold);
break;
case 'S':
sscanf (optarg, "%i", &opts.ssize);
if (opts.ssize > 128)
{
opts.ssize = 128;
}
else if (opts.ssize < 1)
{
opts.ssize = 1;
}
printf ("Setting QPSK symbol buffer to %i\n", opts.ssize);
break;
case 'M':
sscanf (optarg, "%i", &opts.msize);
if (opts.msize > 1024)
{
opts.msize = 1024;
}
else if (opts.msize < 1)
{
opts.msize = 1;
}
printf ("Setting QPSK Min/Max buffer to %i\n", opts.msize);
break;
case 'r':
opts.playfiles = 1;
opts.errorbars = 0;
opts.datascope = 0;
state.optind = optind;
break;
default:
usage ();
exit (0);
}
}
if (opts.resume > 0)
{
openSerial (&opts, &state);
}
if (opts.playfiles == 1)
{
opts.split = 1;
opts.playoffset = 0;
opts.delay = 0;
openAudioOutDevice (&opts, 8000);
}
else if (strcmp (opts.audio_in_dev, opts.audio_out_dev) != 0)
{
opts.split = 1;
opts.playoffset = 0;
opts.delay = 0;
openAudioOutDevice (&opts, 8000);
openAudioInDevice (&opts);
}
else
{
opts.split = 0;
opts.playoffset = 38;
opts.delay = 0;
openAudioInDevice (&opts);
opts.audio_out_fd = opts.audio_in_fd;
}
if (opts.playfiles == 1)
{
playMbeFiles (&opts, &state, argc, argv);
}
else
{
liveScanner (&opts, &state);
}
return (0);
}

122
dsd_mbe.c
View File

@@ -1,122 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
void
playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
{
int i;
char imbe_d[88];
char ambe_d[49];
for (i = state->optind; i < argc; i++)
{
sprintf (opts->mbe_in_file, argv[i]);
openMbeInFile (opts, state);
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
printf ("playing %s\n", opts->mbe_in_file);
while (feof (opts->mbe_in_f) == 0)
{
if (state->mbe_file_type == 0)
{
readImbe4400Data (opts, state, imbe_d);
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
processAudio (opts, state);
if (opts->wav_out_fd != -1)
{
writeSynthesizedVoice (opts, state);
}
if (opts->audio_out == 1)
{
playSynthesizedVoice (opts, state);
}
}
else if (state->mbe_file_type == 1)
{
readAmbe2250Data (opts, state, ambe_d);
mbe_processAmbe2250Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
processAudio (opts, state);
if (opts->wav_out_fd != -1)
{
writeSynthesizedVoice (opts, state);
}
if (opts->audio_out == 1)
{
playSynthesizedVoice (opts, state);
}
}
}
}
}
void
processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24])
{
int i;
char imbe_d[88];
char ambe_d[49];
for (i = 0; i < 88; i++)
{
imbe_d[i] = 0;
}
if ((state->synctype == 0) || (state->synctype == 1))
{
mbe_processImbe7200x4400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe_fr, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->mbe_out_f != NULL)
{
saveImbe4400Data (opts, state, imbe_d);
}
}
else if ((state->synctype == 14) || (state->synctype == 15))
{
mbe_processImbe7100x4400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe7100_fr, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->mbe_out_f != NULL)
{
saveImbe4400Data (opts, state, imbe_d);
}
}
else
{
mbe_processAmbe3600x2250Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->mbe_out_f != NULL)
{
saveAmbe2250Data (opts, state, ambe_d);
}
}
if (opts->errorbars == 1)
{
printf ("%s", state->err_str);
}
processAudio (opts, state);
if (opts->wav_out_fd != -1)
{
writeSynthesizedVoice (opts, state);
}
if (opts->audio_out == 1)
{
playSynthesizedVoice (opts, state);
}
}

View File

@@ -1,180 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
int
getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
{
short sample;
int i, sum, symbol, count;
sum = 0;
count = 0;
for (i = 0; i < state->samplesPerSymbol; i++)
{
// timing control
if ((i == 0) && (have_sync == 0))
{
if (state->rf_mod == 1)
{
if ((state->jitter >= 0) && (state->jitter < state->symbolCenter))
{
i++; // fall back
}
else if ((state->jitter > state->symbolCenter) && (state->jitter < 10))
{
i--; // catch up
}
}
else if (state->rf_mod == 2)
{
if ((state->jitter >= state->symbolCenter - 1) && (state->jitter <= state->symbolCenter))
{
i--;
}
else if ((state->jitter >= state->symbolCenter + 1) && (state->jitter <= state->symbolCenter + 2))
{
i++;
}
}
else if (state->rf_mod == 0)
{
if ((state->jitter > 0) && (state->jitter <= state->symbolCenter))
{
i--; // catch up
}
else if ((state->jitter > state->symbolCenter) && (state->jitter < state->samplesPerSymbol))
{
i++; // fall back
}
}
state->jitter = -1;
}
read (opts->audio_in_fd, &sample, 2);
if ((sample > state->max) && (have_sync == 1) && (state->rf_mod == 0))
{
sample = state->max;
}
else if ((sample < state->min) && (have_sync == 1) && (state->rf_mod == 0))
{
sample = state->min;
}
if (sample > state->center)
{
if (state->lastsample < state->center)
{
state->numflips += 1;
}
if (sample > (state->maxref * 1.25))
{
if (state->lastsample < (state->maxref * 1.25))
{
state->numflips += 1;
}
if ((state->jitter < 0) && (state->rf_mod == 1))
{ // first spike out of place
state->jitter = i;
}
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
{
printf ("O");
}
}
else
{
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
{
printf ("+");
}
if ((state->jitter < 0) && (state->lastsample < state->center) && (state->rf_mod != 1))
{ // first transition edge
state->jitter = i;
}
}
}
else
{ // sample < 0
if (state->lastsample > state->center)
{
state->numflips += 1;
}
if (sample < (state->minref * 1.25))
{
if (state->lastsample > (state->minref * 1.25))
{
state->numflips += 1;
}
if ((state->jitter < 0) && (state->rf_mod == 1))
{ // first spike out of place
state->jitter = i;
}
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
{
printf ("X");
}
}
else
{
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
{
printf ("-");
}
if ((state->jitter < 0) && (state->lastsample > state->center) && (state->rf_mod != 1))
{ // first transition edge
state->jitter = i;
}
}
}
if (state->samplesPerSymbol == 5)
{
if ((i >= 2) && (i <= 2))
{
sum += sample;
count++;
}
}
else
{
if (((i >= state->symbolCenter - 1) && (i <= state->symbolCenter + 2) && (state->rf_mod == 0)) || (((i == state->symbolCenter) || (i == state->symbolCenter + 1)) && (state->rf_mod != 0)))
{
sum += sample;
count++;
}
}
state->lastsample = sample;
}
symbol = (sum / count);
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
{
if (state->jitter >= 0)
{
printf (" %i\n", state->jitter);
}
else
{
printf ("\n");
}
}
state->symbolcnt++;
return (symbol);
}

View File

@@ -1,171 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
void
upsample (dsd_state * state, float invalue)
{
int i, j, sum;
float *outbuf1, a, b, c, d;
float phasestart, phaseend, resamplestart, resampleend, phasestep, resampleamp2;
float resamplemin, resamplemax;
if (state->audio_out_idx2 > 23)
{
outbuf1 = state->audio_out_float_buf_p;
outbuf1--;
c = *outbuf1;
outbuf1 -= 6;
b = *outbuf1;
outbuf1 -= 6;
a = *outbuf1;
d = (invalue);
resamplemin = 32767;
resamplemax = -32768;
if (a < resamplemin)
{
resamplemin = a;
}
if (a > resamplemax)
{
resamplemax = a;
}
if (b < resamplemin)
{
resamplemin = b;
}
if (b > resamplemax)
{
resamplemax = b;
}
if (c < resamplemin)
{
resamplemin = c;
}
if (c > resamplemax)
{
resamplemax = c;
}
if (d < resamplemin)
{
resamplemin = d;
}
if (d > resamplemax)
{
resamplemax = d;
}
resampleamp2 = ((float) resamplemax - (float) resamplemin) / (float) 2;
#ifdef UPSAMPLE_DEBUG
printf ("a:%f b:%f c:%f d:%f resamplemin:%f resamplemax:%f resampleamp2:%f\n", a, b, c, d, resamplemin, resamplemax, resampleamp2);
#endif
if (resampleamp2 == 0)
{
resamplestart = (float) 0;
resampleend = (float) 0;
}
else
{
resamplestart = (((float) b - (float) resamplemin) / resampleamp2) - (float) 1.0;
resampleend = (((float) c - (float) resamplemin) / resampleamp2) - (float) 1.0;
}
if (resamplestart < -1)
{
resamplestart = (float) -1;
}
else if (resamplestart > (float) 1)
{
resamplestart = (float) 1;
}
if (resampleend < -1)
{
resampleend = (float) -1;
}
else if (resampleend > (float) 1)
{
resampleend = (float) 1;
}
phasestart = asinf (resamplestart);
phaseend = asinf (resampleend);
phasestep = (phaseend - phasestart) / 6;
#ifdef UPSAMPLE_DEBUG
printf ("resamplestart:%f, resampleend:%f, phasestart:%f, phaseend:%f, phasestep:%f\n", resamplestart, resampleend, phasestart, phaseend, phasestep);
#endif
// rewrite samples between b and c
outbuf1 = state->audio_out_float_buf_p;
outbuf1 -= 6;
for (i = 1; i < 6; i++)
{
*outbuf1 = (resamplemin + resampleamp2 + (resampleamp2 * sin (phasestart + (phasestep * (float) i))));
#ifdef UPSAMPLE_DEBUG
printf ("audio_out_idx2:%i outbuf:%f\n", state->audio_out_idx2, *outbuf1);
#endif
outbuf1++;
}
#ifdef UPSAMPLE_DEBUG
printf ("(c):%f\n", *outbuf1);
#endif
// write d
outbuf1 += 6;
*outbuf1 = d;
#ifdef UPSAMPLE_DEBUG
printf ("audio_out_idx2:%i outbuf(d):%f\n", state->audio_out_idx2, *outbuf1);
#endif
outbuf1++;
// smooth
outbuf1 -= 36;
for (j = 0; j < 12; j++)
{
for (i = 0; i < 12; i++)
{
sum = 0;
outbuf1--;
sum += *outbuf1;
outbuf1++;
sum += *outbuf1;
outbuf1++;
sum += *outbuf1;
outbuf1--;
*outbuf1 = (sum / (float) 3);
outbuf1++;
}
outbuf1 -= 11;
}
}
else
{
outbuf1 = state->audio_out_float_buf_p;
outbuf1--;
c = *outbuf1;
*outbuf1 = ((invalue * (float) 0.166) + (c * (float) 0.834));
outbuf1++;
*outbuf1 = ((invalue * (float) 0.332) + (c * (float) 0.668));
outbuf1++;
*outbuf1 = ((invalue * (float) 0.5) + (c * (float) 0.5));
outbuf1++;
*outbuf1 = ((invalue * (float) 0.668) + (c * (float) 0.332));
outbuf1++;
*outbuf1 = ((invalue * (float) 0.834) + (c * (float) 0.166));
outbuf1++;
*outbuf1 = invalue;
}
}

79
dstar.c
View File

@@ -1,79 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Note: D-STAR support is very incomplete and does not work yet
* It is unknown if the ambe3600x2250 decoder is even compatible with D-STAR
* voice frames. If it is then the interleave pattern needs to be determined.
* GMSK modulation optimizations will also required to get a usable bit error
* rate. This was included mainly as an example of how other systmes might
* be supported.
*/
#include "dsd.h"
#include "dstar_const.h"
void
processDSTAR (dsd_opts * opts, dsd_state * state)
{
// extracts AMBE frames from D-STAR voice frame
int i, j, dibit;
char ambe_fr[4][24];
char dstar[300];
const int *w, *x;
if (opts->errorbars == 1)
{
printf ("e:");
}
#ifdef DSTAR_DUMP
printf ("\n");
#endif
for (j = 0; j < 210; j++) // 210 is very long but used for debugging
{
// voice frame
w = dW;
x = dX;
for (i = 0; i < 72; i++)
{
dibit = getDibit (opts, state);
dstar[i] = dibit + 48;
ambe_fr[*w][*x] = (1 & dibit);
w++;
x++;
}
dstar[i] = 32;
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
// data frame
for (i = 73; i < 97; i++)
{
dstar[i] = getDibit (opts, state) + 48;
}
dstar[i] = 0;
#ifdef DSTAR_DUMP
printf ("%s\n", dstar);
#endif
}
if (opts->errorbars == 1)
{
printf ("\n");
}
}

View File

@@ -1,311 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* dstar interleave experiments
*/
#ifndef _MAIN
extern const int dW[72];
extern const int dX[72];
#else
/*
* 4x18
*/
/*
const int dW[72] = {
0,0,1,2,
0,0,1,2,
0,0,1,2,
0,0,1,2,
0,0,1,3,
0,0,1,3,
0,1,1,3,
0,1,1,3,
0,1,1,3,
0,1,1,3,
0,1,1,3,
0,1,2,3,
0,1,2,3,
0,1,2,3,
0,1,2,3,
0,1,2,3,
0,1,2,3,
0,1,2,3
};
const int dX[72] = {
23,5,10,3,
22,4,9,2,
21,3,8,1,
20,2,7,0,
19,1,6,13,
18,0,5,12,
17,22,4,11,
16,21,3,10,
15,20,2,9,
14,19,1,8,
13,18,0,7,
12,17,10,6,
11,16,9,5,
10,15,8,4,
9,14,7,3,
8,13,6,2,
7,12,5,1,
6,11,4,0
};
*/
/*
* 3x24 alt
*/
const int dW[72] = {
1, 0, 3,
1, 0, 3,
1, 0, 3,
1, 0, 3,
1, 0, 3,
1, 0, 3,
1, 0, 3,
1, 0, 3,
1, 0, 3,
1, 0, 3,
1, 0, 3,
1, 0, 3,
1, 0, 3,
1, 0, 2,
1, 0, 2,
1, 0, 2,
1, 0, 2,
1, 0, 2,
1, 0, 2,
1, 0, 2,
1, 0, 2,
1, 0, 2,
1, 0, 2,
0, 3, 2,
};
const int dX[72] = {
22, 22, 12,
21, 21, 11,
20, 20, 10,
19, 19, 9,
18, 18, 8,
17, 17, 7,
16, 16, 6,
15, 15, 5,
14, 14, 4,
13, 13, 3,
12, 12, 2,
11, 11, 1,
10, 10, 0,
9, 9, 10,
8, 8, 9,
7, 7, 8,
6, 6, 7,
5, 5, 6,
4, 4, 5,
3, 3, 4,
2, 2, 3,
1, 1, 2,
0, 0, 1,
23, 13, 0
};
/*
* 3x24
*/
/*
const int dW[72] = {
0,1,2,
0,1,2,
0,1,2,
0,1,2,
0,1,2,
0,1,2,
0,1,2,
0,1,2,
0,1,2,
0,1,2,
0,1,3,
0,1,3,
0,1,3,
0,1,3,
0,1,3,
0,1,3,
0,1,3,
0,1,3,
0,1,3,
0,1,3,
0,1,3,
0,1,3,
0,1,3,
0,2,3,
};
const int dX[72] = {
23,22,9,
22,21,8,
21,20,7,
20,19,6,
19,18,5,
18,17,4,
17,16,3,
16,15,2,
15,14,1,
14,13,0,
13,12,13,
12,11,12,
11,10,11,
10,9,10,
9,8,9,
8,7,8,
7,6,7,
6,5,6,
5,4,5,
4,3,4,
3,2,3,
2,1,2,
1,0,1,
0,10,0
};
*/
/*
* 2x36
*/
/*
const int dW[72] = {
0,1,
0,1,
0,1,
0,1,
0,1,
0,1,
0,1,
0,1,
0,1,
0,1,
0,1,
0,2,
0,2,
0,2,
0,2,
0,2,
0,2,
0,2,
0,2,
0,2,
0,2,
0,2,
0,3,
0,3,
1,3,
1,3,
1,3,
1,3,
1,3,
1,3,
1,3,
1,3,
1,3,
1,3,
1,3,
1,3
};
const int dX[72] = {
23,10,
22,9,
21,8,
20,7,
19,6,
18,5,
17,4,
16,3,
15,2,
14,1,
13,0,
12,10,
11,9,
10,8,
9,7,
8,6,
7,5,
6,4,
5,3,
4,2,
3,1,
2,0,
1,13,
0,12,
22,11,
21,10,
20,9,
19,8,
18,7,
17,6,
16,5,
15,4,
14,3,
13,2,
12,1,
11,0
};
*/
/*
* non-interleaved
*/
/*
const int dW[72] = {
0,0,0,0,0,0,
0,0,0,0,0,0,
0,0,0,0,0,0,
0,0,0,0,0,0,
1,1,1,1,1,1,
1,1,1,1,1,1,
1,1,1,1,1,1,
1,1,1,1,1,2,
2,2,2,2,2,2,
2,2,2,2,3,3,
3,3,3,3,3,3,
3,3,3,3,3,3
};
const int dX[72] = {
23,22,21,20,19,18,
17,16,15,14,13,12,
11,10,9,8,7,6,
5,4,3,2,1,0,
22,21,20,19,18,17,
16,15,14,13,12,11,
10,9,8,7,6,5,
4,3,2,1,0,10,
9,8,7,6,5,4,
3,2,1,0,13,12,
11,10,9,8,7,6,
5,4,3,2,1,0
};
*/
#endif

412
include/Golay24.hpp Normal file
View File

@@ -0,0 +1,412 @@
#ifndef GOLAY24_HPP_7a68240afda9406facf81fcad3851111
#define GOLAY24_HPP_7a68240afda9406facf81fcad3851111
/**
* Based on the work of Mr Hank Wallace.
*
* Code adapted from http://www.aqdi.com/golay.htm
*
* Tried before with the implementation from ITPP library but couldn't make it produce the same outputs
* expected from the P25 transmissions that I have tested. This implementation does work.
*/
#include <stdio.h>
#include <assert.h>
#define POLY 0xAE3
class Golay24
{
private:
unsigned int golay(unsigned int cw)
/* This function calculates [23,12] Golay codewords.
The format of the returned int is
[checkbits(11),data(12)]. */
{
int i;
unsigned int c;
cw&=0xfffl;
c=cw; /* save original codeword */
for (i=1; i<=12; i++) /* examine each data bit */
{
if (cw & 1) /* test data bit */
cw^=POLY; /* XOR polynomial */
cw>>=1; /* shift intermediate result */
}
return((cw<<12)|c); /* assemble codeword */
}
int parity(unsigned int cw)
/* This function checks the overall parity of codeword cw.
If parity is even, 0 is returned, else 1. */
{
unsigned char p;
/* XOR the bytes of the codeword */
p=*(unsigned char*)&cw;
p^=*((unsigned char*)&cw+1);
p^=*((unsigned char*)&cw+2);
/* XOR the halves of the intermediate result */
p=p ^ (p>>4);
p=p ^ (p>>2);
p=p ^ (p>>1);
/* return the parity result */
return(p & 1);
}
unsigned int syndrome(unsigned int cw)
/* This function calculates and returns the syndrome
of a [23,12] Golay codeword. */
{
int i;
cw&=0x7fffffl;
for (i=1; i<=12; i++) /* examine each data bit */
{
if (cw & 1) /* test data bit */
cw^=POLY; /* XOR polynomial */
cw>>=1; /* shift intermediate result */
}
return(cw<<12); /* value pairs with upper bits of cw */
}
int weight(unsigned int cw)
/* This function calculates the weight of
23 bit codeword cw. */
{
int bits,k;
/* nibble weight table */
const char wgt[16] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
bits=0; /* bit counter */
k=0;
/* do all bits, six nibbles max */
while ((k<6) && (cw))
{
bits=bits+wgt[cw & 0xf];
cw>>=4;
k++;
}
return(bits);
}
unsigned int rotate_left(unsigned int cw, int n)
/* This function rotates 23 bit codeword cw left by n bits. */
{
int i;
if (n != 0)
{
for (i=1; i<=n; i++)
{
if ((cw & 0x400000l) != 0)
cw=(cw << 1) | 1;
else
cw<<=1;
}
}
return(cw & 0x7fffffl);
}
unsigned int rotate_right(unsigned int cw, int n)
/* This function rotates 23 bit codeword cw right by n bits. */
{
int i;
if (n != 0)
{
for (i=1; i<=n; i++)
{
if ((cw & 1) != 0)
cw=(cw >> 1) | 0x400000l;
else
cw>>=1;
}
}
return(cw & 0x7fffffl);
}
public:
unsigned int correct(unsigned int cw, int *errs, unsigned int *errors_detected)
/* This function corrects Golay [23,12] codeword cw, returning the
corrected codeword. This function will produce the corrected codeword
for three or fewer errors. It will produce some other valid Golay
codeword for four or more errors, possibly not the intended
one. *errs is set to the number of bit errors corrected. */
{
unsigned char
w; /* current syndrome limit weight, 2 or 3 */
unsigned int
mask; /* mask for bit flipping */
int
i,j; /* index */
unsigned int
s, /* calculated syndrome */
cwsaver; /* saves initial value of cw */
cwsaver=cw; /* save */
*errs=0;
*errors_detected = 0;
w=3; /* initial syndrome weight threshold */
j=-1; /* -1 = no trial bit flipping on first pass */
mask=1;
while (j<23) /* flip each trial bit */
{
if (j != -1) /* toggle a trial bit */
{
if (j>0) /* restore last trial bit */
{
cw=cwsaver ^ mask;
mask+=mask; /* point to next bit */
}
cw=cwsaver ^ mask; /* flip next trial bit */
w=2; /* lower the threshold while bit diddling */
}
s=syndrome(cw); /* look for errors */
if (s) /* errors exist */
{
(*errors_detected)++;
for (i=0; i<23; i++) /* check syndrome of each cyclic shift */
{
if ((*errs=weight(s)) <= w) /* syndrome matches error pattern */
{
cw=cw ^ s; /* remove errors */
cw=rotate_right(cw,i); /* unrotate data */
return(s=cw);
}
else
{
cw=rotate_left(cw,1); /* rotate to next pattern */
s=syndrome(cw); /* calc new syndrome */
}
}
j++; /* toggle next trial bit */
}
else
{
return(cw); /* return corrected codeword */
}
}
return(cwsaver); /* return original if no corrections */
} /* correct */
unsigned int encode(unsigned int data)
{
unsigned int codeword =golay(data); /* make a test codeword */
if (parity(codeword)) {
codeword^=0x800000l;
}
return codeword;
}
unsigned int encode23(unsigned int data)
{
return golay(data); /* make a test codeword */
}
int decode(int *errs, unsigned int *cw)
/* This function decodes codeword *cw. Here error correction is attempted,
with *errs set to the number of
bits corrected, and returning 0 if no errors exist, or 1 if parity errors
exist. */
{
unsigned int parity_bit;
unsigned int detected_errors;
parity_bit=*cw & 0x800000l; /* save parity bit */
*cw&=~0x800000l; /* remove parity bit for correction */
*cw=correct(*cw, errs, &detected_errors); /* correct up to three bits */
*cw|=parity_bit; /* restore parity bit */
/* check for 4 bit errors */
if (parity(*cw)) /* odd parity is an error */
return(1);
return(0); /* no errors */
} /* decode */
int detect(int *errs, unsigned int cw)
/* This function decodes codeword cw. Here error detection is performed on cw,
returning 0 if no errors exist, 1 if an overall parity error exists, and
2 if a codeword error exists. */
{
*errs=0;
if (parity(cw)) /* odd parity is an error */
{
*errs=1;
return(1);
}
if (syndrome(cw))
{
*errs=1;
return(2);
}
else
return(0); /* no errors */
} /* decode */
};
/**
* Convenience class that adapts Mr Wallace's implementation to the input/output format of DSD.
* DSD works with data stored in char arrays where each element represents a bit (0 and 1 values).
* The original implementation works with codewords that pack the 24 bits of information (12 data and
* another 12 of parity) in an integer.
*/
class DSDGolay24 : public Golay24
{
public:
unsigned int adapt_to_codeword(const char* word, unsigned int length, const char* parity)
{
unsigned int codeword = 0;
// Data needs to be packed with the 12 bits of parity as the most significant
// bits and 12 bits of data as the less significant. All these discovered by trial and error.
for (unsigned int i=0; i<12; i++) {
assert(parity[11-i] == 0 || parity[11-i] == 1);
codeword <<= 1;
codeword |= parity[11-i];
}
for (unsigned int i=0; i<length; i++) {
char bit = word[length-1-i];
assert(bit == 0 || bit == 1);
codeword <<= 1;
codeword |= bit;
}
// We only have length bits of data. We fill up the less significant bits of codeword with zeros.
if (length < 12) {
codeword <<= (12 - length);
}
return codeword;
}
void adapt_to_word(unsigned int codeword, char* word, unsigned int length)
{
// put back in word the bits from codeword
for (unsigned int i=0, mask = 1<<(12-length); i<length; i++, mask<<=1) {
word[i] = (codeword & mask) != 0? 1 : 0;
}
}
unsigned int adapt_from_word(char* word, unsigned int length)
{
unsigned int codeword = 0;
// encode the hex bits into a codeword
for (unsigned int i=0; i<length; i++) {
int bit = word[length-1-i];
assert(bit == 0 || bit == 1);
codeword <<= 1;
codeword |= bit;
}
// We only have length bits of data. We fill up the less significant bits of codeword with zeros.
if (length < 12) {
codeword <<= (12-length);
}
return codeword;
}
/**
* Important method that takes an hex word, a 12 bit parity word and uses the Golay24 implementation
* to error correct it.
* \param hex The data to error correct, packed in an array of 6 chars, representing a bit each.
* \param parity The 12 bits of parity packed in an array of chars.
* \param fixed_errors Output argument, returns the number of errors detected and fixed.
* \return 1 if we were unable to correct the hex word. Too many errors detected.
* in this case the original data remains unchanged.
* 0 if the data was successfully error corrected.
*/
int decode_6(char* hex, const char* parity, int* fixed_errors)
{
unsigned int codeword = adapt_to_codeword(hex, 6, parity);
// codeword now contains:
// bits 0- 5: zeros
// bits 6-11: hex bits
// bits 12-23: golay (24,12) parity bits
// bits are ordered from left to right, so bit 0 is the most significant and bit 23 is the less.
int uncorrectable_errors = Golay24::decode(fixed_errors, &codeword);
// codeword is now hopefully fixed
// If there are uncorrectable errors and the fixed proposal includes ones
// in bits 0-5 then probably the fix is not good and we discard it.
// Bits 0-5 should always be zero, if there is a problem it's on the other bits
// from 6 to 24.
if (uncorrectable_errors == 1 && (codeword & 0x3f) != 0) {
// discard, don't touch hex
} else {
// put it back into our hex format
adapt_to_word(codeword, hex, 6);
uncorrectable_errors = 0;
}
return uncorrectable_errors;
}
int decode_12(char* dodeca, const char* parity, int* fixed_errors)
{
unsigned int codeword = adapt_to_codeword(dodeca, 12, parity);
// codeword contains:
// bits 0-11: dodeca bits
// bits 12-23: golay (24,12) parity bits
// bits are ordered from left to right, so bit 0 is the most significant and bit 23 is the less.
int uncorrectable_errors = Golay24::decode(fixed_errors, &codeword);
// codeword is now hopefully fixed
// If there are uncorrectable errors and the fixed proposal includes ones
// in bits 0-5 then probably the fix is not good and we discard it.
// Bits 0-5 should always be zero, if there is a problem it's on the other bits
// from 6 to 24.
if (uncorrectable_errors == 1 && (codeword & 0x3f) != 0) {
// discard, don't touch hex
} else {
// put it back into our hex format
adapt_to_word(codeword, dodeca, 12);
uncorrectable_errors = 0;
}
return uncorrectable_errors;
}
void encode_6(char* hex, char* out_parity)
{
unsigned int data = adapt_from_word(hex, 6);
unsigned int codeword = Golay24::encode(data);
// Fill up the parity
for (unsigned int i=0, mask = 1<<12; i<12; i++, mask<<=1) {
out_parity[i] = (codeword & mask) != 0? 1 : 0;
}
}
void encode_12(char* dodeca, char* out_parity)
{
unsigned int data = adapt_from_word(dodeca, 12);
unsigned int codeword = Golay24::encode(data);
// Fill up the parity
for (unsigned int i=0, mask = 1<<12; i<12; i++, mask<<=1) {
out_parity[i] = (codeword & mask) != 0? 1 : 0;
}
}
};
#endif // GOLAY24_HPP_7a68240afda9406facf81fcad3851111

413
include/Hamming.hpp Normal file
View File

@@ -0,0 +1,413 @@
#ifndef HAMMING_HPP_e7123c3795b94d14b5774d5d8f016a04
#define HAMMING_HPP_e7123c3795b94d14b5774d5d8f016a04
#include <bitset>
#include <string>
#include <assert.h>
#include <iostream>
/**
* \brief Holds data for the Hamming_10_6_3 class. See next.
*/
class Hamming_10_6_3_data
{
public:
std::bitset<10> g0;
std::bitset<10> g1;
std::bitset<10> g2;
std::bitset<10> g3;
std::bitset<10> g4;
std::bitset<10> g5;
std::bitset<6> gt0;
std::bitset<6> gt1;
std::bitset<6> gt2;
std::bitset<6> gt3;
std::bitset<10> h0;
std::bitset<10> h1;
std::bitset<10> h2;
std::bitset<10> h3;
int bad_bit_table[16];
Hamming_10_6_3_data()
{
// G matrix come from the APCO 25 reference documentation:
g0 = std::bitset<10>(std::string("1000001110"));
g1 = std::bitset<10>(std::string("0100001101"));
g2 = std::bitset<10>(std::string("0010001011"));
g3 = std::bitset<10>(std::string("0001000111"));
g4 = std::bitset<10>(std::string("0000100011"));
g5 = std::bitset<10>(std::string("0000011100"));
gt0 = std::bitset<6>(std::string("111001"));
gt1 = std::bitset<6>(std::string("110101"));
gt2 = std::bitset<6>(std::string("101110"));
gt3 = std::bitset<6>(std::string("011110"));
// H matrix, calculated transposing G. See wikipedia.
h0 = std::bitset<10>(std::string("1110011000"));
h1 = std::bitset<10>(std::string("1101010100"));
h2 = std::bitset<10>(std::string("1011100010"));
h3 = std::bitset<10>(std::string("0111100001"));
// bad_bit_table indicates which is the bit to correct from the calculated syndromes.
// It's calculated from the previous matrices: See the left-most column of the matrix whose rows are
// h0,h1,h2 and h3. It's [1 1 1 0]. It means: when the syndrome value is the binary number 1110, the
// bit that is wrong is the bit 9 (the left-most column is for the bit 9). Another example with
// the right-most column: it's column is [0 0 0 1], so when the syndrome is 0001, the bit to fix is
// bit 0 (one of the parity bits).
bad_bit_table[ 1] = 0;
bad_bit_table[ 2] = 1;
bad_bit_table[ 4] = 2;
bad_bit_table[ 8] = 3;
bad_bit_table[12] = 4;
bad_bit_table[ 3] = 5;
bad_bit_table[ 7] = 6;
bad_bit_table[11] = 7;
bad_bit_table[13] = 8;
bad_bit_table[14] = 9;
// If the syndrome is zero, then the data is correct, no errors detected.
bad_bit_table[ 0] = -2;
// Incorrect values, if the syndrome gets one of this values, then we fail to correct the data.
bad_bit_table[ 5] = -1;
bad_bit_table[ 6] = -1;
bad_bit_table[ 9] = -1;
bad_bit_table[10] = -1;
bad_bit_table[15] = -1;
}
};
class Hamming_15_11_3_data
{
public:
std::bitset<15> g0;
std::bitset<15> g1;
std::bitset<15> g2;
std::bitset<15> g3;
std::bitset<15> g4;
std::bitset<15> g5;
std::bitset<15> g6;
std::bitset<15> g7;
std::bitset<15> g8;
std::bitset<15> g9;
std::bitset<15> g10;
std::bitset<11> gt0;
std::bitset<11> gt1;
std::bitset<11> gt2;
std::bitset<11> gt3;
std::bitset<15> h0;
std::bitset<15> h1;
std::bitset<15> h2;
std::bitset<15> h3;
int bad_bit_table[16];
Hamming_15_11_3_data()
{
// G matrix come from the APCO 25 reference documentation:
g0 = std::bitset<15>(std::string("100000000001111"));
g1 = std::bitset<15>(std::string("010000000001110"));
g2 = std::bitset<15>(std::string("001000000001101"));
g3 = std::bitset<15>(std::string("000100000001100"));
g4 = std::bitset<15>(std::string("000010000001011"));
g5 = std::bitset<15>(std::string("000001000001010"));
g6 = std::bitset<15>(std::string("000000100001001"));
g7 = std::bitset<15>(std::string("000000010000111"));
g8 = std::bitset<15>(std::string("000000001000110"));
g9 = std::bitset<15>(std::string("000000000100101"));
g10 = std::bitset<15>(std::string("000000000010011"));
gt0 = std::bitset<11>(std::string("11111110000"));
gt1 = std::bitset<11>(std::string("11110001110"));
gt2 = std::bitset<11>(std::string("11001101101"));
gt3 = std::bitset<11>(std::string("10101011011"));
// H matrix, calculated transposing G. See wikipedia.
h0 = std::bitset<15>(std::string("111111100001000"));
h1 = std::bitset<15>(std::string("111100011100100"));
h2 = std::bitset<15>(std::string("110011011010010"));
h3 = std::bitset<15>(std::string("101010110110001"));
// bad_bit_table indicates which is the bit to correct from the calculated syndromes.
// It's calculated from the previous matrices: See the left-most column of the matrix whose rows are
// h0,h1,h2 and h3. It's [1 1 1 0]. It means: when the syndrome value is the binary number 1110, the
// bit that is wrong is the bit 9 (the left-most column is for the bit 9). Another example with
// the right-most column: it's column is [0 0 0 1], so when the syndrome is 0001, the bit to fix is
// bit 0 (one of the parity bits).
bad_bit_table[ 1] = 0;
bad_bit_table[ 2] = 1;
bad_bit_table[ 4] = 2;
bad_bit_table[ 8] = 3;
bad_bit_table[12] = 4;
bad_bit_table[ 3] = 5;
bad_bit_table[ 7] = 6;
bad_bit_table[11] = 7;
bad_bit_table[13] = 8;
bad_bit_table[14] = 9;
// If the syndrome is zero, then the data is correct, no errors detected.
bad_bit_table[ 0] = -2;
// Incorrect values, if the syndrome gets one of this values, then we fail to correct the data.
bad_bit_table[ 5] = -1;
bad_bit_table[ 6] = -1;
bad_bit_table[ 9] = -1;
bad_bit_table[10] = -1;
bad_bit_table[15] = -1;
}
};
/**
* An interface used by the two implementations defined here.
*/
class Hamming_Inteface
{
public:
virtual ~Hamming_Inteface()
{
// Does nothing
}
/**
* \brief Decodes using the Hamming (10,6,3) algorithm a sequence of 10 bits expressed as an integer.
* \arg input The number to decode.
* \arg output A pointer to int where the decoded result is stored.
* \return Count of detected errors.
*/
virtual int decode(int input, int* output) = 0;
/**
* \brief Decodes using the Hamming (10,6,3) algorithm a sequence of 10 bits expressed as a pointer
* to six chars (data) and a pointer to four chars (parity). Altogether ten chars, each holding one bit.
* \arg hex A pointer to six chars. If error are detected the values are modified acordingly.
* \arg parity A pointer to four chars. Represent the parity of the data.
* \return Count of detected errors.
*/
virtual int decode(char* hex, char* parity) = 0;
virtual int encode(int input) = 0;
virtual void encode(char* hex, char* out_parity) = 0;
};
/**
* \brief Hamming (10,6,3) error correction implementation.
*/
class Hamming_10_6_3 : public Hamming_Inteface
{
private:
static Hamming_10_6_3_data data;
public:
/**
* \brief Decodes using the Hamming (10,6,3) algorithm a sequence of 10 bits expressed as a std::bitset.
* \arg input The sequence to decode.
* \return Count of detected errors.
*/
int decode(std::bitset<10>& input);
int decode(int input, int* output)
{
assert (input < 1024 && input >= 0);
std::bitset<10> bitset_input(input);
int error_count = decode(bitset_input);
if (error_count == 1) {
// t has possibly been modified
input = (int)bitset_input.to_ulong();
} else {
// both in case that there were no errors or there were irrecoverable errors we leave the data as
// it is
}
// discard the four parity bits at the end
*output = input >> 4;
return error_count;
}
int decode(char* hex, char* parity)
{
// Make a bitset from hex and parity
std::bitset<10> value;
// in the bitset 9 is the left-most and 0 is the right-most
for (unsigned int i=0; i<6; i++) {
value[9-i] = (hex[i] == 1)? true : false;
}
for (unsigned int i=0; i<4; i++) {
value[3-i] = (parity[i] == 1)? true : false;
}
int error_count = decode(value);
// Modify hex if needed
if (error_count == 1) {
for (unsigned int i=0; i<6; i++) {
hex[i] = value[9-i];
}
} else {
// No errors or irrecoverable errors, in both cases don't touch the input
}
return error_count;
}
int encode(std::bitset<6>& input);
int encode(int input)
{
assert (input < 64 && input >= 0);
std::bitset<6> bitset_input(input);
return encode(bitset_input);
}
void encode(char* hex, char* out_parity)
{
// Make a bitset from hex
std::bitset<6> value;
// in the bitset 5 is the left-most and 0 is the right-most
for (unsigned int i=0; i<6; i++) {
value[5-i] = (hex[i] == 1)? true : false;
}
int parity = encode(value);
// Put the calculated parity in the form of a char array
for (int i=3; i>=0; i--) {
out_parity[i] = parity & 1;
parity >>= 1;
}
}
};
/**
* \brief Stores data for the Hamming_10_6_3_TableImpl that comes next.
*/
class Hamming_10_6_3_TableImpl_data
{
public:
int fixed_values[1024];
int error_counts[1024];
int encode_parities[64];
/**
* \brief On initialization it builds a table to calculate every possible outcome (there are 1024) before
* hand. Uses Hamming_10_6_3 to build the table.
*/
Hamming_10_6_3_TableImpl_data()
{
Hamming_10_6_3 hamming;
// Build the tables
for (int i=0; i<1024; i++) {
int fixed;
int error_count = hamming.decode(i, &fixed);
fixed_values[i] = fixed;
error_counts[i] = error_count;
}
for (int i=0; i<64; i++) {
int parity = hamming.encode(i);
encode_parities[i] = parity;
}
}
};
/**
* \brief Hamming (10,6,3) error correction implementation.
*/
class Hamming_10_6_3_TableImpl : public Hamming_Inteface
{
private:
static Hamming_10_6_3_TableImpl_data data;
public:
int decode(int input, int* output);
static int convert_hex_to_int(char* hex)
{
// Make an int from hex
int value = 0;
// in the bitset 9 is the left-most and 0 is the right-most
for (unsigned int i=0; i<6; i++) {
assert(hex[i] == 0 || hex[i] == 1);
value <<= 1;
value |= hex[i];
}
return value;
}
static int convert_hex_parity_to_int(char* hex, char* parity)
{
// Make an int from hex and parity
int value = 0;
// in the bitset 9 is the left-most and 0 is the right-most
for (unsigned int i=0; i<6; i++) {
assert(hex[i] == 0 || hex[i] == 1);
value <<= 1;
value |= hex[i];
}
for (unsigned int i=0; i<4; i++) {
assert(parity[i] == 0 || parity[i] == 1);
value <<= 1;
value |= parity[i];
}
return value;
}
static void convert_int_to_hex(int value, char* hex)
{
unsigned int v = value;
for (unsigned int i=0; i<6; i++) {
hex[5-i] = v & 1;
v >>= 1;
}
}
int decode(char* hex, char* parity)
{
int value = convert_hex_parity_to_int(hex, parity);
int fixed;
int error_count = decode(value, &fixed);
// Modify hex if needed
if (error_count == 1) {
convert_int_to_hex(fixed, hex);
} else {
// No errors or irrecoverable errors, in both cases don't touch the input
}
return error_count;
}
int encode(int input);
void encode(char* hex, char* out_parity)
{
int value = convert_hex_to_int(hex);
int parity = encode(value);
// Put the calculated parity in the form of a char array
for (int i=3; i>=0; i--) {
out_parity[i] = parity & 1;
parity >>= 1;
}
}
};
#endif // HAMMING_HPP_e7123c3795b94d14b5774d5d8f016a04

670
include/ReedSolomon.hpp Normal file
View File

@@ -0,0 +1,670 @@
#ifndef REEDSOLOMON_HPP_b1405fdab6374ba2a4e65e8d45ec3d80
#define REEDSOLOMON_HPP_b1405fdab6374ba2a4e65e8d45ec3d80
/**
* Code taken and adapted from www.eccpage.com/rs.c
* Credit goes to Mr Simon Rockliff.
*
* Tried before with the implementation from ITPP library but couldn't make it produce the same outputs
* expected from the P25 transmissions that I have tested. This implementation does work.
*/
/* This program is an encoder/decoder for Reed-Solomon codes. Encoding is in
systematic form, decoding via the Berlekamp iterative algorithm.
In the present form , the constants mm, nn, tt, and kk=nn-2tt must be
specified (the double letters are used simply to avoid clashes with
other n,k,t used in other programs into which this was incorporated!)
Also, the irreducible polynomial used to generate GF(2**mm) must also be
entered -- these can be found in Lin and Costello, and also Clark and Cain.
The representation of the elements of GF(2**m) is either in index form,
where the number is the power of the primitive element alpha, which is
convenient for multiplication (add the powers modulo 2**m-1) or in
polynomial form, where the bits represent the coefficients of the
polynomial representation of the number, which is the most convenient form
for addition. The two forms are swapped between via lookup tables.
This leads to fairly messy looking expressions, but unfortunately, there
is no easy alternative when working with Galois arithmetic.
The code is not written in the most elegant way, but to the best
of my knowledge, (no absolute guarantees!), it works.
However, when including it into a simulation program, you may want to do
some conversion of global variables (used here because I am lazy!) to
local variables where appropriate, and passing parameters (eg array
addresses) to the functions may be a sensible move to reduce the number
of global variables and thus decrease the chance of a bug being introduced.
This program does not handle erasures at present, but should not be hard
to adapt to do this, as it is just an adjustment to the Berlekamp-Massey
algorithm. It also does not attempt to decode past the BCH bound -- see
Blahut "Theory and practice of error control codes" for how to do this.
Simon Rockliff, University of Adelaide 21/9/89
26/6/91 Slight modifications to remove a compiler dependent bug which hadn't
previously surfaced. A few extra comments added for clarity.
Appears to all work fine, ready for posting to net!
Notice
--------
This program may be freely modified and/or given to whoever wants it.
A condition of such distribution is that the author's contribution be
acknowledged by his name being left in the comments heading the program,
however no responsibility is accepted for any financial or other loss which
may result from some unforseen errors or malfunctioning of the program
during use.
Simon Rockliff, 26th June 1991
*/
#include <math.h>
#include <stdio.h>
template <int TT>
class ReedSolomon_63
{
private:
static const int MM = 6; /* RS code over GF(2**mm) */
static const int NN = 63; /* nn=2**mm -1 length of codeword */
//int tt; /* number of errors that can be corrected */
//int kk; /* kk = nn-2*tt */
static const int KK = NN-2*TT;
// distance = nn-kk+1 = 2*tt+1
int* alpha_to;
int* index_of;
int* gg;
void generate_gf(int* generator_polinomial)
/* generate GF(2**mm) from the irreducible polynomial p(X) in pp[0]..pp[mm]
lookup tables: index->polynomial form alpha_to[] contains j=alpha**i;
polynomial form -> index form index_of[j=alpha**i] = i
alpha=2 is the primitive element of GF(2**mm)
*/
{
register int i, mask;
mask = 1;
alpha_to[MM] = 0;
for (i = 0; i < MM; i++) {
alpha_to[i] = mask;
index_of[alpha_to[i]] = i;
if (generator_polinomial[i] != 0)
alpha_to[MM] ^= mask;
mask <<= 1;
}
index_of[alpha_to[MM]] = MM;
mask >>= 1;
for (i = MM + 1; i < NN; i++) {
if (alpha_to[i - 1] >= mask)
alpha_to[i] = alpha_to[MM] ^ ((alpha_to[i - 1] ^ mask) << 1);
else
alpha_to[i] = alpha_to[i - 1] << 1;
index_of[alpha_to[i]] = i;
}
index_of[0] = -1;
}
void gen_poly()
/* Obtain the generator polynomial of the tt-error correcting, length
nn=(2**mm -1) Reed Solomon code from the product of (X+alpha**i), i=1..2*tt
*/
{
register int i, j;
gg[0] = 2; /* primitive element alpha = 2 for GF(2**mm) */
gg[1] = 1; /* g(x) = (X+alpha) initially */
for (i = 2; i <= NN - KK; i++) {
gg[i] = 1;
for (j = i - 1; j > 0; j--)
if (gg[j] != 0)
gg[j] = gg[j - 1] ^ alpha_to[(index_of[gg[j]] + i) % NN];
else
gg[j] = gg[j - 1];
gg[0] = alpha_to[(index_of[gg[0]] + i) % NN]; /* gg[0] can never be zero */
}
/* convert gg[] to index form for quicker encoding */
for (i = 0; i <= NN - KK; i++)
gg[i] = index_of[gg[i]];
}
public:
ReedSolomon_63()
{
alpha_to = new int[NN + 1];
index_of = new int[NN + 1];
gg = new int[NN - KK + 1];
// Polynom used in P25 is alpha**6+alpha+1
int generator_polinomial[] = { 1, 1, 0, 0, 0, 0, 1 }; /* specify irreducible polynomial coeffts */
generate_gf(generator_polinomial);
gen_poly();
}
virtual ~ReedSolomon_63()
{
delete[] gg;
delete[] index_of;
delete[] alpha_to;
}
void encode(const int* data, int* bb)
/* take the string of symbols in data[i], i=0..(k-1) and encode systematically
to produce 2*tt parity symbols in bb[0]..bb[2*tt-1]
data[] is input and bb[] is output in polynomial form.
Encoding is done by using a feedback shift register with appropriate
connections specified by the elements of gg[], which was generated above.
Codeword is c(X) = data(X)*X**(nn-kk)+ b(X) */
{
register int i, j;
int feedback;
for (i = 0; i < NN - KK; i++)
bb[i] = 0;
for (i = KK - 1; i >= 0; i--) {
feedback = index_of[data[i] ^ bb[NN - KK - 1]];
if (feedback != -1) {
for (j = NN - KK - 1; j > 0; j--)
if (gg[j] != -1)
bb[j] = bb[j - 1] ^ alpha_to[(gg[j] + feedback) % NN];
else
bb[j] = bb[j - 1];
bb[0] = alpha_to[(gg[0] + feedback) % NN];
} else {
for (j = NN - KK - 1; j > 0; j--)
bb[j] = bb[j - 1];
bb[0] = 0;
}
}
}
int decode(const int* input, int* recd)
/* assume we have received bits grouped into mm-bit symbols in recd[i],
i=0..(nn-1), and recd[i] is polynomial form.
We first compute the 2*tt syndromes by substituting alpha**i into rec(X) and
evaluating, storing the syndromes in s[i], i=1..2tt (leave s[0] zero) .
Then we use the Berlekamp iteration to find the error location polynomial
elp[i]. If the degree of the elp is >tt, we cannot correct all the errors
and hence just put out the information symbols uncorrected. If the degree of
elp is <=tt, we substitute alpha**i , i=1..n into the elp to get the roots,
hence the inverse roots, the error location numbers. If the number of errors
located does not equal the degree of the elp, we have more than tt errors
and cannot correct them. Otherwise, we then solve for the error value at
the error location and correct the error. The procedure is that found in
Lin and Costello. For the cases where the number of errors is known to be too
large to correct, the information symbols as received are output (the
advantage of systematic encoding is that hopefully some of the information
symbols will be okay and that if we are in luck, the errors are in the
parity part of the transmitted codeword). Of course, these insoluble cases
can be returned as error flags to the calling routine if desired. */
{
register int i, j, u, q;
int elp[NN - KK + 2][NN - KK], d[NN - KK + 2], l[NN - KK + 2], u_lu[NN - KK
+ 2], s[NN - KK + 1];
int count = 0, syn_error = 0, root[TT], loc[TT], z[TT + 1], err[NN], reg[TT
+ 1];
int irrecoverable_error = 0;
for (int i = 0; i < NN; i++)
recd[i] = index_of[input[i]]; /* put recd[i] into index form (ie as powers of alpha) */
/* first form the syndromes */
for (i = 1; i <= NN - KK; i++) {
s[i] = 0;
for (j = 0; j < NN; j++)
if (recd[j] != -1)
s[i] ^= alpha_to[(recd[j] + i * j) % NN]; /* recd[j] in index form */
/* convert syndrome from polynomial form to index form */
if (s[i] != 0)
syn_error = 1; /* set flag if non-zero syndrome => error */
s[i] = index_of[s[i]];
}
if (syn_error) /* if errors, try and correct */
{
/* compute the error location polynomial via the Berlekamp iterative algorithm,
following the terminology of Lin and Costello : d[u] is the 'mu'th
discrepancy, where u='mu'+1 and 'mu' (the Greek letter!) is the step number
ranging from -1 to 2*tt (see L&C), l[u] is the
degree of the elp at that step, and u_l[u] is the difference between the
step number and the degree of the elp.
*/
/* initialise table entries */
d[0] = 0; /* index form */
d[1] = s[1]; /* index form */
elp[0][0] = 0; /* index form */
elp[1][0] = 1; /* polynomial form */
for (i = 1; i < NN - KK; i++) {
elp[0][i] = -1; /* index form */
elp[1][i] = 0; /* polynomial form */
}
l[0] = 0;
l[1] = 0;
u_lu[0] = -1;
u_lu[1] = 0;
u = 0;
do {
u++;
if (d[u] == -1) {
l[u + 1] = l[u];
for (i = 0; i <= l[u]; i++) {
elp[u + 1][i] = elp[u][i];
elp[u][i] = index_of[elp[u][i]];
}
} else
/* search for words with greatest u_lu[q] for which d[q]!=0 */
{
q = u - 1;
while ((d[q] == -1) && (q > 0))
q--;
/* have found first non-zero d[q] */
if (q > 0) {
j = q;
do {
j--;
if ((d[j] != -1) && (u_lu[q] < u_lu[j]))
q = j;
} while (j > 0);
};
/* have now found q such that d[u]!=0 and u_lu[q] is maximum */
/* store degree of new elp polynomial */
if (l[u] > l[q] + u - q)
l[u + 1] = l[u];
else
l[u + 1] = l[q] + u - q;
/* form new elp(x) */
for (i = 0; i < NN - KK; i++)
elp[u + 1][i] = 0;
for (i = 0; i <= l[q]; i++)
if (elp[q][i] != -1)
elp[u + 1][i + u - q] = alpha_to[(d[u] + NN - d[q]
+ elp[q][i]) % NN];
for (i = 0; i <= l[u]; i++) {
elp[u + 1][i] ^= elp[u][i];
elp[u][i] = index_of[elp[u][i]]; /*convert old elp value to index*/
}
}
u_lu[u + 1] = u - l[u + 1];
/* form (u+1)th discrepancy */
if (u < NN - KK) /* no discrepancy computed on last iteration */
{
if (s[u + 1] != -1)
d[u + 1] = alpha_to[s[u + 1]];
else
d[u + 1] = 0;
for (i = 1; i <= l[u + 1]; i++)
if ((s[u + 1 - i] != -1) && (elp[u + 1][i] != 0))
d[u + 1] ^= alpha_to[(s[u + 1 - i]
+ index_of[elp[u + 1][i]]) % NN];
d[u + 1] = index_of[d[u + 1]]; /* put d[u+1] into index form */
}
} while ((u < NN - KK) && (l[u + 1] <= TT));
u++;
if (l[u] <= TT) /* can correct error */
{
/* put elp into index form */
for (i = 0; i <= l[u]; i++)
elp[u][i] = index_of[elp[u][i]];
/* find roots of the error location polynomial */
for (i = 1; i <= l[u]; i++)
reg[i] = elp[u][i];
count = 0;
for (i = 1; i <= NN; i++) {
q = 1;
for (j = 1; j <= l[u]; j++)
if (reg[j] != -1) {
reg[j] = (reg[j] + j) % NN;
q ^= alpha_to[reg[j]];
};
if (!q) /* store root and error location number indices */
{
root[count] = i;
loc[count] = NN - i;
count++;
};
};
if (count == l[u]) /* no. roots = degree of elp hence <= tt errors */
{
/* form polynomial z(x) */
for (i = 1; i <= l[u]; i++) /* Z[0] = 1 always - do not need */
{
if ((s[i] != -1) && (elp[u][i] != -1))
z[i] = alpha_to[s[i]] ^ alpha_to[elp[u][i]];
else if ((s[i] != -1) && (elp[u][i] == -1))
z[i] = alpha_to[s[i]];
else if ((s[i] == -1) && (elp[u][i] != -1))
z[i] = alpha_to[elp[u][i]];
else
z[i] = 0;
for (j = 1; j < i; j++)
if ((s[j] != -1) && (elp[u][i - j] != -1))
z[i] ^= alpha_to[(elp[u][i - j] + s[j]) % NN];
z[i] = index_of[z[i]]; /* put into index form */
};
/* evaluate errors at locations given by error location numbers loc[i] */
for (i = 0; i < NN; i++) {
err[i] = 0;
if (recd[i] != -1) /* convert recd[] to polynomial form */
recd[i] = alpha_to[recd[i]];
else
recd[i] = 0;
}
for (i = 0; i < l[u]; i++) /* compute numerator of error term first */
{
err[loc[i]] = 1; /* accounts for z[0] */
for (j = 1; j <= l[u]; j++)
if (z[j] != -1)
err[loc[i]] ^= alpha_to[(z[j] + j * root[i]) % NN];
if (err[loc[i]] != 0) {
err[loc[i]] = index_of[err[loc[i]]];
q = 0; /* form denominator of error term */
for (j = 0; j < l[u]; j++)
if (j != i)
q += index_of[1
^ alpha_to[(loc[j] + root[i]) % NN]];
q = q % NN;
err[loc[i]] = alpha_to[(err[loc[i]] - q + NN) % NN];
recd[loc[i]] ^= err[loc[i]]; /*recd[i] must be in polynomial form */
}
}
} else {
/* no. roots != degree of elp => >tt errors and cannot solve */
irrecoverable_error = 1;
}
} else {
/* elp has degree >tt hence cannot solve */
irrecoverable_error = 1;
}
} else {
/* no non-zero syndromes => no errors: output received codeword */
for (i = 0; i < NN; i++)
if (recd[i] != -1) /* convert recd[] to polynomial form */
recd[i] = alpha_to[recd[i]];
else
recd[i] = 0;
}
if (irrecoverable_error) {
for (i = 0; i < NN; i++) /* could return error flag if desired */
if (recd[i] != -1) /* convert recd[] to polynomial form */
recd[i] = alpha_to[recd[i]];
else
recd[i] = 0; /* just output received codeword as is */
}
return irrecoverable_error;
}
protected:
int bin_to_hex(const char* input)
{
int output = ((input[0] != 0)? 32 : 0) |
((input[1] != 0)? 16 : 0) |
((input[2] != 0)? 8 : 0) |
((input[3] != 0)? 4 : 0) |
((input[4] != 0)? 2 : 0) |
((input[5] != 0)? 1 : 0);
return output;
}
void hex_to_bin(int input, char* output)
{
output[0] = ((input & 32) != 0)? 1 : 0;
output[1] = ((input & 16) != 0)? 1 : 0;
output[2] = ((input & 8) != 0)? 1 : 0;
output[3] = ((input & 4) != 0)? 1 : 0;
output[4] = ((input & 2) != 0)? 1 : 0;
output[5] = ((input & 1) != 0)? 1 : 0;
}
};
/**
* Convenience class that does a Reed-Solomon (36,20,17) error correction adapting input and output to
* the DSD data format: hex words packed as char arrays.
*/
class DSDReedSolomon_36_20_17 : public ReedSolomon_63<8>
{
public:
// tt = (dd-1)/2
// dd = 17 --> tt = 8
DSDReedSolomon_36_20_17() : ReedSolomon_63<8>()
{
// Does nothing
}
/**
* Does a Reed-Solomon decode adapting the input and output to the expected DSD data format.
* \param hex_data Data packed bits, originally a char[20][6], so containing 20 hex works, each char
* is a bit. Bits are corrected in place.
* \param hex_parity Parity packed bits, originally a char[16][6], 16 hex words.
* \return 1 if irrecoverable errors have been detected, 0 otherwise.
*/
int decode(char* hex_data, const char* hex_parity)
{
int input[63];
int output[63];
// First put the parity data, 16 hex words
for(unsigned int i=0; i<16; i++) {
input[i] = bin_to_hex(hex_parity + i*6);
}
// Then the 20 hex words of data
for(unsigned int i=16; i<16+20; i++) {
input[i] = bin_to_hex(hex_data + (i-16)*6);
}
// Fill up with zeros to complete the 47 expected hex words of data
for(unsigned int i=16+20; i<63; i++) {
input[i] = 0;
}
// Now we can call decode on the base class
int irrecoverable_errors = ReedSolomon_63<8>::decode(input, output);
// Convert it back to binary and put it into hex_data. If decode failed we should have
// the input unchanged.
for(unsigned int i=16; i<16+20; i++) {
hex_to_bin(output[i], hex_data + (i-16)*6);
}
return irrecoverable_errors;
}
void encode(const char* hex_data, char* out_hex_parity)
{
int input[47];
int output[63];
// Put the 20 hex words of data
for(unsigned int i=0; i<20; i++) {
input[i] = bin_to_hex(hex_data + i*6);
}
// Fill up with zeros to complete the 47 expected hex words of data
for(unsigned int i=20; i<47; i++) {
input[i] = 0;
}
// Now we can call encode on the base class
ReedSolomon_63<8>::encode(input, output);
// Convert it back to binary form and put it into the parity
for(unsigned int i=0; i<16; i++) {
hex_to_bin(output[i], out_hex_parity + i*6);
}
}
};
/**
* Convenience class that does a Reed-Solomon (24,12,13) error correction adapting input and output to
* the DSD data format: hex words packed as char arrays.
*/
class DSDReedSolomon_24_12_13 : public ReedSolomon_63<6>
{
public:
// tt = (dd-1)/2
// dd = 13 --> tt = 6
DSDReedSolomon_24_12_13() : ReedSolomon_63<6>()
{
// Does nothing
}
/**
* Does a Reed-Solomon decode adapting the input and output to the expected DSD data format.
* \param hex_data Data packed bits, originally a char[12][6], so containing 12 hex works, each char
* is a bit. Bits are corrected in place.
* \param hex_parity Parity packed bits, originally a char[12][6], 12 hex words.
* \return 1 if irrecoverable errors have been detected, 0 otherwise.
*/
int decode(char* hex_data, const char* hex_parity)
{
int input[63];
int output[63];
// First put the parity data, 12 hex words
for(unsigned int i=0; i<12; i++) {
input[i] = bin_to_hex(hex_parity + i*6);
}
// Then the 12 hex words of data
for(unsigned int i=12; i<12+12; i++) {
input[i] = bin_to_hex(hex_data + (i-12)*6);
}
// Fill up with zeros to complete the 51 expected hex words of data
for(unsigned int i=12+12; i<63; i++) {
input[i] = 0;
}
// Now we can call decode on the base class
int irrecoverable_errors = ReedSolomon_63<6>::decode(input, output);
// Convert it back to binary and put it into hex_data. If decode failed we should have
// the input unchanged.
for(unsigned int i=12; i<12+12; i++) {
hex_to_bin(output[i], hex_data + (i-12)*6);
}
return irrecoverable_errors;
}
void encode(const char* hex_data, char* out_hex_parity)
{
int input[51];
int output[63];
// Put the 12 hex words of data
for(unsigned int i=0; i<12; i++) {
input[i] = bin_to_hex(hex_data + i*6);
}
// Fill up with zeros to complete the 51 expected hex words of data
for(unsigned int i=12; i<51; i++) {
input[i] = 0;
}
// Now we can call encode on the base class
ReedSolomon_63<6>::encode(input, output);
// Convert it back to binary form and put it into the parity
for(unsigned int i=0; i<12; i++) {
hex_to_bin(output[i], out_hex_parity + i*6);
}
}
};
/**
* Convenience class that does a Reed-Solomon (24,16,9) error correction adapting input and output to
* the DSD data format: hex words packed as char arrays.
*/
class DSDReedSolomon_24_16_9 : public ReedSolomon_63<4>
{
public:
// tt = (dd-1)/2
// dd = 9 --> tt = 4
DSDReedSolomon_24_16_9() : ReedSolomon_63<4>()
{
// Does nothing
}
/**
* Does a Reed-Solomon decode adapting the input and output to the expected DSD data format.
* \param hex_data Data packed bits, originally a char[16][6], so containing 16 hex works, each char
* is a bit. Bits are corrected in place.
* \param hex_parity Parity packed bits, originally a char[8][6], 8 hex words.
* \return 1 if irrecoverable errors have been detected, 0 otherwise.
*/
int decode(char* hex_data, const char* hex_parity)
{
int input[63];
int output[63];
// First put the parity data, 8 hex words
for(unsigned int i=0; i<8; i++) {
input[i] = bin_to_hex(hex_parity + i*6);
}
// Then the 16 hex words of data
for(unsigned int i=8; i<8+16; i++) {
input[i] = bin_to_hex(hex_data + (i-8)*6);
}
// Fill up with zeros to complete the 55 expected hex words of data
for(unsigned int i=8+16; i<63; i++) {
input[i] = 0;
}
// Now we can call decode on the base class
int irrecoverable_errors = ReedSolomon_63<4>::decode(input, output);
// Convert it back to binary and put it into hex_data. If decode failed we should have
// the input unchanged.
for(unsigned int i=8; i<8+16; i++) {
hex_to_bin(output[i], hex_data + (i-8)*6);
}
return irrecoverable_errors;
}
void encode(const char* hex_data, char* out_hex_parity)
{
int input[55];
int output[63];
// Put the 16 hex words of data
for(unsigned int i=0; i<16; i++) {
input[i] = bin_to_hex(hex_data + i*6);
}
// Fill up with zeros to complete the 55 expected hex words of data
for(unsigned int i=16; i<55; i++) {
input[i] = 0;
}
// Now we can call encode on the base class
ReedSolomon_63<4>::encode(input, output);
// Convert it back to binary form and put it into the parity
for(unsigned int i=0; i<8; i++) {
hex_to_bin(output[i], out_hex_parity + i*6);
}
}
};
#endif // REEDSOLOMON_HPP_b1405fdab6374ba2a4e65e8d45ec3d80

298
include/descramble.h Normal file
View File

@@ -0,0 +1,298 @@
/* descramble.h */
// Functions for processing the radio-header:
// descramble
// deinterleave
// FECdecoder
// (C) 2011 Jonathan Naylor G4KLX
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
// This code was originally written by JOnathan Naylor, G4KLX, as part
// of the "pcrepeatercontroller" project
// More info:
// http://groups.yahoo.com/group/pcrepeatercontroller
// Changes:
// Convert C++ to C
// Version 20111106: initial release
#include <stdio.h>
#include <string.h>
// function traceBack
int traceBack (int * out, int * m_pathMemory0, int * m_pathMemory1, int * m_pathMemory2, int * m_pathMemory3) {
enum FEC_STATE { S0, S1, S2, S3 } state;
int loop;
int length=0;
state=S0;
for (loop=329; loop >= 0; loop--, length++) {
switch (state) {
case S0: // if state S0
if (m_pathMemory0[loop]) {
state = S2; // lower path
} else {
state = S0; // upper path
}; // end else - if
out[loop]=0;
break;
case S1: // if state S1
if (m_pathMemory1[loop]) {
state = S2; // lower path
} else {
state = S0; // upper path
}; // end else - if
out[loop]=1;
break;
case S2: // if state S2
if (m_pathMemory2[loop]) {
state = S3; // lower path
} else {
state = S1; // upper path
}; // end else - if
out[loop]=0;
break;
case S3: // if state S3
if (m_pathMemory3[loop]) {
state = S3; // lower path
} else {
state = S1; // upper path
}; // end else - if
out[loop]=1;
break;
}; // end switch
}; // end for
return(length);
}; // end function
// function viterbiDecode
void viterbiDecode (int n, int *data, int *m_pathMemory0, int *m_pathMemory1, int *m_pathMemory2, int *m_pathMemory3, int *m_pathMetric) {
int tempMetric[4];
int metric[8];
int loop;
int m1;
int m2;
metric[0]=(data[1]^0)+(data[0]^0);
metric[1]=(data[1]^1)+(data[0]^1);
metric[2]=(data[1]^1)+(data[0]^0);
metric[3]=(data[1]^0)+(data[0]^1);
metric[4]=(data[1]^1)+(data[0]^1);
metric[5]=(data[1]^0)+(data[0]^0);
metric[6]=(data[1]^0)+(data[0]^1);
metric[7]=(data[1]^1)+(data[0]^0);
// Pres. state = S0, Prev. state = S0 & S2
m1=metric[0]+m_pathMetric[0];
m2=metric[4]+m_pathMetric[2];
if (m1<m2) {
m_pathMemory0[n]=0;
tempMetric[0]=m1;
} else {
m_pathMemory0[n]=1;
tempMetric[0]=m2;
}; // end else - if
// Pres. state = S1, Prev. state = S0 & S2
m1=metric[1]+m_pathMetric[0];
m2=metric[5]+m_pathMetric[2];
if (m1<m2) {
m_pathMemory1[n]=0;
tempMetric[1]=m1;
} else {
m_pathMemory1[n]=1;
tempMetric[1]=m2;
}; // end else - if
// Pres. state = S2, Prev. state = S2 & S3
m1=metric[2]+m_pathMetric[1];
m2=metric[6]+m_pathMetric[3];
if (m1<m2) {
m_pathMemory2[n]=0;
tempMetric[2]=m1;
} else {
m_pathMemory2[n]=1;
tempMetric[2]=m2;
}
// Pres. state = S3, Prev. state = S1 & S3
m1=metric[3]+m_pathMetric[1];
m2=metric[7]+m_pathMetric[3];
if (m1 < m2) {
m_pathMemory3[n]=0;
tempMetric[3]=m1;
} else {
m_pathMemory3[n]=1;
tempMetric[3]=m2;
}; // end else - if
for (loop=0;loop<4;loop++) {
m_pathMetric[loop]=tempMetric[loop];
}; // end for
}; // end function ViterbiDecode
// function FECdecoder
// returns outlen
int FECdecoder (int * in, int * out) {
int outLen;
int m_pathMemory0[330];
int m_pathMemory1[330];
int m_pathMemory2[330];
int m_pathMemory3[330];
int m_pathMetric[4];
int loop,loop2;
int n=0;
memset(m_pathMemory0,0,330*sizeof(int));
memset(m_pathMemory1,0,330*sizeof(int));
memset(m_pathMemory2,0,330*sizeof(int));
memset(m_pathMemory3,0,330*sizeof(int));
for (loop=0;loop<4;loop++) {
m_pathMetric[loop]=0;
}; // end for
for (loop2=0;loop2<660;loop2+=2, n++) {
int data[2];
if (in[loop2]) {
data[1]=1;
} else {
data[1]=0;
}; // end else - if
if (in[loop2+1]) {
data[0]=1;
} else {
data[0]=0;
}; // end else - if
viterbiDecode(n, data, m_pathMemory0, m_pathMemory1, m_pathMemory2, m_pathMemory3, m_pathMetric);
}; // end for
outLen=traceBack(out, m_pathMemory0, m_pathMemory1, m_pathMemory2, m_pathMemory3);
// Swap endian-ness
// code removed (done converting bits into octets), done in main program
//for (loop=0;loop<330;loop+=8) {
// int temp;
// temp=out[loop];out[loop]=out[loop+7];out[loop+7]=temp;
// temp=out[loop+1];out[loop+1]=out[loop+6];out[loop+6]=temp;
// temp=out[loop+2];out[loop+2]=out[loop+5];out[loop+5]=temp;
// temp=out[loop+3];out[loop+3]=out[loop+4];out[loop+4]=temp;
//}
return(outLen);
}; // end function FECdecoder
// function deinterleave
void deinterleave (int * in, int * out) {
int k=0;
int loop=0;
// function starts here
// init vars
k=0;
for (loop=0;loop<660;loop++) {
out[k]=in[loop];
k += 24;
if (k >= 672) {
k -= 671;
} else if (k >= 660) {
k -= 647;
}; // end elsif - if
}; // end for
}; // end function deinterleave
/// function scramble
void scramble (int * in,int * out) {
static const int SCRAMBLER_TABLE_BITS[] = {
0,0,0,0,1,1,1,0,1,1,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,
0,0,1,0,0,1,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,
1,1,0,1,0,1,0,0,1,1,1,0,0,1,1,1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,0,
1,1,1,1,1,0,1,0,0,1,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,0,
0,0,0,1,1,1,0,1,1,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,
0,1,0,0,1,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,1,
1,0,1,0,1,0,0,1,1,1,0,0,1,1,1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,0,1,
1,1,1,1,0,1,0,0,1,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,
0,0,1,1,1,0,1,1,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,
1,0,0,1,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,1,1,
0,1,0,1,0,0,1,1,1,0,0,1,1,1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,0,1,1,
1,1,1,0,1,0,0,1,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,
0,1,1,1,0,1,1,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,1,
0,0,1,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,1,1,0,
1,0,1,0,0,1,1,1,0,0,1,1,1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,0,1,1,1,
1,1,0,1,0,0,1,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,
1,1,1,0,1,1,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,
0,1,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,1,1,0,1,
0,1,0,0,1,1,1,0,0,1,1,1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,0,1,1,1,1,
1,0,1,0,0,1,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,
1,1,0,1,1,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,
1,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,1,1,0,1,0,
1,0,0,1,1,1,0,0,1,1,1,1,0,1,1,0};
const int SCRAMBLER_TABLE_BITS_LENGTH=720;
int loop=0;
int m_count=0;
for (loop=0; loop < 660; loop++) {
out[loop] = in[loop] ^ SCRAMBLER_TABLE_BITS[m_count++];
if (m_count >= SCRAMBLER_TABLE_BITS_LENGTH) {
m_count = 0U;
}; // end if
}; // end for
}; // end function scramble

76
include/dpmr_const.h Normal file
View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
//#ifdef __cplusplus
//extern "C" {
//#endif
#ifndef _MAIN
extern const int dPmrW[36];
extern const int dPmrX[36];
extern const int dPmrY[36];
extern const int dPmrZ[36];
//#else
/*
* dPMR AMBE interleave schedule
*/
// bit 1
const int dPmrW[36] = {
0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 2,
0, 2, 0, 2, 0, 2,
0, 2, 0, 2, 0, 2
};
const int dPmrX[36] = {
23, 10, 22, 9, 21, 8,
20, 7, 19, 6, 18, 5,
17, 4, 16, 3, 15, 2,
14, 1, 13, 0, 12, 10,
11, 9, 10, 8, 9, 7,
8, 6, 7, 5, 6, 4
};
// bit 0
const int dPmrY[36] = {
0, 2, 0, 2, 0, 2,
0, 2, 0, 3, 0, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3
};
const int dPmrZ[36] = {
5, 3, 4, 2, 3, 1,
2, 0, 1, 13, 0, 12,
22, 11, 21, 10, 20, 9,
19, 8, 18, 7, 17, 6,
16, 5, 15, 4, 14, 3,
13, 2, 12, 1, 11, 0
};
#endif
//#ifdef __cplusplus
//}
//#endif

964
include/dsd.h Normal file
View File

@@ -0,0 +1,964 @@
#ifndef DSD_H
#define DSD_H
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
//pretty pretty colors
#define KNRM "\x1B[0m"
#define KRED "\x1B[31m"
#define KGRN "\x1B[32m"
#define KYEL "\x1B[33m"
#define KBLU "\x1B[34m"
#define KMAG "\x1B[35m"
#define KCYN "\x1B[36m"
#define KWHT "\x1B[37m"
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
//#define __USE_XOPEN //compiler warning on this, need to rewrite dsd_file so it doesn't use strptime
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <mbelib.h>
#include <sndfile.h>
#include "p25p1_heuristics.h"
#include <pulse/simple.h> //PULSE AUDIO
#include <pulse/error.h> //PULSE AUDIO
#define SAMPLE_RATE_IN 48000 //48000
#define SAMPLE_RATE_OUT 8000 //8000,
#ifdef USE_RTLSDR
#include <rtl-sdr.h>
#endif
#include <locale.h>
#include <ncurses.h>
static volatile int exitflag;
typedef struct
{
uint8_t RFChannelType;
uint8_t FunctionnalChannelType;
uint8_t Option;
uint8_t Direction;
uint8_t CompleteLich; /* 7 bits of LICH without parity */
uint8_t PreviousCompleteLich; /* 7 bits of previous LICH without parity */
} NxdnLich_t;
typedef struct
{
uint8_t StructureField;
uint8_t RAN;
uint8_t Data[18];
uint8_t CrcIsGood;
} NxdnSacchRawPart_t;
typedef struct
{
uint8_t Data[80];
uint8_t CrcIsGood;
} NxdnFacch1RawPart_t;
typedef struct
{
uint8_t Data[184];
uint8_t CrcIsGood;
} NxdnFacch2RawPart_t;
typedef struct
{
uint8_t F1;
uint8_t F2;
uint8_t MessageType;
/****************************/
/***** VCALL parameters *****/
/****************************/
uint8_t CCOption;
uint8_t CallType;
uint8_t VoiceCallOption;
uint16_t SourceUnitID;
uint16_t DestinationID; /* May be a Group ID or a Unit ID */
uint8_t CipherType;
uint8_t KeyID;
uint8_t VCallCrcIsGood;
/*******************************/
/***** VCALL_IV parameters *****/
/*******************************/
uint8_t IV[8];
uint8_t VCallIvCrcIsGood;
/*****************************/
/***** Custom parameters *****/
/*****************************/
/* Specifies if the "CipherType" and the "KeyID" parameter are valid
* 1 = Valid ; 0 = CRC error */
uint8_t CipherParameterValidity;
/* Used on DES and AES encrypted frames */
uint8_t PartOfCurrentEncryptedFrame; /* Could be 1 or 2 */
uint8_t PartOfNextEncryptedFrame; /* Could be 1 or 2 */
uint8_t CurrentIVComputed[8];
uint8_t NextIVComputed[8];
} NxdnElementsContent_t;
//Read Solomon (12,9) constant
#define RS_12_9_DATASIZE 9
#define RS_12_9_CHECKSUMSIZE 3
//Read Solomon (12,9) struct
typedef struct
{
uint8_t data[RS_12_9_DATASIZE+RS_12_9_CHECKSUMSIZE];
} rs_12_9_codeword_t;
// Maximum degree of various polynomials.
#define RS_12_9_POLY_MAXDEG (RS_12_9_CHECKSUMSIZE*2)
typedef struct
{
uint8_t data[RS_12_9_POLY_MAXDEG];
} rs_12_9_poly_t;
#define RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND 0
#define RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CORRECTED 1
#define RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CANT_BE_CORRECTED 2
typedef uint8_t rs_12_9_correct_errors_result_t;
typedef struct
{
uint8_t bytes[3];
} rs_12_9_checksum_t;
typedef struct
{
char VoiceFrame[3][72]; /* 3 x 72 bit of voice per time slot frame */
char Sync[48]; /* 48 bit of data or SYNC per time slot frame */
}TimeSlotRawVoiceFrame_t;
typedef struct
{
char DeInterleavedVoiceSample[3][4][24]; /* 3 x (4 x 24) deinterleaved bit of voice per time slot frame */
}TimeSlotDeinterleavedVoiceFrame_t;
typedef struct
{
int errs1[3]; /* 3 x errors #1 computed when demodulate the AMBE voice bit of the frame */
int errs2[3]; /* 3 x errors #2 computed when demodulate the AMBE voice bit of the frame */
char AmbeBit[3][49]; /* 3 x 49 bit of AMBE voice of the frame */
}TimeSlotAmbeVoiceFrame_t;
typedef struct
{
unsigned int ProtectFlag;
unsigned int Reserved;
unsigned int FullLinkControlOpcode;
unsigned int FeatureSetID;
unsigned int ServiceOptions;
unsigned int GroupAddress; /* Destination ID or TG (in Air Interface format) */
unsigned int SourceAddress; /* Source ID (in Air Interface format) */
unsigned int DataValidity; /* 0 = All Full LC data are incorrect ; 1 = Full LC data are correct (CRC checked OK) */
unsigned int LeftOvers; //what is the 8 bits for that isn't used
}FullLinkControlPDU_t;
typedef struct
{
TimeSlotRawVoiceFrame_t TimeSlotRawVoiceFrame[6]; /* A voice superframe contains 6 timeslot voice frame */
TimeSlotDeinterleavedVoiceFrame_t TimeSlotDeinterleavedVoiceFrame[6];
TimeSlotAmbeVoiceFrame_t TimeSlotAmbeVoiceFrame[6];
FullLinkControlPDU_t FullLC;
} TimeSlotVoiceSuperFrame_t;
//dPMR
/* Could only be 2 or 4 */
#define NB_OF_DPMR_VOICE_FRAME_TO_DECODE 2
typedef struct
{
unsigned char RawVoiceBit[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4][72];
unsigned int errs1[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4]; /* 8 x errors #1 computed when demodulate the AMBE voice bit of the frame */
unsigned int errs2[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4]; /* 8 x errors #2 computed when demodulate the AMBE voice bit of the frame */
unsigned char AmbeBit[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4][49]; /* 8 x 49 bit of AMBE voice of the frame */
unsigned char CCHData[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][48];
unsigned int CCHDataHammingOk[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
unsigned char CCHDataCRC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
unsigned int CCHDataCrcOk[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
unsigned char CalledID[8];
unsigned int CalledIDOk;
unsigned char CallingID[8];
unsigned int CallingIDOk;
unsigned int FrameNumbering[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
unsigned int CommunicationMode[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
unsigned int Version[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
unsigned int CommsFormat[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
unsigned int EmergencyPriority[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
unsigned int Reserved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
unsigned char SlowData[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
unsigned int ColorCode[NB_OF_DPMR_VOICE_FRAME_TO_DECODE / 2];
} dPMRVoiceFS2Frame_t;
//
typedef struct
{
int onesymbol;
char mbe_in_file[1024];
FILE *mbe_in_f;
int errorbars;
int datascope;
int symboltiming;
int verbose;
int p25enc;
int p25lc;
int p25status;
int p25tg;
int scoperate;
char audio_in_dev[1024];
int audio_in_fd;
SNDFILE *audio_in_file;
SF_INFO *audio_in_file_info;
uint32_t rtlsdr_center_freq;
int rtlsdr_ppm_error; //was int, changed to float
int audio_in_type; // 0 for device, 1 for file, 3 for rtlsdr
char audio_out_dev[1024];
int audio_out_fd;
SNDFILE *audio_out_file;
SF_INFO *audio_out_file_info;
int audio_out_type; // 0 for device, 1 for file,
int split;
int playoffset;
int playoffsetR;
char mbe_out_dir[1024];
char mbe_out_file[1024];
char mbe_out_path[2048]; //1024
FILE *mbe_out_f;
FILE *symbol_out_f;
float audio_gain;
float audio_gainR;
int audio_out;
int dmr_stereo_wav;
char wav_out_file[1024];
char wav_out_fileR[1024];
char wav_out_file_raw[1024];
char symbol_out_file[1024];
char lrrp_out_file[1024];
char szNumbers[1024]; //**tera 10/32/64 char str
short int symbol_out;
short int mbe_out; //flag for mbe out, don't attempt fclose more than once
SNDFILE *wav_out_f;
SNDFILE *wav_out_fR;
SNDFILE *wav_out_raw;
//int wav_out_fd;
int serial_baud;
char serial_dev[1024];
int serial_fd;
int resume;
int frame_dstar;
int frame_x2tdma;
int frame_p25p1;
int frame_p25p2;
int inverted_p2;
int p2counter;
int frame_nxdn48;
int frame_nxdn96;
int frame_dmr;
int frame_provoice;
int mod_c4fm;
int mod_qpsk;
int mod_gfsk;
int uvquality;
int inverted_x2tdma;
int inverted_dmr;
int mod_threshold;
int ssize;
int msize;
int playfiles;
int delay;
int use_cosine_filter;
int unmute_encrypted_p25;
int rtl_dev_index;
int rtl_gain_value;
int rtl_squelch_level;
int rtl_volume_multiplier;
int rtl_udp_port;
int rtl_bandwidth;
int rtl_started;
int monitor_input_audio;
int pulse_raw_rate_in;
int pulse_raw_rate_out;
int pulse_digi_rate_in;
int pulse_digi_rate_out;
int pulse_raw_in_channels;
int pulse_raw_out_channels;
int pulse_digi_in_channels;
int pulse_digi_out_channels;
int pulse_flush;
pa_simple *pulse_raw_dev_in;
pa_simple *pulse_raw_dev_out;
pa_simple *pulse_digi_dev_in;
pa_simple *pulse_digi_dev_out;
pa_simple *pulse_digi_dev_outR;
int use_ncurses_terminal;
int ncurses_compact;
int ncurses_history;
int reset_state;
int payload;
char output_name[1024];
unsigned int dPMR_curr_frame_is_encrypted;
int dPMR_next_part_of_superframe;
int inverted_dpmr;
int frame_dpmr;
short int dmr_stereo;
short int lrrp_file_output;
short int dmr_mute_encL;
short int dmr_mute_encR;
int frame_ysf;
int inverted_ysf;
short int aggressive_framesync;
FILE *symbolfile;
int call_alert;
} dsd_opts;
typedef struct
{
int *dibit_buf;
int *dibit_buf_p;
int *dmr_payload_buf;
int *dmr_payload_p;
int repeat;
short *audio_out_buf;
short *audio_out_buf_p;
short *audio_out_bufR;
short *audio_out_buf_pR;
float *audio_out_float_buf;
float *audio_out_float_buf_p;
float *audio_out_float_bufR;
float *audio_out_float_buf_pR;
float audio_out_temp_buf[160];
float *audio_out_temp_buf_p;
float audio_out_temp_bufR[160];
float *audio_out_temp_buf_pR;
int audio_out_idx;
int audio_out_idx2;
int audio_out_idxR;
int audio_out_idx2R;
//int wav_out_bytes;
int center;
int jitter;
int synctype;
int min;
int max;
int lmid;
int umid;
int minref;
int maxref;
int lastsample;
int sbuf[128];
int sidx;
int maxbuf[1024];
int minbuf[1024];
int midx;
char err_str[64];
char err_buf[64];
char err_strR[64];
char err_bufR[64];
char fsubtype[16];
char ftype[16];
int symbolcnt;
int symbolc;
int rf_mod;
int numflips;
int lastsynctype;
int lastp25type;
int offset;
int carrier;
char tg[25][16];
int tgcount;
int lasttg;
int lasttgR;
int lastsrc;
int lastsrcR;
int nac;
int errs;
int errs2;
int errsR;
int errs2R;
int mbe_file_type;
int optind;
int numtdulc;
int firstframe;
char slot0light[8];
//char slot1light[8];
float aout_gain;
float aout_gainR;
float aout_max_buf[200];
float aout_max_bufR[200];
float *aout_max_buf_p;
float *aout_max_buf_pR;
int aout_max_buf_idx;
int aout_max_buf_idxR;
int samplesPerSymbol;
int symbolCenter;
char algid[9];
char keyid[17];
int currentslot;
int hardslot;
mbe_parms *cur_mp;
mbe_parms *prev_mp;
mbe_parms *prev_mp_enhanced;
mbe_parms *cur_mp2;
mbe_parms *prev_mp2;
mbe_parms *prev_mp_enhanced2;
int p25kid;
int payload_algid;
int payload_algidR;
int payload_keyid;
int payload_keyidR;
int payload_mfid;
int payload_mfidR;
int payload_mi;
int payload_miR;
unsigned long long int payload_miN;
unsigned long long int payload_miP;
int p25vc;
unsigned long long int K;
unsigned long long int K1;
unsigned long long int K2;
unsigned long long int K3;
unsigned long long int K4;
int M;
int menuopen;
unsigned int debug_audio_errors;
unsigned int debug_audio_errorsR;
unsigned int debug_header_errors;
unsigned int debug_header_critical_errors;
// Last dibit read
int last_dibit;
// Heuristics state data for +P5 signals
P25Heuristics p25_heuristics;
// Heuristics state data for -P5 signals
P25Heuristics inv_p25_heuristics;
//input sample buffer for monitoring Input
short input_sample_buffer; //HERE HERE
short pulse_raw_out_buffer; //HERE HERE
unsigned int dmr_color_code;
unsigned int nxdn_last_ran;
unsigned int nxdn_last_rid;
unsigned int nxdn_last_tg;
unsigned int nxdn_cipher_type;
unsigned int nxdn_key;
char nxdn_call_type[1024];
char dmr_callsign[2][6][99]; //plenty of room in case of overflow;
char dmr_lrrp[2][6][9999]; //memory hog
unsigned long long int dmr_lrrp_source[2];
NxdnSacchRawPart_t NxdnSacchRawPart[4];
NxdnFacch1RawPart_t NxdnFacch1RawPart[2];
NxdnFacch2RawPart_t NxdnFacch2RawPart;
NxdnElementsContent_t NxdnElementsContent;
NxdnLich_t NxdnLich;
int printNXDNAmbeVoiceSampleHex;
char ambe_ciphered[49];
char ambe_deciphered[49];
unsigned int color_code;
unsigned int color_code_ok;
unsigned int PI;
unsigned int PI_ok;
unsigned int LCSS;
unsigned int LCSS_ok;
unsigned int dmr_fid;
unsigned int dmr_so;
unsigned int dmr_fidR;
unsigned int dmr_soR;
char slot1light[8];
char slot2light[8];
int directmode;
TimeSlotVoiceSuperFrame_t TS1SuperFrame;
TimeSlotVoiceSuperFrame_t TS2SuperFrame;
char dmr_branding[25];
uint8_t dmr_12_rate_sf[2][288]; //was 84, expanded to 288 to prevent possible crash if more comes in than expected
uint8_t dmr_34_rate_sf[2][288]; //was 96, expanded to 288 to prevent possible crash if more comes in than expected
int dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame
uint8_t data_header_blocks[2]; //collect number of blocks to follow from data header per slot
uint8_t data_header_padding[2]; //collect number of padding octets in last block per slot
uint8_t data_header_format[2]; //collect format of data header (conf or unconf) per slot
uint8_t data_header_sap[2]; //collect sap info per slot
uint8_t data_block_counter[2]; //counter for number of data blocks collected
uint8_t data_p_head[2]; //flag for dmr proprietary header to follow
dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame;
//These flags are used to known the DMR frame
//printing format (hex or binary)
int printDMRRawVoiceFrameHex;
int printDMRRawVoiceFrameBin;
int printDMRRawDataFrameHex;
int printDMRRawDataFrameBin;
int printDMRAmbeVoiceSampleHex;
int printDMRAmbeVoiceSampleBin;
//These flags are used to known the dPMR frame
//orinting format (hex or binary)
int printdPMRRawVoiceFrameHex;
int printdPMRRawVoiceFrameBin;
int printdPMRRawDataFrameHex;
int printdPMRRawDataFrameBin;
int printdPMRAmbeVoiceSampleHex;
int printdPMRAmbeVoiceSampleBin;
unsigned char * dpmr_caller_id;
unsigned char * dpmr_target_id;
int dpmr_color_code;
short int dmr_stereo; //need state variable for upsample function
short int dmr_ms_rc;
short int dmr_ms_mode;
unsigned int dmrburstL;
unsigned int dmrburstR;
unsigned long long int R;
unsigned long long int RR;
unsigned long long int H;
unsigned long long int HYTL;
unsigned long long int HYTR;
int DMRvcL;
int DMRvcR;
short int dmr_encL;
short int dmr_encR;
//P2 variables
unsigned long long int p2_wacn;
unsigned long long int p2_sysid;
unsigned long long int p2_cc; //p1 NAC
int p2_hardset; //flag for checking whether or not P2 wacn and sysid are hard set by user
int p2_scramble_offset; //offset counter for scrambling application
int p2_vch_chan_num; //vch channel number (0 or 1, not the 0-11 TS)
int ess_b[2][96]; //external storage for ESS_B fragments
int fourv_counter[2]; //external reference counter for ESS_B fragment collection
int p2_is_lcch; //flag to tell us when a frame is lcch and not sacch
//iden freq storage for frequency calculations
int p25_chan_iden;
int p25_chan_type[16];
int p25_trans_off[16];
int p25_chan_spac[16];
long int p25_base_freq[16];
//p25 frequency storage for display in ncurses
long int p25_cc_freq; //cc freq from net_stat
long int p25_vc_freq[2]; //vc freq from voice grant updates, etc
//experimental symbol file capture read throttle
int symbol_throttle; //throttle speed
int use_throttle; //only use throttle if set to 1
//dstar header for ncurses
unsigned char dstarradioheader[41];
#ifdef TRACE_DSD
char debug_prefix;
char debug_prefix_2;
unsigned int debug_sample_index;
unsigned int debug_sample_left_edge;
unsigned int debug_sample_right_edge;
FILE* debug_label_file;
FILE* debug_label_dibit_file;
FILE* debug_label_imbe_file;
#endif
} dsd_state;
/*
* Frame sync patterns
*/
#define FUSION_SYNC "31111311313113131131" //HA!
#define INV_FUSION_SYNC "13333133131331313313" //HA!
#define INV_P25P1_SYNC "333331331133111131311111"
#define P25P1_SYNC "111113113311333313133333"
#define P25P2_SYNC "11131131111333133333"
#define INV_P25P2_SYNC "33313313333111311111"
#define X2TDMA_BS_VOICE_SYNC "113131333331313331113311"
#define X2TDMA_BS_DATA_SYNC "331313111113131113331133"
#define X2TDMA_MS_DATA_SYNC "313113333111111133333313"
#define X2TDMA_MS_VOICE_SYNC "131331111333333311111131"
#define DSTAR_HD "131313131333133113131111"
#define INV_DSTAR_HD "313131313111311331313333"
#define DSTAR_SYNC "313131313133131113313111"
#define INV_DSTAR_SYNC "131313131311313331131333"
#define NXDN_MS_DATA_SYNC "313133113131111333"
#define INV_NXDN_MS_DATA_SYNC "131311331313333111"
#define INV_NXDN_BS_DATA_SYNC "131311331313333131"
#define NXDN_BS_DATA_SYNC "313133113131111313"
#define NXDN_MS_VOICE_SYNC "313133113131113133"
#define INV_NXDN_MS_VOICE_SYNC "131311331313331311"
#define INV_NXDN_BS_VOICE_SYNC "131311331313331331"
#define NXDN_BS_VOICE_SYNC "313133113131113113"
#define DMR_BS_DATA_SYNC "313333111331131131331131"
#define DMR_BS_VOICE_SYNC "131111333113313313113313"
#define DMR_MS_DATA_SYNC "311131133313133331131113"
#define DMR_MS_VOICE_SYNC "133313311131311113313331"
//Testing NXDN FSW again
#define NXDN_FSW "3131331131"
#define DMR_RC_DATA_SYNC "131331111133133133311313"
#define DMR_DIRECT_MODE_TS1_DATA_SYNC "331333313111313133311111"
#define DMR_DIRECT_MODE_TS1_VOICE_SYNC "113111131333131311133333"
#define DMR_DIRECT_MODE_TS2_DATA_SYNC "311311111333113333133311"
#define DMR_DIRECT_MODE_TS2_VOICE_SYNC "133133333111331111311133"
#define INV_PROVOICE_SYNC "31313111333133133311331133113311"
#define PROVOICE_SYNC "13131333111311311133113311331133"
#define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311"
#define PROVOICE_EA_SYNC "31131311331331111133131311311133"
#define DPMR_FRAME_SYNC_1 "111333331133131131111313"
#define DPMR_FRAME_SYNC_2 "113333131331"
#define DPMR_FRAME_SYNC_3 "133131333311"
#define DPMR_FRAME_SYNC_4 "333111113311313313333131"
/* dPMR Frame Sync 1 to 4 - Inverted */
#define INV_DPMR_FRAME_SYNC_1 "333111113311313313333131"
#define INV_DPMR_FRAME_SYNC_2 "331111313113"
#define INV_DPMR_FRAME_SYNC_3 "311313111133"
#define INV_DPMR_FRAME_SYNC_4 "111333331133131131111313"
/*
* function prototypes
*/
void processDMRdata (dsd_opts * opts, dsd_state * state);
void processDMRvoice (dsd_opts * opts, dsd_state * state);
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
void dmrBS (dsd_opts * opts, dsd_state * state);
void dmrMS (dsd_opts * opts, dsd_state * state);
void dmrMSData (dsd_opts * opts, dsd_state * state);
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state);
void processdPMRvoice (dsd_opts * opts, dsd_state * state);
void processAudio (dsd_opts * opts, dsd_state * state);
void processAudioR (dsd_opts * opts, dsd_state * state);
void openPulseInput (dsd_opts * opts); //not sure if we need to just pass opts, or opts and state yet
void openPulseOutput (dsd_opts * opts); //not sure if we need to just pass opts, or opts and state yet
void closePulseInput (dsd_opts * opts);
void closePulseOutput (dsd_opts * opts);
void writeSynthesizedVoice (dsd_opts * opts, dsd_state * state);
void writeSynthesizedVoiceR (dsd_opts * opts, dsd_state * state);
void playSynthesizedVoice (dsd_opts * opts, dsd_state * state);
void playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state);
void openAudioOutDevice (dsd_opts * opts, int speed);
void openAudioInDevice (dsd_opts * opts);
int getDibit (dsd_opts * opts, dsd_state * state);
int get_dibit_and_analog_signal (dsd_opts * opts, dsd_state * state, int * out_analog_signal);
void skipDibit (dsd_opts * opts, dsd_state * state, int count);
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
void saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
void PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d);
void PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d);
int readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
int readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
void openMbeInFile (dsd_opts * opts, dsd_state * state);
void closeMbeOutFile (dsd_opts * opts, dsd_state * state);
void openMbeOutFile (dsd_opts * opts, dsd_state * state);
void openWavOutFile (dsd_opts * opts, dsd_state * state);
void openWavOutFileL (dsd_opts * opts, dsd_state * state);
void openWavOutFileR (dsd_opts * opts, dsd_state * state);
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state);
void openSymbolOutFile (dsd_opts * opts, dsd_state * state);
void closeSymbolOutFile (dsd_opts * opts, dsd_state * state);
void writeSymbolOutFile (dsd_opts * opts, dsd_state * state);
void writeRawSample (dsd_opts * opts, dsd_state * state, short sample);
void closeWavOutFile (dsd_opts * opts, dsd_state * state);
void closeWavOutFileL (dsd_opts * opts, dsd_state * state);
void closeWavOutFileR (dsd_opts * opts, dsd_state * state);
void closeWavOutFileRaw (dsd_opts * opts, dsd_state * state);
void printFrameInfo (dsd_opts * opts, dsd_state * state);
void processFrame (dsd_opts * opts, dsd_state * state);
void printFrameSync (dsd_opts * opts, dsd_state * state, char *frametype, int offset, char *modulation);
int getFrameSync (dsd_opts * opts, dsd_state * state);
int comp (const void *a, const void *b);
void noCarrier (dsd_opts * opts, dsd_state * state);
void initOpts (dsd_opts * opts);
void initState (dsd_state * state);
void usage ();
void liveScanner (dsd_opts * opts, dsd_state * state);
void cleanupAndExit (dsd_opts * opts, dsd_state * state);
void sigfun (int sig);
int main (int argc, char **argv);
void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv);
void processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24]);
void openSerial (dsd_opts * opts, dsd_state * state);
void resumeScan (dsd_opts * opts, dsd_state * state);
int getSymbol (dsd_opts * opts, dsd_state * state, int have_sync);
void upsample (dsd_state * state, float invalue);
void processDSTAR (dsd_opts * opts, dsd_state * state);
void processNXDNVoice (dsd_opts * opts, dsd_state * state);
void processNXDNData (dsd_opts * opts, dsd_state * state);
void processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo);
void processHDU (dsd_opts * opts, dsd_state * state);
void processLDU1 (dsd_opts * opts, dsd_state * state);
void processLDU2 (dsd_opts * opts, dsd_state * state);
void processTDU (dsd_opts * opts, dsd_state * state);
void processTDULC (dsd_opts * opts, dsd_state * state);
void processProVoice (dsd_opts * opts, dsd_state * state);
void processX2TDMAdata (dsd_opts * opts, dsd_state * state);
void processX2TDMAvoice (dsd_opts * opts, dsd_state * state);
void processDSTAR_HD (dsd_opts * opts, dsd_state * state);
void processYSF(dsd_opts * opts, dsd_state * state); //YSF
void processP2(dsd_opts * opts, dsd_state * state); //P2
void processTSBK(dsd_opts * opts, dsd_state * state);
short dmr_filter(short sample);
short nxdn_filter(short sample);
int strncmperr(const char *s1, const char *s2, size_t size, int MaxErr);
/* Global functions */ //also borrowed
uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength);
uint32_t ConvertAsciiToByte(uint8_t AsciiMsbByte, uint8_t AsciiLsbByte, uint8_t * OutputByte);
void Convert49BitSampleInto7Bytes(char * InputBit, char * OutputByte);
void Convert7BytesInto49BitSample(char * InputByte, char * OutputBit);
void ncursesOpen (dsd_opts * opts, dsd_state * state);
void ncursesPrinter (dsd_opts * opts, dsd_state * state);
void ncursesClose ();
/* NXDN frame decoding functions */
void ProcessNXDNFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
void ProcessNxdnRCCHFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
void ProcessNxdnRTCHFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
void ProcessNxdnRDCHFrame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
void ProcessNxdnRTCH_C_Frame(dsd_opts * opts, dsd_state * state, uint8_t Inverted);
void ProcessNXDNIdleData (dsd_opts * opts, dsd_state * state, uint8_t Inverted);
void ProcessNXDNFacch1Data (dsd_opts * opts, dsd_state * state, uint8_t Inverted);
void ProcessNXDNUdchData (dsd_opts * opts, dsd_state * state, uint8_t Inverted);
//end borrow
/* NXDN functions */
void CNXDNConvolution_start(void);
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
uint8_t NXDN_decode_LICH(uint8_t InputLich[8],
uint8_t * RFChannelType,
uint8_t * FunctionnalChannelType,
uint8_t * Option,
uint8_t * Direction,
uint8_t * CompleteLichBinaryFormat,
uint8_t Inverted);
uint8_t NXDN_SACCH_raw_part_decode(uint8_t * Input, uint8_t * Output);
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state);
uint8_t NXDN_FACCH1_decode(uint8_t * Input, uint8_t * Output);
uint8_t NXDN_UDCH_decode(uint8_t * Input, uint8_t * Output);
void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state,
uint8_t CrcCorrect, uint8_t * ElementsContent);
void NXDN_decode_VCALL(dsd_opts * opts, dsd_state * state, uint8_t * Message);
void NXDN_decode_VCALL_IV(dsd_opts * opts, dsd_state * state, uint8_t * Message);
char * NXDN_Call_Type_To_Str(uint8_t CallType);
void NXDN_Voice_Call_Option_To_Str(uint8_t VoiceCallOption, uint8_t * Duplex, uint8_t * TransmissionMode);
char * NXDN_Cipher_Type_To_Str(uint8_t CipherType);
uint16_t CRC15BitNXDN(uint8_t * BufferIn, uint32_t BitLength);
uint16_t CRC12BitNXDN(uint8_t * BufferIn, uint32_t BitLength);
uint8_t CRC6BitNXDN(uint8_t * BufferIn, uint32_t BitLength);
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
void printdPMRAmbeVoiceSample(dsd_opts * opts, dsd_state * state);
void printdPMRRawVoiceFrame (dsd_opts * opts, dsd_state * state);
//dPMR functions
void ScrambledPMRBit(uint32_t * LfsrValue, uint8_t * BufferIn, uint8_t * BufferOut, uint32_t NbOfBitToScramble);
void DeInterleave6x12DPmrBit(uint8_t * BufferIn, uint8_t * BufferOut);
uint8_t CRC7BitdPMR(uint8_t * BufferIn, uint32_t BitLength);
uint8_t CRC8BitdPMR(uint8_t * BufferIn, uint32_t BitLength);
void ConvertAirInterfaceID(uint32_t AI_ID, uint8_t ID[8]);
int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]);
void ProcessDMR (dsd_opts * opts, dsd_state * state);
void DMRDataFrameProcess(dsd_opts * opts, dsd_state * state);
void DMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
//BPTC (Block Product Turbo Code) functions
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output);
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3]);
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77]);
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32]);
//Read Solomon (12,9) functions
void rs_12_9_calc_syndrome(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome);
uint8_t rs_12_9_check_syndrome(rs_12_9_poly_t *syndrome);
rs_12_9_correct_errors_result_t rs_12_9_correct_errors(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome, uint8_t *errors_found);
rs_12_9_checksum_t *rs_12_9_calc_checksum(rs_12_9_codeword_t *codeword);
//SYNC DMR data extraction functions
void ProcessDmrVoiceLcHeader(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessDmrTerminaisonLC(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessVoiceBurstSync(dsd_opts * opts, dsd_state * state);
uint16_t ComputeCrcCCITT(uint8_t * DMRData);
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask);
uint8_t ComputeCrc5Bit(uint8_t * DMRData);
uint8_t * DmrAlgIdToStr(uint8_t AlgID);
uint8_t * DmrAlgPrivacyModeToStr(uint32_t PrivacyMode);
void ProcessDmrPIHeader(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessCSBK(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessDataData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void Process1Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void Process12Data(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void Process34Data(dsd_opts * opts, dsd_state * state, unsigned char tdibits[98], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessMBCData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessMBChData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessReservedData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
void ProcessUnifiedData(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t syncdata[48], uint8_t SlotType[20]);
//LFSR code courtesy of https://github.com/mattames/LFSR/
int LFSR(dsd_state * state);
void LFSRN (char * BufferIn, char * BufferOut, dsd_state * state);
void Hamming_7_4_init();
void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits);
bool Hamming_7_4_decode(unsigned char *rxBits);
void Hamming_12_8_init();
void Hamming_12_8_encode(unsigned char *origBits, unsigned char *encodedBits);
bool Hamming_12_8_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords);
void Hamming_13_9_init();
void Hamming_13_9_encode(unsigned char *origBits, unsigned char *encodedBits);
bool Hamming_13_9_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords);
void Hamming_15_11_init();
void Hamming_15_11_encode(unsigned char *origBits, unsigned char *encodedBits);
bool Hamming_15_11_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords);
void Hamming_16_11_4_init();
void Hamming_16_11_4_encode(unsigned char *origBits, unsigned char *encodedBits);
bool Hamming_16_11_4_decode(unsigned char *rxBits, unsigned char *decodedBits, int nbCodewords);
void Golay_20_8_init();
void Golay_20_8_encode(unsigned char *origBits, unsigned char *encodedBits);
bool Golay_20_8_decode(unsigned char *rxBits);
void Golay_23_12_init();
void Golay_23_12_encode(unsigned char *origBits, unsigned char *encodedBits);
bool Golay_23_12_decode(unsigned char *rxBits);
void Golay_24_12_init();
void Golay_24_12_encode(unsigned char *origBits, unsigned char *encodedBits);
bool Golay_24_12_decode(unsigned char *rxBits);
void QR_16_7_6_init();
void QR_16_7_6_encode(unsigned char *origBits, unsigned char *encodedBits);
bool QR_16_7_6_decode(unsigned char *rxBits);
void InitAllFecFunction(void);
void resetState (dsd_state * state);
void dstar_header_decode(dsd_state * state, int radioheaderbuffer[660]);
//P25 PDU Handler
void process_MAC_VPDU(dsd_opts * opts, dsd_state * state, int type, unsigned long long int MAC[24]);
//P25 xCCH Handlers (SACCH, FACCH, LCCH)
void process_SACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[180]);
void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]);
//P25 Channel to Frequency
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel);
#ifdef __cplusplus
extern "C" {
#endif
#ifdef USE_RTLSDR
void open_rtlsdr_stream(dsd_opts *opts);
void cleanup_rtlsdr_stream();
void get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state);
void rtlsdr_sighandler();
#endif
//TRELLIS
void CDMRTrellisTribitsToBits(const unsigned char* tribits, unsigned char* payload);
unsigned int CDMRTrellisCheckCode(const unsigned char* points, unsigned char* tribits);
//unsigned int CDMRTrellisCheckCode(const unsigned char* points, unsigned char* state, unsigned char* tribits);
bool CDMRTrellisFixCode(unsigned char* points, unsigned int failPos, unsigned char* payload);
void CDMRTrellisDeinterleave(const unsigned char* data, signed char* dibits);
void CDMRTrellisDibitsToPoints(const signed char* dibits, unsigned char* points);
void CDMRTrellisPointsToDibits(const unsigned char* points, signed char* dibits);
void CDMRTrellisBitsToTribits(const unsigned char* payload, unsigned char* tribits);
bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload);
//Phase 2 Helper Functions
int ez_rs28_ess (int payload[96], int parity[168]); //ezpwd bridge for FME
int ez_rs28_facch (int payload[156], int parity[114]); //ezpwd bridge for FME
int ez_rs28_sacch (int payload[180], int parity[132]); //ezpwd bridge for FME
unsigned long long int isch_lookup (unsigned long long int isch); //isch map lookup
int bd_bridge (int payload[196], uint8_t decoded[12]); //bridge to Michael Ossmann Block De-interleaver and 1/2 rate trellis decoder
// uint16_t crc16(uint8_t buf[], int len);
int crc16_lb_bridge (int payload[190], int len);
int crc12_xb_bridge (int payload[190], int len);
#ifdef __cplusplus
}
#endif
#endif // DSD_H

129
include/dstar_const.h Normal file
View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* dstar interleave experiments
*/
#ifndef _MAIN
extern const int dW[72];
extern const int dX[72];
#else
const int dW[72] = {
// 0-11
0, 0,
3, 2,
1, 1,
0, 0,
1, 1,
0, 0,
// 12-23
3, 2,
1, 1,
3, 2,
1, 1,
0, 0,
3, 2,
// 24-35
0, 0,
3, 2,
1, 1,
0, 0,
1, 1,
0, 0,
// 36-47
3, 2,
1, 1,
3, 2,
1, 1,
0, 0,
3, 2,
// 48-59
0, 0,
3, 2,
1, 1,
0, 0,
1, 1,
0, 0,
// 60-71
3, 2,
1, 1,
3, 3,
2, 1,
0, 0,
3, 3,
};
const int dX[72] = {
// 0-11
10, 22,
11, 9,
10, 22,
11, 23,
8, 20,
9, 21,
// 12-23
10, 8,
9, 21,
8, 6,
7, 19,
8, 20,
9, 7,
// 24-35
6, 18,
7, 5,
6, 18,
7, 19,
4, 16,
5, 17,
// 36-47
6, 4,
5, 17,
4, 2,
3, 15,
4, 16,
5, 3,
// 48-59
2, 14,
3, 1,
2, 14,
3, 15,
0, 12,
1, 13,
// 60-71
2, 0,
1, 13,
0, 12,
10, 11,
0, 12,
1, 13,
};
#endif

6
include/dstar_header.h Normal file
View File

@@ -0,0 +1,6 @@
/* This is the header file for dstar_header.c, which is under the GPL. */
#ifndef _DSTAR_HEADER_H
#define _DSTAR_HEADER_H
//void dstar_header_decode(int radioheaderbuffer[660]);
#endif /* _DSTAR_HEADER_H */

89
include/fcs.h Normal file
View File

@@ -0,0 +1,89 @@
/* fcs.h */
// Viterbi decoder using Traceback method.
// Original Source was written by Sho Tamaoki and Tom Wada
// See http://www.lsi.ie.u-ryukyu.ac.jp/~sho/midterm/
// Modified by Satoshi Yasuda 7m3tjz/ad6gz
// Modified by Jonathan Nayor, G4KLX (C) 2009
// Converted from C++ to C by Kristoff Bonne, ON1ARF
/*
* Copyright (C) 2010 by Kristoff Bonne, ON1ARF
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <stdint.h>
static const unsigned short ccittTab[] = {
0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,
0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,
0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876,
0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,
0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5,
0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c,
0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974,
0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb,
0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3,
0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a,
0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72,
0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9,
0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1,
0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738,
0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70,
0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7,
0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff,
0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036,
0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e,
0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5,
0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd,
0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134,
0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c,
0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3,
0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb,
0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232,
0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a,
0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1,
0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9,
0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,
0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78};
uint16_t calc_fcs (unsigned char * dvstartframe, int size) {
// this function calculated the CRC-values of a DSTAR digital
// voice frame. It calculates this value on octets 0 up to 38 of the D-STAR
// radio header (fields flag1, flag2, flag3, destination, departure, companion,
// own1 and own2)
uint16_t m_crc;
int loop;
unsigned short tmp;
m_crc=0xFFFF;
for (loop=0; loop < size; loop++) {
tmp = (m_crc & 0x00ff) ^ dvstartframe[loop];
m_crc = (m_crc >> 8) ^ ccittTab[tmp];
}; // end for
// calculate and save crc-value in fields 54 and 55 of dvframe
m_crc = ~m_crc;
tmp = m_crc;
m_crc = (m_crc << 8) | (tmp >> 8 & 0xFF);
// done
return(m_crc);
}; // end function

1448
include/fec.h Normal file

File diff suppressed because it is too large Load Diff

1
include/git_ver.h Normal file
View File

@@ -0,0 +1 @@
extern const char GIT_TAG[];

257
include/nxdn_const.h Normal file
View File

@@ -0,0 +1,257 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _MAIN
extern const int nW[36];
extern const int nX[36];
extern const int nY[36];
extern const int nZ[36];
extern const char nxdnpr[145];
extern const char nxdnpr2[145]; //why is this 145 but referenced later as 182 or 384
//borrow from LEH
extern const unsigned char nsW[72];
extern const int DSDNXDN_SACCH_m_Interleave[60];
extern const int DSDNXDN_SACCH_m_PunctureList[12];
extern const int DSDNXDN_FACCH1_m_Interleave[144];
extern const int DSDNXDN_FACCH1_m_PunctureList[48];
extern const int DSDNXDN_UDCH_m_Interleave[348];
extern const int DSDNXDN_UDCH_m_PunctureList[58];
//
//enum
//{
#define NXDN_VCALL 0b000001 /* VCALL = VCALL_REQ = VCALL_RESP */
#define NXDN_VCALL_IV 0b000011
#define NXDN_DCALL_HDR 0b001001 /* Header Format - NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
#define NXDN_DCALL_USR 0b001011 /* User Data Format */
#define NXDN_DCALL_ACK 0b001100
#define NXDN_REL_EX 0b000111
#define NXDN_HEAD_DLY 0b001111
#define NXDN_SDCALL_REQ_HDR 0b111000 /* Header Format */
#define NXDN_SDCALL_REQ_USR 0b111001 /* User Data Format */
#define NXDN_SDCALL_RESP 0b111011
#define NXDN_SDCALL_IV 0b111010
#define NXDN_STAT_INQ_REQ 0b110000
#define NXDN_STAT_INQ_RESP 0b110001
#define NXDN_STAT_REQ 0b110010
#define NXDN_STAT_RESP 0b110011
#define NXDN_REM_CON_REQ 0b110100
#define NXDN_REM_CON_RESP 0b110101
#define NXDN_REM_CON_E_REQ 0b110110
#define NXDN_REM_CON_E_RESP 0b110111
//NXDN_VCALL_REQ = 0b000001, /* VCALL = VCALL_REQ = VCALL_RESP */
//NXDN_VCALL_RESP = 0b000001, /* VCALL = VCALL_REQ = VCALL_RESP */
#define NXDN_VCALL_REC_REQ = 0b000010 /* NXDN_VCALL_REC_REQ = NXDN_VCALL_REC_RESP */
//NXDN_VCALL_REC_RESP = 0b000010,
#define NXDN_VCALL_CONN_REQ = 0b000011 /* NXDN_VCALL_CONN_REQ = NXDN_VCALL_CONN_RESP */
//NXDN_VCALL_CONN_RESP = 0b000011, /* NXDN_VCALL_CONN_REQ = NXDN_VCALL_CONN_RESP */
#define NXDN_VCALL_ASSGN_DUP = 0b000101
//NXDN_DCALL_REQ = 0b001001, /* NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
//NXDN_DCALL_RESP = 0b001001, /* NXDN_DCALL_HDR = NXDN_DCALL_REQ = NXDN_DCALL_RESP */
#define NXDN_DCALL_REC_REQ = 0b001010 /* NXDN_DCALL_REC_REQ = NXDN_DCALL_REC_RESP */
//NXDN_DCALL_REC_RESP = 0b001010, /* NXDN_DCALL_REC_REQ = NXDN_DCALL_REC_RESP */
#define NXDN_DCALL_ASSGN = 0b001110
#define NXDN_DCALL_ASSGN_DUP = 0b001101
#define NXDN_IDLE 0b010000
#define NXDN_DISC_REQ = 0b010001 /* NXDN_DISC_REQ = NXDN_DISC */
#define NXDN_DISC = 0b010001 /* NXDN_DISC_REQ = NXDN_DISC */
#else
/*
* pseudorandom bit sequence
*/
const char nxdnpr[145] = { 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1 };
//const unsigned char nxdnpr2[384] =
const unsigned char nxdnpr2[182] = //generated from default in manual with the x^9+x^4+1 poly?
{
0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0,
1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0,
0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1,
0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1,
1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1,
1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0,
0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1,
0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1,
1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1,
1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1,
1, 1, 0, 0, 0, 1//, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
// 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0,
// 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
// 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
// 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1,
// 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0,
// 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1,
// 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1,
// 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1,
// 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0,
// 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0,
// 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
// 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1
};
/*
* NXDN AMBE interleave schedule
*/
const int nW[36] = { 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 2,
0, 2, 0, 2, 0, 2,
0, 2, 0, 2, 0, 2
};
const int nX[36] = { 23, 10, 22, 9, 21, 8,
20, 7, 19, 6, 18, 5,
17, 4, 16, 3, 15, 2,
14, 1, 13, 0, 12, 10,
11, 9, 10, 8, 9, 7,
8, 6, 7, 5, 6, 4
};
const int nY[36] = { 0, 2, 0, 2, 0, 2,
0, 2, 0, 3, 0, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3
};
const int nZ[36] = { 5, 3, 4, 2, 3, 1,
2, 0, 1, 13, 0, 12,
22, 11, 21, 10, 20, 9,
19, 8, 18, 7, 17, 6,
16, 5, 15, 4, 14, 3,
13, 2, 12, 1, 11, 0
};
/*
* NXDN SACCH interleave
*/
const unsigned char nsW[72] = {
0, 12, 24, 36, 48, 60,
1, 13, 25, 37, 49, 61,
2, 14, 26, 38, 50, 62,
3, 15, 27, 39, 51, 63,
4, 16, 28, 40, 52, 64,
5, 17, 29, 41, 53, 65,
6, 18, 30, 42, 54, 66,
7, 19, 31, 43, 55, 67,
8, 20, 32, 44, 56, 68,
9, 21, 33, 45, 57, 69,
10,22, 34, 46, 58, 70,
11,23, 35, 47, 59, 71
};
const int DSDNXDN_SACCH_m_Interleave[60] =
{
0, 12, 24, 36, 48,
1, 13, 25, 37, 49,
2, 14, 26, 38, 50,
3, 15, 27, 39, 51,
4, 16, 28, 40, 52,
5, 17, 29, 41, 53,
6, 18, 30, 42, 54,
7, 19, 31, 43, 55,
8, 20, 32, 44, 56,
9, 21, 33, 45, 57,
10, 22, 34, 46, 58,
11, 23, 35, 47, 59,
};
const int DSDNXDN_SACCH_m_PunctureList[12] = { 5, 11, 17, 23, 29, 35, 41, 47, 53, 59, 65, 71 };
const unsigned int DSDNXDN_FACCH1_m_Interleave[144] =
{
0, 16, 32, 48, 64, 80, 96, 112, 128,
1, 17, 33, 49, 65, 81, 97, 113, 129,
2, 18, 34, 50, 66, 82, 98, 114, 130,
3, 19, 35, 51, 67, 83, 99, 115, 131,
4, 20, 36, 52, 68, 84, 100, 116, 132,
5, 21, 37, 53, 69, 85, 101, 117, 133,
6, 22, 38, 54, 70, 86, 102, 118, 134,
7, 23, 39, 55, 71, 87, 103, 119, 135,
8, 24, 40, 56, 72, 88, 104, 120, 136,
9, 25, 41, 57, 73, 89, 105, 121, 137,
10, 26, 42, 58, 74, 90, 106, 122, 138,
11, 27, 43, 59, 75, 91, 107, 123, 139,
12, 28, 44, 60, 76, 92, 108, 124, 140,
13, 29, 45, 61, 77, 93, 109, 125, 141,
14, 30, 46, 62, 78, 94, 110, 126, 142,
15, 31, 47, 63, 79, 95, 111, 127, 143,
};
const unsigned int DSDNXDN_FACCH1_m_PunctureList[48] =
{
1U, 5U, 9U, 13U, 17U, 21U, 25U, 29U, 33U, 37U,
41U, 45U, 49U, 53U, 57U, 61U, 65U, 69U, 73U, 77U,
81U, 85U, 89U, 93U, 97U, 101U, 105U, 109U, 113U, 117U,
121U, 125U, 129U, 133U, 137U, 141U, 145U, 149U, 153U, 157U,
161U, 165U, 169U, 173U, 177U, 181U, 185U, 189U
};
const int DSDNXDN_UDCH_m_Interleave[348] = {
0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132, 144, 156, 168, 180, 192, 204, 216, 228, 240, 252, 264, 276, 288, 300, 312, 324, 336,
1, 13, 25, 37, 49, 61, 73, 85, 97, 109, 121, 133, 145, 157, 169, 181, 193, 205, 217, 229, 241, 253, 265, 277, 289, 301, 313, 325, 337,
2, 14, 26, 38, 50, 62, 74, 86, 98, 110, 122, 134, 146, 158, 170, 182, 194, 206, 218, 230, 242, 254, 266, 278, 290, 302, 314, 326, 338,
3, 15, 27, 39, 51, 63, 75, 87, 99, 111, 123, 135, 147, 159, 171, 183, 195, 207, 219, 231, 243, 255, 267, 279, 291, 303, 315, 327, 339,
4, 16, 28, 40, 52, 64, 76, 88, 100, 112, 124, 136, 148, 160, 172, 184, 196, 208, 220, 232, 244, 256, 268, 280, 292, 304, 316, 328, 340,
5, 17, 29, 41, 53, 65, 77, 89, 101, 113, 125, 137, 149, 161, 173, 185, 197, 209, 221, 233, 245, 257, 269, 281, 293, 305, 317, 329, 341,
6, 18, 30, 42, 54, 66, 78, 90, 102, 114, 126, 138, 150, 162, 174, 186, 198, 210, 222, 234, 246, 258, 270, 282, 294, 306, 318, 330, 342,
7, 19, 31, 43, 55, 67, 79, 91, 103, 115, 127, 139, 151, 163, 175, 187, 199, 211, 223, 235, 247, 259, 271, 283, 295, 307, 319, 331, 343,
8, 20, 32, 44, 56, 68, 80, 92, 104, 116, 128, 140, 152, 164, 176, 188, 200, 212, 224, 236, 248, 260, 272, 284, 296, 308, 320, 332, 344,
9, 21, 33, 45, 57, 69, 81, 93, 105, 117, 129, 141, 153, 165, 177, 189, 201, 213, 225, 237, 249, 261, 273, 285, 297, 309, 321, 333, 345,
10, 22, 34, 46, 58, 70, 82, 94, 106, 118, 130, 142, 154, 166, 178, 190, 202, 214, 226, 238, 250, 262, 274, 286, 298, 310, 322, 334, 346,
11, 23, 35, 47, 59, 71, 83, 95, 107, 119, 131, 143, 155, 167, 179, 191, 203, 215, 227, 239, 251, 263, 275, 287, 299, 311, 323, 335, 347,
};
const int DSDNXDN_UDCH_m_PunctureList[58] = {
3, 11,
17, 25,
31, 39,
45, 53,
59, 67,
73, 81,
87, 95,
101, 109,
115, 123,
129, 137,
143, 151,
157, 165,
171, 179,
185, 193,
199, 207,
213, 221,
227, 235,
241, 249,
255, 263,
269, 277,
283, 291,
297, 305,
311, 319,
325, 333,
339, 347,
353, 361,
367, 375,
381, 389,
395, 403,
};
//#endif /* _MAIN */
#endif

60
include/p25p1_check_hdu.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef P25P1_CHECK_HDU_H_f5f079faf2d64cf5831e1da1ab83b9ba
#define P25P1_CHECK_HDU_H_f5f079faf2d64cf5831e1da1ab83b9ba
#ifdef __cplusplus
extern "C" {
#endif
/**
* Attempts to correct an hex (6-bit) word using the Golay(24,12,8) FEC.
* \param hex The 6-bit word to error correct, given as a char array of 6 elements, one bit each.
* \param parity A 12-bit word with the Golay parity data, given as a char array of 12 elements.
* \param fixed_error Output. Filled with the number of errors fixed in the input.
* \return 1 if there were uncorrectable errors in the input, 0 otherwise.
*/
int check_and_fix_golay_24_6(char* hex, char* parity, int* fixed_errors);
/**
* Attempts to correct a 12-bit word using the Golay(24,12,8) FEC.
* \param dodeca The 12-bit word to error correct, given as a char array of 12 elements, one bit each.
* \param parity A 12-bit word with the Golay parity data, given as a char array of 12 elements.
* \param fixed_error Output. Filled with the number of errors fixed in the input.
* \return 1 if there were uncorrectable errors in the input, 0 otherwise.
*/
int check_and_fix_golay_24_12(char* dodeca, char* parity, int* fixed_errors);
/**
* Encodes an hex word using the Golay(24,12,8) FEC.
* \param hex A 6-bit word to encode.
* \param out_parity The address to be filled with the calculated Golay parity.
*/
void encode_golay_24_6(char* hex, char* out_parity);
/**
* Encodes an 12-bitword using the Golay(24,12,8) FEC.
* \param dodeca A 12-bit word to encode.
* \param out_parity The address to be filled with the calculated Golay parity.
*/
void encode_golay_24_12(char* dodeca, char* out_parity);
/**
* Attempts to correct 20 hex words using the Reed-Solomon(36,20,17) FEC.
* \param data The packed hex words, each of 6 chars, one after the other.
* \param parity The corresponding 16 hex words with the parity information.
* \return 1 if irrecoverable errors have been detected, 0 otherwise.
*/
int check_and_fix_redsolomon_36_20_17(char* data, char* parity);
/**
* Calculates the Reed-Solomon parity of 20 words of 6 bits each.
* \param hex_data The address with the words whose parity we want to calculate.
* \param fixed_parity The address to be filled with the calculated parity. 16 words of 6 bits are produced.
*/
void encode_reedsolomon_36_20_17(char* hex_data, char* fixed_parity);
#ifdef __cplusplus
}
#endif
#endif // P25P1_CHECK_HDU_H_f5f079faf2d64cf5831e1da1ab83b9ba

58
include/p25p1_check_ldu.h Normal file
View File

@@ -0,0 +1,58 @@
#ifndef P25P1_CHECK_LDU_H_c1734445a67e47caa25673d7a4ce7520
#define P25P1_CHECK_LDU_H_c1734445a67e47caa25673d7a4ce7520
#ifdef __cplusplus
extern "C" {
#endif
/**
* Attempts to error correct a hex (6-bit) word using the Hamming(10,6,3) FEC.
* \param hex The 6-bit word to error correct.
* \param parity The parity to use in the error correction. 4 bits.
* \return The estimated error count. It might be erroneous.
*/
int check_and_fix_hamming_10_6_3(char* hex, char* parity);
/**
* Encodes an hex word with the Hamming(10,6,3) FEC.
* \param hex The 6-bit word to error correct.
* \param out_parity The address where to store the calculated parity. 4 bytes are used, one for each bit.
*/
void encode_hamming_10_6_3(char* hex, char* out_parity);
/**
* Attempts to correct 12 hex words using the Reed-Solomon(24,12,13) FEC.
* \param data The packed hex words, each of 6 chars, one after the other.
* \param parity The corresponding 12 hex words with the parity information.
* \return 1 if irrecoverable errors have been detected, 0 otherwise.
*/
int check_and_fix_reedsolomon_24_12_13(char* data, char* parity);
/**
* Encodes 12 hex words with the Reed-Solomon(24,12,13) FEC.
* \param hex_data The packed 12 hex words to encode.
* \fixed_parity The address were to store the 12 hex words with the Reed-Solomon parity.
*/
void encode_reedsolomon_24_12_13(char* hex_data, char* fixed_parity);
/**
* Attempts to correct 16 hex words using the Reed-Solomon(24,16,9) FEC.
* \param data The packed 16 hex words, each of 6 chars, one after the other.
* \param parity The corresponding 8 hex words with the parity information.
* \return 1 if irrecoverable errors have been detected, 0 otherwise.
*/
int check_and_fix_reedsolomon_24_16_9(char* data, char* parity);
/**
* Encodes 12 hex words with the Reed-Solomon(24,16,9) FEC.
* \param hex_data The packed 16 hex words to encode.
* \fixed_parity The address were to store the 8 hex words with the Reed-Solomon parity.
*/
void encode_reedsolomon_24_16_9(char* hex_data, char* fixed_parity);
#ifdef __cplusplus
}
#endif
#endif // P25P1_CHECK_LDU_H_c1734445a67e47caa25673d7a4ce7520

26
include/p25p1_check_nid.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef P25P1_CHECK_NID_H_3af071e917ea43fdb51326e2cbfbde0a
#define P25P1_CHECK_NID_H_3af071e917ea43fdb51326e2cbfbde0a
#ifdef __cplusplus
extern "C" {
#endif
/**
* Checks a NID value, returns the fixed NAC, DUID and also an indication if it failed to decode the NID.
* \param bch_code Input. An array to the 63 bytes, each containing one bit of the NID. This includes the
* NAC (12 bits), DUID (4 bits) and 47 bits of BCH parity.
* \param new_nac Output. An address where to store the calculated NAC value after error correction. Should
* be large enough to accommodate for an integer.
* \param new_duid Output. An address where to store the calculated DUID value after error correction. Should
* be able to accommodate 3 chars.
* \param parity Input. The parity value read.
* \return 0 if there were errors processing the NID.
*/
int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity);
#ifdef __cplusplus
}
#endif
#endif // P25P1_CHECK_NID_H_3af071e917ea43fdb51326e2cbfbde0a

73
include/p25p1_hdu.h Normal file
View File

@@ -0,0 +1,73 @@
#ifndef P25P1_HDU_H_9f95c3a5072842e8aaf94444e1452d20
#define P25P1_HDU_H_9f95c3a5072842e8aaf94444e1452d20
#include "p25p1_heuristics.h"
/**
* Reads a dibit (two bits) from the stream of data. Takes into account the periodic occurrence of status
* bits that show up every 36 dibits. Status bits are discarded.
* \param opts A pointer the the DSD options.
* \param state A pointer the the DSD state structure.
* \param output A pointer to be filled with the dibit read. Two bytes used for each dibit.
* \param status_count Variable used to keep track of the status symbols in the dibit stream. There is one
* status every 36 dibits. With this counter we can skip the status.
* \param analog_signal A pointer with the actual analog value read from the input signal and that has been
* interpreted as the dibit we are returning in output.
* \param did_read_status Address were a boolean is returned. This is set to true when one status symbol was
* read (and skipped). This indicates that the sequence on dibits has been broken and is used by the P25
* heuristics.
*/
int read_dibit (dsd_opts* opts, dsd_state* state, char* output, int* status_count, int* analog_signal,
int* did_read_status);
/**
* Reads a number of dibits and stores its value together with the actual analog value that has been
* intrepreted as the dibit into an AnalogSignal structure. This is used later in the P25 heuristics module
* to improve the accuracy of the digitizer.
* \param opts A pointer the the DSD options.
* \param state A pointer the the DSD state structure.
* \param output A pointer to be filled with the dibits read.
* \param count The number of bits to read (remember: 1 dibit = 2 bits).
* \param status_count Variable used to keep track of the status symbols in the dibit stream. There is one
* status every 36 dibits. With this counter we can skip the status.
* \param analog_signal_array The pointer to the start of a sequence of AnalogSignal structures. The
* information is updated for each dibit read.
* \param analog_signal_index The actual index into the AnalogSignal array. This is increased here wich each
* dibit read.
*/
void read_dibit_update_analog_data (dsd_opts* opts, dsd_state* state, char* buffer, unsigned int count,
int* status_count, AnalogSignal* analog_signal_array, int* analog_signal_index);
/**
* Reads a word, made up of length bits or length/2 dibits. Also keeps track of the status dibits.
*/
void read_word (dsd_opts* opts, dsd_state* state, char* word, unsigned int length, int* status_count,
AnalogSignal* analog_signal_array, int* analog_signal_index);
/**
* Reads 4 bits from the stream of data. Those are the parity bits that follow each hex word.
*/
void read_hamm_parity (dsd_opts* opts, dsd_state* state, char* parity, int* status_count,
AnalogSignal* analog_signal_array, int* analog_signal_index);
/**
* Reads 12 bits from the stream of data. Those are the extended Golay parity bits that follow a word of data.
*/
void read_golay24_parity (dsd_opts* opts, dsd_state* state, char* parity, int* status_count, AnalogSignal* analog_signal_array, int* analog_signal_index);
/**
* Reads bits from the data stream that should all be zeros. This is valuable information for the
* heuristics module and we update it.
* \param opts A pointer the the DSD options.
* \param state A pointer the the DSD state structure.
* \param analog_signal_array The pointer to the start of a sequence of AnalogSignal structures.
* \param length Number of bits to read.
* \param status_count Variable used to keep track of the status symbols in the dibit stream.
* \param new_sequence Bool used to update the sequence_broken value of the first AnalogSignal element.
*/
void read_zeros(dsd_opts* opts, dsd_state* state, AnalogSignal* analog_signal_array, unsigned int length,
int* status_count, int new_sequence);
#endif // P25P1_HDU_H_9f95c3a5072842e8aaf94444e1452d20

View File

@@ -0,0 +1,91 @@
#ifndef P25P1_HEURISTICS_H_030dd3530b7546abbb56f8dd1e66a2f6
#define P25P1_HEURISTICS_H_030dd3530b7546abbb56f8dd1e66a2f6
#define HEURISTICS_SIZE 200
typedef struct
{
int values[HEURISTICS_SIZE];
float means[HEURISTICS_SIZE];
int index;
int count;
float sum;
float var_sum;
} SymbolHeuristics;
typedef struct
{
unsigned int bit_count;
unsigned int bit_error_count;
SymbolHeuristics symbols[4][4];
} P25Heuristics;
typedef struct
{
int value;
int dibit;
int corrected_dibit;
int sequence_broken;
} AnalogSignal;
#ifdef __cplusplus
extern "C"{
#endif
/**
* Initializes the heuristics state.
* \param heuristics The P25Heuristics structure to initialize.
*/
void initialize_p25_heuristics(P25Heuristics* heuristics);
/**
* Important method that estimates the most likely symbol for a given analog signal value and previous dibit.
* This is called by the digitizer.
* \param rf_mod Indicates the modulation used. The previous dibit is only used on C4FM.
* \param heuristics Pointer to the P25Heuristics module with all the needed state information.
* \param previous_dibit The previous dibit.
* \param analog_value The signal's analog value we want to interpret as a dibit.
* \param dibit Address were to store the estimated dibit.
* \return A boolean set to true if we are able to estimate a dibit. The reason why we might not be able
* to estimate it is because we don't have enough information to model the Gaussians (not enough data
* has been passed to contribute_to_heuristics).
*/
int estimate_symbol(int rf_mod, P25Heuristics* heuristics, int previous_dibit, int analog_value, int* dibit);
/**
* Log some useful information on the heuristics state.
*/
void debug_print_heuristics(P25Heuristics* heuristics);
/**
* This method contributes valuable information from dibits whose value we are confident is correct. We take
* the dibits and corresponding analog signal values to model the Gaussians for each dibit (and previous
* dibit if enabled).
* \param rf_mod Indicates the modulation used. The previous dibit is only used on C4FM.
* \param heuristics Pointer to the P25Heuristics module with all the needed state information.
* \param analog_signal_array Sequence of AnalogSignal which contain the cleared dibits and analog values.
* \param count number of cleared dibits passed (= number of elements to use from analog_signal_array).
*/
void contribute_to_heuristics(int rf_mod, P25Heuristics* heuristics, AnalogSignal* analog_signal_array, int count);
/**
* Updates the estimate for the BER (bit error rate). Mind this is method is not called for every single
* bit in the data stream but only for those bits over which we have an estimate of its error rate,
* specifically the bits that are protected by Reed-Solomon codes.
* \param heuristics The heuristics state.
* \param bits The number of bits we have read.
* \param errors The number of errors we estimate in those bits.
*/
void update_error_stats(P25Heuristics* heuristics, int bits, int errors);
/**
* Returns the estimate for the BER (bit error rate).
* \return The estimated BER. This is just the percentage of errors over the processed bits.
*/
float get_P25_BER_estimate(P25Heuristics* heuristics);
#ifdef __cplusplus
}
#endif
#endif // P25P1_HEURISTICS_H_030dd3530b7546abbb56f8dd1e66a2f6

49
include/p25p1_ldu.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef P25P1_LDU_H_a3c417fcb7804991b0e6d828066bd133
#define P25P1_LDU_H_a3c417fcb7804991b0e6d828066bd133
#include "dsd.h"
#include "p25p1_hdu.h"
#include "p25p1_const.h"
#include "p25p1_heuristics.h"
/**
* Separate imbe frames and deinterleave.
* This important methods read the IMBE data from the stream and passes it to the vocoder to produce audio.
* \param opts The DSD options.
* \param state The DSD state.
* \status_count An index that allows us to skip the status words interleaved every 36 dibit in the data
* stream.
*/
void process_IMBE (dsd_opts* opts, dsd_state* state, int* status_count);
/**
* Reads an hex word, its parity bits and attempts to error correct it using the Hamming FEC.
* \param opts The DSD options.
* \param state The DSD state.
* \param hex Pointer where to store the read hex word. Six bytes, one per bit.
* \param status_count An index that allows us to skip the status words interleaved every 36 dibit in the data
* stream.
* \param analog_singal_array Pointer to a sequence of AnalogSignal elements.
* \param analog_signal_index The current index in the analog_singal_array. This value is increased on each
* dibit read.
*/
void read_and_correct_hex_word (dsd_opts* opts, dsd_state* state, char* hex, int* status_count,
AnalogSignal* analog_signal_array, int* analog_signal_index);
/**
* Correct the information in analog_signal_array according with the content of data, which has been
* error corrected and should be valid.
* \param Dibits that have already been error corrected and we trust are correct.
* \count Number of dibits.
* \param analog_signal_array Pointer to a sequence of AnalogSignal elements, as many as the value of count.
*/
void correct_hamming_dibits(char* hex_data, int hex_count, AnalogSignal* analog_signal_array);
/**
* Logs some debug info.
*/
void debug_ldu_header(dsd_state* state);
#endif // P25P1_LDU_H_a3c417fcb7804991b0e6d828066bd133

6
include/pa_devs.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef PA_DEVS_H
#define PA_DEVS_H
void printPortAudioDevices();
#endif

10
install.sh Normal file
View File

@@ -0,0 +1,10 @@
sudo cp tone8.wav /usr/share/
sudo cp tone24.wav /usr/share/
sudo chmod 777 /usr/share/tone8.wav
sudo chmod 777 /usr/share/tone24.wav
mkdir build
cd build
cmake ..
make -j `nproc`
sudo make install
sudo ldconfig

View File

@@ -1,312 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
void
processHDU (dsd_opts * opts, dsd_state * state)
{
char mi[73], mfid[9], algid[9], kid[17], tgid[17], tmpstr[255];
int dibit, count, i, j;
long talkgroup;
int algidhex, kidhex;
mi[72] = 0;
mfid[8] = 0;
algid[8] = 0;
kid[16] = 0;
tgid[16] = 0;
skipDibit (opts, state, 25);
count = 57;
dibit = getDibit (opts, state);
mi[0] = (1 & (dibit >> 1)) + 48; // bit 1
mi[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[2] = (1 & (dibit >> 1)) + 48; // bit 1
mi[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[4] = (1 & (dibit >> 1)) + 48; // bit 1
mi[5] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
mi[6] = (1 & (dibit >> 1)) + 48; // bit 1
mi[7] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[8] = (1 & (dibit >> 1)) + 48; // bit 1
mi[9] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[10] = (1 & (dibit >> 1)) + 48; // bit 1
mi[11] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 7);
dibit = getDibit (opts, state);
mi[12] = (1 & (dibit >> 1)) + 48; // bit 1
mi[13] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[14] = (1 & (dibit >> 1)) + 48; // bit 1
mi[15] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[16] = (1 & (dibit >> 1)) + 48; // bit 1
mi[17] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
mi[18] = (1 & (dibit >> 1)) + 48; // bit 1
mi[19] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[20] = (1 & (dibit >> 1)) + 48; // bit 1
mi[21] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[22] = (1 & (dibit >> 1)) + 48; // bit 1
mi[23] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
mi[24] = (1 & (dibit >> 1)) + 48; // bit 1
mi[25] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[26] = (1 & (dibit >> 1)) + 48; // bit 1
mi[27] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[28] = (1 & (dibit >> 1)) + 48; // bit 1
mi[29] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
mi[30] = (1 & (dibit >> 1)) + 48; // bit 1
mi[31] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[32] = (1 & (dibit >> 1)) + 48; // bit 1
mi[33] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[34] = (1 & (dibit >> 1)) + 48; // bit 1
mi[35] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 7);
dibit = getDibit (opts, state);
mi[36] = (1 & (dibit >> 1)) + 48; // bit 1
mi[37] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[38] = (1 & (dibit >> 1)) + 48; // bit 1
mi[39] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[40] = (1 & (dibit >> 1)) + 48; // bit 1
mi[41] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
mi[42] = (1 & (dibit >> 1)) + 48; // bit 1
mi[43] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[44] = (1 & (dibit >> 1)) + 48; // bit 1
mi[45] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[46] = (1 & (dibit >> 1)) + 48; // bit 1
mi[47] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
mi[48] = (1 & (dibit >> 1)) + 48; // bit 1
mi[49] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[50] = (1 & (dibit >> 1)) + 48; // bit 1
mi[51] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[52] = (1 & (dibit >> 1)) + 48; // bit 1
mi[53] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
mi[54] = (1 & (dibit >> 1)) + 48; // bit 1
mi[55] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[56] = (1 & (dibit >> 1)) + 48; // bit 1
mi[57] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[58] = (1 & (dibit >> 1)) + 48; // bit 1
mi[59] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 7);
dibit = getDibit (opts, state);
mi[60] = (1 & (dibit >> 1)) + 48; // bit 1
mi[61] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[62] = (1 & (dibit >> 1)) + 48; // bit 1
mi[63] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[64] = (1 & (dibit >> 1)) + 48; // bit 1
mi[65] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
mi[66] = (1 & (dibit >> 1)) + 48; // bit 1
mi[67] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[68] = (1 & (dibit >> 1)) + 48; // bit 1
mi[69] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[70] = (1 & (dibit >> 1)) + 48; // bit 1
mi[71] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
mfid[0] = (1 & (dibit >> 1)) + 48; // bit 1
mfid[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mfid[2] = (1 & (dibit >> 1)) + 48; // bit 1
mfid[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mfid[4] = (1 & (dibit >> 1)) + 48; // bit 1
mfid[5] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
mfid[6] = (1 & (dibit >> 1)) + 48; // bit 1
mfid[7] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
algid[0] = (1 & (dibit >> 1)) + 48; // bit 1
algid[1] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 1);
dibit = getDibit (opts, state);
algid[2] = (1 & (dibit >> 1)) + 48; // bit 1
algid[3] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
algid[4] = (1 & (dibit >> 1)) + 48; // bit 1
algid[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
algid[6] = (1 & (dibit >> 1)) + 48; // bit 1
algid[7] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
kid[0] = (1 & (dibit >> 1)) + 48; // bit 1
kid[1] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
kid[2] = (1 & (dibit >> 1)) + 48; // bit 1
kid[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
kid[4] = (1 & (dibit >> 1)) + 48; // bit 1
kid[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
kid[6] = (1 & (dibit >> 1)) + 48; // bit 1
kid[7] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
kid[8] = (1 & (dibit >> 1)) + 48; // bit 1
kid[9] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
kid[10] = (1 & (dibit >> 1)) + 48; // bit 1
kid[11] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
kid[12] = (1 & (dibit >> 1)) + 48; // bit 1
kid[13] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
kid[14] = (1 & (dibit >> 1)) + 48; // bit 1
kid[15] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 1);
dibit = getDibit (opts, state);
tgid[0] = (1 & (dibit >> 1)) + 48; // bit 1
tgid[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
tgid[2] = (1 & (dibit >> 1)) + 48; // bit 1
tgid[3] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
tgid[4] = (1 & (dibit >> 1)) + 48; // bit 1
tgid[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
tgid[6] = (1 & (dibit >> 1)) + 48; // bit 1
tgid[7] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
tgid[8] = (1 & (dibit >> 1)) + 48; // bit 1
tgid[9] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
tgid[10] = (1 & (dibit >> 1)) + 48; // bit 1
tgid[11] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
tgid[12] = (1 & (dibit >> 1)) + 48; // bit 1
tgid[13] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
tgid[14] = (1 & (dibit >> 1)) + 48; // bit 1
tgid[15] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 160);
if (opts->p25enc == 1)
{
algidhex = strtol (algid, NULL, 2);
kidhex = strtol (kid, NULL, 2);
printf ("mi: %s algid: $%x kid: $%x\n", mi, algidhex, kidhex);
}
if (opts->p25lc == 1)
{
printf ("mfid: %s tgid: %s ", mfid, tgid);
if (opts->p25tg == 0)
{
printf ("\n");
}
}
j = 0;
if (strcmp (mfid, "10010000") == 0)
{
for (i = 4; i < 16; i++)
{
if (state->tgcount < 24)
{
state->tg[state->tgcount][j] = tgid[i];
}
tmpstr[j] = tgid[i];
j++;
}
tmpstr[12] = 48;
tmpstr[13] = 48;
tmpstr[14] = 48;
tmpstr[15] = 48;
}
else
{
for (i = 0; i < 16; i++)
{
if (state->tgcount < 24)
{
state->tg[state->tgcount][j] = tgid[i];
}
tmpstr[j] = tgid[i];
j++;
}
}
tmpstr[16] = 0;
talkgroup = strtol (tmpstr, NULL, 2);
state->lasttg = talkgroup;
if (opts->p25tg == 1)
{
printf ("tg: %li\n", talkgroup);
}
}

View File

@@ -1,295 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
#include "p25p1_const.h"
void
processLDU1 (dsd_opts * opts, dsd_state * state)
{
// extracts IMBE frames rom LDU frame
int i, j, k, dibit, stats, count, scount;
char imbe_fr[8][23];
char lcformat[9], mfid[9], lcinfo[57], lsd1[9], lsd2[9], status[25];
const int *w, *x, *y, *z;
lcformat[8] = 0;
mfid[8] = 0;
lcinfo[56] = 0;
lsd1[8] = 0;
lsd2[8] = 0;
status[24] = 0;
skipDibit (opts, state, 3);
status[0] = getDibit (opts, state) + 48;
skipDibit (opts, state, 21);
count = 57;
scount = 1;
if (opts->errorbars == 1)
{
printf ("e:");
}
// separate imbe frames and deinterleave
stats = 21; // we skip the status dibits that occur every 36 symbols
// the first IMBE frame starts 14 symbols before next status
// so we start counter at 22
for (i = 0; i < 9; i++)
{ // 9 IMBE frames per LDU
w = iW;
x = iX;
y = iY;
z = iZ;
for (j = 0; j < 72; j++)
{
if (stats == 35)
{
status[scount] = getDibit (opts, state) + 48;
scount++;
stats = 1;
count++;
}
else
{
stats++;
}
dibit = getDibit (opts, state);
count++;
imbe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
imbe_fr[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
processMbeFrame (opts, state, imbe_fr, NULL, NULL);
// skip over non imbe data sometimes between frames
if ((i < 4) || (i == 8))
{
k = 0;
}
else if (i == 7)
{
//k=16;
k = 0;
}
else
{
k = 20;
}
for (j = 0; j < k; j++)
{
if (stats == 35)
{
status[scount] = getDibit (opts, state) + 48;
scount++;
count++;
stats = 1;
}
else
{
stats++;
}
skipDibit (opts, state, 1);
count++;
}
if (i == 1)
{
dibit = getDibit (opts, state);
lcformat[0] = (1 & (dibit >> 1)) + 48; // bit 1
lcformat[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcformat[2] = (1 & (dibit >> 1)) + 48; // bit 1
lcformat[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcformat[4] = (1 & (dibit >> 1)) + 48; // bit 1
lcformat[5] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
lcformat[6] = (1 & (dibit >> 1)) + 48; // bit 1
lcformat[7] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mfid[0] = (1 & (dibit >> 1)) + 48; // bit 1
mfid[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mfid[2] = (1 & (dibit >> 1)) + 48; // bit 1
mfid[3] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
status[scount] = getDibit (opts, state) + 48;
scount++;
dibit = getDibit (opts, state);
mfid[4] = (1 & (dibit >> 1)) + 48; // bit 1
mfid[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mfid[6] = (1 & (dibit >> 1)) + 48; // bit 1
mfid[7] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[0] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[1] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
lcinfo[2] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[4] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[6] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[7] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
stats = 10;
}
else if (i == 2)
{
dibit = getDibit (opts, state);
lcinfo[8] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[9] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[10] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[11] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[12] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[13] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
lcinfo[14] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[15] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[16] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[17] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[18] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[19] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
lcinfo[20] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[21] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[22] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[23] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[24] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[25] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
lcinfo[26] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[27] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[28] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[29] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[30] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[31] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
stats = 32;
}
else if (i == 3)
{
dibit = getDibit (opts, state);
lcinfo[32] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[33] = (1 & dibit) + 48; // bit 0
status[scount] = getDibit (opts, state) + 48;
scount++;
dibit = getDibit (opts, state);
lcinfo[34] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[35] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[36] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[37] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
lcinfo[38] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[39] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[40] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[41] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[42] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[43] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
lcinfo[44] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[45] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[46] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[47] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[48] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[49] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
lcinfo[50] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[51] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[52] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[53] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[54] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[55] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
stats = 19;
}
else if (i == 7)
{
dibit = getDibit (opts, state);
lsd1[0] = (1 & (dibit >> 1)) + 48; // bit 1
lsd1[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lsd1[2] = (1 & (dibit >> 1)) + 48; // bit 1
lsd1[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lsd1[4] = (1 & (dibit >> 1)) + 48; // bit 1
lsd1[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lsd1[6] = (1 & (dibit >> 1)) + 48; // bit 1
lsd1[7] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 4);
dibit = getDibit (opts, state);
lsd2[0] = (1 & (dibit >> 1)) + 48; // bit 1
lsd2[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lsd2[2] = (1 & (dibit >> 1)) + 48; // bit 1
lsd2[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lsd2[4] = (1 & (dibit >> 1)) + 48; // bit 1
lsd2[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lsd2[6] = (1 & (dibit >> 1)) + 48; // bit 1
lsd2[7] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 4);
stats = 33;
}
}
// trailing status symbol
status[scount] = getDibit (opts, state) + 48;
scount++;
if (opts->errorbars == 1)
{
printf ("\n");
}
if (opts->p25status == 1)
{
printf ("status: %s lsd1: %s lsd2: %s\n", status, lsd1, lsd2);
}
processP25lcw (opts, state, lcformat, mfid, lcinfo);
}

View File

@@ -1,352 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
#include "p25p1_const.h"
void
processLDU2 (dsd_opts * opts, dsd_state * state)
{
// extracts IMBE frames rom LDU frame
int i, j, k, dibit, stats, count, scount;
char imbe_fr[8][23];
char mi[73], algid[9], kid[17], lsd3[9], lsd4[9], status[25];
const int *w, *x, *y, *z;
int algidhex, kidhex;
status[24] = 0;
mi[72] = 0;
algid[8] = 0;
kid[16] = 0;
lsd3[8] = 0;
lsd4[8] = 0;
skipDibit (opts, state, 3);
status[0] = getDibit (opts, state) + 48;
skipDibit (opts, state, 21);
scount = 1;
count = 57;
if (opts->errorbars == 1)
{
printf ("e:");
}
// separate imbe frames and deinterleave
stats = 21;
// we skip the status dibits that occur every 36 symbols
// the first IMBE frame starts 14 symbols before next status
// so we start counter at 22
for (i = 0; i < 9; i++)
{ // 9 IMBE frames per LDU
w = iW;
x = iX;
y = iY;
z = iZ;
for (j = 0; j < 72; j++)
{
if (stats == 35)
{
status[scount] = getDibit (opts, state) + 48;
scount++;
stats = 1;
count++;
}
else
{
stats++;
}
dibit = getDibit (opts, state);
count++;
imbe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
imbe_fr[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
processMbeFrame (opts, state, imbe_fr, NULL, NULL);
// skip over non imbe data sometimes between frames
if ((i < 5) || (i == 8))
{
k = 0;
}
else if (i == 7)
{
//k=16;
k = 0;
}
else
{
k = 20;
}
for (j = 0; j < k; j++)
{
if (stats == 35)
{
status[scount] = getDibit (opts, state) + 48;
scount++;
count++;
stats = 1;
}
else
{
stats++;
}
skipDibit (opts, state, 1);
count++;
}
if (i == 1)
{
dibit = getDibit (opts, state);
mi[0] = (1 & (dibit >> 1)) + 48; // bit 1
mi[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[2] = (1 & (dibit >> 1)) + 48; // bit 1
mi[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[4] = (1 & (dibit >> 1)) + 48; // bit 1
mi[5] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
mi[6] = (1 & (dibit >> 1)) + 48; // bit 1
mi[7] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[8] = (1 & (dibit >> 1)) + 48; // bit 1
mi[9] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[10] = (1 & (dibit >> 1)) + 48; // bit 1
mi[11] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
status[scount] = getDibit (opts, state) + 48;
scount++;
dibit = getDibit (opts, state);
mi[12] = (1 & (dibit >> 1)) + 48; // bit 1
mi[13] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[14] = (1 & (dibit >> 1)) + 48; // bit 1
mi[15] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[16] = (1 & (dibit >> 1)) + 48; // bit 1
mi[17] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
mi[18] = (1 & (dibit >> 1)) + 48; // bit 1
mi[19] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[20] = (1 & (dibit >> 1)) + 48; // bit 1
mi[21] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[22] = (1 & (dibit >> 1)) + 48; // bit 1
mi[23] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
stats = 10;
}
else if (i == 2)
{
dibit = getDibit (opts, state);
mi[24] = (1 & (dibit >> 1)) + 48; // bit 1
mi[25] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[26] = (1 & (dibit >> 1)) + 48; // bit 1
mi[27] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[28] = (1 & (dibit >> 1)) + 48; // bit 1
mi[29] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
mi[30] = (1 & (dibit >> 1)) + 48; // bit 1
mi[31] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[32] = (1 & (dibit >> 1)) + 48; // bit 1
mi[33] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[34] = (1 & (dibit >> 1)) + 48; // bit 1
mi[35] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
mi[36] = (1 & (dibit >> 1)) + 48; // bit 1
mi[37] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[38] = (1 & (dibit >> 1)) + 48; // bit 1
mi[39] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[40] = (1 & (dibit >> 1)) + 48; // bit 1
mi[41] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
mi[42] = (1 & (dibit >> 1)) + 48; // bit 1
mi[43] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[44] = (1 & (dibit >> 1)) + 48; // bit 1
mi[45] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[46] = (1 & (dibit >> 1)) + 48; // bit 1
mi[47] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
stats = 32;
}
else if (i == 3)
{
dibit = getDibit (opts, state);
mi[48] = (1 & (dibit >> 1)) + 48; // bit 1
mi[49] = (1 & dibit) + 48; // bit 0
status[scount] = getDibit (opts, state) + 48;
scount++;
dibit = getDibit (opts, state);
mi[50] = (1 & (dibit >> 1)) + 48; // bit 1
mi[51] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[52] = (1 & (dibit >> 1)) + 48; // bit 1
mi[53] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
mi[54] = (1 & (dibit >> 1)) + 48; // bit 1
mi[55] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[56] = (1 & (dibit >> 1)) + 48; // bit 1
mi[57] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[58] = (1 & (dibit >> 1)) + 48; // bit 1
mi[59] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
mi[60] = (1 & (dibit >> 1)) + 48; // bit 1
mi[61] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[62] = (1 & (dibit >> 1)) + 48; // bit 1
mi[63] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[64] = (1 & (dibit >> 1)) + 48; // bit 1
mi[65] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
mi[66] = (1 & (dibit >> 1)) + 48; // bit 1
mi[67] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[68] = (1 & (dibit >> 1)) + 48; // bit 1
mi[69] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mi[70] = (1 & (dibit >> 1)) + 48; // bit 1
mi[71] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
stats = 19;
}
else if (i == 4)
{
dibit = getDibit (opts, state);
algid[0] = (1 & (dibit >> 1)) + 48; // bit 1
algid[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
algid[2] = (1 & (dibit >> 1)) + 48; // bit 1
algid[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
algid[4] = (1 & (dibit >> 1)) + 48; // bit 1
algid[5] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
algid[6] = (1 & (dibit >> 1)) + 48; // bit 1
algid[7] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
kid[0] = (1 & (dibit >> 1)) + 48; // bit 1
kid[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
kid[2] = (1 & (dibit >> 1)) + 48; // bit 1
kid[3] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
dibit = getDibit (opts, state);
kid[4] = (1 & (dibit >> 1)) + 48; // bit 1
kid[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
kid[6] = (1 & (dibit >> 1)) + 48; // bit 1
kid[7] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
kid[8] = (1 & (dibit >> 1)) + 48; // bit 1
kid[9] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 1);
status[scount] = getDibit (opts, state) + 48;
scount++;
skipDibit (opts, state, 1);
dibit = getDibit (opts, state);
kid[10] = (1 & (dibit >> 1)) + 48; // bit 1
kid[11] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
kid[12] = (1 & (dibit >> 1)) + 48; // bit 1
kid[13] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
kid[14] = (1 & (dibit >> 1)) + 48; // bit 1
kid[15] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 2);
stats = 6;
}
else if (i == 7)
{
dibit = getDibit (opts, state);
lsd3[0] = (1 & (dibit >> 1)) + 48; // bit 1
lsd3[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lsd3[2] = (1 & (dibit >> 1)) + 48; // bit 1
lsd3[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lsd3[4] = (1 & (dibit >> 1)) + 48; // bit 1
lsd3[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lsd3[6] = (1 & (dibit >> 1)) + 48; // bit 1
lsd3[7] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 4);
dibit = getDibit (opts, state);
lsd4[0] = (1 & (dibit >> 1)) + 48; // bit 1
lsd4[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lsd4[2] = (1 & (dibit >> 1)) + 48; // bit 1
lsd4[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lsd4[4] = (1 & (dibit >> 1)) + 48; // bit 1
lsd4[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lsd4[6] = (1 & (dibit >> 1)) + 48; // bit 1
lsd4[7] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 4);
stats = 33;
}
}
//trailing status symbol
status[scount] = getDibit (opts, state) + 48;
scount++;
if (opts->errorbars == 1)
{
printf ("\n");
}
if (opts->p25status == 1)
{
printf ("status: %s lsd3: %s lsd4: %s\n", status, lsd3, lsd4);
}
if (opts->p25enc == 1)
{
algidhex = strtol (algid, NULL, 2);
kidhex = strtol (kid, NULL, 2);
printf ("mi: %s algid: $%x kid: $%x\n", mi, algidhex, kidhex);
}
}

View File

@@ -1,158 +0,0 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
void
processTDULC (dsd_opts * opts, dsd_state * state)
{
char lcinfo[57], lcformat[9], mfid[9];
int dibit, count;
lcformat[8] = 0;
mfid[8] = 0;
lcinfo[56] = 0;
skipDibit (opts, state, 25);
count = 57;
dibit = getDibit (opts, state);
lcformat[0] = (1 & (dibit >> 1)) + 48; // bit 1
lcformat[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcformat[2] = (1 & (dibit >> 1)) + 48; // bit 1
lcformat[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcformat[4] = (1 & (dibit >> 1)) + 48; // bit 1
lcformat[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcformat[6] = (1 & (dibit >> 1)) + 48; // bit 1
lcformat[7] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mfid[0] = (1 & (dibit >> 1)) + 48; // bit 1
mfid[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mfid[2] = (1 & (dibit >> 1)) + 48; // bit 1
mfid[3] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
mfid[4] = (1 & (dibit >> 1)) + 48; // bit 1
mfid[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
mfid[6] = (1 & (dibit >> 1)) + 48; // bit 1
mfid[7] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 1);
dibit = getDibit (opts, state);
lcinfo[0] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[1] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[2] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[3] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[4] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[5] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[6] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[7] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
lcinfo[8] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[9] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[10] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[11] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[12] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[13] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[14] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[15] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[16] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[17] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[18] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[19] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
lcinfo[20] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[21] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[22] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[23] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[24] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[25] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[26] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[27] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[28] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[29] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[30] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[31] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
lcinfo[32] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[33] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 1);
dibit = getDibit (opts, state);
lcinfo[34] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[35] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[36] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[37] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[38] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[39] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[40] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[41] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[42] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[43] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 6);
dibit = getDibit (opts, state);
lcinfo[44] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[45] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[46] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[47] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[48] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[49] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[50] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[51] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[52] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[53] = (1 & dibit) + 48; // bit 0
dibit = getDibit (opts, state);
lcinfo[54] = (1 & (dibit >> 1)) + 48; // bit 1
lcinfo[55] = (1 & dibit) + 48; // bit 0
skipDibit (opts, state, 91);
processP25lcw (opts, state, lcformat, mfid, lcinfo);
}

21
rebuild.sh Normal file
View File

@@ -0,0 +1,21 @@
#! /bin/bash
clear
echo DSD-FME Digital Speech Decoder - Florida Man Edition
echo Automatic Git Pull and Rebuild
echo
sleep 1
##Open your clone folder##
git pull https://github.com/lwvmobile/dsd-fme pulseaudio
sleep 2
sudo cp tone8.wav /usr/share/
sudo cp tone24.wav /usr/share/
sudo chmod 777 /usr/share/tone8.wav
sudo chmod 777 /usr/share/tone24.wav
##cd into your build folder##
cd build
##cmake usually isn't necesary, but could be if I update the cmakelist.txt
cmake ..
make -j `nproc`
sudo make install
sudo ldconfig

83
src/Hamming.cpp Normal file
View File

@@ -0,0 +1,83 @@
#include "Hamming.hpp"
// definition outside class declaration
Hamming_10_6_3_data Hamming_10_6_3::data;
// definition outside class declaration
// Should go *after* Hamming_10_6_3_data since Hamming_10_6_3_TableImpl_data constructor uses
// Hamming_10_6_3 which in its turn depends on Hamming_10_6_3_data
Hamming_10_6_3_TableImpl_data Hamming_10_6_3_TableImpl::data;
int Hamming_10_6_3::decode(std::bitset<10>& input)
{
int error_count;
// Compute syndromes
int s0 = ((data.h0 & input).count() & 1) << 3;
int s1 = ((data.h1 & input).count() & 1) << 2;
int s2 = ((data.h2 & input).count() & 1) << 1;
int s3 = ((data.h3 & input).count() & 1);
int parity = s0 | s1 | s2 | s3;
if (parity == 0) {
//std::cout << input << " is OK" << std::endl;
error_count = 0;
} else {
// Error detected, attempt to fix it
int bad_bit_index = data.bad_bit_table[parity];
if (bad_bit_index < 0) {
//std::cout << "Irrecoverable error: " << std::bitset<6>(input.to_string()) << std::endl;
error_count = 2;
} else {
// Error in a data bit, or more than one error
// If there is one erroneous bit, the bad one is the one indicated by parity
if (bad_bit_index < 4) {
// Error detected in a parity bit
//std::cout << "Error on parity bit " << bad_bit_index << std::endl;
error_count = 1;
} else {
input.flip(bad_bit_index);
//std::cout << "Error on data bit " << bad_bit_index << ", hopefully fixed: " << std::bitset<6>(input.to_string()) << std::endl;
error_count = 1;
}
}
}
//std::cout << std::endl;
return error_count;
}
int Hamming_10_6_3::encode(std::bitset<6>& input)
{
// Compute syndromes
int s0 = ((data.gt0 & input).count() & 1) << 3;
int s1 = ((data.gt1 & input).count() & 1) << 2;
int s2 = ((data.gt2 & input).count() & 1) << 1;
int s3 = ((data.gt3 & input).count() & 1);
int parity = s0 | s1 | s2 | s3;
return parity;
}
int Hamming_10_6_3_TableImpl::decode(int input, int* output)
{
assert (input < 1024 && input >= 0);
// Making use of a table...
*output = data.fixed_values[input];
return data.error_counts[input];
}
int Hamming_10_6_3_TableImpl::encode(int input)
{
assert (input < 64 && input >= 0);
// Making use of a table...
return data.encode_parities[input];
}

560
src/bptc.c Normal file
View File

@@ -0,0 +1,560 @@
/*
============================================================================
Name : bptc.c
Author : MotoTRBO_Encrypted_DSD_Author
Version : 1.0
Date : 2017 - December - 11
Copyright : None
Description : BPTC library source
============================================================================
*/
/* Include ------------------------------------------------------------------*/
#include "dsd.h"
/* Define -------------------------------------------------------------------*/
/* Global variables ---------------------------------------------------------*/
const uint8_t BPTCInterleavingIndex[196] =
{
0, 181, 166, 151,
136, 121, 106, 91,
76, 61, 46, 31,
16, 1, 182, 167,
152, 137, 122, 107,
92, 77, 62, 47,
32, 17, 2, 183,
168, 153, 138, 123,
108, 93, 78, 63,
48, 33, 18, 3,
184, 169, 154, 139,
124, 109, 94, 79,
64, 49, 34, 19,
4, 185, 170, 155,
140, 125, 110, 95,
80, 65, 50, 35,
20, 5, 186, 171,
156, 141, 126, 111,
96, 81, 66, 51,
36, 21, 6, 187,
172, 157, 142, 127,
112, 97, 82, 67,
52, 37, 22, 7,
188, 173, 158, 143,
128, 113, 98, 83,
68, 53, 38, 23,
8, 189, 174, 159,
144, 129, 114, 99,
84, 69, 54, 39,
24, 9, 190, 175,
160, 145, 130, 115,
100, 85, 70, 55,
40, 25, 10, 191,
176, 161, 146, 131,
116, 101, 86, 71,
56, 41, 26, 11,
192, 177, 162, 147,
132, 117, 102, 87,
72, 57, 42, 27,
12, 193, 178, 163,
148, 133, 118, 103,
88, 73, 58, 43,
28, 13, 194, 179,
164, 149, 134, 119,
104, 89, 74, 59,
44, 29, 14, 195,
180, 165, 150, 135,
120, 105, 90, 75,
60, 45, 30, 15
};
const uint8_t BPTCDeInterleavingIndex[196] =
{
0, 13, 26, 39,
52, 65, 78, 91,
104, 117, 130, 143,
156, 169, 182, 195,
12, 25, 38, 51,
64, 77, 90, 103,
116, 129, 142, 155,
168, 181, 194, 11,
24, 37, 50, 63,
76, 89, 102, 115,
128, 141, 154, 167,
180, 193, 10, 23,
36, 49, 62, 75,
88, 101, 114, 127,
140, 153, 166, 179,
192, 9, 22, 35,
48, 61, 74, 87,
100, 113, 126, 139,
152, 165, 178, 191,
8, 21, 34, 47,
60, 73, 86, 99,
112, 125, 138, 151,
164, 177, 190, 7,
20, 33, 46, 59,
72, 85, 98, 111,
124, 137, 150, 163,
176, 189, 6, 19,
32, 45, 58, 71,
84, 97, 110, 123,
136, 149, 162, 175,
188, 5, 18, 31,
44, 57, 70, 83,
96, 109, 122, 135,
148, 161, 174, 187,
4, 17, 30, 43,
56, 69, 82, 95,
108, 121, 134, 147,
160, 173, 186, 3,
16, 29, 42, 55,
68, 81, 94, 107,
120, 133, 146, 159,
172, 185, 2, 15,
28, 41, 54, 67,
80, 93, 106, 119,
132, 145, 158, 171,
184, 1, 14, 27,
40, 53, 66, 79,
92, 105, 118, 131,
144, 157, 170, 183
};
const uint8_t DeInterleaveReverseChannelBptc[32] =
{
0, 17, 2, 19, 4, 21, 6, 23,
8, 25, 10, 27, 12, 29, 14, 31,
16, 1, 18, 3, 20, 5, 22, 7,
24, 9, 26, 11, 28, 13, 30, 15
};
/* Functions ----------------------------------------------------------------*/
/*
* @brief : This function deinterleave the DMR data by using BPTC (196,96)
*
* @param Input : Pointer of DMR input data interleaved (196 bytes)
*
* @param Output : Pointer where DMR deinterleaved data will be written (196 bytes)
*
* @return None
*/
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output)
{
uint32_t i, DeInterleaveIndex;
for(i = 0; i < 196; i++)
{
DeInterleaveIndex = BPTCDeInterleavingIndex[i];
Output[DeInterleaveIndex] = (Input[i] & 1);
}
} /* End BPTCDeInterleaveDMRData() */
/*
* @brief : This function extract the 96 bits of a deinteleaved 196 bits
* buffer using BPTC (196,96)
*
* @param InputDeInteleavedData : Pointer of DMR input data deinterleaved (196 bytes)
*
* @param DMRDataExtracted : Pointer where the DMR data will be written (96 bytes)
*
* @return The total number of irrecoverable Hamming check errors
*/
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3])
{
uint32_t i, j, k;
uint8_t DataMatrix[13][15];
uint8_t LineUncorrected[15];
uint8_t LineCorrected[11];
uint8_t ColumnUncorrected[13];
uint8_t ColumnCorrected[9];
uint32_t HammingIrrecoverableErrorNb = 0;
/* First step : Reconstitute the BPTC 15x11 matrix
* Note : Input data shall be deinterleaved */
k = 1; /* Discard R(3) bit - See DMR standard chapter B1.1 BPTC (196,96) */
for(i = 0; i < 13; i++)
{
for(j = 0; j < 15; j++)
{
/* Only the LSBit of the byte is stored */
DataMatrix[i][j] = (InputDeInteleavedData[k] & 1);
k++;
}
}
/* R(0) to R(2) may be used to transport some
* Restricted Access System (RAS) information,
* So save these three bytes before reset all of them
* See patent US 2013/0288643 A1 */
R[0] = DataMatrix[0][2]; /* Save R(0) */
R[1] = DataMatrix[0][1]; /* Save R(1) */
R[2] = DataMatrix[0][0]; /* Save R(2) */
/* Set to 0 R(0) to R(2) - See DMR standard chapter B1.1 BPTC (196,96) */
DataMatrix[0][0] = 0; /* R(2) */
DataMatrix[0][1] = 0; /* R(1) */
DataMatrix[0][2] = 0; /* R(0) */
/* Init the Hamming (15,11,3) library
* Not needed because it has already been done
* in the "InitAllFecFunction()" function */
//Hamming_15_11_init();
/* Init the Hamming (13,9,3) library
* Not needed because it has already been done
* in the "InitAllFecFunction()" function */
//Hamming_13_9_init();
/* Init the Hamming error counter */
HammingIrrecoverableErrorNb = 0;
/* Process the the Hamming (15,11,3) code
* check on each line (first time).
* Do not check the 4th last lines */
for(i = 0; i < 9; i++)
{
/* Get a full line */
for(j = 0; j < 15; j++)
{
LineUncorrected[j] = DataMatrix[i][j];
}
/* Apply Hamming (15,11,3) code correction */
if(Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
}
/* Re-inject the line in the matrix (only the util data [11 bit],
* not the Hamming part [4 bit]) */
for(j = 0; j < 11; j++)
{
DataMatrix[i][j] = LineCorrected[j];
}
}
/* Process the the Hamming (15,11,3) code
* check on each column/row (first time) */
for(i = 0; i < 15; i++)
{
/* Get a full column */
for(j = 0; j < 13; j++)
{
ColumnUncorrected[j] = DataMatrix[j][i];
}
/* Apply Hamming (15,11,3) code correction */
if(Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
}
/* Re-inject the column in the matrix (only the util data [11 bit],
* not the Hamming part [4 bit]) */
for(j = 0; j < 9; j++)
{
DataMatrix[j][i] = ColumnCorrected[j];
}
}
/* The first Hamming code check has maybe corrected bit
* witch can be use to correct other bit, so make the
* same operation twice */
/* Reset the number of irrecoverable errors */
HammingIrrecoverableErrorNb = 0;
/* Process the the Hamming (15,11,3) code
* check on each line (second time).
* Do not check the 4th last lines */
for(i = 0; i < 9; i++)
{
/* Get a full line */
for(j = 0; j < 15; j++)
{
LineUncorrected[j] = DataMatrix[i][j];
}
/* Apply Hamming (15,11,3) code correction */
if(Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
{
/* Row/Line contains irrecoverable errors */
HammingIrrecoverableErrorNb++;
}
else
{
/* Row/Line contains correct data */
}
/* Re-inject the line in the matrix (only the util data [11 bit],
* not the Hamming part [4 bit]) */
for(j = 0; j < 11; j++)
{
DataMatrix[i][j] = LineCorrected[j];
}
}
/* Process the the Hamming (15,11,3) code
* check on each column/row (second time) */
for(i = 0; i < 15; i++)
{
/* Get a full column */
for(j = 0; j < 13; j++)
{
ColumnUncorrected[j] = DataMatrix[j][i];
}
/* Apply Hamming (15,11,3) code correction */
if(Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
{
/* Row/Line contains irrecoverable errors */
HammingIrrecoverableErrorNb++;
}
else
{
/* Row/Line contains correct data */
}
/* Re-inject the column in the matrix (only the util data [11 bit],
* not the Hamming part [4 bit]) */
for(j = 0; j < 9; j++)
{
DataMatrix[j][i] = ColumnCorrected[j];
}
}
/* Extract the DMR data (96 bit) from the matrix */
/* First line : Do not take R(2), R(1) and R(0) */
k = 0;
for(i = 3; i < 11; i++)
{
DMRDataExtracted[k] = DataMatrix[0][i];
k++;
}
/* Next lines */
for(i = 1; i < 9; i++)
{
for(j = 0; j < 11; j++)
{
DMRDataExtracted[k] = DataMatrix[i][j];
k++;
}
}
/* Return the number of irrecoverable Hamming errors */
return HammingIrrecoverableErrorNb;
} /* End BPTC_196x96_Extract_Data() */
/*
* @brief : This function extract the 77 bits of a deinteleaved 128 bits
* buffer using BPTC (128,77).
*
* @note : See DMR standard ETSI TS 102 361-1 chapter B.2.1 for details
*
* @param InputDeInteleavedData : Pointer of DMR input data deinterleaved (128 bytes
act as a 16 x 8 byte data matrix)
*
* @param DMRDataExtracted : Pointer where the DMR data will be written (77 bytes)
* DMRDataExtracted[0..71] = Util data (hamming corrected)
* DMRDataExtracted[72..76] = 5 bit of CRC extracted (to be checked)
*
* @return 0 = All is OK
* Other = Error - Irrecoverable Hamming parity check error number
*/
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77])
{
uint32_t i, j, k;
uint8_t DataMatrix[8][16];
uint8_t LineUncorrected[16];
uint8_t LineCorrected[11];
uint32_t HammingIrrecoverableErrorNb = 0;
uint32_t ParityCheckErrorNb = 0;
uint32_t NbOfOne;
/* First step : Reconstitute the BPTC 16x8 matrix */
for(i = 0; i < 8; i++)
{
for(j = 0; j < 16; j++)
{
/* Only the LSBit of the byte is stored */
DataMatrix[i][j] = (InputDataMatrix[i][j] & 1);
}
}
/* Init the Hamming (16,11,4) library
* Not needed because it has already been done
* in the "InitAllFecFunction()" function */
//Hamming_16_11_4_init();
/* Process the the Hamming (16,11,4) code
* check on each line.
* Do not check the last line (line nb 8) */
for(i = 0; i < 7; i++)
{
/* Get a full line */
for(j = 0; j < 16; j++)
{
LineUncorrected[j] = DataMatrix[i][j];
}
/* Apply Hamming (16,11,4) code correction */
if(Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
}
/* Re-inject the line in the matrix (only the util data [11 bit],
* not the Hamming part [4 bit]) */
for(j = 0; j < 11; j++)
{
DataMatrix[i][j] = LineCorrected[j];
}
}
/* Extract the DMR data (77 bit) from the matrix */
k = 0;
/* 2 first lines */
for(i = 0; i < 2; i++)
{
for(j = 0; j < 11; j++)
{
DMRDataExtracted[k] = DataMatrix[i][j];
k++;
}
}
/* 5 Next lines */
for(i = 2; i < 7; i++)
{
for(j = 0; j < 10; j++)
{
DMRDataExtracted[k] = DataMatrix[i][j];
k++;
}
}
/* 5 bit of CRC */
for(i = 2; i < 7; i++)
{
DMRDataExtracted[k] = DataMatrix[i][10];
k++;
}
/* Verify the data integrity by checking
* all column parity bit */
ParityCheckErrorNb = 0;
for(i = 0; i < 16; i++)
{
NbOfOne = 0;
/* Get a full column */
for(j = 0; j < 7; j++)
{
NbOfOne += DataMatrix[j][i];
}
/* Check the parity bit (number of "1"
* must be even) */
if((NbOfOne % 2) != DataMatrix[7][i])
{
ParityCheckErrorNb++;
}
}
/* Return the number of irrecoverable Hamming errors +
* the number of parity check error */
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
} /* End BPTC_128x77_Extract_Data() */
/*
* @brief : This function extract the 32 bits of a deinteleaved 32 bits
* buffer using Reverse Channel Single Burst BPTC.
*
* @note : See DMR standard ETSI TS 102 361-1 chapter B.2.2.2 for details
*
* @param InputInterleavedData : Pointer of DMR input data interleaved (32 bytes
act as a 16 x 2 bits data matrix)
*
* @param DMRDataExtracted : Pointer where the DMR data will be written (77 bytes)
* DMRDataExtracted[0..10] = Util data (Hamming corrected)
* DMRDataExtracted[11..15] = Hamming data
* DMRDataExtracted[16..31] = 16 bits of odd parity (to be checked)
*
* @return 0 = All is OK
* Other = Error - Irrecoverable Hamming parity check error number
*/
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32])
{
uint32_t i;
uint8_t DataMatrix[32];
uint8_t LineUncorrected[16];
uint8_t LineCorrected[11];
uint32_t HammingIrrecoverableErrorNb = 0;
uint32_t ParityCheckErrorNb = 0;
for(i = 0; i < 32; i++)
{
DataMatrix[DeInterleaveReverseChannelBptc[i]] = InputInterleavedData[i] & 1;
}
/* Copy the entire input buffer to the output buffer */
for(i = 0; i < 32; i++)
{
DMRDataExtracted[i] = DataMatrix[i];
}
/* Prepare the line to be corrected */
for(i = 0; i < 16; i++)
{
LineUncorrected[i] = DataMatrix[i];
}
/* Apply Hamming (16,11,4) code correction */
if(Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
{
HammingIrrecoverableErrorNb++;
}
/* Copy the corrected content to the output buffer */
for(i = 0; i < 11; i++)
{
DMRDataExtracted[i] = LineCorrected[i];
}
/* Check Parity bits */
for(i = 0; i < 16; i++)
{
/* Odd parity ==> If data = 1 then parity = 0
* If data = 0 then parity = 1 */
if(DMRDataExtracted[i] == DMRDataExtracted[i + 16]) ParityCheckErrorNb++;
}
//fprintf(stderr, "Hamming ERR=%u ; Parity ERR=%u", HammingIrrecoverableErrorNb, ParityCheckErrorNb);
/* Return the number of irrecoverable Hamming errors +
* the number of parity check error */
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
} /* End BPTC_16x2_Extract_Data() */
/* End of file */

889
src/dmr_bs.c Normal file
View File

@@ -0,0 +1,889 @@
#include "dsd.h"
#include "dmr_const.h"
//A subroutine for processing each TDMA frame individually to allow for
//processing voice and/or data on both BS slots (channels) simultaneously
void dmrBS (dsd_opts * opts, dsd_state * state)
{
int i, j, k, l, dibit;
int *dibit_p;
char ambe_fr[4][24] = {0};
char ambe_fr2[4][24] = {0};
char ambe_fr3[4][24] = {0};
const int *w, *x, *y, *z;
char sync[25] = {0};
char syncdata[48] = {0};
//char cachdata[13] = {0};
int mutecurrentslot;
int msMode;
char cc[4];
unsigned char EmbeddedSignalling[16] = {0};
int EmbeddedSignallingOk;
unsigned int internalcolorcode;
int internalslot;
char redundancyA[36];
char redundancyB[36];
unsigned short int vc1;
unsigned short int vc2;
int cach_okay = 0;
//cach fec
char cachdata[25] = {0}; //increased from 24 to 25 to see if this fixes ubuntu 32 bug
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
//add time to mirror printFrameSync
time_t now;
char * getTime(void) //get pretty hh:mm:ss timestamp
{
time_t t = time(NULL);
char * curr;
char * stamp = asctime(localtime( & t));
curr = strtok(stamp, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
return curr;
}
//Init slot lights
sprintf (state->slot1light, " slot1 ");
sprintf (state->slot2light, " slot2 ");
//Init the color code status
state->color_code_ok = 0;
vc1 = 2;
vc2 = 2;
short int loop = 1;
short int skipcount = 0;
//Run Loop while the getting is good
while (loop == 1) {
// CACH
internalslot = -1; //reset here so we know if this value is being set properly
for(i = 0; i < 12; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i] = dibit;
cachdata[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
}
//decode and correct cach and compare
cach_okay = 69;
if ( Hamming_7_4_decode (cachdata) ) //see if we need to de-interleave this...probably do.
{
cach_okay = 1;
}
if (cach_okay == 1)
{
//fprintf (stderr, "CACH Okay ");
}
if (cach_okay != 1)
{
//fprintf (stderr, "CACH FEC Error %d", cach_okay);
if (opts->aggressive_framesync == 1)
{
goto END;
}
}
state->currentslot = cachdata[1];
internalslot = cachdata[1];
//this cach fec seems to work quite well, really helps on shaky signals
//Setup for first AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//First AMBE Frame, Full 36
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+12] = dibit;
redundancyA[i] = dibit;
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//check for repetitive data if caught in a 'no carrier' loop? Just picking random values.
//this will test for no carrier (input signal) and return us to no sync state if necessary
if (redundancyA[16] == redundancyB[16] && redundancyA[27] == redundancyB[27] &&
redundancyA[01] == redundancyB[01] && redundancyA[32] == redundancyB[32] &&
redundancyA[03] == redundancyB[03] && redundancyA[33] == redundancyB[33] &&
redundancyA[13] == redundancyB[13] && redundancyA[07] == redundancyB[07] )
{
goto END;
}
// end redundancy test, set B to A
for(i = 0; i < 36; i++)
{
redundancyB[i] = redundancyA[i];
}
//Setup for Second AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//Second AMBE Frame, First Half 18 dibits just before Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+48] = dibit;
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//short int l = 0; //do I still use this?
// signaling data or sync
long long int late_entry_pi[24] = {0};
short int m = 0;
for(i = 0; i < 24; i++)
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i+66] = dibit;
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
syncdata[(2*i)+1] = (1 & dibit); // bit 0
sync[i] = (dibit | 1) + 48;
if (i < 4 || i > 19)
{
// fprintf(stderr, "\n EMB = ");
EmbeddedSignalling[(2*i)] = (1 & (dibit >> 1)); // bit 1
//fprintf (stderr, "%02b", dibit);
EmbeddedSignalling[(2*i)+1] = (1 & dibit); // bit 0
}
//Used for ProcessVoiceBurstSync
if(internalslot == 0 && vc1 > 1 && vc1 < 6) //grab on vc1 values 2-5 B C D and E
{
/* Time slot 1 superframe buffer filling => SYNC data */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[vc1-1].Sync[i*2] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[vc1-1].Sync[i*2+1] = (1 & dibit); // bit 0
}
if(internalslot == 1 && vc2 > 1 && vc2 < 6) //grab on vc2 values 2-5 B C D and E
{
/* Time slot 2 superframe buffer filling => SYNC data */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[vc2-1].Sync[i*2] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[vc2-1].Sync[i*2+1] = (1 & dibit); // bit 0
}
}
sync[24] = 0;
EmbeddedSignallingOk = -1;
if(QR_16_7_6_decode(EmbeddedSignalling))
{
EmbeddedSignallingOk = 1;
}
internalcolorcode = 69; //set so we know if this value is being set properly
if( EmbeddedSignallingOk == 1 ) //don't set on 1?
{
//state->color_code = (unsigned int)((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
internalcolorcode = (unsigned int)((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
state->color_code_ok = EmbeddedSignallingOk;
//Power Indicator, not the other PI (header)
state->PI = (unsigned int)EmbeddedSignalling[4];
state->PI_ok = EmbeddedSignallingOk;
//Link Control Start Stop Indicator
state->LCSS = (unsigned int)((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
state->LCSS_ok = EmbeddedSignallingOk;
for (i = 0; i < 8; i++)
{
//fprintf (stderr, " %b", EmbeddedSignalling[i]);
}
}
//else skipcount++;
else
{
for (i = 0; i < 8; i++)
{
//fprintf (stderr, " %b", EmbeddedSignalling[i]);
}
}
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
//dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i+90] = dibit;
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//Setup for Third AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//Third AMBE Frame, Full 36
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
//dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i+108] = dibit;
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//reset vc counters to 1 if new voice sync frame on each slot
if ( strcmp (sync, DMR_BS_VOICE_SYNC) == 0)
{
if (internalslot == 0)
{
vc1 = 1;
state->DMRvcL = 0; //reset here if internal sync forces reset
}
if (internalslot == 1)
{
vc2 = 1;
state->DMRvcR = 0; //reset here if internal sync forces reset
}
}
// if( strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0)
// {
// fprintf (stderr,"%s ", getTime());
// fprintf (stderr, " Sync: DMR TDMA Direct Mode TS 1 \n");
// goto SKIP;
// }
//
// if( strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
// {
// fprintf (stderr,"%s ", getTime());
// fprintf (stderr, " Sync: DMR TDMA Direct Mode TS 2 \n");
// goto SKIP;
// }
//check for sync pattern here after collected the rest of the payload, decide what to do with it
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 )
{
fprintf (stderr,"%s ", getTime());
if (internalslot == 0)
{
sprintf(state->slot1light, "[slot1]");
sprintf(state->slot2light, " slot2 ");
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR ");
}
else fprintf (stderr,"Sync: -DMR ");
//constantly reset the vc counter to 1 each data frame in anticipation of new voice frame
vc1 = 1;
}
if (internalslot == 1)
{
sprintf(state->slot2light, "[slot2]");
sprintf(state->slot1light, " slot1 ");
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR ");
}
else fprintf (stderr,"Sync: -DMR ");
//constantly reset the vc counter to 1 each data frame in anticipation of new voice frame
vc2 = 1;
}
processDMRdata (opts, state);
skipcount++; //after 2 data frames, drop back to getFrameSync and process subsequent data with processDMRdata
goto SKIP;
}
//find way to mitigate or correct coming back here after leaving in inverted signal
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 && opts->inverted_dmr == 1)
{
skipcount++;
goto SKIP;
}
//extra check to see if we have missed a voice/data frame sync at this point and then exit if required
if (1 == 1) //opts->aggressive_framesync
{
//extremely aggressive
if ( vc1 > 7 || vc2 > 7 )
{
goto END;
}
}
//only play voice on no data sync
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0)
{
if (EmbeddedSignallingOk == 0)
{
//goto END;
}
skipcount = 0; //reset skip count if processing voice frames
fprintf (stderr,"%s ", getTime());
if (internalslot == 0)
{
state->dmrburstL = 16; //use 16 for Voice?
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR [SLOT1] slot2 | | DMRSTEREO | VC%d ",vc1);
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
else
{
fprintf (stderr,"Sync: -DMR [SLOT1] slot2 | | DMRSTEREO | VC%d ",vc1);
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
}
if (internalslot == 1)
{
state->dmrburstR = 16; //use 16 for Voice?
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR slot1 [SLOT2] | | DMRSTEREO | VC%d ",vc2);
if (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10)
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if (state->DMRvcR == 0 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
else
{
fprintf (stderr,"Sync: -DMR slot1 [SLOT2] | | DMRSTEREO | VC%d ",vc2);
if (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10)
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if (state->DMRvcR == 0 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
}
if (internalslot == 0 && vc1 == 6) //presumably when full (and no sync issues)
{
//process voice burst
ProcessVoiceBurstSync(opts, state);
fprintf (stderr, "\n");
}
if (internalslot == 1 && vc2 == 6) //presumably when full (and no sync issues)
{
//process voice burst
ProcessVoiceBurstSync(opts, state);
fprintf (stderr, "\n");
}
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
if (internalslot == 0 && vc1 == 6)
{
state->DMRvcL = 0;
if (state->payload_algid >= 0x21)
{
if (state->payload_mi != 0 && state->payload_algid >= 0x21)
{
LFSR(state);
}
fprintf (stderr, "\n");
}
}
if (internalslot == 1 && vc2 == 6)
{
state->DMRvcR = 0;
if (state->payload_algidR >= 0x21)
{
if (state->payload_miR != 0 && state->payload_algidR >= 0x21)
{
LFSR(state);
}
fprintf (stderr, "\n");
}
}
if (internalslot == 0)
{
vc1++;
}
if (internalslot == 1)
{
vc2++;
}
}
// recalibrate center/umid/lmid, not sure this helps or does anything particularly in this case
state->center = ((state->max) + (state->min)) / 2;
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
//Escape conditions to break the loop
//7 seems optimal, allows for an extra frame each in case of late entry on dual voices
if ( (vc1 > 7 && vc2 > 7) )
{
goto END;
}
//14 probably allows a few more in case of a bit of jitter but eventually breaks loop (1 second?)
if ( (vc1 > 14 || vc2 > 14) )
{
goto END;
}
//earlier skip conditions teleport us here to process further skip conditions before restarting while loop
SKIP:
if (skipcount > 2) //after 2 consecutive data frames, drop back to getFrameSync and process with processDMRdata
{
goto END;
}
//since we are in a while loop, run ncursesPrinter here.
if (opts->use_ncurses_terminal == 1)
{
ncursesPrinter(opts, state);
}
} // while loop
//Teleport here on END condition, set or reset variables and return to look for new frame sync
END:
state->dmr_stereo = 0;
state->errs2R = 0;
state->errs2 = 0;
//if we have a cach err, then produce sync pattern/err message
if (cach_okay != 1)
{
fprintf (stderr,"%s ", getTime());
fprintf (stderr,"Sync: DMR ");
fprintf (stderr, "%s", KRED);
fprintf (stderr, "| ** VOICE CACH ERR ** ");
fprintf (stderr, "%s", KNRM);
//run LFSR if either slot had an active MI in it.
state->currentslot = 0;
if (state->payload_algid >= 0x21) LFSR(state);
state->currentslot = 1;
if (state->payload_algidR >= 0x21) LFSR(state);
fprintf (stderr, "\n");
}
}
//Process buffered half frame and 2nd half and then jump to full BS decoding
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
{
int i, j, k, l, dibit;
int *dibit_p;
char ambe_fr[4][24] = {0};
char ambe_fr2[4][24] = {0};
char ambe_fr3[4][24] = {0};
const int *w, *x, *y, *z;
char sync[25];
char syncdata[48];
int mutecurrentslot;
int msMode;
char cc[4];
unsigned char EmbeddedSignalling[16];
unsigned int EmbeddedSignallingOk;
//cach fec
char cachdata[25] = {0}; //increased from 24 to 25 to see if this fixes ubuntu 32 bug
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
//add time to mirror printFrameSync
time_t now;
char * getTime(void) //get pretty hh:mm:ss timestamp
{
time_t t = time(NULL);
char * curr;
char * stamp = asctime(localtime( & t));
curr = strtok(stamp, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
return curr;
}
//payload buffer
//CACH + First Half Payload + Sync = 12 + 54 + 24
dibit_p = state->dmr_payload_p - 90;
for (i = 0; i < 90; i++) //90
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i] = dibit;
}
for(i = 0; i < 12; i++)
{
dibit = state->dmr_stereo_payload[i];
cachdata[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
}
//decode and correct cach and compare
int cach_okay = 69;
if ( Hamming_7_4_decode (cachdata) ) //see if we need to de-interleave this...probably do.
{
cach_okay = 1;
}
if (cach_okay == 1)
{
//fprintf (stderr, "CACH Okay ");
}
if (cach_okay != 1)
{
//fprintf (stderr, "CACH FEC Error %d", cach_okay);
if (opts->aggressive_framesync == 1)
{
goto END;
}
}
state->currentslot = cachdata[1];
//Setup for first AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//First AMBE Frame, Full 36
for(i = 0; i < 36; i++)
{
dibit = state->dmr_stereo_payload[i+12];
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//Setup for Second AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//Second AMBE Frame, First Half 18 dibits just before Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = state->dmr_stereo_payload[i+48];
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
// signaling data or sync, just redo it
for(i = 0; i < 24; i++)
{
dibit = state->dmr_stereo_payload[i+66];
sync[i] = (dibit | 1) + 48;
}
sync[24] = 0;
//this method seems to work to make sure we aren't in a data sync especially when inverted signal
//consider only testing this when inverted signal if this causes issues with sync later on
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
{
goto END;
}
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//Setup for Third AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//Third AMBE Frame, Full 36
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
fprintf (stderr,"%s ", getTime());
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR | Frame Sync | DMRSTEREO | VC1 ");
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) )
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) )
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
else
{
fprintf (stderr,"Sync: -DMR | Frame Sync | DMRSTEREO | VC1 ");
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) )
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) )
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
state->DMRvcL = 0;
state->DMRvcR = 0;
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode
END:
//if we have a cach err, then produce sync pattern/err message
if (cach_okay != 1)
{
fprintf (stderr,"%s ", getTime());
fprintf (stderr,"Sync: DMR ");
fprintf (stderr, "%s", KRED);
fprintf (stderr, "| ** VOICE CACH ERR ** ");
fprintf (stderr, "%s", KNRM);
//run LFSR if either slot had an active MI in it.
state->currentslot = 0;
if (state->payload_algid >= 0x21) LFSR(state);
state->currentslot = 1;
if (state->payload_algidR >= 0x21) LFSR(state);
fprintf (stderr, "\n");
}
}

571
src/dmr_data.c Normal file
View File

@@ -0,0 +1,571 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
void
processDMRdata (dsd_opts * opts, dsd_state * state)
{
int i, dibit;
int *dibit_p;
char sync[25];
char syncdata[48];
char cachdata[25] = {0};
char cc[5] = {0};
char ccAscii[5] = {0};
char bursttype[5];
unsigned int burst;
char info[196];
unsigned char SlotType[20];
unsigned int SlotTypeOk;
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
#ifdef DMR_DUMP
char syncbits[49];
char cachbits[25];
int k;
#endif
ccAscii[4] = 0;
bursttype[4] = 0;
dibit_p = state->dmr_payload_p - 90;
// CACH
for (i = 0; i < 12; i++)
{
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if (state->dmr_stereo == 1)
{
dibit = (int)state->dmr_stereo_payload[i];
}
cachdata[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
}
//decode and correct cach and compare
int cach_okay = 69;
if ( Hamming_7_4_decode (cachdata) ) //is now de-interleaved appropriately
{
cach_okay = 1;
}
if (cach_okay == 1)
{
//fprintf (stderr, "CACH Okay ");
}
if (cach_okay != 1)
{
//fprintf (stderr, "CACH FEC Error %d", cach_okay);
if (opts->aggressive_framesync == 1) //may not worry about it on data
{
goto END;
}
}
state->currentslot = cachdata[1];
//minor bugfix for MSData when MSData has accumulated data blocks
//(i.e. data header and 1/2 rate data), force all data into slot 0
if (state->dmr_ms_mode == 1)
{
state->currentslot = 0;
}
if (state->currentslot == 0 && state->dmr_ms_mode == 0)
{
state->slot1light[0] = '[';
state->slot1light[6] = ']';
state->slot2light[0] = ' ';
state->slot2light[6] = ' ';
}
//else
if (state->currentslot == 1 && state->dmr_ms_mode == 0)
{
state->slot2light[0] = '[';
state->slot2light[6] = ']';
state->slot1light[0] = ' ';
state->slot1light[6] = ' ';
}
//end correct cach and set slot
#ifdef DMR_DUMP
k = 0;
for (i = 0; i < 12; i++)
{
dibit = cachdata[i];
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
cachbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
cachbits[24] = 0;
fprintf(stderr, "%s ", cachbits);
#endif
//trellis bits
unsigned char trellisdibits[98];
// Current slot - First half - Data Payload - 1st part
for (i = 0; i < 49; i++)
{
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if (state->dmr_stereo == 1)
{
dibit = (int)state->dmr_stereo_payload[i+12];
}
trellisdibits[i] = dibit;
info[2*i] = (1 & (dibit >> 1)); // bit 1
info[(2*i)+1] = (1 & dibit); // bit 0
}
// slot type
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if (state->dmr_stereo == 1)
{
dibit = (int)state->dmr_stereo_payload[61]; //61, not i+61
}
cc[0] = (1 & (dibit >> 1)); // bit 1
cc[1] = (1 & dibit); // bit 0
SlotType[0] = (1 & (dibit >> 1)); // bit 1
SlotType[1] = (1 & dibit); // bit 0
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if (state->dmr_stereo == 1)
{
dibit = (int)state->dmr_stereo_payload[62];
}
cc[2] = (1 & (dibit >> 1)); // bit 1
cc[3] = (1 & dibit); // bit 0
SlotType[2] = (1 & (dibit >> 1)); // bit 1
SlotType[3] = (1 & dibit); // bit 0
ccAscii[0] = cc[0] + '0';
ccAscii[1] = cc[1] + '0';
ccAscii[2] = cc[2] + '0';
ccAscii[3] = cc[3] + '0';
ccAscii[4] = cc[4] + '\0';
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if (state->dmr_stereo == 1) //state
{
dibit = (int)state->dmr_stereo_payload[63]; //(int)
}
SlotType[4] = (1 & (dibit >> 1)); // bit 1
SlotType[5] = (1 & dibit); // bit 0
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if (state->dmr_stereo == 1) //state
{
dibit = (int)state->dmr_stereo_payload[64]; //(int)
}
SlotType[6] = (1 & (dibit >> 1)); // bit 1
SlotType[7] = (1 & dibit); // bit 0
// Parity bit
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if (state->dmr_stereo == 1)
{
dibit = (int)state->dmr_stereo_payload[65];
}
SlotType[8] = (1 & (dibit >> 1)); // bit 1
SlotType[9] = (1 & dibit); // bit 0
// signalling data or sync
for (i = 0; i < 24; i++)
{
dibit = *dibit_p;
dibit_p++;
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
if (state->dmr_stereo == 1)
{
dibit = (int)state->dmr_stereo_payload[i+66];
}
syncdata[2*i] = (1 & (dibit >> 1)); // bit 1
syncdata[(2*i)+1] = (1 & dibit); // bit 0
sync[i] = (dibit | 1) + 48;
}
sync[24] = 0;
#ifdef DMR_DUMP
k = 0;
for (i = 0; i < 24; i++)
{
syncbits[k] = syncdata[(k*2)] + 48; // bit 1
k++;
syncbits[k] = syncdata[(k*2)+1] + 48; // bit 0
k++;
}
syncbits[48] = 0;
fprintf(stderr, "%s ", syncbits);
#endif
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) )//|| (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
{
if (state->currentslot == 0)
{
sprintf(state->slot1light, "[slot1]");
}
else
{
sprintf(state->slot2light, "[slot2]");
}
}
else if(strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0)
{
state->currentslot = 0;
sprintf(state->slot1light, "[sLoT1]");
}
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
{
state->currentslot = 1;
sprintf(state->slot2light, "[sLoT2]");
}
if (opts->errorbars == 1 && state->dmr_ms_mode == 0)
{
fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
}
// Slot type - Second part - Parity bit
for (i = 0; i < 5; i++)
{
if (state->dmr_stereo == 0) //only get dibits if not using dmr_stereo method
{
dibit = getDibit(opts, state);
}
if (opts->inverted_dmr == 1)
{
//dibit = (dibit ^ 2);
}
if (state->dmr_stereo == 1)
{
dibit = (int)state->dmr_stereo_payload[i+90]; //double checked these i+ values make sure we are on the right ones
}
SlotType[(i*2) + 10] = (1 & (dibit >> 1)); // bit 1
SlotType[(i*2) + 11] = (1 & dibit); // bit 0
}
/* Check and correct the SlotType (apply Golay(20,8) FEC check) */
// golay (20,8) hamming-weight of 6 reliably corrects at most 2 bit-errors
if( Golay_20_8_decode(SlotType) )
{
SlotTypeOk = 1;
//fprintf (stderr, "Slot Okay ");
}
else
{
if (opts->aggressive_framesync == 1)
{
SlotTypeOk = 0;
goto END;
}
}
/* Slot Type parity checked => Fill the color code */
state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0);
state->color_code_ok = SlotTypeOk;
/* Reconstitute the burst type */
//consider assigning this only when slottypeok or similar check passes, eliminate bad burst types from decoding randomly
burst = (unsigned int)((SlotType[4] << 3) + (SlotType[5] << 2) + (SlotType[6] << 1) + SlotType[7]);
if (state->currentslot == 0)
{
state->dmrburstL = burst;
}
if (state->currentslot == 1)
{
state->dmrburstR = burst;
}
/* Reconstitute the burst type */
bursttype[0] = SlotType[4] + '0';
bursttype[1] = SlotType[5] + '0';
bursttype[2] = SlotType[6] + '0';
bursttype[3] = SlotType[7] + '0';
bursttype[4] = '\0';
if (strcmp (bursttype, "0000") == 0)
{
sprintf(state->fsubtype, " PI Header ");
}
else if (strcmp (bursttype, "0001") == 0)
{
sprintf(state->fsubtype, " VOICE LC ");
}
else if (strcmp (bursttype, "0010") == 0)
{
sprintf(state->fsubtype, " TLC ");
}
else if (strcmp (bursttype, "0011") == 0)
{
sprintf(state->fsubtype, " CSBK ");
}
else if (strcmp (bursttype, "0100") == 0)
{
sprintf(state->fsubtype, " MBC Header ");
}
else if (strcmp (bursttype, "0101") == 0)
{
sprintf(state->fsubtype, " MBC ");
}
else if (strcmp (bursttype, "0110") == 0)
{
sprintf(state->fsubtype, " DATA Header ");
}
else if (strcmp (bursttype, "0111") == 0)
{
sprintf(state->fsubtype, " RATE 1/2 DATA");
}
else if (strcmp (bursttype, "1000") == 0)
{
sprintf(state->fsubtype, " RATE 3/4 DATA");
}
else if (strcmp (bursttype, "1001") == 0)
{
sprintf(state->fsubtype, " Slot idle ");
}
else if (strcmp (bursttype, "1010") == 0)
{
sprintf(state->fsubtype, " Rate 1 DATA ");
}
else
{
sprintf(state->fsubtype, " ");
}
// Current slot - First half - Data Payload - 1st part
//trellis
for (i = 0; i < 49; i++)
{
if (state->dmr_stereo == 0) //only get dibits if not using dmr_stereo method
{
dibit = getDibit(opts, state);
}
if (opts->inverted_dmr == 1)
{
//dibit = (dibit ^ 2);
}
if (state->dmr_stereo == 1)
{
dibit = (int)state->dmr_stereo_payload[i+95]; //double checked these i+ values make sure we are on the right ones
}
trellisdibits[i+49] = dibit;
info[(2*i) + 98] = (1 & (dibit >> 1)); // bit 1
info[(2*i) + 99] = (1 & dibit); // bit 0
}
// Skip cach (24 bit = 12 dibit) and next slot 1st half (98 + 10 bit = 49 + 5 dibit)
if (state->dmr_stereo == 0)
{
skipDibit (opts, state, 12 + 49 + 5);
}
if (opts->errorbars == 1)
{
/* Print the color code */
//if (burst == 0b1001 && state->color_code_ok) //only print on idle for data types, otherwise prints on voice frames.
if (SlotTypeOk == 1)
{
fprintf (stderr, "%s", KCYN);
fprintf(stderr, "| Color Code=%02d ", (int)state->color_code);
fprintf (stderr, "%s", KNRM);
//state->dmr_color_code = state->color_code;
}
else fprintf(stderr, "| ");
//if(state->color_code_ok) fprintf(stderr, "(OK) |");
if(state->color_code_ok) fprintf(stderr, "| (CRC OK ) |"); //add line break
else
{
fprintf (stderr, "%s", KRED);
fprintf(stderr, "| (CRC ERR) |");
fprintf (stderr, "%s", KNRM);
}
//fprintf (stderr, "\n"); //print line break
if (strcmp (state->fsubtype, " ") == 0)
{
//fprintf(stderr, " Unknown burst type: %s", bursttype);
fprintf(stderr, " Unknown");
}
else
{
fprintf(stderr, "%s", state->fsubtype);
}
}
switch(burst)
{
/* Burst = PI header */
case 0b0000:
{
ProcessDmrPIHeader(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = Voice LC header */
case 0b0001:
{
/* Extract data from Voice LC Header */
ProcessDmrVoiceLcHeader(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = TLC */
case 0b0010:
{
//Not sure if I really care about this in the context of what it shows
ProcessDmrTerminaisonLC(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = CSBK */
case 0b0011:
{
//going to disable until I can find more useful info to present
ProcessCSBK(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = MBC Header */
case 0b0100:
{
ProcessMBChData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = MBC Continuation*/
case 0b0101:
{
ProcessMBCData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = DATA Header */
case 0b0110:
{
ProcessDataData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = RATE 1/2 DATA */
case 0b0111:
{
Process12Data(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = RATE 3/4 DATA */
case 0b1000:
{
//need to keep working on improving the trellis decode, but is partially viable now
Process34Data(opts, state, trellisdibits, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = Slot idle */
case 0b1001:
{
if(state->color_code_ok && state->dmr_stereo == 0) state->dmr_color_code = state->color_code; //try setting this on idle if crc ok
break;
}
/* Burst = Rate 1 DATA */
case 0b1010:
{
Process1Data(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Burst = Unified Single Block DATA */
case 0b1101:
{
ProcessUnifiedData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
/* Default */
default:
{
/* Nothing to do */
ProcessReservedData(opts, state, (uint8_t *)info, (uint8_t *)syncdata, (uint8_t *)SlotType);
break;
}
} /* End switch(burst) */
if (opts->errorbars == 1)
{
fprintf(stderr, "\n");
}
END:
if (SlotTypeOk == 0 || cach_okay != 1)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, "| ** CACH or Burst Type FEC ERR ** ");
fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n");
}
} /* End processDMRdata() */

942
src/dmr_ms.c Normal file
View File

@@ -0,0 +1,942 @@
#include "dsd.h"
#include "dmr_const.h"
//A subroutine for processing MS voice
void dmrMS (dsd_opts * opts, dsd_state * state)
{
//fprintf (stderr, "\n Quick DMR Voice Testing\n");
int i, j, k, l, dibit;
int *dibit_p;
char ambe_fr[4][24] = {0};
char ambe_fr2[4][24] = {0};
char ambe_fr3[4][24] = {0};
char ambe_fr4[4][24] = {0};
char t_ambe_fr[4][24] = {9};
char t_ambe_fr2[4][24] = {9};
char t_ambe_fr3[4][24] = {9};
const int *w, *x, *y, *z;
char sync[25];
char syncdata[48];
//standalone inbound rc sync exists on dibits 48-96?
//or 44-92? of each potential payload, middle 10 ms or 30 ms frame
char rcsync[25];
char rcsyncdata[96]; //96 bit RC sync data
char cachdata[13] = {0};
int mutecurrentslot;
int msMode;
char cc[4];
unsigned char EmbeddedSignalling[16];
unsigned char EmbeddedSignallingInverse[16];
int EmbeddedSignallingOk;
unsigned int internalcolorcode;
int internalslot;
int activeslot;
char redundancyA[36];
char redundancyB[36];
unsigned short int vc1;
unsigned short int vc2;
//add time to mirror printFrameSync
time_t now;
char * getTime(void) //get pretty hh:mm:ss timestamp
{
time_t t = time(NULL);
char * curr;
char * stamp = asctime(localtime( & t));
curr = strtok(stamp, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
return curr;
}
//Init the color code status
state->color_code_ok = 0;
vc1 = 2;
vc2 = 2;
short int loop = 1;
short int skipcount = 0;
//Hardset variables for MS
state->currentslot = 0; //0
internalslot = 0;
activeslot = 0;
//Run Loop while the getting is good
while (loop == 1) {
short int m = 0;
state->dmrburstL = 16; //Use 16 for Voice
for(i = 0; i < 12; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
cachdata[i] = dibit;
state->dmr_stereo_payload[i] = dibit;
}
//Setup for first AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//First AMBE Frame, Full 36
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i+12] = dibit;
redundancyA[i] = dibit;
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//check for repetitive data if caught in a 'no carrier' loop? Just picking random values.
//this will test for no carrier (input signal) and return us to no sync state if necessary
if (redundancyA[16] == redundancyB[16] && redundancyA[27] == redundancyB[27] &&
redundancyA[01] == redundancyB[01] && redundancyA[32] == redundancyB[32] &&
redundancyA[03] == redundancyB[03] && redundancyA[33] == redundancyB[33] &&
redundancyA[13] == redundancyB[13] && redundancyA[07] == redundancyB[07] )
{
//goto END;
}
for(i = 0; i < 36; i++)
{
redundancyB[i] = redundancyA[i];
}
//Setup for Second AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//Second AMBE Frame, First Half 18 dibits just before Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i+48] = dibit;
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
// signaling data or sync
for(i = 0; i < 24; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i+66] = dibit;
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
syncdata[(2*i)+1] = (1 & dibit); // bit 0
sync[i] = (dibit | 1) + 48;
if (i < 4 || i > 19) //
{
EmbeddedSignalling[2*i] = (1 & (dibit >> 1)); // bit 1
EmbeddedSignalling[2*i+1] = (1 & dibit); // bit 0
}
// load the superframe to do the voiceburstsync processing
if(internalslot == 0 && vc1 > 1 && vc1 < 6) //grab on vc1 values 2-5 B C D and E
{
/* Time slot 1 superframe buffer filling => SYNC data */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[vc1-1].Sync[i*2] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[vc1-1].Sync[i*2+1] = (1 & dibit); // bit 0
}
if(internalslot == 1 && vc2 > 1 && vc2 < 6) //grab on vc2 values 2-5 B C D and E
{
/* Time slot 2 superframe buffer filling => SYNC data */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[vc2-1].Sync[i*2] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[vc2-1].Sync[i*2+1] = (1 & dibit); // bit 0
}
}
sync[24] = 0;
EmbeddedSignallingOk = -1;
// for (i = 0; i < 16; i++)
// {
// EmbeddedSignallingInverse[i] = EmbeddedSignalling[i] ^ 1;
// }
if(QR_16_7_6_decode(EmbeddedSignalling))
{
EmbeddedSignallingOk = 1;
}
//test for inverted signal - this should move to data, and have a slottype check here perhaps?
// else if (QR_16_7_6_decode(EmbeddedSignallingInverse))
// {
// if (opts->inverted_dmr == 0)
// {
// opts->inverted_dmr = 1;
// }
// else opts->inverted_dmr = 0;
// goto END;
// }
internalcolorcode = 69; //set so we know if this value is being set properly
if(EmbeddedSignallingOk == 1)
{
state->color_code = (unsigned int)((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
internalcolorcode = (unsigned int)((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
state->color_code_ok = EmbeddedSignallingOk;
//Power Indicator, not the other PI (header)
state->PI = (unsigned int)EmbeddedSignalling[4];
state->PI_ok = EmbeddedSignallingOk;
//Link Control Start Stop Indicator
state->LCSS = (unsigned int)((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
state->LCSS_ok = EmbeddedSignallingOk;
}
//else skipcount++;
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i+90] = dibit;
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
//WIP for RC inbound sync data, part 2, second 24 bits (12 dibits)
if (i <= 12)
{
rcsyncdata[i+12] = (dibit | 1) + 48; //test and double check positions for this sync data
}
}
//Setup for Third AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//Third AMBE Frame, Full 36
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i+108] = dibit;
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
if ( ((strcmp (sync, DMR_MS_VOICE_SYNC) == 0) && opts->inverted_dmr == 0) ||
((strcmp (sync, DMR_MS_DATA_SYNC) == 0) && opts->inverted_dmr == 1) )
{
if (internalslot == 0)
{
vc1 = 1;
state->DMRvcL = 0; //reset here if jitter/sync forces reset
fprintf (stderr, "MS Slot 1 Voice Sync \n");
}
if (internalslot == 1)
{
vc2 = 1;
state->DMRvcR = 0; //reset here if jitter/sync forces reset
}
state->dmr_ms_mode = 1; //set to 1 here??
}
state->dmr_ms_mode = 1; //set to 1 here?
//don't know if we will catch an RC sync in here or not, should be on VC6 if it occurs
if ( (strcmp (rcsync, DMR_RC_DATA_SYNC) == 0) || (strcmp (sync, DMR_RC_DATA_SYNC) == 0) ) //should be in same position as sync
{
fprintf (stderr,"%s ", getTime());
if (internalslot == 0)
{
sprintf(state->slot1light, "[slot1]");
sprintf(state->slot2light, " slot2 ");
//fprintf (stderr,"Sync: +DMR [slot1] slot2 | Color Code=%02d | DMRSTEREO | RC ", state->color_code);
fprintf (stderr,"Sync: +DMR MS/DM MODE | Color Code=%02d | DMRSTEREO | RC ", state->color_code);
//test with vc1 reset disabled, if all is well, leave disabled
//vc1 = 1;
}
if (internalslot == 1)
{
sprintf(state->slot2light, "[slot2]");
sprintf(state->slot1light, " slot1 ");
//fprintf (stderr,"Sync: +DMR slot1 [slot2] | Color Code=%02d | DMRSTEREO | RC ", state->color_code);
fprintf (stderr,"Sync: +DMR MS/DM MODE | Color Code=%02d | DMRSTEREO | RC ", state->color_code);
//test with vc1 reset disabled, if all is well, leave disabled
//vc2 = 1;
}
fprintf (stderr, "\n "); //21 spaces to line up the sync
//processDMRdata (opts, state);
state->dmr_ms_rc = 1; //don't know what to do with this, if anything...
goto SKIP;
}
//check for sync pattern here after collected the rest of the payload, decide what to do with it
//if ( (strcmp (sync, DMR_MS_DATA_SYNC) == 0) )
//fixed to compensate for inverted signal
if ( ((strcmp (sync, DMR_MS_DATA_SYNC) == 0) && opts->inverted_dmr == 0) ||
((strcmp (sync, DMR_MS_VOICE_SYNC) == 0) && opts->inverted_dmr == 1) )
{
fprintf (stderr,"%s ", getTime());
if (internalslot == 0)
{
sprintf(state->slot1light, "[slot1]");
sprintf(state->slot2light, " slot2 ");
//fprintf (stderr,"Sync: +DMR [slot1] slot2 | Color Code=%02d | DMRSTEREO | MS Data ", state->color_code);
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR ");
}
else fprintf (stderr,"Sync: -DMR ");
//test with vc1 reset disabled, if all is well, leave disabled
//vc1 = 1;
}
if (internalslot == 1)
{
sprintf(state->slot2light, "[slot2]");
sprintf(state->slot1light, " slot1 ");
//fprintf (stderr,"Sync: +DMR slot1 [slot2] | Color Code=%02d | DMRSTEREO | Data ", state->color_code);
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR MS/DM MODE ");
}
else fprintf (stderr,"Sync: -DMR MS/DM MODE ");
//test with vc1 reset disabled, if all is well, leave disabled
//vc2 = 1;
}
//fprintf (stderr, "\n "); //21 spaces to line up the sync
//fprintf (stderr, " "); //21 spaces to line up the sync
state->dmr_ms_mode = 1; //set to 1 here??
processDMRdata (opts, state);
skipcount++;
//goto SKIP;
goto END;
}
if( ( ( ((strcmp (sync, DMR_MS_DATA_SYNC) != 0) && opts->inverted_dmr == 0) ||
((strcmp (sync, DMR_MS_VOICE_SYNC) != 0) && opts->inverted_dmr == 1) ) )
&& internalslot == activeslot && vc1 < 7)
{
skipcount = 0; //reset skip count if processing voice frames
fprintf (stderr,"%s ", getTime());
if (internalslot == 0 && opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR MS/DM MODE | Color Code=%02d | DMRSTEREO | VC%d ", state->color_code, vc1);
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
if (internalslot == 0 && opts->inverted_dmr == 1)
{
fprintf (stderr,"Sync: -DMR MS/DM MODE | Color Code=%02d | DMRSTEREO | VC%d ", state->color_code, vc1);
if (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10)
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68)
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "\n");
}
if (internalslot == 0 && vc1 == 6) //presumably when full (and no sync issues)
{
//process voice burst
ProcessVoiceBurstSync(opts, state);
fprintf (stderr, "\n");
}
state->dmr_ms_mode == 1;
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
//state->directmode = 0; //flag off for testing
if (state->directmode == 0)
{
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
else
{
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
if (vc1 == 6 && state->payload_algid >= 21)
{
state->DMRvcL = 0;
if (state->payload_mi != 0 && state->payload_algid >= 0x21)
{
LFSR(state);
}
fprintf (stderr, "\n");
}
}
vc1++;
//6 seems optimal for MS, allows to quickly exit and resync if wonky or something
if ( (vc1 > 6) || skipcount > 2) //escape to find new sync if deadspins without a renewed voice frame sync inside of loop
{
goto END;
}
SKIP:
skipDibit (opts, state, 144); //skip to next tdma channel
state->dmr_ms_rc = 0;
//since we are in a repetitive loop, run ncursesPrinter here
if (opts->use_ncurses_terminal == 1)
{
ncursesPrinter(opts, state);
}
} // end while loop
//reset these variables when exiting MS/DM mode.
END:
//get first half payload dibits and store them in the payload for the next repitition
skipDibit (opts, state, 144);
//CACH + First Half Payload = 12 + 54
for (i = 0; i < 66; i++) //66
{
dibit = getDibit(opts, state);
if (opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i] = dibit;
}
state->dmr_stereo = 0;
state->dmr_ms_mode = 0;
state->dmr_ms_rc = 0;
state->directmode = 0; //flag off
state->DMRvcL = 0;
}
//collect buffered 1st half and get 2nd half voice payload and then jump to full MS Voice decoding.
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
{
int i, j, k, l, dibit;
int *dibit_p;
char ambe_fr[4][24] = {0};
char ambe_fr2[4][24] = {0};
char ambe_fr3[4][24] = {0};
char ambe_fr4[4][24] = {0}; //shim for playing back direct mode samples where ambe1 is bad, duplicate 2 to 4 and play both to smooth audio
const int *w, *x, *y, *z;
char sync[25];
char syncdata[48];
char cachdata[13] = {0};
int mutecurrentslot;
int msMode;
char cc[4];
unsigned char EmbeddedSignalling[16];
unsigned int EmbeddedSignallingOk;
//add time to mirror sync
time_t now;
char * getTime(void) //get pretty hh:mm:ss timestamp
{
time_t t = time(NULL);
char * curr;
char * stamp = asctime(localtime( & t));
curr = strtok(stamp, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
return curr;
}
state->dmrburstL = 16; //Use 16 for Voice?
state->currentslot = 0; //force to slot 0
dibit_p = state->dmr_payload_p - 90;
//CACH + First Half Payload + Sync = 12 + 54 + 24
for (i = 0; i < 90; i++) //90
{
state->dmr_stereo_payload[i] = *dibit_p;
dibit_p++;
}
for(i = 0; i < 12; i++)
{
dibit = state->dmr_stereo_payload[i];
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
cachdata[i] = dibit;
}
//Setup for first AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//First AMBE Frame, Full 36
for(i = 0; i < 36; i++)
{
dibit = state->dmr_stereo_payload[i+12];
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
//state->dmr_stereo_payload[i+12] = dibit;
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//Setup for Second AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//Second AMBE Frame, First Half 18 dibits just before Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = state->dmr_stereo_payload[i+48];
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//Setup for Third AMBE Frame
//Interleave Schedule
w = rW;
x = rX;
y = rY;
z = rZ;
//Third AMBE Frame, Full 36
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
w++;
x++;
y++;
z++;
}
//work around to set erroneous PI header values to 0 if K is active
if (state->K > 0 || state->K1 > 0)
{
state->payload_keyid = 0; //just for testing
state->payload_keyidR = 0; //just for testing
}
fprintf (stderr,"%s ", getTime());
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR MS/DM MODE | Frame Sync | DMRSTEREO | VC1 ");
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fid == 0x10) )
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if ( (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->DMRvcR == 0 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fid == 0x68) )
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
}
fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n");
}
else
{
fprintf (stderr,"Sync: -DMR MS/DM MODE | Frame Sync | DMRSTEREO | VC1 ");
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fid == 0x10) )
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
}
if ( (state->DMRvcL == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->DMRvcR == 0 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fid == 0x68) )
{
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %016llX", state->K1);
if (state->K2 != 0)
{
fprintf(stderr, " %016llX", state->K2);
}
if (state->K3 != 0)
{
fprintf(stderr, " %016llX", state->K3);
}
if (state->K4 != 0)
{
fprintf(stderr, " %016llX", state->K4);
}
}
fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n");
}
state->dmr_ms_mode = 1;
state->DMRvcL = 0;
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
//state->directmode = 0; //flag off for testing samples
if (state->directmode == 0)
{
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
else
{
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
skipDibit (opts, state, 144); //skip to next TDMA slot
dmrMS (opts, state); //bootstrap into full TDMA frame
}
void dmrMSData (dsd_opts * opts, dsd_state * state)
{
int i, b, c;
int dibit;
int *dibit_p;
unsigned int burst;
unsigned char MSCach[24] = {0}; //12 dibits = 24 bits
unsigned char MSSync[48] = {0}; //24 dibits = 48 bits
unsigned char MSSlot[20] = {0}; //10 dibits = 20 bits
unsigned char MSBurst[5] = {0};
int cachInterleave[24] = {0, 7, 8, 9, 1, 10, 11, 12, 2, 13, 14, 15, 3, 16, 4, 17, 18, 19, 5, 20, 21, 22, 6, 23};
//add time to mirror sync
time_t now;
char * getTime(void) //get pretty hh:mm:ss timestamp
{
time_t t = time(NULL);
char * curr;
char * stamp = asctime(localtime( & t));
curr = strtok(stamp, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
return curr;
}
b = 0;
c = 0;
//CACH + First Half Payload + Sync = 12 + 54 + 24
dibit_p = state->dmr_payload_p - 90;
for (i = 0; i < 90; i++) //90
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i] = dibit;
//load cach
if (i < 12)
{
MSCach[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
MSCach[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
}
//load slot, first half
if (i > 60 && i < 66)
{
MSSlot[(b*2)] = (1 & (dibit >> 1)); // bit 1
MSSlot[(b*2)+1] = (1 & dibit); // bit
b++;
}
//load sync
if (i > 66)
{
MSSync[(c*2)] = (1 & (dibit >> 1)); // bit 1
MSSync[(c*2)+1] = (1 & dibit); // bit 0
c++;
}
}
for (i = 0; i < 54; i++)
{
dibit = getDibit(opts, state);
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
state->dmr_stereo_payload[i+90] = dibit;
//load slot, second half
if (i < 6)
{
MSSlot[(b*2)] = (1 & (dibit >> 1)); // bit 1
MSSlot[(b*2)+1] = (1 & dibit); // bit
b++;
}
}
//state->directmode = 0; //flag off for testing
//with FEC fix, now we can (hopefully) feel confident in running MS data
if (1 == 1) //opts->payload == 1 && state->directmode == 0
{
fprintf (stderr, "%s ", getTime());
if (opts->inverted_dmr == 0)
{
fprintf (stderr,"Sync: +DMR MS/DM MODE ");
}
else fprintf (stderr,"Sync: -DMR MS/DM MODE ");
}
// decode and correct cach and slot
// golay (20,8) hamming-weight of 6 reliably corrects at most 2 bit-errors
int cach_okay = -1;
if ( Hamming_7_4_decode (MSCach) )
{
cach_okay = 1;
}
if (cach_okay == 1)
{
//fprintf (stderr, "CACH Okay ");
}
else
{
//fprintf (stderr, "CACH ERR ");
if (opts->aggressive_framesync == 1) //may not worry about it on data
{
goto END;
}
}
int slot_okay = -1;
if(Golay_20_8_decode(MSSlot))
{
slot_okay = 1;
}
if (slot_okay == 1)
{
//fprintf (stderr, "Slot Okay ");
}
else goto END;
sprintf(state->slot1light, "%s", "");
sprintf(state->slot2light, "%s", "");
//process data
state->dmr_stereo = 1;
state->dmr_ms_mode = 1;
//with FEC fix, now we can (hopefully) feel confident in running MS data
if (1 == 1) //opts->payload == 1 && state->directmode == 0
{
processDMRdata (opts, state);
}
END:
if (slot_okay != 1 || cach_okay != 1)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, "| ** CACH or Burst Type FEC ERR ** ");
fprintf (stderr, "%s", KNRM);
fprintf (stderr, "\n");
}
state->dmr_stereo = 0;
state->dmr_ms_mode = 0;
state->directmode = 0; //flag off
//get potential first half payload dibits and store them in the payload for the next repitition, MS voice or data.
skipDibit (opts, state, 144);
//CACH + First Half Payload = 12 + 54
for (i = 0; i < 66; i++) //66
{
dibit = getDibit(opts, state);
state->dmr_stereo_payload[i] = dibit;
}
}

395
src/dmr_process_voice.c Normal file
View File

@@ -0,0 +1,395 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
#include "dmr_const.h"
#include "p25p1_check_hdu.h"
void ProcessDMR (dsd_opts * opts, dsd_state * state)
{
uint32_t i, j;
uint32_t Frame;
TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameL = NULL;
TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameR = NULL;
TimeSlotVoiceSuperFrame_t * TSVoiceSupFrameRC = NULL; //Reverse Test for MS Backwards
int *errs;
int *errs2;
unsigned long long int k;
int x;
k = 0;
TSVoiceSupFrameL = &state->TS1SuperFrame;
TSVoiceSupFrameR = &state->TS2SuperFrame;
int Pr[256] = {
0x0000, 0x1F00, 0xE300, 0xFC00, 0x2503, 0x3A03, 0xC603, 0xD903,
0x4A05, 0x5505, 0xA905, 0xB605, 0x6F06, 0x7006, 0x8C06, 0x9306,
0x2618, 0x3918, 0xC518, 0xDA18, 0x031B, 0x1C1B, 0xE01B, 0xFF1B,
0x6C1D, 0x731D, 0x8F1D, 0x901D, 0x491E, 0x561E, 0xAA1E, 0xB51E, //31
0x4B28, 0x5428, 0xA828, 0xB728, 0x6E2B, 0x712B, 0x8D2B, 0x922B,
0x012D, 0x1E2D, 0xE22D, 0xFD2D, 0x242E, 0x3B2E, 0xC72E, 0xD82E,
0x6D30, 0x7230, 0x8E30, 0x9130, 0x4833, 0x5733, 0xAB33, 0xB433,
0x2735, 0x3835, 0xC435, 0xDB35, 0x0236, 0x1D36, 0xE136, 0xFE36, //63
0x2B49, 0x3449, 0xC849, 0xD749, 0x0E4A, 0x114A, 0xED4A, 0xF24A,
0x614C, 0xAE4C, 0x824C, 0x9D4C, 0x444F, 0x5B4F, 0xA74F, 0xB84F,
0x0D51, 0x1251, 0xEE51, 0xF151, 0x2852, 0x3752, 0xCB52, 0xD452,
0x4754, 0x5854, 0xA454, 0xBB54, 0x6257, 0x7D57, 0x8157, 0x9E57, //95
0x6061, 0x7F61, 0x8361, 0x9C61, 0x4562, 0x5A62, 0xA662, 0xB962,
0x2A64, 0x3564, 0xC964, 0xD664, 0x0F67, 0x1067, 0xEC67, 0xF367,
0x4679, 0x5979, 0xA579, 0xBA79, 0x637A, 0x7C7A, 0x807A, 0x9F7A,
0x0C7C, 0x137C, 0xEF7C, 0xF07C, 0x297F, 0x367F, 0xCA7F, 0xD57F, //127
0x4D89, 0x5289, 0xAE89, 0xB189, 0x688A, 0x778A, 0x8B8A, 0x948A,
0x078C, 0x188C, 0xE48C, 0xFB8C, 0x228F, 0x3D8F, 0xC18F, 0xDE8F,
0x6B91, 0x7491, 0x8891, 0x9791, 0x4E92, 0x5192, 0xAD92, 0xB292,
0x2194, 0x3E94, 0xC294, 0xDD94, 0x0497, 0x1B97, 0xE797, 0xF897, //159
0x06A1, 0x19A1, 0xE5A1, 0xFAA1, 0x23A2, 0x3CA2, 0xC0A2, 0xDFA2,
0x4CA4, 0x53A4, 0xAFA4, 0xB0A4, 0x69A7, 0x76A7, 0x8AA7, 0x95A7,
0x20B9, 0x3FB9, 0xC3B9, 0xDCB9, 0x05BA, 0x1ABA, 0xE6BA, 0xF9BA,
0x6ABC, 0x75BC, 0x89BC, 0x96BC, 0x4FBF, 0x50BF, 0xACBF, 0xB3BF, //191
0x66C0, 0x79C0, 0x85C0, 0x9AC0, 0x43C3, 0x5CC3, 0xA0C3, 0xBFC3,
0x2CC5, 0x33C5, 0xCFC5, 0xD0C5, 0x09C6, 0x16C6, 0xEAC6, 0xF5C6,
0x84D0, 0x85DF, 0x8AD3, 0x8BDC, 0xB6D5, 0xB7DA, 0xB8D6, 0xB9D9,
0xD0DA, 0xD1D5, 0xDED9, 0xDFD6, 0xE2DF, 0xE3D0, 0xECDC, 0xEDD3, //223
0x2DE8, 0x32E8, 0xCEE8, 0xD1E8, 0x08EB, 0x17EB, 0xEBEB, 0xF4EB,
0x67ED, 0x78ED, 0x84ED, 0x9BED, 0x42EE, 0x5DEE, 0xA1EE, 0xBEEE,
0x0BF0, 0x14F0, 0xE8F0, 0xF7F0, 0x2EF3, 0x31F3, 0xCDF3, 0xD2F3,
0x41F5, 0x5EF5, 0xA2F5, 0xBDF5, 0x64F6, 0x7BF6, 0x87F6, 0x98F6 //255
};
if ( (state->currentslot == 0 && state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->currentslot == 0 && state->K > 0 && state->M == 1) )
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
k = Pr[state->K];
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
for(Frame = 0; Frame < 6; Frame++)
{
for(i = 0; i < 3; i++)
{
for(j = 0; j < 48; j++)
{
x = ( ((k << j) & 0x800000000000) >> 47 );
TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= x;
}
}
}
}
if ( (state->currentslot == 1 && state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
(state->currentslot == 1 && state->K > 0 && state->M == 1) )
{
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " PrK %lld", state->K);
fprintf (stderr, "%s", KNRM);
k = Pr[state->K];
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
for(Frame = 0; Frame < 6; Frame++)
{
for(i = 0; i < 3; i++)
{
for(j = 0; j < 48; j++)
{
x = ( ((k << j) & 0x800000000000) >> 47 );
TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= x;
}
}
}
}
if ( (state->currentslot == 0 && state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->currentslot == 0 && state->K1 > 0 && state->M == 1) )
{
int pos = 0;
unsigned long long int k1 = state->K1;
unsigned long long int k2 = state->K2;
unsigned long long int k3 = state->K3;
unsigned long long int k4 = state->K4;
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %010llX", k1);
int T_Key[256] = {0};
int pN[882] = {0};
int len = 0;
if (k2 == 0)
{
len = 39;
k1 = k1 << 24;
}
if (k2 != 0)
{
len = 127;
fprintf(stderr, " %016llX", k2);
}
if (k4 != 0)
{
len = 255;
fprintf(stderr, " %016llX", k3);
fprintf(stderr, " %016llX", k4);
}
fprintf (stderr, "%s", KNRM);
for (i = 0; i < 64; i++)
{
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
}
for (i = 0; i < 882; i++)
{
pN[i] = T_Key[pos];
pos++;
if (pos > len)
{
pos = 0;
}
}
pos = 0;
for(Frame = 0; Frame < 6; Frame++)
{
for(i = 0; i < 3; i++)
{
for(j = 0; j < 49; j++)
{
TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= pN[pos];
pos++;
}
}
}
}
if ( (state->currentslot == 1 && state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
(state->currentslot == 1 && state->K1 > 0 && state->M == 1) )
{
int pos = 0;
unsigned long long int k1 = state->K1;
unsigned long long int k2 = state->K2;
unsigned long long int k3 = state->K3;
unsigned long long int k4 = state->K4;
fprintf (stderr, "\n");
fprintf (stderr, "%s", KYEL);
fprintf(stderr, " SPT %010llX", k1);
int T_Key[256] = {0};
int pN[882] = {0};
int len = 0;
if (k2 == 0)
{
len = 39;
k1 = k1 << 24;
}
if (k2 != 0)
{
len = 127;
fprintf(stderr, " %016llX", k2);
}
if (k4 != 0)
{
len = 255;
fprintf(stderr, " %016llX", k3);
fprintf(stderr, " %016llX", k4);
}
fprintf (stderr, "%s", KNRM);
for (i = 0; i < 64; i++)
{
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
}
for (i = 0; i < 882; i++)
{
pN[i] = T_Key[pos];
pos++;
if (pos > len)
{
pos = 0;
}
}
pos = 0;
for(Frame = 0; Frame < 6; Frame++)
{
for(i = 0; i < 3; i++)
{
for(j = 0; j < 49; j++)
{
TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i][j] ^= pN[pos];
pos++;
}
}
}
}
if (state->currentslot == 0)
{
for(Frame = 0; Frame < 6; Frame++) //6
{
/* 1 DMR frame contains 3 AMBE voice samples */
for(i = 0; i < 3; i++)
{
errs = (int*)&(TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs1[i]);
errs2 = (int*)&(TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs2[i]);
state->errs = TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs1[i]; //correct placement
state->errs2 = TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].errs2[i]; //correct placement
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, errs, errs2, state->err_str,
TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i],
state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->payload == 1)
{
PrintAMBEData(opts, state, TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
}
//quick enc toggle to determine whether or not to play enc traffic
int enc_bit = 0;
enc_bit = (state->dmr_so >> 6) & 0x1;
if (enc_bit == 1)
{
state->dmr_encL = 1;
}
else state->dmr_encL = 0;
if (state->K != 0 || state->K1 != 0)
{
state->dmr_encL = 0;
}
//quick test to determine whether or not to run the next few steps below
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
{
if (opts->mbe_out_f != NULL)
{
saveAmbe2450Data (opts, state, TSVoiceSupFrameL->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
}
state->debug_audio_errors += *errs2;
processAudio(opts, state);
if (opts->wav_out_f != NULL)
{
writeSynthesizedVoice (opts, state);
}
if (opts->audio_out == 1) //opts->audio_out == 1
{
playSynthesizedVoice (opts, state);
}
} //second bracket for the test enc condition
} /* End for(i = 0; i < 3; i++) */
} /* End for(Frame = 0; Frame < 6; Frame++) */
} //End if Current Slot == 0
if (state->currentslot == 1)
{
for(Frame = 0; Frame < 6; Frame++)
{
/* 1 DMR frame contains 3 AMBE voice samples */
for(i = 0; i < 3; i++)
{
errs = (int*)&(TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs1[i]);
errs2 = (int*)&(TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs2[i]);
state->errs = TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs1[i]; //correct placement
state->errs2 = TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].errs2[i]; //correct placement
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, errs, errs2, state->err_str,
TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i],
state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->payload == 1)
{
PrintAMBEData(opts, state, TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
}
//quick enc toggle to determine whether or not to play enc traffic
int enc_bit = 0;
enc_bit = (state->dmr_soR >> 6) & 0x1;
if (enc_bit == 1)
{
state->dmr_encR = 1;
}
else state->dmr_encR = 0;
if (state->K != 0 || state->K1 != 0)
{
state->dmr_encR = 0;
}
//quick test to determine whether or not to run the next few steps below
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
{
if (opts->mbe_out_f != NULL)
{
saveAmbe2450Data (opts, state, TSVoiceSupFrameR->TimeSlotAmbeVoiceFrame[Frame].AmbeBit[i]);
}
state->debug_audio_errors += *errs2;
if (opts->audio_out == 1)
{
processAudio(opts, state);
}
if (opts->wav_out_f != NULL)
{
writeSynthesizedVoice (opts, state);
}
if (opts->audio_out == 1)
{
playSynthesizedVoice (opts, state);
}
}
} /* End for(i = 0; i < 3; i++) */
} /* End for(Frame = 0; Frame < 6; Frame++) */
} //End if Current Slot == 1
} /* End ProcessDMR */
/* End of file */

2970
src/dmr_sync.c Normal file

File diff suppressed because it is too large Load Diff

660
src/dmr_voice.c Normal file
View File

@@ -0,0 +1,660 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
#include "dmr_const.h"
void processDMRvoice (dsd_opts * opts, dsd_state * state)
{
// extracts AMBE frames from DMR frame
int i, j, k, l, dibit;
int *dibit_p;
char ambe_fr[4][24] = {0};
char ambe_fr2[4][24] = {0};
char ambe_fr3[4][24] = {0};
const int *w, *x, *y, *z;
char sync[25];
char syncdata[48];
char cachdata[13] = {0};
int mutecurrentslot;
int msMode;
char cc[4];
unsigned char EmbeddedSignalling[16];
unsigned int EmbeddedSignallingOk;
#ifdef DMR_DUMP
int k;
char syncbits[49];
char cachbits[25];12
#endif
/* Init the superframe buffers */
memset(&state->TS1SuperFrame, 0, sizeof(TimeSlotVoiceSuperFrame_t));
memset(&state->TS2SuperFrame, 0, sizeof(TimeSlotVoiceSuperFrame_t));
/* Init the color code status */
state->color_code_ok = 0;
mutecurrentslot = 0;
msMode = 0;
dibit_p = state->dibit_buf_p - 144;
for(j = 0; j < 6; j++)
{
// 2nd half of previous slot
for(i = 0; i < 54; i++)
{
if(j > 0)
{
dibit = getDibit(opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
}
// CACH
for(i = 0; i < 12; i++)
{
if(j > 0)
{
dibit = getDibit(opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
cachdata[i] = dibit;
if(i == 2)
{
/* Change the current slot only if we are in relayed mode (not in direct mode) */
if(state->directmode == 0) state->currentslot = (1 & (dibit >> 1)); // bit 1
if(state->currentslot == 0)
{
state->slot1light[0] = '[';
state->slot1light[6] = ']';
state->slot2light[0] = ' ';
state->slot2light[6] = ' ';
}
else
{
state->slot2light[0] = '[';
state->slot2light[6] = ']';
state->slot1light[0] = ' ';
state->slot1light[6] = ' ';
}
}
}
cachdata[12] = 0;
//set state-dmrburstL and R here to a value for ncurses
if (state->currentslot == 0)
{
state->dmrburstL = 16; //use 16 for Voice?
}
if (state->currentslot == 1)
{
state->dmrburstR = 16; //Use 16 for Voice?
}
#ifdef DMR_DUMP
k = 0;
for(i = 0; i < 12; i++)
{
dibit = cachdata[i];
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
cachbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
cachbits[24] = 0;
fprintf(stderr, "%s ", cachbits);
#endif
// current slot frame 1
w = rW;
x = rX;
y = rY;
z = rZ;
k = 0;
for(i = 0; i < 36; i++)
{
if(j > 0)
{
dibit = getDibit(opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[*y][*z] = (1 & dibit); // bit 0
/* Save the current frame in the appropriate superframe buffer */
if(state->currentslot == 0)
{
/* Time slot 1 superframe buffer filling => RAW voice bit */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & dibit); // bit 0
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*y][*z] = (1 & dibit); // bit 0
}
else
{
/* Time slot 2 superframe buffer filling => RAW voice bit */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[0][k++] = (1 & dibit); // bit 0
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[0][*y][*z] = (1 & dibit); // bit 0
}
w++;
x++;
y++;
z++;
}
// current slot frame 2 first half
w = rW;
x = rX;
y = rY;
z = rZ;
k = 0;
for(i = 0; i < 18; i++)
{
if(j > 0)
{
dibit = getDibit(opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
/* Save the current frame in the appropriate superframe buffer */
if(state->currentslot == 0)
{
/* Time slot 1 superframe buffer filling => RAW voice bit */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
}
else
{
/* Time slot 2 superframe buffer filling => RAW voice bit */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
}
w++;
x++;
y++;
z++;
}
l = 0;
// signaling data or sync
for(i = 0; i < 24; i++)
{
if(j > 0)
{
dibit = getDibit(opts, state);
}
else
{
dibit = *dibit_p;
dibit_p++;
if(opts->inverted_dmr == 1)
{
dibit = (dibit ^ 2);
}
}
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
syncdata[(2*i)+1] = (1 & dibit); // bit 0
sync[i] = (dibit | 1) + 48;
/* Save the current frame in the appropriate superframe buffer */
if(state->currentslot == 0)
{
/* Time slot 1 superframe buffer filling => SYNC data */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & dibit); // bit 0
}
else
{
/* Time slot 2 superframe buffer filling => SYNC data */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].Sync[l++] = (1 & dibit); // bit 0
}
}
sync[24] = 0;
/* Copy the Embedded signaling (1st part) */
for(i = 0; i < 8; i++)
{
EmbeddedSignalling[i] = syncdata[i];
}
/* Copy the Embedded signaling (2nd part) */
for(i = 0; i < 8; i++)
{
EmbeddedSignalling[i + 8] = syncdata[i + 40];
}
/* Check and correct the SlotType (apply Golay(20,8) FEC check) */
if(QR_16_7_6_decode(EmbeddedSignalling)) EmbeddedSignallingOk = 1;
else EmbeddedSignallingOk = 0;
/* Get the color code */
cc[0] = EmbeddedSignalling[0];
cc[1] = EmbeddedSignalling[1];
cc[2] = EmbeddedSignalling[2];
cc[3] = EmbeddedSignalling[3];
/* The color code is normally the same for all
* 6 received voice frame, so in case of error
* do not store it except if the last SYNC frame
* has been received. */
if(EmbeddedSignallingOk || ((state->color_code_ok == 0) && (j == 5)))
{
/* Save the color code */
state->color_code = (unsigned int)((cc[0] << 3) + (cc[1] << 2) + (cc[2] << 1) + cc[3]);
state->color_code_ok = EmbeddedSignallingOk;
}
/* Save the Power Control (PI) indicator */
state->PI = (unsigned int)EmbeddedSignalling[4];
state->PI_ok = EmbeddedSignallingOk;
/* Save the Link Control Start Stop (LCSS) indicator */
state->LCSS = (unsigned int)((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
state->LCSS_ok = EmbeddedSignallingOk;
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
{
mutecurrentslot = 1;
state->directmode = 0;
if(state->currentslot == 0)
{
sprintf(state->slot1light, "[slot1]");
}
else
{
sprintf(state->slot2light, "[slot2]");
}
}
else if(strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0)
{
mutecurrentslot = 1;
state->currentslot = 0;
state->directmode = 1; /* Direct mode */
sprintf(state->slot1light, "[sLoT1]");
}
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
{
mutecurrentslot = 1;
state->currentslot = 1;
state->directmode = 1; /* Direct mode */
sprintf(state->slot1light, "[sLoT2]");
}
else if((strcmp (sync, DMR_BS_VOICE_SYNC) == 0) ) //|| (strcmp (sync, DMR_MS_VOICE_SYNC) == 0))
{
mutecurrentslot = 0;
state->directmode = 0;
if(state->currentslot == 0)
{
sprintf(state->slot1light, "[SLOT1]");
}
else
{
sprintf(state->slot2light, "[SLOT2]");
}
}
else if(strcmp (sync, DMR_DIRECT_MODE_TS1_VOICE_SYNC) == 0)
{
mutecurrentslot = 0;
state->currentslot = 0;
state->directmode = 1; /* Direct mode */
sprintf(state->slot1light, "[sLoT1]");
}
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0)
{
mutecurrentslot = 0;
state->currentslot = 1;
state->directmode = 1; /* Direct mode */
sprintf(state->slot2light, "[sLoT2]");
}
#ifdef DMR_DUMP
k = 0;
for(i = 0; i < 24; i++)
{
dibit = syncdata[i];
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
syncbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
syncbits[48] = 0;
fprintf(stderr, "%s ", syncbits);
#endif
// current slot frame 2 second half
for(i = 0; i < 18; i++)
{
dibit = getDibit(opts, state);
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
/* Save the current frame in the appropriate superframe buffer */
if(state->currentslot == 0)
{
/* Time slot 1 superframe buffer filling => RAW voice bit */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
}
else
{
/* Time slot 2 superframe buffer filling */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[1][k++] = (1 & dibit); // bit 0
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[1][*y][*z] = (1 & dibit); // bit 0
}
w++;
x++;
y++;
z++;
}
if(mutecurrentslot == 0)
{
if(state->firstframe == 1)
{ // we don't know if anything received before the first sync after no carrier is valid
state->firstframe = 0;
}
else
{
//processMbeFrame (opts, state, NULL, ambe_fr, NULL);
//processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
}
}
// current slot frame 3
w = rW;
x = rX;
y = rY;
z = rZ;
k = 0;
for(i = 0; i < 36; i++)
{
dibit = getDibit(opts, state);
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
/* Save the current frame in the appropriate superframe buffer */
if(state->currentslot == 0)
{
/* Time slot 1 superframe buffer filling => RAW voice bit */
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & dibit); // bit 0
/* Time slot 1 superframe buffer filling => Deinterleaved voice bit */
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*y][*z] = (1 & dibit); // bit 0
}
else
{
/* Time slot 2 superframe buffer filling => RAW voice bit */
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotRawVoiceFrame[j].VoiceFrame[2][k++] = (1 & dibit); // bit 0
/* Time slot 2 superframe buffer filling => Deinterleaved voice bit */
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*w][*x] = (1 & (dibit >> 1)); // bit 1
state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[2][*y][*z] = (1 & dibit); // bit 0
}
w++;
x++;
y++;
z++;
}
if(mutecurrentslot == 0)
{
//processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
}
// CACH
for(i = 0; i < 12; i++)
{
dibit = getDibit(opts, state);
cachdata[i] = dibit;
}
cachdata[12] = 0;
#ifdef DMR_DUMP
k = 0;
for(i = 0; i < 12; i++)
{
dibit = cachdata[i];
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
cachbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
cachbits[24] = 0;
fprintf(stderr, "%s ", cachbits);
#endif
// next slot
skipDibit (opts, state, 54);
// signaling data or sync
for(i = 0; i < 24; i++)
{
dibit = getDibit(opts, state);
syncdata[i] = dibit;
sync[i] = (dibit | 1) + 48;
}
sync[24] = 0;
syncdata[24] = 0;
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (msMode == 1))
{
state->directmode = 0;
if(state->currentslot == 0)
{
sprintf(state->slot2light, " slot2 ");
}
else
{
sprintf(state->slot1light, " slot1 ");
}
}
else if(strcmp (sync, DMR_BS_VOICE_SYNC) == 0)
{
state->directmode = 0;
if(state->currentslot == 0)
{
sprintf(state->slot2light, " SLOT2 ");
}
else
{
sprintf(state->slot1light, " SLOT1 ");
}
}
else if((strcmp (sync, DMR_DIRECT_MODE_TS1_VOICE_SYNC) == 0) || (strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0))
{
state->currentslot = 0;
state->directmode = 1; /* Direct mode */
sprintf(state->slot1light, " sLoT1 ");
}
else if((strcmp (sync, DMR_DIRECT_MODE_TS2_VOICE_SYNC) == 0) || (strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0))
{
state->currentslot = 1;
state->directmode = 1; /* Direct mode */
sprintf(state->slot1light, " sLoT2 ");
}
#ifdef DMR_DUMP
k = 0;
for(i = 0; i < 24; i++)
{
dibit = syncdata[i];
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
k++;
syncbits[k] = (1 & dibit) + 48; // bit 0
k++;
}
syncbits[48] = 0;
fprintf(stderr, "%s ", syncbits);
#endif
if(j == 5) //j == 5
{
// 2nd half next slot
skipDibit (opts, state, 54);
// CACH
skipDibit (opts, state, 12);
// first half current slot
skipDibit (opts, state, 54);
}
/* Extract the 49 bits AMBE of the voice frame */
if(state->currentslot == 0)
{
/* 3 x 49 bit of AMBE sample per voice frame => Slot 1buffer here */
for(i = 0; i < 3; i++)
{
state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i] = mbe_eccAmbe3600x2450C0(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] = state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i];
mbe_demodulateAmbe3600x2450Data(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] += mbe_eccAmbe3600x2450Data(state->TS1SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i],
&(state->TS1SuperFrame.TimeSlotAmbeVoiceFrame[j].AmbeBit[i][0]));
}
}
else
{
/* 3 x 49 bit of AMBE sample per voice frame => Slot 2 buffer here */
for(i = 0; i < 3; i++)
{
state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i] = mbe_eccAmbe3600x2450C0(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] = state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs1[i];
mbe_demodulateAmbe3600x2450Data(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i]);
state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].errs2[i] += mbe_eccAmbe3600x2450Data(state->TS2SuperFrame.TimeSlotDeinterleavedVoiceFrame[j].DeInterleavedVoiceSample[i],
&(state->TS2SuperFrame.TimeSlotAmbeVoiceFrame[j].AmbeBit[i][0]));
}
}
} /* End for(j = 0; j < 6; j++) */
if(opts->errorbars == 1)
{
fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
/* Print the color code */
fprintf (stderr, "%s", KCYN);
fprintf(stderr, "| Color Code=%02d ", (int)state->color_code);
fprintf (stderr, "%s", KNRM);
//if(state->color_code_ok) fprintf(stderr, "(OK) |");
if(state->color_code_ok)
{
fprintf(stderr, "| (CRC OK ) |"); //add line break
state->dmr_color_code = state->color_code; //set here when voice? otherwise is set off of idle.
}
else
{
fprintf (stderr, "%s", KRED);
fprintf(stderr, "| (CRC ERR) |"); //add line break
fprintf (stderr, "%s", KNRM);
}
fprintf(stderr, " VOICE ");
}
/* Perform the SYNC DMR data embedded decoding */
ProcessVoiceBurstSync(opts, state);
/* Perform the DMR voice decoding */
ProcessDMR(opts, state);
//LFSR if required, change condition?
if ( (state->currentslot == 0 && state->payload_algid == 0x21 ) || //&& opts->payload == 1
(state->currentslot == 1 && state->payload_algidR == 0x21 ) ) //&& opts->payload == 1
{
LFSR(state);
}
if(opts->errorbars == 1)
{
fprintf(stderr, "\n");
}
} /* End processDMRvoice() */

278
src/dpmr_data.c Normal file
View File

@@ -0,0 +1,278 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
/* Convert the Channel code (24 bit) into a valid
* dPMR color code [0..63]
* Return -1 on error */
int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24])
{
uint32_t ChannelCodeByte;
int32_t ColorCode;
uint32_t i;
/* Reconstitute the 24 bit channel code into a 3 bytes integer */
ChannelCodeByte = 0;
for(i = 0; i < 24; i++)
{
ChannelCodeByte <<= 1;
ChannelCodeByte |= (ChannelCodeBit[i] & 1);
}
/* By analyzing all channel code on the
* dPMR standard, I observe that if we decompose the
* 24 bit into 12 dibit, all dibit's LSB are always
* equal to "1", so apply a mask to correct
* flipped LSB dibit.
*
* For more details see ETSI TS 102 658 chapter
* 6.1.5.2.2 "Channel Code Determined by Frequency
* and System Identity Code".
*
* Apply the mask 0x555555
* 0x555555 = 0b010101010101010101010101 => All dibit's LSB
* are equal to "1".
*/
ChannelCodeByte |= 0x555555;
/* Switch to the right color code */
switch(ChannelCodeByte & 0xFF0000)
{
case 0x570000:
{
switch(ChannelCodeByte)
{
case 0x575F77: {ColorCode = 0; break;}
case 0x577577: {ColorCode = 1; break;}
case 0x57DD75: {ColorCode = 2; break;}
case 0x57F775: {ColorCode = 3; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0x570000 */
case 0x550000:
{
switch(ChannelCodeByte)
{
case 0x55577D: {ColorCode = 4; break;}
case 0x557D7D: {ColorCode = 5; break;}
case 0x55D57F: {ColorCode = 6; break;}
case 0x55FF7F: {ColorCode = 7; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0x550000 */
case 0x5F0000:
{
switch(ChannelCodeByte)
{
case 0x5F555F: {ColorCode = 8; break;}
case 0x5F7F5F: {ColorCode = 9; break;}
case 0x5FD75D: {ColorCode = 10; break;}
case 0x5FFD5D: {ColorCode = 11; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0x5F0000 */
case 0x5D0000:
{
switch(ChannelCodeByte)
{
case 0x5D5D55: {ColorCode = 12; break;}
case 0x5D7755: {ColorCode = 13; break;}
case 0x5DDF57: {ColorCode = 14; break;}
case 0x5DF557: {ColorCode = 15; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0x5D0000 */
case 0x770000:
{
switch(ChannelCodeByte)
{
case 0x775DD7: {ColorCode = 16; break;}
case 0x7777D7: {ColorCode = 17; break;}
case 0x77DFD5: {ColorCode = 18; break;}
case 0x77F5D5: {ColorCode = 19; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0x770000 */
case 0x750000:
{
switch(ChannelCodeByte)
{
case 0x7555DD: {ColorCode = 20; break;}
case 0x757FDD: {ColorCode = 21; break;}
case 0x75D7DF: {ColorCode = 22; break;}
case 0x75FDDF: {ColorCode = 23; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0x750000 */
case 0x7F0000:
{
switch(ChannelCodeByte)
{
case 0x7F57FF: {ColorCode = 24; break;}
case 0x7F7DFF: {ColorCode = 25; break;}
case 0x7FD5FD: {ColorCode = 26; break;}
case 0x7FFFFD: {ColorCode = 27; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0x7F0000 */
case 0x7D0000:
{
switch(ChannelCodeByte)
{
case 0x7D5FF5: {ColorCode = 28; break;}
case 0x7D75F5: {ColorCode = 29; break;}
case 0x7DDDF7: {ColorCode = 30; break;}
case 0x7DF7F7: {ColorCode = 31; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0x7D0000 */
case 0xD70000:
{
switch(ChannelCodeByte)
{
case 0xD755F7: {ColorCode = 32; break;}
case 0xD77FF7: {ColorCode = 33; break;}
case 0xD7D7F5: {ColorCode = 34; break;}
case 0xD7FDF5: {ColorCode = 35; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0xD70000 */
case 0xD50000:
{
switch(ChannelCodeByte)
{
case 0xD55DFD: {ColorCode = 36; break;}
case 0xD577FD: {ColorCode = 37; break;}
case 0xD5DFFF: {ColorCode = 38; break;}
case 0xD5F5FF: {ColorCode = 39; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0xD50000 */
case 0xDF0000:
{
switch(ChannelCodeByte)
{
case 0xDF5FDF: {ColorCode = 40; break;}
case 0xDF75DF: {ColorCode = 41; break;}
case 0xDFDDDD: {ColorCode = 42; break;}
case 0xDFF7DD: {ColorCode = 43; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0xDF0000 */
case 0xDD0000:
{
switch(ChannelCodeByte)
{
case 0xDD57D5: {ColorCode = 44; break;}
case 0xDD7DD5: {ColorCode = 45; break;}
case 0xDDD5D7: {ColorCode = 46; break;}
case 0xDDFFD7: {ColorCode = 47; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0xDD0000 */
case 0xF70000:
{
switch(ChannelCodeByte)
{
case 0xF75757: {ColorCode = 48; break;}
case 0xF77D57: {ColorCode = 49; break;}
case 0xF7D555: {ColorCode = 50; break;}
case 0xF7FF55: {ColorCode = 51; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0xF70000 */
case 0xF50000:
{
switch(ChannelCodeByte)
{
case 0xF55F5D: {ColorCode = 52; break;}
case 0xF5755D: {ColorCode = 53; break;}
case 0xF5DD5F: {ColorCode = 54; break;}
case 0xF5F75F: {ColorCode = 55; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0xF50000 */
case 0xFF0000:
{
switch(ChannelCodeByte)
{
case 0xFF5D7F: {ColorCode = 56; break;}
case 0xFF777F: {ColorCode = 57; break;}
case 0xFFDF7D: {ColorCode = 58; break;}
case 0xFFF57D: {ColorCode = 59; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0xFF0000 */
case 0xFD0000:
{
switch(ChannelCodeByte)
{
case 0xFD5575: {ColorCode = 60; break;}
case 0xFD7F75: {ColorCode = 61; break;}
case 0xFDD777: {ColorCode = 62; break;}
case 0xFDFD77: {ColorCode = 63; break;}
default: {ColorCode = -1; break;}
} /* End switch(ChannelCodeByte) */
break;
} /* End case 0xFD0000 */
default:
{
ColorCode = -1;
break;
}
} /* End switch(ChannelCodeByte & 0xFF0000) */
/* Return the colour code */
return ColorCode;
}
/* End of file */

759
src/dpmr_voice.c Normal file
View File

@@ -0,0 +1,759 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
#include "dpmr_const.h"
void processdPMRvoice (dsd_opts * opts, dsd_state * state)
{
uint32_t i, j, k, dibit;
uint8_t CCH[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
uint8_t CCHDescrambled[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
uint8_t CCHDeInterleaved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
uint8_t CCHDataHammingCorrected[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][48] = {0};
uint8_t CCHDataCRC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
uint8_t CCHDataCRCComputed[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
uint8_t CC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE / 2][24] = {0};
char ambe_fr[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4][4][24] = {0};
const int *w, *x, *y, *z;
uint32_t ScramblerLFSR = 0;
bool correctable = true;
bool HammingCorrectable[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][6] = {0};
uint32_t CrcOk[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
uint32_t HammingOk[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
uint32_t Temp = 0;
/* CCH (Control CHannel) data */
uint32_t CCH_FrameNumber[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
uint32_t CCH_CalledID = 0;
uint32_t CCH_CallingID = 0;
uint32_t CCH_CommunicationMode[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
uint32_t CCH_Version[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
uint32_t CCH_CommsFormat[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
uint32_t CCH_EmergencyPriority[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
uint32_t CCH_Reserved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
uint32_t CCH_SlowData[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
uint32_t *errs;
uint32_t *errs2;
uint8_t AmbeBitDescrambled[49] = {0};
uint32_t ScramblerKey = 0;
uint32_t PartOfSuperFrame = 0;
uint32_t VoiceFrameFlag = 1; /* We consider the current frame as a voice frame */
uint32_t AttachedDataFlag = 0;
uint8_t CalledID[8] = {0};
uint8_t CallingID[8] = {0};
if (opts->errorbars == 1)
{
//fprintf(stderr, "VOICE e:");
}
/* First CCH (Control CHannel) - 72 bit */
k = 0;
for (i = 0; i < 36; i++)
{
dibit = getDibit (opts, state);
if (opts->inverted_dpmr == 1)
{
dibit = (dibit ^ 2);
}
CCH[0][(i * 2)] = (1 & (dibit >> 1)); // bit 1
CCH[0][(i * 2) + 1] = (1 & dibit); // bit 0
}
/* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */
k = 0;
for (j = 0; j < 4; j++)
{
w = dPmrW;
x = dPmrX;
y = dPmrY;
z = dPmrZ;
k = 0;
for (i = 0; i < 36; i++)
{
dibit = getDibit (opts, state);
if (opts->inverted_dpmr == 1)
{
dibit = (dibit ^ 2);
}
ambe_fr[j][*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[j][*y][*z] = (1 & dibit); // bit 0
state->dPMRVoiceFS2Frame.RawVoiceBit[j][k] = (1 & (dibit >> 1)); // bit 1
state->dPMRVoiceFS2Frame.RawVoiceBit[j][k + 1] = (1 & dibit); // bit 0
k = k + 2;
w++;
x++;
y++;
z++;
}
}
/* First CC (Channel Code) - 24 bit */
k = 0;
for (i = 0; i < 12; i++)
{
dibit = getDibit (opts, state);
if (opts->inverted_dpmr == 1)
{
dibit = (dibit ^ 2);
}
CC[0][k++] = (1 & (dibit >> 1)); // bit 1
CC[0][k++] = (1 & dibit); // bit 0
}
/* Get the color code */
state->dPMRVoiceFS2Frame.ColorCode[0] = (unsigned int)GetdPmrColorCode(CC[0]);
/* Second CCH (Control CHannel) - 72 bit */
k = 0;
for (i = 0; i < 36; i++)
{
dibit = getDibit (opts, state);
if (opts->inverted_dpmr == 1)
{
dibit = (dibit ^ 2);
}
CCH[1][k++] = (1 & (dibit >> 1)); // bit 1
CCH[1][k++] = (1 & dibit); // bit 0
}
/* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */
//Sync issues occur when FS2 occurs immediatly after the 4th rep, so cutting of 4 early to gain constant resync
//probably can't resync fast enough when the next sync starts immediately after the 4th rep of 36 dibits
//consider writing a method that will loop this entire process based on the frame numbering found inside the CCH info, is that possible?
k = 0;
for (j = 0; j < 4; j++)
{
w = dPmrW;
x = dPmrX;
y = dPmrY;
z = dPmrZ;
k = 0;
for (i = 0; i < 36; i++)
{
dibit = getDibit (opts, state);
if (opts->inverted_dpmr == 1)
{
dibit = (dibit ^ 2);
}
ambe_fr[j + 4][*w][*x] = (1 & (dibit >> 1)); // bit 1
ambe_fr[j + 4][*y][*z] = (1 & dibit); // bit 0
state->dPMRVoiceFS2Frame.RawVoiceBit[j + 4][k] = (1 & (dibit >> 1)); // bit 1
state->dPMRVoiceFS2Frame.RawVoiceBit[j + 4][k + 1] = (1 & dibit); // bit 0
k = k + 2;
w++;
x++;
y++;
z++;
if (j == 3 && i == 28) //find sweet spot to cut off early
{
i = 40; //break early so FS2 sync can resync
}
}
}
/* Initialize the Hamming (12,8) error code detection
* Not needed because it has already been done
* in the "InitAllFecFunction()" function */
//Hamming_12_8_init();
/* Decoding all CCH (Control CHannel) */
for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++)
{
/* Init LFSR value - All bit to '1' */
ScramblerLFSR = 0x1FF;
/* Descramble data - Input 72 bit => Output 72 bit */
ScrambledPMRBit(&ScramblerLFSR, CCH[i], CCHDescrambled[i], 72);
/* Deinterleave data - Input 72 bit => Output 72 bit */
DeInterleave6x12DPmrBit(CCHDescrambled[i], CCHDeInterleaved[i]);
correctable = true;
for(j = 0; j < 6; j++)
{
/* Apply the Hamming(12,8) correction - Input 72 bit => Output 48 bit */
HammingCorrectable[i][j] = Hamming_12_8_decode(&CCHDeInterleaved[i][j*12], &CCHDataHammingCorrected[i][j*8], 1);
if(HammingCorrectable[i][j] == false) correctable = false;
}
if(correctable)
{
//fprintf(stderr, "CCH data corrected successfully\n");
HammingOk[i] = 1;
}
else
{
//fprintf(stderr, "ERROR !!! CCH data contains uncorrectable bit\n");
HammingOk[i] = 0;
}
/* Reconstitute the 7 bit CRC */
CCHDataCRC[i] = 0;
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][41]) << 6;
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][42]) << 5;
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][43]) << 4;
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][44]) << 3;
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][45]) << 2;
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][46]) << 1;
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][47]) << 0;
/* Compute the 7 bit CRC */
CCHDataCRCComputed[i] = CRC7BitdPMR(CCHDataHammingCorrected[i], 41);
if(CCHDataCRC[i] == CCHDataCRCComputed[i])
{
//fprintf(stderr, "Good CCH CRCs ! CRC received = 0x%02X, CRC computed = 0x%02X\n",
// CCHDataCRC[i], CCHDataCRCComputed[i]);
CrcOk[i] = 1;
}
else
{
//fprintf(stderr, "ERROR !!! CCH CRCs are different ! CRC received = 0x%02X, CRC computed = 0x%02X\n",
// CCHDataCRC[i], CCHDataCRCComputed[i]);
CrcOk[i] = 0;
}
/* Fill the frame number */
CCH_FrameNumber[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][0], 2);
/* Fill the communication mode */
CCH_CommunicationMode[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][14], 3);
/* Fill the version */
CCH_Version[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][17], 2);
/* Fill the comm format */
CCH_CommsFormat[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][19], 2);
/* Fill the emergency priority bit */
CCH_EmergencyPriority[i] = (uint32_t)CCHDataHammingCorrected[i][21];
/* Fill the reserved bit */
CCH_Reserved[i] = (uint32_t)CCHDataHammingCorrected[i][22];
/* Fill the slow data */
CCH_SlowData[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][23], 18);
/* Copy the CCH data into the structure */
memcpy(state->dPMRVoiceFS2Frame.CCHData[i], CCHDataHammingCorrected[i], 48);
state->dPMRVoiceFS2Frame.CCHDataHammingOk[i] = HammingOk[i];
state->dPMRVoiceFS2Frame.CCHDataCRC[i] = CCHDataCRC[i];
state->dPMRVoiceFS2Frame.CCHDataCrcOk[i] = CrcOk[i];
state->dPMRVoiceFS2Frame.FrameNumbering[i] = CCH_FrameNumber[i];
state->dPMRVoiceFS2Frame.CommunicationMode[i] = CCH_CommunicationMode[i];
state->dPMRVoiceFS2Frame.Version[i] = CCH_Version[i];
state->dPMRVoiceFS2Frame.CommsFormat[i] = CCH_CommsFormat[i];
state->dPMRVoiceFS2Frame.EmergencyPriority[i] = CCH_EmergencyPriority[i];
state->dPMRVoiceFS2Frame.Reserved[i] = CCH_Reserved[i];
state->dPMRVoiceFS2Frame.SlowData[i] = CCH_SlowData[i];
} /* End for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++) */
/* Get the last TG */
strcpy((char *)CalledID, (char *)state->dPMRVoiceFS2Frame.CalledID);
CalledID[7] = '\0';
/* Get the last source ID */
strcpy((char *)CallingID, (char *)state->dPMRVoiceFS2Frame.CallingID);
CallingID[7] = '\0';
/* Print Hamming debug info */
//fprintf(stderr, " | ");
for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++)
{
//fprintf(stderr, "Hamming[%u]=", i);
for(j = 0; j < 6; j++)
{
//if(HammingCorrectable[i][j]) fprintf(stderr, "1");
//else fprintf(stderr, "0");
}
//fprintf(stderr, " HammingOk[%u]=%u", i, HammingOk[i]);
//fprintf(stderr, " CrcOk[%u]=%u", i, CrcOk[i]);
//fprintf(stderr, " Bit[%u]=", i);
//for(j = 0; j < 7; j++)
//{
// if(CCHDataHammingCorrected[i][j + 41]) fprintf(stderr, "1");
// else fprintf(stderr, "0");
//}
//fprintf(stderr, " CRCrec=0x%02X CRCcomp=0x%02X", CCHDataCRC[i], CCHDataCRCComputed[i]);
//if(i < (NB_OF_DPMR_VOICE_FRAME_TO_DECODE - 1)) fprintf(stderr, " - ");
}
#if (NB_OF_DPMR_VOICE_FRAME_TO_DECODE == 2)
/* To determine the part of the payload frame, we need to check the
* Hamming code result, the CRC and the frame number */
// if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 0)) ||
// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 1)))
if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 0)) ||
((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 1)))
{
/* First part of the super frame */
PartOfSuperFrame = 1;
//fprintf(stderr, "\nFirst part of the Super Frame");
/* The next part will normally be the second part */
opts->dPMR_next_part_of_superframe = 2;
/* Fill the called ID (talk group - TG) */
Temp = ConvertBitIntoBytes(&CCHDataHammingCorrected[0][2], 12);
CCH_CalledID = ((Temp << 12) & 0x00FFF000); /* 12 MSBit */
Temp = ConvertBitIntoBytes(&CCHDataHammingCorrected[1][2], 12);
CCH_CalledID |= (Temp & 0x00000FFF); /* 12 LSBit */
ConvertAirInterfaceID(CCH_CalledID, CalledID);
CalledID[7] = '\0';
/* Determine if the called TG ID only is correct */
//if(HammingOk[0] && CrcOk[0] && HammingOk[1] && CrcOk[1])
if((CrcOk[0] || (HammingCorrectable[0][0] && HammingCorrectable[0][1])) &&
(CrcOk[1] || (HammingCorrectable[1][0] && HammingCorrectable[1][1])))
{
/* Save CCH data parameters */
state->dPMRVoiceFS2Frame.CalledIDOk = 1;
}
else
{
state->dPMRVoiceFS2Frame.CalledIDOk = 0;
}
/* BUGFIX : Copy in all case the TG ID */
strcpy((char *)state->dPMRVoiceFS2Frame.CalledID, (char *)CalledID);
}
/* To determine the part of the payload frame, we need to check the
* Hamming code result, the CRC and the frame number */
// else if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 2)) ||
// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 3)))
else if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 2)) ||
((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 3)))
{
/* Second part of the super frame */
PartOfSuperFrame = 2;
//fprintf(stderr, "\nSecond part of the Super Frame");
/* The next part will normally be the first part */
opts->dPMR_next_part_of_superframe = 1;
/* Fill the calling ID (source) */
Temp = ConvertBitIntoBytes(&CCHDataHammingCorrected[0][2], 12);
CCH_CallingID = ((Temp << 12) & 0x00FFF000); /* 12 MSBit */
Temp = ConvertBitIntoBytes(&CCHDataHammingCorrected[1][2], 12);
CCH_CallingID |= (Temp & 0x00000FFF); /* 12 LSBit */
ConvertAirInterfaceID(CCH_CallingID, CallingID);
CallingID[7] = '\0';
/* Determine if the calling SRC ID is correct */
//if(HammingOk[0] && CrcOk[0] && HammingOk[1] && CrcOk[1])
if((CrcOk[0] || (HammingCorrectable[0][0] && HammingCorrectable[0][1])) &&
(CrcOk[1] || (HammingCorrectable[1][0] && HammingCorrectable[1][1])))
{
/* Save CCH data parameters */
state->dPMRVoiceFS2Frame.CallingIDOk = 1;
}
else
{
state->dPMRVoiceFS2Frame.CallingIDOk = 0;
}
/* BUGFIX : Copy in all case the SRC ID */
strcpy((char *)state->dPMRVoiceFS2Frame.CallingID, (char *)CallingID);
}
else
{
/* The dPMR source and destination ID are now invalid */
state->dPMRVoiceFS2Frame.CalledIDOk = 0;
state->dPMRVoiceFS2Frame.CallingIDOk = 0;
/* Unknown part of superframe => We suppose
* it is the next part of the previous frame received */
PartOfSuperFrame = opts->dPMR_next_part_of_superframe;
/* Set the next part of the superframe to receive */
if(opts->dPMR_next_part_of_superframe == 1)
{
//fprintf(stderr, "Supposed first part of the Super Frame\n");
/* Set the next part as the second part */
opts->dPMR_next_part_of_superframe = 2;
}
else if(opts->dPMR_next_part_of_superframe == 2)
{
//fprintf(stderr, "Supposed second part of the Super Frame\n");
/* Set the next part as the first part */
opts->dPMR_next_part_of_superframe = 1;
}
else
{
//fprintf(stderr, "Unknown part of the Super Frame\n");
/* Unknown current part, set to 0 */
opts->dPMR_next_part_of_superframe = 0;
}
}
if(state->dPMRVoiceFS2Frame.ColorCode[0] != (unsigned int)(-1))
{
//only print the CC and src/tg info if CRC is good
/* Display the color code */
//fprintf(stderr, " | Color Code=%02d ", (int)state->dPMRVoiceFS2Frame.ColorCode[0]);
}
else
{
/* Unknown color code, display "??" */
//fprintf(stderr, " | Color Code=?? ");
}
/* Display the destination ID (talk group - TG) */
//fprintf(stderr, "| TG=%s", CalledID);
if(state->dPMRVoiceFS2Frame.CalledIDOk)
{
fprintf (stderr, "%s", KGRN);
fprintf(stderr, "\n| TG=%s", CalledID);
fprintf (stderr, "%s", KNRM);
//strcpy (state->dpmr_target_id, CalledID); //HERE HERE
state->dpmr_target_id = CalledID;
//fprintf(stderr, " (CRC OK) ");
if(state->dPMRVoiceFS2Frame.ColorCode[0] != (unsigned int)(-1))
{
fprintf (stderr, "%s", KGRN);
fprintf(stderr, " | Color Code=%02d ", (int)state->dPMRVoiceFS2Frame.ColorCode[0]);
fprintf (stderr, "%s", KNRM);
state->dpmr_color_code = (int)state->dPMRVoiceFS2Frame.ColorCode[0];
}
}
else
{
//fprintf(stderr, " (CRC ERR) ");
}
/* Display the source ID */
//fprintf(stderr, "| Src=%s", CallingID);
if(state->dPMRVoiceFS2Frame.CallingIDOk)
{
fprintf (stderr, "%s", KGRN);
fprintf(stderr, "| Src=%s", CallingID);
fprintf (stderr, "%s", KNRM);
//strcpy (state->dpmr_caller_id, CallingID);
state->dpmr_caller_id = CallingID;
//fprintf(stderr, " (CRC OK) \n");
}
else
{
fprintf (stderr, "%s", KRED);
fprintf(stderr, " (CRC ERR) \n");
fprintf (stderr, "%s", KNRM);
}
#endif /* (NB_OF_DPMR_VOICE_FRAME_TO_DECODE == 2) */
//Play only voice frames by first looking to see if there is a TCH voice in CommunicationMode,
//then sending it to be processed by processMbeFrame, its much cleaner this way
short start = 0;
short end = 4;
for (short o = 0; o < 2; o++)
{
//depending on first or second TCH, set start and end variables appropriately
if (o == 1)
{
start = 4;
end = 8;
//add extra line break on second repitition only when dumping payload to console
if (opts->payload == 1)
{
fprintf(stderr, "\n");
}
}
//fprintf (stderr, "\nCommunication Mode: %d\n", state->dPMRVoiceFS2Frame.CommunicationMode[o]);
//fprintf (stderr, "Encryption Mode: %d\n", state->dPMRVoiceFS2Frame.Version[o]);
//fprintf (stderr, "Superframe Number: %d", state->dPMRVoiceFS2Frame.FrameNumbering[o]); //should be 1-4, but may be same number on TCH 1 or TCH 2
if((state->dPMRVoiceFS2Frame.CommunicationMode[o] == 0) ||
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 1) ||
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 5))
{
//There are 4 AMBE voice samples per TCH
for(i = start; i < end; i++)
{
processMbeFrame (opts, state, NULL, ambe_fr[i], NULL);
} //End for(i = start; i < end); i++)
} //if comms and not encrypted
}//End for TCH for o loop
if (opts->errorbars == 1)
{
fprintf(stderr, "\n");
}
//keep code below, could be handy cheat sheet for other info
#ifdef dPMR_PRINT_DEBUG_INFO
//Display the CCH content
for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++)
{
fprintf(stderr, "i = %u - ", i);
fprintf(stderr, "Comm Mode = %01u - ", state->dPMRVoiceFS2Frame.CommunicationMode[i]);
fprintf(stderr, "Version = %01u - ", state->dPMRVoiceFS2Frame.Version[i]);
fprintf(stderr, "Comms Format = %01u - ", state->dPMRVoiceFS2Frame.CommsFormat[i]);
fprintf(stderr, "Emergency = %01u - ", state->dPMRVoiceFS2Frame.EmergencyPriority[i]);
fprintf(stderr, "Reserved = %01u - ", state->dPMRVoiceFS2Frame.Reserved[i]);
fprintf(stderr, "Slow Data = 0x%05X - ", state->dPMRVoiceFS2Frame.SlowData[i]);
if(HammingOk[i] && CrcOk[i]) fprintf(stderr, "Valid");
else fprintf(stderr, "CRC ERROR");
fprintf(stderr, "\n");
}
#endif //dPMR_PRINT_DEBUG_INFO
} //End processdPMRvoice()
/* Scrambler used for dPMR transmission (different of the
* voice encryption scrambler), see ETSI TS 102 658 chapter
* 7.3 for the polynomial description.
* It is a X^9 + X^5 + 1 polynomial. */
void ScrambledPMRBit(uint32_t * LfsrValue, uint8_t * BufferIn, uint8_t * BufferOut, uint32_t NbOfBitToScramble)
{
uint8_t S[9] = {0};
uint32_t i;
uint8_t Temp;
uint32_t LFSRValue;
LFSRValue = *LfsrValue;
/* Load the initial LFSR value */
for(i = 0; i < 9; i++)
{
S[i] = LFSRValue & 1;
LFSRValue >>= 1;
}
/* There are 72 bit to descramble for voice and 288 bit for data */
for(i = 0; i < NbOfBitToScramble; i++)
{
BufferOut[i] = (BufferIn[i] ^ S[0]) & 0x01;
/* Shift registers */
Temp = S[4] ^ S[0];
S[0] = S[1];
S[1] = S[2];
S[2] = S[3];
S[3] = S[4];
S[4] = S[5];
S[5] = S[6];
S[6] = S[7];
S[7] = S[8];
S[8] = Temp;
}
/* Save the final LFSR value */
LFSRValue = 0;
for(i = 9; i > 0; i--)
{
LFSRValue <<= 1;
LFSRValue |= S[i - 1] & 1;
}
*LfsrValue = LFSRValue;
} /* End ScrambleDPmrBit() */
/* Scrambler used for dPMR scrambling / descrambling,
* see ETSI TS 102 658 chapter 7.4 for the
* polynomial description.
* It is a X^9 + X^5 + 1 polynomial. */
void DeInterleave6x12DPmrBit(uint8_t * BufferIn, uint8_t * BufferOut)
{
uint8_t Matrix[12][6] = {0};
uint32_t i, j, k;
/* Step 1 : Filling the 12 x 6 bit matrix */
k = 0;
for(i = 0; i < 12; i++)
{
for(j = 0; j < 6; j++)
{
Matrix[i][j] = BufferIn[k++];
}
}
/* Step 2 : Filling the output buffer with deinterleaved data */
k = 0;
for(j = 0; j < 6; j++)
{
for(i = 0; i < 12; i++)
{
BufferOut[k++] = Matrix[i][j];
}
}
} /* End DeInterleave6x12DPmrBit() */
/* CRC 7 bit computation with the following
* polynomial : X^7 + X^3 + 1 */
uint8_t CRC7BitdPMR(uint8_t * BufferIn, uint32_t BitLength)
{
uint8_t ShiftRegister = 0x00; /* All bit to '0' (7 LSBit only used) */
uint8_t Polynome = 0x09; /* X^7 + X^3 + 1 */
uint32_t i;
for(i = 0; i < BitLength; i++)
{
if(((ShiftRegister >> 6) & 1) ^ BufferIn[i])
{
ShiftRegister = ((ShiftRegister << 1) ^ Polynome) & 0x7F;
}
else
{
ShiftRegister = (ShiftRegister << 1) & 0x7F;
}
}
return ShiftRegister;
} /* End CRC7BitdPMR() */
/* CRC 8 bit computation with the following
* polynomial : X^8 + X^2 + X + 1 */
uint8_t CRC8BitdPMR(uint8_t * BufferIn, uint32_t BitLength)
{
uint8_t ShiftRegister = 0xFF; /* All bit to '1' */
uint8_t Polynome = 0x07; /* X^7 + X^3 + 1 */
uint32_t i;
for(i = 0; i < BitLength; i++)
{
if(((ShiftRegister >> 7) & 1) ^ BufferIn[i])
{
ShiftRegister = ((ShiftRegister << 1) ^ Polynome) & 0xFF;
}
else
{
ShiftRegister = (ShiftRegister << 1) & 0xFF;
}
}
return ShiftRegister;
} /* End CRC8BitdPMR() */
/* Convert an air interface identifier (AI ID) into
* a 7 ASCII digit string.
*
* See dPMR standard chapter A.1.2.1.1.6
* "Mapping of dialled strings to the AI address space" */
void ConvertAirInterfaceID(uint32_t AI_ID, uint8_t ID[8])
{
uint32_t AI_ID_Temp = AI_ID;
uint32_t Digit;
/* 1st digit */
Digit = AI_ID_Temp / 1464100;
AI_ID_Temp = AI_ID_Temp % 1464100;
if(Digit == 10) ID[0] = '*';
else ID[0] = Digit + '0';
/* 2nd digit */
Digit = AI_ID_Temp / 146410;
AI_ID_Temp = AI_ID_Temp % 146410;
if(Digit == 10) ID[1] = '*';
else ID[1] = Digit + '0';
/* 3rd digit */
Digit = AI_ID_Temp / 14641;
AI_ID_Temp = AI_ID_Temp % 14641;
if(Digit == 10) ID[2] = '*';
else ID[2] = Digit + '0';
/* 4th digit */
Digit = AI_ID_Temp / 1331;
AI_ID_Temp = AI_ID_Temp % 1331;
if(Digit == 10) ID[3] = '*';
else ID[3] = Digit + '0';
/* 5th digit */
Digit = AI_ID_Temp / 121;
AI_ID_Temp = AI_ID_Temp % 121;
if(Digit == 10) ID[4] = '*';
else ID[4] = Digit + '0';
/* 6th digit */
Digit = AI_ID_Temp / 11;
AI_ID_Temp = AI_ID_Temp % 11;
if(Digit == 10) ID[5] = '*';
else ID[5] = Digit + '0';
/* 7th digit */
Digit = AI_ID_Temp;
if(Digit == 10) ID[6] = '*';
else ID[6] = Digit + '0';
/* Add the "end of string" */
ID[7] = '\0';
} /* End convertAirInterfaceID() */
/* End of file */

624
src/dsd_audio.c Normal file
View File

@@ -0,0 +1,624 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
pa_sample_spec ss;
pa_sample_spec tt;
pa_sample_spec zz;
pa_sample_spec cc;
void closePulseOutput (dsd_opts * opts)
{
pa_simple_free (opts->pulse_digi_dev_out);
if (opts->dmr_stereo == 1)
{
pa_simple_free (opts->pulse_digi_dev_outR);
}
}
void closePulseInput (dsd_opts * opts)
{
pa_simple_free (opts->pulse_digi_dev_in);
}
void openPulseOutput(dsd_opts * opts)
{
ss.format = PA_SAMPLE_S16NE;
ss.channels = opts->pulse_raw_out_channels; //doing tests with 2 channels at 22050 for 44100 audio default in pulse
ss.rate = opts->pulse_raw_rate_out; //48000
tt.format = PA_SAMPLE_S16NE;
tt.channels = opts->pulse_digi_out_channels; //doing tests with 2 channels at 22050 for 44100 audio default in pulse
tt.rate = opts->pulse_digi_rate_out; //48000, switches to 8000 when using RTL dongle
if (opts->monitor_input_audio == 1)
{
opts->pulse_raw_dev_out = pa_simple_new(NULL, "DSD-FME3", PA_STREAM_PLAYBACK, NULL, "Raw Audio Out", &ss, NULL, NULL, NULL);
}
//tt
pa_channel_map* left = 0; //NULL and 0 are same in this context
pa_channel_map* right = 0; //NULL and 0 are same in this context
if (opts->dmr_stereo == 0)
{
opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, NULL, opts->output_name, &tt, left, NULL, NULL);
}
if (opts->dmr_stereo == 1)
{
opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME1", PA_STREAM_PLAYBACK, NULL, "XDMA SLOT 1", &tt, left, NULL, NULL);
opts->pulse_digi_dev_outR = pa_simple_new(NULL, "DSD-FME2", PA_STREAM_PLAYBACK, NULL, "XDMA SLOT 2", &tt, right, NULL, NULL);
}
}
void openPulseInput(dsd_opts * opts)
{
cc.format = PA_SAMPLE_S16NE;
cc.channels = opts->pulse_digi_in_channels;
cc.rate = opts->pulse_digi_rate_in; //48000
opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, NULL, opts->output_name, &cc, NULL, NULL, NULL);
}
void
processAudio (dsd_opts * opts, dsd_state * state)
{
int i, n;
float aout_abs, max, gainfactor, gaindelta, maxbuf;
if (opts->audio_gain == (float) 0)
{
// detect max level
max = 0;
//attempt to reduce crackle by overriding the max value when not using pulse input
if (opts->audio_in_type != 0)
{
max = 3000;
}
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
for (n = 0; n < 160; n++)
{
aout_abs = fabsf (*state->audio_out_temp_buf_p);
if (aout_abs > max)
{
max = aout_abs;
}
state->audio_out_temp_buf_p++;
}
*state->aout_max_buf_p = max;
state->aout_max_buf_p++;
state->aout_max_buf_idx++;
if (state->aout_max_buf_idx > 24)
{
state->aout_max_buf_idx = 0;
state->aout_max_buf_p = state->aout_max_buf;
}
// lookup max history
for (i = 0; i < 25; i++)
{
maxbuf = state->aout_max_buf[i];
if (maxbuf > max)
{
max = maxbuf;
}
}
// determine optimal gain level
if (max > (float) 0)
{
gainfactor = ((float) 30000 / max);
}
else
{
gainfactor = (float) 50;
}
if (gainfactor < state->aout_gain)
{
state->aout_gain = gainfactor;
gaindelta = (float) 0;
}
else
{
if (gainfactor > (float) 50)
{
gainfactor = (float) 50;
}
gaindelta = gainfactor - state->aout_gain;
if (gaindelta > ((float) 0.05 * state->aout_gain))
{
gaindelta = ((float) 0.05 * state->aout_gain);
}
}
gaindelta /= (float) 160;
}
else
{
gaindelta = (float) 0;
}
if(opts->audio_gain >= 0)
{
// adjust output gain
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
for (n = 0; n < 160; n++)
{
*state->audio_out_temp_buf_p = (state->aout_gain + ((float) n * gaindelta)) * (*state->audio_out_temp_buf_p);
state->audio_out_temp_buf_p++;
}
state->aout_gain += ((float) 160 * gaindelta);
}
// copy audio data to output buffer and upsample if necessary
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
//we only want to upsample when using sample rates greater than 8k for output,
//hard set to 8k for RTL mono and MBE playback, otherwise crackling may occur.
if (opts->pulse_digi_rate_out > 8000)
{
for (n = 0; n < 160; n++)
{
upsample (state, *state->audio_out_temp_buf_p);
state->audio_out_temp_buf_p++;
state->audio_out_float_buf_p += 6;
state->audio_out_idx += 6;
state->audio_out_idx2 += 6;
}
state->audio_out_float_buf_p -= (960 + opts->playoffset);
// copy to output (short) buffer
for (n = 0; n < 960; n++)
{
if (*state->audio_out_float_buf_p > 32767.0F)
{
*state->audio_out_float_buf_p = 32767.0F;
}
else if (*state->audio_out_float_buf_p < -32768.0F)
{
*state->audio_out_float_buf_p = -32768.0F;
}
*state->audio_out_buf_p = (short) *state->audio_out_float_buf_p;
state->audio_out_buf_p++;
state->audio_out_float_buf_p++;
}
state->audio_out_float_buf_p += opts->playoffset;
}
else
{
for (n = 0; n < 160; n++)
{
if (*state->audio_out_temp_buf_p > 32767.0F)
{
*state->audio_out_temp_buf_p = 32767.0F;
}
else if (*state->audio_out_temp_buf_p < -32768.0F)
{
*state->audio_out_temp_buf_p = -32768.0F;
}
*state->audio_out_buf_p = (short) *state->audio_out_temp_buf_p;
state->audio_out_buf_p++;
state->audio_out_temp_buf_p++;
state->audio_out_idx++;
state->audio_out_idx2++;
}
}
}
void
processAudioR (dsd_opts * opts, dsd_state * state)
{
int i, n;
float aout_abs, max, gainfactor, gaindelta, maxbuf;
if (opts->audio_gainR == (float) 0)
{
// detect max level
max = 0;
//attempt to reduce crackle by overriding the max value when not using pulse input
if (opts->audio_in_type != 0)
{
max = 3000;
}
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
for (n = 0; n < 160; n++)
{
aout_abs = fabsf (*state->audio_out_temp_buf_pR);
if (aout_abs > max)
{
max = aout_abs;
}
state->audio_out_temp_buf_pR++;
}
*state->aout_max_buf_pR = max;
state->aout_max_buf_pR++;
state->aout_max_buf_idxR++;
if (state->aout_max_buf_idxR > 24)
{
state->aout_max_buf_idxR = 0;
state->aout_max_buf_pR = state->aout_max_bufR;
}
// lookup max history
for (i = 0; i < 25; i++)
{
maxbuf = state->aout_max_bufR[i];
if (maxbuf > max)
{
max = maxbuf;
}
}
// determine optimal gain level
if (max > (float) 0)
{
gainfactor = ((float) 30000 / max);
}
else
{
gainfactor = (float) 50;
}
if (gainfactor < state->aout_gainR)
{
state->aout_gainR = gainfactor;
gaindelta = (float) 0;
}
else
{
if (gainfactor > (float) 50)
{
gainfactor = (float) 50;
}
gaindelta = gainfactor - state->aout_gainR;
if (gaindelta > ((float) 0.05 * state->aout_gainR))
{
gaindelta = ((float) 0.05 * state->aout_gainR);
}
}
gaindelta /= (float) 160;
}
else
{
gaindelta = (float) 0;
}
if(opts->audio_gainR >= 0)
{
// adjust output gain
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
for (n = 0; n < 160; n++)
{
*state->audio_out_temp_buf_pR = (state->aout_gainR + ((float) n * gaindelta)) * (*state->audio_out_temp_buf_pR);
state->audio_out_temp_buf_pR++;
}
state->aout_gainR += ((float) 160 * gaindelta);
}
// copy audio data to output buffer and upsample if necessary
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
//we only want to upsample when using sample rates greater than 8k for output,
//hard set to 8k for RTL mono and MBE playback, otherwise crackling may occur.
if (opts->pulse_digi_rate_out > 8000)
{
for (n = 0; n < 160; n++)
{
upsample (state, *state->audio_out_temp_buf_pR);
state->audio_out_temp_buf_pR++;
state->audio_out_float_buf_pR += 6;
state->audio_out_idxR += 6;
state->audio_out_idx2R += 6;
}
state->audio_out_float_buf_pR -= (960 + opts->playoffsetR);
// copy to output (short) buffer
for (n = 0; n < 960; n++)
{
if (*state->audio_out_float_buf_pR > 32767.0F)
{
*state->audio_out_float_buf_pR = 32767.0F;
}
else if (*state->audio_out_float_buf_pR < -32768.0F)
{
*state->audio_out_float_buf_pR = -32768.0F;
}
*state->audio_out_buf_pR = (short) *state->audio_out_float_buf_pR;
state->audio_out_buf_pR++;
state->audio_out_float_buf_pR++;
}
state->audio_out_float_buf_pR += opts->playoffsetR;
}
else
{
for (n = 0; n < 160; n++)
{
if (*state->audio_out_temp_buf_pR > 32767.0F)
{
*state->audio_out_temp_buf_pR = 32767.0F;
}
else if (*state->audio_out_temp_buf_pR < -32768.0F)
{
*state->audio_out_temp_buf_pR = -32768.0F;
}
*state->audio_out_buf_pR = (short) *state->audio_out_temp_buf_pR;
state->audio_out_buf_pR++;
state->audio_out_temp_buf_pR++;
state->audio_out_idxR++;
state->audio_out_idx2R++;
}
}
}
void writeSynthesizedVoice (dsd_opts * opts, dsd_state * state)
{
int n;
short aout_buf[160];
short *aout_buf_p;
aout_buf_p = aout_buf;
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
for (n = 0; n < 160; n++)
{
if (*state->audio_out_temp_buf_p > (float) 32767)
{
*state->audio_out_temp_buf_p = (float) 32767;
}
else if (*state->audio_out_temp_buf_p < (float) -32768)
{
*state->audio_out_temp_buf_p = (float) -32768;
}
*aout_buf_p = (short) *state->audio_out_temp_buf_p;
aout_buf_p++;
state->audio_out_temp_buf_p++;
}
sf_write_short(opts->wav_out_f, aout_buf, 160);
}
void writeSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
{
int n;
short aout_buf[160];
short *aout_buf_p;
aout_buf_p = aout_buf;
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
for (n = 0; n < 160; n++)
{
if (*state->audio_out_temp_buf_pR > (float) 32767)
{
*state->audio_out_temp_buf_pR = (float) 32767;
}
else if (*state->audio_out_temp_buf_pR < (float) -32768)
{
*state->audio_out_temp_buf_pR = (float) -32768;
}
*aout_buf_p = (short) *state->audio_out_temp_buf_pR;
aout_buf_p++;
state->audio_out_temp_buf_pR++;
}
sf_write_short(opts->wav_out_fR, aout_buf, 160);
}
void writeRawSample (dsd_opts * opts, dsd_state * state, short sample)
{
int n;
short aout_buf[160];
short *aout_buf_p;
//sf_write_short(opts->wav_out_raw, aout_buf, 160);
//only write if actual audio, truncate silence
if (sample != 0)
{
sf_write_short(opts->wav_out_raw, &sample, 2); //2 to match pulseaudio input sample read
}
}
void
playSynthesizedVoice (dsd_opts * opts, dsd_state * state)
{
ssize_t result;
if (state->audio_out_idx > opts->delay)
{
// output synthesized speech to sound card
if(opts->audio_out_type == 2)
{
//go F yourself PA
}
else
{
//Test just sending it straight on since I think I've figured out the STDIN and RTL for Stereo
pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL); //Yay! It works.
state->audio_out_idx = 0;
}
}
if (state->audio_out_idx2 >= 800000)
{
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
state->audio_out_buf_p = state->audio_out_buf + 100;
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
memset (state->audio_out_buf, 0, 100 * sizeof (short));
state->audio_out_idx2 = 0;
}
}
void
playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
{
ssize_t result;
if (state->audio_out_idxR > opts->delay)
{
// output synthesized speech to sound card
if(opts->audio_out_type == 2)
{
//go F yourself PA
}
else
{
pa_simple_write(opts->pulse_digi_dev_outR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2), NULL); //Yay! It works.
state->audio_out_idxR = 0;
}
}
if (state->audio_out_idx2R >= 800000)
{
state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100;
state->audio_out_buf_pR = state->audio_out_bufR + 100;
memset (state->audio_out_float_bufR, 0, 100 * sizeof (float));
memset (state->audio_out_bufR, 0, 100 * sizeof (short));
state->audio_out_idx2R = 0;
}
}
void
openAudioOutDevice (dsd_opts * opts, int speed)
{
//converted to handle any calls to use portaudio
if(strncmp(opts->audio_out_dev, "pa:", 3) == 0)
{
opts->audio_out_type = 0;
fprintf (stderr,"Error, Port Audio is not supported by FME!\n");
fprintf (stderr,"Using Pulse Audio Output Stream Instead! \n");
sprintf (opts->audio_out_dev, "pulse");
}
if(strncmp(opts->audio_in_dev, "pulse", 5) == 0)
{
opts->audio_in_type = 0;
}
else
{
struct stat stat_buf;
// if(stat(opts->audio_out_dev, &stat_buf) != 0 && strncmp(opts->audio_out_dev, "pulse", 5 != 0)) //HERE
// {
// fprintf (stderr,"Error, couldn't open %s\n", opts->audio_out_dev);
// exit(1);
// }
// if( (!(S_ISCHR(stat_buf.st_mode) || S_ISBLK(stat_buf.st_mode))) && strncmp(opts->audio_out_dev, "pulse", 5 != 0))
// {
// // this is not a device
// fprintf (stderr,"Error, %s is not a device. use -w filename for wav output.\n", opts->audio_out_dev);
// exit(1);
// }
}
fprintf (stderr,"Audio Out Device: %s\n", opts->audio_out_dev);
}
void
openAudioInDevice (dsd_opts * opts)
{
// get info of device/file
if(strncmp(opts->audio_in_dev, "-", 1) == 0)
{
opts->audio_in_type = 1;
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
opts->audio_in_file_info->samplerate=48000;
opts->pulse_digi_rate_out = 8000; //set out rate to 8000 for stdin input
opts->audio_in_file_info->channels=1;
opts->audio_in_file_info->seekable=0;
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
opts->audio_in_file = sf_open_fd(fileno(stdin), SFM_READ, opts->audio_in_file_info, 0);
if(opts->audio_in_file == NULL) {
fprintf (stderr,"Error, couldn't open stdin with libsndfile: %s\n", sf_strerror(NULL));
exit(1); //had this one disabled, re-enabling it now
}
}
//converted to handle any calls to use portaudio
else if(strncmp(opts->audio_in_dev, "pa:", 2) == 0)
{
opts->audio_in_type = 0;
fprintf (stderr,"Error, Port Audio is not supported by FME!\n");
fprintf (stderr,"Using Pulse Audio Input Stream Instead! \n");
sprintf (opts->audio_in_dev, "pulse");
}
else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0)
{
opts->audio_in_type = 3;
}
else if(strncmp(opts->audio_in_dev, "pulse", 5) == 0)
{
opts->audio_in_type = 0;
}
//convert from opening wav files to OP25 symbol capture files since opening wav files is busted
else
{
struct stat stat_buf;
if (stat(opts->audio_in_dev, &stat_buf) != 0)
{
fprintf (stderr,"Error, couldn't open %s\n", opts->audio_in_dev);
exit(1);
}
if (S_ISREG(stat_buf.st_mode))
{
// is this a regular file? then process with libsndfile.
//opts->pulse_digi_rate_out = 8000; //this for wav files input?
// opts->audio_in_type = 1;
// opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
// opts->audio_in_file_info->channels = 1;
// opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
//
// if(opts->audio_in_file == NULL)
// {
// fprintf (stderr,"Error, couldn't open file %s\n", opts->audio_in_dev);
// exit(1);
// }
opts->symbolfile = fopen(opts->audio_in_dev, "r");
opts->audio_in_type = 4; //symbol capture bin files
}
else
{
// this is a device, use old handling, pulse audio now
opts->audio_in_type = 0;
}
}
if (opts->split == 1)
{
fprintf (stderr,"Audio In Device: %s\n", opts->audio_in_dev);
}
else
{
fprintf (stderr,"Audio In/Out Device: %s\n", opts->audio_in_dev);
}
}

482
src/dsd_dibit.c Normal file
View File

@@ -0,0 +1,482 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
#include "dsd.h"
#include "p25p1_heuristics.h"
static void
print_datascope(dsd_opts* opts, dsd_state* state, int* sbuf2)
{
int i, j, o;
char modulation[8];
int spectrum[64];
if (state->rf_mod == 0)
{
sprintf (modulation, "C4FM");
}
else if (state->rf_mod == 1)
{
sprintf (modulation, "QPSK");
}
else if (state->rf_mod == 2)
{
sprintf (modulation, "GFSK");
}
for (i = 0; i < 64; i++)
{
spectrum[i] = 0;
}
for (i = 0; i < opts->ssize; i++)
{
o = (sbuf2[i] + 32768) / 1024;
spectrum[o]++;
}
if (state->symbolcnt > (4800 / opts->scoperate))
{
state->symbolcnt = 0;
fprintf (stderr, "\n");
fprintf (stderr, "Demod mode: %s Nac: %4X\n", modulation, state->nac);
fprintf (stderr, "Frame Type: %s Talkgroup: %7i\n", state->ftype, state->lasttg);
fprintf (stderr, "Frame Subtype: %s Source: %12i\n", state->fsubtype, state->lastsrc);
fprintf (stderr, "TDMA activity: %s %s Voice errors: %s\n", state->slot0light, state->slot1light, state->err_str);
fprintf (stderr, "+----------------------------------------------------------------+\n");
for (i = 0; i < 10; i++)
{
printf ("|");
for (j = 0; j < 64; j++)
{
if (i == 0)
{
if ((j == ((state->min) + 32768) / 1024) || (j == ((state->max) + 32768) / 1024))
{
fprintf (stderr, "#");
}
else if ((j == ((state->lmid) + 32768) / 1024) || (j == ((state->umid) + 32768) / 1024))
{
fprintf (stderr, "^");
}
else if (j == (state->center + 32768) / 1024)
{
fprintf (stderr, "!");
}
else
{
if (j == 32)
{
fprintf (stderr, "|");
}
else
{
fprintf (stderr, " ");
}
}
}
else
{
if (spectrum[j] > 9 - i)
{
fprintf (stderr, "*");
}
else
{
if (j == 32)
{
fprintf (stderr, "|");
}
else
{
fprintf (stderr, " ");
}
}
}
}
fprintf (stderr, "|\n");
}
fprintf (stderr, "+----------------------------------------------------------------+\n");
}
}
static void
use_symbol (dsd_opts* opts, dsd_state* state, int symbol)
{
int i;
int sbuf2[128];
int lmin, lmax, lsum;
for (i = 0; i < opts->ssize; i++)
{
sbuf2[i] = state->sbuf[i];
}
qsort (sbuf2, opts->ssize, sizeof (int), comp);
// continuous update of min/max in rf_mod=1 (QPSK) mode
// in c4fm min/max must only be updated during sync
if (state->rf_mod == 1)
{
lmin = (sbuf2[0] + sbuf2[1]) / 2;
lmax = (sbuf2[(opts->ssize - 1)] + sbuf2[(opts->ssize - 2)]) / 2;
state->minbuf[state->midx] = lmin;
state->maxbuf[state->midx] = lmax;
if (state->midx == (opts->msize - 1))
{
state->midx = 0;
}
else
{
state->midx++;
}
lsum = 0;
for (i = 0; i < opts->msize; i++)
{
lsum += state->minbuf[i];
}
state->min = lsum / opts->msize;
lsum = 0;
for (i = 0; i < opts->msize; i++)
{
lsum += state->maxbuf[i];
}
state->max = lsum / opts->msize;
state->center = ((state->max) + (state->min)) / 2;
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
state->maxref = (int)((state->max) * 0.80F);
state->minref = (int)((state->min) * 0.80F);
}
else
{
state->maxref = state->max;
state->minref = state->min;
}
// Increase sidx
if (state->sidx >= (opts->ssize - 1))
{
state->sidx = 0;
if (opts->datascope == 1)
{
print_datascope(opts, state, sbuf2);
}
}
else
{
state->sidx++;
}
if (state->dibit_buf_p > state->dibit_buf + 900000)
{
state->dibit_buf_p = state->dibit_buf + 200;
}
//dmr buffer
if (state->dmr_payload_p > state->dmr_payload_buf + 900000)
{
state->dmr_payload_p = state->dmr_payload_buf + 200;
}
//dmr buffer end
}
static int
invert_dibit(int dibit)
{
switch (dibit)
{
case 0:
return 2;
case 1:
return 3;
case 2:
return 0;
case 3:
return 1;
}
// Error, shouldn't be here
assert(0);
return -1;
}
static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
//int digitize (dsd_opts* opts, dsd_state* state, int symbol)
{
// determine dibit state
if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18))
{
// 6 +D-STAR
// 14 +ProVoice
// 18 +D-STAR_HD
if (symbol > state->center)
{
*state->dibit_buf_p = 1;
state->dibit_buf_p++;
return (0); // +1
}
else
{
*state->dibit_buf_p = 3;
state->dibit_buf_p++;
return (1); // +3
}
}
else if ((state->synctype == 7) || (state->synctype == 15)|| (state->synctype == 19))
{
// 7 -D-STAR
// 15 -ProVoice
// 19 -D-STAR_HD
if (symbol > state->center)
{
*state->dibit_buf_p = 1;
state->dibit_buf_p++;
return (1); // +3
}
else
{
*state->dibit_buf_p = 3;
state->dibit_buf_p++;
return (0); // +1
}
}
else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) ||
(state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) ||
(state->synctype == 17) || (state->synctype == 36) )
{
// 1 -P25p1
// 3 -X2-TDMA (inverted signal voice frame)
// 5 -X2-TDMA (inverted signal data frame)
// 9 -NXDN (inverted voice frame)
// 11 -DMR (inverted signal voice frame)
// 13 -DMR (inverted signal data frame)
// 17 -NXDN (inverted data frame)
int valid;
int dibit;
valid = 0;
if (state->synctype == 1)
{
// Use the P25 heuristics if available
valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit);
}
if (valid == 0)
{
// Revert to the original approach: choose the symbol according to the regions delimited
// by center, umid and lmid
if (symbol > state->center)
{
if (symbol > state->umid)
{
dibit = 3; // -3
}
else
{
dibit = 2; // -1
}
}
else
{
if (symbol < state->lmid)
{
dibit = 1; // +3
}
else
{
dibit = 0; // +1
}
}
}
state->last_dibit = dibit;
*state->dibit_buf_p = invert_dibit(dibit);
state->dibit_buf_p++;
//dmr buffer
*state->dmr_payload_p = invert_dibit(dibit);
state->dmr_payload_p++;
//dmr buffer end
return dibit;
}
else
{
// 0 +P25p1
// 2 +X2-TDMA (non inverted signal data frame)
// 4 +X2-TDMA (non inverted signal voice frame)
// 8 +NXDN (non inverted voice frame)
// 10 +DMR (non inverted signal data frame)
// 12 +DMR (non inverted signal voice frame)
// 16 +NXDN (non inverted data frame)
int valid;
int dibit;
valid = 0;
if (state->synctype == 0)
{
// Use the P25 heuristics if available
valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit);
}
if (valid == 0)
{
// Revert to the original approach: choose the symbol according to the regions delimited
// by center, umid and lmid
if (symbol > state->center)
{
if (symbol > state->umid)
{
dibit = 1; // +3
}
else
{
dibit = 0; // +1
}
}
else
{
if (symbol < state->lmid)
{
dibit = 3; // -3
}
else
{
dibit = 2; // -1
}
}
}
state->last_dibit = dibit;
*state->dibit_buf_p = dibit;
state->dibit_buf_p++;
//dmr buffer
//note to self, perceived bug with initial dibit buffer appears to be caused by
//media player, when playing back from audacity, the initial few dmr frames are
//decoded properly, need to investigate the root cause of what audacity is doing
//vs other audio sources...perhaps just the audio level itself?
*state->dmr_payload_p = dibit;
state->dmr_payload_p++;
//dmr buffer end
return dibit;
}
}
int
get_dibit_and_analog_signal (dsd_opts* opts, dsd_state* state, int* out_analog_signal)
{
int symbol;
int dibit;
#ifdef TRACE_DSD
unsigned int l, r;
#endif
state->numflips = 0;
#ifdef TRACE_DSD
l = state->debug_sample_index;
#endif
symbol = getSymbol (opts, state, 1);
#ifdef TRACE_DSD
r = state->debug_sample_index;
#endif
state->sbuf[state->sidx] = symbol;
if (out_analog_signal != NULL)
{
*out_analog_signal = symbol;
}
use_symbol (opts, state, symbol);
dibit = digitize (opts, state, symbol);
if (opts->audio_in_type == 4)
{
//assign dibit from last symbol/dibit read from capture bin
dibit = state->symbolc;
if (state->use_throttle == 1)
{
usleep(0);
}
}
//symbol/dibit file capture/writing
if (opts->symbol_out == 1)
{
//fprintf (stderr, "%d", dibit);
fputc (dibit, opts->symbol_out_f);
}
#ifdef TRACE_DSD
{
float left, right;
if (state->debug_label_dibit_file == NULL) {
state->debug_label_dibit_file = fopen ("pp_label_dibit.txt", "w");
}
left = l / 48000.0;
right = r / 48000.0;
fprintf(state->debug_label_dibit_file, "%f\t%f\t%i\n", left, right, dibit);
}
#endif
return dibit;
}
/**
* \brief This important method reads the last analog signal value (getSymbol call) and digitizes it.
* Depending of the ongoing transmission it in converted into a bit (0/1) or a di-bit (00/01/10/11).
*/
int
getDibit (dsd_opts* opts, dsd_state* state)
{
return get_dibit_and_analog_signal (opts, state, NULL);
}
void
skipDibit (dsd_opts * opts, dsd_state * state, int count)
{
short sample;
int i;
for (i = 0; i < (count); i++)
{
sample = getDibit (opts, state);
}
}

624
src/dsd_file.c Normal file
View File

@@ -0,0 +1,624 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
time_t nowF;
char * getTimeF(void) //get pretty hh:mm:ss timestamp
{
time_t t = time(NULL);
char * curr;
char * stamp = asctime(localtime( & t));
curr = strtok(stamp, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
return curr;
}
char * getDateF(void) {
char datename[120]; //bug in 32-bit Ubuntu when using date in filename, date is garbage text
char * curr2;
struct tm * to;
time_t t;
t = time(NULL);
to = localtime( & t);
strftime(datename, sizeof(datename), "%Y-%m-%d", to);
curr2 = strtok(datename, " ");
return curr2;
}
void
saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
{
int i, j, k;
unsigned char b;
unsigned char err;
err = (unsigned char) state->errs2;
fputc (err, opts->mbe_out_f);
k = 0;
if (opts->payload == 1) //make opt variable later on to toggle this
{
//fprintf(stderr, "\n IMBE ");
}
for (i = 0; i < 11; i++)
{
b = 0;
for (j = 0; j < 8; j++)
{
b = b << 1;
b = b + imbe_d[k];
k++;
}
if (opts->payload == 1) //make opt variable later on to toggle this
{
//fprintf (stderr, "%02X", b);
}
fputc (b, opts->mbe_out_f);
}
if (opts->payload == 1)
{
//fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
}
fflush (opts->mbe_out_f);
}
void
saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
{
int i, j, k;
unsigned char b;
unsigned char err;
err = (unsigned char) state->errs2;
fputc (err, opts->mbe_out_f);
k = 0;
if (opts->payload == 1) //make opt variable later on to toggle this
{
//fprintf(stderr, "\n AMBE ");
}
//for (i = 0; i < 6; i++)
for (i = 0; i < 6; i++) //using 7 seems to break older amb files where it was 6, need to test 7 on 7 some more
{
b = 0;
for (j = 0; j < 8; j++)
{
b = b << 1;
b = b + ambe_d[k];
k++;
}
if (opts->payload == 1 && i < 6) //make opt variable later on to toggle this
{
//fprintf (stderr, "%02X", b);
}
if (opts->payload == 1 && i == 6) //7th octet should only contain 1 bit? value will be either 0x00 or 0x80?
{
//fprintf (stderr, "%02X", b & 0x80); //7th octet should only contain 1 bit?
}
fputc (b, opts->mbe_out_f);
}
if (opts->payload == 1)
{
//fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
}
b = ambe_d[48];
fputc (b, opts->mbe_out_f);
fflush (opts->mbe_out_f);
}
void
PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d) //for P25P1 and ProVoice
{
int i, j, k;
unsigned char b;
unsigned char err;
err = (unsigned char) state->errs2;
k = 0;
if (opts->payload == 1) //print IMBE info
{
fprintf(stderr, "\n IMBE ");
}
for (i = 0; i < 11; i++)
{
b = 0;
for (j = 0; j < 8; j++)
{
b = b << 1;
b = b + imbe_d[k];
k++;
}
if (opts->payload == 1)
{
fprintf (stderr, "%02X", b);
}
}
if (opts->payload == 1)
{
fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
}
//fprintf (stderr, "\n");
}
void
PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d) //For DMR Stereo, may use this for NXDN too
{
int i, j, k;
unsigned char b;
unsigned char err;
err = (unsigned char) state->errs2;
k = 0;
if (opts->dmr_stereo == 0)
{
fprintf (stderr, "\n");
}
if (opts->payload == 1) //print AMBE info from DMR Stereo method
{
fprintf(stderr, " AMBE ");
}
for (i = 0; i < 7; i++) //using 7 seems to break older amb files where it was 6, need to test 7 on 7 some more
{
b = 0;
for (j = 0; j < 8; j++)
{
b = b << 1;
b = b + ambe_d[k];
k++;
}
if (opts->payload == 1 && i < 6) //make opt variable later on to toggle this
{
fprintf (stderr, "%02X", b);
}
if (opts->payload == 1 && i == 6) //7th octet should only contain 1 bit, value will be either 0x00 or 0x80?
{
fprintf (stderr, "%02X", b & 0x80); //7th octet should only contain 1 bit
}
}
if (state->currentslot == 0)
{
fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
}
else fprintf(stderr, " err = [%X] [%X] ", state->errsR, state->errs2R);
b = ambe_d[48];
if (opts->dmr_stereo == 1) //need to fix the printouts again
{
fprintf (stderr, "\n");
}
}
int
readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
{
int i, j, k;
unsigned char b, x;
state->errs2 = fgetc (opts->mbe_in_f);
state->errs = state->errs2;
k = 0;
if (opts->payload == 1) //make opt variable later on to toggle this
{
fprintf(stderr, "\n");
}
for (i = 0; i < 11; i++)
{
b = fgetc (opts->mbe_in_f);
if (feof (opts->mbe_in_f))
{
return (1);
}
for (j = 0; j < 8; j++)
{
imbe_d[k] = (b & 128) >> 7;
x = x << 1;
x |= ((b & 0x80) >> 7);
b = b << 1;
b = b & 255;
k++;
}
//new printout on reading MBE files back in
if (opts->payload == 1) //make opt variable later on to toggle this
{
fprintf (stderr, "%02X", x);
}
//
}
if (opts->payload == 1)
{
fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2); //not sure that errs here are legit values
}
return (0);
}
int
readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
{
int i, j, k;
unsigned char b, x;
state->errs2 = fgetc (opts->mbe_in_f);
state->errs = state->errs2;
k = 0;
if (opts->payload == 1) //make opt variable later on to toggle this
{
fprintf(stderr, "\n");
}
//for (i = 0; i < 6; i++)
for (i = 0; i < 6; i++) //breaks backwards compatablilty with 6 files
{
b = fgetc (opts->mbe_in_f);
if (feof (opts->mbe_in_f))
{
return (1);
}
for (j = 0; j < 8; j++)
{
ambe_d[k] = (b & 128) >> 7;
x = x << 1;
x |= ((b & 0x80) >> 7);
b = b << 1;
b = b & 255;
k++;
}
if (opts->payload == 1 && i < 6) //make opt variable later on to toggle this
{
fprintf (stderr, "%02X", x);
}
if (opts->payload == 1 && i == 6) //7th octet should only contain 1 bit? value will be either 0x00 or 0x80?
{
fprintf (stderr, "%02X", x & 0x80); //7th octet should only contain 1 bit?
}
}
if (opts->payload == 1)
{
fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
}
b = fgetc (opts->mbe_in_f);
ambe_d[48] = (b & 1);
return (0);
}
void
openMbeInFile (dsd_opts * opts, dsd_state * state)
{
char cookie[5];
opts->mbe_in_f = fopen (opts->mbe_in_file, "ro");
if (opts->mbe_in_f == NULL)
{
fprintf (stderr,"Error: could not open %s\n", opts->mbe_in_file);
}
// read cookie
cookie[0] = fgetc (opts->mbe_in_f);
cookie[1] = fgetc (opts->mbe_in_f);
cookie[2] = fgetc (opts->mbe_in_f);
cookie[3] = fgetc (opts->mbe_in_f);
cookie[4] = 0;
if (strstr (cookie, ".amb") != NULL)
{
state->mbe_file_type = 1;
}
else if (strstr (cookie, ".imb") != NULL)
{
state->mbe_file_type = 0;
}
else
{
state->mbe_file_type = -1;
fprintf (stderr,"Error - unrecognized file type\n");
}
}
//temporarily disabled until the compiler warnings can be fixed, use symbol capture or per call instead!
//probably the easiest thing is to just have this run fclose, and have name assignment on open instead.
// void
// closeMbeOutFile (dsd_opts * opts, dsd_state * state)
// {
//
// char shell[255], newfilename[64], ext[5], datestr[32], new_path[1024];
// char tgid[17];
// int sum, i, j;
// int talkgroup;
// struct tm timep;
// int result;
//
// if (opts->mbe_out_f != NULL)
// {
// if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
// {
// sprintf (ext, ".imb");
// //strptime (opts->mbe_out_file, "%s.imb", &timep);
// }
// else
// {
// sprintf (ext, ".amb");
// //strptime (opts->mbe_out_file, "%s.amb", &timep);
// }
//
// if (state->tgcount > 0)
// {
// for (i = 0; i < 16; i++)
// {
// sum = 0;
// for (j = 0; j < state->tgcount; j++)
// {
// sum = sum + state->tg[j][i] - 48;
// }
// tgid[i] = (char) (((float) sum / (float) state->tgcount) + 48.5);
// }
// tgid[16] = 0;
// talkgroup = (int) strtol (tgid, NULL, 2);
// }
// else
// {
// talkgroup = 0;
// }
//
// fflush (opts->mbe_out_f);
// fclose (opts->mbe_out_f);
// opts->mbe_out_f = NULL;
// strftime (datestr, 31, "%Y-%m-%d-%H%M%S", &timep);
//
// sprintf (newfilename, "%s%s", datestr, ext); //this one
//
// if (state->lastsynctype == 0 || state->lastsynctype == 1)
// {
// sprintf (newfilename, "%s-nac%X-tg%i%s", datestr, state->nac, talkgroup, ext);
// }
//
// //sprintf (new_path, "%s%s", opts->mbe_out_dir, newfilename);
// #ifdef _WIN32
// //sprintf (shell, "move %s %s", opts->mbe_out_path, new_path);
// #else
// //sprintf (shell, "mv %s %s", opts->mbe_out_path, new_path);
// #endif
// //result = system (shell);
//
// state->tgcount = 0;
// for (i = 0; i < 25; i++)
// {
// for (j = 0; j < 16; j++)
// {
// state->tg[i][j] = 48;
// }
// }
// }
// }
//much simpler version
void closeMbeOutFile (dsd_opts * opts, dsd_state * state)
{
if (opts->mbe_out == 1)
{
if (opts->mbe_out_f != NULL)
{
fflush (opts->mbe_out_f);
fclose (opts->mbe_out_f);
opts->mbe_out_f = NULL;
opts->mbe_out = 0;
fprintf (stderr, "\nClosing MBE out file.");
}
}
}
void
openMbeOutFile (dsd_opts * opts, dsd_state * state)
{
//struct timeval tv;
int i, j;
char ext[5];
if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
{
sprintf (ext, ".imb");
}
else
{
sprintf (ext, ".amb");
}
// reset talkgroup id buffer
for (i = 0; i < 12; i++)
{
for (j = 0; j < 25; j++)
{
state->tg[j][i] = 0;
}
}
state->tgcount = 0;
//gettimeofday (&tv, NULL);
//rewrite below line to take getTime function instead?
// sprintf (opts->mbe_out_file, "%i%s", (int) tv.tv_sec, ext);
sprintf (opts->mbe_out_file, "%s %s%s", getDateF(), getTimeF(), ext);
sprintf (opts->mbe_out_path, "%s%s", opts->mbe_out_dir, opts->mbe_out_file);
opts->mbe_out_f = fopen (opts->mbe_out_path, "w");
if (opts->mbe_out_f == NULL)
{
fprintf (stderr,"Error, couldn't open %s\n", opts->mbe_out_path);
}
else opts->mbe_out = 1;
//
fprintf (opts->mbe_out_f, "%s", ext);
fflush (opts->mbe_out_f);
}
void
openWavOutFile (dsd_opts * opts, dsd_state * state)
{
SF_INFO info;
info.samplerate = 8000; //8000
info.channels = 1;
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
if (opts->wav_out_f == NULL)
{
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
return;
}
// state->wav_out_bytes = 0;
}
void openWavOutFileL (dsd_opts * opts, dsd_state * state)
{
SF_INFO info;
info.samplerate = 8000; //8000
info.channels = 1;
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
if (opts->wav_out_f == NULL)
{
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
return;
}
// state->wav_out_bytes = 0;
}
void openWavOutFileR (dsd_opts * opts, dsd_state * state)
{
SF_INFO info;
info.samplerate = 8000; //8000
info.channels = 1;
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
opts->wav_out_fR = sf_open (opts->wav_out_fileR, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
if (opts->wav_out_f == NULL)
{
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_fileR);
return;
}
// state->wav_out_bytes = 0;
}
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
{
SF_INFO info;
info.samplerate = 48000; //8000
info.channels = 1;
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_WRITE, &info);
//opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
opts->wav_out_raw = sf_open (opts->wav_out_file_raw, SFM_RDWR, &info);
if (opts->wav_out_raw == NULL)
{
fprintf (stderr,"Error - could not open raw wav output file %s\n", opts->wav_out_file_raw);
return;
}
// state->wav_out_bytes = 0;
}
void closeWavOutFile (dsd_opts * opts, dsd_state * state)
{
sf_close(opts->wav_out_f);
}
void closeWavOutFileL (dsd_opts * opts, dsd_state * state)
{
sf_close(opts->wav_out_f);
}
void closeWavOutFileR (dsd_opts * opts, dsd_state * state)
{
sf_close(opts->wav_out_fR);
}
void closeWavOutFileRaw (dsd_opts * opts, dsd_state * state)
{
sf_close(opts->wav_out_raw);
}
void openSymbolOutFile (dsd_opts * opts, dsd_state * state)
{
//Do something
opts->symbol_out_f = fopen (opts->symbol_out_file, "w");
}
void writeSymbolOutFile (dsd_opts * opts, dsd_state * state)
{
//Do something
}
void closeSymbolOutFile (dsd_opts * opts, dsd_state * state)
{
//Do something
if (opts->symbol_out == 1)
{
if (opts->symbol_out_file != NULL) //check first, or issuing a second fclose will crash the SOFTWARE
{
fclose(opts->symbol_out_f); //free(): double free detected in tcache 2 (this is a new one) happens when closing more than once
//noCarrier(opts, state); //free(): double free detected in tcache 2 (this is a new one)
}
opts->symbol_out = 0; //set flag to 1
//sprintf (opts->symbol_out_file, "fme-capture.bin"); //use fme for the symbol bin ext
//openSymbolOutFile (opts, state);
}
}

170
src/dsd_filters.c Normal file
View File

@@ -0,0 +1,170 @@
// DMR filter
#define NZEROS 60
float ngain = 7.423339364f;
static float xv[NZEROS+1];
float xcoeffs[] =
{ -0.0083649323f, -0.0265444850f, -0.0428141462f, -0.0537571943f,
-0.0564141052f, -0.0489161045f, -0.0310068662f, -0.0043393881f,
+0.0275375106f, +0.0595423283f, +0.0857543325f, +0.1003565948f,
+0.0986944931f, +0.0782804830f, +0.0395670487f, -0.0136691535f,
-0.0744390415f, -0.1331834575f, -0.1788967208f, -0.2005995448f,
-0.1889627181f, -0.1378439993f, -0.0454976231f, +0.0847488694f,
+0.2444859269f, +0.4209222342f, +0.5982295474f, +0.7593684540f,
+0.8881539892f, +0.9712773915f, +0.9999999166f, +0.9712773915f,
+0.8881539892f, +0.7593684540f, +0.5982295474f, +0.4209222342f,
+0.2444859269f, +0.0847488694f, -0.0454976231f, -0.1378439993f,
-0.1889627181f, -0.2005995448f, -0.1788967208f, -0.1331834575f,
-0.0744390415f, -0.0136691535f, +0.0395670487f, +0.0782804830f,
+0.0986944931f, +0.1003565948f, +0.0857543325f, +0.0595423283f,
+0.0275375106f, -0.0043393881f, -0.0310068662f, -0.0489161045f,
-0.0564141052f, -0.0537571943f, -0.0428141462f, -0.0265444850f,
-0.0083649323f,
};
// NXDN48 filter
//#define NXZEROS 134
//float nxgain = 15.95930463f;
#define NXZEROS 91 //Osmocom OP25
float nxgain = 1.0f; //Osmocom OP25
static float nxv[NXZEROS+1];
//OP25 Osmocom NXDN48 Coeffs seem to do well, will want to do some VS testing
float nxcoeffs[] =
{ 0.00203767f, 0.00491622f, 0.00748189f, 0.00934561f, 0.01017049f, 0.00972167f,
0.00790748f, 0.00480597f, 0.00067202f, -0.00407743f, -0.00890019f, -0.01318478f,
-0.01632047f, -0.01777371f, -0.01716241f, -0.01431852f, -0.00933066f, -0.00255979f,
0.00537624f, 0.01364992f, 0.02130038f, 0.02733235f, 0.03082824f, 0.03106179f,
0.02760061f, 0.02038545f, 0.00977572f, -0.0034462f, -0.01811514f, -0.03276675f,
-0.04575844f, -0.05541762f, -0.06020353f, -0.0588669f, -0.05059129f, -0.03510097f,
-0.01272309f, 0.01560398f, 0.04837926f, 0.08364189f, 0.11912012f, 0.15241359f,
0.18119246f, 0.20339503f, 0.21740525f, 0.2221932f, 0.21740525f, 0.20339503f,
0.18119246f, 0.15241359f, 0.11912012f, 0.08364189f, 0.04837926f, 0.01560398f,
-0.01272309f, -0.03510097f, -0.05059129f, -0.0588669f, -0.06020353f, -0.05541762f,
-0.04575844f, -0.03276675f, -0.01811514f, -0.0034462f, 0.00977572f, 0.02038545f,
0.02760061f, 0.03106179f, 0.03082824f, 0.02733235f, 0.02130038f, 0.01364992f,
0.00537624f, -0.00255979f, -0.00933066f, -0.01431852f, -0.01716241f, -0.01777371f,
-0.01632047f, -0.01318478f, -0.00890019f, -0.00407743f, 0.00067202f, 0.00480597f,
0.00790748f, 0.00972167f, 0.01017049f, 0.00934561f, 0.00748189f, 0.00491622f,
0.00203767f};
//OLD nxCOEFFS
// { +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f,
// +0.022653298f, +0.016379869f, +0.008737200f, +0.000003302f,
// -0.009468531f, -0.019262057f, -0.028914291f, -0.037935027f,
// -0.045828927f, -0.052119261f, -0.056372283f, -0.058221106f,
// -0.057387924f, -0.053703443f, -0.047122444f, -0.037734535f,
// -0.025769308f, -0.011595336f, +0.004287292f, +0.021260954f,
// +0.038610717f, +0.055550276f, +0.071252765f, +0.084885375f,
// +0.095646450f, +0.102803611f, +0.105731303f, +0.103946126f,
// +0.097138329f, +0.085197939f, +0.068234131f, +0.046586711f,
// +0.020828821f, -0.008239664f, -0.039608255f, -0.072081234f,
// -0.104311776f, -0.134843790f, -0.162160200f, -0.184736015f,
// -0.201094346f, -0.209863285f, -0.209831516f, -0.200000470f,
// -0.179630919f, -0.148282051f, -0.105841323f, -0.052543664f,
// +0.011020985f, +0.083912428f, +0.164857408f, +0.252278939f,
// +0.344336996f, +0.438979335f, +0.534000832f, +0.627109358f,
// +0.715995947f, +0.798406824f, +0.872214756f, +0.935487176f,
// +0.986548646f, +1.024035395f, +1.046939951f, +1.054644241f,
// +1.046939951f, +1.024035395f, +0.986548646f, +0.935487176f,
// +0.872214756f, +0.798406824f, +0.715995947f, +0.627109358f,
// +0.534000832f, +0.438979335f, +0.344336996f, +0.252278939f,
// +0.164857408f, +0.083912428f, +0.011020985f, -0.052543664f,
// -0.105841323f, -0.148282051f, -0.179630919f, -0.200000470f,
// -0.209831516f, -0.209863285f, -0.201094346f, -0.184736015f,
// -0.162160200f, -0.134843790f, -0.104311776f, -0.072081234f,
// -0.039608255f, -0.008239664f, +0.020828821f, +0.046586711f,
// +0.068234131f, +0.085197939f, +0.097138329f, +0.103946126f,
// +0.105731303f, +0.102803611f, +0.095646450f, +0.084885375f,
// +0.071252765f, +0.055550276f, +0.038610717f, +0.021260954f,
// +0.004287292f, -0.011595336f, -0.025769308f, -0.037734535f,
// -0.047122444f, -0.053703443f, -0.057387924f, -0.058221106f,
// -0.056372283f, -0.052119261f, -0.045828927f, -0.037935027f,
// -0.028914291f, -0.019262057f, -0.009468531f, +0.000003302f,
// +0.008737200f, +0.016379869f, +0.022653298f, +0.027362877f,
// +0.030401148f, +0.031747267f, +0.031462429f,
// };
// DMR filter F4EXB
#define DMRNZEROS 60
// DMR filter F4EXB
const float dmrgain = 6.82973073748f;
// DMR filter F4EXB
static float dxv[DMRNZEROS+1];
// DMR filter F4EXB - root raised cosine alpha=0.7 Ts = 6650 S/s Fc = 48kHz
const float dmrcoeffs[] =
{0.0301506278, 0.0269200615, 0.0159662432, -0.0013114705, -0.0216605133,
-0.0404938748, -0.0528141756, -0.0543747957, -0.0428325003, -0.0186176083,
0.0147202645, 0.0508418571, 0.0816392577, 0.0988113688, 0.0957187780,
0.0691512084, 0.0206194642, -0.0431564563, -0.1107569268, -0.1675773224,
-0.1981519842, -0.1889130786, -0.1308939560, -0.0218608492, 0.1325685970,
0.3190962499, 0.5182530574, 0.7070497652, 0.8623526878, 0.9644213921,
1.0000000000, 0.9644213921, 0.8623526878, 0.7070497652, 0.5182530574,
0.3190962499, 0.1325685970, -0.0218608492, -0.1308939560, -0.1889130786,
-0.1981519842, -0.1675773224, -0.1107569268, -0.0431564563, 0.0206194642,
0.0691512084, 0.0957187780, 0.0988113688, 0.0816392577, 0.0508418571,
0.0147202645, -0.0186176083, -0.0428325003, -0.0543747957, -0.0528141756,
-0.0404938748, -0.0216605133, -0.0013114705, 0.0159662432, 0.0269200615, 0.0301506278};
short dsd_input_filter(short sample, int mode);
short
dmr_filter(short sample)
{
return dsd_input_filter(sample, 3);
}
short
nxdn_filter(short sample)
{
return dsd_input_filter(sample, 2);
}
short
dsd_input_filter(short sample, int mode)
{
float sum; int i;
float gain;
int zeros;
float *v, *coeffs;
switch(mode) {
case 1:
gain = ngain;
v = xv;
coeffs = xcoeffs;
zeros = NZEROS;
break;
case 2:
gain = nxgain;
v = nxv;
coeffs = nxcoeffs;
zeros = NXZEROS;
break;
case 3:
gain = dmrgain;
v = dxv;
coeffs = (float *)dmrcoeffs;
zeros = DMRNZEROS;
break;
default:
return sample;
}
for (i = 0; i < zeros; i++)
v[i] = v[i+1];
v[zeros] = sample; // unfiltered sample in
sum = 0.0f;
for (i = 0; i <= zeros; i++)
sum += (coeffs[i] * v[i]);
return (short)(sum / gain); // filtered sample out
}

722
src/dsd_frame.c Normal file
View File

@@ -0,0 +1,722 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
#if !defined(NULL)
#define NULL 0
#endif
#include "p25p1_check_nid.h"
void
printFrameInfo (dsd_opts * opts, dsd_state * state)
{
int level;
level = (int) state->max / 164;
if (opts->verbose > 0)
{
//fprintf (stderr,"inlvl: %2i%% ", level);
}
if (state->nac != 0)
{
fprintf (stderr, "%s", KCYN);
fprintf (stderr,"nac: [%4X] ", state->nac);
fprintf (stderr, "%s", KNRM);
}
if (opts->verbose > 1)
{
fprintf (stderr, "%s", KGRN);
fprintf (stderr,"src: [%8i] ", state->lastsrc);
fprintf (stderr, "%s", KNRM);
}
fprintf (stderr, "%s", KGRN);
fprintf (stderr,"tg: [%5i] ", state->lasttg);
fprintf (stderr, "%s", KNRM);
}
void
processFrame (dsd_opts * opts, dsd_state * state)
{
int i, j, dibit;
char duid[3];
char nac[13];
int level;
char status_0;
char bch_code[63];
int index_bch_code;
unsigned char parity;
char v;
int new_nac;
char new_duid[3];
int check_result;
nac[12] = 0;
duid[2] = 0;
j = 0;
if (state->rf_mod == 1)
{
state->maxref = (int)(state->max * 0.80F);
state->minref = (int)(state->min * 0.80F);
}
else
{
state->maxref = state->max;
state->minref = state->min;
}
if ((state->synctype == 8) || (state->synctype == 9)) //NXDN Voice
{
//state->rf_mod = 2; //wrong type of modulation HERE HERE
state->nac = 0;
state->lastsrc = 0;
state->lasttg = 0;
if (opts->errorbars == 1)
{
if (opts->verbose > 0)
{
level = (int) state->max / 164;
//fprintf (stderr, "inlvl: %2i%% ", level);
}
}
state->nac = 0;
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
openMbeOutFile (opts, state);
}
sprintf (state->fsubtype, " VOICE ");
processNXDNVoice (opts, state);
//ProcessNXDNFrame (opts, state, 0);
return;
}
else if ((state->synctype == 16) || (state->synctype == 17)) //NXDN Data
{
//state->rf_mod = 2;
state->nac = 0;
state->lastsrc = 0;
state->lasttg = 0;
if (opts->errorbars == 1)
{
if (opts->verbose > 0)
{
level = (int) state->max / 164;
//fprintf (stderr, "inlvl: %2i%% ", level);
}
}
state->nac = 0;
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
openMbeOutFile (opts, state);
}
sprintf (state->fsubtype, " DATA ");
processNXDNData (opts, state);
//ProcessNXDNFrame (opts, state, 0);
return;
}
else if ((state->synctype == 6) || (state->synctype == 7))
{
state->nac = 0;
state->lastsrc = 0;
state->lasttg = 0;
if (opts->errorbars == 1)
{
if (opts->verbose > 0)
{
level = (int) state->max / 164;
//fprintf (stderr,"inlvl: %2i%% ", level);
}
}
state->nac = 0;
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
openMbeOutFile (opts, state);
}
sprintf (state->fsubtype, " VOICE ");
processDSTAR (opts, state);
return;
}
else if ((state->synctype == 18) || (state->synctype == 19))
{
state->nac = 0;
state->lastsrc = 0;
state->lasttg = 0;
if (opts->errorbars == 1)
{
if (opts->verbose > 0)
{
level = (int) state->max / 164;
//fprintf (stderr,"inlvl: %2i%% ", level);
}
}
state->nac = 0;
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
openMbeOutFile (opts, state);
}
sprintf (state->fsubtype, " DATA ");
processDSTAR_HD (opts, state);
return;
}
//Start DMR Types
else if ((state->synctype >= 10) && (state->synctype <= 13) || (state->synctype == 32) || (state->synctype == 33) || (state->synctype == 34) ) //32-34 DMR MS and RC
{
//disable so radio id doesn't blink in and out during ncurses and aggressive_framesync
state->nac = 0;
//state->lastsrc = 0;
//state->lasttg = 0;
//state->lastsrcR = 0;
//state->lasttgR = 0;
if (opts->errorbars == 1)
{
if (opts->verbose > 0)
{
level = (int) state->max / 164;
//fprintf (stderr,"inlvl: %2i%% ", level);
}
}
if ( (state->synctype == 11) || (state->synctype == 12) || (state->synctype == 32) ) //DMR Voice Modes
{
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL) && opts->dmr_stereo == 0)
{
openMbeOutFile (opts, state);
}
sprintf (state->fsubtype, " VOICE ");
if (opts->dmr_stereo == 0 && state->synctype < 32) // -T option for DMR (TDMA) stereo
{
sprintf (state->slot1light, " slot1 ");
sprintf (state->slot2light, " slot2 ");
processDMRvoice (opts, state);
}
if (opts->dmr_stereo == 0 && state->synctype == 32)
{
fprintf (stderr, "%s", KRED);
fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
fprintf (stderr, "%s", KNRM);
}
if (opts->dmr_stereo == 1) //opts->dmr_stereo == 1
{
state->dmr_stereo = 1; //set the state to 1 when handling pure voice frames
if (state->synctype > 31 )
{
dmrMSBootstrap (opts, state); //bootstrap into MS Bootstrap (voice only)
}
else dmrBSBootstrap (opts, state); //bootstrap into BS Bootstrap
}
}
else if ( (state->synctype == 33) || (state->synctype == 34) ) //MS Data and RC data
{
if (opts->dmr_stereo == 0)
{
closeMbeOutFile (opts, state);
state->err_str[0] = 0;
fprintf (stderr, "%s", KRED);
fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
fprintf (stderr, "%s", KNRM);
}
if (opts->dmr_stereo == 1)
{
dmrMSData (opts, state);
}
}
else
{
if (opts->dmr_stereo == 0)
{
closeMbeOutFile (opts, state);
state->err_str[0] = 0;
sprintf (state->slot1light, " slot1 ");
sprintf (state->slot2light, " slot2 ");
state->payload_miP = 0; //zero out when switching back to data sync
processDMRdata (opts, state);
}
//switch dmr_stereo to 0 when handling BS data frame syncs with processDMRdata
if (opts->dmr_stereo == 1)
{
state->dmr_stereo = 0; //set the state to zero for handling pure data frames
sprintf (state->slot1light, " slot1 ");
sprintf (state->slot2light, " slot2 ");
state->payload_miP = 0; //zero out when switching back to data sync
processDMRdata (opts, state);
}
}
return;
}
else if ((state->synctype >= 2) && (state->synctype <= 5))
{
state->nac = 0;
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
}
if ((state->synctype == 3) || (state->synctype == 4))
{
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
openMbeOutFile (opts, state);
}
sprintf (state->fsubtype, " VOICE ");
processX2TDMAvoice (opts, state);
}
else
{
closeMbeOutFile (opts, state);
state->err_str[0] = 0;
processX2TDMAdata (opts, state);
}
return;
}
else if ((state->synctype == 14) || (state->synctype == 15))
{
state->nac = 0;
state->lastsrc = 0;
state->lasttg = 0;
if (opts->errorbars == 1)
{
if (opts->verbose > 0)
{
level = (int) state->max / 164;
//fprintf (stderr,"inlvl: %2i%% ", level);
}
}
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
openMbeOutFile (opts, state);
}
sprintf (state->fsubtype, " VOICE ");
processProVoice (opts, state);
return;
}
//ysf
else if ((state->synctype == 30) || (state->synctype == 31))
{
//Do stuff
//fprintf(stderr, "YSF Sync! \n");
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
//openMbeOutFile (opts, state);
}
//sprintf(state->fsubtype, " VOICE ");
processYSF(opts, state);
return;
}
//P25 P2
else if ((state->synctype == 35) || (state->synctype == 36))
{
//Do stuff
//fprintf(stderr, "YSF Sync! \n");
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
//openMbeOutFile (opts, state);
}
//sprintf(state->fsubtype, " VOICE ");
processP2(opts, state);
return;
}
//dPMR
else if ((state->synctype == 20) || (state->synctype == 24))
{
/* dPMR Frame Sync 1 */
fprintf(stderr, "dPMR Frame Sync 1 ");
}
else if ((state->synctype == 21) || (state->synctype == 25))
{
/* dPMR Frame Sync 2 */
fprintf(stderr, "dPMR Frame Sync 2 ");
state->nac = 0;
state->lastsrc = 0;
state->lasttg = 0;
if (opts->errorbars == 1)
{
if (opts->verbose > 0)
{
level = (int) state->max / 164;
}
}
state->nac = 0;
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
{
openMbeOutFile (opts, state);
}
sprintf(state->fsubtype, " VOICE ");
processdPMRvoice (opts, state);
return;
}
else if ((state->synctype == 22) || (state->synctype == 26))
{
/* dPMR Frame Sync 3 */
fprintf(stderr, "dPMR Frame Sync 3 ");
}
else if ((state->synctype == 23) || (state->synctype == 27))
{
/* dPMR Frame Sync 4 */
fprintf(stderr, "dPMR Frame Sync 4 ");
}
//dPMR
else //P25
{
// Read the NAC, 12 bits
j = 0;
index_bch_code = 0;
for (i = 0; i < 6; i++)
{
dibit = getDibit (opts, state);
v = 1 & (dibit >> 1); // bit 1
nac[j] = v + '0';
j++;
bch_code[index_bch_code] = v;
index_bch_code++;
v = 1 & dibit; // bit 0
nac[j] = v + '0';
j++;
bch_code[index_bch_code] = v;
index_bch_code++;
}
//this one setting bogus nac data
// state->nac = strtol (nac, NULL, 2);
// Read the DUID, 4 bits
for (i = 0; i < 2; i++)
{
dibit = getDibit (opts, state);
duid[i] = dibit + '0';
bch_code[index_bch_code] = 1 & (dibit >> 1); // bit 1
index_bch_code++;
bch_code[index_bch_code] = 1 & dibit; // bit 0
index_bch_code++;
}
// Read the BCH data for error correction of NAC and DUID
for (i = 0; i < 3; i++)
{
dibit = getDibit (opts, state);
bch_code[index_bch_code] = 1 & (dibit >> 1); // bit 1
index_bch_code++;
bch_code[index_bch_code] = 1 & dibit; // bit 0
index_bch_code++;
}
// Intermission: read the status dibit
status_0 = getDibit (opts, state) + '0';
// ... continue reading the BCH error correction data
for (i = 0; i < 20; i++)
{
dibit = getDibit (opts, state);
bch_code[index_bch_code] = 1 & (dibit >> 1); // bit 1
index_bch_code++;
bch_code[index_bch_code] = 1 & dibit; // bit 0
index_bch_code++;
}
// Read the parity bit
dibit = getDibit (opts, state);
bch_code[index_bch_code] = 1 & (dibit >> 1); // bit 1
parity = (1 & dibit); // bit 0
// Check if the NID is correct
check_result = check_NID (bch_code, &new_nac, new_duid, parity);
if (check_result) {
if (new_nac != state->nac) {
// NAC fixed by error correction
state->nac = new_nac;
if (state->p2_hardset == 0 && new_nac != 0)
{
state->p2_cc = new_nac;
}
state->debug_header_errors++;
}
if (strcmp(new_duid, duid) != 0) {
// DUID fixed by error correction
//fprintf (stderr,"Fixing DUID %s -> %s\n", duid, new_duid);
duid[0] = new_duid[0];
duid[1] = new_duid[1];
state->debug_header_errors++;
}
} else {
// Check of NID failed and unable to recover its value
//fprintf (stderr,"NID error\n");
duid[0] = 'E';
duid[1] = 'E';
state->debug_header_critical_errors++;
}
}
if (strcmp (duid, "00") == 0)
{
// Header Data Unit
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr," HDU\n");
}
if (opts->mbe_out_dir[0] != 0)
{
closeMbeOutFile (opts, state);
openMbeOutFile (opts, state);
}
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
state->lastp25type = 2;
state->dmrburstL = 25;
state->currentslot = 0;
sprintf (state->fsubtype, " HDU ");
processHDU (opts, state);
}
else if (strcmp (duid, "11") == 0)
{
// Logical Link Data Unit 1
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr," LDU1 ");
}
if (opts->mbe_out_dir[0] != 0)
{
if (opts->mbe_out_f == NULL)
{
openMbeOutFile (opts, state);
}
}
state->lastp25type = 1;
state->dmrburstL = 26;
state->currentslot = 0;
sprintf (state->fsubtype, " LDU1 ");
state->numtdulc = 0;
if (state->payload_algid == 0x81)
{
fprintf (stderr, "\n");
}
processLDU1 (opts, state);
}
else if (strcmp (duid, "22") == 0)
{
// Logical Link Data Unit 2
state->dmrburstL = 27;
state->currentslot = 0;
if (state->lastp25type != 1)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr,"\n Ignoring LDU2 not preceeded by LDU1\n");
}
state->lastp25type = 0;
sprintf (state->fsubtype, " ");
}
else
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr," LDU2 ");
}
if (opts->mbe_out_dir[0] != 0)
{
if (opts->mbe_out_f == NULL)
{
openMbeOutFile (opts, state);
}
}
state->lastp25type = 2;
sprintf (state->fsubtype, " LDU2 ");
state->numtdulc = 0;
processLDU2 (opts, state);
}
}
else if (strcmp (duid, "33") == 0)
{
// Terminator with subsequent Link Control
state->dmrburstL = 28;
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr," TDULC\n");
}
if (opts->mbe_out_dir[0] != 0)
{
closeMbeOutFile (opts, state);
}
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
state->lasttg = 0;
state->lastsrc = 0;
state->lastp25type = 0;
state->err_str[0] = 0;
sprintf (state->fsubtype, " TDULC ");
state->numtdulc++;
if ((opts->resume > 0) && (state->numtdulc > opts->resume))
{
resumeScan (opts, state);
}
processTDULC (opts, state);
state->err_str[0] = 0;
}
else if (strcmp (duid, "03") == 0)
{
// Terminator without subsequent Link Control
state->dmrburstL = 28;
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr," TDU\n");
}
if (opts->mbe_out_dir[0] != 0)
{
closeMbeOutFile (opts, state);
}
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
state->lasttg = 0;
state->lastsrc = 0;
state->lastp25type = 0;
state->err_str[0] = 0;
sprintf (state->fsubtype, " TDU ");
processTDU (opts, state);
}
else if (strcmp (duid, "13") == 0)
{
state->dmrburstL = 29;
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr," TSBK");
}
if (opts->resume > 0)
{
resumeScan (opts, state);
}
state->lasttg = 0;
state->lastsrc = 0;
state->lastp25type = 3;
sprintf (state->fsubtype, " TSBK ");
// Now processing NID
processTSBK(opts, state);
}
else if (strcmp (duid, "30") == 0)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr," PDU\n");
}
if (opts->resume > 0)
{
resumeScan (opts, state);
}
if (opts->mbe_out_dir[0] != 0)
{
if (opts->mbe_out_f == NULL)
{
openMbeOutFile (opts, state);
}
}
state->lastp25type = 4;
sprintf (state->fsubtype, " PDU ");
}
// try to guess based on previous frame if unknown type
else if (state->lastp25type == 1)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr,"(LDU2) ");
}
if (opts->mbe_out_dir[0] != 0)
{
if (opts->mbe_out_f == NULL)
{
openMbeOutFile (opts, state);
}
}
//state->lastp25type = 0;
// Guess that the state is LDU2
state->lastp25type = 2;
sprintf (state->fsubtype, "(LDU2) ");
state->numtdulc = 0;
processLDU2 (opts, state);
}
else if (state->lastp25type == 2)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr,"(LDU1) ");
}
if (opts->mbe_out_dir[0] != 0)
{
if (opts->mbe_out_f == NULL)
{
openMbeOutFile (opts, state);
}
}
//state->lastp25type = 0;
// Guess that the state is LDU1
state->lastp25type = 1;
sprintf (state->fsubtype, "(LDU1) ");
state->numtdulc = 0;
processLDU1 (opts, state);
}
else if (state->lastp25type == 3)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr," (TSBK)\n");
}
//state->lastp25type = 0;
// Guess that the state is TSBK
state->lastp25type = 3;
sprintf (state->fsubtype, "(TSBK) ");
// Now processing NID
skipDibit (opts, state, 328-25);
}
else if (state->lastp25type == 4)
{
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr," (PDU)\n");
}
state->lastp25type = 0;
}
else
{
state->lastp25type = 0;
sprintf (state->fsubtype, " ");
if (opts->errorbars == 1)
{
printFrameInfo (opts, state);
fprintf (stderr," duid:%s *Unknown DUID*\n", duid); //prints on dPMR frame 3
// fprintf (stderr, "\n"); //prints on dPMR frame 3
}
}
}

1670
src/dsd_frame_sync.c Normal file

File diff suppressed because it is too large Load Diff

1771
src/dsd_main.c Normal file

File diff suppressed because it is too large Load Diff

575
src/dsd_mbe.c Normal file
View File

@@ -0,0 +1,575 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
void
playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
{
int i;
char imbe_d[88];
char ambe_d[49];
for (i = state->optind; i < argc; i++)
{
sprintf (opts->mbe_in_file, "%s", argv[i]);
openMbeInFile (opts, state);
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
fprintf (stderr, "playing %s\n", opts->mbe_in_file);
while (feof (opts->mbe_in_f) == 0)
{
if (state->mbe_file_type == 0)
{
readImbe4400Data (opts, state, imbe_d);
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->audio_out == 1)
{
processAudio(opts, state);
}
if (opts->wav_out_f != NULL)
{
writeSynthesizedVoice (opts, state);
}
if (opts->audio_out == 1)
{
playSynthesizedVoice (opts, state);
}
}
else if (state->mbe_file_type == 1)
{
readAmbe2450Data (opts, state, ambe_d);
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->audio_out == 1)
{
processAudio(opts, state);
}
if (opts->wav_out_f != NULL)
{
writeSynthesizedVoice (opts, state);
}
if (opts->audio_out == 1)
{
playSynthesizedVoice (opts, state);
}
}
if (exitflag == 1)
{
cleanupAndExit (opts, state);
}
}
}
}
void
processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24])
{
//
int Pr[256] = {
0x0000, 0x1F00, 0xE300, 0xFC00, 0x2503, 0x3A03, 0xC603, 0xD903,
0x4A05, 0x5505, 0xA905, 0xB605, 0x6F06, 0x7006, 0x8C06, 0x9306,
0x2618, 0x3918, 0xC518, 0xDA18, 0x031B, 0x1C1B, 0xE01B, 0xFF1B,
0x6C1D, 0x731D, 0x8F1D, 0x901D, 0x491E, 0x561E, 0xAA1E, 0xB51E, //31
0x4B28, 0x5428, 0xA828, 0xB728, 0x6E2B, 0x712B, 0x8D2B, 0x922B,
0x012D, 0x1E2D, 0xE22D, 0xFD2D, 0x242E, 0x3B2E, 0xC72E, 0xD82E,
0x6D30, 0x7230, 0x8E30, 0x9130, 0x4833, 0x5733, 0xAB33, 0xB433,
0x2735, 0x3835, 0xC435, 0xDB35, 0x0236, 0x1D36, 0xE136, 0xFE36, //63
0x2B49, 0x3449, 0xC849, 0xD749, 0x0E4A, 0x114A, 0xED4A, 0xF24A,
0x614C, 0xAE4C, 0x824C, 0x9D4C, 0x444F, 0x5B4F, 0xA74F, 0xB84F,
0x0D51, 0x1251, 0xEE51, 0xF151, 0x2852, 0x3752, 0xCB52, 0xD452,
0x4754, 0x5854, 0xA454, 0xBB54, 0x6257, 0x7D57, 0x8157, 0x9E57, //95
0x6061, 0x7F61, 0x8361, 0x9C61, 0x4562, 0x5A62, 0xA662, 0xB962,
0x2A64, 0x3564, 0xC964, 0xD664, 0x0F67, 0x1067, 0xEC67, 0xF367,
0x4679, 0x5979, 0xA579, 0xBA79, 0x637A, 0x7C7A, 0x807A, 0x9F7A,
0x0C7C, 0x137C, 0xEF7C, 0xF07C, 0x297F, 0x367F, 0xCA7F, 0xD57F, //127
0x4D89, 0x5289, 0xAE89, 0xB189, 0x688A, 0x778A, 0x8B8A, 0x948A,
0x078C, 0x188C, 0xE48C, 0xFB8C, 0x228F, 0x3D8F, 0xC18F, 0xDE8F,
0x6B91, 0x7491, 0x8891, 0x9791, 0x4E92, 0x5192, 0xAD92, 0xB292,
0x2194, 0x3E94, 0xC294, 0xDD94, 0x0497, 0x1B97, 0xE797, 0xF897, //159
0x06A1, 0x19A1, 0xE5A1, 0xFAA1, 0x23A2, 0x3CA2, 0xC0A2, 0xDFA2,
0x4CA4, 0x53A4, 0xAFA4, 0xB0A4, 0x69A7, 0x76A7, 0x8AA7, 0x95A7,
0x20B9, 0x3FB9, 0xC3B9, 0xDCB9, 0x05BA, 0x1ABA, 0xE6BA, 0xF9BA,
0x6ABC, 0x75BC, 0x89BC, 0x96BC, 0x4FBF, 0x50BF, 0xACBF, 0xB3BF, //191
0x66C0, 0x79C0, 0x85C0, 0x9AC0, 0x43C3, 0x5CC3, 0xA0C3, 0xBFC3,
0x2CC5, 0x33C5, 0xCFC5, 0xD0C5, 0x09C6, 0x16C6, 0xEAC6, 0xF5C6,
0x84D0, 0x85DF, 0x8AD3, 0x8BDC, 0xB6D5, 0xB7DA, 0xB8D6, 0xB9D9,
0xD0DA, 0xD1D5, 0xDED9, 0xDFD6, 0xE2DF, 0xE3D0, 0xECDC, 0xEDD3, //223
0x2DE8, 0x32E8, 0xCEE8, 0xD1E8, 0x08EB, 0x17EB, 0xEBEB, 0xF4EB,
0x67ED, 0x78ED, 0x84ED, 0x9BED, 0x42EE, 0x5DEE, 0xA1EE, 0xBEEE,
0x0BF0, 0x14F0, 0xE8F0, 0xF7F0, 0x2EF3, 0x31F3, 0xCDF3, 0xD2F3,
0x41F5, 0x5EF5, 0xA2F5, 0xBDF5, 0x64F6, 0x7BF6, 0x87F6, 0x98F6 //255
};
//
int i;
char imbe_d[88];
char ambe_d[49];
char ambe_d_str[50];
unsigned long long int k;
int x;
for (i = 0; i < 88; i++)
{
imbe_d[i] = 0;
}
for (i = 0; i < 49; i++)
{
ambe_d[i] = 0;
}
if ((state->synctype == 0) || (state->synctype == 1))
{
// 0 +P25p1
// 1 -P25p1
state->errs = mbe_eccImbe7200x4400C0 (imbe_fr);
state->errs2 = state->errs;
mbe_demodulateImbe7200x4400Data (imbe_fr);
state->errs2 += mbe_eccImbe7200x4400Data (imbe_fr, imbe_d);
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
//mbe_processImbe7200x4400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe_fr, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->payload == 1)
{
PrintIMBEData (opts, state, imbe_d);
}
//increment vc counter by one.
state->p25vc++;
if (opts->mbe_out_f != NULL && state->dmr_encL == 0) //only save if this bit not set
{
saveImbe4400Data (opts, state, imbe_d);
}
}
else if ((state->synctype == 14) || (state->synctype == 15)) //pV Sync
{
state->errs = mbe_eccImbe7100x4400C0 (imbe7100_fr);
state->errs2 = state->errs;
mbe_demodulateImbe7100x4400Data (imbe7100_fr);
state->errs2 += mbe_eccImbe7100x4400Data (imbe7100_fr, imbe_d);
mbe_convertImbe7100to7200(imbe_d); //needs extra conversion step apparently
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
//below line the old way of doing this
// mbe_processImbe7100x4400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe7100_fr, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->payload == 1)
{
PrintIMBEData (opts, state, imbe_d);
}
if (opts->mbe_out_f != NULL)
{
saveImbe4400Data (opts, state, imbe_d);
}
}
else if ((state->synctype == 6) || (state->synctype == 7))
{
mbe_processAmbe3600x2400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->payload == 1)
{
PrintAMBEData (opts, state, ambe_d);
}
if (opts->mbe_out_f != NULL)
{
saveAmbe2450Data (opts, state, ambe_d);
}
}
else if ((state->synctype == 8) || (state->synctype == 9))
{
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
state->errs2 = state->errs;
mbe_demodulateAmbe3600x2450Data (ambe_fr);
state->errs2 += mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
if ( (state->nxdn_cipher_type == 0x01 && state->R > 0) ||
(state->M == 1 && state->R > 0) )
{
if (state->payload_miN == 0)
{
state->payload_miN = state->R;
}
char ambe_temp[49];
for (short int i = 0; i < 49; i++)
{
ambe_temp[i] = ambe_d[i];
ambe_d[i] = 0;
}
LFSRN(ambe_temp, ambe_d, state);
}
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->payload == 1)
{
PrintAMBEData (opts, state, ambe_d);
}
if (opts->mbe_out_f != NULL && (state->dmr_encL == 0 || opts->dmr_mute_encL == 0) )
{
saveAmbe2450Data (opts, state, ambe_d);
}
}
else
{
//stereo slots and slot 0 (left slot)
if (state->currentslot == 0 && opts->dmr_stereo == 1)
{
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
state->errs2 = state->errs;
mbe_demodulateAmbe3600x2450Data (ambe_fr);
state->errs2 += mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
(state->K > 0 && state->M == 1) )
{
k = Pr[state->K];
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
for (short int j = 0; j < 48; j++) //49
{
x = ( ((k << j) & 0x800000000000) >> 47 );
ambe_d[j] ^= x;
}
}
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
(state->K1 > 0 && state->M == 1) )
{
int pos = 0;
unsigned long long int k1 = state->K1;
unsigned long long int k2 = state->K2;
unsigned long long int k3 = state->K3;
unsigned long long int k4 = state->K4;
int T_Key[256] = {0};
int pN[882] = {0};
int len = 0;
if (k2 == 0)
{
len = 39;
k1 = k1 << 24;
}
if (k2 != 0)
{
len = 127;
}
if (k4 != 0)
{
len = 255;
}
for (i = 0; i < 64; i++)
{
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
}
for (i = 0; i < 882; i++)
{
pN[i] = T_Key[pos];
pos++;
if (pos > len)
{
pos = 0;
}
}
//sanity check
if (state->DMRvcL > 17) //18
{
state->DMRvcL = 17; //18
}
pos = state->DMRvcL * 49;
for(i = 0; i < 49; i++)
{
ambe_d[i] ^= pN[pos];
pos++;
}
state->DMRvcL++;
}
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
//old method for this step below
//mbe_processAmbe3600x2450Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->payload == 1) // && state->R == 0 this is why slot 1 didn't primt abme, probably had it set during testing
{
PrintAMBEData (opts, state, ambe_d);
}
}
//stereo slots and slot 1 (right slot)
if (state->currentslot == 1 && opts->dmr_stereo == 1)
{
state->errsR = mbe_eccAmbe3600x2450C0 (ambe_fr);
state->errs2R = state->errsR;
mbe_demodulateAmbe3600x2450Data (ambe_fr);
state->errs2R += mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
if ( (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
(state->K > 0 && state->M == 1) )
{
k = Pr[state->K];
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
for (short int j = 0; j < 48; j++)
{
x = ( ((k << j) & 0x800000000000) >> 47 );
ambe_d[j] ^= x;
}
}
if ( (state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
(state->K1 > 0 && state->M == 1))
{
int pos = 0;
unsigned long long int k1 = state->K1;
unsigned long long int k2 = state->K2;
unsigned long long int k3 = state->K3;
unsigned long long int k4 = state->K4;
int T_Key[256] = {0};
int pN[882] = {0};
int len = 0;
if (k2 == 0)
{
len = 39;
k1 = k1 << 24;
}
if (k2 != 0)
{
len = 127;
}
if (k4 != 0)
{
len = 255;
}
for (i = 0; i < 64; i++)
{
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
}
for (i = 0; i < 882; i++)
{
pN[i] = T_Key[pos];
pos++;
if (pos > len)
{
pos = 0;
}
}
//sanity check
if (state->DMRvcR > 17) //18
{
state->DMRvcR = 17; //18
}
pos = state->DMRvcR * 49;
for(i = 0; i < 49; i++)
{
ambe_d[i] ^= pN[pos];
pos++;
}
state->DMRvcR++;
}
mbe_processAmbe2450Dataf (state->audio_out_temp_bufR, &state->errsR, &state->errs2R, state->err_strR,
ambe_d, state->cur_mp2, state->prev_mp2, state->prev_mp_enhanced2, opts->uvquality);
//old method for this step below
//mbe_processAmbe3600x2450Framef (state->audio_out_temp_bufR, &state->errsR, &state->errs2R, state->err_strR, ambe_fr, ambe_d, state->cur_mp2, state->prev_mp2, state->prev_mp_enhanced2, opts->uvquality);
if (opts->payload == 1)
{
PrintAMBEData (opts, state, ambe_d);
}
}
//if using anything but DMR Stereo, borrowing state->dmr_encL to signal enc or clear for other types
if (opts->dmr_stereo == 0)
{
mbe_processAmbe3600x2450Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
if (opts->payload == 1)
{
PrintAMBEData (opts, state, ambe_d);
}
//only save MBE files if not enc or unmuted, THIS does not seem to work for some reason
if (opts->mbe_out_f != NULL && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
{
saveAmbe2450Data (opts, state, ambe_d);
}
}
}
//quick enc check to determine whether or not to play enc traffic
int enc_bit = 0;
//end enc check
if (opts->dmr_stereo == 1 && state->currentslot == 0)
{
enc_bit = (state->dmr_so >> 6) & 0x1;
if (enc_bit == 1)
{
state->dmr_encL = 1;
}
//checkdown for P25 1 and 2
else if (state->payload_algid != 0 && state->payload_algid != 0x80)
{
state->dmr_encL = 1;
}
else state->dmr_encL = 0;
//second checkdown for P25-2 WACN, SYSID, and CC set
if (state->synctype == 35 || state->synctype == 36)
{
if (state->p2_wacn == 0 || state->p2_sysid == 0 || state->p2_cc == 0)
{
state->dmr_encL = 1;
}
}
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
{
state->debug_audio_errors += state->errs2;
if (opts->audio_out == 1)
{
processAudio(opts, state);
}
if (opts->audio_out == 1)
{
playSynthesizedVoice (opts, state);
}
}
}
if (opts->dmr_stereo == 1 && state->currentslot == 1)
{
enc_bit = (state->dmr_soR >> 6) & 0x1;
if (enc_bit == 0x1)
{
state->dmr_encR = 1;
}
//checkdown for P25 1 and 2
else if (state->payload_algidR != 0 && state->payload_algidR != 0x80)
{
state->dmr_encR = 1;
}
else state->dmr_encR = 0;
//second checkdown for P25-2 WACN, SYSID, and CC set
if (state->synctype == 35 || state->synctype == 36)
{
if (state->p2_wacn == 0 || state->p2_sysid == 0 || state->p2_cc == 0)
{
state->dmr_encR = 1;
}
}
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
{
state->debug_audio_errorsR += state->errs2R;
if (opts->audio_out == 1)
{
processAudioR(opts, state);
}
if (opts->audio_out == 1)
{
playSynthesizedVoiceR (opts, state);
}
}
}
//if using anything but DMR Stereo, borrowing state->dmr_encL to signal enc or clear for other types
if (opts->dmr_stereo == 0 && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
{
state->debug_audio_errors += state->errs2;
if (opts->audio_out == 1)
{
processAudio(opts, state);
}
if (opts->audio_out == 1)
{
playSynthesizedVoice (opts, state);
}
}
//if using anything but DMR Stereo, borrowing state->dmr_encL to signal enc or clear for other types
if (opts->wav_out_f != NULL && opts->dmr_stereo == 0 && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0))
{
writeSynthesizedVoice (opts, state);
}
//per call can only be used when ncurses terminal is active since we use its call history matrix for when to record
if (opts->dmr_stereo_wav == 1 && opts->dmr_stereo == 1 && state->currentslot == 0) //opts->use_ncurses_terminal == 1 &&
{
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
{
//write wav to per call on left channel Slot 1
writeSynthesizedVoice (opts, state);
}
}
//per call can only be used when ncurses terminal is active since we use its call history matrix for when to record
if (opts->dmr_stereo_wav == 1 && opts->dmr_stereo == 1 && state->currentslot == 1) //opts->use_ncurses_terminal == 1 &&
{
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
{
//write wav to per call on right channel Slot 2
writeSynthesizedVoiceR (opts, state);
}
}
}

2577
src/dsd_ncurses.c Normal file

File diff suppressed because it is too large Load Diff

106
src/dsd_reset.c Normal file
View File

@@ -0,0 +1,106 @@
#include "dsd.h"
#include "p25p1_heuristics.h"
void
resetState (dsd_state * state)
{
int i, j;
// state->dibit_buf = malloc (sizeof (int) * 1000000);
// state->dibit_buf_p = state->dibit_buf + 200;
// memset (state->dibit_buf, 0, sizeof (int) * 200);
state->repeat = 0; //not sure yet
//state->audio_out_buf = malloc (sizeof (short) * 1000000);
//memset (state->audio_out_buf, 0, 100 * sizeof (short));
//state->audio_out_buf_p = state->audio_out_buf + 100;
//state->audio_out_float_buf = malloc (sizeof (float) * 1000000);
//memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
//state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
//state->audio_out_idx = 0;
//state->audio_out_idx2 = 0;
//state->audio_out_temp_buf_p = state->audio_out_temp_buf;
//state->wav_out_bytes = 0;
state->center = 0;
state->jitter = -1;
state->synctype = -1;
state->min = -15000;
state->max = 15000;
state->lmid = 0;
state->umid = 0;
state->minref = -12000;
state->maxref = 12000;
state->lastsample = 0;
for (i = 0; i < 128; i++)
{
state->sbuf[i] = 0;
}
state->sidx = 0;
for (i = 0; i < 1024; i++)
{
state->maxbuf[i] = 15000;
}
for (i = 0; i < 1024; i++)
{
state->minbuf[i] = -15000;
}
state->midx = 0;
state->err_str[0] = 0;
sprintf (state->fsubtype, " ");
sprintf (state->ftype, " ");
state->symbolcnt = 0;
state->rf_mod = 0;
state->numflips = 0;
state->lastsynctype = -1;
state->lastp25type = 0;
state->offset = 0;
state->carrier = 0;
for (i = 0; i < 25; i++)
{
for (j = 0; j < 16; j++)
{
state->tg[i][j] = 48;
}
}
state->tgcount = 0;
state->lasttg = 0;
state->lastsrc = 0;
state->nac = 0;
state->errs = 0;
state->errs2 = 0;
state->mbe_file_type = -1;
state->optind = 0;
state->numtdulc = 0;
state->firstframe = 0;
sprintf (state->slot0light, " slot0 ");
sprintf (state->slot1light, " slot1 ");
state->aout_gain = 25;
memset (state->aout_max_buf, 0, sizeof (float) * 200);
state->aout_max_buf_p = state->aout_max_buf;
state->aout_max_buf_idx = 0;
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
sprintf (state->algid, "________");
sprintf (state->keyid, "________________");
state->currentslot = 0;
state->cur_mp = malloc (sizeof (mbe_parms));
state->prev_mp = malloc (sizeof (mbe_parms));
state->prev_mp_enhanced = malloc (sizeof (mbe_parms));
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
state->p25kid = 0;
state->debug_audio_errors = 0;
state->debug_header_errors = 0;
state->debug_header_critical_errors = 0;
state->nxdn_last_ran = 0;
// state->payload_algid = 0;
// state->payload_algidR = 0;
state->dmr_encL = 0;
state->dmr_encR = 0;
//each time you run this, it increses memory use by 4MB, massive memory leak
//need to revisit this sometime and look into only resetting only the necesary items to let P25 switch between signals (C4FM or Wide) without needing a restart
initialize_p25_heuristics(&state->p25_heuristics); //see if we want to re-init this or not, currently seems to cause memory leak when running over and over, mitigated with a reset flag
}

View File

@@ -8,16 +8,17 @@ openSerial (dsd_opts * opts, dsd_state * state)
struct termios tty;
speed_t baud;
printf ("Opening serial port %s and setting baud to %i\n", opts->serial_dev, opts->serial_baud);
fprintf (stderr, "Opening serial port %s and setting baud to %i\n", opts->serial_dev, opts->serial_baud);
opts->serial_fd = open (opts->serial_dev, O_WRONLY);
if (opts->serial_fd == -1)
{
printf ("Error, couldn't open %s\n", opts->serial_dev);
fprintf (stderr, "Error, couldn't open %s\n", opts->serial_dev);
exit (1);
}
tty.c_cflag = 0;
baud = B115200;
switch (opts->serial_baud)
{
case 1200:
@@ -70,19 +71,20 @@ void
resumeScan (dsd_opts * opts, dsd_state * state)
{
char cmd[7];
char cmd[16];
ssize_t result;
if (opts->serial_fd > 0)
{
sprintf (cmd, "\rKEY00\r");
write (opts->serial_fd, cmd, 7);
result = write (opts->serial_fd, cmd, 7);
cmd[0] = 2;
cmd[1] = 75;
cmd[2] = 15;
cmd[3] = 3;
cmd[4] = 93;
cmd[5] = 0;
write (opts->serial_fd, cmd, 5);
result = write (opts->serial_fd, cmd, 5);
state->numtdulc = 0;
}
}

422
src/dsd_symbol.c Normal file
View File

@@ -0,0 +1,422 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
int
getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
{
short sample, sample2;
int i, sum, symbol, count;
ssize_t result;
sum = 0;
count = 0;
sample = 0; //init sample with a value of 0...see if this was causing issues with raw audio monitoring
for (i = 0; i < state->samplesPerSymbol; i++) //right HERE
{
// timing control
if ((i == 0) && (have_sync == 0))
{
if (state->samplesPerSymbol == 20)
{
if ((state->jitter >= 7) && (state->jitter <= 10))
{
i--;
}
else if ((state->jitter >= 11) && (state->jitter <= 14))
{
i++;
}
}
else if (state->rf_mod == 1)
{
if ((state->jitter >= 0) && (state->jitter < state->symbolCenter))
{
i++; // fall back
}
else if ((state->jitter > state->symbolCenter) && (state->jitter < 10))
{
i--; // catch up
}
}
else if (state->rf_mod == 2)
{
if ((state->jitter >= state->symbolCenter - 1) && (state->jitter <= state->symbolCenter))
{
i--;
}
else if ((state->jitter >= state->symbolCenter + 1) && (state->jitter <= state->symbolCenter + 2))
{
i++;
}
}
else if (state->rf_mod == 0)
{
if ((state->jitter > 0) && (state->jitter <= state->symbolCenter))
{
i--; // catch up
}
else if ((state->jitter > state->symbolCenter) && (state->jitter < state->samplesPerSymbol))
{
i++; // fall back
}
}
state->jitter = -1;
}
// Read the new sample from the input
if(opts->audio_in_type == 0) // && state->menuopen == 0 still not quite working
{
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
//look into how processAudio handles playback, not sure if latency issues, or garbage sample values crash pulse when written
// if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample < 32767 && sample > -32767)
if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample != 0)
{
state->pulse_raw_out_buffer = sample; //steal raw out buffer sample here?
pa_simple_write(opts->pulse_raw_dev_out, (void*)&state->pulse_raw_out_buffer, 2, NULL);
}
//playback is wrong, depends on samples_per_symbol, maybe make a buffer and store first?
//making buffer might also fix raw audio monitoring as well
if (opts->wav_out_file_raw[0] != 0) //if set for recording sample raw files
{
//writeRawSample (opts, state, sample);
}
}
else if (opts->audio_in_type == 1) {
result = sf_read_short(opts->audio_in_file, &sample, 1);
//fprintf (stderr, "..");
if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample < 32767 && sample > -32767)
{
state->pulse_raw_out_buffer = sample; //steal raw out buffer sample here?
pa_simple_write(opts->pulse_raw_dev_out, (void*)&state->pulse_raw_out_buffer, 2, NULL);
}
if(result == 0) {
cleanupAndExit (opts, state);
}
}
else if (opts->audio_in_type == 2)
{
#ifdef USE_PORTAUDIO
PaError err = Pa_ReadStream( opts->audio_in_pa_stream, &sample, 1 );
if( err != paNoError )
{
fprintf( stderr, "An error occured while using the portaudio input stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
}
#endif
}
else if (opts->audio_in_type == 3)
{
#ifdef USE_RTLSDR
// TODO: need to read demodulated stream here
// get_rtlsdr_sample(&sample);
get_rtlsdr_sample(&sample, opts, state);
if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample < 32767 && sample > -32767)
{
state->pulse_raw_out_buffer = sample; //steal raw out buffer sample here?
pa_simple_write(opts->pulse_raw_dev_out, (void*)&state->pulse_raw_out_buffer, 2, NULL);
}
#endif
}
#ifdef TRACE_DSD
state->debug_sample_index++;
#endif
// printf ("res: %zd\n, offset: %lld", result, sf_seek(opts->audio_in_file, 0, SEEK_CUR));
if (opts->use_cosine_filter)
{
if ( (state->lastsynctype >= 10 && state->lastsynctype <= 13) || state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34)
{
sample = dmr_filter(sample);
}
else if (state->lastsynctype == 8 || state->lastsynctype == 9 ||
state->lastsynctype == 16 || state->lastsynctype == 17 ||
state->lastsynctype == 20 || state->lastsynctype == 21 ||
state->lastsynctype == 22 || state->lastsynctype == 23 ||
state->lastsynctype == 24 || state->lastsynctype == 25 ||
state->lastsynctype == 26 || state->lastsynctype == 27 ) //||
//state->lastsynctype == 35 || state->lastsynctype == 36) //phase 2
{
if(state->samplesPerSymbol == 20)
{
sample = nxdn_filter(sample);
}
else if (state->samplesPerSymbol == 8) //phase 2 cqpsk
{
//sample = dmr_filter(sample); //work on filter later
}
else // the 12.5KHz NXDN filter is the same as the DMR filter
{
sample = dmr_filter(sample);
}
}
}
if ((sample > state->max) && (have_sync == 1) && (state->rf_mod == 0))
{
sample = state->max;
}
else if ((sample < state->min) && (have_sync == 1) && (state->rf_mod == 0))
{
sample = state->min;
}
if (sample > state->center)
{
if (state->lastsample < state->center)
{
state->numflips += 1;
}
if (sample > (state->maxref * 1.25))
{
if (state->lastsample < (state->maxref * 1.25))
{
state->numflips += 1;
}
if ((state->jitter < 0) && (state->rf_mod == 1))
{ // first spike out of place
state->jitter = i;
}
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
{
fprintf (stderr, "O");
}
}
else
{
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
{
fprintf (stderr, "+");
}
if ((state->jitter < 0) && (state->lastsample < state->center) && (state->rf_mod != 1))
{ // first transition edge
state->jitter = i;
}
}
}
else
{ // sample < 0
if (state->lastsample > state->center)
{
state->numflips += 1;
}
if (sample < (state->minref * 1.25))
{
if (state->lastsample > (state->minref * 1.25))
{
state->numflips += 1;
}
if ((state->jitter < 0) && (state->rf_mod == 1))
{ // first spike out of place
state->jitter = i;
}
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
{
fprintf (stderr, "X");
}
}
else
{
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
{
fprintf (stderr, "-");
}
if ((state->jitter < 0) && (state->lastsample > state->center) && (state->rf_mod != 1))
{ // first transition edge
state->jitter = i;
}
}
}
if (state->samplesPerSymbol == 20) //nxdn 4800 baud 2400 symbol rate
{
if ((i >= 9) && (i <= 11))
{
sum += sample;
count++;
}
}
if (state->samplesPerSymbol == 5) //provoice or gfsk
{
if (i == 2)
{
sum += sample;
count++;
}
}
else
{
if (state->rf_mod == 0)
{
// 0: C4FM modulation
if ((i >= state->symbolCenter - 1) && (i <= state->symbolCenter + 2))
{
sum += sample;
count++;
}
#ifdef TRACE_DSD
if (i == state->symbolCenter - 1) {
state->debug_sample_left_edge = state->debug_sample_index - 1;
}
if (i == state->symbolCenter + 2) {
state->debug_sample_right_edge = state->debug_sample_index - 1;
}
#endif
}
else
{
// 1: QPSK modulation
// 2: GFSK modulation
// Note: this has been changed to use an additional symbol to the left
// On the p25_raw_unencrypted.flac it is evident that the timing
// comes one sample too late.
// This change makes a significant improvement in the BER, at least for
// this file.
//if ((i == state->symbolCenter) || (i == state->symbolCenter + 1))
if ((i == state->symbolCenter - 1) || (i == state->symbolCenter + 1))
{
sum += sample;
count++;
}
#ifdef TRACE_DSD
//if (i == state->symbolCenter) {
if (i == state->symbolCenter - 1) {
state->debug_sample_left_edge = state->debug_sample_index - 1;
}
if (i == state->symbolCenter + 1) {
state->debug_sample_right_edge = state->debug_sample_index - 1;
}
#endif
}
}
state->lastsample = sample;
}
symbol = (sum / count);
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
{
if (state->jitter >= 0)
{
fprintf (stderr, " %i\n", state->jitter);
}
else
{
fprintf (stderr, "\n");
}
}
#ifdef TRACE_DSD
if (state->samplesPerSymbol == 10) {
float left, right;
if (state->debug_label_file == NULL) {
state->debug_label_file = fopen ("pp_label.txt", "w");
}
left = state->debug_sample_left_edge / SAMPLE_RATE_IN;
right = state->debug_sample_right_edge / SAMPLE_RATE_IN;
if (state->debug_prefix != '\0') {
if (state->debug_prefix == 'I') {
fprintf(state->debug_label_file, "%f\t%f\t%c%c %i\n", left, right, state->debug_prefix, state->debug_prefix_2, symbol);
} else {
fprintf(state->debug_label_file, "%f\t%f\t%c %i\n", left, right, state->debug_prefix, symbol);
}
} else {
fprintf(state->debug_label_file, "%f\t%f\t%i\n", left, right, symbol);
}
}
#endif
//read op25/fme symbol bin files
if (opts->audio_in_type == 4)
{
//use fopen and read in a symbol, check op25 for clues
if(opts->symbolfile == NULL)
{
fprintf(stderr, "Error Opening File %s\n", opts->audio_in_dev); //double check this
return(-1);
}
state->symbolc = fgetc(opts->symbolfile);
//experimental throttle
useconds_t stime = state->symbol_throttle;
if (state->use_throttle == 1)
{
usleep(stime);
}
//fprintf(stderr, "%d", state->symbolc);
if( feof(opts->symbolfile) )
{
opts->audio_in_type = 0; //switch to pulse after playback, ncurses terminal can initiate replay if wanted
fclose(opts->symbolfile);
openPulseInput(opts);
}
//assign symbol/dibit values based on modulation type
if (state->rf_mod == 2) //GFSK
{
symbol = state->symbolc;
if (state->symbolc == 0 && state->synctype >= 0)
{
symbol = -3; //-1
}
if (state->symbolc == 1 && state->synctype >= 0)
{
symbol = -1; //-3
}
}
else //everything else
{
if (state->symbolc == 0)
{
symbol = 1; //-1
}
if (state->symbolc == 1)
{
symbol = 3; //-3
}
if (state->symbolc == 2)
{
symbol = -1; //1
}
if (state->symbolc == 3)
{
symbol = -3; //3
}
}
}
state->symbolcnt++;
return (symbol);
}

132
src/dsd_upsample.c Normal file
View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "dsd.h"
void
upsample (dsd_state * state, float invalue)
{
int i, j, sum;
float *outbuf1, c, d;
if (state->currentslot == 0 && state->dmr_stereo == 1)
{
outbuf1 = state->audio_out_float_buf_p;
}
if (state->currentslot == 1 && state->dmr_stereo == 1)
{
outbuf1 = state->audio_out_float_buf_pR;
}
if (state->dmr_stereo == 0)
{
outbuf1 = state->audio_out_float_buf_p;
}
//outbuf1 = state->audio_out_float_buf_p;
outbuf1--;
c = *outbuf1;
d = invalue;
// basic triangle interpolation
outbuf1++;
*outbuf1 = ((invalue * (float) 0.166) + (c * (float) 0.834));
outbuf1++;
*outbuf1 = ((invalue * (float) 0.332) + (c * (float) 0.668));
outbuf1++;
*outbuf1 = ((invalue * (float) 0.5) + (c * (float) 0.5));
outbuf1++;
*outbuf1 = ((invalue * (float) 0.668) + (c * (float) 0.332));
outbuf1++;
*outbuf1 = ((invalue * (float) 0.834) + (c * (float) 0.166));
outbuf1++;
*outbuf1 = d;
outbuf1++;
if (state->currentslot == 1 && state->dmr_stereo == 1)
{
if (state->audio_out_idx2R > 24)
{
// smoothing
outbuf1 -= 16;
for (j = 0; j < 4; j++)
{
for (i = 0; i < 6; i++)
{
sum = 0;
outbuf1 -= 2;
sum += (int)*outbuf1;
outbuf1 += 2;
sum += (int)*outbuf1;
outbuf1 += 2;
sum += (int)*outbuf1;
outbuf1 -= 2;
*outbuf1 = (sum / (float) 3);
outbuf1++;
}
outbuf1 -= 8;
}
}
}
if (state->currentslot == 0 && state->dmr_stereo == 1)
{
if (state->audio_out_idx2 > 24)
{
// smoothing
outbuf1 -= 16;
for (j = 0; j < 4; j++)
{
for (i = 0; i < 6; i++)
{
sum = 0;
outbuf1 -= 2;
sum += (int)*outbuf1;
outbuf1 += 2;
sum += (int)*outbuf1;
outbuf1 += 2;
sum += (int)*outbuf1;
outbuf1 -= 2;
*outbuf1 = (sum / (float) 3);
outbuf1++;
}
outbuf1 -= 8;
}
}
}
if (state->dmr_stereo == 0)
{
if (state->audio_out_idx2 > 24)
{
// smoothing
outbuf1 -= 16;
for (j = 0; j < 4; j++)
{
for (i = 0; i < 6; i++)
{
sum = 0;
outbuf1 -= 2;
sum += (int)*outbuf1;
outbuf1 += 2;
sum += (int)*outbuf1;
outbuf1 += 2;
sum += (int)*outbuf1;
outbuf1 -= 2;
*outbuf1 = (sum / (float) 3);
outbuf1++;
}
outbuf1 -= 8;
}
}
}
}

183
src/dstar.c Normal file
View File

@@ -0,0 +1,183 @@
/*
* Copyright (C) 2010 DSD Author
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Note: D-STAR support is fairly complete at this point.
* The ambe3600x2450 decoder is similar butnot compatible with D-STAR voice frames.
* The dstar interleave pattern is different as well.
* GMSK modulation optimizations will also required to get a usable bit error
*/
#include "dsd.h"
#include "dstar_const.h"
#include "dstar_header.h"
void processDSTAR(dsd_opts * opts, dsd_state * state) {
// extracts AMBE frames from D-STAR voice frame
int i, j, dibit;
char ambe_fr[4][24];
unsigned char data[9];
unsigned int bits[4];
int framecount;
int sync_missed = 0;
unsigned char slowdata[4];
unsigned int bitbuffer = 0;
const int *w, *x;
//set carrier to 1 so ncurses will light up green
state->carrier = 1;
if (opts->errorbars == 1) {
fprintf (stderr, "e:");
}
#ifdef DSTAR_DUMP
fprintf (stderr, "\n");
#endif
if (state->synctype == 18) {
framecount = 0;
state->synctype = 6;
} else if (state->synctype == 19) {
framecount = 0;
state->synctype = 7;
} else {
framecount = 1; //just saw a sync frame; there should be 20 not 21 till the next
}
//why is this running in a loop for?
while (sync_missed < 3) {
memset(ambe_fr, 0, 96);
// voice frame
w = dW;
x = dX;
for (i = 0; i < 72; i++) {
dibit = getDibit(opts, state);
bitbuffer <<= 1;
if (dibit == 1) {
bitbuffer |= 0x01;
}
if ((bitbuffer & 0x00FFFFFF) == 0x00AAB468) {
// we're slipping bits
fprintf (stderr, "sync in voice after i=%d, restarting\n", i);
//ugh just start over
i = 0;
w = dW;
x = dX;
framecount = 1;
continue;
}
ambe_fr[*w][*x] = (1 & dibit);
w++;
x++;
}
processMbeFrame(opts, state, NULL, ambe_fr, NULL);
// data frame - 24 bits
for (i = 73; i < 97; i++) {
dibit = getDibit(opts, state);
bitbuffer <<= 1;
if (dibit == 1) {
bitbuffer |= 0x01;
}
if ((bitbuffer & 0x00FFFFFF) == 0x00AAB468) {
// looking if we're slipping bits
if (i != 96) {
fprintf (stderr, "sync after i=%d\n", i);
i = 96;
}
}
}
slowdata[0] = (bitbuffer >> 16) & 0x000000FF;
slowdata[1] = (bitbuffer >> 8) & 0x000000FF;
slowdata[2] = (bitbuffer) & 0x000000FF;
slowdata[3] = 0;
if ((bitbuffer & 0x00FFFFFF) == 0x00AAB468) {
//We got sync!
//fprintf (stderr, "Sync on framecount = %d\n", framecount);
sync_missed = 0;
} else if ((bitbuffer & 0x00FFFFFF) == 0xAAAAAA) {
//End of transmission
fprintf (stderr, "End of transmission\n");
goto end;
} else if (framecount % 21 == 0) {
fprintf (stderr, "Missed sync on framecount = %d, value = %x/%x/%x\n",
framecount, slowdata[0], slowdata[1], slowdata[2]);
sync_missed++;
} else if (framecount != 0 && (bitbuffer & 0x00FFFFFF) != 0x000000) {
slowdata[0] ^= 0x70;
slowdata[1] ^= 0x4f;
slowdata[2] ^= 0x93;
//fprintf (stderr, "unscrambled- %02X %02X %02X %02X \n", slowdata[0], slowdata[1], slowdata[2], slowdata[3] );
} else if (framecount == 0) {
//fprintf (stderr, "never scrambled-%c\n",slowdata);
//fprintf (stderr, "never scrambled- %02X %02X %02X %02X \n", slowdata[0], slowdata[1], slowdata[2], slowdata[3] );
}
for (short int i = 0; i < 4; i++)
{
if (slowdata[i] <= 0x7E && slowdata[i] >=0x20)
{
//fprintf (stderr, "%c", slowdata[i]);
}
//else fprintf (stderr, ".");
}
//fprintf (stderr, "\n");
framecount++;
//since we are in a while loop, run ncursesPrinter here.
if (opts->use_ncurses_terminal == 1)
{
ncursesPrinter(opts, state);
}
} //end while loop
end: if (opts->errorbars == 1) {
fprintf (stderr, "\n");
}
}
void processDSTAR_HD(dsd_opts * opts, dsd_state * state) {
int i, j;
int radioheaderbuffer[660];
for (j = 0; j < 660; j++) {
radioheaderbuffer[j] = getDibit(opts, state);
}
// Note: These routines contain GPLed code. Remove if you object to that.
// Due to this, they are in a separate source file.
dstar_header_decode(state, radioheaderbuffer);
//We officially have sync now, so just pass on to the above routine:
processDSTAR(opts, state);
}

87
src/dstar_header.c Normal file
View File

@@ -0,0 +1,87 @@
/*
*
* This code is taken largely from on1arf's GMSK code. Original copyright below:
*
*
* Copyright (C) 2011 by Kristoff Bonne, ON1ARF
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "fcs.h"
#include "descramble.h"
#include "dstar_header.h"
#include "dsd.h"
void dstar_header_decode(dsd_state * state, int radioheaderbuffer[660]) {
int radioheaderbuffer2[660];
unsigned char radioheader[41];
int octetcount, bitcount, loop;
unsigned char bit2octet[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
unsigned int FCSinheader;
unsigned int FCScalculated;
int len;
scramble(radioheaderbuffer, radioheaderbuffer2);
deinterleave(radioheaderbuffer2, radioheaderbuffer);
len = FECdecoder(radioheaderbuffer, radioheaderbuffer2);
memset(radioheader, 0, 41);
//memset(state->dstarradioheader, 0, 41);
// note we receive 330 bits, but we only use 328 of them (41 octets)
// bits 329 and 330 are unused
octetcount = 0;
bitcount = 0;
for (loop = 0; loop < 328; loop++) {
if (radioheaderbuffer2[loop]) {
radioheader[octetcount] |= bit2octet[bitcount];
};
bitcount++;
// increase octetcounter and reset bitcounter every 8 bits
if (bitcount >= 8) {
octetcount++;
bitcount = 0;
}
}
//state->dstarradioheader = radioheader;
for (short i = 0; i < 41; i++)
{
state->dstarradioheader[i] = radioheader[i];
}
// print header
fprintf (stderr, "\nDSTAR HEADER: ");
//fprintf (stderr, "FLAG1: %02X - FLAG2: %02X - FLAG3: %02X\n", radioheader[0],
// radioheader[1], radioheader[2]);
fprintf (stderr, "RPT 2: %c%c%c%c%c%c%c%c ", radioheader[3], radioheader[4],
radioheader[5], radioheader[6], radioheader[7], radioheader[8],
radioheader[9], radioheader[10]);
fprintf (stderr, "RPT 1: %c%c%c%c%c%c%c%c ", radioheader[11], radioheader[12],
radioheader[13], radioheader[14], radioheader[15], radioheader[16],
radioheader[17], radioheader[18]);
fprintf (stderr, "YOUR: %c%c%c%c%c%c%c%c ", radioheader[19], radioheader[20],
radioheader[21], radioheader[22], radioheader[23], radioheader[24],
radioheader[25], radioheader[26]);
fprintf (stderr, "MY: %c%c%c%c%c%c%c%c/%c%c%c%c\n", radioheader[27],
radioheader[28], radioheader[29], radioheader[30], radioheader[31],
radioheader[32], radioheader[33], radioheader[34], radioheader[35],
radioheader[36], radioheader[37], radioheader[38]);
//FCSinheader = ((radioheader[39] << 8) | radioheader[40]) & 0xFFFF;
//FCScalculated = calc_fcs((unsigned char*) radioheader, 39);
//fprintf (stderr, "Check sum = %04X ", FCSinheader);
//if (FCSinheader == FCScalculated) {
// fprintf (stderr, "(OK)\n");
//} else {
// fprintf (stderr, "(NOT OK- Calculated FCS = %04X)\n", FCScalculated);
//}; // end else - if
}

323
src/ez.cpp Normal file
View File

@@ -0,0 +1,323 @@
/*-------------------------------------------------------------------------------
* ez.cpp
* EZPWD R-S bridge and ISCH map lookup
*
* original copyrights for portions used below (OP25, EZPWD)
*
* LWVMOBILE
* 2022-09 DSD-FME Florida Man Edition
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include "ezpwd/rs"
#include <map>
std::vector<uint8_t> ESS_A(28,0); // ESS_A and ESS_B are hexbits vectors
std::vector<uint8_t> ESS_B(16,0);
ezpwd::RS<63,35> rs28;
std::vector<uint8_t> HB(63,0);
std::vector<uint8_t> HBS(63,0);
std::vector<uint8_t> HBL(63,0);
std::vector<int> Erasures;
//Reed-Solomon Correction of ESS section
int ez_rs28_ess (int payload[96], int parity[168])
{
//do something
uint8_t a, b, i, j, k;
k = 0;
for (i = 0; i < 16; i++)
{
b = 0;
for (j = 0; j < 6; j++)
{
b = b << 1;
b = b + payload[k]; //convert bits to hexbits.
k++;
}
ESS_B[i] = b;
// fprintf (stderr, " %X", ESS_B[i]);
}
k = 0;
for (i = 0; i < 28; i++)
{
a = 0;
for (j = 0; j < 6; j++)
{
a = a << 1;
a = a + parity[k]; //convert bits to hexbits.
k++;
}
ESS_A[i] = a;
// fprintf (stderr, " %X ", ESS_A[i]);
}
int ec;
ec = rs28.decode (ESS_B, ESS_A);
// fprintf (stderr, "\n EC = %d \n", ec);
//convert ESS_B back to bits
k = 0;
for (i = 0; i < 16; i++)
{
b = 0;
for (j = 0; j < 6; j++)
{
b = (ESS_B[i] >> (5-j) & 0x1);
payload[k] = b;
k++;
}
// fprintf (stderr, " %X", ESS_B[i]);
}
return(ec);
}
//Reed-Solomon Correction of FACCH section
int ez_rs28_facch (int payload[156], int parity[114])
{
//do something!
int ec = -2;
int i, j, k, b;
//init HB
for (i = 0; i < 64; i++)
{
HB[i] = 0;
}
//Erasures for FACCH
Erasures = {0,1,2,3,4,5,6,7,8,54,55,56,57,58,59,60,61,62};
//convert bits to hexbits, 156 for payload, 114 parity
j = 9; //starting position according to OP25
for (i = 0; i < 156; i += 6)
{
HB[j] = (payload[i] << 5) + (payload[i+1] << 4) + (payload[i+2] << 3) + (payload[i+3] << 2) + (payload[i+4] << 1) + payload[i+5];
j++;
}
//j should continue from its last increment
for (i = 0; i < 114; i += 6)
{
HB[j] = (parity[i] << 5) + (parity[i+1] << 4) + (parity[i+2] << 3) + (parity[i+3] << 2) + (parity[i+4] << 1) + parity[i+5];
j++;
}
ec = rs28.decode(HB, Erasures);
//convert HB back to bits
//fprintf (stderr, "\n");
k = 0;
for (i = 0; i < 26; i++) //26*6=156 bits
{
b = 0;
for (j = 0; j < 6; j++)
{
b = (HB[i+9] >> (5-j) & 0x1); //+9 to mach our starting position
payload[k] = b;
//fprintf (stderr, "%d", payload[k]);
k++;
}
}
return (ec);
}
//Reed-Solomon Correction of SACCH section
int ez_rs28_sacch (int payload[180], int parity[132])
{
//do something!
int ec = -2;
int i, j, k, b;
//init HBS
for (i = 0; i < 64; i++)
{
HBS[i] = 0;
}
//Erasures for SACCH
Erasures = {0,1,2,3,4,57,58,59,60,61,62};
//convert bits to hexbits, 156 for payload, 114 parity
j = 5; //starting position according to OP25
for (i = 0; i < 180; i += 6)
{
HBS[j] = (payload[i] << 5) + (payload[i+1] << 4) + (payload[i+2] << 3) + (payload[i+3] << 2) + (payload[i+4] << 1) + payload[i+5];
j++;
}
//j should continue from its last increment
for (i = 0; i < 132; i += 6)
{
HBS[j] = (parity[i] << 5) + (parity[i+1] << 4) + (parity[i+2] << 3) + (parity[i+3] << 2) + (parity[i+4] << 1) + parity[i+5];
j++;
}
ec = rs28.decode(HBS, Erasures);
//convert HBS back to bits
// fprintf (stderr, "\n");
k = 0;
for (i = 0; i < 30; i++) //30*6=180 bits
{
b = 0;
for (j = 0; j < 6; j++)
{
b = (HBS[i+5] >> (5-j) & 0x1); //+5 to mach our starting position
payload[k] = b;
// fprintf (stderr, "%d", payload[k]);
k++;
}
}
return (ec);
}
//I-ISCH Lookup section, borrowed from OP25
std::map <std::string, int> isch_map;
void map_isch()
{
isch_map["184229d461"] = 0;
isch_map["18761451f6"] = 1;
isch_map["181ae27e2f"] = 2;
isch_map["182edffbb8"] = 3;
isch_map["18df8a7510"] = 4;
isch_map["18ebb7f087"] = 5;
isch_map["188741df5e"] = 6;
isch_map["18b37c5ac9"] = 7;
isch_map["1146a44f13"] = 8;
isch_map["117299ca84"] = 9;
isch_map["111e6fe55d"] = 10;
isch_map["112a5260ca"] = 11;
isch_map["11db07ee62"] = 12;
isch_map["11ef3a6bf5"] = 13;
isch_map["1183cc442c"] = 14;
isch_map["11b7f1c1bb"] = 15;
isch_map["1a4a2e239e"] = 16;
isch_map["1a7e13a609"] = 17;
isch_map["1a12e589d0"] = 18;
isch_map["1a26d80c47"] = 19;
isch_map["1ad78d82ef"] = 20;
isch_map["1ae3b00778"] = 21;
isch_map["1a8f4628a1"] = 22;
isch_map["1abb7bad36"] = 23;
isch_map["134ea3b8ec"] = 24;
isch_map["137a9e3d7b"] = 25;
isch_map["13166812a2"] = 26;
isch_map["1322559735"] = 27;
isch_map["13d300199d"] = 28;
isch_map["13e73d9c0a"] = 29;
isch_map["138bcbb3d3"] = 30;
isch_map["13bff63644"] = 31;
isch_map["1442f705ef"] = 32;
isch_map["1476ca8078"] = 33;
isch_map["141a3cafa1"] = 34;
isch_map["142e012a36"] = 35;
isch_map["14df54a49e"] = 36;
isch_map["14eb692109"] = 37;
isch_map["14879f0ed0"] = 38;
isch_map["14b3a28b47"] = 39;
isch_map["1d467a9e9d"] = 40;
isch_map["1d72471b0a"] = 41;
isch_map["1d1eb134d3"] = 42;
isch_map["1d2a8cb144"] = 43;
isch_map["1ddbd93fec"] = 44;
isch_map["1defe4ba7b"] = 45;
isch_map["1d831295a2"] = 46;
isch_map["1db72f1035"] = 47;
isch_map["164af0f210"] = 48;
isch_map["167ecd7787"] = 49;
isch_map["16123b585e"] = 50;
isch_map["162606ddc9"] = 51;
isch_map["16d7535361"] = 52;
isch_map["16e36ed6f6"] = 53;
isch_map["168f98f92f"] = 54;
isch_map["16bba57cb8"] = 55;
isch_map["1f4e7d6962"] = 56;
isch_map["1f7a40ecf5"] = 57;
isch_map["1f16b6c32c"] = 58;
isch_map["1f228b46bb"] = 59;
isch_map["1fd3dec813"] = 60;
isch_map["1fe7e34d84"] = 61;
isch_map["1f8b15625d"] = 62;
isch_map["1fbf28e7ca"] = 63;
isch_map["84d62c339"] = 64;
isch_map["8795f46ae"] = 65;
isch_map["815a96977"] = 66;
isch_map["82194ece0"] = 67;
isch_map["8d0c16248"] = 68;
isch_map["8e4fce7df"] = 69;
isch_map["8880ac806"] = 70;
isch_map["8bc374d91"] = 71;
isch_map["149ef584b"] = 72;
isch_map["17dd2dddc"] = 73;
isch_map["11124f205"] = 74;
isch_map["125197792"] = 75;
isch_map["1d44cf93a"] = 76;
isch_map["1e0717cad"] = 77;
isch_map["18c875374"] = 78;
isch_map["1b8bad6e3"] = 79;
isch_map["a456534c6"] = 80;
isch_map["a7158b151"] = 81;
isch_map["a1dae9e88"] = 82;
isch_map["a29931b1f"] = 83;
isch_map["ad8c695b7"] = 84;
isch_map["aecfb1020"] = 85;
isch_map["a800d3ff9"] = 86;
isch_map["ab430ba6e"] = 87;
isch_map["341e8afb4"] = 88;
isch_map["375d52a23"] = 89;
isch_map["3192305fa"] = 90;
isch_map["32d1e806d"] = 91;
isch_map["3dc4b0ec5"] = 92;
isch_map["3e8768b52"] = 93;
isch_map["38480a48b"] = 94;
isch_map["3b0bd211c"] = 95;
isch_map["44dbc12b7"] = 96;
isch_map["479819720"] = 97;
isch_map["41577b8f9"] = 98;
isch_map["4214a3d6e"] = 99;
isch_map["4d01fb3c6"] = 100;
isch_map["4e4223651"] = 101;
isch_map["488d41988"] = 102;
isch_map["4bce99c1f"] = 103;
isch_map["d493189c5"] = 104;
isch_map["d7d0c0c52"] = 105;
isch_map["d11fa238b"] = 106;
isch_map["d25c7a61c"] = 107;
isch_map["dd49228b4"] = 108;
isch_map["de0afad23"] = 109;
isch_map["d8c5982fa"] = 110;
isch_map["db864076d"] = 111;
isch_map["645bbe548"] = 112;
isch_map["6718660df"] = 113;
isch_map["61d704f06"] = 114;
isch_map["6294dca91"] = 115;
isch_map["6d8184439"] = 116;
isch_map["6ec25c1ae"] = 117;
isch_map["680d3ee77"] = 118;
isch_map["6b4ee6be0"] = 119;
isch_map["f41367e3a"] = 120;
isch_map["f750bfbad"] = 121;
isch_map["f19fdd474"] = 122;
isch_map["f2dc051e3"] = 123;
isch_map["fdc95df4b"] = 124;
isch_map["fe8a85adc"] = 125;
isch_map["f845e7505"] = 126;
isch_map["fb063f092"] = 127;
}
unsigned long long int isch_lookup (unsigned long long int isch)
{
map_isch(); //initialize the lookup map
char s[64];
unsigned long long int decoded = -2; //initialize lookup to an invalid number
sprintf(s, "%llx", isch);
decoded = isch_map[s];
return(decoded);
}

128
src/ezpwd/asserter Normal file
View File

@@ -0,0 +1,128 @@
#ifndef _EZPWD_ASSERTER
#define _EZPWD_ASSERTER
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iomanip>
#include <sstream>
namespace ezpwd {
#define ISEQUAL( ... ) isequal(__FILE__, __LINE__, __VA_ARGS__ )
#define ISTRUE( ... ) istrue( __FILE__, __LINE__, __VA_ARGS__ )
#define ISFALSE( ... ) isfalse(__FILE__, __LINE__, __VA_ARGS__ )
#define ISNEAR( ... ) isnear( __FILE__, __LINE__, __VA_ARGS__ )
#define FAILURE( ... ) failure(__FILE__, __LINE__, __VA_ARGS__ )
struct asserter {
bool failed; // The last test failed
int failures; // Total number of failures
std::string out; // Last failure
asserter()
: failed( false )
, failures( 0 )
, out()
{
;
}
//
// output( <std::ostream> ) -- Output description of last failed test (or nothing if successful)
// <std::ostream> << <asserter>
//
std::ostream &output(
std::ostream &lhs )
const
{
return lhs << out;
}
//
// (bool) <asserter> -- Return status of last test
//
operator bool()
{
return failed;
}
template < typename T >
asserter &istrue( const char *file, int line, const T &a, const std::string &comment = std::string() )
{
return isequal( file, line, !!a, true, comment );
}
template < typename T >
asserter &isfalse( const char *file, int line, const T &a, const std::string &comment = std::string() )
{
return isequal( file, line, !!a, false, comment );
}
template < typename T >
asserter &isequal( const char *file, int line, const T &a, const T &b, const std::string &comment = std::string() )
{
if ( ! ( a == b )) {
std::ostringstream oss;
oss << a << " != " << b;
return failure( file, line, oss.str(), comment );
}
return success();
}
template < typename T >
asserter &isnear( const char *file, int line, const T &a, const T &b, const T &delta, const std::string &comment = std::string() )
{
T difference;
difference = ( a < b
? T( b - a )
: T( a - b ));
if ( ! ( difference < ( delta < T( 0 ) ? T( -delta ) : T( delta )))) {
std::ostringstream oss;
oss << std::setprecision( 13 ) << a << " != " << b << " +/- " << delta;
return failure( file, line, oss.str(), comment );
}
return success();
}
asserter &failure( const char *file, int line, const std::string &comparison,
const std::string &comment = std::string() )
{
++failures;
const char *needle = "/";
const char *slash = std::find_end( file, file + strlen( file ),
needle, needle + strlen( needle ));
if ( slash == file + strlen( file ))
slash = file;
else
slash += 1;
std::ostringstream oss;
oss
<< std::setw( 24 ) << slash << ", "
<< std::setw( -5 ) << line
<< "; FAILURE: " << comparison
<< ( comment.size() ? ": " : "" ) << comment
<< std::endl;
out = oss.str();
failed = true;
return *this;
}
asserter &success()
{
out.clear();
failed = false;
return *this;
}
}; // class asserter
} // namespace ezpwd
std::ostream &operator<<(
std::ostream &lhs,
ezpwd::asserter &rhs )
{
return rhs.output( lhs );
}
#endif // _EZPWD_ARRAY

485
src/ezpwd/bch Normal file
View File

@@ -0,0 +1,485 @@
/*
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
*
* Copyright (c) 2017, Hard Consulting Corporation.
*
* Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. See the LICENSE file at the top of the
* source tree. Ezpwd Reed-Solomon is also available under Commercial license. The Djelic BCH code
* under djelic/ and the c++/ezpwd/bch_base wrapper is redistributed under the terms of the GPLv2+,
* regardless of the overall licensing terms.
*
* Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*/
#ifndef _EZPWD_BCH
#define _EZPWD_BCH
#include <sstream>
#include "rs_base" // Basic DEBUG, EZPWD_... preprocessor stuff, ezpwd::log_, etc.
#include "bch_base"
namespace ezpwd {
//
// ezpwd::bch_base -- Interface to underlying Djelic Linux Kernel API
// ezpwd::bch<SYMBOLS, CORRECTION> -- General BCH codec types; min. CORRECTION capacity, undefined PAYLOAD
//
// These implementations retain the original Djelic Linux Kernel API; specifically, they find
// a BCH codec of the given Galois order M (ie. has codewords of size 2**M-1), and at least the
// target bit-error correction capacity T. They may correct more than T errors, and the number
// of parity ECC bits will be selected by the algorithm. You need to compute the maximum
// non-parity payload by computing _bch->n - _bch->ecc_bits.
//
// The data and parity bits must always be on separate blocks of int8_t/uint8_t-sized data
// in the container. This is required because the underlying API must break the data and parity
// out as separate arrays for processing. So, if the computed ecc_bits is not evenly divisible
// by 8, some care must be taken to ensure that it is packed into exactly ecc_bytes of data at
// the end of the supplied container. Alternatively, it can be kept in a separate container.
// This is probably safest, as the bch_base/bch/BCH classes will never attempt to resize the
// data/parity containers when supplied separately.
//
// Like the Reed-Solomon APIs, the bch_base/bch/BCH APIs will alter the size of a variable
// container in encode(...), to add the BCH ECC "parity" data (eg. std::vector, std::string).
// Fixed containers (eg. std::array) are never resized, and it is assumed that ecc_bytes of
// parity data exist at the end of the container.
//
class bch_base {
public:
ezpwd::bch_control *_bch;
bch_base( const bch_base & ) = delete; // no copy-constructor
bch_base(
size_t m,
size_t t,
unsigned int prim_poly = 0 )
: _bch( ezpwd::init_bch( int( m ), int( t ), prim_poly ))
{
;
}
virtual ~bch_base()
{
ezpwd::free_bch( this->_bch );
}
size_t ecc_bytes()
const
{
return _bch->ecc_bytes;
}
size_t ecc_bits()
const
{
return _bch->ecc_bits;
}
size_t t()
const
{
return _bch->t;
}
//
// <ostream> << bch_base -- output codec in standard BCH( N, N-ECC, T ) form
//
virtual std::ostream &output(
std::ostream &lhs )
const
{
return lhs << *this->_bch;
}
//
// encode -- container interfaces
//
// Returns number of ECC *bits* initialized (to be consistent w/ decode, which returns
// number of bit errors corrected).
//
int encode(
std::string &data )
const
{
typedef uint8_t uT;
typedef std::pair<uT *, uT *>
uTpair;
data.resize( data.size() + ecc_bytes() );
return encode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ));
}
int encode(
const std::string &data,
std::string &parity )
const
{
typedef uint8_t uT;
typedef std::pair<const uT *, const uT *>
cuTpair;
typedef std::pair<uT *, uT *>
uTpair;
parity.resize( ecc_bytes() );
return encode( cuTpair( (const uT *)&data.front(), (const uT *)&data.front() + data.size() ),
uTpair( (uT *)&parity.front(), (uT *)&parity.front() + parity.size() ));
}
template < typename T >
int encode(
std::vector<T> &data )
const
{
typedef typename std::make_unsigned<T>::type
uT;
typedef std::pair<uT *, uT *>
uTpair;
data.resize( data.size() + ecc_bytes() );
return encode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ));
}
template < typename T >
int encode(
const std::vector<T>&data,
std::vector<T> &parity )
const
{
typedef typename std::make_unsigned<T>::type
uT;
typedef std::pair<const uT *, const uT *>
cuTpair;
typedef std::pair<uT *, uT *>
uTpair;
parity.resize( ecc_bytes() );
return encode( cuTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ),
uTpair( (uT *)&parity.front(), (uT *)&parity.front() + parity.size() ));
}
template < typename T, size_t N >
int encode(
std::array<T,N> &data,
int pad = 0 ) // ignore 'pad' symbols at start of array
const
{
typedef typename std::make_unsigned<T>::type
uT;
typedef std::pair<uT *, uT *>
uTpair;
return encode( uTpair( (uT *)&data.front() + pad, (uT *)&data.front() + data.size() ));
}
//
// encode -- denote data+parity or data, parity using pairs of uint8_t iterators
// encode -- base implementation, in terms of uint8_t pointers
//
virtual int encode(
const std::pair<uint8_t *, uint8_t *>
&data )
const
{
return encode( data.first, data.second - data.first - ecc_bytes(), data.second - ecc_bytes() );
}
virtual int encode(
const std::pair<const uint8_t *, const uint8_t *>
&data,
const std::pair<uint8_t *, uint8_t *>
&parity )
const
{
if ( size_t( parity.second - parity.first ) != ecc_bytes() ) {
EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: parity length incompatible with number of ECC bytes", -1 );
}
return encode( data.first, data.second - data.first, parity.first );
}
virtual int encode(
const uint8_t *data,
size_t len,
uint8_t *parity )
const
{
memset( parity, 0, ecc_bytes() ); // Djelic encode_bch requires ECC to be initialized to 0
ezpwd::encode_bch( this->_bch, data, len, parity );
return int( ecc_bits() );
}
//
// decode -- container interface, w/ optional corrected bit-error positions reported
//
// Does not correct errors in parity!
//
int decode(
std::string &data,
std::vector<int> *position= 0 )
const
{
typedef uint8_t uT;
typedef std::pair<uT *, uT *>
uTpair;
return decode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ),
position );
}
int decode(
std::string &data,
std::string &parity,
std::vector<int> *position= 0 )
const
{
typedef uint8_t uT;
typedef std::pair<uT *, uT *>
uTpair;
return decode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ),
uTpair( (uT *)&parity.front(), (uT *)&parity.front() + parity.size() ),
position );
}
template < typename T >
int decode(
std::vector<T> &data,
std::vector<int> *position= 0 )
const
{
typedef typename std::make_unsigned<T>::type
uT;
typedef std::pair<uT *, uT *>
uTpair;
return decode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ),
position );
}
template < typename T >
int decode(
std::vector<T> &data,
std::vector<T> &parity,
std::vector<int> *position= 0 )
const
{
typedef typename std::make_unsigned<T>::type
uT;
typedef std::pair<uT *, uT *>
uTpair;
return decode( uTpair( (uT *)&data.front(), (uT *)&data.front() + data.size() ),
uTpair( (uT *)&parity.front(), (uT *)&parity.front() + parity.size() ),
position );
}
template < typename T, size_t N >
int decode(
std::array<T,N> &data,
int pad = 0, // ignore 'pad' symbols at start of array
std::vector<int> *position= 0 )
const
{
typedef typename std::make_unsigned<T>::type
uT;
typedef std::pair<uT *, uT *>
uTpair;
return decode( uTpair( (uT *)&data.front() + pad, (uT *)&data.front() + data.size() ),
position );
}
//
// decode -- denote data+parity or data, parity using pairs of uint8_t iterators
// decode -- decode and correct BCH codeword, returning number of corrections, or -1 if failed
//
// Corrects data in-place (unlike the basic Djelic Linux Kernel API, which only returns
// error positions. For consistency with ezpwd::rs..., we report all error positions as
// std::vector<int>, even though the underlying Djelic API reports them as arrays of
// unsigned int.
//
virtual int decode(
const std::pair<uint8_t *, uint8_t *>
&data,
std::vector<int> *position= 0 )
const
{
return decode( data.first, data.second - data.first - ecc_bytes(), data.second - ecc_bytes(),
position );
}
virtual int decode(
const std::pair<uint8_t *, uint8_t *>
&data,
const std::pair<uint8_t *, uint8_t *>
&parity,
std::vector<int> *position= 0 )
const
{
if ( size_t( parity.second - parity.first ) != ecc_bytes() ) {
EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: parity length incompatible with number ECC bytes", -1 );
}
return decode( data.first, data.second - data.first, parity.first,
position );
}
virtual int decode(
uint8_t *data,
size_t len,
uint8_t *parity,
std::vector<int> *position= 0 )
const
{
if ( position )
position->resize( t() * 2 ); // may be able to correct beyond stated capacity!
int corrects = ezpwd::correct_bch(
this->_bch, data, len, parity, 0, 0,
position ? (unsigned int *)&(*position)[0] : 0 );
if ( position && corrects >= 0 )
position->resize( corrects );
return corrects;
}
//
// {en,de}coded -- returns an encoded/corrected copy of the provided container
//
// NOTE:
//
// Must return exceptions on failure; If exceptions inhibited, returns a
// default-constructed instance of the supplied data container. This may be sufficient to
// reliably deduce failure; if not, this interface should not be used.
//
// Overloads decoded to also allow recovery of corrected error positions and count.
//
template <typename C>
C encoded(
C data )
const
{
if ( encode( data ) < 0 )
EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: Could not encode data", C() );
return data;
}
template <typename C>
C decoded(
C data )
const
{
if ( decode( data ) < 0 )
EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: Could not decode data", C() );
return data;
}
template <typename C>
C decoded(
C data,
std::vector<int> &position )
const
{
if ( decode( data, &position ) < 0 )
EZPWD_RAISE_OR_RETURN( std::runtime_error, "BCH: Could not decode data", C() );
return data;
}
}; // class bch_base
template < size_t SYMBOLS, size_t CORRECTION >
class bch
: public bch_base
{
public:
bch()
: bch_base( ezpwd::log_<SYMBOLS + 1>::value, CORRECTION )
{
;
}
virtual ~bch()
{
;
}
}; // class bch
//
// std::ostream << ezpwd::bch_base
//
// Output a BCH codec description in standard form eg. BCH( 255, 239, 2 )
//
inline
std::ostream &operator<<(
std::ostream &lhs,
const ezpwd::bch_base
&rhs )
{
return rhs.output( lhs );
}
//
// ezpwd::BCH<SYMBOLS, PAYLOAD, CAPACITY> -- Standard BCH codec types
//
// Specify and create a standard BCH codec with exactly the specified capacities. We create
// the undering BCH codec using SYMBOLS and CORRECTION capacity; the actual correction capacity
// T, the number of PARITY bits and hence PAYLOAD (CAPACITY - PARITY) is selected automatically
// by the underlying Djelic Linux Kernel BCH codec API. For this interface, we demand that the
// caller *knows* all of these values at compile time, however, mostly for future optimization
// purposes. We validate them, and fail the constructor if they don't match. See bch_test for
// an enumeration of all possible BCH codecs.
//
// In the future, this API may be re-implemented to not use the generic BCH API, but a more
// optimized locally-defined implementation that leverages the fixed SYMBOLS, PAYLOAD and
// CORRECTION capacities to produce more optimal code.
//
template < size_t SYMBOLS, size_t PAYLOAD, size_t CORRECTION >
class BCH
: public bch<SYMBOLS, CORRECTION>
{
public:
static const size_t M = ezpwd::log_<SYMBOLS + 1>::value; // Galois field order; eg. 255 --> 8
static const size_t N = SYMBOLS;
static const size_t T = CORRECTION;
static const size_t LOAD = PAYLOAD;
BCH()
: bch<SYMBOLS, CORRECTION>()
{
if ( this->_bch->t != T || this->_bch->n != N
|| this->_bch->n - this->_bch->ecc_bits != LOAD ) {
std::ostringstream err;
this->output( err )
<< " specified doesn't match underlying " << *this->_bch << " produced.";
EZPWD_RAISE_OR_ABORT( std::runtime_error, err.str().c_str() );
}
}
virtual ~BCH()
{
;
}
//
// <ostream> << BCH<...> -- output codec in standard BCH( N, N-ECC, T ) form
//
virtual std::ostream &output(
std::ostream &lhs )
const
{
return lhs
<< "BCH( " << std::setw( 3 ) << N
<< ", " << std::setw( 3 ) << LOAD
<< ", " << std::setw( 3 ) << T
<< " )";
}
}; // class BCH
//
// std::ostream << ezpwd::BCH<...>
//
// Output a BCH codec description in standard form eg. BCH( 255, 239, 2 )
//
// NOTE: clang/gcc disagree on the scoping of operator<< template/non-template functions...
//
template <size_t SYMBOLS, size_t PAYLOAD, size_t CORRECTION>
inline
std::ostream &operator<<(
std::ostream &lhs,
const ezpwd::BCH<SYMBOLS, PAYLOAD, CORRECTION>
&rhs )
{
return rhs.output( lhs );
}
} // namespace ezpwd
#endif // _EZPWD_BCH

219
src/ezpwd/bch_base Normal file
View File

@@ -0,0 +1,219 @@
/*
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
*
* Copyright (c) 2017, Hard Consulting Corporation.
*
* Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. See the LICENSE file at the top of the
* source tree. Ezpwd Reed-Solomon is also available under Commercial license. The Djelic BCH code
* under djelic/ and the c++/ezpwd/bch_base wrapper is redistributed under the terms of the GPLv2+,
* regardless of the overall licensing terms.
*
* Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*/
#ifndef _EZPWD_BCH_BASE
#define _EZPWD_BCH_BASE
#include <iostream>
#include <iomanip>
#include <vector>
//
// Presently, we simply import the Linux Kernel's "C" BCH API directly into the ezpwd:: namespace In
// order to compile, you must (at least) run "make djelic" in the base directory of the
// https://github.com/pjkundert/bch.git repo.
//
namespace ezpwd {
/**
* struct bch_control - BCH control structure
* @m: Galois field order
* @n: maximum codeword size in bits (= 2^m-1)
* @t: error correction capability in bits
* @ecc_bits: ecc exact size in bits, i.e. generator polynomial degree (<=m*t)
* @ecc_bytes: ecc max size (m*t bits) in bytes
* @a_pow_tab: Galois field GF(2^m) exponentiation lookup table
* @a_log_tab: Galois field GF(2^m) log lookup table
* @mod8_tab: remainder generator polynomial lookup tables
* @ecc_buf: ecc parity words buffer
* @ecc_buf2: ecc parity words buffer
* @xi_tab: GF(2^m) base for solving degree 2 polynomial roots
* @syn: syndrome buffer
* @cache: log-based polynomial representation buffer
* @elp: error locator polynomial
* @poly_2t: temporary polynomials of degree 2t
*/
/**
* init_bch - initialize a BCH encoder/decoder
* @m: Galois field order, should be in the range 5-15
* @t: maximum error correction capability, in bits
* @prim_poly: user-provided primitive polynomial (or 0 to use default)
*
* Returns:
* a newly allocated BCH control structure if successful, NULL otherwise
*
* This initialization can take some time, as lookup tables are built for fast
* encoding/decoding; make sure not to call this function from a time critical
* path. Usually, init_bch() should be called on module/driver init and
* free_bch() should be called to release memory on exit.
*
* You may provide your own primitive polynomial of degree @m in argument
* @prim_poly, or let init_bch() use its default polynomial.
*
* Once init_bch() has successfully returned a pointer to a newly allocated
* BCH control structure, ecc length in bytes is given by member @ecc_bytes of
* the structure.
*/
/**
* encode_bch - calculate BCH ecc parity of data
* @bch: BCH control structure
* @data: data to encode
* @len: data length in bytes
* @ecc: ecc parity data, must be initialized by caller
*
* The @ecc parity array is used both as input and output parameter, in order to
* allow incremental computations. It should be of the size indicated by member
* @ecc_bytes of @bch, and should be initialized to 0 before the first call.
*
* The exact number of computed ecc parity bits is given by member @ecc_bits of
* @bch; it may be less than m*t for large values of t.
*/
/**
* decode_bch - decode received codeword and find bit error locations
* @bch: BCH control structure
* @data: received data, ignored if @calc_ecc is provided
* @len: data length in bytes, must always be provided
* @recv_ecc: received ecc, if NULL then assume it was XORed in @calc_ecc
* @calc_ecc: calculated ecc, if NULL then calc_ecc is computed from @data
* @syn: hw computed syndrome data (if NULL, syndrome is calculated)
* @errloc: output array of error locations
*
* Returns:
* The number of errors found, or -EBADMSG if decoding failed, or -EINVAL if
* invalid parameters were provided
*
* Depending on the available hw BCH support and the need to compute @calc_ecc
* separately (using encode_bch()), this function should be called with one of
* the following parameter configurations -
*
* by providing @data and @recv_ecc only:
* decode_bch(@bch, @data, @len, @recv_ecc, NULL, NULL, @errloc)
*
* by providing @recv_ecc and @calc_ecc:
* decode_bch(@bch, NULL, @len, @recv_ecc, @calc_ecc, NULL, @errloc)
*
* by providing ecc = recv_ecc XOR calc_ecc:
* decode_bch(@bch, NULL, @len, NULL, ecc, NULL, @errloc)
*
* by providing syndrome results @syn:
* decode_bch(@bch, NULL, @len, NULL, NULL, @syn, @errloc)
*
* Once decode_bch() has successfully returned with a positive value, error
* locations returned in array @errloc should be interpreted as follows -
*
* if (errloc[n] >= 8*len), then n-th error is located in ecc (no need for
* data correction)
*
* if (errloc[n] < 8*len), then n-th error is located in data and can be
* corrected with statement data[errloc[n]/8] ^= 1 << (errloc[n] % 8);
*
* Note that this function does not perform any data correction by itself, it
* merely indicates error locations.
*/
/**
* init_bch - initialize a BCH encoder/decoder
* @m: Galois field order, should be in the range 5-15
* @t: maximum error correction capability, in bits
* @prim_poly: user-provided primitive polynomial (or 0 to use default)
*
* Returns:
* a newly allocated BCH control structure if successful, NULL otherwise
*
* This initialization can take some time, as lookup tables are built for fast
* encoding/decoding; make sure not to call this function from a time critical
* path. Usually, init_bch() should be called on module/driver init and
* free_bch() should be called to release memory on exit.
*
* You may provide your own primitive polynomial of degree @m in argument
* @prim_poly, or let init_bch() use its default polynomial.
*
* Once init_bch() has successfully returned a pointer to a newly allocated
* BCH control structure, ecc length in bytes is given by member @ecc_bytes of
* the structure.
*/
extern "C" {
#include "../../djelic_bch.h"
}
//
// correct_bch -- corrects data (but not parity!), as suggested by decode_bch, above
//
// A convenience interface that defaults all of the not strictly required parameters, and
// automatically corrects bit-errors in data *and* the supplied parity. Does not attempt to
// correct bit errors found in the parity data. If not supplied, 'errloc' is allocated
// internally; otherwise, it is assumed to be of at least size bch->t (the minimum error
// correction capacity of the BCH codec).
//
// However, beware -- at larger values of T, the actual correction capacity of the BCH codec
// could be greater than the requested T. Therefore, it is recommended that you always supply a
// larger than required errloc array; recommend T*2?
//
inline
int correct_bch(
struct bch_control *bch,
uint8_t *data,
unsigned int len,
uint8_t *recv_ecc,
const uint8_t *calc_ecc= 0,
const unsigned int *syn = 0,
unsigned int *errloc = 0 ) // must be sized at least bch->t; often, greater!
{
unsigned int _errloc[511]; // much larger than the correction capacity of largest supported BCH codec
if ( ! errloc )
errloc = _errloc;
int err = decode_bch( bch, data, len, recv_ecc, calc_ecc, syn, errloc );
if ( err > 0 ) {
// A +'ve number of bit-error correction locations were found
for ( int n = 0; n < err; ++n ) {
/**
* if (errloc[n] < 8*len), then n-th error is located in data and can be corrected
* with statement data[errloc[n]/8] ^= 1 << (errloc[n] % 8). If in the parity, it
* is assumed to be located at the end of the data, so offset by 'len' bytes.
*/
if ( errloc[n] < 8*len ) {
data[errloc[n] / 8] ^= 1 << ( errloc[n] % 8 );
} else if ( recv_ecc && errloc[n] < 8 * len + 8 * bch->ecc_bytes ) {
recv_ecc[errloc[n] / 8 - len]
^= 1 << ( errloc[n] % 8 );
}
}
}
return err;
}
//
// <ostream> << <ezpwd::bch_control> -- output codec in standard BCH( N, N-ECC, T ) form
//
inline
std::ostream &operator<<(
std::ostream &lhs,
const ezpwd::bch_control
&bch )
{
return lhs
<< "BCH( " << std::setw( 3 ) << bch.n
<< ", " << std::setw( 3 ) << bch.n - bch.ecc_bits
<< ", " << std::setw( 3 ) << bch.t
<< " )";
}
} // namespace ezpwd
#endif // _EZPWD_BCH_BASE

506
src/ezpwd/corrector Normal file
View File

@@ -0,0 +1,506 @@
/*
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
*
* Copyright (c) 2014, Hard Consulting Corporation.
*
* Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. See the LICENSE file at the top of the
* source tree. Ezpwd Reed-Solomon is also available under Commercial license. c++/ezpwd/rs_base
* is redistributed under the terms of the LGPL, regardless of the overall licensing terms.
*
* Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*/
#ifndef _EZPWD_CORRECTOR
#define _EZPWD_CORRECTOR
#include "rs"
#include "serialize"
namespace ezpwd {
//
// best_avg -- collect <password>,<confidence> guesses, and return the unambiguous best one
//
typedef std::map<std::string, std::pair<int, int>> // (<password>, (<count>, <avgsum>))
best_avg_base_t;
class best_avg
: public best_avg_base_t
{
public:
using best_avg_base_t::begin;
using best_avg_base_t::end;
using best_avg_base_t::insert;
using best_avg_base_t::find;
using best_avg_base_t::iterator;
using best_avg_base_t::const_iterator;
using best_avg_base_t::value_type;
using best_avg_base_t::mapped_type;
//
// add -- add the given pct to the current average for <string> str
//
iterator add(
const std::string &str,
int pct )
{
iterator i = find( str );
if ( i == end() )
i = insert( i, value_type( str, mapped_type() ));
i->second.first += 1;
i->second.second += pct;
return i;
}
//
// best -- return the unambiguously best value (average is >, or == but longer), or end()
//
const_iterator best()
const
{
const_iterator top = end();
bool uni = false;
for ( const_iterator i = begin(); i != end(); ++i ) {
if ( top == end()
or i->second.second/i->second.first > top->second.second/top->second.first
or ( i->second.second/i->second.first == top->second.second/top->second.first
and i->first.size() > top->first.size())) {
top = i;
uni = true;
} else if ( i->second.second/i->second.first == top->second.second/top->second.first
and i->first.size() == top->first.size()) {
uni = false;
}
}
return uni ? top : end();
}
//
// evaluation -- process a (<password>,(<count>,<avgsum>)) into (<average>,<password>)
// sort -- return a multimap indexed by <average> --> <string>
// output -- output the <string>: <average>, sorted by average
//
static std::pair<const int,const std::string &>
evaluation( const value_type &val )
{
return std::pair<const int,const std::string &>( val.second.second/val.second.first, val.first );
}
typedef std::multimap<const int,const std::string &>
sorted_t;
sorted_t sort()
const
{
sorted_t dst;
std::transform( begin(), end(), std::inserter( dst, dst.begin() ), evaluation );
return dst;
}
std::ostream &output(
std::ostream &lhs )
const
{
for ( auto i : sort() )
lhs << std::setw( 16 ) << i.second
<< ": " << std::setw( 3 ) << i.first
<< std::endl;
return lhs;
}
};
} // namespace ezpwd
std::ostream &operator<<(
std::ostream &lhs,
const ezpwd::best_avg &rhs )
{
return rhs.output( lhs );
}
namespace ezpwd {
//
// ezpwd::corrector -- Apply statistical corrections to a string, returning the confidence
//
// All methods are static; no instance is required, as this is primarily used to create
// external language APIs.
//
template <
size_t PARITY,
size_t N = 64,
typename SERIAL = serialize::base< N, serialize::ezpwd< N >>>
class corrector {
public:
static
std::ostream &output(
std::ostream &lhs )
{
lhs << "corrector<PARITY=" << PARITY << ",N=" << N << ",SERIAL=" << SERIAL() << ">";
return lhs;
}
//
// parity(<string>) -- Returns 'PARITY' base-N symbols of R-S parity to the supplied password
//
static std::string parity(
const std::string &password )
{
std::string parity;
rscodec.encode( password, parity );
SERIAL::encode( parity );
return parity;
}
//
// encode(<string>) -- append PARITY base-N parity symbols to password
//
// The supplied password buffer size must be sufficient to contain PARITY additional
// symbols, plus the terminating NUL. Returns the resultant encoded password size
// (excluding the NUL).
//
static size_t encode(
std::string &password )
{
password += parity( password );
return password.size();
}
static size_t encode(
char *password,
size_t size ) // maximum available size
{
size_t len = ::strlen( password ); // length w/o terminating NUL
if ( len + PARITY + 1 > size )
throw std::runtime_error( "ezpwd::rspwd::encode password buffer has insufficient capacity" );
std::string par = parity( std::string( password, password + len ));
if ( par.size() != PARITY )
throw std::runtime_error( "ezpwd::rspwd::encode computed parity with incorrect size" );
std::copy( par.begin(), par.end(), password + len );
len += PARITY;
password[len] = 0;
return len;
}
//
// decode(<string>[,...]) -- Applies R-S error correction on the encoded string, removing parity
//
// Up to 'PARITY' Reed-Solomon parity symbols are examined, to determine if the supplied
// string is a valid R-S codeword and hence very likely to be correct. Optionally supply a
// vector of erasure positions.
//
// An optional 'minimum' final password length may be provided; no R-S parity is assumed
// to exist in the first 'minimum' password characters (default: PARITY). This prevents
// accidentally finding valid R-S codewords in passwords of known minimum length; validation
// codes, for example. Likewise, the optional 'maximum' allows us to limit the number of
// parity symbols that may be assumed to be missing from the end of the codeword.
//
// Returns a confidence strength rating, which is the ratio:
//
// 100 - ( errors * 2 + erasures ) * 100 / parity
//
// if an R-S codeword was solved, and 0 otherwise. If a codeword is solved, but the number
// of errors and erasures corrected indicates that all parity was consumed, the caller may
// opt to not use the corrected string, because there is a chance that our R-S polynomial
// was overwhelmed with errors and actually returned an incorrect codeword. Therefore,
// solving a codeword using all available parity results in 100 - PARITY * 100 / PARITY ==
// 0, which indicates that there is no certainty of correctness; all R-S parity resources
// were used in error/erasure recover, with none left to confirm that the result is actually
// correct. If only zero-strength results are achieved, the longest will be returned (the
// full, original string).
//
// Supports the following forms of error/erasure:
//
// 0) Full parity. All data and parity supplied, and an R-S codeword is solved.
//
// 1) Partial parity. All data and some parity supplied; remainder are deemed erasures.
//
// If PARITY > 2, then up to PARITY/2-1 trailing parity terms are marked as erasures.
// If the R-S codeword is solved and a safe number of errors are found, then we can have
// reasonable confidence that the string is correct.
//
// 1a) Erase errors. Permute the combinations of up to PARITY-1 erasures.
//
// o) Raw password. No parity terms supplied; not an R-S codeword
//
// If none of the error/erasure forms succeed, the password is returned unmodified.
//
// If a non-zero 'minimum' or 'maximum' are provided, they constrain the possible
// resultant password sizes that will be attempted.
//
static
int decode(
std::string &password,
const std::vector<int>
&erasures,
size_t minimum = PARITY,//always deemed at least 1
size_t maximum = 0 ) // if 0, no limit
{
int confidence;
best_avg best;
// Full/Partial parity. Apply some parity erasure if we have some erasure/correction
// capability while maintaining at least one excess parity symbol for verification.
// This can potentially result in longer password being returned, if the R-S decoder
// accidentally solves a codeword.
//
// For example, if PARITY=3 (or 4) then (PARITY+1)/2 == 2, and we would only attempt up
// to 1 parity erasure. This would leave 1 parity symbol to replace the 1 erasure, and
// 1 remaining to validate the integrity of the password.
//
// The password must be long enough to contain at least 1 non-parity symbol, and the
// designated number of non-erased parity symbols! However, by convention we'll demand
// that the password contain at least PARITY symbols -- any less, and we can
// accidentally correct the few remaining password symbols.
//
// Also, if any parity symbols won't decode (eg. were entered in error), we must deem
// them to be erasures, too, and if the number of erasures exceeds the capacity of the
// R-S codec, it'll fail (throw an exception, or at best solve with 0 confidence).
for ( size_t era = 0 // how many parity symbols to deem erased
; era < (PARITY+1)/2
; ++era ) {
if ( password.size() < ( minimum ? minimum : 1 ) + PARITY - era ) {
#if defined( DEBUG ) && DEBUG >= 1
output( std::cout )
<< " Rejected too short password \""
<< password << std::string( era, '_' )
<< "\"" << " (" << era << " parity skipped)"
<< std::endl;
#endif
continue; // too few password symbols to start checking parity
}
if ( maximum and password.size() > maximum + PARITY - era ) {
#if defined( DEBUG ) && DEBUG >= 1
output( std::cout )
<< " Rejected too long password \""
<< password << std::string( era, '_' )
<< "\"" << " (" << era << " parity skipped)"
<< std::endl;
#endif
continue; // too few parity symbols erased to start checking parity
}
// Copy password, adding 'era' additional NULs
std::string fixed( password.size() + era, 0 );
std::copy( password.begin(), password.end(), fixed.begin() );
// Decode the base-N parity, denoting any invalid (mistyped or trailing NUL) symbols
// as erasures (adjust erasure offsets to be from start of password, not start of
// parity). All newly added 'era' symbols will be NUL, and will be invalid. After
// decoding parity, if we've slipped below our minimum R-S capacity threshold
// (ie. because of mistyped parity symbols), don't attempt.
std::vector<int> all_era;
SERIAL::decode( fixed.begin() + fixed.size() - PARITY,
fixed.begin() + fixed.size(), &all_era, 0,
serialize::ws_invalid, serialize::pd_invalid );
if ( all_era.size() >= (PARITY+1)/2 ) {
#if defined( DEBUG ) && DEBUG >= 1
output( std::cout )
<< " Rejected low parity password \""
<< password << std::string( era, '_' )
<< "\"" << " (" << all_era.size() << " parity erasures + "
<< era << " skipped)"
<< std::endl;
#endif
continue; // Too many missing parity symbols
}
if ( all_era.size() + erasures.size() > PARITY ) {
#if defined( DEBUG ) && DEBUG >= 1
output( std::cout )
<< " Rejected hi erasure password \""
<< password << std::string( era, '_' )
<< "\"" << " (" << all_era.size() + erasures.size() << " total erasures + "
<< era << " skipped)"
<< std::endl;
#endif
continue; // Total erasures beyond capacity
}
for ( auto &o : all_era )
o += fixed.size() - PARITY;
std::copy( erasures.begin(), erasures.end(), std::back_inserter( all_era ));
// Enough parity to try to decode. A successful R-S decode with 0 (remaining)
// confidence indicates a successfully validated R-S codeword! Use it (ex. parity).
try {
std::vector<int> position;
int corrects= rscodec.decode( fixed, all_era, &position );
confidence = strength<PARITY>( corrects, all_era, position );
fixed.resize( fixed.size() - PARITY );
if ( confidence >= 0 )
best.add( fixed, confidence );
#if defined( DEBUG ) && DEBUG >= 1
output( std::cout )
<< " Reed-Solomon w/ " << era << " of " << PARITY
<< " parity erasures " << std::setw( 3 ) << confidence
<< "% confidence: \"" << password
<< "\" ==> \"" << fixed
<< "\" (corrects: " << corrects
<< ", erasures at " << all_era
<< ", fixed at " << position << "): "
<< std::endl
<< best;
#endif
} catch ( std::exception &exc ) {
#if defined( DEBUG ) && DEBUG >= 2 // should see only when ezpwd::reed_solomon<...>::decode fails
output( std::cout ) << " invalid part parity password: " << exc.what() << std::endl;
#endif
}
}
// Partial parity, but below threshold for usable error detection. For the first 1 to
// (PARITY+1)/2 parity symbols (eg. for PARITY == 3, (PARITY+1)/2 == 1 ), we cannot
// perform meaningful error or erasure detection. However, if we see that the terminal
// symbols match the R-S symbols we expect from a correct password, we'll ascribe a
// partial confidence due to the matching parity symbols.
//
// password: sock1t
// w/ 3 parity: sock1tkeB
// password ----^^^^^^
// ^^^--- parity
//
for ( size_t era = (PARITY+1)/2 // how many parity symbols are not present
; era < PARITY
; ++era ) {
if ( password.size() < ( minimum ? minimum : 1 ) + PARITY - era ) {
#if defined( DEBUG ) && DEBUG >= 1
output( std::cout )
<< " Rejected too short password \""
<< password << std::string( era, '_' )
<< "\""
<< std::endl;
#endif
continue; // too few password symbols to start checking parity
}
if ( maximum and password.size() > maximum + PARITY - era ) {
#if defined( DEBUG ) && DEBUG >= 1
output( std::cout )
<< " Rejected too long password \""
<< password << std::string( era, '_' )
<< "\"" << " (" << era << " parity skipped)"
<< std::endl;
#endif
continue; // too few parity symbols erased to start checking parity
}
std::string fixed = password;
size_t len = password.size() - ( PARITY - era );
fixed.resize( len );
encode( fixed );
auto differs = std::mismatch( fixed.begin(), fixed.end(), password.begin() );
size_t par_equ = differs.second - password.begin();
if ( par_equ < len || par_equ > len + PARITY )
throw std::runtime_error( "miscomputed R-S parity matching length" );
par_equ -= len;
// At least one parity symbol is requires to give any confidence
if ( par_equ > 0 ) {
std::string basic( fixed.begin(), fixed.begin() + len );
confidence = par_equ * 100 / PARITY; // each worth a normal parity symbol
best.add( basic, confidence );
#if defined( DEBUG ) && DEBUG >= 1
output( std::cout )
<< " Check Chars. w/ " << era << " of " << PARITY
<< " parity missing " << std::setw( 3 ) << confidence
<< "% confidence: \"" << password
<< "\" ==> \"" << basic
<< " (from computed: \"" << fixed << "\")"
<< ": "
<< std::endl
<< best;
#endif
}
}
// Select the best guess and return its confidence. Otherwise, use raw password? If no
// error/erasure attempts succeeded (if no 'best' w/ confidence >= 0), then we'll use
// the raw password w/ 0 confidence, if it meets the minimum/maximum length
// requirements.
confidence = -1;
if ( password.size() >= ( minimum ? minimum : 1 )
and ( maximum == 0 or password.size() <= maximum ))
confidence = 0;
typename best_avg::const_iterator
bi = best.best();
#if defined( DEBUG )
output( std::cout )
<< " Selected " << ( bi != best.end() ? "corrected" : "unmodified" )
<< " password \"" << ( bi != best.end() ? bi->first : password )
<< "\" of length " << ( minimum ? minimum : 1) << "-" << maximum
<< " (vs. \"" << password
<< "\") w/ confidence " << (bi != best.end() ? bi->second.second : confidence )
<< "%, from: "
<< std::endl
<< best;
#endif
if ( bi != best.end() ) {
auto better = best.evaluation( *bi ); // --> (<average>,<password>)
password = better.second;
confidence = better.first;
}
return confidence;
}
static
int decode(
std::string &password,
size_t minimum = PARITY,
size_t maximum = 0 )
{
return decode( password, std::vector<int>(), minimum, maximum );
}
//
// decode(<char*>,<size_t>,<size_t>,<size_t>) -- C interface to decode(<string>)
//
// Traditional C interface. The provided NUL-terminated password+parity is decoded
// (parity removed), and the confidence % is returned.
//
// If any failure occurs, a -'ve value will be returned, and the supplied password
// buffer will be used to contain an error description.
//
static int decode(
char *password, // NUL terminated
size_t siz, // available size
size_t minimum = PARITY,//minimum resultant password length
size_t maximum = 0 ) // maximum ''
{
std::string corrected( password );
int confidence;
try {
confidence = decode( corrected, minimum, maximum );
if ( corrected.size() + 1 > siz )
throw std::runtime_error( "password buffer has insufficient capacity" );
std::copy( corrected.begin(), corrected.end(), password );
password[corrected.size()] = 0;
} catch ( std::exception &exc ) {
confidence = -1;
ezpwd::streambuf_to_buffer sbf( password, siz );
std::ostream( &sbf ) << "corrector<" << PARITY << "> failed: " << exc.what();
}
return confidence;
}
//
// rscodec -- A ?-bit RS(N-1,N-1-PARITY) Reed-Solomon codec
//
// Encodes and decodes R-S symbols over the lower 6 bits of the supplied data. Requires
// that the last N (parity) symbols of the data are in the range [0,63]. The excess bits on
// the data symbols are masked and restored during decoding.
//
static const ezpwd::RS<N-1,N-1-PARITY>
rscodec;
};
template < size_t PARITY, size_t N, typename SERIAL >
const ezpwd::RS<N-1,N-1-PARITY>
corrector<PARITY,N,SERIAL>::rscodec;
} // namespace ezpwd
template < size_t PARITY, size_t N, typename SERIAL >
std::ostream &operator<<(
std::ostream &lhs,
const ezpwd::corrector<PARITY,N,SERIAL>
&rhs )
{
return rhs.output( lhs );
}
#endif // _EZPWD_CORRECTOR

9
src/ezpwd/definitions Normal file
View File

@@ -0,0 +1,9 @@
//
// C++ Definitions -- include once in a single C++ compilation unit
//
#ifndef _EZPWD_DEFINITIONS
#define _EZPWD_DEFINITIONS
#include "serialize_definitions"
#endif // _EZPWD_DEFINITIONS

725
src/ezpwd/ezcod Normal file
View File

@@ -0,0 +1,725 @@
/*
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
*
* Copyright (c) 2014, Hard Consulting Corporation.
*
* Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. See the LICENSE file at the top of the
* source tree. Ezpwd Reed-Solomon is also available under Commercial license. c++/ezpwd/rs_base
* is redistributed under the terms of the LGPL, regardless of the overall licensing terms.
*
* Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*/
#ifndef _EZPWD_EZCOD
#define _EZPWD_EZCOD
#include <math.h> // M_PI
#include <cmath>
#include <cctype>
#include <cstdint>
#include <ezpwd/rs>
#include <ezpwd/output>
#include <ezpwd/serialize>
//
// EZCOD 3:10 location code w/ Reed-Solomon Error Correction, and average 3m accuracy
//
// - each successive symbol provides greater precision
// - codes nearby each-other are identical in leading characters
// - average 3m precision achieved in 9 symbols
// - more than 4 base-10 digits of precision in both lat and lon after the decimal
// - from 1 to 3 symbols of Reed-Solomon parity
// - 1 parity symbol supplies validation w/ strength equivalent to a check character
// - 2 parity symbols provides correction of 1 lost symbol (no errors)
// - 3 parity symbols provides correction of any 1 error, with verification,
// or recovery of up to any 3 lost symbols (with no other errors)
//
//
// To achieve at least 4 decimal digits of precision after the decimal point, we must have
// defined lat to within 1 part in 1,800,000, and lon to within 1 part in 3,600,000. As each symbol
// supplies bits, we'll refine the computed lat/lon further, reducing the outstanding fraction of
// "parts" yet to be defined.
//
// bits
// symbols latitude longitude
// bits mul parts bits mul parts
// 1 2 4 4 3 8 8
// 2 2 4 16 3 8 64
// 3 3 8 128 2 4 256 // not quite integer lat/lon accuracy
//
// 4 2 4 512 3 8 2,048
// 5 3 8 4,096 2 4 8,192
// 6 2 4 16,384 3 8 65,536
//
// 7 3 8 131,072 2 4 262,144
// 8 2 4 524,288 3 8 2,097,152
// 9 3 8 4,194,304 2 4 8,388,608 parts resolution in 3:10 code
// over [-90, 90] over [-180,180] yields ~3m resolution
//
// vs. 1,800,000 3,600,000 parts resolution in 10:10 code
// over [-90, 90] over [-180,180] yields ~10m resolution
//
// Therefore, within 9 symbols we define lat and lon with better than double the precision of
// 10:10 code's 4 decimal digits after the decimal point. This yields an approximate lineal
// precision of 40,075,000m / 8,388,608 == ~5m in both dimensions at the equator, vs. 40,075,000m /
// 3,600,000 == ~11m for 10:10 codes.
//
// The 10:10 code provides a single check character, which provides about P(1-1/32) certainty
// that the provided code is correct. With EZCOD 3:10/11/12 codes, we provide varying levels of
// detection/correction strength.
//
// - 1 parity symbol: act as a check character (like 10:10 codes), or provide 1 symbol of erasure
// (lost symbol) recovery with no excess parity for validation.
//
// - 2 parity symbols: provide 1 symbol of erasure correction (w/ no other errors) with 1 excess parity
// symbol for validation, or 1 symbol of error detection with no excess parity for validation.
//
// - 3 parity symbols: correct 1 error anywhere w/ 1 excess parity symbol for validation, or up
// to 3 erasures with no excess parity for validation.
//
// Therefore, we'll provide Reed-Solomon RS(31,28-30) error correction (5 bit symbols,
// indicating 31 symbols in the field, and from 1 to 3 roots, therefore up to 28 data symbols in the
// field) over the 9 lat/lon data symbols.
//
//
// MINIMIZING ERROR
//
// Each input lat/lon coordinate will be effectively truncated by the encoding procedure to the
// level of precision (parts) encoded by each symbol. Subsequent symbols then add their (smaller)
// parts to increase precision.
//
// After the last symbol, we know that the actual input coordinates where somewhere
// within the rectangle:
//
// [0,0] -> [0,lon_precision] -> [lat_precision,lon_precision] -> [lat_precision,0]
//
// At first glance, the best way is to perform rounding instead of truncation on ecoding, by
// simply adding 1/2 of the precision. Then, the unmodified output lat/lon decoded represents the
// point nearest actual input coordinate. However, this is NOT ideal. Remember -- the decoding may
// not have access to all the symbols! We want to minimize the error even if only some of the
// symbols are available. Thus, we must apply a correction on decoding.
//
// One way gain rounding instead of truncation on decoding is, after adding the last symbol's
// precision, to add 50% of the value represented by the first bit of the next (missing) symbol's
// precision parts. This would be analogous to receiving the first 2 digits of a 3 digit number:
//
// original: 123
// received: 12_
// range: [120,130)
// guessed: 125 (add 1/2 of the parts represented by the missing digit)
//
// If this is done, then the resulting coordinate would be in the middle of the rectangle of
// possible input lat/lon values that could have resulted in the encoded value. This also works if
// we don't receive and decode all of the symbols; We'll end up with a lat/lon in the middle of the
// (large) rectangle of possible input coordinates.
//
namespace ezpwd {
class ezcod_base {
public:
double latitude; // [-90,+90] angle, degrees
double latitude_error; // total error bar, in meters
double longitude; // [-180,180]
double longitude_error; // total error bar, in meters
double accuracy; // linear accuracy radius, in meters
int confidence; // % parity in excess of last decode
double certainty; // and the associated probability
explicit ezcod_base(
double _lat = 0,
double _lon = 0 )
: latitude( _lat )
, latitude_error( 0 )
, longitude( _lon )
, longitude_error( 0 )
, accuracy( 0 )
, confidence( 100 )
, certainty( 1 )
{
;
}
virtual ~ezcod_base()
{
;
}
typedef std::pair<unsigned char, unsigned char>
symbols_t;
virtual symbols_t symbols()
const
= 0;
virtual std::ostream &output(
std::ostream &lhs )
const
= 0;
virtual std::string encode(
unsigned _preci = 0 ) // override precision
const
= 0;
virtual int decode(
const std::string &_ezcod )
= 0;
};
} // namespace ezpwd
inline std::ostream &operator<<(
std::ostream &lhs,
const ezpwd::ezcod_base
&rhs )
{
return rhs.output( lhs );
}
namespace ezpwd {
//
// ezcod<P,L> -- defaults to 1 PARITY and 9 location symbols (3m) of PRECISION
//
template < unsigned P=1, unsigned L=9 >
class ezcod
: public ezcod_base {
private:
typedef std::array<symbols_t, 12>
bits_t;
static const bits_t bits;
typedef std::array<std::pair<uint32_t, uint32_t>, 12>
parts_t;
static const parts_t parts;
#if defined( DEBUG )
public:
#endif
static const ezpwd::RS<31,31-P>
rscodec;
public:
static constexpr const unsigned PARITY = P; // specified symbols of R-S parity
static constexpr const unsigned PRECISION = L; // default symbols of location precision
static constexpr const unsigned CHUNK = 3; // default chunk size
static constexpr const char SEP_NONE = -1;
static constexpr const char SEP_DEFAULT = 0;
static constexpr const char SEP_DOT = '.';
static constexpr const char SEP_BANG = '!';
static constexpr const char SEP_SPACE = ' ';
static constexpr const char CHK_NONE = -1;
static constexpr const char CHK_DEFAULT = 0;
static constexpr const char CHK_DASH = '-';
static constexpr const char CHK_SPACE = ' ';
unsigned precision;
unsigned chunk; // Location symbol chunk sizes
char separator; // Separator between location and parity symbols
char space; // Fill space between location symbol chunks
//
// ezcod<P,L>() -- supply non-defaults for location precision, chunk size, etc.
//
explicit ezcod(
double _lat = 0,
double _lon = 0,
unsigned _preci = 0,
unsigned _chunk = 0,
char _seper = 0,
char _space = 0 )
: ezcod_base( _lat, _lon )
, precision( _preci ? _preci : PRECISION )
, chunk( _chunk ? _chunk : CHUNK )
, separator( _seper )
, space( _space )
{
if ( P < 1 )
throw std::runtime_error( "ezpwd::ezcod:: At least one parity symbol must be specified" );
if ( precision < 1 || precision > bits.max_size() )
throw std::runtime_error( std::string( "ezpwd::ezcod:: Only 1-" )
+ std::to_string( bits.max_size() )
+ " location symbol may be specified" );
}
explicit ezcod(
const std::string &_ezcod,
unsigned _preci = 0,
unsigned _chunk = 0,
char _seper = 0,
char _space = 0 )
: ezcod( 0, 0, _preci, _chunk, _seper, _space )
{
decode( _ezcod );
}
virtual ~ezcod()
{
;
}
//
// symbols -- return working parity and location precision
//
virtual ezcod_base::symbols_t
symbols()
const
{
return ezcod_base::symbols_t( P, precision );
}
virtual std::ostream &output(
std::ostream &lhs )
const
{
std::streamsize prec = lhs.precision();
std::ios_base::fmtflags
flg = lhs.flags();
lhs.precision( 10 );
std::string uni = "m ";
double acc = accuracy;
double dec = 2;
if ( acc > 1000 ) {
uni = "km";
acc /= 1000;
} else if ( acc < 1 ) {
uni = "mm";
acc *= 1000;
}
if ( acc >= 100 )
dec = 0;
else if ( acc >= 10 )
dec = 1;
lhs << encode( precision )
<< " (" << std::setw( 3 ) << confidence
<< "%) == " << std::showpos << std::fixed << std::setprecision( 10 ) << std::setw( 15 ) << latitude
<< ", " << std::showpos << std::fixed << std::setprecision( 10 ) << std::setw( 15 ) << longitude
<< " +/- " << std::noshowpos << std::fixed << std::setprecision( dec ) << std::setw( 6 ) << acc << uni;
lhs.precision( prec );
lhs.flags( flg );
return lhs;
}
//
// encode() -- encode the lat/lon to 'precision' symbols EZCOD representation
//
virtual std::string encode(
unsigned _preci = 0 ) // override precision
const
{
// Convert lat/lon into a fraction of number of parts assigned to each
double lat_frac= ( latitude + 90 ) / 180;
if ( lat_frac < 0 || lat_frac > 1 )
throw std::runtime_error( "ezpwd::ezcod::encode: Latitude not in range [-90,90]" );
double lon_frac= ( longitude + 180 ) / 360;
if ( lon_frac < 0 || lon_frac > 1 )
throw std::runtime_error( "ezpwd::ezcod::encode: Longitude not in range [-180,180]" );
if ( _preci == 0 )
_preci = precision;
if ( _preci < 1 || _preci > bits.max_size() )
throw std::runtime_error( std::string( "ezpwd::ezcod:: Only 1-" )
+ std::to_string( bits.max_size() )
+ " location symbol may be specified" );
// Compute the integer number of lat/lon parts represented by each coordinate, for the
// specified level of precision, and then truncate to the range [0,..._parts),
// eg. Latitude 90 --> 89.999...
uint32_t lat_parts = parts[_preci-1].first; // [ -90,90 ] / 4,194,304 parts in 9 symbols
uint32_t lon_parts = parts[_preci-1].second; // [-180,180] / 8,388,608 parts ''
uint32_t lat_rem = std::min( lat_parts-1, uint32_t( lat_parts * lat_frac ));
uint32_t lon_rem = std::min( lon_parts-1, uint32_t( lon_parts * lon_frac ));
// Initial loop condition; lat/lon multiplier is left at the base multiplier of the
// previous loop. Then, loop computing the units multiplier, and hten removing the most
// significant bits (multiples of the units multiplier). They will both reach 1
unsigned int lat_mult= lat_parts;
unsigned int lon_mult= lon_parts;
std::string res;
res.reserve( _preci // approximate result length
+ ( chunk && chunk < _preci
? _preci / chunk - 1
: 0 )
+ 1 + P );
for ( auto &b : bits ) {
unsigned char lat_bits= b.first;
unsigned char lon_bits= b.second;
lat_mult >>= lat_bits;
lon_mult >>= lon_bits;
if ( ! lat_mult || ! lon_mult )
break;
// Each set of bits represents the number of times the current multiplier (after
// division by the number of bits we're outputting) would go into the remainder.
// Eg. If _mult was 1024, and _rem is 123 and _bits is 3, we're going to put out
// the next 3 bits of the value 199. The last value ended removing all multiples of
// 1024. So, we first get the new multiplier: 1024 >> 3 == 128. So, we're
// indicating, as a 3-bit value, how many multiples of 128 there are in the value
// 199: 199 / 128 == 1, so the 3-bit value we output is 001
uint32_t lat_val = lat_rem / lat_mult;
lat_rem -= lat_val * lat_mult;
uint32_t lon_val = lon_rem / lon_mult;
lon_rem -= lon_val * lon_mult;
res += char( ( lat_val << lon_bits ) | lon_val );
}
// Add the R-S parity symbols and base-32 encode, add parity separator and chunk
rscodec.encode( res );
serialize::base32::encode( res );
switch( separator ) {
case SEP_NONE:
break;
case SEP_DOT: default:
res.insert( _preci, 1, SEP_DOT );
break;
case SEP_BANG:
case SEP_SPACE:
res.insert( _preci, 1, separator );
break;
}
if ( space != CHK_NONE && chunk && chunk < _preci ) {
for ( unsigned c = _preci / chunk - 1; c > 0; --c ) {
switch ( space ) {
case CHK_NONE:
break;
case CHK_SPACE: default:
res.insert( c * chunk, 1, CHK_SPACE );
break;
case CHK_DASH:
res.insert( c * chunk, 1, space );
break;
}
}
}
return res;
}
//
// deserialize -- Extract base-32, skip whitespace, mark invalid symbols as erasures
// validate -- Remove base-32 encoding, validate and remove parity, returning confidence
// decode -- Attempt to decode a lat/lon, returning the confidence percentage
//
// If data but no parity symbols are supplied, no error checking is performed, and the
// confidence returned will be 0%. No erasures within the supplied data are allowed (as
// there is no capacity to correct them), and an exception will be thrown.
//
// If parity is supplied, then erasures are allowed. So long as the total number of
// erasures is <= the supplied parity symbols, then the decode will proceed (using the
// parity symbols to fill in the erasures), and the returned confidence will reflect the
// amount of unused parity capacity. Each erasure consumes one parity symbol to repair.
//
// We'll allow question-mark or any of the slash characters: "_/\?" to indicate an
// erasure. Either of the "!." symbol may be used to indicates the split between location
// symbols and parity symbols, and must be in a position that corresponds to the indicated
// number of location (this->precision) and parity 'P' symbols. Whitespace symbols and dash
// are ignored: " -".
//
// Thus, an EZCOD like "R3U 1JU QUY!0" may only be decoded by an ezcod<P=1>. Without
// the "!" or ".", it could be an ezcod<P=2> w/precision == 8 -- there's no way to know for
// sure. If no explicit position-parity separator is given, then we assume the default:
// this->precision location symbols, then up to P parity symbols. If additional parity
// symbols are supplied after the separator, then However, an ezcod...<P=3>
//
// If an explicit "!" or "." separator IS provided, then we will attempt to decode the
// position with the given number of position symbols, and up to P parity symbols.
//
// NOTE
//
// Due to a perhaps unexpected feature of R-S codewords, a codeword with MORE parity
// can be successfully decoded by an R-S codec specifying LESS parity symbols. It so
// happens that the data plus (excess) parity + (remaining) parity is STILL a valid codeword
// (so long as the R-S Galois parameters are identical).
//
// Therefore, EZCODs with more parity are accepted by EZCOD parsers configured for less
// parity. Of course, they will have less error/erasure correction strength -- using the
// correctly configured EZCOD codec expecting more R-S parity will maximize the value of all
// the supplied parity.
//
// The full amount of parity (ie. everything after the location/parity separator) is
// discarded in all cases, before the EZCOD location is decoded.
//
private:
unsigned deserialize(
std::string &dec,
std::vector<int> &erasure,
std::vector<char> &invalid )
const
{
serialize::base32::decode( dec, &erasure, &invalid );
// The special symbol '!' or '.' indicates the end of the EZCOD location symbols and the
// beginning of parity: ensure the symbol counts are consistent with the encoding. By
// default the parity symbols begin at offset precision. If we see more than precision
// symbols, we assume that the Lth and subsequent symbols are parity. If a
// location/parity separator is provided, it must be at position this->precision!
// Return offset of start of parity in codeword.
unsigned parbeg = this->PRECISION; // Parity begins after Location, by default
for ( unsigned i = 0; i < invalid.size(); ++i ) {
switch ( invalid[i] ) {
case '!': case '.':
// Remember the offset of the first parity symbol (it'll be in the position of
// the last '!' or '.' symbol we're about to erase), and adjust the indices of
// any erasures following.
parbeg = erasure[i];
dec.erase( parbeg, 1 );
invalid.erase( invalid.begin() + i );
erasure.erase( erasure.begin() + i );
for ( unsigned j = i; j < erasure.size(); ++j )
erasure[j] -= 1;
break;
case '_': case '/': case '\\': case '?':
break;
default:
throw std::runtime_error( std::string( "ezpwd::ezcod::decode: invalid symbol presented: '" )
+ invalid[i] + "'" );
}
}
#if defined( DEBUG ) && DEBUG >= 1
std::cout << " --> 0x" << std::vector<uint8_t>( dec.begin(), dec.begin() + std::min( size_t( parbeg ), dec.length()) )
<< " + 0x" << std::vector<uint8_t>( dec.begin() + std::min( size_t( parbeg ), dec.length()),
dec.begin() + dec.length() )
<< " parity" << std::endl;
#endif
return parbeg;
}
int validate(
std::string &dec )
const
{
// Compute and return validity (which may later be assigned to this->confidence)
int validity = 0; // if no R-S parity provided
#if defined( DEBUG ) && DEBUG >= 1
std::cout << *this << " validate( " << dec << " ) ";
#endif
std::vector<int> erasure;
std::vector<char> invalid;
unsigned parbeg = deserialize( dec, erasure, invalid );
if ( dec.size() > parbeg || erasure.size() > 0 ) {
// Some R-S parity symbol(s) were provided (or erasures were marked). See if we can
// successfully decode/correct, or (at least) use one parity symbol as a check
// character. If we identify more erasures than R-S parity, we must fail; we can't
// recover the data. This will of course be the case if we have *any* erasures in
// the data, and no parity.
unsigned parity = 0;
if ( dec.size() > parbeg )
parity = dec.size() - parbeg;
while ( dec.size() < parbeg + P ) {
erasure.push_back( dec.size() );
dec.resize( dec.size() + 1 );
}
#if defined( DEBUG ) && DEBUG >= 2
std::cout << " --> erasures: " << erasure.size() << " vs. parity: " << parity
<< ": " << std::vector<uint8_t>( dec.begin(), dec.end() ) << std::endl;
#endif
if ( erasure.size() > parity ) {
// We cannot do R-S decoding; not enough parity symbols to even cover erasures.
// If parity symbol(s) were provided ('parity' > 0), and all erasures were due the
// missing remaining parity symbols, we can use the existing parity symbol(s) as
// "check character(s)", by simply re-encoding the supplied non-parity data, and
// see if the generated parity symbol(s) match the supplied parity. This has
// basically the same strength as the 10:10 code's check character.
if ( parity + erasure.size() == P ) {
// All erasures must be at end, in remaining parity symbols!
std::string chk( dec.begin(), dec.begin() + parbeg );
rscodec.encode( chk );
// each parity symbol provided must match the corresponding encoded chk symbol
for ( unsigned i = 0; i < parity; ++i )
if ( dec[parbeg+i] != chk[parbeg+i] )
throw std::runtime_error( "ezpwd::ezcod::decode: Error correction failed; check character mismatch" );
// Check character(s) matched; erasure.size()/P of confidence gone
validity = ezpwd::strength<P>( erasure.size(), erasure, erasure );
} else
throw std::runtime_error( "ezpwd::ezcod::decode: Error correction failed; too many erasures" );
} else {
// We can try R-S decoding; we have (at least) enough parity to try to recover
// any missing symbol(s).
std::vector<int>position;
int corrects= rscodec.decode( dec, erasure, &position );
if ( corrects < 0 )
throw std::runtime_error( "ezpwd::ezcod::decode: Error correction failed; R-S decode failed" );
// Compute confidence, from spare parity capacity. Since R-S decode will not
// return the position of erasures that turn out (by accident) to be correct,
// but they have consumed parity capacity, we re-add them into the correction
// position vector. If the R-S correction reports more corrections than the
// parity can possibly have handled correctly, (eg. 2 reported erasures and an
// unexpected error), then the decode is almost certainly incorrect; fail.
validity = ezpwd::strength<P>( corrects, erasure, position );
if ( validity < 0 )
throw std::runtime_error( "ezpwd::ezcod::decode: Error correction failed; R-S decode overwhelmed" );
}
if ( dec.size() > parbeg )
dec.resize( parbeg ); // Discard any parity symbols
}
return validity;
}
public:
virtual int decode( const std::string &str )
{
// Decode the R-S encoding, computing the confidence. Will raise an exception on any
// error. Don't change this->confidence, this->latitude, ... until there is no longer a
// chance of exception.
std::string decoded( str );
int validity= validate( decoded );
// Unpack the supplied location data; we'll take as much as we are given, up to the
// maximum possible 12 symbols supported (9 symbols yielding ~3m resolution).
uint32_t lat_tot = 0;
uint32_t lon_tot = 0;
uint32_t lat_mult= 1;
uint32_t lon_mult= 1;
auto di = decoded.begin();
for ( auto &b : bits ) {
if ( di == decoded.end() )
break;
unsigned char c = *di++;
unsigned char lat_bits= b.first;
unsigned char lon_bits= b.second;
uint32_t lat_val = c >> lon_bits;
uint32_t lon_val = c & (( 1 << lon_bits ) - 1 );
lat_mult <<= lat_bits;
lat_tot <<= lat_bits;
lat_tot += lat_val;
lon_mult <<= lon_bits;
lon_tot <<= lon_bits;
lon_tot += lon_val;
}
// Convert the sum of lat/lon parts back into degrees, and round the (truncated) value
// to the middle of the error rectangle. This allows us to minimize error even if we
// didn't have access to all of the origin symbols to decode. The absolute error bar as
// a proportional factor [0,1) for lat/lon is at most the scale of the last parts
// multiplier used. We'll use this later to compute the error in meters; for example,
// if the last value we added worked out to be worth units of 25m of the circumference,
// then we must now be within [0,25m) of the original point.
double lat_err = 1.0 / lat_mult;
double lon_err = 1.0 / lon_mult;
latitude = 180 * ( double( lat_tot ) / lat_mult + lat_err / 2 ) - 90;
longitude = 360 * ( double( lon_tot ) / lon_mult + lon_err / 2 ) - 180;
// Remember the decoded location precision for future encoding (overrides the default).
// Compute the certainty probability (0.0,1.0] given the number of parity symbols in
// excess: Given a base-32 symbol: 1 - 1 / ( 32 ^ P ) where P is the number of
// unconsumed parity.
precision = decoded.size();
confidence = validity;
certainty = 0.0;
if ( PARITY * confidence / 100 )
certainty = 1.0 - 1.0 / std::pow( double( 32.0 ),
double( PARITY * confidence / 100 ));
// Compute the resolution error (in m.) of the decoded lat/lon and compute the minimum
// accuracy -- the radius of the circle around the computed latitude/longitude, inside
// which the original latitude/longitude must have been.
//
// original latitude error bar
// \ /
// o -
// | longitude error bar
// | /
// |--x--|
// /|
// / |
// computed -
//
// The maximum distance is the length of the diagonal of the error rectangle defined by
// 1/2 the latitude/longitude error bars.
//
double lon_circ= 1 * M_PI * 6371000;
double lat_circ= 2 * M_PI * 6371000 * std::cos( latitude * M_PI / 180 );
latitude_error = lat_err * lon_circ;
longitude_error = lon_err * lat_circ;
accuracy = sqrt( latitude_error / 2 * latitude_error / 2
+ longitude_error / 2 * longitude_error / 2 );
return confidence;
}
}; // class ezcod
//
// ezcod::rscodec -- Reed-Solomon parity codec
// ezcod::bits -- distribution of lat/lon precision in each code symbol
//
// Quickly establishes an extra bit of precision for Longitude, and then evenly distributes
// future precision between lat/lon, always maintaining extra precision for Longitude.
//
template < unsigned P, unsigned L >
const ezpwd::RS<31,31-P> ezcod<P,L>::rscodec;
// Number of lat/lon bits represented for each location symbol
template < unsigned P, unsigned L >
const typename ezcod<P,L>::bits_t
ezcod<P,L>::bits = {
{
// bits per symbol lat lon
ezcod<P,L>::bits_t::value_type( 2, 3 ),
ezcod<P,L>::bits_t::value_type( 2, 3 ),
ezcod<P,L>::bits_t::value_type( 3, 2 ),
// -- --
// 7 8
ezcod<P,L>::bits_t::value_type( 2, 3 ),
ezcod<P,L>::bits_t::value_type( 3, 2 ),
ezcod<P,L>::bits_t::value_type( 2, 3 ),
// -- --
// 14 16
ezcod<P,L>::bits_t::value_type( 3, 2 ),
ezcod<P,L>::bits_t::value_type( 2, 3 ),
ezcod<P,L>::bits_t::value_type( 3, 2 ),
// -- --
// 22 23
ezcod<P,L>::bits_t::value_type( 2, 3 ),
ezcod<P,L>::bits_t::value_type( 3, 2 ),
ezcod<P,L>::bits_t::value_type( 2, 3 ),
// -- --
// 29 31
}
};
// Total number of parts that lat/lon is subdivided into, for that number of location symbols.
template < unsigned P, unsigned L >
const typename ezcod<P,L>::parts_t
ezcod<P,L>::parts = {
{
// parts per symbol lat parts lon parts lat lon bits
ezcod<P,L>::parts_t::value_type( 1UL << 2, 1UL << 3 ), // 2, 3
ezcod<P,L>::parts_t::value_type( 1UL << 4, 1UL << 6 ), // 2, 3
ezcod<P,L>::parts_t::value_type( 1UL << 7, 1UL << 8 ), // 3, 2
// -- --
// 7 8
ezcod<P,L>::parts_t::value_type( 1UL << 9, 1UL << 11 ), // 2, 3
ezcod<P,L>::parts_t::value_type( 1UL << 12, 1UL << 13 ), // 3, 2
ezcod<P,L>::parts_t::value_type( 1UL << 14, 1UL << 16 ), // 2, 3
// -- --
// 14 16
ezcod<P,L>::parts_t::value_type( 1UL << 17, 1UL << 18 ), // 3, 2
ezcod<P,L>::parts_t::value_type( 1UL << 19, 1UL << 21 ), // 2, 3
ezcod<P,L>::parts_t::value_type( 1UL << 22, 1UL << 23 ), // 3, 2
// -- --
// 22 23
ezcod<P,L>::parts_t::value_type( 1UL << 24, 1UL << 26 ), // 2, 3
ezcod<P,L>::parts_t::value_type( 1UL << 27, 1UL << 28 ), // 3, 2
ezcod<P,L>::parts_t::value_type( 1UL << 29, 1UL << 31 ), // 2, 3
// -- --
// 29 31
}
};
} // namespace ezpwd
#endif // _EZPWD_EZCOD

344
src/ezpwd/output Normal file
View File

@@ -0,0 +1,344 @@
#ifndef _EZPWD_OUTPUT
#define _EZPWD_OUTPUT
#include <cstdio>
#include <streambuf>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>
#include <set>
#include <map>
#include <vector>
//
// ezpwd::hexchr -- escape/hexify char c, output using func/meth f, in width w >= 2
// ezpwd::hexify -- hexify something that can be converted to an unsigned char
// ezpwd::hexout -- hexify each element in the range (beg,end], limited by stream's width
//
// std::ostream << ezpwd::hexify( c ) // output any char escaped/hex
// std::ostream << ezpwd::hexout( beg, end ) // output any char iterator to ostream
// std::ostream << std::vector<unsigend char>
// std::ostream << std::array<unsigend char, N>
// ezpwd::hexchr( c, [](unsigned char c){...;} )// output escaped/hex char via functor
// ezpwd::hexout( beg, end, FILE* ) // output any char iterator to FILE*
//
// Output unprintable unsigned char data in hex, escape printable/whitespace data.
//
namespace ezpwd {
struct hexify {
unsigned char c;
std::streamsize w;
explicit hexify(
unsigned char _c,
std::streamsize _w = 2 )
: c( _c )
, w( _w )
{ ; }
explicit hexify(
char _c,
std::streamsize _w = 2 )
: c( (unsigned char)_c )
, w( _w )
{ ; }
};
struct hexstr {
const std::string &s;
explicit hexstr(
const std::string &_s )
: s( _s )
{ ; }
};
template <typename F> // a functor taking a char
void
hexchr( unsigned char c, F f = []( unsigned char c ) { std::cout.put( c );}, size_t w = 2 )
{
for ( ; w > 2; --w )
f( ' ' );
if ( std::isprint( c ) || std::isspace( c )
|| c == '\0' || c == '\a' || c == '\b' || c == 0x1B ) { // '\e' is not standard
switch ( c ) {
case 0x00: f( '\\' ); f( '0' ); break; // NUL
case 0x07: f( '\\' ); f( 'a' ); break; // BEL
case 0x08: f( '\\' ); f( 'b' ); break; // BS
case 0x09: f( '\\' ); f( 't' ); break; // HT
case 0x0A: f( '\\' ); f( 'n' ); break; // LF
case 0x0B: f( '\\' ); f( 'v' ); break; // VT
case 0x0C: f( '\\' ); f( 'f' ); break; // FF
case 0x0D: f( '\\' ); f( 'r' ); break; // CR
case 0x1B: f( '\\' ); f( 'e' ); break; // ESC
case '\"': f( '\\' ); f( '"' ); break; // "
case '\'': f( '\\' ); f( '\''); break; // '
case '\\': f( '\\' ); f( '\\'); break; // '\'
default: f( ' ' ); f( c ); // space, any other printable character
}
} else {
f( "0123456789ABCDEF"[( c >> 4 ) & 0x0f ] );
f( "0123456789ABCDEF"[( c >> 0 ) & 0x0f ] );
}
}
inline
std::ostream &operator<<(
std::ostream &lhs,
const ezpwd::hexify&rhs )
{
ezpwd::hexchr( rhs.c, [ &lhs ]( unsigned char c ) { lhs.put( c ); }, rhs.w );
return lhs;
}
template < typename iter_t >
inline
std::ostream &hexout(
std::ostream &lhs,
const iter_t &beg,
const iter_t &end )
{
std::streamsize wid = lhs.width( 0 );
int col = 0;
for ( auto i = beg; i != end; ++i ) {
if ( wid && col == wid ) {
lhs << std::endl;
col = 0;
}
lhs << hexify( *i );
++col;
}
return lhs;
}
template < typename iter_t >
inline
std::FILE *hexout(
const iter_t &beg,
const iter_t &end,
std::FILE *lhs )
{
for ( auto i = beg; i != end; ++i ) {
ezpwd::hexchr( *i, [ lhs ]( unsigned char c ) { std::fputc( c, lhs ); } );
}
return lhs;
}
inline
std::ostream &operator<<(
std::ostream &lhs,
const ezpwd::hexstr&rhs )
{
return ezpwd::hexout( lhs, rhs.s.begin(), rhs.s.end() );
}
} // namespace ezpwd
namespace std {
template < size_t S >
inline
std::ostream &operator<<(
std::ostream &lhs,
const std::array<unsigned char,S>
&rhs )
{
return ezpwd::hexout( lhs, rhs.begin(), rhs.end() );
}
inline
std::ostream &operator<<(
std::ostream &lhs,
const std::vector<unsigned char>
&rhs )
{
return ezpwd::hexout( lhs, rhs.begin(), rhs.end() );
}
//
// <ostream&> << pair<T,U>
// <ostream&> << set<T> -- sorted by T
// <ostream&> << map<T,U> -- sorted by T (key)
// <ostream&> << vector<T>
//
// Handle output of various container types.
//
// Output pairs and sets of pairs, respecting specified widths (as appropriate). For example
// a set of pairs of integeters 's', if output as "... << std::setw( 13 ) << s;", would yield:
//
// ( 1, 2) ( 3, 4) ...
//
template <class T, class U>
std::ostream &operator<<(
std::ostream &lhs,
const std::pair<T,U> &rhs )
{
std::streamsize w = std::max( std::streamsize( 0 ),
std::streamsize( lhs.width() - 3 ));
lhs << std::setw( 0 )
<< '(' << std::setw( w / 2 ) << rhs.first
<< ',' << std::setw( w - w / 2 ) << rhs.second
<< ')';
return lhs;
}
template <class T>
std::ostream &operator<<(
std::ostream &lhs,
const std::set<T> &rhs )
{
std::streamsize w = lhs.width(); // If width is set, use if for each item
for ( typename std::set<T>::const_iterator
si = rhs.begin()
; si != rhs.end()
; ++si ) {
if ( si != rhs.begin())
lhs << ' ';
lhs << std::setw( w ) << *si;
}
lhs << std::setw( 0 ); // If container empty, must clear
return lhs;
}
template <class T, class U>
std::ostream &operator<<(
std::ostream &lhs,
const std::map<T,U>&rhs )
{
std::streamsize w = lhs.width(); // If width is set, use if for each item
std::vector<T> key;
for ( typename std::map<T,U>::const_iterator
mi = rhs.begin()
; mi != rhs.end()
; ++mi )
key.push_back( mi->first );
std::sort( key.begin(), key.end() );
for ( typename std::vector<T>::const_iterator
ki = key.begin()
; ki != key.end()
; ++ki ) {
if ( ki != key.begin())
lhs << ' ';
lhs << std::setw( w ) << *rhs.find( *ki );
}
lhs << std::setw( 0 ); // If container empty, must clear
return lhs;
}
template <class T>
std::ostream &operator<<(
std::ostream &lhs,
const std::vector<T> &rhs )
{
for( size_t i = 0; i < rhs.size(); ++i ) {
if ( i )
lhs << ", ";
lhs << rhs[i];
}
return lhs;
}
} // namespace std
//
// ezpwd::buf_t -- describe a C string buffer, to allow C++ output operations
// ezpwd::streambuf_to_buf_t -- output charcters, always NUL terminated
//
// <buf_t> << ... -- Copy the <string> into the C <char*,size_t> buffer, always NUL terminating
//
// Copies <string> contents into buffer, and always NUL-terminates. Returns advanced buf_t (NOT
// including the terminating NUL, suitable for repeating ... << <string> operations.
//
// std::ostream( &<streambuf_to_buf_t> ) << ...
//
// Use standard ostream operations to send output to a C buffer, always NUL
// terminating, and never exceeding capacity.
//
namespace ezpwd {
typedef std::pair<char *,size_t>
buf_t;
class streambuf_to_buffer
: public std::streambuf {
private:
char *_buf;
size_t _siz;
public:
//
// streambuf_to_buf_t -- remember buf_t details
// ~streambuf_to_buf_t -- no virtual destructor required; nothing to clean up
//
streambuf_to_buffer(
char *buf,
size_t siz )
: _buf( buf )
, _siz( siz )
{
if ( _siz > 0 )
*_buf = 0;
}
explicit streambuf_to_buffer(
const buf_t &buf )
: streambuf_to_buffer( buf.first, buf.second )
{
;
}
//
// overflow -- Append c, always NUL terminating
//
virtual int overflow(
int c )
{
if ( _siz <= 1 )
return EOF; // No room for c and NUL; EOF
if ( EOF == c )
return 0; // EOF provided; do nothing
--_siz;
*_buf++ = char( c );
*_buf = 0;
return c;
}
}; // class streambuf_to_buffer
} // namespace ezpwd
namespace std {
inline
ezpwd::buf_t operator<<(
const ezpwd::buf_t &buf,
const std::string &str )
{
if ( buf.first && str.size() + 1 <= buf.second ) {
std::copy( str.begin(), str.end(), buf.first );
buf.first[str.size()] = 0;
return ezpwd::buf_t( buf.first + str.size(), buf.second - str.size() );
} else if ( buf.first && buf.second ) {
std::copy( str.begin(), str.begin() + buf.second - 1, buf.first );
buf.first[buf.second-1] = 0;
return ezpwd::buf_t( buf.first + buf.second - 1, 1 );
}
return buf; // NULL pointer or 0 size.
}
//
// <std::string> << ...
//
// Useful (but inefficient) standard output formatting directly to a std::string. Use only for
// testing code, for efficiency reasons...
//
template < typename T >
inline
std::string operator<<(
const std::string &lhs,
const T &rhs )
{
std::ostringstream oss;
oss << rhs;
return std::string( lhs ).append( oss.str() );
}
} // namespace std
#endif // _EZPWD_OUTPUT

168
src/ezpwd/rs Normal file
View File

@@ -0,0 +1,168 @@
/*
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
*
* Copyright (c) 2014, Hard Consulting Corporation.
*
* Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. See the LICENSE file at the top of the
* source tree. Ezpwd Reed-Solomon is also available under Commercial license. c++/ezpwd/rs_base
* is redistributed under the terms of the LGPL, regardless of the overall licensing terms.
*
* Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*/
#ifndef _EZPWD_RS
#define _EZPWD_RS
#include "rs_base"
//
// ezpwd::RS<SYMBOLS,PAYLOAD> -- Implements an RS(SYMBOLS,PAYLOAD) codec
// ezpwd::RS_CCSDS<...> -- CCSDS standard 8-bit R-S codec
//
// Support for Reed-Solomon codecs for symbols of 2 to 16 bits is supported. The R-S "codeword"
// for an N-bit symbol is defined to be 2^N-1 symbols in size. For example, for 5-bit symbols,
// 2^5-1 == 31, so the notation for defining an Reed-Solomon codec for 5-bit symbols is always:
// RS(31,PAYLOAD), where PAYLOAD is always some value less than 31. The difference is the number of
// "parity" symbols.
//
// For example, to define an RS codeword of 31 symbols w/ 4 symbols of parity and up to 27
// symbols of data, you would say: RS(31,27). Of course, you can supply smaller amounts of data;
// the balance is assumed to be NUL (zero) symbols.
//
namespace ezpwd {
//
// __RS( ... ) -- Define a reed-solomon codec
//
// @SYMBOLS: Total number of symbols; must be a power of 2 minus 1, eg 2^8-1 == 255
// @PAYLOAD: The maximum number of non-parity symbols, eg 253 ==> 2 parity symbols
// @POLY: A primitive polynomial appropriate to the SYMBOLS size
// @FCR: The first consecutive root of the Reed-Solomon generator polynomial
// @PRIM: The primitive root of the generator polynomial
//
# define __RS_TYP( TYPE, SYMBOLS, PAYLOAD, POLY, FCR, PRIM ) \
ezpwd::reed_solomon< \
TYPE, \
ezpwd::log_< (SYMBOLS) + 1 >::value, \
(SYMBOLS) - (PAYLOAD), FCR, PRIM, \
ezpwd::gfpoly< \
ezpwd::log_< (SYMBOLS) + 1 >::value, \
POLY >>
# define __RS( NAME, TYPE, SYMBOLS, PAYLOAD, POLY, FCR, PRIM ) \
__RS_TYP( TYPE, SYMBOLS, PAYLOAD, POLY, FCR, PRIM ) { \
NAME() \
: __RS_TYP( TYPE, SYMBOLS, PAYLOAD, POLY, FCR, PRIM )() \
{;} \
}
//
// RS<SYMBOLS, PAYLOAD> -- Standard partial specializations for Reed-Solomon codec type access
//
// Normally, Reed-Solomon codecs are described with terms like RS(255,252). Obtain various
// standard Reed-Solomon codecs using macros of a similar form, eg. RS<255, 252>. Standard PLY,
// FCR and PRM values are provided for various SYMBOL sizes, along with appropriate basic types
// capable of holding all internal Reed-Solomon tabular data.
//
// In order to provide "default initialization" of const RS<...> types, a user-provided
// default constructor must be provided.
//
template < size_t SYMBOLS, size_t PAYLOAD > struct RS;
template < size_t PAYLOAD > struct RS< 3, PAYLOAD> : public __RS( RS, uint8_t, 3, PAYLOAD, 0x7, 1, 1 );
template < size_t PAYLOAD > struct RS< 7, PAYLOAD> : public __RS( RS, uint8_t, 7, PAYLOAD, 0xb, 1, 1 );
template < size_t PAYLOAD > struct RS< 15, PAYLOAD> : public __RS( RS, uint8_t, 15, PAYLOAD, 0x13, 1, 1 );
template < size_t PAYLOAD > struct RS< 31, PAYLOAD> : public __RS( RS, uint8_t, 31, PAYLOAD, 0x25, 1, 1 );
template < size_t PAYLOAD > struct RS< 63, PAYLOAD> : public __RS( RS, uint8_t, 63, PAYLOAD, 0x43, 1, 1 );
template < size_t PAYLOAD > struct RS< 127, PAYLOAD> : public __RS( RS, uint8_t, 127, PAYLOAD, 0x89, 1, 1 );
template < size_t PAYLOAD > struct RS< 255, PAYLOAD> : public __RS( RS, uint8_t, 255, PAYLOAD, 0x11d, 1, 1 );
template < size_t PAYLOAD > struct RS< 511, PAYLOAD> : public __RS( RS, uint16_t, 511, PAYLOAD, 0x211, 1, 1 );
template < size_t PAYLOAD > struct RS< 1023, PAYLOAD> : public __RS( RS, uint16_t, 1023, PAYLOAD, 0x409, 1, 1 );
template < size_t PAYLOAD > struct RS< 2047, PAYLOAD> : public __RS( RS, uint16_t, 2047, PAYLOAD, 0x805, 1, 1 );
template < size_t PAYLOAD > struct RS< 4095, PAYLOAD> : public __RS( RS, uint16_t, 4095, PAYLOAD, 0x1053, 1, 1 );
template < size_t PAYLOAD > struct RS< 8191, PAYLOAD> : public __RS( RS, uint16_t, 8191, PAYLOAD, 0x201b, 1, 1 );
template < size_t PAYLOAD > struct RS<16383, PAYLOAD> : public __RS( RS, uint16_t, 16383, PAYLOAD, 0x4443, 1, 1 );
template < size_t PAYLOAD > struct RS<32767, PAYLOAD> : public __RS( RS, uint16_t, 32767, PAYLOAD, 0x8003, 1, 1 );
template < size_t PAYLOAD > struct RS<65535, PAYLOAD> : public __RS( RS, uint16_t, 65535, PAYLOAD, 0x1100b, 1, 1 );
template < size_t SYMBOLS, size_t PAYLOAD > struct RS_CCSDS;
template < size_t PAYLOAD > struct RS_CCSDS<255, PAYLOAD> : public __RS( RS_CCSDS, uint8_t, 255, PAYLOAD, 0x187, 112, 11 );
//
// strength<PARITY> -- compute strength (given N parity symbols) of R-S correction
//
// Returns a confidence strength rating, which is the ratio:
//
// 100 - ( errors * 2 + erasures ) * 100 / parity
//
// which is proportional to the number of parity symbols unused by the reported number of
// corrected symbols. If 0, then all parity resources were consumed to recover the R-S
// codeword, and we can have no confidence in the result. If -'ve, indicates more parity
// resources were consumed than available, indicating that the result is likely incorrect.
//
// Accounts for the fact that a signalled erasure may not be reported in the corrected
// position vector, if the symbol happens to match the computed value. Note that even if the
// error or erasure occurs within the "parity" portion of the codeword, this doesn't reduce the
// effective strength -- all symbols in the R-S complete codeword are equally effective in
// recovering any other symbol in error/erasure.
//
template < size_t PARITY >
int strength(
int corrected,
const std::vector<int>&erasures, // original erasures positions
const std::vector<int>&positions ) // all reported correction positions
{
// -'ve indicates R-S failure; all parity consumed, but insufficient to correct the R-S
// codeword. Missing an unknown number of additional required parity symbols, so just
// return -1 as the strength.
if ( corrected < 0 ) {
#if defined( DEBUG ) && DEBUG >= 2
std::cout
<< corrected << " corrections (R-S decode failure) == -1 confidence"
<< std::endl;
#endif
return -1;
}
if ( corrected != int( positions.size() ))
EZPWD_RAISE_OR_RETURN( std::runtime_error, "inconsistent R-S decode results", -1 );
// Any erasures that don't turn out to contain errors are not returned as fixed positions.
// However, they have consumed parity resources. Search for each erasure location in
// positions, and if not reflected, add to the corrected/missed counters.
int missed = 0;
for ( auto e : erasures ) {
if ( std::find( positions.begin(), positions.end(), e ) == positions.end() ) {
++corrected;
++missed;
#if defined( DEBUG ) && DEBUG >= 2
std::cout
<< corrected << " corrections (R-S erasure missed): " << e
<< std::endl;
#endif
}
}
int errors = corrected - erasures.size();
int consumed= errors * 2 + erasures.size();
int confidence= 100 - consumed * 100 / PARITY;
#if defined( DEBUG ) && DEBUG >= 2
std::cout
<< corrected << " corrections (R-S decode success)"
<< " at: " << positions
<< ", " << erasures.size() + missed
<< " erasures (" << missed
<< " unreported) at: " << erasures
<< ") ==> " << errors
<< " errors, and " << consumed << " / " << PARITY
<< " parity used == " << confidence
<< "% confidence"
<< std::endl;
#endif
return confidence;
}
} // namespace ezpwd
#endif // _EZPWD_RS

Some files were not shown because too many files have changed in this diff Show More