mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 09:17:20 -05:00
Compare commits
446 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3086b7852e | ||
|
|
8f6796d6f6 | ||
|
|
e9b1d76a98 | ||
|
|
afba623b20 | ||
|
|
9b92855b4e | ||
|
|
4138f92917 | ||
|
|
0bb9dd760c | ||
|
|
8b4f91ed8b | ||
|
|
a8fd2c7834 | ||
|
|
8670b99d69 | ||
|
|
6bd37821a9 | ||
|
|
215594a2a8 | ||
|
|
3f31d4ffe0 | ||
|
|
4a83c1beae | ||
|
|
67a78d8be0 | ||
|
|
14ba90a33f | ||
|
|
66fd3fd7bf | ||
|
|
d1ffcda819 | ||
|
|
f81575335e | ||
|
|
163db6b872 | ||
|
|
e6927d8115 | ||
|
|
3a19bf5583 | ||
|
|
181a6a5695 | ||
|
|
0d2244fac0 | ||
|
|
bd7d43de79 | ||
|
|
c37400bf81 | ||
|
|
bab139e2e4 | ||
|
|
6714761d8e | ||
|
|
467992ceff | ||
|
|
cb84f7469e | ||
|
|
ad4013d7d2 | ||
|
|
5b0e40e758 | ||
|
|
05a9268aef | ||
|
|
f3d2dbb4dd | ||
|
|
370d6be28e | ||
|
|
3224f0af37 | ||
|
|
5a73bcac8b | ||
|
|
af76c8de0d | ||
|
|
a3ecbe7414 | ||
|
|
4e04056f4b | ||
|
|
d8cf47b225 | ||
|
|
57d4c7afbe | ||
|
|
cb5c160fbf | ||
|
|
5390a52313 | ||
|
|
1d78e000da | ||
|
|
90700e7d28 | ||
|
|
0e9c2fc3dc | ||
|
|
4d24e93905 | ||
|
|
1505103c18 | ||
|
|
861ca3cb54 | ||
|
|
c228207588 | ||
|
|
09f92579ee | ||
|
|
a277707a4d | ||
|
|
41a236d244 | ||
|
|
837bfac5f5 | ||
|
|
2f05bfacfa | ||
|
|
f61c59ccc0 | ||
|
|
a682d9e882 | ||
|
|
88b3145cfd | ||
|
|
7199848f16 | ||
|
|
b7bb59dbba | ||
|
|
eb9152fb4e | ||
|
|
2ec4250e5b | ||
|
|
092da7b6d0 | ||
|
|
df6d7a6609 | ||
|
|
7f34ff9340 | ||
|
|
12e4b4cd6f | ||
|
|
6189ec95fa | ||
|
|
0f6140a30a | ||
|
|
5cb14c356b | ||
|
|
44cdfd912d | ||
|
|
ec4c4637df | ||
|
|
e5a7f8c41a | ||
|
|
03fb413750 | ||
|
|
f22d61df62 | ||
|
|
d20de53f91 | ||
|
|
3ba7a96bbd | ||
|
|
3e36e5e78d | ||
|
|
83edd7ff2b | ||
|
|
416edcbd59 | ||
|
|
65ce60f6b5 | ||
|
|
e461335b2e | ||
|
|
b9223e5207 | ||
|
|
805fdd7583 | ||
|
|
2b847c2270 | ||
|
|
6043b14c13 | ||
|
|
11b353089b | ||
|
|
b39d5d8401 | ||
|
|
5d44425b10 | ||
|
|
f45f4fe413 | ||
|
|
10ed2eac5a | ||
|
|
222cd706bf | ||
|
|
18714523c7 | ||
|
|
6c89c6260b | ||
|
|
48006c28c0 | ||
|
|
634eba522f | ||
|
|
81588b3201 | ||
|
|
1ca0f8a7d5 | ||
|
|
7133ad9dae | ||
|
|
caf21d2f0c | ||
|
|
2b0a85f99f | ||
|
|
3a3f717236 | ||
|
|
b4c7c60427 | ||
|
|
0f2dfe4dca | ||
|
|
f0cc508bf9 | ||
|
|
5b46fb8ab3 | ||
|
|
1a87f81f08 | ||
|
|
af6a5b39de | ||
|
|
2c839f7fd2 | ||
|
|
af0c0a4418 | ||
|
|
c495c90ef9 | ||
|
|
f8be80318b | ||
|
|
dbea859331 | ||
|
|
2463fe22f7 | ||
|
|
dc0c78e14d | ||
|
|
a2ac82c470 | ||
|
|
c402dfff96 | ||
|
|
3f586d9c98 | ||
|
|
4ab74a6bf0 | ||
|
|
d89507ebde | ||
|
|
806fe955cb | ||
|
|
c82e028032 | ||
|
|
357f59b290 | ||
|
|
cbd3d98151 | ||
|
|
5a39fe65db | ||
|
|
37c61aa5cb | ||
|
|
74d08ffaa6 | ||
|
|
171f372a4a | ||
|
|
3924c70386 | ||
|
|
656d7b2add | ||
|
|
66ec027bd2 | ||
|
|
6ad98fbf69 | ||
|
|
9b4e790095 | ||
|
|
e69451b0d7 | ||
|
|
1d30d5ec32 | ||
|
|
710e76d0ab | ||
|
|
994e728bfb | ||
|
|
2fd250b357 | ||
|
|
ed68f41f0e | ||
|
|
16e84ae104 | ||
|
|
4f1ecda2ad | ||
|
|
685c8aebe4 | ||
|
|
6de3e6c9f3 | ||
|
|
a848f9c0fe | ||
|
|
7d6c3dc67c | ||
|
|
2263ddae63 | ||
|
|
f8b9d47c6d | ||
|
|
064cc21b2b | ||
|
|
56da871ce2 | ||
|
|
ddc2a52a33 | ||
|
|
ec9c220ad7 | ||
|
|
02a27111d3 | ||
|
|
4c3a3e0959 | ||
|
|
ea40e3e3b7 | ||
|
|
ab873d3821 | ||
|
|
3cd1eefad8 | ||
|
|
8b48328431 | ||
|
|
2c842f5115 | ||
|
|
4ad5a8f9dd | ||
|
|
33cbad7618 | ||
|
|
504ee431b5 | ||
|
|
53fca195ba | ||
|
|
12dbd90509 | ||
|
|
2339cee016 | ||
|
|
4c44a47c61 | ||
|
|
11d4b2b330 | ||
|
|
cb57478fcc | ||
|
|
672e5cbd44 | ||
|
|
c1eb511855 | ||
|
|
2ccc27c03b | ||
|
|
43c1c9823b | ||
|
|
a3530c58a0 | ||
|
|
48ddba2ab8 | ||
|
|
9b554dfd93 | ||
|
|
edf465d03c | ||
|
|
e7a477dc88 | ||
|
|
7c461844ad | ||
|
|
ab29dd8e6a | ||
|
|
800fc3101e | ||
|
|
13ec6855f8 | ||
|
|
6a649376fc | ||
|
|
1ee3dba499 | ||
|
|
94487fd1c0 | ||
|
|
4af78289b2 | ||
|
|
5ee669aa18 | ||
|
|
6afb0d718f | ||
|
|
ea1f0bdf2f | ||
|
|
184b8b6a39 | ||
|
|
0fc9380b00 | ||
|
|
9f330e7902 | ||
|
|
d756ab6574 | ||
|
|
beb96f73db | ||
|
|
f053e53e76 | ||
|
|
c994f00e5d | ||
|
|
5834914e91 | ||
|
|
2b71652b23 | ||
|
|
d44c050ca4 | ||
|
|
1e9ea14a51 | ||
|
|
f6fdbfa922 | ||
|
|
7a341fa1c9 | ||
|
|
786f306eac | ||
|
|
b61b77e773 | ||
|
|
bba8579192 | ||
|
|
aca0adc4c5 | ||
|
|
b996d95b1c | ||
|
|
71be900630 | ||
|
|
5530d2ac9c | ||
|
|
a64c4187e5 | ||
|
|
568a8521c0 | ||
|
|
ab186da2f3 | ||
|
|
11d72c39d3 | ||
|
|
8669f6c309 | ||
|
|
fdaeae1307 | ||
|
|
010ff8cb60 | ||
|
|
12c7379d2a | ||
|
|
ae462d74a5 | ||
|
|
aa2600cdc2 | ||
|
|
69034b7c2f | ||
|
|
49ea9f7f52 | ||
|
|
670e4e7bec | ||
|
|
5b4d624bb2 | ||
|
|
1a4440f1ab | ||
|
|
c917c61b85 | ||
|
|
a239cd94fe | ||
|
|
a6c83a7fbb | ||
|
|
c7e52ab4fe | ||
|
|
df14508778 | ||
|
|
178b76f092 | ||
|
|
36fbbfd4d7 | ||
|
|
d1903b1574 | ||
|
|
fea03a0242 | ||
|
|
2ed2dc36aa | ||
|
|
2223183805 | ||
|
|
25affc5b54 | ||
|
|
ad13d06acc | ||
|
|
291f9ed963 | ||
|
|
455a7a9329 | ||
|
|
7e9eadaea5 | ||
|
|
387f2253b1 | ||
|
|
af401a7e5b | ||
|
|
4404c1afed | ||
|
|
417a48cc71 | ||
|
|
9acff308b9 | ||
|
|
48c9ae4b28 | ||
|
|
c3c4956ebb | ||
|
|
9751eb165f | ||
|
|
409321eb43 | ||
|
|
6f2682d667 | ||
|
|
46196cf283 | ||
|
|
80526be3ed | ||
|
|
d26d45eb7e | ||
|
|
36f522922d | ||
|
|
d70cab2499 | ||
|
|
6583fbe8f7 | ||
|
|
b3383cd543 | ||
|
|
9596301aee | ||
|
|
fbbd4285f5 | ||
|
|
97bc736f86 | ||
|
|
460b8999c3 | ||
|
|
88d7a59afe | ||
|
|
6be364c6ce | ||
|
|
3d83c0b06c | ||
|
|
3b9185fc40 | ||
|
|
5897131f05 | ||
|
|
c7a83c8c51 | ||
|
|
0eda6f5cf1 | ||
|
|
718d049927 | ||
|
|
e0c2e740d2 | ||
|
|
aab0208605 | ||
|
|
d037d2e0ad | ||
|
|
dd8e48789f | ||
|
|
d37ccf2883 | ||
|
|
6fb16cbd95 | ||
|
|
7a3e1a67da | ||
|
|
0f5c019d44 | ||
|
|
9092259ce1 | ||
|
|
079ed7e8cc | ||
|
|
3bb8a5608f | ||
|
|
142fb0c60e | ||
|
|
d088d09328 | ||
|
|
baca1be1b3 | ||
|
|
47c43f9c9c | ||
|
|
b1dcbeb459 | ||
|
|
ceb98222b6 | ||
|
|
34338ce2b1 | ||
|
|
3ef8c846c6 | ||
|
|
325bff80d0 | ||
|
|
fcf680a138 | ||
|
|
05b9e29724 | ||
|
|
fcfe06e1b2 | ||
|
|
7e4a537035 | ||
|
|
62bf2d91ca | ||
|
|
003b956d2c | ||
|
|
c408a57198 | ||
|
|
9f0e0adee8 | ||
|
|
b23705b7b6 | ||
|
|
eaec87ac44 | ||
|
|
4f012390c0 | ||
|
|
d125d12132 | ||
|
|
617b5d8072 | ||
|
|
4b81636947 | ||
|
|
4c8f0f3c50 | ||
|
|
130afdc278 | ||
|
|
a4aab9a56d | ||
|
|
2b8ce4b248 | ||
|
|
be165c8c77 | ||
|
|
46fa8b5c60 | ||
|
|
628b9c30c2 | ||
|
|
13a453193a | ||
|
|
b58b2b578a | ||
|
|
b66cac75d2 | ||
|
|
6573521363 | ||
|
|
f5ed83289f | ||
|
|
2859ad3927 | ||
|
|
aaa8e5c3f1 | ||
|
|
f6673b6f59 | ||
|
|
f3dac806a8 | ||
|
|
92db25f56d | ||
|
|
4e60c61eb0 | ||
|
|
fd9f9126d7 | ||
|
|
7e6b3adf9d | ||
|
|
e0bddd6699 | ||
|
|
d98040895d | ||
|
|
5a8b6f1d4a | ||
|
|
4ca81a5dc2 | ||
|
|
7222e71c08 | ||
|
|
cb9e6c632c | ||
|
|
005d356859 | ||
|
|
7a94be9292 | ||
|
|
9ae874449f | ||
|
|
1e95db0977 | ||
|
|
93ccc668d7 | ||
|
|
defa6455d6 | ||
|
|
7c5fef56e9 | ||
|
|
6ed5e0bcab | ||
|
|
31157b72ab | ||
|
|
0cb68d7c53 | ||
|
|
b67a3bb6de | ||
|
|
d0086a5f96 | ||
|
|
2b84518caf | ||
|
|
60593dcce9 | ||
|
|
fad831d1aa | ||
|
|
3c11d72652 | ||
|
|
97a17df9d1 | ||
|
|
2523383b14 | ||
|
|
a50f3b714f | ||
|
|
df3f58b05e | ||
|
|
2debc3e5f1 | ||
|
|
f383d165fd | ||
|
|
5324d53c47 | ||
|
|
f0f067510f | ||
|
|
2bdcc9cc65 | ||
|
|
0b0754c1d5 | ||
|
|
502607a608 | ||
|
|
290a2204ba | ||
|
|
f175834e45 | ||
|
|
98f9e36291 | ||
|
|
5f8aedb864 | ||
|
|
b674f277d9 | ||
|
|
386cf93eb1 | ||
|
|
7ee04e58c5 | ||
|
|
9eaaa323fe | ||
|
|
6605925162 | ||
|
|
6fbda52998 | ||
|
|
8e60a7b6a8 | ||
|
|
e18cbf730e | ||
|
|
8db618f83d | ||
|
|
60807e09ab | ||
|
|
c55f8b71fd | ||
|
|
cc1fb3b33c | ||
|
|
cfcbf6a8da | ||
|
|
dd5c11acb3 | ||
|
|
fd998e2bf7 | ||
|
|
6a9aa076b8 | ||
|
|
45eb53f245 | ||
|
|
74fa117850 | ||
|
|
5dd236ec96 | ||
|
|
55b190ef49 | ||
|
|
df3c0dcab4 | ||
|
|
d077cd8316 | ||
|
|
05caba143b | ||
|
|
f2767707ef | ||
|
|
4e338dede9 | ||
|
|
f207eba8f9 | ||
|
|
3e9dc91e81 | ||
|
|
c39b0e6e43 | ||
|
|
a9995c695a | ||
|
|
f062fe0ef3 | ||
|
|
5f30a6aad6 | ||
|
|
e0c574c470 | ||
|
|
68c471d639 | ||
|
|
1c05c6da6e | ||
|
|
8f61ac63a3 | ||
|
|
840dbac8b0 | ||
|
|
a0c7d16905 | ||
|
|
9655ab0bd7 | ||
|
|
9e6b3b7978 | ||
|
|
d8181ec79e | ||
|
|
f8cd8d715f | ||
|
|
20c55d073b | ||
|
|
1e854adb84 | ||
|
|
6ab62d006a | ||
|
|
9d0fe07931 | ||
|
|
b45e563e54 | ||
|
|
ad0c849dc4 | ||
|
|
3229737e6c | ||
|
|
596451eba0 | ||
|
|
c23ff4fe0c | ||
|
|
134f2e8e21 | ||
|
|
01a2b6959a | ||
|
|
7299b78c3b | ||
|
|
9b35ef5c83 | ||
|
|
75972cb024 | ||
|
|
461031d3f1 | ||
|
|
772eb7f810 | ||
|
|
3f4ff87078 | ||
|
|
1553973718 | ||
|
|
95c3d8fd27 | ||
|
|
d7b36f32c0 | ||
|
|
9b9cb78ddf | ||
|
|
e40e89ae81 | ||
|
|
2f6214fe72 | ||
|
|
271a7e459a | ||
|
|
e3dde6f7f9 | ||
|
|
16110bd251 | ||
|
|
945f93d678 | ||
|
|
2630e98128 | ||
|
|
855fdd64e8 | ||
|
|
158ae7c520 | ||
|
|
7bd0a10c65 | ||
|
|
e0376bc009 | ||
|
|
25765ef4d0 | ||
|
|
9965f8a36d | ||
|
|
4763717b59 | ||
|
|
8c8dea4142 | ||
|
|
ac816f9207 | ||
|
|
040ea148b0 | ||
|
|
27960ae4be | ||
|
|
5fb79e9baa | ||
|
|
de3e5e7f60 | ||
|
|
e3bafe96e9 | ||
|
|
f978a8759e | ||
|
|
260ec82ea1 | ||
|
|
e40c32d8ad | ||
|
|
20271c6df5 | ||
|
|
fa73feded2 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +0,0 @@
|
|||||||
*.o
|
|
||||||
dsd
|
|
||||||
92
CHANGELOG
92
CHANGELOG
@@ -1,92 +0,0 @@
|
|||||||
1.6.0
|
|
||||||
New features:
|
|
||||||
Auto mutes P25 encrypted signals by default.
|
|
||||||
Raised cosine filters.
|
|
||||||
|
|
||||||
Fixed bugs:
|
|
||||||
Changed the crazy dibit buffer which filled for ever until it
|
|
||||||
ran out of allocated memory then caused a segmentation error. The
|
|
||||||
buffer is now allowed to fill to 90% before being returned to the
|
|
||||||
initial pointer value where it over writes the old samples, it
|
|
||||||
seems to work ok and no segmentation errors now.
|
|
||||||
Input level is now calculated differently, before when it reported 50%
|
|
||||||
the soundcard would be fully overloaded. With the new method aim for 30% inlvl.
|
|
||||||
|
|
||||||
1.4.1
|
|
||||||
New features:
|
|
||||||
Several new sync types for existing formats now recognized:
|
|
||||||
Decodes voice from NXDN 4800 (6.25kHz) signals
|
|
||||||
Decodes voice from NXDN 9600 (12.5kHz) repeater output
|
|
||||||
Decodes voice from DMR/MotoTRBO simplex/repeater input
|
|
||||||
Decodes voice from X2-TDMA simplex/repeater input
|
|
||||||
|
|
||||||
Fixed bugs:
|
|
||||||
renamed "input:" to "inlvl:" to reduce confusion. This value
|
|
||||||
indicates the audio input level, NOT the "decode success
|
|
||||||
rate". Voice decode errors are indicated by the errorbars "=".
|
|
||||||
|
|
||||||
1.4
|
|
||||||
New features:
|
|
||||||
Decodes voice from NXDN 9600 (12.5 kHz) simplex/repeater input
|
|
||||||
NXDN96 frames enabled by default
|
|
||||||
Improved resistance to NXDN96 sync false positives
|
|
||||||
|
|
||||||
Fixed bugs:
|
|
||||||
.wav file header updated after playing .imb/.amb data files
|
|
||||||
.imb/.amb files now have correct tgid in filename
|
|
||||||
|
|
||||||
1.3.1 New features:
|
|
||||||
Support for ProVoice EA sync
|
|
||||||
CTRL-C is now caught so .wav files can be properly closed
|
|
||||||
DSD now shows mbelib version as well as it's own version
|
|
||||||
-R resume option now triggers on any TSDU so control channels can be left
|
|
||||||
in conventional scanlists.
|
|
||||||
Auto output gain now has 0.5 second hold time for faster error burst recovery
|
|
||||||
(was 1.5 seconds)
|
|
||||||
Audio output upsampling function simplified and improved
|
|
||||||
|
|
||||||
Fixed bugs:
|
|
||||||
DSD_Author.pgp now has correct public key (was copy of mbelib_Author key)
|
|
||||||
TGID and SRC are now cleared after TDULC or TDU.
|
|
||||||
Voice error counter is now reset in noCarrier()
|
|
||||||
TGID and SRC were not displaying for X2-TDMA frames
|
|
||||||
Fixed buffer issue in resumeScan()
|
|
||||||
Fixed error in .wav file headers preventing playback on some apps
|
|
||||||
|
|
||||||
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
68
CMakeLists.txt
Normal 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)
|
||||||
15
COPYRIGHT
15
COPYRIGHT
@@ -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
|
Copyright (C) 2010 DSD Author
|
||||||
GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||||
|
|
||||||
|
|||||||
115
Makefile
115
Makefile
@@ -1,115 +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 -Wall
|
|
||||||
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 nxdn_voice.o nxdn_data.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
|
|
||||||
|
|
||||||
nxdn48_voice.o: nxdn48_voice.c nxdn_const.h dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c nxdn48_voice.c -o nxdn48_voice.o
|
|
||||||
|
|
||||||
nxdn_voice.o: nxdn_voice.c nxdn_const.h dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c nxdn_voice.c -o nxdn_voice.o
|
|
||||||
|
|
||||||
nxdn_data.o: nxdn_data.c nxdn_const.h dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c nxdn_data.c -o nxdn_data.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) -o dsd $(OBJS) $(LIBS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.o
|
|
||||||
rm -f dsd
|
|
||||||
|
|
||||||
install: dsd
|
|
||||||
$(INSTALL) dsd $(DEST_BIN)
|
|
||||||
chgrp uucp $(DEST_BIN)/dsd
|
|
||||||
chmod g+s $(DEST_BIN)/dsd
|
|
||||||
521
README
521
README
@@ -1,521 +0,0 @@
|
|||||||
Digital Speech Decoder 1.6.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.
|
|
||||||
|
|
||||||
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.4.1
|
|
||||||
requires mbelib 1.1 or later.
|
|
||||||
|
|
||||||
Supported formats in version 1.6.0:
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
NXDN Digital radio standard used by NEXEDGE and IDAS brands.
|
|
||||||
Supports both 9600 baud (12.5 kHz) and
|
|
||||||
4800 baud (6.25 kHz) digital voice.
|
|
||||||
|
|
||||||
Support includes decoding and synthesis of speech and
|
|
||||||
the ability to save and replay .amb data files.
|
|
||||||
|
|
||||||
Development (no speech) support only:
|
|
||||||
|
|
||||||
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 -fd to enable.
|
|
||||||
|
|
||||||
Unsupported formats in version 1.4 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.
|
|
||||||
|
|
||||||
Supported demodulation optimizations in version 1.4:
|
|
||||||
|
|
||||||
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 inlvl: 39% nac: 5C2 src: 0 tg: 32464 TDULC
|
|
||||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 0 tg: 32464 TDULC
|
|
||||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 0 tg: 32464 TDULC
|
|
||||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 0 tg: 32464 TDULC
|
|
||||||
Sync: -P25p1 mod: C4FM inlvl: 38% nac: 5C2 src: 0 tg: 32464 TDU
|
|
||||||
Sync: -P25p1 mod: C4FM inlvl: 38% nac: 5C2 src: 0 tg: 32464 HDU
|
|
||||||
Sync: -P25p1 mod: C4FM inlvl: 42% nac: 5C2 src: 0 tg: 32464 LDU1 e:
|
|
||||||
Sync: (-P25p1) mod: C4FM inlvl: 39% nac: 5C2 src: 52610 tg: 32464 (LDU2) e:
|
|
||||||
Sync: -P25p1 mod: C4FM inlvl: 38% nac: 5C2 src: 52610 tg: 32464 LDU1 e:
|
|
||||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 52610 tg: 32464 LDU2 e:
|
|
||||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 52610 tg: 32464 LDU1 e:
|
|
||||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 52610 tg: 32464 LDU2 e:
|
|
||||||
Sync: -P25p1 mod: C4FM inlvl: 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.
|
|
||||||
|
|
||||||
"inlvl" 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 inlvl: 59% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
|
|
||||||
Sync: -X2-TDMA mod: QPSK inlvl: 47% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
|
|
||||||
Sync: -X2-TDMA mod: QPSK inlvl: 43% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
|
|
||||||
Sync: (-X2-TDMA) mod: QPSK inlvl: 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 or TSDU
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
-fi Decode only NXDN48* (6.25 kHz) / IDAS*
|
|
||||||
-fn Decode only NXDN96 (12.5 kHz)
|
|
||||||
-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
|
|
||||||
|
|
||||||
* denotes frame types that cannot be auto-detected.
|
|
||||||
|
|
||||||
ProVoice and NXDN48 not auto-detected as use different symbol
|
|
||||||
rates (9600 and 2400) than most formats (4800). D-STAR is not
|
|
||||||
enabled by default as voice decode does not work and it has a
|
|
||||||
short sync word that is prone to false triggering. It is included
|
|
||||||
for development/testing only.
|
|
||||||
|
|
||||||
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
264
README.md
Normal 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.
|
||||||
|
|
||||||
|
[](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`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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
15
cmake/FindITPP.cmake
Normal 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)
|
||||||
16
cmake/FindLibSndFile.cmake
Normal file
16
cmake/FindLibSndFile.cmake
Normal 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
15
cmake/FindMBE.cmake
Normal 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)
|
||||||
68
cmake/FindPULSEAUDIO.cmake
Normal file
68
cmake/FindPULSEAUDIO.cmake
Normal 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
29
cmake/FindRTLSDR.cmake
Normal 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)
|
||||||
23
cmake/cmake_uninstall.cmake.in
Normal file
23
cmake/cmake_uninstall.cmake.in
Normal 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
130
cmake/git_revision.cmake
Normal 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()
|
||||||
38
cmake/git_revision.cmake.in
Normal file
38
cmake/git_revision.cmake.in
Normal 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
3
configure
vendored
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
echo "There is no configure script, just run make"
|
|
||||||
240
dmr_data.c
240
dmr_data.c
@@ -1,240 +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_BS_DATA_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
382
dmr_voice.c
382
dmr_voice.c
@@ -1,382 +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;
|
|
||||||
int msMode;
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
int k;
|
|
||||||
char syncbits[49];
|
|
||||||
char cachbits[25];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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_BS_DATA_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
|
|
||||||
{
|
|
||||||
mutecurrentslot = 1;
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->slot0light, "[slot0]");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->slot1light, "[slot1]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((strcmp (sync, DMR_BS_VOICE_SYNC) == 0) || (strcmp (sync, DMR_MS_VOICE_SYNC) == 0))
|
|
||||||
{
|
|
||||||
mutecurrentslot = 0;
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->slot0light, "[SLOT0]");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->slot1light, "[SLOT1]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((strcmp (sync, DMR_MS_VOICE_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
|
|
||||||
{
|
|
||||||
msMode = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_BS_DATA_SYNC) == 0) || (msMode == 1))
|
|
||||||
{
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->slot1light, " slot1 ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->slot0light, " slot0 ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcmp (sync, DMR_BS_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");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
26
download-and-install-nodeps.sh
Normal file
26
download-and-install-nodeps.sh
Normal 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
70
download-and-install.sh
Normal 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
BIN
dsd-fme-qgis-map.qgz
Normal file
Binary file not shown.
BIN
dsd-fme.png
Normal file
BIN
dsd-fme.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 160 KiB |
BIN
dsd-fme2.png
Normal file
BIN
dsd-fme2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 153 KiB |
248
dsd.h
248
dsd.h
@@ -1,248 +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 <signal.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>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* global variables
|
|
||||||
*/
|
|
||||||
int exitflag;
|
|
||||||
|
|
||||||
|
|
||||||
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_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;
|
|
||||||
} 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 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 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;
|
|
||||||
int p25kid;
|
|
||||||
} dsd_state;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Frame sync patterns
|
|
||||||
*/
|
|
||||||
#define INV_P25P1_SYNC "333331331133111131311111"
|
|
||||||
#define P25P1_SYNC "111113113311333313133333"
|
|
||||||
|
|
||||||
#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_SYNC "313131313133131113313111"
|
|
||||||
#define INV_DSTAR_SYNC "131313131311313331131333"
|
|
||||||
|
|
||||||
#define NXDN_MS_DATA_SYNC "313133113131111333"
|
|
||||||
#define INV_NXDN_MS_DATA_SYNC "131311331313333111"
|
|
||||||
#define NXDN_MS_VOICE_SYNC "313133113131113133"
|
|
||||||
#define INV_NXDN_MS_VOICE_SYNC "131311331313331311"
|
|
||||||
#define INV_NXDN_BS_DATA_SYNC "131311331313333131"
|
|
||||||
#define NXDN_BS_DATA_SYNC "313133113131111313"
|
|
||||||
#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"
|
|
||||||
|
|
||||||
#define INV_PROVOICE_SYNC "31313111333133133311331133113311"
|
|
||||||
#define PROVOICE_SYNC "13131333111311311133113311331133"
|
|
||||||
#define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311"
|
|
||||||
#define PROVOICE_EA_SYNC "31131311331331111133131311311133"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 closeWavOutFile (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 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);
|
|
||||||
369
dsd_audio.c
369
dsd_audio.c
@@ -1,369 +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 > 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
ssize_t result;
|
|
||||||
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = write (opts->wav_out_fd, aout_buf, 320);
|
|
||||||
fflush (opts->wav_out_f);
|
|
||||||
state->wav_out_bytes += 320;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
playSynthesizedVoice (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
ssize_t result;
|
|
||||||
|
|
||||||
if (state->audio_out_idx > opts->delay)
|
|
||||||
{
|
|
||||||
// output synthesized speech to sound card
|
|
||||||
result = 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
295
dsd_dibit.c
295
dsd_dibit.c
@@ -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"
|
|
||||||
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->dibit_buf_p > state->dibit_buf + 900000)
|
|
||||||
{
|
|
||||||
state->dibit_buf_p = state->dibit_buf + 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
406
dsd_file.c
406
dsd_file.c
@@ -1,406 +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 & 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)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
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, "nac%X-%s-tg%i%s", state->nac, datestr, talkgroup, ext);
|
|
||||||
sprintf (shell, "mv %s %s", opts->mbe_out_file, newfilename);
|
|
||||||
result = system (shell);
|
|
||||||
|
|
||||||
state->tgcount = 0;
|
|
||||||
for (i = 0; i < 25; i++)
|
|
||||||
{
|
|
||||||
for (j = 0; j < 16; j++)
|
|
||||||
{
|
|
||||||
state->tg[i][j] = 48;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
state->wav_out_bytes = 0;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// block align
|
|
||||||
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 (0, opts->wav_out_f);
|
|
||||||
|
|
||||||
fflush (opts->wav_out_f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
closeWavOutFile (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
int length;
|
|
||||||
|
|
||||||
if (opts->wav_out_f != NULL)
|
|
||||||
{
|
|
||||||
rewind (opts->wav_out_f);
|
|
||||||
length = state->wav_out_bytes;
|
|
||||||
|
|
||||||
fprintf (opts->wav_out_f, "RIFF");
|
|
||||||
// total length
|
|
||||||
fputc (((36 + length) & 0xff), opts->wav_out_f);
|
|
||||||
fputc ((((36 + length) >> 8) & 0xff), opts->wav_out_f);
|
|
||||||
fputc ((((36 + length) >> 16) & 0xff), opts->wav_out_f);
|
|
||||||
fputc ((((36 + length) >> 24) & 0xff), 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 (128, opts->wav_out_f);
|
|
||||||
fputc (62, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
|
|
||||||
// block align
|
|
||||||
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 ((length & 0xff), opts->wav_out_f);
|
|
||||||
fputc (((length >> 8) & 0xff), opts->wav_out_f);
|
|
||||||
fputc (((length >> 16) & 0xff), opts->wav_out_f);
|
|
||||||
fputc (((length >> 24) & 0xff), opts->wav_out_f);
|
|
||||||
|
|
||||||
fflush (opts->wav_out_f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
448
dsd_frame.c
448
dsd_frame.c
@@ -1,448 +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) state->max / 164;
|
|
||||||
if (opts->verbose > 0)
|
|
||||||
{
|
|
||||||
printf ("inlvl: %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->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;
|
|
||||||
printf ("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);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ((state->synctype == 16) || (state->synctype == 17))
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
printf ("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);
|
|
||||||
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;
|
|
||||||
printf ("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 >= 10) && (state->synctype <= 13))
|
|
||||||
{
|
|
||||||
state->nac = 0;
|
|
||||||
state->lastsrc = 0;
|
|
||||||
state->lasttg = 0;
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
if (opts->verbose > 0)
|
|
||||||
{
|
|
||||||
level = (int) state->max / 164;
|
|
||||||
printf ("inlvl: %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;
|
|
||||||
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;
|
|
||||||
printf ("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;
|
|
||||||
}
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" 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 ");
|
|
||||||
skipDibit (opts, state, 40);
|
|
||||||
}
|
|
||||||
else if (strcmp (duid, "13") == 0)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" TSDU\n");
|
|
||||||
}
|
|
||||||
if (opts->resume > 0)
|
|
||||||
{
|
|
||||||
resumeScan (opts, state);
|
|
||||||
}
|
|
||||||
state->lasttg = 0;
|
|
||||||
state->lastsrc = 0;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
797
dsd_frame_sync.c
797
dsd_frame_sync.c
@@ -1,797 +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 = +NXDN (non inverted voice frame)
|
|
||||||
* 9 = -NXDN (inverted voice frame)
|
|
||||||
* 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
|
|
||||||
* 16 = +NXDN (non inverted data frame)
|
|
||||||
* 17 = -NXDN (inverted data frame)
|
|
||||||
*/
|
|
||||||
|
|
||||||
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];
|
|
||||||
|
|
||||||
for (i = 18; i < 24; i++)
|
|
||||||
{
|
|
||||||
lbuf[i] = 0;
|
|
||||||
lbuf2[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// detect frame sync
|
|
||||||
t = 0;
|
|
||||||
synctest[24] = 0;
|
|
||||||
synctest18[18] = 0;
|
|
||||||
synctest32[32] = 0;
|
|
||||||
synctest_pos = 0;
|
|
||||||
synctest_p = synctest_buf + 10;
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->dibit_buf_p > state->dibit_buf + 900000)
|
|
||||||
{
|
|
||||||
state->dibit_buf_p = state->dibit_buf + 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
//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;
|
|
||||||
}
|
|
||||||
|
|
||||||
*synctest_p = dibit;
|
|
||||||
if (t >= 18)
|
|
||||||
{
|
|
||||||
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, P25P1_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_P25P1_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_BS_DATA_SYNC) == 0) || (strcmp (synctest, X2TDMA_MS_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_BS_VOICE_SYNC) == 0) || (strcmp (synctest, X2TDMA_MS_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_MS_DATA_SYNC) == 0) || (strcmp (synctest, DMR_BS_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_MS_VOICE_SYNC) == 0) || (strcmp (synctest, DMR_BS_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) || (strcmp (synctest32, PROVOICE_EA_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) || (strcmp (synctest32, INV_PROVOICE_EA_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_nxdn96 == 1) || (opts->frame_nxdn48 == 1))
|
|
||||||
{
|
|
||||||
strncpy (synctest18, (synctest_p - 17), 18);
|
|
||||||
if ((strcmp (synctest18, NXDN_BS_VOICE_SYNC) == 0) || (strcmp (synctest18, NXDN_MS_VOICE_SYNC) == 0))
|
|
||||||
{
|
|
||||||
if ((state->lastsynctype == 8) || (state->lastsynctype == 16))
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
if (state->samplesPerSymbol == 20)
|
|
||||||
{
|
|
||||||
sprintf (state->ftype, " NXDN48 ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " +NXDN48 ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->ftype, " NXDN96 ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " +NXDN96 ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->lastsynctype = 8;
|
|
||||||
return (8);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->lastsynctype = 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((strcmp (synctest18, INV_NXDN_BS_VOICE_SYNC) == 0) || (strcmp (synctest18, INV_NXDN_MS_VOICE_SYNC) == 0))
|
|
||||||
{
|
|
||||||
if ((state->lastsynctype == 9) || (state->lastsynctype == 17))
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
if (state->samplesPerSymbol == 20)
|
|
||||||
{
|
|
||||||
sprintf (state->ftype, " NXDN48 ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -NXDN48 ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->ftype, " NXDN96 ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -NXDN96 ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->lastsynctype = 9;
|
|
||||||
return (9);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->lastsynctype = 9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((strcmp (synctest18, NXDN_BS_DATA_SYNC) == 0) || (strcmp (synctest18, NXDN_MS_DATA_SYNC) == 0))
|
|
||||||
{
|
|
||||||
if ((state->lastsynctype == 8) || (state->lastsynctype == 16))
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
if (state->samplesPerSymbol == 20)
|
|
||||||
{
|
|
||||||
sprintf (state->ftype, " NXDN48 ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " +NXDN48 ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->ftype, " NXDN96 ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " +NXDN96 ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->lastsynctype = 16;
|
|
||||||
return (16);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->lastsynctype = 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((strcmp (synctest18, INV_NXDN_BS_DATA_SYNC) == 0) || (strcmp (synctest18, INV_NXDN_MS_DATA_SYNC) == 0))
|
|
||||||
{
|
|
||||||
if ((state->lastsynctype == 9) || (state->lastsynctype == 17))
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, " NXDN ");
|
|
||||||
if (state->samplesPerSymbol == 20)
|
|
||||||
{
|
|
||||||
sprintf (state->ftype, " NXDN48 ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -NXDN48 ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->ftype, " NXDN96 ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -NXDN96 ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->lastsynctype = 17;
|
|
||||||
return (17);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->lastsynctype = 17;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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_BS_VOICE_SYNC) != 0) || (strcmp (synctest, X2TDMA_MS_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_BS_DATA_SYNC) != 0) || (strcmp (synctest, X2TDMA_MS_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_BS_VOICE_SYNC) != 0) || (strcmp (synctest, DMR_MS_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_BS_DATA_SYNC) != 0) || (strcmp (synctest, DMR_MS_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 (exitflag == 1)
|
|
||||||
{
|
|
||||||
cleanupAndExit (opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (synctest_pos < 10200)
|
|
||||||
{
|
|
||||||
synctest_pos++;
|
|
||||||
synctest_p++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// buffer reset
|
|
||||||
synctest_pos = 0;
|
|
||||||
synctest_p = synctest_buf;
|
|
||||||
noCarrier (opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->lastsynctype != 1)
|
|
||||||
{
|
|
||||||
if (synctest_pos >= 1800)
|
|
||||||
{
|
|
||||||
if ((opts->errorbars == 1) && (opts->verbose > 1) && (state->carrier == 1))
|
|
||||||
{
|
|
||||||
printf ("Sync: no sync\n");
|
|
||||||
}
|
|
||||||
noCarrier (opts, state);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
698
dsd_main.c
698
dsd_main.c
@@ -1,698 +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 "nxdn_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->errs = 0;
|
|
||||||
state->errs2 = 0;
|
|
||||||
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_nxdn48 = 0;
|
|
||||||
opts->frame_nxdn96 = 1;
|
|
||||||
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->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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
usage ()
|
|
||||||
{
|
|
||||||
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 or TSDU\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 (" -fi Decode only NXDN48* (6.25 kHz) / IDAS*\n");
|
|
||||||
printf (" -fn Decode only NXDN96 (12.5 kHz)\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cleanupAndExit (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
noCarrier (opts, state);
|
|
||||||
if (opts->wav_out_f != NULL)
|
|
||||||
{
|
|
||||||
closeWavOutFile (opts, state);
|
|
||||||
}
|
|
||||||
printf ("Exiting.\n");
|
|
||||||
exit (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
sigfun (int sig)
|
|
||||||
{
|
|
||||||
exitflag = 1;
|
|
||||||
signal (SIGINT, SIG_DFL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
|
|
||||||
int c;
|
|
||||||
extern char *optarg;
|
|
||||||
extern int optind, opterr, optopt;
|
|
||||||
dsd_opts opts;
|
|
||||||
dsd_state state;
|
|
||||||
char versionstr[25];
|
|
||||||
mbe_printVersion (versionstr);
|
|
||||||
|
|
||||||
printf ("Digital Speech Decoder 1.6.0\n");
|
|
||||||
printf ("mbelib version %s\n", versionstr);
|
|
||||||
|
|
||||||
initOpts (&opts);
|
|
||||||
initState (&state);
|
|
||||||
|
|
||||||
exitflag = 0;
|
|
||||||
signal (SIGINT, sigfun);
|
|
||||||
|
|
||||||
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_nxdn48 = 0;
|
|
||||||
opts.frame_nxdn96 = 1;
|
|
||||||
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_nxdn48 = 0;
|
|
||||||
opts.frame_nxdn96 = 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_nxdn48 = 0;
|
|
||||||
opts.frame_nxdn96 = 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_nxdn48 = 0;
|
|
||||||
opts.frame_nxdn96 = 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 ("Setting symbol rate to 9600 / second\n");
|
|
||||||
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_nxdn48 = 0;
|
|
||||||
opts.frame_nxdn96 = 0;
|
|
||||||
opts.frame_dmr = 0;
|
|
||||||
opts.frame_provoice = 0;
|
|
||||||
printf ("Decoding only P25 Phase 1 frames.\n");
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'i')
|
|
||||||
{
|
|
||||||
opts.frame_dstar = 0;
|
|
||||||
opts.frame_x2tdma = 0;
|
|
||||||
opts.frame_p25p1 = 0;
|
|
||||||
opts.frame_nxdn48 = 1;
|
|
||||||
opts.frame_nxdn96 = 0;
|
|
||||||
opts.frame_dmr = 0;
|
|
||||||
opts.frame_provoice = 0;
|
|
||||||
state.samplesPerSymbol = 20;
|
|
||||||
state.symbolCenter = 10;
|
|
||||||
opts.mod_c4fm = 0;
|
|
||||||
opts.mod_qpsk = 0;
|
|
||||||
opts.mod_gfsk = 1;
|
|
||||||
state.rf_mod = 2;
|
|
||||||
printf ("Setting symbol rate to 2400 / second\n");
|
|
||||||
printf ("Enabling only GFSK modulation optimizations.\n");
|
|
||||||
printf ("Decoding only NXDN 4800 baud frames.\n");
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'n')
|
|
||||||
{
|
|
||||||
opts.frame_dstar = 0;
|
|
||||||
opts.frame_x2tdma = 0;
|
|
||||||
opts.frame_p25p1 = 0;
|
|
||||||
opts.frame_nxdn48 = 0;
|
|
||||||
opts.frame_nxdn96 = 1;
|
|
||||||
opts.frame_dmr = 0;
|
|
||||||
opts.frame_provoice = 0;
|
|
||||||
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 NXDN 9600 baud frames.\n");
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'r')
|
|
||||||
{
|
|
||||||
opts.frame_dstar = 0;
|
|
||||||
opts.frame_x2tdma = 0;
|
|
||||||
opts.frame_p25p1 = 0;
|
|
||||||
opts.frame_nxdn48 = 0;
|
|
||||||
opts.frame_nxdn96 = 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 = 25; // 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);
|
|
||||||
}
|
|
||||||
cleanupAndExit (&opts, &state);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
126
dsd_mbe.c
126
dsd_mbe.c
@@ -1,126 +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, "%s", 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
238
dsd_symbol.c
238
dsd_symbol.c
@@ -1,238 +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;
|
|
||||||
ssize_t result;
|
|
||||||
|
|
||||||
sum = 0;
|
|
||||||
count = 0;
|
|
||||||
for (i = 0; i < state->samplesPerSymbol; i++)
|
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = read (opts->audio_in_fd, &sample, 2);
|
|
||||||
{
|
|
||||||
#define NZEROS 60
|
|
||||||
#define GAIN 7.423339364e+00
|
|
||||||
|
|
||||||
static float xv[NZEROS+1];
|
|
||||||
|
|
||||||
static float xcoeffs[] =
|
|
||||||
{ -0.0083649323, -0.0265444850, -0.0428141462, -0.0537571943,
|
|
||||||
-0.0564141052, -0.0489161045, -0.0310068662, -0.0043393881,
|
|
||||||
+0.0275375106, +0.0595423283, +0.0857543325, +0.1003565948,
|
|
||||||
+0.0986944931, +0.0782804830, +0.0395670487, -0.0136691535,
|
|
||||||
-0.0744390415, -0.1331834575, -0.1788967208, -0.2005995448,
|
|
||||||
-0.1889627181, -0.1378439993, -0.0454976231, +0.0847488694,
|
|
||||||
+0.2444859269, +0.4209222342, +0.5982295474, +0.7593684540,
|
|
||||||
+0.8881539892, +0.9712773915, +0.9999999166, +0.9712773915,
|
|
||||||
+0.8881539892, +0.7593684540, +0.5982295474, +0.4209222342,
|
|
||||||
+0.2444859269, +0.0847488694, -0.0454976231, -0.1378439993,
|
|
||||||
-0.1889627181, -0.2005995448, -0.1788967208, -0.1331834575,
|
|
||||||
-0.0744390415, -0.0136691535, +0.0395670487, +0.0782804830,
|
|
||||||
+0.0986944931, +0.1003565948, +0.0857543325, +0.0595423283,
|
|
||||||
+0.0275375106, -0.0043393881, -0.0310068662, -0.0489161045,
|
|
||||||
-0.0564141052, -0.0537571943, -0.0428141462, -0.0265444850,
|
|
||||||
-0.0083649323,
|
|
||||||
};
|
|
||||||
|
|
||||||
float sum; int i;
|
|
||||||
|
|
||||||
for (i = 0; i < NZEROS; i++)
|
|
||||||
xv[i] = xv[i+1];
|
|
||||||
|
|
||||||
xv[NZEROS] = sample; // unfiltered sample in
|
|
||||||
sum = 0.0;
|
|
||||||
|
|
||||||
for (i = 0; i <= NZEROS; i++)
|
|
||||||
sum += (xcoeffs[i] * xv[i]);
|
|
||||||
|
|
||||||
sample = sum / GAIN; // filtered sample out
|
|
||||||
}
|
|
||||||
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 == 20)
|
|
||||||
{
|
|
||||||
if ((i >= 9) && (i <= 11))
|
|
||||||
{
|
|
||||||
sum += sample;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state->samplesPerSymbol == 5)
|
|
||||||
{
|
|
||||||
if (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);
|
|
||||||
}
|
|
||||||
79
dstar.c
79
dstar.c
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
311
dstar_const.h
311
dstar_const.h
@@ -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
412
include/Golay24.hpp
Normal 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
413
include/Hamming.hpp
Normal 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
670
include/ReedSolomon.hpp
Normal 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
298
include/descramble.h
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -15,23 +15,25 @@
|
|||||||
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
//#ifdef __cplusplus
|
||||||
|
//extern "C" {
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
|
||||||
#ifndef _MAIN
|
#ifndef _MAIN
|
||||||
extern const int nW[36];
|
extern const int dPmrW[36];
|
||||||
extern const int nX[36];
|
extern const int dPmrX[36];
|
||||||
extern const int nY[36];
|
extern const int dPmrY[36];
|
||||||
extern const int nZ[36];
|
extern const int dPmrZ[36];
|
||||||
extern const char nxdnpr[145];
|
|
||||||
|
|
||||||
#else
|
//#else
|
||||||
/*
|
/*
|
||||||
* pseudorandom bit sequence
|
* dPMR AMBE interleave schedule
|
||||||
*/
|
*/
|
||||||
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 };
|
// bit 1
|
||||||
|
const int dPmrW[36] = {
|
||||||
/*
|
0, 1, 0, 1, 0, 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, 1,
|
0, 1, 0, 1, 0, 1,
|
||||||
0, 1, 0, 1, 0, 2,
|
0, 1, 0, 1, 0, 2,
|
||||||
@@ -39,7 +41,8 @@ const int nW[36] = { 0, 1, 0, 1, 0, 1,
|
|||||||
0, 2, 0, 2, 0, 2
|
0, 2, 0, 2, 0, 2
|
||||||
};
|
};
|
||||||
|
|
||||||
const int nX[36] = { 23, 10, 22, 9, 21, 8,
|
const int dPmrX[36] = {
|
||||||
|
23, 10, 22, 9, 21, 8,
|
||||||
20, 7, 19, 6, 18, 5,
|
20, 7, 19, 6, 18, 5,
|
||||||
17, 4, 16, 3, 15, 2,
|
17, 4, 16, 3, 15, 2,
|
||||||
14, 1, 13, 0, 12, 10,
|
14, 1, 13, 0, 12, 10,
|
||||||
@@ -47,7 +50,9 @@ const int nX[36] = { 23, 10, 22, 9, 21, 8,
|
|||||||
8, 6, 7, 5, 6, 4
|
8, 6, 7, 5, 6, 4
|
||||||
};
|
};
|
||||||
|
|
||||||
const int nY[36] = { 0, 2, 0, 2, 0, 2,
|
// bit 0
|
||||||
|
const int dPmrY[36] = {
|
||||||
|
0, 2, 0, 2, 0, 2,
|
||||||
0, 2, 0, 3, 0, 3,
|
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,
|
||||||
@@ -55,7 +60,8 @@ const int nY[36] = { 0, 2, 0, 2, 0, 2,
|
|||||||
1, 3, 1, 3, 1, 3
|
1, 3, 1, 3, 1, 3
|
||||||
};
|
};
|
||||||
|
|
||||||
const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
const int dPmrZ[36] = {
|
||||||
|
5, 3, 4, 2, 3, 1,
|
||||||
2, 0, 1, 13, 0, 12,
|
2, 0, 1, 13, 0, 12,
|
||||||
22, 11, 21, 10, 20, 9,
|
22, 11, 21, 10, 20, 9,
|
||||||
19, 8, 18, 7, 17, 6,
|
19, 8, 18, 7, 17, 6,
|
||||||
@@ -64,3 +70,7 @@ const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//#ifdef __cplusplus
|
||||||
|
//}
|
||||||
|
//#endif
|
||||||
964
include/dsd.h
Normal file
964
include/dsd.h
Normal 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
129
include/dstar_const.h
Normal 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
6
include/dstar_header.h
Normal 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
89
include/fcs.h
Normal 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
1448
include/fec.h
Normal file
File diff suppressed because it is too large
Load Diff
1
include/git_ver.h
Normal file
1
include/git_ver.h
Normal file
@@ -0,0 +1 @@
|
|||||||
|
extern const char GIT_TAG[];
|
||||||
257
include/nxdn_const.h
Normal file
257
include/nxdn_const.h
Normal 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
60
include/p25p1_check_hdu.h
Normal 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
58
include/p25p1_check_ldu.h
Normal 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
26
include/p25p1_check_nid.h
Normal 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
73
include/p25p1_hdu.h
Normal 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
|
||||||
91
include/p25p1_heuristics.h
Normal file
91
include/p25p1_heuristics.h
Normal 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
49
include/p25p1_ldu.h
Normal 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
6
include/pa_devs.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef PA_DEVS_H
|
||||||
|
#define PA_DEVS_H
|
||||||
|
|
||||||
|
void printPortAudioDevices();
|
||||||
|
|
||||||
|
#endif
|
||||||
10
install.sh
Normal file
10
install.sh
Normal 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
|
||||||
36
nxdn_data.c
36
nxdn_data.c
@@ -1,36 +0,0 @@
|
|||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
processNXDNData (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
int i, dibit;
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("DATA ");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 30; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf ("%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = 0; i < 144; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf ("%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
59
nxdn_voice.c
59
nxdn_voice.c
@@ -1,59 +0,0 @@
|
|||||||
#include "dsd.h"
|
|
||||||
#include "nxdn_const.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
processNXDNVoice (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
int i, j, dibit;
|
|
||||||
char ambe_fr[4][24];
|
|
||||||
const int *w, *x, *y, *z;
|
|
||||||
const char *pr;
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("VOICE e:");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 30; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf ("%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pr = nxdnpr;
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
w = nW;
|
|
||||||
x = nX;
|
|
||||||
y = nY;
|
|
||||||
z = nZ;
|
|
||||||
for (i = 0; i < 36; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf ("%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
ambe_fr[*w][*x] = *pr ^ (1 & (dibit >> 1)); // bit 1
|
|
||||||
pr++;
|
|
||||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
318
p25p1_hdu.c
318
p25p1_hdu.c
@@ -1,318 +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);
|
|
||||||
|
|
||||||
state->p25kid = strtol(kid, NULL, 2);
|
|
||||||
|
|
||||||
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 (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
state->tgcount = state->tgcount + 1;
|
|
||||||
}
|
|
||||||
if (opts->p25tg == 1)
|
|
||||||
{
|
|
||||||
printf ("tg: %li\n", talkgroup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
298
p25p1_ldu1.c
298
p25p1_ldu1.c
@@ -1,298 +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++;
|
|
||||||
}
|
|
||||||
if (state->p25kid == 0)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
355
p25p1_ldu2.c
355
p25p1_ldu2.c
@@ -1,355 +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++;
|
|
||||||
}
|
|
||||||
if (state->p25kid == 0)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
158
p25p1_tdulc.c
158
p25p1_tdulc.c
@@ -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
21
rebuild.sh
Normal 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
83
src/Hamming.cpp
Normal 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
560
src/bptc.c
Normal 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
889
src/dmr_bs.c
Normal 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
571
src/dmr_data.c
Normal 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
942
src/dmr_ms.c
Normal 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
395
src/dmr_process_voice.c
Normal 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
2970
src/dmr_sync.c
Normal file
File diff suppressed because it is too large
Load Diff
660
src/dmr_voice.c
Normal file
660
src/dmr_voice.c
Normal 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
278
src/dpmr_data.c
Normal 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
759
src/dpmr_voice.c
Normal 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
624
src/dsd_audio.c
Normal 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
482
src/dsd_dibit.c
Normal 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
624
src/dsd_file.c
Normal 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
170
src/dsd_filters.c
Normal 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
722
src/dsd_frame.c
Normal 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
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
1771
src/dsd_main.c
Normal file
File diff suppressed because it is too large
Load Diff
575
src/dsd_mbe.c
Normal file
575
src/dsd_mbe.c
Normal 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
2577
src/dsd_ncurses.c
Normal file
File diff suppressed because it is too large
Load Diff
106
src/dsd_reset.c
Normal file
106
src/dsd_reset.c
Normal 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
|
||||||
|
}
|
||||||
@@ -8,11 +8,11 @@ openSerial (dsd_opts * opts, dsd_state * state)
|
|||||||
struct termios tty;
|
struct termios tty;
|
||||||
speed_t baud;
|
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);
|
opts->serial_fd = open (opts->serial_dev, O_WRONLY);
|
||||||
if (opts->serial_fd == -1)
|
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);
|
exit (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
422
src/dsd_symbol.c
Normal file
422
src/dsd_symbol.c
Normal 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);
|
||||||
|
}
|
||||||
@@ -23,8 +23,19 @@ upsample (dsd_state * state, float invalue)
|
|||||||
|
|
||||||
int i, j, sum;
|
int i, j, sum;
|
||||||
float *outbuf1, c, d;
|
float *outbuf1, c, d;
|
||||||
|
if (state->currentslot == 0 && state->dmr_stereo == 1)
|
||||||
outbuf1 = state->audio_out_float_buf_p;
|
{
|
||||||
|
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--;
|
outbuf1--;
|
||||||
c = *outbuf1;
|
c = *outbuf1;
|
||||||
d = invalue;
|
d = invalue;
|
||||||
@@ -43,6 +54,33 @@ upsample (dsd_state * state, float invalue)
|
|||||||
*outbuf1 = d;
|
*outbuf1 = d;
|
||||||
outbuf1++;
|
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)
|
if (state->audio_out_idx2 > 24)
|
||||||
{
|
{
|
||||||
// smoothing
|
// smoothing
|
||||||
@@ -53,11 +91,11 @@ upsample (dsd_state * state, float invalue)
|
|||||||
{
|
{
|
||||||
sum = 0;
|
sum = 0;
|
||||||
outbuf1 -= 2;
|
outbuf1 -= 2;
|
||||||
sum += *outbuf1;
|
sum += (int)*outbuf1;
|
||||||
outbuf1 += 2;
|
outbuf1 += 2;
|
||||||
sum += *outbuf1;
|
sum += (int)*outbuf1;
|
||||||
outbuf1 += 2;
|
outbuf1 += 2;
|
||||||
sum += *outbuf1;
|
sum += (int)*outbuf1;
|
||||||
outbuf1 -= 2;
|
outbuf1 -= 2;
|
||||||
*outbuf1 = (sum / (float) 3);
|
*outbuf1 = (sum / (float) 3);
|
||||||
outbuf1++;
|
outbuf1++;
|
||||||
@@ -65,4 +103,30 @@ upsample (dsd_state * state, float invalue)
|
|||||||
outbuf1 -= 8;
|
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
183
src/dstar.c
Normal 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
87
src/dstar_header.c
Normal 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
323
src/ez.cpp
Normal 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
128
src/ezpwd/asserter
Normal 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
485
src/ezpwd/bch
Normal 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
219
src/ezpwd/bch_base
Normal 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
506
src/ezpwd/corrector
Normal 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
9
src/ezpwd/definitions
Normal 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
725
src/ezpwd/ezcod
Normal 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
344
src/ezpwd/output
Normal 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
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user