mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 01:07:24 -05:00
Compare commits
667 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
846b202192 | ||
|
|
330ebe2bf3 | ||
|
|
79b4a5e720 | ||
|
|
8186b62a91 | ||
|
|
20f0c852aa | ||
|
|
9e4ae23c0b | ||
|
|
585f6f2fa5 | ||
|
|
a4fba3b19c | ||
|
|
a2180eb5d3 | ||
|
|
514161fefa | ||
|
|
bba059f35f | ||
|
|
5977f865e3 | ||
|
|
ade7c0477f | ||
|
|
a2fded8340 | ||
|
|
005e15c45f | ||
|
|
e9962a8923 | ||
|
|
c6611eae9c | ||
|
|
261193959c | ||
|
|
e7a71eabb6 | ||
|
|
afba84e584 | ||
|
|
96faff7711 | ||
|
|
94166b493c | ||
|
|
6a0b1b2dcf | ||
|
|
5c8e3d95a7 | ||
|
|
4c91588af6 | ||
|
|
35c05654a5 | ||
|
|
fbc9b5fab8 | ||
|
|
1126ab6578 | ||
|
|
0842613b24 | ||
|
|
bf4a4b574c | ||
|
|
380657679e | ||
|
|
0a214af29b | ||
|
|
b008122149 | ||
|
|
7b6feb9e4f | ||
|
|
e4861bf94c | ||
|
|
587a3f01a2 | ||
|
|
8d5c82356e | ||
|
|
df039ab1c1 | ||
|
|
f5e3c6eddd | ||
|
|
fc01712132 | ||
|
|
7ec0bf83b4 | ||
|
|
504476c5db | ||
|
|
f78dbd57bc | ||
|
|
d2b27fdf25 | ||
|
|
3aa20f82b9 | ||
|
|
402b2f0264 | ||
|
|
4dd2328e10 | ||
|
|
98bcdd203e | ||
|
|
741ed51d08 | ||
|
|
5d8e07228e | ||
|
|
4a43a48b2e | ||
|
|
0ef3b4ab4b | ||
|
|
00e0f00974 | ||
|
|
91181f9e93 | ||
|
|
5c42c50a40 | ||
|
|
36399386c5 | ||
|
|
8eee4cd699 | ||
|
|
1410e8bd81 | ||
|
|
915375fb1c | ||
|
|
52fff28dc4 | ||
|
|
bd035622b8 | ||
|
|
44163710f9 | ||
|
|
44b1ccdeae | ||
|
|
34113cb782 | ||
|
|
2a336a9275 | ||
|
|
5c08a9193c | ||
|
|
2cae7f9c18 | ||
|
|
e056c97d9d | ||
|
|
dc49e0d08b | ||
|
|
b639f6af28 | ||
|
|
6b45c4ba9b | ||
|
|
2062502a59 | ||
|
|
a490d9ead2 | ||
|
|
c73832ac2c | ||
|
|
fe1fa79f70 | ||
|
|
f2f29f2287 | ||
|
|
3181e58b19 | ||
|
|
6204aec2a5 | ||
|
|
267180ed0e | ||
|
|
8d9cbb3720 | ||
|
|
8af6d2e185 | ||
|
|
abeb92ff8a | ||
|
|
be47075611 | ||
|
|
1640b58ae4 | ||
|
|
7a34d531b1 | ||
|
|
0af814c72f | ||
|
|
1477190dea | ||
|
|
b0cd1881bb | ||
|
|
44ad75ec17 | ||
|
|
1ce487dc13 | ||
|
|
ed9a96fdc8 | ||
|
|
c677efda49 | ||
|
|
fea4f97b89 | ||
|
|
f2e3834e0a | ||
|
|
b8b62ef69a | ||
|
|
4a8a4a088a | ||
|
|
9308c5b251 | ||
|
|
cdfe9435d3 | ||
|
|
f6cd915f4d | ||
|
|
1dfd59801e | ||
|
|
c4513fab7a | ||
|
|
a3b8839897 | ||
|
|
9e009867ff | ||
|
|
e95e2218d9 | ||
|
|
a9742043b8 | ||
|
|
05eef039c9 | ||
|
|
818e952b7b | ||
|
|
38b962aacf | ||
|
|
0217202801 | ||
|
|
ef07b48540 | ||
|
|
db67a8b227 | ||
|
|
4e2c4577d5 | ||
|
|
e581c96560 | ||
|
|
f3505269d0 | ||
|
|
20ccdbbced | ||
|
|
fc2fb99502 | ||
|
|
143c560958 | ||
|
|
d122d7e935 | ||
|
|
d0f9bb4bad | ||
|
|
ac752029bd | ||
|
|
5b994f5129 | ||
|
|
ec7d7c24a5 | ||
|
|
1d49481f2d | ||
|
|
7ca8cb421c | ||
|
|
4eb44b3c67 | ||
|
|
e16e4b61fc | ||
|
|
fbd04fa869 | ||
|
|
7f9b852f36 | ||
|
|
f5a7ab2b77 | ||
|
|
ffe6098fb8 | ||
|
|
157ca8c043 | ||
|
|
b722e43a3a | ||
|
|
2ba9953d33 | ||
|
|
bb2199ee84 | ||
|
|
4efec4e4ef | ||
|
|
12cf9aa80f | ||
|
|
e5a42dbb11 | ||
|
|
67eaeadc03 | ||
|
|
acb5c16c30 | ||
|
|
d16e775e46 | ||
|
|
11d4c8c49c | ||
|
|
1477880720 | ||
|
|
9290098081 | ||
|
|
eeb6924369 | ||
|
|
c02d7c0ba0 | ||
|
|
324191d79f | ||
|
|
e613b85829 | ||
|
|
49f81da4df | ||
|
|
826c99d563 | ||
|
|
2441664b19 | ||
|
|
5c733c0660 | ||
|
|
73824084ad | ||
|
|
209d4e039e | ||
|
|
fa4e84bc77 | ||
|
|
4946897914 | ||
|
|
57e3f518dd | ||
|
|
08a548339c | ||
|
|
36c337ede8 | ||
|
|
f6bb325dac | ||
|
|
626869992e | ||
|
|
5422b337bb | ||
|
|
b6f270be05 | ||
|
|
3ea813ebbc | ||
|
|
53980c98ed | ||
|
|
536372d877 | ||
|
|
896f3a8533 | ||
|
|
68bbea64d5 | ||
|
|
711a4cb871 | ||
|
|
8fd1d8fe35 | ||
|
|
23c396cbe8 | ||
|
|
e91c21fadb | ||
|
|
15a8f80344 | ||
|
|
5130899a67 | ||
|
|
f5ac9ff0b2 | ||
|
|
14dd045312 | ||
|
|
fbb00f78fe | ||
|
|
a5d23d9e12 | ||
|
|
a2b3da5b5a | ||
|
|
a9417afd67 | ||
|
|
438f89bf4c | ||
|
|
2f2eecddfc | ||
|
|
2fceee5a2f | ||
|
|
62c70cbf92 | ||
|
|
b97014ff0b | ||
|
|
68edb9463c | ||
|
|
178f88a28f | ||
|
|
f7d103c6a2 | ||
|
|
10d10063f0 | ||
|
|
dba3b79266 | ||
|
|
0441b8fdbd | ||
|
|
ea399e50a8 | ||
|
|
66f710e877 | ||
|
|
0f227e40c3 | ||
|
|
136ed75342 | ||
|
|
61cefa6a87 | ||
|
|
cc7504c280 | ||
|
|
07139b3d7d | ||
|
|
638f212525 | ||
|
|
9f624e0b62 | ||
|
|
eefe1bac5e | ||
|
|
532c1cc3b6 | ||
|
|
b1c40ed6e7 | ||
|
|
94dbd1512d | ||
|
|
c73d45d553 | ||
|
|
d1efa05164 | ||
|
|
7af5ffe9fa | ||
|
|
8141ea2e78 | ||
|
|
ec4ace1514 | ||
|
|
eb5cbaad93 | ||
|
|
f5d0575978 | ||
|
|
a0d9e349c2 | ||
|
|
7ca65cf687 | ||
|
|
b4a0e2e9f3 | ||
|
|
73f0d5df44 | ||
|
|
b003d0a210 | ||
|
|
83f390ba88 | ||
|
|
c8aafb1be0 | ||
|
|
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 | ||
|
|
5d147c9f04 | ||
|
|
3683f6c631 | ||
|
|
20271c6df5 | ||
|
|
fa73feded2 | ||
|
|
46b9b01baf | ||
|
|
b1024fba95 |
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
*.[ao]
|
||||||
|
*.so*
|
||||||
|
*.dylib
|
||||||
|
build
|
||||||
|
*.sh
|
||||||
|
*.csv
|
||||||
|
examples
|
||||||
|
*.bin
|
||||||
|
*.ans
|
||||||
37
CHANGELOG
37
CHANGELOG
@@ -1,37 +0,0 @@
|
|||||||
1.3 New features:
|
|
||||||
Decodes voice from ProVoice signals (requires -fp option)
|
|
||||||
algid and kid are now shown in hex notation
|
|
||||||
|
|
||||||
Fixed bugs:
|
|
||||||
auto output gain now has faster rise time
|
|
||||||
MoTDMA is now correctly labeled X2-TDMA
|
|
||||||
|
|
||||||
1.2 New features:
|
|
||||||
Decodes voice for DMR standard (including MOTOTRBO)
|
|
||||||
Full metadata (src, talkgroup, lcinfo, mfid, lcformat, mi, algid, keyid) for X2-TDMA
|
|
||||||
TDMA slot identification for X2-TDMA/DMR/MOTOTRBO
|
|
||||||
Identifies non-voice frame types for X2-TDMA/DMR/MOTOTRBO
|
|
||||||
Frame only (no voice yet) support for 9600 baud NXDN
|
|
||||||
Auto leveling audio output gain (default) and -g option for fixed gain
|
|
||||||
GFSK modulation optimizations and improved C4FM/GFSK/QPSK auto detect
|
|
||||||
|
|
||||||
Fixed bugs:
|
|
||||||
nac was showing wrong ID
|
|
||||||
p25 metadata (lcinfo, mfid, lfcormat, mi, keyid, algid, lsd) are now
|
|
||||||
printed out in the correct bit order (MSB -> LSB), was reversed.
|
|
||||||
fixed serveral bugs in dsd_upsample.c, with improved quality
|
|
||||||
|
|
||||||
1.1 New features:
|
|
||||||
Scanner control options to allow scan resume during certain TDULC
|
|
||||||
Improved upsampling function audio quality
|
|
||||||
Greatly improved handling of X2-TDMA frames
|
|
||||||
Much faster QPSK decision point tracking by default
|
|
||||||
|
|
||||||
Fixed bugs:
|
|
||||||
playMbeFiles was not output to .wav file when -w was given
|
|
||||||
Now correctly detects/handles Mot and P25 talkgroup formats
|
|
||||||
PDU frames were not detected
|
|
||||||
|
|
||||||
1.0.3 Fixed buggy C4FM/QPSK auto detection and added -A option to adjust it.
|
|
||||||
|
|
||||||
1.0 Initial release
|
|
||||||
121
CMakeLists.txt
Normal file
121
CMakeLists.txt
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#Ubuntu 20.04 uses 3.16.3
|
||||||
|
#Ubuntu 22.04 uses 3.22.1
|
||||||
|
#Arch currently on 3.26.3
|
||||||
|
#Cygwin currently using 3.23.2 (or newer)
|
||||||
|
cmake_minimum_required(VERSION 3.10.2)
|
||||||
|
project(dsd-fme)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
#use cmake option -DAERO=ON to enable Aero Builds (OSS support and specific tweaks)
|
||||||
|
option(AERO
|
||||||
|
"Build for Aero Environments (Cygwin)" OFF)
|
||||||
|
if (AERO)
|
||||||
|
add_definitions(-DAERO_BUILD)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
#use cmake option -DCOLORS=OFF to disable color output
|
||||||
|
option(COLORS
|
||||||
|
"Build with Colors Enabled" ON)
|
||||||
|
if (COLORS)
|
||||||
|
add_definitions(-DPRETTY_COLORS)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
#use cmake option -DPVC=ON to enable Provoice Conventional Frame Sync
|
||||||
|
option(PVC
|
||||||
|
"Build with Provoice Conventional Frame Sync Enabled" OFF)
|
||||||
|
if (PVC)
|
||||||
|
add_definitions(-DPVCONVENTIONAL)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
#use cmake option -DNXDN=ON to enable the old NXDN Sync Pattern
|
||||||
|
option(NXDN
|
||||||
|
"Build with Old NXDN Sync Pattern Enabled" OFF)
|
||||||
|
if (NXDN)
|
||||||
|
add_definitions(-DNXDNOLDSYNC)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
#use cmake option -DLZ=ON to enable LimaZulu Requested NXDN Tweaks
|
||||||
|
option(LZ
|
||||||
|
"Build with new LimaZulu Requested NXDN Tweaks Enabled" OFF)
|
||||||
|
if (LZ)
|
||||||
|
add_definitions(-DLIMAZULUTWEAKS)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
#use cmake option -DTONES to change default location of the tones.wav files (lwvmobile precompiled 'Aero' releases only)
|
||||||
|
option(NXDN
|
||||||
|
"Build with tone wav files in the dsd-fme folder and not in /usr/share/" OFF)
|
||||||
|
if (TN)
|
||||||
|
add_definitions(-DTONES)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
#use cmake option -DZDEV to change designation of this build from MAIN to zDEV
|
||||||
|
option(NXDN
|
||||||
|
"change designation of this build from MAIN to zDEV" OFF)
|
||||||
|
if (ZDEV)
|
||||||
|
add_definitions(-DZDEV_BUILD)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
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)
|
||||||
19
COPYRIGHT
19
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)
|
||||||
|
|
||||||
@@ -12,3 +27,7 @@ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|||||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
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
|
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
PERFORMANCE OF THIS SOFTWARE.
|
PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
Version: GnuPG v1.4.10
|
Version: GnuPG v1.4.10
|
||||||
|
|
||||||
mQENBEufusYBCADN1SNkRkQzdLM02MCjen8KQmfuA38MrQIV0dZLpPJcn1+hsvZZ
|
mQENBEufuqsBCADAtWFE1qE3xqJE4xggUn6id0fVulM7y+rSH1VPxo7Ps9duc/O9
|
||||||
KWvpUiWCNw1ClGRsJAT5cjonndi1PV7tpzQz+CyMqSP5OwR40eTJBoZhl74hdmU8
|
VegEx8+N5KphDROS4RgHxMiS1O8Qy5Hpq4gEp6RvLNj3s+0DMwqRZoA0tBCkNmvF
|
||||||
QvcKkJ32khSsOXMo60NHk5iIMDELzOGqq57NHhbunj6NtYog2mR5+WT37JLCvF/q
|
K7sF+GncrOu/NZkDIZ8emN9NWWeWynWJvuM2H5HQA9yCq+YTFae5sgyr3APC3xh4
|
||||||
dvYl2KkRwEHD76/b/O1CyheiiqNMGBb13zPN0qO/PRlENSgViLcDh9qVj4ETNNS6
|
OkTuVMcclGTJdVrISlNBDpo/AZLJ/nV0SgITpiZVsI4RSNiQPP1kX+2fIDEAwtxN
|
||||||
zMlP4D4pa07iED4Ua31wiXI04ReznnmvMqzQb8uFbg7d98F3bZpsbNURbmsFHMhj
|
3HIDyegDoX36cOItsb05zHkLxoUoZnWoxMvV3rwnqXg5cr6PWfiwgqWd0avl1sg6
|
||||||
c9qw65nzaMa3OHfZ3Qwl4XRlNqEzZrclIZADABEBAAG0DW1iZWxpYiBBdXRob3KJ
|
abpR6MKV68OCpTkqMrDIXJJrM2sTz+ZB2bBtABEBAAG0CkRTRCBBdXRob3KJATgE
|
||||||
ATgEEwECACIFAkufusYCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEE+Y
|
EwECACIFAkufuqsCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEPqiY10/
|
||||||
6GPqXv4sI/4H/RsumInT5GtDdBTgxt3lERqc5GF8tI5CAQX9DUMMkKxQ306Fgb4M
|
HX/Q11UH/0ZutGQWLZyZD5ZqVdBeYEf6IKhKhaC2vD/+5zUJ2cIgT5xJVTPCKN7X
|
||||||
ISYdepgL6BlbtSyMdKpKsDLScNA6YXER4gMU1nhhyBulqj41kAfsVbG8hiTBy5Ri
|
sygBANJc9GyPRYRvQecdcTHR5B+wUKCPeibv9iQuU3v7Uqb5edXEh2b4jOMWNvHJ
|
||||||
FTo36HrC//Zj1MlaTCB8Rl6B6IftAHIb1U+Gda3WnLDe+R5Pym/76TqeYx23PnJl
|
l3lqBdUefuQdbc2xXCPgn+GkyexXEBsVF3hCzWV9r4bCpY57iWUYZZT0wAbN81hz
|
||||||
V+qfrkjbNyOb2bttyko2iccm9DSq/I1OF0Ufz3P60ssUwHW/b2S6WOXRxiZIq0sz
|
jPKCCRmF+gZUUufxyhUGq/+eAKeUll6lgG5Ms2YKOlUMphfMav45qgWZHj1DASOV
|
||||||
8pAweUFCxIMWo+XYlSpRtwpebfx+JoHJKdW1cSCgY9jwVsOZO1zxc8P+jr3ByiIR
|
wPsiIVUaC6+GtHTTzHYRL05ydxxduX+yyKDO1emCE4fx1n3jdwToHLRrC1ui+AJM
|
||||||
ZPd0PfBt/AHuC6Hmw9nC12e49+4qsHkj50S5AQ0ES5+6xgEIALf60WjqHm552FIX
|
Q1s+bp+bGmMYaYaKc/JtNHcN8mbWyCK5AQ0ES5+6qwEIAJvPrnv2QO5Be3FRsnYL
|
||||||
KqmG0TIrXpCsYoTWzDdO5j0Ro6fmg9U48BqDH5aS4YRvuyPE7q6DGYEnXF5EIPtU
|
qbA1h/Lj0SjnuimaiNOzCYFIYtusLpyWjwWUnOEXdw5FoExqUStOHJ557SDG/zFB
|
||||||
FhUk4FMmez5eMg91UW4Qjqx8M3reoBROYocM5Nfbc9w7St8+3oRS6rUBIh/B6o4O
|
qy+DsNC9ncSRZ5U2tUQUtt50m2lpYvoy/sVf3oN+IsyZ5hZpiAwrxlyMC/aoGR3C
|
||||||
kEjkoW3R7O3uFpC53Oi9IVhx/joWxhi430RrUTig5CQzuPFTAHd3xJVE6Pv44nsY
|
6ZlLkZ42azNCmEmoBniIxQ+XwhwkLMRsOnqLoFyXB9CcDFIXeEhZkVWZT0B5+20f
|
||||||
/PsOhBLu2Z3kRkvjcGbsGDlAl2bDEobzQdsqXDD8gJkF5W4IeBKvRC54SE0FSm57
|
yGSBDJ6VcIAASoaIyvSwU9l/DT7D/s0J8WnZQZmRdrsZ3Ikj0Sv/4D2MJnCVJTSw
|
||||||
G1gadhZ7vT4DWQZvYYKXR+AE7FTcopoAFHdapYokOGLHCguabyeZ5q7+s7J4nddL
|
DHq5Sf7DmUq/WD0iWRLEXWmPt9w3WxP4imhtmIpICxCDoeQUTkqFpOTf29sVcpPt
|
||||||
p5yq8ikAEQEAAYkBHwQYAQIACQUCS5+6xgIbDAAKCRBPmOhj6l7+LPi6B/sEkDkm
|
838AEQEAAYkBHwQYAQIACQUCS5+6qwIbDAAKCRD6omNdPx1/0O8ECADAcPORjGFl
|
||||||
Qs/aTz9HDANBAbTuDZTY4WBbkPdFos3xxC2Z/TH5m4bfHEkvNk5WD+j+vYB3exAc
|
RnIrsgiVMp82GQ7hnZZxktU1WVVx8EsPTT1DD79nIoeKI+UcenzuOGyTX7DGcy+P
|
||||||
uBi7ZiqibRpap4kiAiKKCRXOyvmEmrjKB5wOWbRpkdTeEdPcpsQ4dDDw1ZUKwzjN
|
rWntUbt2ZygPTbP5Wu6zd845y9EzjxQ5q0vDF4oAefQptwqGDVeO/KBv9cZLbnih
|
||||||
1jI/9GOgq49nAGdlU59f3IdPut6oQNq9vrtanTq2xHVN/rOhV9GLE6gLX1YWkpIH
|
VEjQELgC/rVB7nd5p83EL5vb8qL/Eiu65stxwZ3QOT9pp2bsAPk6LJWWnVqJroKI
|
||||||
zYSUheGPsnv4sWRsQDTB7xmF+6RZzjmauKNwsGFscX1TenOWtUbYb9qFCwvNMOSk
|
Pc6KWG7n6mMJPbhFmE8Ld2lcNlSKQwLHIxDocL2GAqh5S5hJeGg2oLMFpy0g2Ron
|
||||||
51sEaD6PK6wWTXMqQlgjaoGqh1ZaePgT0QTh1W/bvOD0fL5NEToJl2h/lBSvO+ez
|
643w0zPfpKC15TlnkJDCIgtRlHovrs42Qkypz9y3e8yOL9O0RLYIKbuKw4mNR3cT
|
||||||
R9B++dt5JX9BMxAH
|
OdeV+TjfiZ5I
|
||||||
=puA7
|
=2oe6
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
|||||||
109
Makefile
109
Makefile
@@ -1,109 +0,0 @@
|
|||||||
# Copyright (C) 2010 DSD Author
|
|
||||||
# GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
#
|
|
||||||
# Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
# purpose with or without fee is hereby granted, provided that the above
|
|
||||||
# copyright notice and this permission notice appear in all copies.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
CC = gcc
|
|
||||||
CFLAGS = -O2
|
|
||||||
INCLUDES = -I. -I/usr/local/include -I/usr/include
|
|
||||||
LIBS = -L/usr/local/lib -lm -lmbe
|
|
||||||
INSTALL=install
|
|
||||||
AR=ar
|
|
||||||
RANLIB=ranlib
|
|
||||||
DEST_BASE=/usr/local
|
|
||||||
DEST_INC=${DEST_BASE}/include
|
|
||||||
DEST_LIB=${DEST_BASE}/lib
|
|
||||||
DEST_BIN=${DEST_BASE}/bin
|
|
||||||
OBJS = dsd_main.o dsd_symbol.o dsd_dibit.o dsd_frame_sync.o dsd_file.o dsd_audio.o dsd_serial.o dsd_frame.o dsd_mbe.o dsd_upsample.o p25p1_hdu.o p25p1_ldu1.o p25p1_ldu2.o p25p1_tdulc.o p25_lcw.o x2tdma_voice.o x2tdma_data.o dstar.o nxdn96.o dmr_voice.o dmr_data.o provoice.o
|
|
||||||
|
|
||||||
all: dsd
|
|
||||||
|
|
||||||
build: all
|
|
||||||
|
|
||||||
dsd_main.o: dsd_main.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_main.c -o dsd_main.o
|
|
||||||
|
|
||||||
dsd_symbol.o: dsd_symbol.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_symbol.c -o dsd_symbol.o
|
|
||||||
|
|
||||||
dsd_dibit.o: dsd_dibit.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_dibit.c -o dsd_dibit.o
|
|
||||||
|
|
||||||
dsd_frame_sync.o: dsd_frame_sync.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_frame_sync.c -o dsd_frame_sync.o
|
|
||||||
|
|
||||||
dsd_file.o: dsd_file.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_file.c -o dsd_file.o
|
|
||||||
|
|
||||||
dsd_audio.o: dsd_audio.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_audio.c -o dsd_audio.o
|
|
||||||
|
|
||||||
dsd_serial.o: dsd_serial.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_serial.c -o dsd_serial.o
|
|
||||||
|
|
||||||
dsd_frame.o: dsd_frame.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_frame.c -o dsd_frame.o
|
|
||||||
|
|
||||||
dsd_mbe.o: dsd_mbe.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_mbe.c -o dsd_mbe.o
|
|
||||||
|
|
||||||
dsd_upsample.o: dsd_upsample.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_upsample.c -o dsd_upsample.o
|
|
||||||
|
|
||||||
p25p1_hdu.o: p25p1_hdu.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c p25p1_hdu.c -o p25p1_hdu.o
|
|
||||||
|
|
||||||
p25p1_ldu1.o: p25p1_ldu1.c p25p1_const.h dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c p25p1_ldu1.c -o p25p1_ldu1.o
|
|
||||||
|
|
||||||
p25p1_ldu2.o: p25p1_ldu2.c p25p1_const.h dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c p25p1_ldu2.c -o p25p1_ldu2.o
|
|
||||||
|
|
||||||
p25p1_tdulc.o: p25p1_tdulc.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c p25p1_tdulc.c -o p25p1_tdulc.o
|
|
||||||
|
|
||||||
p25_lcw.o: p25_lcw.c dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c p25_lcw.c -o p25_lcw.o
|
|
||||||
|
|
||||||
x2tdma_voice.o: x2tdma_voice.c x2tdma_const.h dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c x2tdma_voice.c -o x2tdma_voice.o
|
|
||||||
|
|
||||||
x2tdma_data.o: x2tdma_data.c x2tdma_const.h dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c x2tdma_data.c -o x2tdma_data.o
|
|
||||||
|
|
||||||
dstar.o: dstar.c dstar_const.h dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dstar.c -o dstar.o
|
|
||||||
|
|
||||||
nxdn96.o: nxdn96.c nxdn96_const.h dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c nxdn96.c -o nxdn96.o
|
|
||||||
|
|
||||||
dmr_voice.o: dmr_voice.c dmr_const.h dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dmr_voice.c -o dmr_voice.o
|
|
||||||
|
|
||||||
dmr_data.o: dmr_data.c dmr_const.h dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c dmr_data.c -o dmr_data.o
|
|
||||||
|
|
||||||
provoice.o: provoice.c provoice_const.h dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) -c provoice.c -o provoice.o
|
|
||||||
|
|
||||||
dsd: $(OBJS) dsd.h config.h
|
|
||||||
$(CC) $(CFLAGS) $(LIBS) -o dsd $(OBJS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.o
|
|
||||||
rm -f dsd
|
|
||||||
|
|
||||||
install: dsd
|
|
||||||
$(INSTALL) dsd $(DEST_BIN)
|
|
||||||
chgrp uucp $(DEST_BIN)/dsd
|
|
||||||
chmod g+s $(DEST_BIN)/dsd
|
|
||||||
520
README
520
README
@@ -1,520 +0,0 @@
|
|||||||
Digital Speech Decoder 1.3
|
|
||||||
Copyright (C) 2010 DSD Author
|
|
||||||
GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
DSD is able to decode several digital voice formats from discriminator
|
|
||||||
tap audio and synthesize the decoded speech. Speech
|
|
||||||
synthesis requires mbelib, which is a separate package. DSD 1.3
|
|
||||||
requires mbelib 1.1 or later.
|
|
||||||
|
|
||||||
Supported formats in version 1.3:
|
|
||||||
|
|
||||||
P25 Phase 1 Widely deployed radio standard used in public safety
|
|
||||||
and amateur radio.
|
|
||||||
|
|
||||||
Support includes decoding and synthesis of speech,
|
|
||||||
display of all link control info, and the ability to save
|
|
||||||
and replay .imb data files
|
|
||||||
|
|
||||||
ProVoice EDACS Digital voice format used by public safety and
|
|
||||||
amateur radio.
|
|
||||||
|
|
||||||
Support includes decoding and synthesis of speech and
|
|
||||||
the ability to save and replay .imb data files.
|
|
||||||
Note: not enabled by default, use -fp to enable.
|
|
||||||
|
|
||||||
X2-TDMA Two slot TDMA system currently being deployed by several
|
|
||||||
public safety organizations. Based on the DMR
|
|
||||||
standard with extensions for P25 style signaling.
|
|
||||||
|
|
||||||
Support includes decoding and synthesis of speech,
|
|
||||||
display of all link control info, and the ability to save
|
|
||||||
and replay .amb data files
|
|
||||||
|
|
||||||
DMR/MOTOTRBO "Digital Mobile Radio" Eurpoean two slot TDMA standard.
|
|
||||||
MOTOTRBO is a popular implementation of this standard.
|
|
||||||
|
|
||||||
Support includes decoding and synthesis of speech and
|
|
||||||
the ability to save and replay .amb data files.
|
|
||||||
|
|
||||||
Development (no speech) support only:
|
|
||||||
|
|
||||||
NXDN (9600) Digital radio standard used by NEXEDGE and IDAS brands.
|
|
||||||
|
|
||||||
Development support only. DSD recognizes frames and can
|
|
||||||
extract the voice bits but speech is not yet decoded.
|
|
||||||
The issue is likely either an unusual interleave pattern
|
|
||||||
or a pseudo-random bit mask initialized by a "color code"
|
|
||||||
Note: not enabled by default, use -fn to enable.
|
|
||||||
|
|
||||||
D-STAR Amateur radio digital voice standard
|
|
||||||
|
|
||||||
Development support only. DSD recognized frames and can
|
|
||||||
extract the voice bits but speech is not yet decoded.
|
|
||||||
D-STAR likely uses a version of AMBE not yet supported by
|
|
||||||
mbelib. The voice bit interleave pattern also needs to be
|
|
||||||
determined.
|
|
||||||
Note: not enabled by default, use -fn to enable.
|
|
||||||
|
|
||||||
Unsupported formats in version 1.3 considered for future development:
|
|
||||||
|
|
||||||
P25 Phase 2 This is not yet a published standard. Full support is
|
|
||||||
expected once the standard is published and there are
|
|
||||||
systems operating to test against. Phase 2 will use
|
|
||||||
a vocoder supported by mbelib.
|
|
||||||
|
|
||||||
OpenSKY It is possible that the four slot version uses a vocoder
|
|
||||||
supported by mbelib. The two slot version does not.
|
|
||||||
|
|
||||||
NXDN (4800) This should be supportable as soon as we have .wav files
|
|
||||||
or access to a radio programmed in this format.
|
|
||||||
|
|
||||||
Supported demodulation optimizations in version 1.3:
|
|
||||||
|
|
||||||
C4FM Continuous envelope 2 or 4 level FSK with relatively
|
|
||||||
sharp transitions between symbols. Used by most P25
|
|
||||||
systems.
|
|
||||||
|
|
||||||
Optimizations include calibrating decision points only
|
|
||||||
during sync, 4/10 sample window per symbol, and symbol
|
|
||||||
edge timing calibration.
|
|
||||||
|
|
||||||
GFSK Continuous envelope 2 or 4 level FSK with a narrower
|
|
||||||
Gaussian/"raised cosine" filter that affects transitions
|
|
||||||
between symbols. Used by DMR/MOTOTRBO, NXDN and many
|
|
||||||
others. Noisy C4FM signals may be detected as GFSK
|
|
||||||
|
|
||||||
but this is ok, the optimization changes will help with
|
|
||||||
noisy signals.
|
|
||||||
|
|
||||||
Optimizations are similar to C4FM except symbol transitions
|
|
||||||
are only kept out of the middle 4 samples and only the
|
|
||||||
middle two samples are used.
|
|
||||||
|
|
||||||
QPSK Quadrature Phase Shift Keying (and variants) used in
|
|
||||||
some P25 systems and all known X2-TDMA systems. May be
|
|
||||||
advertised under the marketing term "LSM"
|
|
||||||
|
|
||||||
Optimizations include continuous decision point
|
|
||||||
calibration, using middle two samples, and using the
|
|
||||||
symbol midpoint "spike" for symbol timing.
|
|
||||||
|
|
||||||
Installation
|
|
||||||
|
|
||||||
DSD should easily compile on any Linux or *BSD system with gcc.
|
|
||||||
Just untar and run "make" or "make install". There are some debugging/
|
|
||||||
development options in config.h that normal users will want to leave
|
|
||||||
disabled as they can severely impact performance.
|
|
||||||
|
|
||||||
Operation
|
|
||||||
|
|
||||||
There are two main operating modes, "Live scanner" and "Play files"
|
|
||||||
|
|
||||||
Usage: dsd [options] Live scanner mode
|
|
||||||
|
|
||||||
Live Scanner mode takes 48KHz/16 bit mono audio samples from a
|
|
||||||
sound card input and decodes speech in real time. Options are provided
|
|
||||||
for controling information display and saving mbe data files.
|
|
||||||
The synthesized speech can be output to a soundcard and/or a
|
|
||||||
.wav file.
|
|
||||||
|
|
||||||
Usage: dsd [options] -r <files> Read/Play saved mbe data from file(s)
|
|
||||||
|
|
||||||
|
|
||||||
Play files mode reads mbe data from files specified on the command
|
|
||||||
line (including wildcards) and synthesizes speech from those files.
|
|
||||||
The synthesized speech can be output to a soundcard and/or a
|
|
||||||
.wav file. The -r command line options is used to activate Play files
|
|
||||||
mode.
|
|
||||||
|
|
||||||
Display modes
|
|
||||||
|
|
||||||
There are two main display modes in Live scanner mode. "Errorbars"
|
|
||||||
and "Datascope".
|
|
||||||
|
|
||||||
Errorbars mode output for P25 Phase 1 looks like this:
|
|
||||||
|
|
||||||
|
|
||||||
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 0 tg: 32464 TDULC
|
|
||||||
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 0 tg: 32464 TDULC
|
|
||||||
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 0 tg: 32464 TDULC
|
|
||||||
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 0 tg: 32464 TDULC
|
|
||||||
Sync: -p25p1 mod: C4FM input: 38% nac: 5C2 src: 0 tg: 32464 TDU
|
|
||||||
Sync: -p25p1 mod: C4FM input: 38% nac: 5C2 src: 0 tg: 32464 HDU
|
|
||||||
Sync: -p25p1 mod: C4FM input: 42% nac: 5C2 src: 0 tg: 32464 LDU1 e:
|
|
||||||
Sync: (-p25p1) mod: C4FM input: 39% nac: 5C2 src: 52610 tg: 32464 (LDU2) e:
|
|
||||||
Sync: -p25p1 mod: C4FM input: 38% nac: 5C2 src: 52610 tg: 32464 LDU1 e:
|
|
||||||
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 52610 tg: 32464 LDU2 e:
|
|
||||||
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 52610 tg: 32464 LDU1 e:
|
|
||||||
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 52610 tg: 32464 LDU2 e:
|
|
||||||
Sync: -p25p1 mod: C4FM input: 39% nac: 5C2 src: 52610 tg: 32464 LDU1 e:
|
|
||||||
|
|
||||||
"Sync" indicates the frame type detected and whether the polarity is
|
|
||||||
positive or negative. DSD automatically detects and handles either
|
|
||||||
polarity except for DMR/MOTOTRBO/X2-TDMA which unfortunatley use both
|
|
||||||
sync polarities.
|
|
||||||
|
|
||||||
Most combinations of transmitter, receiver and soundcard show netagive
|
|
||||||
(-) polarity for X2-TDMA signals and (+) polarity for DMR/MOTOTRBO so
|
|
||||||
those are the defaults.
|
|
||||||
|
|
||||||
********
|
|
||||||
You may need to use the -x option to select non-inverted polarity if
|
|
||||||
you are not getting usable X2-TDMA/MOTOTRBO/DMR speech. As they use both
|
|
||||||
normal and inverted sync it is not possible to detect polariy
|
|
||||||
automatically.
|
|
||||||
********
|
|
||||||
|
|
||||||
"mod" indicates the current demodulation optimizations.
|
|
||||||
|
|
||||||
"input" indicates the audio input level. QPSK signals tend to appear
|
|
||||||
much "wider" than C4FM from a discriminator tap so it is important
|
|
||||||
to set your input gain using a QPSK signal if you plan to montir them.
|
|
||||||
It is not necessary nor desirable to get to 100%, in fact your sound
|
|
||||||
card may max out below 100%. It is best to use the Datascope mode for
|
|
||||||
setting input gain (see below). Typical values with good results are
|
|
||||||
40% for C4FM and 66% for QPSK.
|
|
||||||
|
|
||||||
"nac" is the P25 Phase 1 Network Access Code. This is a 12 bit field
|
|
||||||
in each P25 Phase 1 header. It should not be confused with the 16
|
|
||||||
bit System ID used in non-p25 trunking control channels.
|
|
||||||
|
|
||||||
"src" is the radio id of the trasmitting subscriber unit.
|
|
||||||
|
|
||||||
"tg" is the talkgroup derived from link control information.
|
|
||||||
|
|
||||||
"HDU/LDU1/LDU2/TDU/TDULC" are P25 Phase 1 frame types, referred to as
|
|
||||||
frame subtype within DSD.
|
|
||||||
|
|
||||||
"e:" is the beginning of the errorbars display. Each "=" indicates a
|
|
||||||
detected error within the voice data. "R" and "M" indicat that a voice
|
|
||||||
frame was repeated or muted due to excessive errors.
|
|
||||||
|
|
||||||
Values in parentheses () indicate an assumption (soft decision) was
|
|
||||||
made based on the previous frame.
|
|
||||||
|
|
||||||
Errorbars mode output for X2-TDMA looks like this:
|
|
||||||
|
|
||||||
Sync: -X2-TDMA mod: QPSK input: 59% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
|
|
||||||
Sync: -X2-TDMA mod: QPSK input: 47% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
|
|
||||||
Sync: -X2-TDMA mod: QPSK input: 43% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
|
|
||||||
Sync: (-X2-TDMA) mod: QPSK input: 28% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
|
|
||||||
|
|
||||||
DMR/MOTOTRBO display is similar except it does not yet show source
|
|
||||||
and talkgroup information.
|
|
||||||
|
|
||||||
As of version 1.2 DSD shows which specific TDMA slots are active (with
|
|
||||||
capital SLOT letters) and which slot is currently being monitored (with
|
|
||||||
square brackets []. Noisy/degraded signals will affect the accuracy
|
|
||||||
of this display.
|
|
||||||
|
|
||||||
The frame subtypes (Voice/LC etc) are shown based on the DMR standard
|
|
||||||
types.
|
|
||||||
|
|
||||||
Datascope mode output looks like this:
|
|
||||||
|
|
||||||
|
|
||||||
Demod mode: C4FM Nac: 8C3
|
|
||||||
Frame Type: P25 Phase 1 Talkgroup: 16528
|
|
||||||
Frame Subtype: LDU1 Source: 0
|
|
||||||
TDMA activity: slot0 slot1 Voice errors:
|
|
||||||
+----------------------------------------------------------------+
|
|
||||||
| # ^ !| ^ # |
|
|
||||||
| * | * |
|
|
||||||
| * | * |
|
|
||||||
| * | * * |
|
|
||||||
| * * | * * |
|
|
||||||
| * * | ** * |
|
|
||||||
| * ** | ** * |
|
|
||||||
| ** ** | ** * |
|
|
||||||
| ** ** | ** * |
|
|
||||||
| ** ** | ** * |
|
|
||||||
+----------------------------------------------------------------+
|
|
||||||
C4FM Example
|
|
||||||
|
|
||||||
Demod mode: C4FM Nac: 126
|
|
||||||
Frame Type: P25 Phase 1 Talkgroup: 25283
|
|
||||||
Frame Subtype: LDU2 Source: 0
|
|
||||||
TDMA activity: slot0 slot1 Voice errors:
|
|
||||||
+----------------------------------------------------------------+
|
|
||||||
| # ^ ! ^ # |
|
|
||||||
| * | |
|
|
||||||
| * | |
|
|
||||||
| ** | |
|
|
||||||
| ** | * |
|
|
||||||
| * ** | * * |
|
|
||||||
| ** ** | * * |
|
|
||||||
| *** ** | ** * |
|
|
||||||
| *** ** | *** * |
|
|
||||||
| *** **** | **** * * |
|
|
||||||
+----------------------------------------------------------------+
|
|
||||||
QPSK Example
|
|
||||||
|
|
||||||
|
|
||||||
At the top is various information about the signal, similar to the
|
|
||||||
information provided in Errorbars mode. The large box is similar to
|
|
||||||
a spectrum analyzer viewing the channel bandwidth.
|
|
||||||
|
|
||||||
The horizontal axis is the input audio level, minimum on the left and
|
|
||||||
maximum on the right. The vertical axis is the number of samples
|
|
||||||
seend at each audio level.
|
|
||||||
|
|
||||||
The "*" symbols represent the number of audio
|
|
||||||
samples that were at each level during the aggregation period.
|
|
||||||
(default = 36 symbols) The -S options controls the aggregation period
|
|
||||||
as well as the QPSK tracking symbol buffer, so changing that will affect
|
|
||||||
QPSK performance as well as the Datascope display.
|
|
||||||
|
|
||||||
As you can see from the figures above, clean C4FM signals tend to have
|
|
||||||
four very sharply defined audio levels. The datascope pattern also
|
|
||||||
tends to be faily stable with minor shifts left and right as the
|
|
||||||
receiver tries to frequency track any DC offset.
|
|
||||||
|
|
||||||
QPSK signals on the other hand tend to appear much broader (and artifact
|
|
||||||
of how they are distored by FM PLL discriminators). They also tend
|
|
||||||
to vary wildly in width and centering. This is especially true when
|
|
||||||
monitoring simulcast systems. Muliple QPSK signals interfere much more
|
|
||||||
dramatically with an FM discriminator than C4FM signals.
|
|
||||||
|
|
||||||
*******
|
|
||||||
For this reason it is important to isolate your receiver to one
|
|
||||||
transmitter tower, _especially_ for QPSK signals.
|
|
||||||
*******
|
|
||||||
|
|
||||||
The "#" symbols indicate the detected min/max values that are used
|
|
||||||
to calibrate the symbol decision points. These are indicated by
|
|
||||||
"!" for the center decision point and "^" for the mid decision points.
|
|
||||||
|
|
||||||
Display Options
|
|
||||||
|
|
||||||
There are several options to control the type and quantity of
|
|
||||||
information displayed in Errorbars mode:
|
|
||||||
|
|
||||||
-e Show Frame Info and errorbars (default)
|
|
||||||
-pe Show p25 encryption sync bits
|
|
||||||
-pl Show p25 link control bits
|
|
||||||
-ps Show p25 status bits and low speed data
|
|
||||||
-pt Show p25 talkgroup info
|
|
||||||
-q Don't show Frame Info/errorbars
|
|
||||||
-s Datascope (disables other display options)
|
|
||||||
-t Show symbol timing during sync
|
|
||||||
-v <num> Frame information Verbosity
|
|
||||||
-z <num> Frame rate for datascope
|
|
||||||
|
|
||||||
Most of these options are self explanitory. Symbol timing is a noisy
|
|
||||||
option that allows you to view the quality of the frame sync samples
|
|
||||||
and accuracy of the symbol timing adjustments.
|
|
||||||
|
|
||||||
Symbol Timing display looks like this:
|
|
||||||
|
|
||||||
Symbol Timing:
|
|
||||||
----------
|
|
||||||
----------
|
|
||||||
----------
|
|
||||||
----------
|
|
||||||
----------
|
|
||||||
-+++++++++ 1
|
|
||||||
+---------- 0
|
|
||||||
----------
|
|
||||||
++++++++++ 0
|
|
||||||
++++++++++
|
|
||||||
---------- 0
|
|
||||||
----------
|
|
||||||
++++++++++ 0
|
|
||||||
++++++++++
|
|
||||||
++++++++++
|
|
||||||
++++++++++
|
|
||||||
---------- 0
|
|
||||||
++++++++++ 0
|
|
||||||
---------- 0
|
|
||||||
++++++++++ 0
|
|
||||||
++++++++++
|
|
||||||
++++++++++
|
|
||||||
++++++++++
|
|
||||||
++++++++++
|
|
||||||
C4FM example
|
|
||||||
|
|
||||||
Symbol Timing:
|
|
||||||
+---------
|
|
||||||
----------
|
|
||||||
----------
|
|
||||||
----------
|
|
||||||
-----X---- 5
|
|
||||||
--+++O++++- 4
|
|
||||||
----------
|
|
||||||
----X----- 4
|
|
||||||
++++O++--- 4
|
|
||||||
--++O++++- 4
|
|
||||||
----X----- 4
|
|
||||||
----------
|
|
||||||
++++O+++-- 4
|
|
||||||
-+++O+++-- 4
|
|
||||||
--++O+++-- 4
|
|
||||||
--++O+++-- 4
|
|
||||||
----------
|
|
||||||
++++O++++- 4
|
|
||||||
----------
|
|
||||||
++++O+++-- 4
|
|
||||||
-+++O++++- 4
|
|
||||||
-+++O+++++ 4
|
|
||||||
-+++O++--- 4
|
|
||||||
--++O+++-- 4
|
|
||||||
QPSK example
|
|
||||||
|
|
||||||
Symbol timing is only displayed for symbols during the frame sync
|
|
||||||
period. Each horizontal line represents the 10 audio samples for each
|
|
||||||
symbol. "-" indicates an audio sample below the center reference level
|
|
||||||
and "+" represents a sample above center. "X" indicates a low spike
|
|
||||||
below a reference threshold (reference minimum for C4FM and 80%
|
|
||||||
of reference minimum for QPSK). "O" represents a high spike above
|
|
||||||
the high reference threshold. The numbers to the right indicate which
|
|
||||||
sample position the targeted transition occurred (+/- for C4FM or
|
|
||||||
spike high/low for QPSK). The number of audio samples for the next
|
|
||||||
symbol are adjusted to get this value closer to the target (0 for
|
|
||||||
C4FM and 4 for QPSK). This shows how DSD maintains accurate symbol
|
|
||||||
timing. Symbol timing adjustments are only made during sync, which
|
|
||||||
is the only time reliable transitions can be observed.
|
|
||||||
|
|
||||||
In both examples above the symbol timing was off by one sample at
|
|
||||||
the beginning of the frame sync period and was adjusted. Generally
|
|
||||||
if you see any spike values "X/O" in C4FM mode, or lots of them in
|
|
||||||
QPSK mode it indicates noise on the input signal.
|
|
||||||
|
|
||||||
Input/Output Options
|
|
||||||
|
|
||||||
-i <device> Audio input device (default is /dev/audio)
|
|
||||||
-o <device> Audio output device (default is /dev/audio)
|
|
||||||
-d <dir> Create mbe data files, use this directory
|
|
||||||
-g <num> Audio output gain (default = 0 = auto)
|
|
||||||
-n Do not send synthesized speech to audio output device
|
|
||||||
-w <file> Output synthesized speech to a .wav file
|
|
||||||
|
|
||||||
The audio in device can be a sound card OR a .wav file if the file
|
|
||||||
is in the exact format 48k/16bits/mono/pcm. Audio in should be an
|
|
||||||
unfilterd discriminator tap signal.
|
|
||||||
|
|
||||||
The audio out device should be a sound card (use the -w options to
|
|
||||||
output to a .wav file).
|
|
||||||
|
|
||||||
If the audio in device is the same as the audio out device, the
|
|
||||||
synthesized speech has to be upsampled to the 48k sample rate required
|
|
||||||
for input. A fast upsample function is provided but still leaves some
|
|
||||||
artifacts.
|
|
||||||
|
|
||||||
********
|
|
||||||
The best sound and minimum cpu usage is achieved with separate sound
|
|
||||||
cards for input and output
|
|
||||||
********
|
|
||||||
|
|
||||||
if you specify different input/output devices DSD will use 8k as the
|
|
||||||
output sample rate and the lack of resampling results in much better
|
|
||||||
audio as well as lowe cpu consumption.
|
|
||||||
|
|
||||||
If you are using onboard "AC97" sound device you may find that DSD uses
|
|
||||||
much more cpu than expected, in some cases more than is available.
|
|
||||||
This is because many AC97 sound devices are designed to rely on CPU
|
|
||||||
processing power instead of hardware. You may also find that 8k sample
|
|
||||||
rate output is upsampled in the driver using a very basic algorithim
|
|
||||||
resulting in severe distortion. The solution is to use a real hardware
|
|
||||||
sound device (pci card, usb device etc).
|
|
||||||
|
|
||||||
As of version 1.2 DSD now automatically levels the output audio. This
|
|
||||||
greately improves readability and eliminates the painful effects of
|
|
||||||
noise bursts. You can specify a fixed audio output gain with the -g
|
|
||||||
option.
|
|
||||||
|
|
||||||
Scanner control options:
|
|
||||||
|
|
||||||
-B <num> Serial port baud rate (default=115200)
|
|
||||||
-C <device> Serial port for scanner control (default=/dev/ttyUSB0)
|
|
||||||
-R <num> Resume scan after <num> TDULC frames or any PDU
|
|
||||||
|
|
||||||
On some P25 systems Packet Data Units (PDU) are sent on the same
|
|
||||||
frequencies used for voice traffic. If done constantly this can
|
|
||||||
be a severe hinderance to scanning the system in conventional
|
|
||||||
mode. The -R option enables sending a "resume scan" command to
|
|
||||||
a scanner connected to a serial port. Use -B and -C to set the baud
|
|
||||||
rate and serial port device if necessary.
|
|
||||||
|
|
||||||
Decoder options
|
|
||||||
|
|
||||||
-fa Auto-detect frame type (default)
|
|
||||||
-f1 Decode only P25 Phase 1
|
|
||||||
-fd Decode only D-STAR* (no audio)
|
|
||||||
-fn Decode only NXDN96* (no audio)
|
|
||||||
-fp Decode only ProVoice*
|
|
||||||
-fr Decode only DMR/MOTOTRBO
|
|
||||||
-fx Decode only X2-TDMA
|
|
||||||
-ma Auto-select modulation optimizations (default)
|
|
||||||
-mc Use only C4FM modulation optimizations
|
|
||||||
-mg Use only GFSK modulation optimizations
|
|
||||||
-mq Use only QPSK modulation optimizations
|
|
||||||
-u <num> Unvoiced speech quality (default=3)
|
|
||||||
-xx Expect non-inverted X2-TDMA signal
|
|
||||||
-xr Expect inverted DMR/MOTOTRBO signal
|
|
||||||
|
|
||||||
|
|
||||||
NXDN and D-STAR are not enabled by default as they are for development
|
|
||||||
testing only and have shorter frame sync patterns that are prone to
|
|
||||||
false triggering.
|
|
||||||
|
|
||||||
MBE speech synthesis is broken down into two main types of sounds,
|
|
||||||
"Voiced" and "Unvoiced". Voiced speech bands are synthesized with
|
|
||||||
a single sine wave centered in the frequency band with the appropriate
|
|
||||||
phase and amplitude.
|
|
||||||
|
|
||||||
Unvoiced speech is supposed to be generated with a noise source, 256
|
|
||||||
point DFT a number of band filters, followed by a 256 point inverse DFT.
|
|
||||||
For computational simplicity mbelib uses a different method. For each
|
|
||||||
unvoiced speech band, a number of sine waves are generated, each with a
|
|
||||||
different random initial phase. The number of waves used per band is
|
|
||||||
controlled by the -u option. A setting of 4 would approximate the
|
|
||||||
performance of the 256 point DFT method as the maximum number of voice
|
|
||||||
bands is 56, and very low frequencies are not synthesized. Values less
|
|
||||||
than 3 have a noticable lack of unvoiced speech and/or artifacts. The
|
|
||||||
defualt of 3 provides good speech quality with reasonable cpu use.
|
|
||||||
Increasing the quality above the default rapidly consumes more CPU for
|
|
||||||
increasingly diminishing returns.
|
|
||||||
|
|
||||||
|
|
||||||
Advanced decoder options
|
|
||||||
-A <num> QPSK modulation auto detection threshold (default=26)
|
|
||||||
-S <num> Symbol buffer size for QPSK decision point tracking
|
|
||||||
(default=36)
|
|
||||||
-M <num> Min/Max buffer size for QPSK decision point tracking
|
|
||||||
(default=15)
|
|
||||||
|
|
||||||
Encryption
|
|
||||||
|
|
||||||
********
|
|
||||||
Decryption of speech is NOT supported, even if you lawfully posess the
|
|
||||||
encryption keys. Decryption support will not be added in the future as
|
|
||||||
the authors wish to steer as far away from the legal issues associated
|
|
||||||
with encryption as possible.
|
|
||||||
********
|
|
||||||
|
|
||||||
We realize that there are many legitemate and lawful uses of decryption
|
|
||||||
software including system/interoperability testing and lawful monitoring.
|
|
||||||
This software is distributed under a liberal BSD license so there is
|
|
||||||
nothing to stop others from supplying patches, forking this project or
|
|
||||||
incorporating it into a commercial product and adding decryption support.
|
|
||||||
|
|
||||||
There is support for displaying the encryption sync bits transmitted in
|
|
||||||
the clear on P25 Phase 1 systems. These bits do not allow for the
|
|
||||||
decryption of signals without the secret encryption keys. The
|
|
||||||
encryption sync bits are useful for determining whether a signal is
|
|
||||||
encrypted vs merely noisy or degraded. As the encryption sync bits
|
|
||||||
typically include long strings of zeros when a transmission is not
|
|
||||||
encrypted they can also be used to visually estimate bit error rates.
|
|
||||||
32
README.md
Normal file
32
README.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
# Digital Speech Decoder - Florida Man Edition
|
||||||
|
|
||||||
|
DSD-FME is an evolution of the original DSD project from 'DSD Author' using the base code of [szechyjs](https://github.com/szechyjs/dsd "szechyjs"), some code and ideas from [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve"), [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25") and
|
||||||
|
[Osmocom OP25](https://gitea.osmocom.org/op25/op25 "Osmocom OP25"), along with other snippets of code, information, and inspirations from other projects including [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDRTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), [OK-DMRlib](https://github.com/OK-DMR/ok-dmrlib "OK-DMRlib"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"), Eric Cottrell, SP5WWP and others. Finally, this is all brought together with original code to extend the fuctionality and add new features including NCurses Terminal and Menu system, Pulse Audio, TCP Direct Link Audio, RIGCTL, Trunking Features, LRRP/GPS Mapping, P25 Phase 2, EDACS, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users in mind, so please understand that this version may not compile, compile easily, or run correctly in other environments.
|
||||||
|
|
||||||
|
This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu, Forts, thewraithe2008, RayAir, and others for the many hours of wav samples and information provided by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. I'd also like to thank mrscanner2008 for providing an additional remote where additional NXDN Type-C, 'Idas' Type-D, and XPT decoding and trunking could be sorted out. Thank you everybody.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Information
|
||||||
|
|
||||||
|
See the [examples](https://github.com/lwvmobile/dsd-fme/tree/main/examples "examples") folder for information on [cloning and installing](https://github.com/lwvmobile/dsd-fme/blob/main/examples/Install_Notes.md "cloning and installing"), [example usage](https://github.com/lwvmobile/dsd-fme/blob/main/examples/Example_Usage.md "example usage"), and [trunking examples](https://github.com/lwvmobile/dsd-fme/blob/main/examples/trunking.sh "trunking examples").
|
||||||
|
|
||||||
|
## 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"
|
|
||||||
251
dmr_data.c
251
dmr_data.c
@@ -1,251 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
processDMRdata (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
int i, dibit;
|
|
||||||
int *dibit_p;
|
|
||||||
char sync[25];
|
|
||||||
char syncdata[25];
|
|
||||||
char cachdata[13];
|
|
||||||
char cc[5];
|
|
||||||
char bursttype[5];
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
int k;
|
|
||||||
char syncbits[49];
|
|
||||||
char cachbits[25];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
cc[4] = 0;
|
|
||||||
bursttype[4] = 0;
|
|
||||||
|
|
||||||
dibit_p = state->dibit_buf_p - 90;
|
|
||||||
|
|
||||||
// CACH
|
|
||||||
for (i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
cachdata[i] = dibit;
|
|
||||||
if (i == 2)
|
|
||||||
{
|
|
||||||
state->currentslot = (1 & (dibit >> 1)); // bit 1
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
state->slot0light[0] = '[';
|
|
||||||
state->slot0light[6] = ']';
|
|
||||||
state->slot1light[0] = ' ';
|
|
||||||
state->slot1light[6] = ' ';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->slot1light[0] = '[';
|
|
||||||
state->slot1light[6] = ']';
|
|
||||||
state->slot0light[0] = ' ';
|
|
||||||
state->slot0light[6] = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cachdata[12] = 0;
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
k = 0;
|
|
||||||
for (i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
dibit = cachdata[i];
|
|
||||||
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
k++;
|
|
||||||
cachbits[k] = (1 & dibit) + 48; // bit 0
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
cachbits[24] = 0;
|
|
||||||
printf ("%s ", cachbits);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// current slot
|
|
||||||
dibit_p += 49;
|
|
||||||
|
|
||||||
// slot type
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
cc[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
cc[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
cc[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
cc[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
bursttype[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
bursttype[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
bursttype[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
bursttype[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
|
|
||||||
// parity bit
|
|
||||||
dibit_p++;
|
|
||||||
|
|
||||||
if (strcmp (bursttype, "0000") == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->fsubtype, " PI Header ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0001") == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->fsubtype, " VOICE Header ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0010") == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->fsubtype, " TLC ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0011") == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->fsubtype, " CSBK ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0100") == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->fsubtype, " MBC Header ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0101") == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->fsubtype, " MBC ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0110") == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->fsubtype, " DATA Header ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "0111") == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->fsubtype, " RATE 1/2 DATA");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "1000") == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->fsubtype, " RATE 3/4 DATA");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "1001") == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->fsubtype, " Slot idle ");
|
|
||||||
}
|
|
||||||
else if (strcmp (bursttype, "1010") == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->fsubtype, " Rate 1 DATA ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->fsubtype, " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// signaling data or sync
|
|
||||||
for (i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
syncdata[i] = dibit;
|
|
||||||
sync[i] = (dibit | 1) + 48;
|
|
||||||
}
|
|
||||||
sync[24] = 0;
|
|
||||||
syncdata[24] = 0;
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
k = 0;
|
|
||||||
for (i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
dibit = syncdata[i];
|
|
||||||
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
k++;
|
|
||||||
syncbits[k] = (1 & dibit) + 48; // bit 0
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
syncbits[48] = 0;
|
|
||||||
printf ("%s ", syncbits);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (strcmp (sync, DMR_DATA_SYNC) == 0)
|
|
||||||
{
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->slot0light, "[slot0]");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->slot1light, "[slot1]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcmp (sync, DMR_VOICE_SYNC) == 0)
|
|
||||||
{
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->slot0light, "[SLOT0]");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->slot1light, "[SLOT1]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("%s %s ", state->slot0light, state->slot1light);
|
|
||||||
}
|
|
||||||
|
|
||||||
// current slot second half, cach, next slot 1st half
|
|
||||||
skipDibit (opts, state, 120);
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
if (strcmp (state->fsubtype, " ") == 0)
|
|
||||||
{
|
|
||||||
printf (" Unknown burst type: %s\n", bursttype);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf ("%s\n", state->fsubtype);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
376
dmr_voice.c
376
dmr_voice.c
@@ -1,376 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
#include "dmr_const.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
processDMRvoice (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
// extracts AMBE frames from DMR frame
|
|
||||||
int i, j, dibit;
|
|
||||||
int *dibit_p;
|
|
||||||
char ambe_fr[4][24];
|
|
||||||
char ambe_fr2[4][24];
|
|
||||||
char ambe_fr3[4][24];
|
|
||||||
const int *w, *x, *y, *z;
|
|
||||||
char sync[25];
|
|
||||||
char syncdata[25];
|
|
||||||
char cachdata[13];
|
|
||||||
int mutecurrentslot;
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
int k;
|
|
||||||
char syncbits[49];
|
|
||||||
char cachbits[25];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
mutecurrentslot = 0;
|
|
||||||
|
|
||||||
dibit_p = state->dibit_buf_p - 144;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
// 2nd half of previous slot
|
|
||||||
for (i = 0; i < 54; i++)
|
|
||||||
{
|
|
||||||
if (j > 0)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CACH
|
|
||||||
for (i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
if (j > 0)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cachdata[i] = dibit;
|
|
||||||
if (i == 2)
|
|
||||||
{
|
|
||||||
state->currentslot = (1 & (dibit >> 1)); // bit 1
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
state->slot0light[0] = '[';
|
|
||||||
state->slot0light[6] = ']';
|
|
||||||
state->slot1light[0] = ' ';
|
|
||||||
state->slot1light[6] = ' ';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->slot1light[0] = '[';
|
|
||||||
state->slot1light[6] = ']';
|
|
||||||
state->slot0light[0] = ' ';
|
|
||||||
state->slot0light[6] = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cachdata[12] = 0;
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
k = 0;
|
|
||||||
for (i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
dibit = cachdata[i];
|
|
||||||
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
k++;
|
|
||||||
cachbits[k] = (1 & dibit) + 48; // bit 0
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
cachbits[24] = 0;
|
|
||||||
printf ("%s ", cachbits);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// current slot frame 1
|
|
||||||
w = rW;
|
|
||||||
x = rX;
|
|
||||||
y = rY;
|
|
||||||
z = rZ;
|
|
||||||
for (i = 0; i < 36; i++)
|
|
||||||
{
|
|
||||||
if (j > 0)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// current slot frame 2 first half
|
|
||||||
w = rW;
|
|
||||||
x = rX;
|
|
||||||
y = rY;
|
|
||||||
z = rZ;
|
|
||||||
for (i = 0; i < 18; i++)
|
|
||||||
{
|
|
||||||
if (j > 0)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// signaling data or sync
|
|
||||||
for (i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
if (j > 0)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dibit = *dibit_p;
|
|
||||||
dibit_p++;
|
|
||||||
if (opts->inverted_dmr == 1)
|
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
syncdata[i] = dibit;
|
|
||||||
sync[i] = (dibit | 1) + 48;
|
|
||||||
}
|
|
||||||
sync[24] = 0;
|
|
||||||
syncdata[24] = 0;
|
|
||||||
|
|
||||||
if (strcmp (sync, DMR_DATA_SYNC) == 0)
|
|
||||||
{
|
|
||||||
mutecurrentslot = 1;
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->slot0light, "[slot0]");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->slot1light, "[slot1]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcmp (sync, DMR_VOICE_SYNC) == 0)
|
|
||||||
{
|
|
||||||
mutecurrentslot = 0;
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->slot0light, "[SLOT0]");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->slot1light, "[SLOT1]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((j == 0) && (opts->errorbars == 1))
|
|
||||||
{
|
|
||||||
printf ("%s %s VOICE e:", state->slot0light, state->slot1light);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
k = 0;
|
|
||||||
for (i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
dibit = syncdata[i];
|
|
||||||
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
k++;
|
|
||||||
syncbits[k] = (1 & dibit) + 48; // bit 0
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
syncbits[48] = 0;
|
|
||||||
printf ("%s ", syncbits);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// current slot frame 2 second half
|
|
||||||
for (i = 0; i < 18; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mutecurrentslot == 0)
|
|
||||||
{
|
|
||||||
if (state->firstframe == 1)
|
|
||||||
{ // we don't know if anything received before the first sync after no carrier is valid
|
|
||||||
state->firstframe = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// current slot frame 3
|
|
||||||
w = rW;
|
|
||||||
x = rX;
|
|
||||||
y = rY;
|
|
||||||
z = rZ;
|
|
||||||
for (i = 0; i < 36; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
if (mutecurrentslot == 0)
|
|
||||||
{
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// CACH
|
|
||||||
for (i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
cachdata[i] = dibit;
|
|
||||||
}
|
|
||||||
cachdata[12] = 0;
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
k = 0;
|
|
||||||
for (i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
dibit = cachdata[i];
|
|
||||||
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
k++;
|
|
||||||
cachbits[k] = (1 & dibit) + 48; // bit 0
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
cachbits[24] = 0;
|
|
||||||
printf ("%s ", cachbits);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// next slot
|
|
||||||
skipDibit (opts, state, 54);
|
|
||||||
|
|
||||||
// signaling data or sync
|
|
||||||
for (i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
syncdata[i] = dibit;
|
|
||||||
sync[i] = (dibit | 1) + 48;
|
|
||||||
}
|
|
||||||
sync[24] = 0;
|
|
||||||
syncdata[24] = 0;
|
|
||||||
|
|
||||||
if (strcmp (sync, DMR_DATA_SYNC) == 0)
|
|
||||||
{
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->slot1light, " slot1 ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->slot0light, " slot0 ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcmp (sync, DMR_VOICE_SYNC) == 0)
|
|
||||||
{
|
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
|
||||||
sprintf (state->slot1light, " SLOT1 ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (state->slot0light, " SLOT0 ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DMR_DUMP
|
|
||||||
k = 0;
|
|
||||||
for (i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
dibit = syncdata[i];
|
|
||||||
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
k++;
|
|
||||||
syncbits[k] = (1 & dibit) + 48; // bit 0
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
syncbits[48] = 0;
|
|
||||||
printf ("%s ", syncbits);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (j == 5)
|
|
||||||
{
|
|
||||||
// 2nd half next slot
|
|
||||||
skipDibit (opts, state, 54);
|
|
||||||
|
|
||||||
// CACH
|
|
||||||
skipDibit (opts, state, 12);
|
|
||||||
|
|
||||||
// first half current slot
|
|
||||||
skipDibit (opts, state, 54);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
23
download-and-install-nodeps.sh
Normal file
23
download-and-install-nodeps.sh
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#! /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
|
||||||
|
sudo cp tone8.wav /usr/share/
|
||||||
|
sudo cp tone24.wav /usr/share/
|
||||||
|
sudo cp tone48.wav /usr/share/
|
||||||
|
sudo chmod 777 /usr/share/tone8.wav
|
||||||
|
sudo chmod 777 /usr/share/tone24.wav
|
||||||
|
sudo chmod 777 /usr/share/tone48.wav
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j `nproc`
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
67
download-and-install.sh
Normal file
67
download-and-install.sh
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#! /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
|
||||||
|
sudo cp tone8.wav /usr/share/
|
||||||
|
sudo cp tone24.wav /usr/share/
|
||||||
|
sudo cp tone48.wav /usr/share/
|
||||||
|
sudo chmod 777 /usr/share/tone8.wav
|
||||||
|
sudo chmod 777 /usr/share/tone24.wav
|
||||||
|
sudo chmod 777 /usr/share/tone48.wav
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j `nproc`
|
||||||
|
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-fme2.png
Normal file
BIN
dsd-fme2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
BIN
dsd-fme3.png
Normal file
BIN
dsd-fme3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 251 KiB |
218
dsd.h
218
dsd.h
@@ -1,218 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#define __USE_XOPEN
|
|
||||||
#include <time.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#ifdef SOLARIS
|
|
||||||
#include <sys/audioio.h>
|
|
||||||
#endif
|
|
||||||
#ifdef BSD
|
|
||||||
#include <sys/soundcard.h>
|
|
||||||
#endif
|
|
||||||
#include <math.h>
|
|
||||||
#include <mbelib.h>
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int onesymbol;
|
|
||||||
char mbe_in_file[1024];
|
|
||||||
FILE *mbe_in_f;
|
|
||||||
int errorbars;
|
|
||||||
int datascope;
|
|
||||||
int symboltiming;
|
|
||||||
int verbose;
|
|
||||||
int p25enc;
|
|
||||||
int p25lc;
|
|
||||||
int p25status;
|
|
||||||
int p25tg;
|
|
||||||
int scoperate;
|
|
||||||
char audio_in_dev[1024];
|
|
||||||
int audio_in_fd;
|
|
||||||
char audio_out_dev[1024];
|
|
||||||
int audio_out_fd;
|
|
||||||
int split;
|
|
||||||
int playoffset;
|
|
||||||
char mbe_out_dir[1024];
|
|
||||||
char mbe_out_file[1024];
|
|
||||||
FILE *mbe_out_f;
|
|
||||||
float audio_gain;
|
|
||||||
int audio_out;
|
|
||||||
char wav_out_file[1024];
|
|
||||||
FILE *wav_out_f;
|
|
||||||
int wav_out_fd;
|
|
||||||
int serial_baud;
|
|
||||||
char serial_dev[1024];
|
|
||||||
int serial_fd;
|
|
||||||
int resume;
|
|
||||||
int frame_dstar;
|
|
||||||
int frame_x2tdma;
|
|
||||||
int frame_p25p1;
|
|
||||||
int frame_nxdn;
|
|
||||||
int frame_dmr;
|
|
||||||
int frame_provoice;
|
|
||||||
int mod_c4fm;
|
|
||||||
int mod_qpsk;
|
|
||||||
int mod_gfsk;
|
|
||||||
int uvquality;
|
|
||||||
int inverted_x2tdma;
|
|
||||||
int inverted_dmr;
|
|
||||||
int mod_threshold;
|
|
||||||
int ssize;
|
|
||||||
int msize;
|
|
||||||
int playfiles;
|
|
||||||
int delay;
|
|
||||||
} dsd_opts;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int *dibit_buf;
|
|
||||||
int *dibit_buf_p;
|
|
||||||
int repeat;
|
|
||||||
short *audio_out_buf;
|
|
||||||
short *audio_out_buf_p;
|
|
||||||
float *audio_out_float_buf;
|
|
||||||
float *audio_out_float_buf_p;
|
|
||||||
float audio_out_temp_buf[160];
|
|
||||||
float *audio_out_temp_buf_p;
|
|
||||||
int audio_out_idx;
|
|
||||||
int audio_out_idx2;
|
|
||||||
int center;
|
|
||||||
int jitter;
|
|
||||||
int synctype;
|
|
||||||
int min;
|
|
||||||
int max;
|
|
||||||
int lmid;
|
|
||||||
int umid;
|
|
||||||
int minref;
|
|
||||||
int maxref;
|
|
||||||
int lastsample;
|
|
||||||
int sbuf[128];
|
|
||||||
int sidx;
|
|
||||||
int maxbuf[1024];
|
|
||||||
int minbuf[1024];
|
|
||||||
int midx;
|
|
||||||
char err_str[64];
|
|
||||||
char fsubtype[16];
|
|
||||||
char ftype[16];
|
|
||||||
int symbolcnt;
|
|
||||||
int rf_mod;
|
|
||||||
int numflips;
|
|
||||||
int lastsynctype;
|
|
||||||
int lastp25type;
|
|
||||||
int offset;
|
|
||||||
int carrier;
|
|
||||||
char tg[25][16];
|
|
||||||
int tgcount;
|
|
||||||
int lasttg;
|
|
||||||
int lastsrc;
|
|
||||||
int nac;
|
|
||||||
int errs;
|
|
||||||
int errs2;
|
|
||||||
int mbe_file_type;
|
|
||||||
int optind;
|
|
||||||
int numtdulc;
|
|
||||||
int firstframe;
|
|
||||||
char slot0light[8];
|
|
||||||
char slot1light[8];
|
|
||||||
float aout_gain;
|
|
||||||
float aout_max_buf[200];
|
|
||||||
float *aout_max_buf_p;
|
|
||||||
int aout_max_buf_idx;
|
|
||||||
int samplesPerSymbol;
|
|
||||||
int symbolCenter;
|
|
||||||
char algid[9];
|
|
||||||
char keyid[17];
|
|
||||||
int currentslot;
|
|
||||||
mbe_parms *cur_mp;
|
|
||||||
mbe_parms *prev_mp;
|
|
||||||
mbe_parms *prev_mp_enhanced;
|
|
||||||
} dsd_state;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Frame sync patterns
|
|
||||||
*/
|
|
||||||
#define INV_FRAME_SYNC "333331331133111131311111"
|
|
||||||
#define FRAME_SYNC "111113113311333313133333"
|
|
||||||
#define X2TDMA_VOICE_SYNC "113131333331313331113311"
|
|
||||||
#define X2TDMA_DATA_SYNC "331313111113131113331133"
|
|
||||||
#define DSTAR_SYNC "313131313133131113313111"
|
|
||||||
#define INV_DSTAR_SYNC "131313131311313331131333"
|
|
||||||
#define INV_NXDN96_SYNC "313133113131111333"
|
|
||||||
#define NXDN96_SYNC "131311331313333111"
|
|
||||||
#define DMR_DATA_SYNC "313333111331131131331131"
|
|
||||||
#define DMR_VOICE_SYNC "131111333113313313113313"
|
|
||||||
#define INV_PROVOICE_SYNC "31313111333133133311331133113311"
|
|
||||||
#define PROVOICE_SYNC "13131333111311311133113311331133"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* function prototypes
|
|
||||||
*/
|
|
||||||
void processDMRdata (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processDMRvoice (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processAudio (dsd_opts * opts, dsd_state * state);
|
|
||||||
void writeSynthesizedVoice (dsd_opts * opts, dsd_state * state);
|
|
||||||
void playSynthesizedVoice (dsd_opts * opts, dsd_state * state);
|
|
||||||
void openAudioOutDevice (dsd_opts * opts, int speed);
|
|
||||||
void openAudioInDevice (dsd_opts * opts);
|
|
||||||
int getDibit (dsd_opts * opts, dsd_state * state);
|
|
||||||
void skipDibit (dsd_opts * opts, dsd_state * state, int count);
|
|
||||||
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
|
||||||
void saveAmbe2250Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
|
||||||
int readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
|
||||||
int readAmbe2250Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
|
||||||
void openMbeInFile (dsd_opts * opts, dsd_state * state);
|
|
||||||
void closeMbeOutFile (dsd_opts * opts, dsd_state * state);
|
|
||||||
void openMbeOutFile (dsd_opts * opts, dsd_state * state);
|
|
||||||
void openWavOutFile (dsd_opts * opts, dsd_state * state);
|
|
||||||
void printFrameInfo (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processFrame (dsd_opts * opts, dsd_state * state);
|
|
||||||
void printFrameSync (dsd_opts * opts, dsd_state * state, char *frametype, int offset, char *modulation);
|
|
||||||
int getFrameSync (dsd_opts * opts, dsd_state * state);
|
|
||||||
int comp (const void *a, const void *b);
|
|
||||||
void noCarrier (dsd_opts * opts, dsd_state * state);
|
|
||||||
void initOpts (dsd_opts * opts);
|
|
||||||
void initState (dsd_state * state);
|
|
||||||
void usage ();
|
|
||||||
void liveScanner (dsd_opts * opts, dsd_state * state);
|
|
||||||
int main (int argc, char **argv);
|
|
||||||
void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv);
|
|
||||||
void processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24]);
|
|
||||||
void openSerial (dsd_opts * opts, dsd_state * state);
|
|
||||||
void resumeScan (dsd_opts * opts, dsd_state * state);
|
|
||||||
int getSymbol (dsd_opts * opts, dsd_state * state, int have_sync);
|
|
||||||
void upsample (dsd_state * state, float invalue);
|
|
||||||
void processDSTAR (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processNXDN96 (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo);
|
|
||||||
void processHDU (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processLDU1 (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processLDU2 (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processTDULC (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processProVoice (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processX2TDMAdata (dsd_opts * opts, dsd_state * state);
|
|
||||||
void processX2TDMAvoice (dsd_opts * opts, dsd_state * state);
|
|
||||||
365
dsd_audio.c
365
dsd_audio.c
@@ -1,365 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
processAudio (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
int i, n;
|
|
||||||
float aout_abs, max, gainfactor, gaindelta, maxbuf;
|
|
||||||
|
|
||||||
if (opts->audio_gain == (float) 0)
|
|
||||||
{
|
|
||||||
// detect max level
|
|
||||||
max = 0;
|
|
||||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
|
||||||
for (n = 0; n < 160; n++)
|
|
||||||
{
|
|
||||||
aout_abs = fabsf (*state->audio_out_temp_buf_p);
|
|
||||||
if (aout_abs > max)
|
|
||||||
{
|
|
||||||
max = aout_abs;
|
|
||||||
}
|
|
||||||
state->audio_out_temp_buf_p++;
|
|
||||||
}
|
|
||||||
*state->aout_max_buf_p = max;
|
|
||||||
state->aout_max_buf_p++;
|
|
||||||
state->aout_max_buf_idx++;
|
|
||||||
if (state->aout_max_buf_idx > 74)
|
|
||||||
{
|
|
||||||
state->aout_max_buf_idx = 0;
|
|
||||||
state->aout_max_buf_p = state->aout_max_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup max history
|
|
||||||
for (i = 0; i < 75; i++)
|
|
||||||
{
|
|
||||||
maxbuf = state->aout_max_buf[i];
|
|
||||||
if (maxbuf > max)
|
|
||||||
{
|
|
||||||
max = maxbuf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine optimal gain level
|
|
||||||
if (max > (float) 0)
|
|
||||||
{
|
|
||||||
gainfactor = ((float) 30000 / max);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gainfactor = (float) 50;
|
|
||||||
}
|
|
||||||
if (gainfactor < state->aout_gain)
|
|
||||||
{
|
|
||||||
state->aout_gain = gainfactor;
|
|
||||||
gaindelta = (float) 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (gainfactor > (float) 50)
|
|
||||||
{
|
|
||||||
gainfactor = (float) 50;
|
|
||||||
}
|
|
||||||
gaindelta = gainfactor - state->aout_gain;
|
|
||||||
if (gaindelta > ((float) 0.05 * state->aout_gain))
|
|
||||||
{
|
|
||||||
gaindelta = ((float) 0.05 * state->aout_gain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gaindelta /= (float) 160;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gaindelta = (float) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// adjust output gain
|
|
||||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
|
||||||
for (n = 0; n < 160; n++)
|
|
||||||
{
|
|
||||||
*state->audio_out_temp_buf_p = (state->aout_gain + ((float) n * gaindelta)) * (*state->audio_out_temp_buf_p);
|
|
||||||
state->audio_out_temp_buf_p++;
|
|
||||||
}
|
|
||||||
state->aout_gain += ((float) 160 * gaindelta);
|
|
||||||
|
|
||||||
// copy audio datat to output buffer and upsample if necessary
|
|
||||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
|
||||||
if (opts->split == 0)
|
|
||||||
{
|
|
||||||
for (n = 0; n < 160; n++)
|
|
||||||
{
|
|
||||||
upsample (state, *state->audio_out_temp_buf_p);
|
|
||||||
state->audio_out_temp_buf_p++;
|
|
||||||
state->audio_out_float_buf_p += 6;
|
|
||||||
state->audio_out_idx += 6;
|
|
||||||
state->audio_out_idx2 += 6;
|
|
||||||
}
|
|
||||||
state->audio_out_float_buf_p -= (960 + opts->playoffset);
|
|
||||||
// copy to output (short) buffer
|
|
||||||
for (n = 0; n < 960; n++)
|
|
||||||
{
|
|
||||||
if (*state->audio_out_float_buf_p > (float) 32760)
|
|
||||||
{
|
|
||||||
*state->audio_out_float_buf_p = (float) 32760;
|
|
||||||
}
|
|
||||||
else if (*state->audio_out_float_buf_p < (float) -32760)
|
|
||||||
{
|
|
||||||
*state->audio_out_float_buf_p = (float) -32760;
|
|
||||||
}
|
|
||||||
*state->audio_out_buf_p = (short) *state->audio_out_float_buf_p;
|
|
||||||
state->audio_out_buf_p++;
|
|
||||||
state->audio_out_float_buf_p++;
|
|
||||||
}
|
|
||||||
state->audio_out_float_buf_p += opts->playoffset;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (n = 0; n < 160; n++)
|
|
||||||
{
|
|
||||||
if (*state->audio_out_temp_buf_p > (float) 32760)
|
|
||||||
{
|
|
||||||
*state->audio_out_temp_buf_p = (float) 32760;
|
|
||||||
}
|
|
||||||
else if (*state->audio_out_temp_buf_p < (float) -32760)
|
|
||||||
{
|
|
||||||
*state->audio_out_temp_buf_p = (float) -32760;
|
|
||||||
}
|
|
||||||
*state->audio_out_buf_p = (short) *state->audio_out_temp_buf_p;
|
|
||||||
state->audio_out_buf_p++;
|
|
||||||
state->audio_out_temp_buf_p++;
|
|
||||||
state->audio_out_idx++;
|
|
||||||
state->audio_out_idx2++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
writeSynthesizedVoice (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
int n;
|
|
||||||
short aout_buf[160];
|
|
||||||
short *aout_buf_p;
|
|
||||||
|
|
||||||
aout_buf_p = aout_buf;
|
|
||||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
|
||||||
for (n = 0; n < 160; n++)
|
|
||||||
{
|
|
||||||
if (*state->audio_out_temp_buf_p > (float) 32760)
|
|
||||||
{
|
|
||||||
*state->audio_out_temp_buf_p = (float) 32760;
|
|
||||||
}
|
|
||||||
else if (*state->audio_out_temp_buf_p < (float) -32760)
|
|
||||||
{
|
|
||||||
*state->audio_out_temp_buf_p = (float) -32760;
|
|
||||||
}
|
|
||||||
*aout_buf_p = (short) *state->audio_out_temp_buf_p;
|
|
||||||
aout_buf_p++;
|
|
||||||
state->audio_out_temp_buf_p++;
|
|
||||||
}
|
|
||||||
|
|
||||||
write (opts->wav_out_fd, aout_buf, 320);
|
|
||||||
fflush (opts->wav_out_f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
playSynthesizedVoice (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (state->audio_out_idx > opts->delay)
|
|
||||||
{
|
|
||||||
// output synthesized speech to sound card
|
|
||||||
write (opts->audio_out_fd, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2));
|
|
||||||
state->audio_out_idx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->audio_out_idx2 >= 800000)
|
|
||||||
{
|
|
||||||
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
|
|
||||||
state->audio_out_buf_p = state->audio_out_buf + 100;
|
|
||||||
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
|
|
||||||
memset (state->audio_out_buf, 0, 100 * sizeof (short));
|
|
||||||
state->audio_out_idx2 = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
openAudioOutDevice (dsd_opts * opts, int speed)
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef SOLARIS
|
|
||||||
sample_info_t aset, aget;
|
|
||||||
|
|
||||||
opts->audio_out_fd = open (opts->audio_out_dev, O_WRONLY);
|
|
||||||
if (opts->audio_out_fd == -1)
|
|
||||||
{
|
|
||||||
printf ("Error, couldn't open %s\n", opts->audio_out_dev);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get current
|
|
||||||
ioctl (opts->audio_out_fd, AUDIO_GETINFO, &aset);
|
|
||||||
|
|
||||||
aset.record.sample_rate = speed;
|
|
||||||
aset.play.sample_rate = speed;
|
|
||||||
aset.record.channels = 1;
|
|
||||||
aset.play.channels = 1;
|
|
||||||
aset.record.precision = 16;
|
|
||||||
aset.play.precision = 16;
|
|
||||||
aset.record.encoding = AUDIO_ENCODING_LINEAR;
|
|
||||||
aset.play.encoding = AUDIO_ENCODING_LINEAR;
|
|
||||||
|
|
||||||
if (ioctl (opts->audio_out_fd, AUDIO_SETINFO, &aset) == -1)
|
|
||||||
{
|
|
||||||
printf ("Error setting sample device parameters\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BSD
|
|
||||||
|
|
||||||
int fmt;
|
|
||||||
|
|
||||||
opts->audio_out_fd = open (opts->audio_out_dev, O_WRONLY);
|
|
||||||
if (opts->audio_out_fd == -1)
|
|
||||||
{
|
|
||||||
printf ("Error, couldn't open %s\n", opts->audio_out_dev);
|
|
||||||
opts->audio_out = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt = 0;
|
|
||||||
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0)
|
|
||||||
{
|
|
||||||
printf ("ioctl reset error \n");
|
|
||||||
}
|
|
||||||
fmt = speed;
|
|
||||||
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SPEED, &fmt) < 0)
|
|
||||||
{
|
|
||||||
printf ("ioctl speed error \n");
|
|
||||||
}
|
|
||||||
fmt = 0;
|
|
||||||
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_STEREO, &fmt) < 0)
|
|
||||||
{
|
|
||||||
printf ("ioctl stereo error \n");
|
|
||||||
}
|
|
||||||
fmt = AFMT_S16_LE;
|
|
||||||
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SETFMT, &fmt) < 0)
|
|
||||||
{
|
|
||||||
printf ("ioctl setfmt error \n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
printf ("Audio Out Device: %s\n", opts->audio_out_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
openAudioInDevice (dsd_opts * opts)
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef SOLARIS
|
|
||||||
sample_info_t aset, aget;
|
|
||||||
int rgain;
|
|
||||||
|
|
||||||
rgain = 64;
|
|
||||||
|
|
||||||
if (opts->split == 1)
|
|
||||||
{
|
|
||||||
opts->audio_in_fd = open (opts->audio_in_dev, O_RDONLY);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
opts->audio_in_fd = open (opts->audio_in_dev, O_RDWR);
|
|
||||||
}
|
|
||||||
if (opts->audio_in_fd == -1)
|
|
||||||
{
|
|
||||||
printf ("Error, couldn't open /dev/audio\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// get current
|
|
||||||
ioctl (opts->audio_in_fd, AUDIO_GETINFO, &aset);
|
|
||||||
|
|
||||||
aset.record.sample_rate = 48000;
|
|
||||||
aset.play.sample_rate = 48000;
|
|
||||||
aset.record.channels = 1;
|
|
||||||
aset.play.channels = 1;
|
|
||||||
aset.record.precision = 16;
|
|
||||||
aset.play.precision = 16;
|
|
||||||
aset.record.encoding = AUDIO_ENCODING_LINEAR;
|
|
||||||
aset.play.encoding = AUDIO_ENCODING_LINEAR;
|
|
||||||
aset.record.port = AUDIO_LINE_IN;
|
|
||||||
aset.record.gain = rgain;
|
|
||||||
|
|
||||||
if (ioctl (opts->audio_in_fd, AUDIO_SETINFO, &aset) == -1)
|
|
||||||
{
|
|
||||||
printf ("Error setting sample device parameters\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BSD
|
|
||||||
int fmt;
|
|
||||||
|
|
||||||
if (opts->split == 1)
|
|
||||||
{
|
|
||||||
opts->audio_in_fd = open (opts->audio_in_dev, O_RDONLY);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
opts->audio_in_fd = open (opts->audio_in_dev, O_RDWR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->audio_in_fd == -1)
|
|
||||||
{
|
|
||||||
printf ("Error, couldn't open %s\n", opts->audio_in_dev);
|
|
||||||
opts->audio_out = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt = 0;
|
|
||||||
if (ioctl (opts->audio_in_fd, SNDCTL_DSP_RESET) < 0)
|
|
||||||
{
|
|
||||||
printf ("ioctl reset error \n");
|
|
||||||
}
|
|
||||||
fmt = 48000;
|
|
||||||
if (ioctl (opts->audio_in_fd, SNDCTL_DSP_SPEED, &fmt) < 0)
|
|
||||||
{
|
|
||||||
printf ("ioctl speed error \n");
|
|
||||||
}
|
|
||||||
fmt = 0;
|
|
||||||
if (ioctl (opts->audio_in_fd, SNDCTL_DSP_STEREO, &fmt) < 0)
|
|
||||||
{
|
|
||||||
printf ("ioctl stereo error \n");
|
|
||||||
}
|
|
||||||
fmt = AFMT_S16_LE;
|
|
||||||
if (ioctl (opts->audio_in_fd, SNDCTL_DSP_SETFMT, &fmt) < 0)
|
|
||||||
{
|
|
||||||
printf ("ioctl setfmt error \n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (opts->split == 1)
|
|
||||||
{
|
|
||||||
printf ("Audio In Device: %s\n", opts->audio_in_dev);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf ("Audio In/Out Device: %s\n", opts->audio_in_dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
290
dsd_dibit.c
290
dsd_dibit.c
@@ -1,290 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
getDibit (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
// returns one dibit value
|
|
||||||
int i, j, o, symbol;
|
|
||||||
int sbuf2[128];
|
|
||||||
int spectrum[64];
|
|
||||||
char modulation[8];
|
|
||||||
int lmin, lmax, lsum;
|
|
||||||
|
|
||||||
state->numflips = 0;
|
|
||||||
|
|
||||||
symbol = getSymbol (opts, state, 1);
|
|
||||||
state->sbuf[state->sidx] = symbol;
|
|
||||||
|
|
||||||
for (i = 0; i < opts->ssize; i++)
|
|
||||||
{
|
|
||||||
sbuf2[i] = state->sbuf[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
qsort (sbuf2, opts->ssize, sizeof (int), comp);
|
|
||||||
// continuous update of min/max in rf_mod=1 (QPSK) mode
|
|
||||||
// in c4fm min/max must only be updated during sync
|
|
||||||
if (state->rf_mod == 1)
|
|
||||||
{
|
|
||||||
lmin = (sbuf2[0] + sbuf2[1]) / 2;
|
|
||||||
lmax = (sbuf2[(opts->ssize - 1)] + sbuf2[(opts->ssize - 2)]) / 2;
|
|
||||||
state->minbuf[state->midx] = lmin;
|
|
||||||
state->maxbuf[state->midx] = lmax;
|
|
||||||
if (state->midx == (opts->msize - 1))
|
|
||||||
{
|
|
||||||
state->midx = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->midx++;
|
|
||||||
}
|
|
||||||
lsum = 0;
|
|
||||||
for (i = 0; i < opts->msize; i++)
|
|
||||||
{
|
|
||||||
lsum += state->minbuf[i];
|
|
||||||
}
|
|
||||||
state->min = lsum / opts->msize;
|
|
||||||
lsum = 0;
|
|
||||||
for (i = 0; i < opts->msize; i++)
|
|
||||||
{
|
|
||||||
lsum += state->maxbuf[i];
|
|
||||||
}
|
|
||||||
state->max = lsum / opts->msize;
|
|
||||||
state->center = ((state->max) + (state->min)) / 2;
|
|
||||||
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
|
|
||||||
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
|
|
||||||
state->maxref = ((state->max) * 0.80);
|
|
||||||
state->minref = ((state->min) * 0.80);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->maxref = state->max;
|
|
||||||
state->minref = state->min;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->sidx == (opts->ssize - 1))
|
|
||||||
{
|
|
||||||
|
|
||||||
state->sidx = 0;
|
|
||||||
|
|
||||||
if (opts->datascope == 1)
|
|
||||||
{
|
|
||||||
if (state->rf_mod == 0)
|
|
||||||
{
|
|
||||||
sprintf (modulation, "C4FM");
|
|
||||||
}
|
|
||||||
else if (state->rf_mod == 1)
|
|
||||||
{
|
|
||||||
sprintf (modulation, "QPSK");
|
|
||||||
}
|
|
||||||
else if (state->rf_mod == 2)
|
|
||||||
{
|
|
||||||
sprintf (modulation, "GFSK");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 64; i++)
|
|
||||||
{
|
|
||||||
spectrum[i] = 0;
|
|
||||||
}
|
|
||||||
for (i = 0; i < opts->ssize; i++)
|
|
||||||
{
|
|
||||||
o = (sbuf2[i] + 32768) / 1024;
|
|
||||||
spectrum[o]++;
|
|
||||||
}
|
|
||||||
if (state->symbolcnt > (4800 / opts->scoperate))
|
|
||||||
{
|
|
||||||
state->symbolcnt = 0;
|
|
||||||
printf ("\n");
|
|
||||||
printf ("Demod mode: %s Nac: %4X\n", modulation, state->nac);
|
|
||||||
printf ("Frame Type: %s Talkgroup: %7i\n", state->ftype, state->lasttg);
|
|
||||||
printf ("Frame Subtype: %s Source: %12i\n", state->fsubtype, state->lastsrc);
|
|
||||||
printf ("TDMA activity: %s %s Voice errors: %s\n", state->slot0light, state->slot1light, state->err_str);
|
|
||||||
printf ("+----------------------------------------------------------------+\n");
|
|
||||||
for (i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
printf ("|");
|
|
||||||
for (j = 0; j < 64; j++)
|
|
||||||
{
|
|
||||||
if (i == 0)
|
|
||||||
{
|
|
||||||
if ((j == ((state->min) + 32768) / 1024) || (j == ((state->max) + 32768) / 1024))
|
|
||||||
{
|
|
||||||
printf ("#");
|
|
||||||
}
|
|
||||||
else if ((j == ((state->lmid) + 32768) / 1024) || (j == ((state->umid) + 32768) / 1024))
|
|
||||||
{
|
|
||||||
printf ("^");
|
|
||||||
}
|
|
||||||
else if (j == (state->center + 32768) / 1024)
|
|
||||||
{
|
|
||||||
printf ("!");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (j == 32)
|
|
||||||
{
|
|
||||||
printf ("|");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (spectrum[j] > 9 - i)
|
|
||||||
{
|
|
||||||
printf ("*");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (j == 32)
|
|
||||||
{
|
|
||||||
printf ("|");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf ("|\n");
|
|
||||||
}
|
|
||||||
printf ("+----------------------------------------------------------------+\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->sidx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine dibit state
|
|
||||||
if ((state->synctype == 6) || (state->synctype == 14))
|
|
||||||
{
|
|
||||||
if (symbol > state->center)
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 1;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 3;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((state->synctype == 7) || (state->synctype == 15))
|
|
||||||
{
|
|
||||||
if (symbol > state->center)
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 1;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 3;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) || (state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13))
|
|
||||||
{
|
|
||||||
if (symbol > state->center)
|
|
||||||
{
|
|
||||||
if (symbol > state->umid)
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 1; // store non-inverted values in dibit_buf
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
return (3);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 0;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
return (2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (symbol < state->lmid)
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 3;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 2;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (symbol > state->center)
|
|
||||||
{
|
|
||||||
if (symbol > state->umid)
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 1;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 0;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (symbol < state->lmid)
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 3;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
return (3);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 2;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
return (2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
skipDibit (dsd_opts * opts, dsd_state * state, int count)
|
|
||||||
{
|
|
||||||
|
|
||||||
short sample;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < (count); i++)
|
|
||||||
{
|
|
||||||
sample = getDibit (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
331
dsd_file.c
331
dsd_file.c
@@ -1,331 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
|
||||||
{
|
|
||||||
int i, j, k;
|
|
||||||
unsigned char b;
|
|
||||||
unsigned char err;
|
|
||||||
|
|
||||||
err = (unsigned char) state->errs2;
|
|
||||||
fputc (err, opts->mbe_out_f);
|
|
||||||
|
|
||||||
k = 0;
|
|
||||||
for (i = 0; i < 11; i++)
|
|
||||||
{
|
|
||||||
b = 0;
|
|
||||||
for (j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
b = b << 1;
|
|
||||||
b = b + imbe_d[k];
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
fputc (b, opts->mbe_out_f);
|
|
||||||
}
|
|
||||||
fflush (opts->mbe_out_f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
saveAmbe2250Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|
||||||
{
|
|
||||||
int i, j, k;
|
|
||||||
unsigned char b;
|
|
||||||
unsigned char err;
|
|
||||||
|
|
||||||
err = (unsigned char) state->errs2;
|
|
||||||
fputc (err, opts->mbe_out_f);
|
|
||||||
|
|
||||||
k = 0;
|
|
||||||
for (i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
b = 0;
|
|
||||||
for (j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
b = b << 1;
|
|
||||||
b = b + ambe_d[k];
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
fputc (b, opts->mbe_out_f);
|
|
||||||
}
|
|
||||||
b = ambe_d[48];
|
|
||||||
fputc (b, opts->mbe_out_f);
|
|
||||||
fflush (opts->mbe_out_f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
|
||||||
{
|
|
||||||
|
|
||||||
int i, j, k;
|
|
||||||
unsigned char b;
|
|
||||||
|
|
||||||
state->errs2 = fgetc (opts->mbe_in_f);
|
|
||||||
state->errs = state->errs2;
|
|
||||||
|
|
||||||
k = 0;
|
|
||||||
for (i = 0; i < 11; i++)
|
|
||||||
{
|
|
||||||
b = fgetc (opts->mbe_in_f);
|
|
||||||
if (feof (opts->mbe_in_f))
|
|
||||||
{
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
for (j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
imbe_d[k] = (b & 128) >> 7;
|
|
||||||
b = b << 1;
|
|
||||||
b = b & 255;
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
readAmbe2250Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|
||||||
{
|
|
||||||
|
|
||||||
int i, j, k;
|
|
||||||
unsigned char b;
|
|
||||||
|
|
||||||
state->errs2 = fgetc (opts->mbe_in_f);
|
|
||||||
state->errs = state->errs2;
|
|
||||||
|
|
||||||
k = 0;
|
|
||||||
for (i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
b = fgetc (opts->mbe_in_f);
|
|
||||||
if (feof (opts->mbe_in_f))
|
|
||||||
{
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
for (j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
ambe_d[k] = (b & 128) >> 7;
|
|
||||||
b = b << 1;
|
|
||||||
b = b & 255;
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b = fgetc (opts->mbe_in_f);
|
|
||||||
ambe_d[48] = b;
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
openMbeInFile (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
char cookie[5];
|
|
||||||
|
|
||||||
opts->mbe_in_f = fopen (opts->mbe_in_file, "ro");
|
|
||||||
if (opts->mbe_in_f == NULL)
|
|
||||||
{
|
|
||||||
printf ("Error: could not open %s\n", opts->mbe_in_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// read cookie
|
|
||||||
cookie[0] = fgetc (opts->mbe_in_f);
|
|
||||||
cookie[1] = fgetc (opts->mbe_in_f);
|
|
||||||
cookie[2] = fgetc (opts->mbe_in_f);
|
|
||||||
cookie[3] = fgetc (opts->mbe_in_f);
|
|
||||||
cookie[4] = 0;
|
|
||||||
if (strstr (cookie, ".amb") != NULL)
|
|
||||||
{
|
|
||||||
state->mbe_file_type = 1;
|
|
||||||
}
|
|
||||||
else if (strstr (cookie, ".imb") != NULL)
|
|
||||||
{
|
|
||||||
state->mbe_file_type = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->mbe_file_type = -1;
|
|
||||||
printf ("Error - unrecognized file type\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
char shell[255], newfilename[64], ext[5], datestr[32];
|
|
||||||
char tgid[17];
|
|
||||||
int sum, i, j;
|
|
||||||
int talkgroup;
|
|
||||||
struct tm timep;
|
|
||||||
|
|
||||||
if (opts->mbe_out_f != NULL)
|
|
||||||
{
|
|
||||||
if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
|
|
||||||
{
|
|
||||||
sprintf (ext, ".imb");
|
|
||||||
strptime (opts->mbe_out_file, "%s.imb", &timep);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (ext, ".amb");
|
|
||||||
strptime (opts->mbe_out_file, "%s.amb", &timep);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 17; i++)
|
|
||||||
{
|
|
||||||
tgid[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->tgcount > 0)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
sum = 0;
|
|
||||||
for (j = 0; j < state->tgcount; j++)
|
|
||||||
{
|
|
||||||
sum = sum + state->tg[j][i] - 48;
|
|
||||||
}
|
|
||||||
tgid[i] = (char) (((float) sum / (float) state->tgcount) + 48.5);
|
|
||||||
}
|
|
||||||
talkgroup = (int) strtol (tgid, NULL, 2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
talkgroup = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fflush (opts->mbe_out_f);
|
|
||||||
fclose (opts->mbe_out_f);
|
|
||||||
opts->mbe_out_f = NULL;
|
|
||||||
strftime (datestr, 31, "%Y-%m-%d-%H%M%S", &timep);
|
|
||||||
sprintf (newfilename, "nac%X-%s-tg%i%s", state->nac, datestr, talkgroup, ext);
|
|
||||||
sprintf (shell, "mv %s %s", opts->mbe_out_file, newfilename);
|
|
||||||
system (shell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
struct timeval tv;
|
|
||||||
int i, j;
|
|
||||||
char ext[5];
|
|
||||||
|
|
||||||
if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
|
|
||||||
{
|
|
||||||
sprintf (ext, ".imb");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (ext, ".amb");
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset talkgroup id buffer
|
|
||||||
for (i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
for (j = 0; j < 25; j++)
|
|
||||||
{
|
|
||||||
state->tg[j][i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state->tgcount = 0;
|
|
||||||
|
|
||||||
gettimeofday (&tv, NULL);
|
|
||||||
sprintf (opts->mbe_out_file, "%i%s", (int) tv.tv_sec, ext);
|
|
||||||
opts->mbe_out_f = fopen (opts->mbe_out_file, "w");
|
|
||||||
if (opts->mbe_out_f == NULL)
|
|
||||||
{
|
|
||||||
printf ("Error, couldn't open %s\n", opts->mbe_out_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// write magic
|
|
||||||
fprintf (opts->mbe_out_f, "%s", ext);
|
|
||||||
|
|
||||||
fflush (opts->mbe_out_f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
openWavOutFile (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
opts->wav_out_f = fopen (opts->wav_out_file, "w");
|
|
||||||
if (opts->wav_out_f == NULL)
|
|
||||||
{
|
|
||||||
printf ("Error - could not open wav output file %s\n", opts->wav_out_file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
opts->wav_out_fd = fileno (opts->wav_out_f);
|
|
||||||
|
|
||||||
fprintf (opts->wav_out_f, "RIFF");
|
|
||||||
// total length
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
|
|
||||||
fprintf (opts->wav_out_f, "WAVE");
|
|
||||||
fprintf (opts->wav_out_f, "fmt ");
|
|
||||||
|
|
||||||
// length of format chunk
|
|
||||||
fputc (16, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
|
|
||||||
// always 0x1
|
|
||||||
fputc (1, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
|
|
||||||
// channels
|
|
||||||
fputc (1, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
|
|
||||||
// sample rate
|
|
||||||
fputc (64, opts->wav_out_f);
|
|
||||||
fputc (31, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
|
|
||||||
// bytes per second
|
|
||||||
fputc (176, opts->wav_out_f);
|
|
||||||
fputc (62, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
|
|
||||||
// bytes/sample
|
|
||||||
fputc (2, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
|
|
||||||
// bits/sample
|
|
||||||
fputc (16, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
|
|
||||||
// data chunk header
|
|
||||||
fprintf (opts->wav_out_f, "data");
|
|
||||||
|
|
||||||
// length of data
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
fputc (0, opts->wav_out_f);
|
|
||||||
fputc (127, opts->wav_out_f);
|
|
||||||
|
|
||||||
fflush (opts->wav_out_f);
|
|
||||||
}
|
|
||||||
421
dsd_frame.c
421
dsd_frame.c
@@ -1,421 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
printFrameInfo (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
int level;
|
|
||||||
|
|
||||||
level = (int) (((float) state->max / (float) 32768) * (float) 100);
|
|
||||||
if (opts->verbose > 0)
|
|
||||||
{
|
|
||||||
printf ("input: %2i%% ", level);
|
|
||||||
}
|
|
||||||
if (state->nac != 0)
|
|
||||||
{
|
|
||||||
printf ("nac: %4X ", state->nac);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->verbose > 1)
|
|
||||||
{
|
|
||||||
printf ("src: %8i ", state->lastsrc);
|
|
||||||
}
|
|
||||||
printf ("tg: %5i ", state->lasttg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
processFrame (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
int i, j, dibit;
|
|
||||||
char duid[3];
|
|
||||||
char nac[13];
|
|
||||||
int level;
|
|
||||||
|
|
||||||
duid[2] = 0;
|
|
||||||
j = 0;
|
|
||||||
|
|
||||||
if (state->rf_mod == 1)
|
|
||||||
{
|
|
||||||
state->maxref = (state->max * 0.80);
|
|
||||||
state->minref = (state->min * 0.80);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->maxref = state->max;
|
|
||||||
state->minref = state->min;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((state->synctype == 8) || (state->synctype == 9))
|
|
||||||
{
|
|
||||||
state->nac = 0;
|
|
||||||
state->lastsrc = 0;
|
|
||||||
state->lasttg = 0;
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
if (opts->verbose > 0)
|
|
||||||
{
|
|
||||||
level = (int) (((float) state->max / (float) 32768) * (float) 100);
|
|
||||||
printf ("input: %2i%% ", level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->nac = 0;
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
|
||||||
processNXDN96 (opts, state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ((state->synctype == 6) || (state->synctype == 7))
|
|
||||||
{
|
|
||||||
state->nac = 0;
|
|
||||||
state->lastsrc = 0;
|
|
||||||
state->lasttg = 0;
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
if (opts->verbose > 0)
|
|
||||||
{
|
|
||||||
level = (int) (((float) state->max / (float) 32768) * (float) 100);
|
|
||||||
printf ("input: %2i%% ", level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->nac = 0;
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
|
||||||
processDSTAR (opts, state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ((state->synctype >= 10) && (state->synctype <= 13))
|
|
||||||
{
|
|
||||||
state->nac = 0;
|
|
||||||
state->lastsrc = 0;
|
|
||||||
state->lasttg = 0;
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
if (opts->verbose > 0)
|
|
||||||
{
|
|
||||||
level = (int) (((float) state->max / (float) 32768) * (float) 100);
|
|
||||||
printf ("input: %2i%% ", level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((state->synctype == 11) || (state->synctype == 12))
|
|
||||||
{
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
|
||||||
processDMRvoice (opts, state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
closeMbeOutFile (opts, state);
|
|
||||||
state->err_str[0] = 0;
|
|
||||||
processDMRdata (opts, state);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ((state->synctype >= 2) && (state->synctype <= 5))
|
|
||||||
{
|
|
||||||
state->nac = 0;
|
|
||||||
state->lastsrc = 0;
|
|
||||||
state->lasttg = 0;
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
}
|
|
||||||
if ((state->synctype == 3) || (state->synctype == 4))
|
|
||||||
{
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
|
||||||
processX2TDMAvoice (opts, state);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
closeMbeOutFile (opts, state);
|
|
||||||
state->err_str[0] = 0;
|
|
||||||
processX2TDMAdata (opts, state);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ((state->synctype == 14) || (state->synctype == 15))
|
|
||||||
{
|
|
||||||
state->nac = 0;
|
|
||||||
state->lastsrc = 0;
|
|
||||||
state->lasttg = 0;
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
if (opts->verbose > 0)
|
|
||||||
{
|
|
||||||
level = (int) (((float) state->max / (float) 32768) * (float) 100);
|
|
||||||
printf ("input: %2i%% ", level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
|
||||||
processProVoice (opts, state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
j = 0;
|
|
||||||
for (i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
nac[j] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
j++;
|
|
||||||
nac[j] = (1 & dibit) + 48; // bit 0
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
nac[12] = 0;
|
|
||||||
state->nac = strtol (nac, NULL, 2);
|
|
||||||
|
|
||||||
for (i = 0; i < 2; i++)
|
|
||||||
{
|
|
||||||
duid[i] = getDibit (opts, state) + 48;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp (duid, "00") == 0)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" HDU\n");
|
|
||||||
}
|
|
||||||
state->cur_mp->repeat = 0;
|
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
closeMbeOutFile (opts, state);
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
|
||||||
state->lastp25type = 2;
|
|
||||||
sprintf (state->fsubtype, " HDU ");
|
|
||||||
processHDU (opts, state);
|
|
||||||
}
|
|
||||||
else if (strcmp (duid, "11") == 0)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" LDU1 ");
|
|
||||||
}
|
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f == NULL)
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->lastp25type = 1;
|
|
||||||
sprintf (state->fsubtype, " LDU1 ");
|
|
||||||
state->numtdulc = 0;
|
|
||||||
processLDU1 (opts, state);
|
|
||||||
}
|
|
||||||
else if (strcmp (duid, "22") == 0)
|
|
||||||
{
|
|
||||||
if (state->lastp25type != 1)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" Ignoring LDU2 not preceeded by LDU1\n");
|
|
||||||
}
|
|
||||||
state->lastp25type = 0;
|
|
||||||
sprintf (state->fsubtype, " ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" LDU2 ");
|
|
||||||
}
|
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f == NULL)
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->lastp25type = 2;
|
|
||||||
sprintf (state->fsubtype, " LDU2 ");
|
|
||||||
state->numtdulc = 0;
|
|
||||||
processLDU2 (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcmp (duid, "33") == 0)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" TDULC\n");
|
|
||||||
}
|
|
||||||
state->cur_mp->repeat = 0;
|
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
closeMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
|
||||||
state->tgcount = 0;
|
|
||||||
state->lastp25type = 0;
|
|
||||||
sprintf (state->fsubtype, " TDULC ");
|
|
||||||
state->numtdulc++;
|
|
||||||
if ((opts->resume > 0) && (state->numtdulc > opts->resume))
|
|
||||||
{
|
|
||||||
resumeScan (opts, state);
|
|
||||||
}
|
|
||||||
processTDULC (opts, state);
|
|
||||||
state->err_str[0] = 0;
|
|
||||||
}
|
|
||||||
else if (strcmp (duid, "03") == 0)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" TDU\n");
|
|
||||||
}
|
|
||||||
state->cur_mp->repeat = 0;
|
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
closeMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
|
||||||
state->tgcount = 0;
|
|
||||||
state->lastp25type = 0;
|
|
||||||
state->err_str[0] = 0;
|
|
||||||
sprintf (state->fsubtype, " TDU ");
|
|
||||||
skipDibit (opts, state, 40);
|
|
||||||
}
|
|
||||||
else if (strcmp (duid, "13") == 0)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" TSDU\n");
|
|
||||||
}
|
|
||||||
state->lastp25type = 3;
|
|
||||||
sprintf (state->fsubtype, " TSDU ");
|
|
||||||
skipDibit (opts, state, 328);
|
|
||||||
}
|
|
||||||
else if (strcmp (duid, "30") == 0)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" PDU\n");
|
|
||||||
if (opts->resume > 0)
|
|
||||||
{
|
|
||||||
resumeScan (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f == NULL)
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->lastp25type = 4;
|
|
||||||
sprintf (state->fsubtype, " PDU ");
|
|
||||||
|
|
||||||
}
|
|
||||||
// try to guess based on previous frame if unknown type
|
|
||||||
else if (state->lastp25type == 1)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf ("(LDU2) ");
|
|
||||||
}
|
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f == NULL)
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->lastp25type = 0;
|
|
||||||
sprintf (state->fsubtype, "(LDU2) ");
|
|
||||||
state->numtdulc = 0;
|
|
||||||
processLDU2 (opts, state);
|
|
||||||
}
|
|
||||||
else if (state->lastp25type == 2)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf ("(LDU1) ");
|
|
||||||
}
|
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f == NULL)
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->lastp25type = 0;
|
|
||||||
sprintf (state->fsubtype, "(LDU1) ");
|
|
||||||
state->numtdulc = 0;
|
|
||||||
processLDU1 (opts, state);
|
|
||||||
}
|
|
||||||
else if (state->lastp25type == 3)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" (TSDU)\n");
|
|
||||||
}
|
|
||||||
state->lastp25type = 0;
|
|
||||||
sprintf (state->fsubtype, "(TSDU) ");
|
|
||||||
skipDibit (opts, state, 328);
|
|
||||||
}
|
|
||||||
else if (state->lastp25type == 4)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" (PDU)\n");
|
|
||||||
}
|
|
||||||
state->lastp25type = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->lastp25type = 0;
|
|
||||||
sprintf (state->fsubtype, " ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
printf (" duid:%s *Unknown DUID*\n", duid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
691
dsd_frame_sync.c
691
dsd_frame_sync.c
@@ -1,691 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
printFrameSync (dsd_opts * opts, dsd_state * state, char *frametype, int offset, char *modulation)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (opts->verbose > 0)
|
|
||||||
{
|
|
||||||
printf ("Sync: %s ", frametype);
|
|
||||||
}
|
|
||||||
if (opts->verbose > 2)
|
|
||||||
{
|
|
||||||
printf ("o: %4i ", offset);
|
|
||||||
}
|
|
||||||
if (opts->verbose > 1)
|
|
||||||
{
|
|
||||||
printf ("mod: %s ", modulation);
|
|
||||||
}
|
|
||||||
if (opts->verbose > 2)
|
|
||||||
{
|
|
||||||
printf ("g: %f ", state->aout_gain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getFrameSync (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
/* detects frame sync and returns frame type
|
|
||||||
* 0 = +p25p1
|
|
||||||
* 1 = -p25p1
|
|
||||||
* 2 = +X2-TDMA (non inverted signal data frame)
|
|
||||||
* 3 = +X2-TDMA (inverted signal voice frame)
|
|
||||||
* 4 = -X2-TDMA (non inverted signal voice frame)
|
|
||||||
* 5 = -X2-TDMA (inverted signal data frame)
|
|
||||||
* 6 = +D-STAR
|
|
||||||
* 7 = -D-STAR
|
|
||||||
* 8 = +NXDN96
|
|
||||||
* 9 = -NXDN96
|
|
||||||
* 10 = +DMR (non inverted singlan data frame)
|
|
||||||
* 11 = -DMR (inverted signal voice frame)
|
|
||||||
* 12 = +DMR (non inverted signal voice frame)
|
|
||||||
* 13 = -DMR (inverted signal data frame)
|
|
||||||
* 14 = +ProVoice
|
|
||||||
* 15 = -ProVoice
|
|
||||||
*/
|
|
||||||
|
|
||||||
int i, j, t, o, dibit, sync, symbol, synctest_pos, lastt;
|
|
||||||
char synctest[25];
|
|
||||||
char synctest18[19];
|
|
||||||
char synctest32[33];
|
|
||||||
char modulation[8];
|
|
||||||
char *synctest_p;
|
|
||||||
char synctest_buf[10240];
|
|
||||||
int lmin, lmax, lidx;
|
|
||||||
int lbuf[24], lbuf2[24];
|
|
||||||
int lsum;
|
|
||||||
char spectrum[64];
|
|
||||||
|
|
||||||
// detect frame sync
|
|
||||||
t = 0;
|
|
||||||
synctest[24] = 0;
|
|
||||||
synctest18[18] = 0;
|
|
||||||
synctest32[32] = 0;
|
|
||||||
synctest_pos = 0;
|
|
||||||
synctest_p = synctest_buf;
|
|
||||||
sync = 0;
|
|
||||||
lmin = 0;
|
|
||||||
lmax = 0;
|
|
||||||
lidx = 0;
|
|
||||||
lastt = 0;
|
|
||||||
state->numflips = 0;
|
|
||||||
if ((opts->symboltiming == 1) && (state->carrier == 1))
|
|
||||||
{
|
|
||||||
printf ("\nSymbol Timing:\n");
|
|
||||||
}
|
|
||||||
while (sync == 0)
|
|
||||||
{
|
|
||||||
t++;
|
|
||||||
symbol = getSymbol (opts, state, 0);
|
|
||||||
lbuf[lidx] = symbol;
|
|
||||||
state->sbuf[state->sidx] = symbol;
|
|
||||||
if (lidx == 23)
|
|
||||||
{
|
|
||||||
lidx = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lidx++;
|
|
||||||
}
|
|
||||||
if (state->sidx == (opts->ssize - 1))
|
|
||||||
{
|
|
||||||
state->sidx = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->sidx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastt == 23)
|
|
||||||
{
|
|
||||||
lastt = 0;
|
|
||||||
if (state->numflips > opts->mod_threshold)
|
|
||||||
{
|
|
||||||
if (opts->mod_qpsk == 1)
|
|
||||||
{
|
|
||||||
state->rf_mod = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (state->numflips > 18)
|
|
||||||
{
|
|
||||||
if (opts->mod_gfsk == 1)
|
|
||||||
{
|
|
||||||
state->rf_mod = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (opts->mod_c4fm == 1)
|
|
||||||
{
|
|
||||||
state->rf_mod = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->numflips = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
//determine dibit state
|
|
||||||
if (symbol > 0)
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 1;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
dibit = 49;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*state->dibit_buf_p = 3;
|
|
||||||
state->dibit_buf_p++;
|
|
||||||
dibit = 51;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
printf("%c", dibit);
|
|
||||||
if (t == 768)
|
|
||||||
{
|
|
||||||
printf("\n");
|
|
||||||
t=0;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
*synctest_p = dibit;
|
|
||||||
if (t >= 24)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
lbuf2[i] = lbuf[i];
|
|
||||||
}
|
|
||||||
qsort (lbuf2, 24, sizeof (int), comp);
|
|
||||||
lmin = (lbuf2[2] + lbuf2[3] + lbuf2[4]) / 3;
|
|
||||||
lmax = (lbuf2[21] + lbuf2[20] + lbuf2[19]) / 3;
|
|
||||||
|
|
||||||
if (state->rf_mod == 1)
|
|
||||||
{
|
|
||||||
state->minbuf[state->midx] = lmin;
|
|
||||||
state->maxbuf[state->midx] = lmax;
|
|
||||||
if (state->midx == (opts->msize - 1))
|
|
||||||
{
|
|
||||||
state->midx = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->midx++;
|
|
||||||
}
|
|
||||||
lsum = 0;
|
|
||||||
for (i = 0; i < opts->msize; i++)
|
|
||||||
{
|
|
||||||
lsum += state->minbuf[i];
|
|
||||||
}
|
|
||||||
state->min = lsum / opts->msize;
|
|
||||||
lsum = 0;
|
|
||||||
for (i = 0; i < opts->msize; i++)
|
|
||||||
{
|
|
||||||
lsum += state->maxbuf[i];
|
|
||||||
}
|
|
||||||
state->max = lsum / opts->msize;
|
|
||||||
state->center = ((state->max) + (state->min)) / 2;
|
|
||||||
state->maxref = ((state->max) * 0.80);
|
|
||||||
state->minref = ((state->min) * 0.80);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->maxref = state->max;
|
|
||||||
state->minref = state->min;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->rf_mod == 0)
|
|
||||||
{
|
|
||||||
sprintf (modulation, "C4FM");
|
|
||||||
}
|
|
||||||
else if (state->rf_mod == 1)
|
|
||||||
{
|
|
||||||
sprintf (modulation, "QPSK");
|
|
||||||
}
|
|
||||||
else if (state->rf_mod == 2)
|
|
||||||
{
|
|
||||||
sprintf (modulation, "GFSK");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->datascope == 1)
|
|
||||||
{
|
|
||||||
if (lidx == 0)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 64; i++)
|
|
||||||
{
|
|
||||||
spectrum[i] = 0;
|
|
||||||
}
|
|
||||||
for (i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
o = (lbuf2[i] + 32768) / 1024;
|
|
||||||
spectrum[o]++;
|
|
||||||
}
|
|
||||||
if (state->symbolcnt > (4800 / opts->scoperate))
|
|
||||||
{
|
|
||||||
state->symbolcnt = 0;
|
|
||||||
printf ("\n");
|
|
||||||
printf ("Demod mode: %s Nac: %4X\n", modulation, state->nac);
|
|
||||||
printf ("Frame Type: %s Talkgroup: %7i\n", state->ftype, state->lasttg);
|
|
||||||
printf ("Frame Subtype: %s Source: %12i\n", state->fsubtype, state->lastsrc);
|
|
||||||
printf ("TDMA activity: %s %s Voice errors: %s\n", state->slot0light, state->slot1light, state->err_str);
|
|
||||||
printf ("+----------------------------------------------------------------+\n");
|
|
||||||
for (i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
printf ("|");
|
|
||||||
for (j = 0; j < 64; j++)
|
|
||||||
{
|
|
||||||
if (i == 0)
|
|
||||||
{
|
|
||||||
if ((j == ((state->min) + 32768) / 1024) || (j == ((state->max) + 32768) / 1024))
|
|
||||||
{
|
|
||||||
printf ("#");
|
|
||||||
}
|
|
||||||
else if (j == (state->center + 32768) / 1024)
|
|
||||||
{
|
|
||||||
printf ("!");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (j == 32)
|
|
||||||
{
|
|
||||||
printf ("|");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (spectrum[j] > 9 - i)
|
|
||||||
{
|
|
||||||
printf ("*");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (j == 32)
|
|
||||||
{
|
|
||||||
printf ("|");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf ("|\n");
|
|
||||||
}
|
|
||||||
printf ("+----------------------------------------------------------------+\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
strncpy (synctest, (synctest_p - 23), 24);
|
|
||||||
if (opts->frame_p25p1 == 1)
|
|
||||||
{
|
|
||||||
if (strcmp (synctest, FRAME_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, " P25 Phase 1 ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " +p25p1 ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = 0;
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
if (strcmp (synctest, INV_FRAME_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, " P25 Phase 1 ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -p25p1 ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = 1;
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts->frame_x2tdma == 1)
|
|
||||||
{
|
|
||||||
if (strcmp (synctest, X2TDMA_DATA_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + (lmax)) / 2;
|
|
||||||
state->min = ((state->min) + (lmin)) / 2;
|
|
||||||
if (opts->inverted_x2tdma == 0)
|
|
||||||
{
|
|
||||||
// data frame
|
|
||||||
sprintf (state->ftype, " X2-TDMA ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " +X2-TDMA ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = 2;
|
|
||||||
return (2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// inverted voice frame
|
|
||||||
sprintf (state->ftype, " X2-TDMA ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -X2-TDMA ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
if (state->lastsynctype != 3)
|
|
||||||
{
|
|
||||||
state->firstframe = 1;
|
|
||||||
}
|
|
||||||
state->lastsynctype = 3;
|
|
||||||
return (3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (strcmp (synctest, X2TDMA_VOICE_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
if (opts->inverted_x2tdma == 0)
|
|
||||||
{
|
|
||||||
// voice frame
|
|
||||||
sprintf (state->ftype, " X2-TDMA ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " +X2-TDMA ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
if (state->lastsynctype != 4)
|
|
||||||
{
|
|
||||||
state->firstframe = 1;
|
|
||||||
}
|
|
||||||
state->lastsynctype = 4;
|
|
||||||
return (4);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// inverted data frame
|
|
||||||
sprintf (state->ftype, " X2-TDMA ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -X2-TDMA ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = 5;
|
|
||||||
return (5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts->frame_dmr == 1)
|
|
||||||
{
|
|
||||||
if (strcmp (synctest, DMR_DATA_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + (lmax)) / 2;
|
|
||||||
state->min = ((state->min) + (lmin)) / 2;
|
|
||||||
if (opts->inverted_dmr == 0)
|
|
||||||
{
|
|
||||||
// data frame
|
|
||||||
sprintf (state->ftype, " DMR ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " +DMR ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = 10;
|
|
||||||
return (10);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// inverted voice frame
|
|
||||||
sprintf (state->ftype, " DMR ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -DMR ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
if (state->lastsynctype != 11)
|
|
||||||
{
|
|
||||||
state->firstframe = 1;
|
|
||||||
}
|
|
||||||
state->lastsynctype = 11;
|
|
||||||
return (11);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (strcmp (synctest, DMR_VOICE_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
if (opts->inverted_dmr == 0)
|
|
||||||
{
|
|
||||||
// voice frame
|
|
||||||
sprintf (state->ftype, " DMR ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " +DMR ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
if (state->lastsynctype != 12)
|
|
||||||
{
|
|
||||||
state->firstframe = 1;
|
|
||||||
}
|
|
||||||
state->lastsynctype = 12;
|
|
||||||
return (12);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// inverted data frame
|
|
||||||
sprintf (state->ftype, " DMR ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -DMR ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = 13;
|
|
||||||
return (13);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts->frame_provoice == 1)
|
|
||||||
{
|
|
||||||
strncpy (synctest32, (synctest_p - 31), 32);
|
|
||||||
if (strcmp (synctest32, PROVOICE_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, " ProVoice ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -ProVoice ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = 14;
|
|
||||||
return (14);
|
|
||||||
}
|
|
||||||
else if (strcmp (synctest32, INV_PROVOICE_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, " ProVoice ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -ProVoice ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = 15;
|
|
||||||
return (15);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (opts->frame_nxdn == 1)
|
|
||||||
{
|
|
||||||
strncpy (synctest18, (synctest_p - 17), 18);
|
|
||||||
if (strcmp (synctest18, NXDN96_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, " NXDN96 ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " +NXDN96 ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = 8;
|
|
||||||
return (8);
|
|
||||||
}
|
|
||||||
if (strcmp (synctest18, INV_NXDN96_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, " NXDN96 ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -NXDN96 ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = 9;
|
|
||||||
return (9);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts->frame_dstar == 1)
|
|
||||||
{
|
|
||||||
if (strcmp (synctest, DSTAR_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, " D-STAR ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " +D-STAR ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = 6;
|
|
||||||
return (6);
|
|
||||||
}
|
|
||||||
if (strcmp (synctest, INV_DSTAR_SYNC) == 0)
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, " D-STAR ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, " -D-STAR ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = 7;
|
|
||||||
return (7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((t == 24) && (state->lastsynctype != -1))
|
|
||||||
{
|
|
||||||
if ((state->lastsynctype == 0) && ((state->lastp25type == 1) || (state->lastp25type == 2)))
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + (lmax)) / 2;
|
|
||||||
state->min = ((state->min) + (lmin)) / 2;
|
|
||||||
sprintf (state->ftype, "(P25 Phase 1)");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, "(+p25p1) ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = -1;
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
else if ((state->lastsynctype == 1) && ((state->lastp25type == 1) || (state->lastp25type == 2)))
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, "(P25 Phase 1)");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, "(-p25p1) ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = -1;
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
else if ((state->lastsynctype == 3) && (strcmp (synctest, X2TDMA_VOICE_SYNC) != 0))
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, "(X2-TDMA) ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, "(-X2-TDMA) ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = -1;
|
|
||||||
return (3);
|
|
||||||
}
|
|
||||||
else if ((state->lastsynctype == 4) && (strcmp (synctest, X2TDMA_DATA_SYNC) != 0))
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, "(X2-TDMA) ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, "(+X2-TDMA) ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = -1;
|
|
||||||
return (4);
|
|
||||||
}
|
|
||||||
else if ((state->lastsynctype == 11) && (strcmp (synctest, DMR_VOICE_SYNC) != 0))
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, "(DMR) ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, "(-DMR) ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = -1;
|
|
||||||
return (11);
|
|
||||||
}
|
|
||||||
else if ((state->lastsynctype == 12) && (strcmp (synctest, DMR_DATA_SYNC) != 0))
|
|
||||||
{
|
|
||||||
state->carrier = 1;
|
|
||||||
state->offset = synctest_pos;
|
|
||||||
state->max = ((state->max) + lmax) / 2;
|
|
||||||
state->min = ((state->min) + lmin) / 2;
|
|
||||||
sprintf (state->ftype, "(DMR) ");
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameSync (opts, state, "(+DMR) ", synctest_pos + 1, modulation);
|
|
||||||
}
|
|
||||||
state->lastsynctype = -1;
|
|
||||||
return (12);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (synctest_pos < 10200)
|
|
||||||
{
|
|
||||||
synctest_pos++;
|
|
||||||
synctest_p++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// buffer reset
|
|
||||||
synctest_pos = 0;
|
|
||||||
synctest_p = synctest_buf;
|
|
||||||
noCarrier (opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->carrier == 1)
|
|
||||||
{
|
|
||||||
if (synctest_pos >= 1800)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
if (opts->verbose > 1)
|
|
||||||
{
|
|
||||||
printf ("Sync: no sync\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
noCarrier (opts, state);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
635
dsd_main.c
635
dsd_main.c
@@ -1,635 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _MAIN
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
#include "p25p1_const.h"
|
|
||||||
#include "x2tdma_const.h"
|
|
||||||
#include "dstar_const.h"
|
|
||||||
#include "nxdn96_const.h"
|
|
||||||
#include "dmr_const.h"
|
|
||||||
#include "provoice_const.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
comp (const void *a, const void *b)
|
|
||||||
{
|
|
||||||
if (*((const int *) a) == *((const int *) b))
|
|
||||||
return 0;
|
|
||||||
else if (*((const int *) a) < *((const int *) b))
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
noCarrier (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
state->dibit_buf_p = state->dibit_buf + 200;
|
|
||||||
memset (state->dibit_buf, 0, sizeof (int) * 200);
|
|
||||||
if (opts->mbe_out_f != NULL)
|
|
||||||
{
|
|
||||||
closeMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
state->jitter = -1;
|
|
||||||
state->lastsynctype = -1;
|
|
||||||
state->carrier = 0;
|
|
||||||
state->max = 15000;
|
|
||||||
state->min = -15000;
|
|
||||||
state->center = 0;
|
|
||||||
state->err_str[0] = 0;
|
|
||||||
sprintf (state->fsubtype, " ");
|
|
||||||
sprintf (state->ftype, " ");
|
|
||||||
state->lasttg = 0;
|
|
||||||
state->lastsrc = 0;
|
|
||||||
state->lastp25type = 0;
|
|
||||||
state->repeat = 0;
|
|
||||||
state->nac = 0;
|
|
||||||
state->numtdulc = 0;
|
|
||||||
sprintf (state->slot0light, " slot0 ");
|
|
||||||
sprintf (state->slot1light, " slot1 ");
|
|
||||||
state->firstframe = 0;
|
|
||||||
if (opts->audio_gain == (float) 0)
|
|
||||||
{
|
|
||||||
state->aout_gain = 25;
|
|
||||||
}
|
|
||||||
memset (state->aout_max_buf, 0, sizeof (float) * 200);
|
|
||||||
state->aout_max_buf_p = state->aout_max_buf;
|
|
||||||
state->aout_max_buf_idx = 0;
|
|
||||||
sprintf (state->algid, "________");
|
|
||||||
sprintf (state->keyid, "________________");
|
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
initOpts (dsd_opts * opts)
|
|
||||||
{
|
|
||||||
|
|
||||||
opts->onesymbol = 10;
|
|
||||||
opts->mbe_in_file[0] = 0;
|
|
||||||
opts->mbe_in_f = NULL;
|
|
||||||
opts->errorbars = 1;
|
|
||||||
opts->datascope = 0;
|
|
||||||
opts->symboltiming = 0;
|
|
||||||
opts->verbose = 2;
|
|
||||||
opts->p25enc = 0;
|
|
||||||
opts->p25lc = 0;
|
|
||||||
opts->p25status = 0;
|
|
||||||
opts->p25tg = 0;
|
|
||||||
opts->scoperate = 15;
|
|
||||||
sprintf (opts->audio_in_dev, "/dev/audio");
|
|
||||||
opts->audio_in_fd = -1;
|
|
||||||
sprintf (opts->audio_out_dev, "/dev/audio");
|
|
||||||
opts->audio_out_fd = -1;
|
|
||||||
opts->split = 0;
|
|
||||||
opts->playoffset = 0;
|
|
||||||
opts->mbe_out_dir[0] = 0;
|
|
||||||
opts->mbe_out_file[0] = 0;
|
|
||||||
opts->mbe_out_f = NULL;
|
|
||||||
opts->audio_gain = 0;
|
|
||||||
opts->audio_out = 1;
|
|
||||||
opts->wav_out_file[0] = 0;
|
|
||||||
opts->wav_out_f = NULL;
|
|
||||||
opts->wav_out_fd = -1;
|
|
||||||
opts->serial_baud = 115200;
|
|
||||||
sprintf (opts->serial_dev, "/dev/ttyUSB0");
|
|
||||||
opts->resume = 0;
|
|
||||||
opts->frame_dstar = 0;
|
|
||||||
opts->frame_x2tdma = 1;
|
|
||||||
opts->frame_p25p1 = 1;
|
|
||||||
opts->frame_nxdn = 0;
|
|
||||||
opts->frame_dmr = 1;
|
|
||||||
opts->frame_provoice = 0;
|
|
||||||
opts->mod_c4fm = 1;
|
|
||||||
opts->mod_qpsk = 1;
|
|
||||||
opts->mod_gfsk = 1;
|
|
||||||
opts->uvquality = 3;
|
|
||||||
opts->inverted_x2tdma = 1; // most transmitter + scanner + sound card combinations show inverted signals for this
|
|
||||||
opts->inverted_dmr = 0; // most transmitter + scanner + sound card combinations show non-inverted signals for this
|
|
||||||
opts->mod_threshold = 26;
|
|
||||||
opts->ssize = 36;
|
|
||||||
opts->msize = 15;
|
|
||||||
opts->playfiles = 0;
|
|
||||||
opts->delay = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
initState (dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
state->dibit_buf = malloc (sizeof (int) * 1000000);
|
|
||||||
state->dibit_buf_p = state->dibit_buf + 200;
|
|
||||||
memset (state->dibit_buf, 0, sizeof (int) * 200);
|
|
||||||
state->repeat = 0;
|
|
||||||
state->audio_out_buf = malloc (sizeof (short) * 1000000);
|
|
||||||
memset (state->audio_out_buf, 0, 100 * sizeof (short));
|
|
||||||
state->audio_out_buf_p = state->audio_out_buf + 100;
|
|
||||||
state->audio_out_float_buf = malloc (sizeof (float) * 1000000);
|
|
||||||
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
|
|
||||||
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
|
|
||||||
state->audio_out_idx = 0;
|
|
||||||
state->audio_out_idx2 = 0;
|
|
||||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
|
||||||
state->center = 0;
|
|
||||||
state->jitter = -1;
|
|
||||||
state->synctype = -1;
|
|
||||||
state->min = -15000;
|
|
||||||
state->max = 15000;
|
|
||||||
state->lmid = 0;
|
|
||||||
state->umid = 0;
|
|
||||||
state->minref = -12000;
|
|
||||||
state->maxref = 12000;
|
|
||||||
state->lastsample = 0;
|
|
||||||
for (i = 0; i < 128; i++)
|
|
||||||
{
|
|
||||||
state->sbuf[i] = 0;
|
|
||||||
}
|
|
||||||
state->sidx = 0;
|
|
||||||
for (i = 0; i < 1024; i++)
|
|
||||||
{
|
|
||||||
state->maxbuf[i] = 15000;
|
|
||||||
}
|
|
||||||
for (i = 0; i < 1024; i++)
|
|
||||||
{
|
|
||||||
state->minbuf[i] = -15000;
|
|
||||||
}
|
|
||||||
state->midx = 0;
|
|
||||||
state->err_str[0] = 0;
|
|
||||||
sprintf (state->fsubtype, " ");
|
|
||||||
sprintf (state->ftype, " ");
|
|
||||||
state->symbolcnt = 0;
|
|
||||||
state->rf_mod = 0;
|
|
||||||
state->numflips = 0;
|
|
||||||
state->lastsynctype = -1;
|
|
||||||
state->lastp25type = 0;
|
|
||||||
state->offset = 0;
|
|
||||||
state->carrier = 0;
|
|
||||||
for (j = 0; i < 25; j++)
|
|
||||||
{
|
|
||||||
for (i = 0; j < 16; i++)
|
|
||||||
{
|
|
||||||
state->tg[i][j] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->tgcount = 0;
|
|
||||||
state->lasttg = 0;
|
|
||||||
state->lastsrc = 0;
|
|
||||||
state->nac = 0;
|
|
||||||
state->errs = 0;
|
|
||||||
state->errs2 = 0;
|
|
||||||
state->mbe_file_type = -1;
|
|
||||||
state->optind = 0;
|
|
||||||
state->numtdulc = 0;
|
|
||||||
state->firstframe = 0;
|
|
||||||
sprintf (state->slot0light, " slot0 ");
|
|
||||||
sprintf (state->slot1light, " slot1 ");
|
|
||||||
state->aout_gain = 25;
|
|
||||||
memset (state->aout_max_buf, 0, sizeof (float) * 200);
|
|
||||||
state->aout_max_buf_p = state->aout_max_buf;
|
|
||||||
state->aout_max_buf_idx = 0;
|
|
||||||
state->samplesPerSymbol = 10;
|
|
||||||
state->symbolCenter = 4;
|
|
||||||
sprintf (state->algid, "________");
|
|
||||||
sprintf (state->keyid, "________________");
|
|
||||||
state->currentslot = 0;
|
|
||||||
state->cur_mp = malloc (sizeof (mbe_parms));
|
|
||||||
state->prev_mp = malloc (sizeof (mbe_parms));
|
|
||||||
state->prev_mp_enhanced = malloc (sizeof (mbe_parms));
|
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
usage ()
|
|
||||||
{
|
|
||||||
printf ("Digital Speech Decoder 1.3\n");
|
|
||||||
printf ("\n");
|
|
||||||
printf ("Usage:\n");
|
|
||||||
printf (" dsd [options] Live scanner mode\n");
|
|
||||||
printf (" dsd [options] -r <files> Read/Play saved mbe data from file(s)\n");
|
|
||||||
printf (" dsd -h Show help\n");
|
|
||||||
printf ("\n");
|
|
||||||
printf ("Display Options:\n");
|
|
||||||
printf (" -e Show Frame Info and errorbars (default)\n");
|
|
||||||
printf (" -pe Show p25 encryption sync bits\n");
|
|
||||||
printf (" -pl Show p25 link control bits\n");
|
|
||||||
printf (" -ps Show p25 status bits and low speed data\n");
|
|
||||||
printf (" -pt Show p25 talkgroup info\n");
|
|
||||||
printf (" -q Don't show Frame Info/errorbars\n");
|
|
||||||
printf (" -s Datascope (disables other display options)\n");
|
|
||||||
printf (" -t Show symbol timing during sync\n");
|
|
||||||
printf (" -v <num> Frame information Verbosity\n");
|
|
||||||
printf (" -z <num> Frame rate for datascope\n");
|
|
||||||
printf ("\n");
|
|
||||||
printf ("Input/Output options:\n");
|
|
||||||
printf (" -i <device> Audio input device (default is /dev/audio)\n");
|
|
||||||
printf (" -o <device> Audio output device (default is /dev/audio)\n");
|
|
||||||
printf (" -d <dir> Create mbe data files, use this directory\n");
|
|
||||||
printf (" -g <num> Audio output gain (default = 0 = auto)\n");
|
|
||||||
printf (" -n Do not send synthesized speech to audio output device\n");
|
|
||||||
printf (" -w <file> Output synthesized speech to a .wav file\n");
|
|
||||||
printf ("\n");
|
|
||||||
printf ("Scanner control options:\n");
|
|
||||||
printf (" -B <num> Serial port baud rate (default=115200)\n");
|
|
||||||
printf (" -C <device> Serial port for scanner control (default=/dev/ttyUSB0)\n");
|
|
||||||
printf (" -R <num> Resume scan after <num> TDULC frames or any PDU\n");
|
|
||||||
printf ("\n");
|
|
||||||
printf ("Decoder options:\n");
|
|
||||||
printf (" -fa Auto-detect frame type (default)\n");
|
|
||||||
printf (" -f1 Decode only P25 Phase 1\n");
|
|
||||||
printf (" -fd Decode only D-STAR* (no audio)\n");
|
|
||||||
printf (" -fn Decode only NXDN96* (no audio)\n");
|
|
||||||
printf (" -fp Decode only ProVoice*\n");
|
|
||||||
printf (" -fr Decode only DMR/MOTOTRBO\n");
|
|
||||||
printf (" -fx Decode only X2-TDMA\n");
|
|
||||||
printf (" -ma Auto-select modulation optimizations (default)\n");
|
|
||||||
printf (" -mc Use only C4FM modulation optimizations\n");
|
|
||||||
printf (" -mg Use only GFSK modulation optimizations\n");
|
|
||||||
printf (" -mq Use only QPSK modulation optimizations\n");
|
|
||||||
printf (" -u <num> Unvoiced speech quality (default=3)\n");
|
|
||||||
printf (" -xx Expect non-inverted X2-TDMA signal\n");
|
|
||||||
printf (" -xr Expect inverted DMR/MOTOTRBO signal\n");
|
|
||||||
printf ("\n");
|
|
||||||
printf (" * denotes frame types that cannot be auto-detected.\n");
|
|
||||||
printf ("\n");
|
|
||||||
printf ("Advanced decoder options:\n");
|
|
||||||
printf (" -A <num> QPSK modulation auto detection threshold (default=26)\n");
|
|
||||||
printf (" -S <num> Symbol buffer size for QPSK decision point tracking\n");
|
|
||||||
printf (" (default=36)\n");
|
|
||||||
printf (" -M <num> Min/Max buffer size for QPSK decision point tracking\n");
|
|
||||||
printf (" (default=15)\n");
|
|
||||||
exit (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
liveScanner (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
noCarrier (opts, state);
|
|
||||||
state->synctype = getFrameSync (opts, state);
|
|
||||||
// recalibrate center/umid/lmid
|
|
||||||
state->center = ((state->max) + (state->min)) / 2;
|
|
||||||
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
|
|
||||||
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
|
|
||||||
while (state->synctype != -1)
|
|
||||||
{
|
|
||||||
processFrame (opts, state);
|
|
||||||
state->synctype = getFrameSync (opts, state);
|
|
||||||
// recalibrate center/umid/lmid
|
|
||||||
state->center = ((state->max) + (state->min)) / 2;
|
|
||||||
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
|
|
||||||
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
|
|
||||||
int c;
|
|
||||||
extern char *optarg;
|
|
||||||
extern int optind, opterr, optopt;
|
|
||||||
|
|
||||||
dsd_opts opts;
|
|
||||||
dsd_state state;
|
|
||||||
|
|
||||||
initOpts (&opts);
|
|
||||||
initState (&state);
|
|
||||||
|
|
||||||
while ((c = getopt (argc, argv, "hep:qstv:z:i:o:d:g:nw:B:C:R:f:m:u:x:A:S:M:r")) != -1)
|
|
||||||
{
|
|
||||||
opterr = 0;
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 'h':
|
|
||||||
usage ();
|
|
||||||
exit (0);
|
|
||||||
case 'e':
|
|
||||||
opts.errorbars = 1;
|
|
||||||
opts.datascope = 0;
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
if (optarg[0] == 'e')
|
|
||||||
{
|
|
||||||
opts.p25enc = 1;
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'l')
|
|
||||||
{
|
|
||||||
opts.p25lc = 1;
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 's')
|
|
||||||
{
|
|
||||||
opts.p25status = 1;
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 't')
|
|
||||||
{
|
|
||||||
opts.p25tg = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'q':
|
|
||||||
opts.errorbars = 0;
|
|
||||||
opts.verbose = 0;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
opts.errorbars = 0;
|
|
||||||
opts.p25enc = 0;
|
|
||||||
opts.p25lc = 0;
|
|
||||||
opts.p25status = 0;
|
|
||||||
opts.p25tg = 0;
|
|
||||||
opts.datascope = 1;
|
|
||||||
opts.symboltiming = 0;
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
opts.symboltiming = 1;
|
|
||||||
opts.errorbars = 1;
|
|
||||||
opts.datascope = 0;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
sscanf (optarg, "%d", &opts.verbose);
|
|
||||||
break;
|
|
||||||
case 'z':
|
|
||||||
sscanf (optarg, "%d", &opts.scoperate);
|
|
||||||
opts.errorbars = 0;
|
|
||||||
opts.p25enc = 0;
|
|
||||||
opts.p25lc = 0;
|
|
||||||
opts.p25status = 0;
|
|
||||||
opts.p25tg = 0;
|
|
||||||
opts.datascope = 1;
|
|
||||||
opts.symboltiming = 0;
|
|
||||||
printf ("Setting datascope frame rate to %i frame per second.\n", opts.scoperate);
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
sscanf (optarg, "%s", opts.audio_in_dev);
|
|
||||||
break;
|
|
||||||
case 'o':
|
|
||||||
sscanf (optarg, "%s", opts.audio_out_dev);
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
sscanf (optarg, "%s", opts.mbe_out_dir);
|
|
||||||
printf ("Writing mbe data files to directory %s\n", opts.mbe_out_dir);
|
|
||||||
break;
|
|
||||||
case 'g':
|
|
||||||
sscanf (optarg, "%f", &opts.audio_gain);
|
|
||||||
if (opts.audio_gain <= (float) 0)
|
|
||||||
{
|
|
||||||
opts.audio_gain = (float) 0;
|
|
||||||
printf ("Enabling audio out auto-gain\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf ("Setting audio out gain to %f\n", opts.audio_gain);
|
|
||||||
state.aout_gain = opts.audio_gain;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
opts.audio_out = 0;
|
|
||||||
printf ("Disabling audio output to soundcard.\n");
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
sscanf (optarg, "%s", opts.wav_out_file);
|
|
||||||
printf ("Writing audio to file %s\n", opts.wav_out_file);
|
|
||||||
openWavOutFile (&opts, &state);
|
|
||||||
break;
|
|
||||||
case 'B':
|
|
||||||
sscanf (optarg, "%d", &opts.serial_baud);
|
|
||||||
break;
|
|
||||||
case 'C':
|
|
||||||
sscanf (optarg, "%s", opts.serial_dev);
|
|
||||||
break;
|
|
||||||
case 'R':
|
|
||||||
sscanf (optarg, "%d", &opts.resume);
|
|
||||||
printf ("Enabling scan resume after %i TDULC frames\n", opts.resume);
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
if (optarg[0] == 'a')
|
|
||||||
{
|
|
||||||
opts.frame_dstar = 0;
|
|
||||||
opts.frame_x2tdma = 1;
|
|
||||||
opts.frame_p25p1 = 1;
|
|
||||||
opts.frame_nxdn = 0;
|
|
||||||
opts.frame_dmr = 1;
|
|
||||||
opts.frame_provoice = 0;
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'd')
|
|
||||||
{
|
|
||||||
opts.frame_dstar = 1;
|
|
||||||
opts.frame_x2tdma = 0;
|
|
||||||
opts.frame_p25p1 = 0;
|
|
||||||
opts.frame_nxdn = 0;
|
|
||||||
opts.frame_dmr = 0;
|
|
||||||
opts.frame_provoice = 0;
|
|
||||||
printf ("Decoding only D-STAR frames.\n");
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'x')
|
|
||||||
{
|
|
||||||
opts.frame_dstar = 0;
|
|
||||||
opts.frame_x2tdma = 1;
|
|
||||||
opts.frame_p25p1 = 0;
|
|
||||||
opts.frame_nxdn = 0;
|
|
||||||
opts.frame_dmr = 0;
|
|
||||||
opts.frame_provoice = 0;
|
|
||||||
printf ("Decoding only X2-TDMA frames.\n");
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'p')
|
|
||||||
{
|
|
||||||
opts.frame_dstar = 0;
|
|
||||||
opts.frame_x2tdma = 0;
|
|
||||||
opts.frame_p25p1 = 0;
|
|
||||||
opts.frame_nxdn = 0;
|
|
||||||
opts.frame_dmr = 0;
|
|
||||||
opts.frame_provoice = 1;
|
|
||||||
state.samplesPerSymbol = 5;
|
|
||||||
state.symbolCenter = 2;
|
|
||||||
opts.mod_c4fm = 0;
|
|
||||||
opts.mod_qpsk = 0;
|
|
||||||
opts.mod_gfsk = 1;
|
|
||||||
state.rf_mod = 2;
|
|
||||||
printf ("Enabling only GFSK modulation optimizations.\n");
|
|
||||||
printf ("Decoding only ProVoice frames.\n");
|
|
||||||
}
|
|
||||||
else if (optarg[0] == '1')
|
|
||||||
{
|
|
||||||
opts.frame_dstar = 0;
|
|
||||||
opts.frame_x2tdma = 0;
|
|
||||||
opts.frame_p25p1 = 1;
|
|
||||||
opts.frame_nxdn = 0;
|
|
||||||
opts.frame_dmr = 0;
|
|
||||||
opts.frame_provoice = 0;
|
|
||||||
printf ("Decoding only P25 Phase 1 frames.\n");
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'n')
|
|
||||||
{
|
|
||||||
opts.frame_dstar = 0;
|
|
||||||
opts.frame_x2tdma = 0;
|
|
||||||
opts.frame_p25p1 = 0;
|
|
||||||
opts.frame_nxdn = 1;
|
|
||||||
opts.frame_dmr = 0;
|
|
||||||
opts.frame_provoice = 0;
|
|
||||||
printf ("Decoding only NXDN 9600 baud frames.\n");
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'r')
|
|
||||||
{
|
|
||||||
opts.frame_dstar = 0;
|
|
||||||
opts.frame_x2tdma = 0;
|
|
||||||
opts.frame_p25p1 = 0;
|
|
||||||
opts.frame_nxdn = 0;
|
|
||||||
opts.frame_dmr = 1;
|
|
||||||
opts.frame_provoice = 0;
|
|
||||||
printf ("Decoding only DMR/MOTOTRBO frames.\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
if (optarg[0] == 'a')
|
|
||||||
{
|
|
||||||
opts.mod_c4fm = 1;
|
|
||||||
opts.mod_qpsk = 1;
|
|
||||||
opts.mod_gfsk = 1;
|
|
||||||
state.rf_mod = 0;
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'c')
|
|
||||||
{
|
|
||||||
opts.mod_c4fm = 1;
|
|
||||||
opts.mod_qpsk = 0;
|
|
||||||
opts.mod_gfsk = 0;
|
|
||||||
state.rf_mod = 0;
|
|
||||||
printf ("Enabling only C4FM modulation optimizations.\n");
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'g')
|
|
||||||
{
|
|
||||||
opts.mod_c4fm = 0;
|
|
||||||
opts.mod_qpsk = 0;
|
|
||||||
opts.mod_gfsk = 1;
|
|
||||||
state.rf_mod = 2;
|
|
||||||
printf ("Enabling only GFSK modulation optimizations.\n");
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'q')
|
|
||||||
{
|
|
||||||
opts.mod_c4fm = 0;
|
|
||||||
opts.mod_qpsk = 1;
|
|
||||||
opts.mod_gfsk = 0;
|
|
||||||
state.rf_mod = 1;
|
|
||||||
printf ("Enabling only QPSK modulation optimizations.\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
sscanf (optarg, "%i", &opts.uvquality);
|
|
||||||
if (opts.uvquality < 1)
|
|
||||||
{
|
|
||||||
opts.uvquality = 1;
|
|
||||||
}
|
|
||||||
else if (opts.uvquality > 64)
|
|
||||||
{
|
|
||||||
opts.uvquality = 64;
|
|
||||||
}
|
|
||||||
printf ("Setting unvoice speech quality to %i waves per band.\n", opts.uvquality);
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
if (optarg[0] == 'x')
|
|
||||||
{
|
|
||||||
opts.inverted_x2tdma = 0;
|
|
||||||
printf ("Expecting non-inverted X2-TDMA signals.\n");
|
|
||||||
}
|
|
||||||
else if (optarg[0] == 'r')
|
|
||||||
{
|
|
||||||
opts.inverted_dmr = 1;
|
|
||||||
printf ("Expecting inverted DMR/MOTOTRBO signals.\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'A':
|
|
||||||
sscanf (optarg, "%i", &opts.mod_threshold);
|
|
||||||
printf ("Setting C4FM/QPSK auto detection threshold to %i\n", opts.mod_threshold);
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
sscanf (optarg, "%i", &opts.ssize);
|
|
||||||
if (opts.ssize > 128)
|
|
||||||
{
|
|
||||||
opts.ssize = 128;
|
|
||||||
}
|
|
||||||
else if (opts.ssize < 1)
|
|
||||||
{
|
|
||||||
opts.ssize = 1;
|
|
||||||
}
|
|
||||||
printf ("Setting QPSK symbol buffer to %i\n", opts.ssize);
|
|
||||||
break;
|
|
||||||
case 'M':
|
|
||||||
sscanf (optarg, "%i", &opts.msize);
|
|
||||||
if (opts.msize > 1024)
|
|
||||||
{
|
|
||||||
opts.msize = 1024;
|
|
||||||
}
|
|
||||||
else if (opts.msize < 1)
|
|
||||||
{
|
|
||||||
opts.msize = 1;
|
|
||||||
}
|
|
||||||
printf ("Setting QPSK Min/Max buffer to %i\n", opts.msize);
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
opts.playfiles = 1;
|
|
||||||
opts.errorbars = 0;
|
|
||||||
opts.datascope = 0;
|
|
||||||
state.optind = optind;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usage ();
|
|
||||||
exit (0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (opts.resume > 0)
|
|
||||||
{
|
|
||||||
openSerial (&opts, &state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.playfiles == 1)
|
|
||||||
{
|
|
||||||
opts.split = 1;
|
|
||||||
opts.playoffset = 0;
|
|
||||||
opts.delay = 0;
|
|
||||||
openAudioOutDevice (&opts, 8000);
|
|
||||||
}
|
|
||||||
else if (strcmp (opts.audio_in_dev, opts.audio_out_dev) != 0)
|
|
||||||
{
|
|
||||||
opts.split = 1;
|
|
||||||
opts.playoffset = 0;
|
|
||||||
opts.delay = 0;
|
|
||||||
openAudioOutDevice (&opts, 8000);
|
|
||||||
openAudioInDevice (&opts);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
opts.split = 0;
|
|
||||||
opts.playoffset = 38;
|
|
||||||
opts.delay = 0;
|
|
||||||
openAudioInDevice (&opts);
|
|
||||||
opts.audio_out_fd = opts.audio_in_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.playfiles == 1)
|
|
||||||
{
|
|
||||||
playMbeFiles (&opts, &state, argc, argv);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
liveScanner (&opts, &state);
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
122
dsd_mbe.c
122
dsd_mbe.c
@@ -1,122 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
|
|
||||||
{
|
|
||||||
|
|
||||||
int i;
|
|
||||||
char imbe_d[88];
|
|
||||||
char ambe_d[49];
|
|
||||||
|
|
||||||
for (i = state->optind; i < argc; i++)
|
|
||||||
{
|
|
||||||
sprintf (opts->mbe_in_file, argv[i]);
|
|
||||||
openMbeInFile (opts, state);
|
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
|
||||||
printf ("playing %s\n", opts->mbe_in_file);
|
|
||||||
while (feof (opts->mbe_in_f) == 0)
|
|
||||||
{
|
|
||||||
if (state->mbe_file_type == 0)
|
|
||||||
{
|
|
||||||
readImbe4400Data (opts, state, imbe_d);
|
|
||||||
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
|
||||||
processAudio (opts, state);
|
|
||||||
if (opts->wav_out_fd != -1)
|
|
||||||
{
|
|
||||||
writeSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->audio_out == 1)
|
|
||||||
{
|
|
||||||
playSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (state->mbe_file_type == 1)
|
|
||||||
{
|
|
||||||
readAmbe2250Data (opts, state, ambe_d);
|
|
||||||
mbe_processAmbe2250Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
|
||||||
processAudio (opts, state);
|
|
||||||
if (opts->wav_out_fd != -1)
|
|
||||||
{
|
|
||||||
writeSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
if (opts->audio_out == 1)
|
|
||||||
{
|
|
||||||
playSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24])
|
|
||||||
{
|
|
||||||
|
|
||||||
int i;
|
|
||||||
char imbe_d[88];
|
|
||||||
char ambe_d[49];
|
|
||||||
|
|
||||||
for (i = 0; i < 88; i++)
|
|
||||||
{
|
|
||||||
imbe_d[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((state->synctype == 0) || (state->synctype == 1))
|
|
||||||
{
|
|
||||||
mbe_processImbe7200x4400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe_fr, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
|
||||||
if (opts->mbe_out_f != NULL)
|
|
||||||
{
|
|
||||||
saveImbe4400Data (opts, state, imbe_d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((state->synctype == 14) || (state->synctype == 15))
|
|
||||||
{
|
|
||||||
mbe_processImbe7100x4400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe7100_fr, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
|
||||||
if (opts->mbe_out_f != NULL)
|
|
||||||
{
|
|
||||||
saveImbe4400Data (opts, state, imbe_d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mbe_processAmbe3600x2250Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
|
||||||
if (opts->mbe_out_f != NULL)
|
|
||||||
{
|
|
||||||
saveAmbe2250Data (opts, state, ambe_d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("%s", state->err_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
processAudio (opts, state);
|
|
||||||
if (opts->wav_out_fd != -1)
|
|
||||||
{
|
|
||||||
writeSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->audio_out == 1)
|
|
||||||
{
|
|
||||||
playSynthesizedVoice (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
180
dsd_symbol.c
180
dsd_symbol.c
@@ -1,180 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|
||||||
{
|
|
||||||
|
|
||||||
short sample;
|
|
||||||
int i, sum, symbol, count;
|
|
||||||
|
|
||||||
sum = 0;
|
|
||||||
count = 0;
|
|
||||||
for (i = 0; i < state->samplesPerSymbol; i++)
|
|
||||||
{
|
|
||||||
// timing control
|
|
||||||
if ((i == 0) && (have_sync == 0))
|
|
||||||
{
|
|
||||||
if (state->rf_mod == 1)
|
|
||||||
{
|
|
||||||
if ((state->jitter >= 0) && (state->jitter < state->symbolCenter))
|
|
||||||
{
|
|
||||||
i++; // fall back
|
|
||||||
}
|
|
||||||
else if ((state->jitter > state->symbolCenter) && (state->jitter < 10))
|
|
||||||
{
|
|
||||||
i--; // catch up
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (state->rf_mod == 2)
|
|
||||||
{
|
|
||||||
if ((state->jitter >= state->symbolCenter - 1) && (state->jitter <= state->symbolCenter))
|
|
||||||
{
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
else if ((state->jitter >= state->symbolCenter + 1) && (state->jitter <= state->symbolCenter + 2))
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (state->rf_mod == 0)
|
|
||||||
{
|
|
||||||
if ((state->jitter > 0) && (state->jitter <= state->symbolCenter))
|
|
||||||
{
|
|
||||||
i--; // catch up
|
|
||||||
}
|
|
||||||
else if ((state->jitter > state->symbolCenter) && (state->jitter < state->samplesPerSymbol))
|
|
||||||
{
|
|
||||||
i++; // fall back
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->jitter = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
read (opts->audio_in_fd, &sample, 2);
|
|
||||||
if ((sample > state->max) && (have_sync == 1) && (state->rf_mod == 0))
|
|
||||||
{
|
|
||||||
sample = state->max;
|
|
||||||
}
|
|
||||||
else if ((sample < state->min) && (have_sync == 1) && (state->rf_mod == 0))
|
|
||||||
{
|
|
||||||
sample = state->min;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sample > state->center)
|
|
||||||
{
|
|
||||||
if (state->lastsample < state->center)
|
|
||||||
{
|
|
||||||
state->numflips += 1;
|
|
||||||
}
|
|
||||||
if (sample > (state->maxref * 1.25))
|
|
||||||
{
|
|
||||||
if (state->lastsample < (state->maxref * 1.25))
|
|
||||||
{
|
|
||||||
state->numflips += 1;
|
|
||||||
}
|
|
||||||
if ((state->jitter < 0) && (state->rf_mod == 1))
|
|
||||||
{ // first spike out of place
|
|
||||||
state->jitter = i;
|
|
||||||
}
|
|
||||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
|
||||||
{
|
|
||||||
printf ("O");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
|
||||||
{
|
|
||||||
printf ("+");
|
|
||||||
}
|
|
||||||
if ((state->jitter < 0) && (state->lastsample < state->center) && (state->rf_mod != 1))
|
|
||||||
{ // first transition edge
|
|
||||||
state->jitter = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ // sample < 0
|
|
||||||
if (state->lastsample > state->center)
|
|
||||||
{
|
|
||||||
state->numflips += 1;
|
|
||||||
}
|
|
||||||
if (sample < (state->minref * 1.25))
|
|
||||||
{
|
|
||||||
if (state->lastsample > (state->minref * 1.25))
|
|
||||||
{
|
|
||||||
state->numflips += 1;
|
|
||||||
}
|
|
||||||
if ((state->jitter < 0) && (state->rf_mod == 1))
|
|
||||||
{ // first spike out of place
|
|
||||||
state->jitter = i;
|
|
||||||
}
|
|
||||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
|
||||||
{
|
|
||||||
printf ("X");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
|
||||||
{
|
|
||||||
printf ("-");
|
|
||||||
}
|
|
||||||
if ((state->jitter < 0) && (state->lastsample > state->center) && (state->rf_mod != 1))
|
|
||||||
{ // first transition edge
|
|
||||||
state->jitter = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state->samplesPerSymbol == 5)
|
|
||||||
{
|
|
||||||
if ((i >= 2) && (i <= 2))
|
|
||||||
{
|
|
||||||
sum += sample;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (((i >= state->symbolCenter - 1) && (i <= state->symbolCenter + 2) && (state->rf_mod == 0)) || (((i == state->symbolCenter) || (i == state->symbolCenter + 1)) && (state->rf_mod != 0)))
|
|
||||||
{
|
|
||||||
sum += sample;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->lastsample = sample;
|
|
||||||
}
|
|
||||||
symbol = (sum / count);
|
|
||||||
|
|
||||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
|
||||||
{
|
|
||||||
if (state->jitter >= 0)
|
|
||||||
{
|
|
||||||
printf (" %i\n", state->jitter);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state->symbolcnt++;
|
|
||||||
return (symbol);
|
|
||||||
}
|
|
||||||
171
dsd_upsample.c
171
dsd_upsample.c
@@ -1,171 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
upsample (dsd_state * state, float invalue)
|
|
||||||
{
|
|
||||||
|
|
||||||
int i, j, sum;
|
|
||||||
float *outbuf1, a, b, c, d;
|
|
||||||
float phasestart, phaseend, resamplestart, resampleend, phasestep, resampleamp2;
|
|
||||||
float resamplemin, resamplemax;
|
|
||||||
|
|
||||||
if (state->audio_out_idx2 > 23)
|
|
||||||
{
|
|
||||||
outbuf1 = state->audio_out_float_buf_p;
|
|
||||||
outbuf1--;
|
|
||||||
c = *outbuf1;
|
|
||||||
outbuf1 -= 6;
|
|
||||||
b = *outbuf1;
|
|
||||||
outbuf1 -= 6;
|
|
||||||
a = *outbuf1;
|
|
||||||
d = (invalue);
|
|
||||||
resamplemin = 32767;
|
|
||||||
resamplemax = -32768;
|
|
||||||
if (a < resamplemin)
|
|
||||||
{
|
|
||||||
resamplemin = a;
|
|
||||||
}
|
|
||||||
if (a > resamplemax)
|
|
||||||
{
|
|
||||||
resamplemax = a;
|
|
||||||
}
|
|
||||||
if (b < resamplemin)
|
|
||||||
{
|
|
||||||
resamplemin = b;
|
|
||||||
}
|
|
||||||
if (b > resamplemax)
|
|
||||||
{
|
|
||||||
resamplemax = b;
|
|
||||||
}
|
|
||||||
if (c < resamplemin)
|
|
||||||
{
|
|
||||||
resamplemin = c;
|
|
||||||
}
|
|
||||||
if (c > resamplemax)
|
|
||||||
{
|
|
||||||
resamplemax = c;
|
|
||||||
}
|
|
||||||
if (d < resamplemin)
|
|
||||||
{
|
|
||||||
resamplemin = d;
|
|
||||||
}
|
|
||||||
if (d > resamplemax)
|
|
||||||
{
|
|
||||||
resamplemax = d;
|
|
||||||
}
|
|
||||||
resampleamp2 = ((float) resamplemax - (float) resamplemin) / (float) 2;
|
|
||||||
#ifdef UPSAMPLE_DEBUG
|
|
||||||
printf ("a:%f b:%f c:%f d:%f resamplemin:%f resamplemax:%f resampleamp2:%f\n", a, b, c, d, resamplemin, resamplemax, resampleamp2);
|
|
||||||
#endif
|
|
||||||
if (resampleamp2 == 0)
|
|
||||||
{
|
|
||||||
resamplestart = (float) 0;
|
|
||||||
resampleend = (float) 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
resamplestart = (((float) b - (float) resamplemin) / resampleamp2) - (float) 1.0;
|
|
||||||
resampleend = (((float) c - (float) resamplemin) / resampleamp2) - (float) 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resamplestart < -1)
|
|
||||||
{
|
|
||||||
resamplestart = (float) -1;
|
|
||||||
}
|
|
||||||
else if (resamplestart > (float) 1)
|
|
||||||
{
|
|
||||||
resamplestart = (float) 1;
|
|
||||||
}
|
|
||||||
if (resampleend < -1)
|
|
||||||
{
|
|
||||||
resampleend = (float) -1;
|
|
||||||
}
|
|
||||||
else if (resampleend > (float) 1)
|
|
||||||
{
|
|
||||||
resampleend = (float) 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
phasestart = asinf (resamplestart);
|
|
||||||
phaseend = asinf (resampleend);
|
|
||||||
phasestep = (phaseend - phasestart) / 6;
|
|
||||||
#ifdef UPSAMPLE_DEBUG
|
|
||||||
printf ("resamplestart:%f, resampleend:%f, phasestart:%f, phaseend:%f, phasestep:%f\n", resamplestart, resampleend, phasestart, phaseend, phasestep);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// rewrite samples between b and c
|
|
||||||
outbuf1 = state->audio_out_float_buf_p;
|
|
||||||
outbuf1 -= 6;
|
|
||||||
for (i = 1; i < 6; i++)
|
|
||||||
{
|
|
||||||
*outbuf1 = (resamplemin + resampleamp2 + (resampleamp2 * sin (phasestart + (phasestep * (float) i))));
|
|
||||||
#ifdef UPSAMPLE_DEBUG
|
|
||||||
printf ("audio_out_idx2:%i outbuf:%f\n", state->audio_out_idx2, *outbuf1);
|
|
||||||
#endif
|
|
||||||
outbuf1++;
|
|
||||||
}
|
|
||||||
#ifdef UPSAMPLE_DEBUG
|
|
||||||
printf ("(c):%f\n", *outbuf1);
|
|
||||||
#endif
|
|
||||||
// write d
|
|
||||||
outbuf1 += 6;
|
|
||||||
*outbuf1 = d;
|
|
||||||
#ifdef UPSAMPLE_DEBUG
|
|
||||||
printf ("audio_out_idx2:%i outbuf(d):%f\n", state->audio_out_idx2, *outbuf1);
|
|
||||||
#endif
|
|
||||||
outbuf1++;
|
|
||||||
|
|
||||||
// smooth
|
|
||||||
outbuf1 -= 36;
|
|
||||||
for (j = 0; j < 12; j++)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
sum = 0;
|
|
||||||
outbuf1--;
|
|
||||||
sum += *outbuf1;
|
|
||||||
outbuf1++;
|
|
||||||
sum += *outbuf1;
|
|
||||||
outbuf1++;
|
|
||||||
sum += *outbuf1;
|
|
||||||
outbuf1--;
|
|
||||||
*outbuf1 = (sum / (float) 3);
|
|
||||||
outbuf1++;
|
|
||||||
}
|
|
||||||
outbuf1 -= 11;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
outbuf1 = state->audio_out_float_buf_p;
|
|
||||||
outbuf1--;
|
|
||||||
c = *outbuf1;
|
|
||||||
*outbuf1 = ((invalue * (float) 0.166) + (c * (float) 0.834));
|
|
||||||
outbuf1++;
|
|
||||||
*outbuf1 = ((invalue * (float) 0.332) + (c * (float) 0.668));
|
|
||||||
outbuf1++;
|
|
||||||
*outbuf1 = ((invalue * (float) 0.5) + (c * (float) 0.5));
|
|
||||||
outbuf1++;
|
|
||||||
*outbuf1 = ((invalue * (float) 0.668) + (c * (float) 0.332));
|
|
||||||
outbuf1++;
|
|
||||||
*outbuf1 = ((invalue * (float) 0.834) + (c * (float) 0.166));
|
|
||||||
outbuf1++;
|
|
||||||
*outbuf1 = invalue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
79
dstar.c
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
|
|
||||||
193
examples/Example_Usage.md
Normal file
193
examples/Example_Usage.md
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
### Example Usage and Notes!
|
||||||
|
|
||||||
|
`dsd-fme` is all you need to run for pulse input, pulse output, and auto detect for DMR BS/MS, and P25. 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, and DMR Stereo)
|
||||||
|
-fs DMR Stereo, also may need to use -xr if inverted DMR
|
||||||
|
-fa Legacy Auto (not recommended)
|
||||||
|
-fi NXDN48
|
||||||
|
-fn NXDN96
|
||||||
|
-fp ProVoice
|
||||||
|
-fm dPMR, also may need to use -xd if inverted dPMR.
|
||||||
|
-fr DMR Mono, also may need to use -xr if inverted DMR.
|
||||||
|
-f1 P25P1
|
||||||
|
-f2 P25P2 (may need to specify wacn/sys/nac manually)
|
||||||
|
-fx X2-TDMA
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conventional Frequency "Fast Scanner"
|
||||||
|
This feature is used to allow DSD-FME to use RIGCTL or RTL input and cycle through frequencies as fast as possible to attain a frame sync.
|
||||||
|
|
||||||
|
This works almost identically to how the trunkng methods work, but instead of locking onto a control channel and tuning based on its decoding, it rapidly tunes through all loaded frequencies in a CSV file and stops when it finds a frame sync. This method is meant for loading a bunch of conventional non-trunking frequencies that signal only when voice or short data bursts are present (DMR T2, Conventional P25 P1, Conventional NXDN, etc) and decode them until the frame sync stops and then resume its scan. This method uses SDR++ RIGCTL, or RTL internal handling to go through signal. -Y is the fast scanner switch.
|
||||||
|
|
||||||
|
To scan through a mixture of DMR and P25 Conventional, use:
|
||||||
|
`dsd-fme -i tcp -C channel_map.csv -G group.csv -Y -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
|
To scan through NXDN48 only conventional, use:
|
||||||
|
`dsd-fme -fi -i tcp -C channel_map.csv -Y -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
|
To scan through NXDN96 only conventional, use:
|
||||||
|
`dsd-fme -fn -i tcp -C channel_map.csv -Y -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
|
The Channel Map setup will be identical to any of the channel maps found for trunking systems, channel numbers do not matter and can be arbitrarily organized, and frequencies can be duplicated to be 'scanned' more frequently in larger CSV files. Scan speed by default is 1 second, and is most stable at that speed. This can be adjusted by using the `-t 2` option, for example, to specify 2 seconds of hang time per frequency. ~~If activity occurs on a frequency, an additional 2 seconds of 'hangtime' will occur on that frequency for any follow up voice activity.~~
|
||||||
|
|
||||||
|
Please Note: DMR Simplex may not behave well on fast scan, I have not been able to test it as such, but given the nature of the on-off timeslot signalling on DMR Simplex, missed frame syncs and skipping/resuming scan may occur.
|
||||||
|
|
||||||
|
## Trunking Examples
|
||||||
|
Please see trunking.sh for sample start up scripts or scroll down to the bottom for more explanation.
|
||||||
|
|
||||||
|
## Example Usage - Default Decoder, Ncurses Terminal, Pulse Input/Output, and Log Console to file
|
||||||
|
`dsd-fme -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)
|
||||||
|
|
||||||
|
### Input ###
|
||||||
|
|
||||||
|
--TCP Direct Audio Link with SDR++
|
||||||
|
|
||||||
|
`dsd-fme -i tcp` Currently defaults to localhost:7355 (SDR++ defaults)
|
||||||
|
|
||||||
|
`dsd-fme -i tcp:192.168.7.5:7356` (remote host and custom port)
|
||||||
|
|
||||||
|
--48000/9600 Mono Wav File Input
|
||||||
|
|
||||||
|
`dsd-fme -i filename.wav` 48k/1 Audio (SDR++, GQRX wav recordings)
|
||||||
|
|
||||||
|
`dsd-fme -i filename.wav -s 96000` 96k/1 (DSDPlus Raw Signal Wav Files)
|
||||||
|
|
||||||
|
Wav File Input Note: Due to 96000 rate audio requiring me to double the symbol rate and center, be sure to use the -s 96000 at the very end of the startup command. Also, some NXDN48/96 may have difficulties decoding properly with wav file input.
|
||||||
|
|
||||||
|
## Example Usage - RTL2832 Dongle Input
|
||||||
|
Short Setup (all default values):
|
||||||
|
|
||||||
|
`dsd-fme -i rtl`
|
||||||
|
|
||||||
|
Detailed Setup:
|
||||||
|
|
||||||
|
`dsd-fme -ft -i rtl:0:154.9875M:26:-2:8:0:6020 `
|
||||||
|
|
||||||
|
```
|
||||||
|
RTL-SDR options:
|
||||||
|
Usage: rtl:dev:freq:gain:ppm:bw:sq:udp
|
||||||
|
NOTE: all arguments after rtl are optional now for trunking, but user configuration is recommended
|
||||||
|
dev <num> RTL-SDR Device Index Number
|
||||||
|
freq <num> RTL-SDR Frequency (851800000 or 851.8M)
|
||||||
|
gain <num> RTL-SDR Device Gain (0-49)(default = 0; Hardware AGC recommended)
|
||||||
|
ppm <num> RTL-SDR PPM Error (default = 0)
|
||||||
|
bw <num> RTL-SDR Bandwidth kHz (default = 12)(8, 12, 16, 24)
|
||||||
|
sq <num> RTL-SDR Squelch Level vs RMS Value (Optional)
|
||||||
|
udp <num> RTL-SDR Legacy UDP Remote Port (Optional -- External Use Only)
|
||||||
|
Example: dsd-fme-zdev -fs -i rtl -C cap_plus_channel.csv -T
|
||||||
|
Example: dsd-fme-zdev -fp -i rtl:0:851.375M:22:-2:24:0:6021
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Usage - Extra Payload/PDU Logging
|
||||||
|
|
||||||
|
`dsd-fme -Z -N 2> log.ans`
|
||||||
|
|
||||||
|
and in a second terminal tab, same folder, run
|
||||||
|
|
||||||
|
`tail -n 40 -f log.ans`
|
||||||
|
|
||||||
|
### EDACS/P25/NXDN/DMR Simple/Single VFO Trunking ###
|
||||||
|
|
||||||
|
EDACS Trunking (w/ channel map import)
|
||||||
|
|
||||||
|
--EDACS/PV Trunking using RIGCTL and TCP Direct Link Audio inside of SDR++ (Tested and Working on EDACS/EDACS-EA with Provoice only, no analog voice monitoring)
|
||||||
|
|
||||||
|
`dsd-fme -i tcp -fp -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
|
--NXDN48 Trunking (standard band plan) with SDR++ (untested for frequency accuracy)
|
||||||
|
|
||||||
|
`dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
|
--NXDN48 Trunking (w/ channel map import) with SDR++ (put control channel frequency at channel 0 in channel_map.csv)
|
||||||
|
|
||||||
|
`dsd-fme -fi -i tcp -T -U 4532 -C channel_map.csv -N 2> log.ans`
|
||||||
|
|
||||||
|
--DMR Trunking (w/ channel map import) with SDR++ (put control channel frequency at channel 0 (TIII and Con+) in channel_map.csv)
|
||||||
|
|
||||||
|
`dsd-fme -fs -i tcp -T -U 4532 -C channel_map.csv -N 2> log.ans`
|
||||||
|
|
||||||
|
--P25 Trunking P1 and P2 (C4FM) with SDR++
|
||||||
|
|
||||||
|
`dsd-fme -i tcp -T -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
|
--P25 Trunking (QPSK) with P1 Control Channel (Should switch symbol rate and center on Phase 2 audio channels)
|
||||||
|
|
||||||
|
`dsd-fme -i tcp -T -U 4532 -N -mq 2> log.ans`
|
||||||
|
|
||||||
|
--P25 Trunking Phase 2 TDMA Control Channel systems with QPSK (non Phase 1 systems)
|
||||||
|
|
||||||
|
`dsd-fme -i tcp -T -U 4532 -N -f2 -m2 2> log.ans`
|
||||||
|
|
||||||
|
Trunking Note1: All samples above can also be run with the RTL input method and setting of RTL UDP remote port. In terms of performance, however, SDR++ will do a much better job on weaker/marginal signal compared to the RTL input. RTL input should only be used on strong signal.
|
||||||
|
|
||||||
|
`dsd-fme -fp -i rtl:0:851.8M:22:-2:24:0:6020 -T -C channel_map.csv -G group.csv -N 2> log.ans`
|
||||||
|
|
||||||
|
Trunking Note2: QPSK Phase 1 and Phase 2 Systems may be subceptible to LSM distortion issues, but seem to do okay, but require really good signal. Some CRC issues still occur with Phase 2 TDMA LCCH Mac Signal that can affect reliability, I believe this issue is ultimately caused by the PSK demodulation inside of FME. I also don't believe this will work on 8-level PSK, but I cannot determine that at the moment. Update: I have improved the LCCH Mac Signal decoding my increasing the QPSK decision point buffers to their maximum values.
|
||||||
|
|
||||||
|
Trunking Note3: DMR Trunking has been coded, and some testing and tweaks have been carried out. Cap+, Con+, and TIII systems seem to do well with trunking now. Placing the frequency for the control channel at channel map 0 in your channel_map.csv file is not required now if using RIGCTL or the RTL Input, both can poll the VFO for the current frequency if it believes its on a control channel, but setting a fake channel number (i.e. 999) first with the CC frequency will result in finding the CC faster on startup if desired. If you need to map out your channels for TIII, you can observe the console output and look for channel numbers. For conveniece I have included the DSDPlus channel numbering (as best as I can figure it, but they seem consistent) into the console print so it will make it easier for users from DSDPlus to map frequencies into the channel_map.csv file. Make sure your channel numbers are the Cd (channel decimal) values from the log, and not the C+ (dsdplus) values. Notice: TIII Site ID value needs work to determine proper DMRLA values for system area and sub area.
|
||||||
|
|
||||||
|
```
|
||||||
|
Talkgroup Voice Channel Grant (TV_GRANT) - Logical
|
||||||
|
Ch [036] Cd [0054] C+ [0110] - TS [1] - Target [01900500] - Source [01900505]
|
||||||
|
```
|
||||||
|
|
||||||
|
Use channel 54 in your import file, which would correspond to dsdplus channels 109 and 110 (109 = TS0 / 110 = TS1).
|
||||||
|
|
||||||
|
For Connect Plus, enumerate your list from 1 to the last channel and add the frequency. For Capacity Plus, LSN 1 and 2 will share the same frequency, 3 and 4 will share, 5 and 6 will share, and 7-8 will share, as Capacity Plus counts each 'channel' as two seperate channels (LSN), one for each time slot. Capacity Plus Quirk: DSD-FME makes its best effort to follow the rest channel in the event that the sync is lost for longer than the hangtime, but occassionally, DSD-FME may lose the rest channel and will have to hunt through all frequencies to find it again.
|
||||||
|
|
||||||
|
Trunking Note4: NXDN Trunking v1 will require a channel map. Please see the example folder for an appropriate channel map. NOTICE: NXDN trunking has been fixed on the RTL (rtl_fm) input method and works correctly now using a soft squelch value to determine when to look for frame sync. Tests show this works very well, but squelch adjustments may be required to differentiate the noise floor from the signal.
|
||||||
|
|
||||||
|
NXDN Trunking Update: NXDN DFA (Direct Frequency Assignment) has been coded from the v2 documents, so using a channel map may not be required, as long as the DFA is configured with standard values, and not 'system definable' values. This will only work on trunking systems that use DFA (newer systems) in accordance to the v2 CAI document NXDN TS 1-A Version 2.0 September 2016. If the system uses 'system definable' values, then make sure to put the channels in a channel map csv file as as the 16-bit OFN values for proper tuning.
|
||||||
|
|
||||||
|
NXDN Type-D/IDAS Update: Type-D or IDAS decoding/trunking has been coded now and tested on a small two channel system. Set up a channel map much like Type-C trunking. If preferred, insert channel 31 at the top of your channel map for the 'home repeater' or go to channel of choice during a TX_REL or DISC message. Extensive testing of Type-D has not been conducted. Some cavaets may currently include iffy per call wav file saving and potentially shifting SRC and TGT ID values due to how SCCH messages are handled and how Busy Repeater Messages are handled. Private calls will need to be enabled by enabling 'data calls', if desired. During testing, only data calls came on private calls, so this was set to disabled by default, but can be enabled with data calls enabled. Also, note that some home repeater channels may or may not carry calls from all systems.
|
||||||
|
|
||||||
|
Channel Map and Group CSV Note: Leave the top line of the channel_map.csv and group.csv as the label, do not delete the line, if no line is there, dsd_import skips the first line so it will not import the first channel or first group in those files if there is something there that isn't a label.
|
||||||
|
|
||||||
|
Hytera XPT: Code has been added for XPT system slco/flco/csbk decoding and trunking. The setup will be similar to Capacity Plus trunking in the csv file, listing each LSN channel to a frequency (see examples/hytera_xpt_chan.csv). Currently, this set up is working with smaller XPT systems that I have been able to test with.
|
||||||
|
|
||||||
|
## NCurses Keyboard Shortcuts ##
|
||||||
|
|
||||||
|
Some Keyboard Shortcuts have been implemented for testing to see how users like them. Just hope nobody pushes a key on accident and doesn't know which one it was or what it does. The current list of keyboard shortcuts include:
|
||||||
|
|
||||||
|
```
|
||||||
|
esc or arrow keys - ncurses menu
|
||||||
|
q - quit
|
||||||
|
c - toggle compact mode
|
||||||
|
h - toggle call history
|
||||||
|
z - toggle console payloads
|
||||||
|
a - toggle call alert beep
|
||||||
|
4 - force privacy/scrambler key assertion over enc identifiers (dmr and nxdn)
|
||||||
|
6 - force rc4 key assertion over missing pi header/le enc identifiers (dmr)
|
||||||
|
i - toggle signal inversion on types that can't auto detect (dmr, dpmr)
|
||||||
|
m - toggle c4fm/qpsk 10/4 (everything but phase 2 signal)
|
||||||
|
M - toggle c4fm/qpsk 8/3 (phase 2 tdma control channel)
|
||||||
|
R - start capturing symbol capture bin (date/time name file)
|
||||||
|
r - stop capturing symbol capture bin
|
||||||
|
spacebar - replay last symbol capture bin (captures must be stopped first)
|
||||||
|
s - stop playing symbol capture bin or wav input file
|
||||||
|
P - start per call decoded wav files (Capital P)
|
||||||
|
p - stop per call decoded wav files (Lower p)
|
||||||
|
t - toggle trunking (needs either rtl input, or rigctl connection)
|
||||||
|
y - toggle scanner (needs either rtl input, or rigctl connection)
|
||||||
|
1 - Lockout Tuning/Playback of TG in Slot 1 or Conventional -- Current Session Only if no group.csv file specified
|
||||||
|
2 - Lockout Tuning/Playback of TG in Slot 2 -- Current Session Only if no group.csv file specified
|
||||||
|
0 - Toggle Audio Smoothing - May produce crackling if enabled on RTL/TCP or wav/bin files
|
||||||
|
w - Toggle Trunking/Playback White List (Allow A Groups Only) / Black List (Block B or DE groups only) Mode
|
||||||
|
g - Toggle Trunking Tuning to Group Calls (DMR T3, Con+, Cap+, P25, NXDN)
|
||||||
|
u - Toggle Trunking Tuning to Private Calls (DMR T3, Cap+, P25)
|
||||||
|
d - Toggle Trunking Tuning to Data Calls (DMR T3, NXDN)
|
||||||
|
e - Toggle Trunking Tuning to Encrypted Calls (P25)
|
||||||
|
8 - Connect to SDR++ TCP Audio Sink with Defaults/Retry Sink Connection on Disconnect
|
||||||
|
9 - Connect to SDR++ RIGCTL Server with Defaults/Retry RIGCTL Connection
|
||||||
|
D - Reset DMR Site Parms/Call Strings, etc.
|
||||||
|
Z - Simulate NoCarrier/No VC/CC sync (capital Z)
|
||||||
|
```
|
||||||
164
examples/Install_Notes.md
Normal file
164
examples/Install_Notes.md
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
## How to clone 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 23.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/main/download-and-install.sh
|
||||||
|
chmod +x download-and-install.sh
|
||||||
|
./download-and-install.sh
|
||||||
|
```
|
||||||
|
f
|
||||||
|
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/main/download-and-install-nodeps.sh
|
||||||
|
chmod +x download-and-install-nodeps.sh
|
||||||
|
./download-and-install-nodeps.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
Debian/Mint/Ubuntu/Pi
|
||||||
|
|
||||||
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
Fedora 36/37 -- from https://github.com/lwvmobile/dsd-fme/issues/99
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo dnf install libsndfile-devel fftw-devel lapack-devel rtl-sdr-devel pulseaudio-libs-devel libusb-devel cmake git ncurses ncurses-devel gcc wget pavucontrol gcc-c++
|
||||||
|
```
|
||||||
|
## Headless Ubuntu Server/Pi
|
||||||
|
|
||||||
|
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.2 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 future compatibility issues.
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/lwvmobile/mbelib
|
||||||
|
cd mbelib
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j `nproc`
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
cd ..
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Some environment/distro users (Cygwin, Arch, and others) may need to manually copy any libmbe files found in the build folder to the /usr/lib folder on root.
|
||||||
|
`sudo cp libmbe* /usr/lib`
|
||||||
|
|
||||||
|
Finish by running these steps to clone and build DSD-FME.
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/lwvmobile/dsd-fme
|
||||||
|
cd dsd-fme
|
||||||
|
sudo cp tone8.wav /usr/share/
|
||||||
|
sudo cp tone24.wav /usr/share/
|
||||||
|
sudo cp tone48.wav /usr/share/
|
||||||
|
sudo chmod 777 /usr/share/tone8.wav
|
||||||
|
sudo chmod 777 /usr/share/tone24.wav
|
||||||
|
sudo chmod 777 /usr/share/tone48.wav
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j `nproc`
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows Cygwin Builds
|
||||||
|
|
||||||
|
If using RTL input support, you must install libusb-win32 before proceeding with Cygwin installation. You will also need to build and compile a version of librtlsdr, but this can be tricky, and personally, I had to resort to using a much older version of the source code to get it to build at all with the rtl support in the precompiled 'Aero' builds.
|
||||||
|
|
||||||
|
Then install all dependencies available in the repo prior to building, either directly selecting them during setup with the setup-x86.exe or setup-x64.exe or by issuing a command similar to this:
|
||||||
|
|
||||||
|
```
|
||||||
|
setup-x64.exe --packages libpulse-devel,libpulse-mainloop-glib0,libpulse-simple0,libpulse0,pulseaudio,pulseaudio-debuginfo,pulseaudio-equalizer,pulseaudio-module-x11,pulseaudio-module-zeroconf,pulseaudio-utils,sox-fmt-pulseaudio,xfce-pulseaudio-plugin,libusb0,libusb1.0,libusb1.0-debuginfo,libusb1.0-devel,libncurses++w10,libncurses-devel,libncursesw10,ncurses,cmake,gcc-core,gcc-debuginfo,gcc-objc,git,make,socat,sox,sox-fmt-ao,unzip,wget,gcc-g++,libsndfile-devel
|
||||||
|
```
|
||||||
|
|
||||||
|
Then manually install ITPP and MBElib, see above install notes. Be sure to run `cp libmbe* /usr/lib` after compiling MBElib.
|
||||||
|
|
||||||
|
Then you can build and install using
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/lwvmobile/dsd-fme
|
||||||
|
cd dsd-fme
|
||||||
|
cp tone8.wav /usr/share/
|
||||||
|
cp tone24.wav /usr/share/
|
||||||
|
cp tone48.wav /usr/share/
|
||||||
|
chmod 777 /usr/share/tone8.wav
|
||||||
|
chmod 777 /usr/share/tone24.wav
|
||||||
|
chmod 777 /usr/share/tone48.wav
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
# -DAERO=ON is optional, but recommended if you want the older OSS support as well
|
||||||
|
cmake -DAERO=ON ..
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Virtual Sinks
|
||||||
|
|
||||||
|
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. Note: This setup is completely optional if using TCP Network Sink Audio and/or RTL Input.
|
||||||
|
|
||||||
|
```
|
||||||
|
pactl load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink
|
||||||
|
pactl 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
|
||||||
|
##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 cp tone48.wav /usr/share/
|
||||||
|
sudo chmod 777 /usr/share/tone8.wav
|
||||||
|
sudo chmod 777 /usr/share/tone24.wav
|
||||||
|
sudo chmod 777 /usr/share/tone48.wav
|
||||||
|
```
|
||||||
10
examples/capacity_plus_chan.csv
Normal file
10
examples/capacity_plus_chan.csv
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
LSN(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||||
|
1,858606250
|
||||||
|
2,858606250
|
||||||
|
3,859606250
|
||||||
|
4,859606250
|
||||||
|
5,860606250
|
||||||
|
6,860606250
|
||||||
|
7,861606250
|
||||||
|
8,861606250
|
||||||
|
|
||||||
|
19
examples/connect_plus_chan.csv
Normal file
19
examples/connect_plus_chan.csv
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||||
|
999,862093750,default cc (come here first), optional use non-existent channel as first entry
|
||||||
|
1,863093750
|
||||||
|
2,862093750
|
||||||
|
3,861093750
|
||||||
|
4,860093750
|
||||||
|
5,859093750
|
||||||
|
6,863593750
|
||||||
|
7,862593750
|
||||||
|
8,861593750
|
||||||
|
9,860593750
|
||||||
|
10,859593750
|
||||||
|
11,863393750
|
||||||
|
12,862393750
|
||||||
|
13,861393750
|
||||||
|
14,860393750
|
||||||
|
15,859393750
|
||||||
|
|
||||||
|
|
||||||
|
8
examples/dmr_t3_chan.csv
Normal file
8
examples/dmr_t3_chan.csv
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||||
|
999,456318750,default cc (come here first), optional use non-existent channel as first entry
|
||||||
|
36,455756250
|
||||||
|
54,455981250
|
||||||
|
81,456318750
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
6
examples/edacs_channel_map.csv
Normal file
6
examples/edacs_channel_map.csv
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
LSN(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||||
|
1,851375000
|
||||||
|
2,851800000
|
||||||
|
3,855987500
|
||||||
|
4,858487500
|
||||||
|
5,852312500
|
||||||
|
5
examples/group.csv
Normal file
5
examples/group.csv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
DEC,Mode(A- Allow, B - Block, DE - Digital Enc),Name of Group,Tag (do not delete this line or won't import properly)
|
||||||
|
100,B,Example Name,Tag
|
||||||
|
1449,A,Fire Dispatch,Fire
|
||||||
|
929,A,Fire Tac,Fire
|
||||||
|
22033,DE,Law Dispatch,Law
|
||||||
|
10
examples/hytera_xpt_chan.csv
Normal file
10
examples/hytera_xpt_chan.csv
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
LSN(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||||
|
1,858606250
|
||||||
|
2,858606250
|
||||||
|
3,859606250
|
||||||
|
4,859606250
|
||||||
|
5,860606250
|
||||||
|
6,860606250
|
||||||
|
7,861606250
|
||||||
|
8,861606250
|
||||||
|
|
||||||
|
6
examples/multi_key.csv
Normal file
6
examples/multi_key.csv
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
key id or tg id (dec), key number or value (dec) //NXDN Scrambler, BP, 10-char Hytera BP only
|
||||||
|
2,70
|
||||||
|
1,123
|
||||||
|
100,70
|
||||||
|
12,48713912656
|
||||||
|
672560,254
|
||||||
|
5
examples/multi_key_hex.csv
Normal file
5
examples/multi_key_hex.csv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
key id(hex), key value (hex) //10-char Hytera BP, RC4
|
||||||
|
1,1234567890
|
||||||
|
2,ABCDEF1234
|
||||||
|
C,0B57935150
|
||||||
|
64,0B57935150
|
||||||
|
8
examples/nxdn_chan_map.csv
Normal file
8
examples/nxdn_chan_map.csv
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
channel(dec), freq(Hz)
|
||||||
|
141,423862500
|
||||||
|
142,424337500
|
||||||
|
143,424912500
|
||||||
|
203,424100000
|
||||||
|
204,424625000
|
||||||
|
271,422650000
|
||||||
|
302,423287500
|
||||||
|
5
examples/nxdn_sc_key.csv
Normal file
5
examples/nxdn_sc_key.csv
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
key(dec) or tg(dec), value(dec), load any single keys as key 0
|
||||||
|
0,23466
|
||||||
|
1,12345
|
||||||
|
2,32700
|
||||||
|
63,32767
|
||||||
|
45
examples/trunking.sh
Normal file
45
examples/trunking.sh
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#example trunking sh scripts (SDR++ with RIGCTL and RTL Input)
|
||||||
|
#make sure to put a # in front of all lines except the one you wish to use
|
||||||
|
|
||||||
|
#P25 P1 FDMA CC with C4FM
|
||||||
|
#dsd-fme -i tcp -U 4532 -T -N 2> log.ans
|
||||||
|
#dsd-fme -i rtl:0:850M:0:-2:12 -T -N 2> log.ans
|
||||||
|
|
||||||
|
#P25 P1 FDMA CC with QPSK (Simulcast)
|
||||||
|
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -mq -N 2> log.ans
|
||||||
|
#dsd-fme -i rtl:0:855.625M:0:-2:12 -T -mq -N 2> log.ans
|
||||||
|
|
||||||
|
#P25 P2 TDMA CC with QPSK (Simulcast)
|
||||||
|
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -f2 -m2 -N 2> log.ans
|
||||||
|
#dsd-fme -i rtl:0:855.625M:0:-2:12 -T -f2 -m2 -N 2> log.ans
|
||||||
|
|
||||||
|
#DMR TIII, Con+, Cap+, XPT
|
||||||
|
dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans
|
||||||
|
#dsd-fme -fs -i rtl:0:450M:44:-2:8 -T -C connect_plus_chan.csv -G group.csv -N 2> log.ans
|
||||||
|
|
||||||
|
#EDACS/EDACS-EA Digital Only
|
||||||
|
#dsd-fme -fp -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||||
|
#dsd-fme -fp -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||||
|
|
||||||
|
#NXDN48 Type-C or Type-D Trunking with Channel Map
|
||||||
|
#dsd-fme -fi -i tcp -T -U 4532 -C nxdn_channel_map.csv -N 2> log.ans
|
||||||
|
#dsd-fme -fi -i rtl:0:855.625M:0:-2:12:100 -T -C nxdn_channel_map.csv -N 2> log.ans
|
||||||
|
|
||||||
|
#NXDN48 Type-C Trunking with DFA
|
||||||
|
#dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans
|
||||||
|
#dsd-fme -fi -i rtl:0:855.625M:0:-2:12:100 -T -N 2> log.ans
|
||||||
|
|
||||||
|
#NXDN96 Trunking with Channel Map
|
||||||
|
#dsd-fme -fn -i tcp -T -U 4532 -C nxdn_channel_map.csv -N 2> log.ans
|
||||||
|
#dsd-fme -fn -i rtl -T -C nxdn_channel_map.csv -N 2> log.ans
|
||||||
|
|
||||||
|
#Notes: parameters after -i tcp can be omitted if using localhost as the default hostname to connect
|
||||||
|
#and 7355 as the default port number. -U is the port number for RIGCTL and will mirror the tcp hostname
|
||||||
|
#running under the assumption that the user will be connnecting the VFO to the same instance of SDR++
|
||||||
|
#You can also specify an ip address to connect to a remote SDR++ instance, make sure that the settings
|
||||||
|
#inside of SDR++ match, i.e., change in SDR++ from 'localhost' to an IP to bind to (machine's IP address)
|
||||||
|
|
||||||
|
#Note2: RTL input should only be used on strong signal, the SDR++ VFO will perform much better on marginal signal
|
||||||
|
#paramters after -i rtl can also be omitted now if using the default configurations
|
||||||
|
|
||||||
|
#Having a -G group.csv is purely optional, but useful if you want to block groups, or give groups names
|
||||||
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,19 +15,25 @@
|
|||||||
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
* NXDN AMBE interleave schedule
|
//#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];
|
||||||
|
|
||||||
#else
|
//#else
|
||||||
|
/*
|
||||||
const int nW[36] = { 0, 1, 0, 1, 0, 1,
|
* dPMR AMBE interleave schedule
|
||||||
|
*/
|
||||||
|
// bit 1
|
||||||
|
const int dPmrW[36] = {
|
||||||
|
0, 1, 0, 1, 0, 1,
|
||||||
0, 1, 0, 1, 0, 1,
|
0, 1, 0, 1, 0, 1,
|
||||||
0, 1, 0, 1, 0, 1,
|
0, 1, 0, 1, 0, 1,
|
||||||
0, 1, 0, 1, 0, 2,
|
0, 1, 0, 1, 0, 2,
|
||||||
@@ -35,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,
|
||||||
@@ -43,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,
|
||||||
@@ -51,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,
|
||||||
@@ -60,3 +70,7 @@ const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//#ifdef __cplusplus
|
||||||
|
//}
|
||||||
|
//#endif
|
||||||
1145
include/dsd.h
Normal file
1145
include/dsd.h
Normal file
File diff suppressed because it is too large
Load Diff
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[];
|
||||||
82
include/nxdn_const.h
Normal file
82
include/nxdn_const.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// Portions Below from Osmocom OP25
|
||||||
|
// NXDN Encoder (C) Copyright 2019 Max H. Parke KA1RBI
|
||||||
|
// thx gr-ysf fr_vch_decoder_bb_impl.cc * Copyright 2015 Mathias Weyland *
|
||||||
|
// OP25 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, or (at your option)
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// OP25 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.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with OP25; see the file COPYING. If not, write to the Free
|
||||||
|
// Software Foundation, Inc., 51 Franklin Street, Boston, MA
|
||||||
|
// 02110-1301, USA.
|
||||||
|
|
||||||
|
static const uint8_t PERM_12_5[] = {
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t PERM_16_9[] = {
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint16_t PERM_12_25[] = {
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint16_t PERM_12_29[] = {
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
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
|
||||||
12
install.sh
Normal file
12
install.sh
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
sudo cp tone8.wav /usr/share/
|
||||||
|
sudo cp tone24.wav /usr/share/
|
||||||
|
sudo cp tone48.wav /usr/share/
|
||||||
|
sudo chmod 777 /usr/share/tone8.wav
|
||||||
|
sudo chmod 777 /usr/share/tone24.wav
|
||||||
|
sudo chmod 777 /usr/share/tone48.wav
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j `nproc`
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
124
nxdn96.c
124
nxdn96.c
@@ -1,124 +0,0 @@
|
|||||||
#include "dsd.h"
|
|
||||||
#include "nxdn96_const.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
processNXDN96 (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
int i, j, k, dibit;
|
|
||||||
|
|
||||||
char ambe_fr[4][24];
|
|
||||||
const int *w, *x, *y, *z;
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("VOICE e:");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf ("\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (k = 0; k < 4; k++)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 222; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf ("%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (k < 3)
|
|
||||||
{
|
|
||||||
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] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (j = 0; j < 3; j++) // we skip the last voice frame until frame sync can work with < 24 symbols
|
|
||||||
{
|
|
||||||
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] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
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 (k < 3)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 18; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf ("%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (i = 0; i < 30; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf ("%c", dibit + 48);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef NXDN_DUMP
|
|
||||||
printf ("\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
162
p25_lcw.c
162
p25_lcw.c
@@ -1,162 +0,0 @@
|
|||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo)
|
|
||||||
{
|
|
||||||
|
|
||||||
char tgid[17], tmpstr[255];
|
|
||||||
long talkgroup, source;
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
tgid[16] = 0;
|
|
||||||
|
|
||||||
if (opts->p25lc == 1)
|
|
||||||
{
|
|
||||||
printf ("lcformat: %s mfid: %s lcinfo: %s ", lcformat, mfid, lcinfo);
|
|
||||||
if (opts->p25tg == 0)
|
|
||||||
{
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp (lcformat, "00000100") == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
// first tg is the active channel
|
|
||||||
if (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
// update tg buffer
|
|
||||||
j = 0;
|
|
||||||
for (i = 40; i < 52; i++)
|
|
||||||
{
|
|
||||||
state->tg[state->tgcount][j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[12] = 48;
|
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
|
||||||
tmpstr[15] = 48;
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
state->tgcount = state->tgcount + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// first one again for tg display
|
|
||||||
j = 0;
|
|
||||||
for (i = 40; i < 52; i++)
|
|
||||||
{
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[12] = 48;
|
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
|
||||||
tmpstr[15] = 48;
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
|
||||||
state->lasttg = talkgroup;
|
|
||||||
if (opts->p25tg == 1)
|
|
||||||
{
|
|
||||||
printf ("tg: %li ", talkgroup);
|
|
||||||
|
|
||||||
// the remaining 3 appear to be other active tg's on the system
|
|
||||||
j = 0;
|
|
||||||
for (i = 28; i < 40; i++)
|
|
||||||
{
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[12] = 48;
|
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
|
||||||
tmpstr[15] = 48;
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
|
||||||
printf ("%li ", talkgroup);
|
|
||||||
j = 0;
|
|
||||||
for (i = 16; i < 28; i++)
|
|
||||||
{
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[12] = 48;
|
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
|
||||||
tmpstr[15] = 48;
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
|
||||||
printf ("%li ", talkgroup);
|
|
||||||
j = 0;
|
|
||||||
for (i = 4; i < 16; i++)
|
|
||||||
{
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[12] = 48;
|
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
|
||||||
tmpstr[15] = 48;
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
|
||||||
printf ("%li\n", talkgroup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (strcmp (lcformat, "00000000") == 0)
|
|
||||||
{
|
|
||||||
j = 0;
|
|
||||||
if (strcmp (mfid, "10010000") == 0)
|
|
||||||
{
|
|
||||||
for (i = 20; i < 32; i++)
|
|
||||||
{
|
|
||||||
if (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
state->tg[state->tgcount][j] = lcinfo[i];
|
|
||||||
}
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[12] = 48;
|
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
|
||||||
tmpstr[15] = 48;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (i = 16; i < 32; i++)
|
|
||||||
{
|
|
||||||
if (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
state->tg[state->tgcount][j] = lcinfo[i];
|
|
||||||
}
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
|
||||||
state->lasttg = talkgroup;
|
|
||||||
if (opts->p25tg == 1)
|
|
||||||
{
|
|
||||||
printf ("tg: %li ", talkgroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
j = 0;
|
|
||||||
for (i = 32; i < 56; i++)
|
|
||||||
{
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[24] = 0;
|
|
||||||
source = strtol (tmpstr, NULL, 2);
|
|
||||||
state->lastsrc = source;
|
|
||||||
if (opts->p25tg == 1)
|
|
||||||
{
|
|
||||||
printf ("src: %li emr: %c\n", source, lcinfo[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((opts->p25tg == 1) && (opts->p25lc == 1))
|
|
||||||
{
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
312
p25p1_hdu.c
312
p25p1_hdu.c
@@ -1,312 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
processHDU (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
|
|
||||||
char mi[73], mfid[9], algid[9], kid[17], tgid[17], tmpstr[255];
|
|
||||||
int dibit, count, i, j;
|
|
||||||
long talkgroup;
|
|
||||||
int algidhex, kidhex;
|
|
||||||
|
|
||||||
mi[72] = 0;
|
|
||||||
mfid[8] = 0;
|
|
||||||
algid[8] = 0;
|
|
||||||
kid[16] = 0;
|
|
||||||
tgid[16] = 0;
|
|
||||||
|
|
||||||
skipDibit (opts, state, 25);
|
|
||||||
count = 57;
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[9] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[11] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 7);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[13] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[15] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[16] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[17] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[18] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[19] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[20] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[21] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[22] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[23] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[24] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[25] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[26] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[27] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[28] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[29] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[30] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[31] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[32] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[33] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[34] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[35] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 7);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[36] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[37] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[38] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[39] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[40] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[41] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[42] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[43] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[44] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[45] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[46] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[47] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[48] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[49] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[50] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[51] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[52] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[53] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[54] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[55] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[56] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[57] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[58] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[59] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 7);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[60] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[61] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[62] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[63] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[64] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[65] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[66] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[67] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[68] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[69] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[70] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[71] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mfid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mfid[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mfid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mfid[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mfid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mfid[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mfid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mfid[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
algid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
algid[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 1);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
algid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
algid[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
algid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
algid[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
algid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
algid[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[9] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[11] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[13] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[15] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 1);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
tgid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
tgid[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
tgid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
tgid[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
tgid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
tgid[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
tgid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
tgid[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
tgid[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
tgid[9] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 6);
|
|
||||||
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
tgid[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
tgid[11] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
tgid[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
tgid[13] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
tgid[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
tgid[15] = (1 & dibit) + 48; // bit 0
|
|
||||||
|
|
||||||
skipDibit (opts, state, 160);
|
|
||||||
|
|
||||||
if (opts->p25enc == 1)
|
|
||||||
{
|
|
||||||
algidhex = strtol (algid, NULL, 2);
|
|
||||||
kidhex = strtol (kid, NULL, 2);
|
|
||||||
printf ("mi: %s algid: $%x kid: $%x\n", mi, algidhex, kidhex);
|
|
||||||
}
|
|
||||||
if (opts->p25lc == 1)
|
|
||||||
{
|
|
||||||
printf ("mfid: %s tgid: %s ", mfid, tgid);
|
|
||||||
if (opts->p25tg == 0)
|
|
||||||
{
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
j = 0;
|
|
||||||
if (strcmp (mfid, "10010000") == 0)
|
|
||||||
{
|
|
||||||
for (i = 4; i < 16; i++)
|
|
||||||
{
|
|
||||||
if (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
state->tg[state->tgcount][j] = tgid[i];
|
|
||||||
}
|
|
||||||
tmpstr[j] = tgid[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[12] = 48;
|
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
|
||||||
tmpstr[15] = 48;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
if (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
state->tg[state->tgcount][j] = tgid[i];
|
|
||||||
}
|
|
||||||
tmpstr[j] = tgid[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
|
||||||
state->lasttg = talkgroup;
|
|
||||||
if (opts->p25tg == 1)
|
|
||||||
{
|
|
||||||
printf ("tg: %li\n", talkgroup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
295
p25p1_ldu1.c
295
p25p1_ldu1.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"
|
|
||||||
#include "p25p1_const.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
processLDU1 (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
// extracts IMBE frames rom LDU frame
|
|
||||||
int i, j, k, dibit, stats, count, scount;
|
|
||||||
char imbe_fr[8][23];
|
|
||||||
char lcformat[9], mfid[9], lcinfo[57], lsd1[9], lsd2[9], status[25];
|
|
||||||
const int *w, *x, *y, *z;
|
|
||||||
|
|
||||||
lcformat[8] = 0;
|
|
||||||
mfid[8] = 0;
|
|
||||||
lcinfo[56] = 0;
|
|
||||||
lsd1[8] = 0;
|
|
||||||
lsd2[8] = 0;
|
|
||||||
status[24] = 0;
|
|
||||||
|
|
||||||
skipDibit (opts, state, 3);
|
|
||||||
status[0] = getDibit (opts, state) + 48;
|
|
||||||
skipDibit (opts, state, 21);
|
|
||||||
count = 57;
|
|
||||||
scount = 1;
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("e:");
|
|
||||||
}
|
|
||||||
|
|
||||||
// separate imbe frames and deinterleave
|
|
||||||
stats = 21; // we skip the status dibits that occur every 36 symbols
|
|
||||||
// the first IMBE frame starts 14 symbols before next status
|
|
||||||
// so we start counter at 22
|
|
||||||
for (i = 0; i < 9; i++)
|
|
||||||
{ // 9 IMBE frames per LDU
|
|
||||||
w = iW;
|
|
||||||
x = iX;
|
|
||||||
y = iY;
|
|
||||||
z = iZ;
|
|
||||||
for (j = 0; j < 72; j++)
|
|
||||||
{
|
|
||||||
if (stats == 35)
|
|
||||||
{
|
|
||||||
status[scount] = getDibit (opts, state) + 48;
|
|
||||||
scount++;
|
|
||||||
stats = 1;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stats++;
|
|
||||||
}
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
count++;
|
|
||||||
imbe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
imbe_fr[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
processMbeFrame (opts, state, imbe_fr, NULL, NULL);
|
|
||||||
|
|
||||||
// skip over non imbe data sometimes between frames
|
|
||||||
if ((i < 4) || (i == 8))
|
|
||||||
{
|
|
||||||
k = 0;
|
|
||||||
}
|
|
||||||
else if (i == 7)
|
|
||||||
{
|
|
||||||
//k=16;
|
|
||||||
k = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
k = 20;
|
|
||||||
}
|
|
||||||
for (j = 0; j < k; j++)
|
|
||||||
{
|
|
||||||
if (stats == 35)
|
|
||||||
{
|
|
||||||
status[scount] = getDibit (opts, state) + 48;
|
|
||||||
scount++;
|
|
||||||
count++;
|
|
||||||
stats = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stats++;
|
|
||||||
}
|
|
||||||
skipDibit (opts, state, 1);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 1)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcformat[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcformat[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcformat[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcformat[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcformat[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcformat[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcformat[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcformat[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mfid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mfid[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mfid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mfid[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
status[scount] = getDibit (opts, state) + 48;
|
|
||||||
scount++;
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mfid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mfid[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mfid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mfid[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
stats = 10;
|
|
||||||
}
|
|
||||||
else if (i == 2)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[9] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[11] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[13] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[15] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[16] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[17] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[18] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[19] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[20] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[21] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[22] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[23] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[24] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[25] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[26] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[27] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[28] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[29] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[30] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[31] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
stats = 32;
|
|
||||||
}
|
|
||||||
else if (i == 3)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[32] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[33] = (1 & dibit) + 48; // bit 0
|
|
||||||
status[scount] = getDibit (opts, state) + 48;
|
|
||||||
scount++;
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[34] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[35] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[36] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[37] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[38] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[39] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[40] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[41] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[42] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[43] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[44] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[45] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[46] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[47] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[48] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[49] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[50] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[51] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[52] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[53] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lcinfo[54] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lcinfo[55] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
stats = 19;
|
|
||||||
}
|
|
||||||
else if (i == 7)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd1[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd1[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd1[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd1[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd1[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd1[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd1[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd1[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 4);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd2[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd2[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd2[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd2[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd2[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd2[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd2[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd2[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 4);
|
|
||||||
stats = 33;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// trailing status symbol
|
|
||||||
status[scount] = getDibit (opts, state) + 48;
|
|
||||||
scount++;
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->p25status == 1)
|
|
||||||
{
|
|
||||||
printf ("status: %s lsd1: %s lsd2: %s\n", status, lsd1, lsd2);
|
|
||||||
}
|
|
||||||
|
|
||||||
processP25lcw (opts, state, lcformat, mfid, lcinfo);
|
|
||||||
}
|
|
||||||
352
p25p1_ldu2.c
352
p25p1_ldu2.c
@@ -1,352 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
#include "p25p1_const.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
processLDU2 (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
// extracts IMBE frames rom LDU frame
|
|
||||||
int i, j, k, dibit, stats, count, scount;
|
|
||||||
char imbe_fr[8][23];
|
|
||||||
char mi[73], algid[9], kid[17], lsd3[9], lsd4[9], status[25];
|
|
||||||
const int *w, *x, *y, *z;
|
|
||||||
int algidhex, kidhex;
|
|
||||||
|
|
||||||
status[24] = 0;
|
|
||||||
mi[72] = 0;
|
|
||||||
algid[8] = 0;
|
|
||||||
kid[16] = 0;
|
|
||||||
lsd3[8] = 0;
|
|
||||||
lsd4[8] = 0;
|
|
||||||
|
|
||||||
skipDibit (opts, state, 3);
|
|
||||||
status[0] = getDibit (opts, state) + 48;
|
|
||||||
skipDibit (opts, state, 21);
|
|
||||||
scount = 1;
|
|
||||||
|
|
||||||
count = 57;
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("e:");
|
|
||||||
}
|
|
||||||
|
|
||||||
// separate imbe frames and deinterleave
|
|
||||||
stats = 21;
|
|
||||||
// we skip the status dibits that occur every 36 symbols
|
|
||||||
// the first IMBE frame starts 14 symbols before next status
|
|
||||||
// so we start counter at 22
|
|
||||||
for (i = 0; i < 9; i++)
|
|
||||||
{ // 9 IMBE frames per LDU
|
|
||||||
w = iW;
|
|
||||||
x = iX;
|
|
||||||
y = iY;
|
|
||||||
z = iZ;
|
|
||||||
for (j = 0; j < 72; j++)
|
|
||||||
{
|
|
||||||
if (stats == 35)
|
|
||||||
{
|
|
||||||
status[scount] = getDibit (opts, state) + 48;
|
|
||||||
scount++;
|
|
||||||
stats = 1;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stats++;
|
|
||||||
}
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
count++;
|
|
||||||
imbe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
|
||||||
imbe_fr[*y][*z] = (1 & dibit); // bit 0
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
y++;
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
processMbeFrame (opts, state, imbe_fr, NULL, NULL);
|
|
||||||
|
|
||||||
// skip over non imbe data sometimes between frames
|
|
||||||
if ((i < 5) || (i == 8))
|
|
||||||
{
|
|
||||||
k = 0;
|
|
||||||
}
|
|
||||||
else if (i == 7)
|
|
||||||
{
|
|
||||||
//k=16;
|
|
||||||
k = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
k = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < k; j++)
|
|
||||||
{
|
|
||||||
if (stats == 35)
|
|
||||||
{
|
|
||||||
status[scount] = getDibit (opts, state) + 48;
|
|
||||||
scount++;
|
|
||||||
count++;
|
|
||||||
stats = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stats++;
|
|
||||||
}
|
|
||||||
skipDibit (opts, state, 1);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 1)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[9] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[11] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
status[scount] = getDibit (opts, state) + 48;
|
|
||||||
scount++;
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[13] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[15] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[16] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[17] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[18] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[19] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[20] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[21] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[22] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[23] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
stats = 10;
|
|
||||||
}
|
|
||||||
else if (i == 2)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[24] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[25] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[26] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[27] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[28] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[29] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[30] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[31] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[32] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[33] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[34] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[35] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[36] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[37] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[38] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[39] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[40] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[41] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[42] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[43] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[44] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[45] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[46] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[47] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
stats = 32;
|
|
||||||
}
|
|
||||||
else if (i == 3)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[48] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[49] = (1 & dibit) + 48; // bit 0
|
|
||||||
status[scount] = getDibit (opts, state) + 48;
|
|
||||||
scount++;
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[50] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[51] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[52] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[53] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[54] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[55] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[56] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[57] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[58] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[59] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[60] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[61] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[62] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[63] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[64] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[65] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[66] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[67] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[68] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[69] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
mi[70] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
mi[71] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
stats = 19;
|
|
||||||
}
|
|
||||||
else if (i == 4)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
algid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
algid[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
algid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
algid[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
algid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
algid[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
algid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
algid[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[9] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 1);
|
|
||||||
status[scount] = getDibit (opts, state) + 48;
|
|
||||||
scount++;
|
|
||||||
skipDibit (opts, state, 1);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[11] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[13] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
kid[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
kid[15] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 2);
|
|
||||||
stats = 6;
|
|
||||||
}
|
|
||||||
else if (i == 7)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd3[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd3[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd3[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd3[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd3[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd3[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd3[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd3[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 4);
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd4[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd4[1] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd4[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd4[3] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd4[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd4[5] = (1 & dibit) + 48; // bit 0
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
lsd4[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
|
||||||
lsd4[7] = (1 & dibit) + 48; // bit 0
|
|
||||||
skipDibit (opts, state, 4);
|
|
||||||
stats = 33;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//trailing status symbol
|
|
||||||
status[scount] = getDibit (opts, state) + 48;
|
|
||||||
scount++;
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->p25status == 1)
|
|
||||||
{
|
|
||||||
printf ("status: %s lsd3: %s lsd4: %s\n", status, lsd3, lsd4);
|
|
||||||
}
|
|
||||||
if (opts->p25enc == 1)
|
|
||||||
{
|
|
||||||
algidhex = strtol (algid, NULL, 2);
|
|
||||||
kidhex = strtol (kid, NULL, 2);
|
|
||||||
printf ("mi: %s algid: $%x kid: $%x\n", mi, algidhex, kidhex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
519
provoice.c
519
provoice.c
@@ -1,519 +0,0 @@
|
|||||||
#include "dsd.h"
|
|
||||||
#include "provoice_const.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
processProVoice (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
|
||||||
int i, j, dibit;
|
|
||||||
|
|
||||||
char imbe7100_fr1[7][24];
|
|
||||||
char imbe7100_fr2[7][24];
|
|
||||||
const int *w, *x;
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("VOICE e:");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 64; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// lid
|
|
||||||
for (i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = 0; i < 64; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// imbe frames 1,2 first half
|
|
||||||
w = pW;
|
|
||||||
x = pX;
|
|
||||||
|
|
||||||
for (i = 0; i < 11; i++)
|
|
||||||
{
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// spacer bits
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// imbe frames 1,2 second half
|
|
||||||
|
|
||||||
for (j = 0; j < 2; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
w -= 5;
|
|
||||||
x -= 5;
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = 0; i < 7; i++)
|
|
||||||
{
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
w -= 5;
|
|
||||||
x -= 5;
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, NULL, imbe7100_fr1);
|
|
||||||
processMbeFrame (opts, state, NULL, NULL, imbe7100_fr2);
|
|
||||||
|
|
||||||
// spacer bits
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// imbe frames 3,4 first half
|
|
||||||
w = pW;
|
|
||||||
x = pX;
|
|
||||||
for (i = 0; i < 11; i++)
|
|
||||||
{
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// spacer bits
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// imbe frames 3,4 second half
|
|
||||||
for (j = 0; j < 2; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
for (i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
w -= 5;
|
|
||||||
x -= 5;
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = 0; i < 7; i++)
|
|
||||||
{
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
w -= 6;
|
|
||||||
x -= 6;
|
|
||||||
for (j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr1[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("_");
|
|
||||||
#endif
|
|
||||||
w -= 5;
|
|
||||||
x -= 5;
|
|
||||||
for (j = 0; j < 5; j++)
|
|
||||||
{
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
imbe7100_fr2[*w][*x] = dibit;
|
|
||||||
w++;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, NULL, imbe7100_fr1);
|
|
||||||
processMbeFrame (opts, state, NULL, NULL, imbe7100_fr2);
|
|
||||||
|
|
||||||
// spacer bits
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
#endif
|
|
||||||
dibit = getDibit (opts, state);
|
|
||||||
#ifdef PROVOICE_DUMP
|
|
||||||
printf ("%i", dibit);
|
|
||||||
printf (" ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
16
rebuild.sh
Normal file
16
rebuild.sh
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#! /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
|
||||||
|
sleep 2
|
||||||
|
##cd into your build folder##
|
||||||
|
cd build
|
||||||
|
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];
|
||||||
|
}
|
||||||
582
src/bptc.c
Normal file
582
src/bptc.c
Normal file
@@ -0,0 +1,582 @@
|
|||||||
|
/*
|
||||||
|
============================================================================
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t DeInterleaveReverseChannelBptcPlacement[32] =
|
||||||
|
{
|
||||||
|
0, 16, 1, 17, 2, 18, 3, 19,
|
||||||
|
4, 20, 5, 21, 6, 22, 7, 23,
|
||||||
|
8, 24, 9, 25, 10, 26, 11, 27,
|
||||||
|
12, 28, 13, 29, 14, 30, 15, 31
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* R(0) to R(2) may be used to transport some
|
||||||
|
* Restricted Access System (RAS) information,
|
||||||
|
* So save these three bits after hamming correction
|
||||||
|
* 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) */
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
*
|
||||||
|
* @param ParityCheckTypeOdd : 0 = Parity to check is even (used in Non-Reverse Channel Single Burst BPTC)
|
||||||
|
* 1 = Parity to check is odd (used in Reverse Channel Single Burst BPTC)
|
||||||
|
*
|
||||||
|
* @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 ParityCheckTypeOdd)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t DataMatrix[32];
|
||||||
|
uint8_t LineUncorrected[16];
|
||||||
|
uint8_t LineCorrected[11];
|
||||||
|
uint32_t HammingIrrecoverableErrorNb = 0;
|
||||||
|
uint32_t ParityCheckOddErrorNb = 0;
|
||||||
|
uint32_t ParityCheckEvenErrorNb = 0;
|
||||||
|
|
||||||
|
//TODO: make this so we can load either rc interleave, or single burst interleave
|
||||||
|
for(i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
DataMatrix[DeInterleaveReverseChannelBptcPlacement[DeInterleaveReverseChannelBptc[i]]] = InputInterleavedData[i] & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the entire input buffer to the output buffer */
|
||||||
|
for(i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
DMRDataExtracted[i] = DataMatrix[i] & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare the line to be corrected */
|
||||||
|
for(i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
LineUncorrected[i] = DataMatrix[i] & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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] & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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] & 1) == (DMRDataExtracted[i + 16] & 1)) ParityCheckOddErrorNb++;
|
||||||
|
if((DMRDataExtracted[i] & 1) != (DMRDataExtracted[i + 16] & 1)) ParityCheckEvenErrorNb++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//fprintf(stderr, "Hamming ERR=%u ; Parity odd ERR=%u, Parity even ERR=%u, ", HammingIrrecoverableErrorNb, ParityCheckOddErrorNb, ParityCheckEvenErrorNb);
|
||||||
|
|
||||||
|
//fprintf(stderr, "Content =\n");
|
||||||
|
//for(i = 0; i < 32; i++)
|
||||||
|
//{
|
||||||
|
// fprintf(stderr, "%u", DMRDataExtracted[i] & 1);
|
||||||
|
// if((i == 15) || (i == 31)) fprintf(stderr, "\n");
|
||||||
|
// else fprintf(stderr, "-");
|
||||||
|
//}
|
||||||
|
|
||||||
|
/* Return the number of irrecoverable Hamming errors +
|
||||||
|
* the number of parity check error */
|
||||||
|
if(ParityCheckTypeOdd) return (HammingIrrecoverableErrorNb + ParityCheckOddErrorNb);
|
||||||
|
else return (HammingIrrecoverableErrorNb + ParityCheckEvenErrorNb);
|
||||||
|
|
||||||
|
} /* End BPTC_16x2_Extract_Data() */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* End of file */
|
||||||
784
src/dmr_block.c
Normal file
784
src/dmr_block.c
Normal file
@@ -0,0 +1,784 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_block.c
|
||||||
|
* DMR Data Header and Data Block Assembly/Handling
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
//hopefully a more simplified (or logical) version...once you get past all the variables
|
||||||
|
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t dheader_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t i, j, k;
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
|
||||||
|
//clear out unified pdu 'superframe' slot
|
||||||
|
for (short int i = 0; i < 288; i++) state->dmr_pdu_sf[slot][i] = 0;
|
||||||
|
|
||||||
|
//reset block counter to 1
|
||||||
|
state->data_block_counter[slot] = 1;
|
||||||
|
|
||||||
|
if (IrrecoverableErrors == 0 && CRCCorrect == 1) //&&CRCCorrect == 1
|
||||||
|
{
|
||||||
|
|
||||||
|
uint8_t gi = dheader_bits[0]; //group or individual data
|
||||||
|
uint8_t a = dheader_bits[1]; //response requested flag
|
||||||
|
uint8_t ab = (uint8_t)ConvertBitIntoBytes(&dheader_bits[2], 2); //appended blocks
|
||||||
|
uint8_t dpf = (uint8_t)ConvertBitIntoBytes(&dheader_bits[4], 4); //data packet format
|
||||||
|
uint8_t sap = (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 4); //service access point
|
||||||
|
uint8_t poc = (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 4); //padding octets
|
||||||
|
|
||||||
|
uint32_t target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[16], 24); //destination llid
|
||||||
|
uint32_t source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[40], 24); //source llid
|
||||||
|
|
||||||
|
//extra tgt/src handling for XPT
|
||||||
|
uint8_t target_hash[24];
|
||||||
|
uint8_t tg_hash = 0;
|
||||||
|
uint8_t is_xpt = 0;
|
||||||
|
//set flag if XPT
|
||||||
|
if (strcmp (state->dmr_branding_sub, "XPT ") == 0) is_xpt = 1;
|
||||||
|
|
||||||
|
//truncate tgt/src to 16-bit values if XPT
|
||||||
|
if (is_xpt == 1)
|
||||||
|
{
|
||||||
|
target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[24], 16); //destination llid
|
||||||
|
source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 16); //source llid
|
||||||
|
if (gi == 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 16; i++) target_hash[i] = dheader_bits[24+i];
|
||||||
|
tg_hash = crc8 (target_hash, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t is_cap = 0;
|
||||||
|
if (strcmp (state->dmr_branding_sub, "Cap+ ") == 0) is_cap = 1;
|
||||||
|
|
||||||
|
if (is_cap)
|
||||||
|
{
|
||||||
|
//truncate tg on group? or just on private/individual data?
|
||||||
|
if (gi == 0) target = (uint32_t)ConvertBitIntoBytes(&dheader_bits[24], 16);
|
||||||
|
source = (uint32_t)ConvertBitIntoBytes(&dheader_bits[48], 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
//store source for dmr pdu packet handling (lrrp) when not available in completed message
|
||||||
|
if (dpf != 15) state->dmr_lrrp_source[slot] = source;
|
||||||
|
|
||||||
|
//set dpf to storage for later use (UDT, SD, etc)
|
||||||
|
state->data_header_format[slot] = dpf;
|
||||||
|
|
||||||
|
//Type Strings
|
||||||
|
char sap_string[20];
|
||||||
|
char mfid_string[20];
|
||||||
|
char udtf_string[20];
|
||||||
|
char sddd_string[20];
|
||||||
|
sprintf (sap_string, "%s", "");
|
||||||
|
sprintf (mfid_string, "%s", "");
|
||||||
|
sprintf (udtf_string, "%s", "");
|
||||||
|
sprintf (sddd_string, "%s", "");
|
||||||
|
|
||||||
|
//see 9.3 - ETSI TS 102 361-1 V2.5.1 (2017-10) for more info
|
||||||
|
uint8_t f = dheader_bits[64]; //F -- Full message flag (F)
|
||||||
|
uint8_t bf = (uint8_t)ConvertBitIntoBytes(&dheader_bits[65], 7); //Blocks to Follow (BF)
|
||||||
|
|
||||||
|
//confirmed data header
|
||||||
|
uint8_t s = dheader_bits[72]; //S -- Re-Synchronize Flag
|
||||||
|
uint8_t ns = (uint8_t)ConvertBitIntoBytes(&dheader_bits[73], 3); //N(S) -- send sequence number
|
||||||
|
uint8_t fsn = (uint8_t)ConvertBitIntoBytes(&dheader_bits[76], 4); //Fragment Sequence Number (FSN)
|
||||||
|
|
||||||
|
//response header
|
||||||
|
uint8_t r_class = (uint8_t)ConvertBitIntoBytes(&dheader_bits[72], 2);
|
||||||
|
uint8_t r_type = (uint8_t)ConvertBitIntoBytes(&dheader_bits[74], 3);
|
||||||
|
uint8_t r_status = (uint8_t)ConvertBitIntoBytes(&dheader_bits[77], 3);
|
||||||
|
|
||||||
|
//short data - status/precoded
|
||||||
|
uint8_t s_ab_msb = (uint8_t)ConvertBitIntoBytes(&dheader_bits[2], 2); //appended block, msb
|
||||||
|
uint8_t s_ab_lsb = (uint8_t)ConvertBitIntoBytes(&dheader_bits[12], 4);//appended block, lsb
|
||||||
|
uint8_t s_ab_fin = (s_ab_msb << 2) | s_ab_lsb; //appended blocks, final value
|
||||||
|
uint8_t s_source_port = (uint8_t)ConvertBitIntoBytes(&dheader_bits[64], 3);
|
||||||
|
uint8_t s_dest_port = (uint8_t)ConvertBitIntoBytes(&dheader_bits[67], 3);
|
||||||
|
uint8_t s_status_precoded = (uint8_t)ConvertBitIntoBytes(&dheader_bits[70], 10);
|
||||||
|
|
||||||
|
//short data - raw
|
||||||
|
uint8_t sd_sarq = dheader_bits[70]; //Selective Automatic Repeat reQuest
|
||||||
|
uint8_t sd_f = dheader_bits[71]; //full message flag
|
||||||
|
uint8_t sd_bp = (uint8_t)ConvertBitIntoBytes(&dheader_bits[72], 8); //bit padding
|
||||||
|
|
||||||
|
//short data - defined
|
||||||
|
uint8_t dd_format = (uint8_t)ConvertBitIntoBytes(&dheader_bits[64], 6);
|
||||||
|
|
||||||
|
//Unified Data Transport (UDT)
|
||||||
|
uint8_t udt_format = (uint8_t)ConvertBitIntoBytes(&dheader_bits[12], 4);
|
||||||
|
uint8_t udt_padnib = (uint8_t)ConvertBitIntoBytes(&dheader_bits[64], 5);
|
||||||
|
uint8_t udt_uab = (uint8_t)ConvertBitIntoBytes(&dheader_bits[70], 2); //udt appended blocks
|
||||||
|
uint8_t udt_sf = dheader_bits[72];
|
||||||
|
uint8_t udt_pf = dheader_bits[73];
|
||||||
|
uint8_t udt_op = (uint8_t)ConvertBitIntoBytes(&dheader_bits[74], 6);
|
||||||
|
|
||||||
|
//p_head
|
||||||
|
uint8_t p_sap = (uint8_t)ConvertBitIntoBytes(&dheader_bits[0], 4);
|
||||||
|
uint8_t p_mfid = (uint8_t)ConvertBitIntoBytes(&dheader_bits[8], 8);
|
||||||
|
uint8_t p_pot = (uint8_t)ConvertBitIntoBytes(&dheader_bits[3], 5); //looking for LRRP pot_report
|
||||||
|
|
||||||
|
fprintf (stderr, "%s ", KGRN);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " Slot %d Data Header - ", slot+1);
|
||||||
|
|
||||||
|
if (gi == 1 && dpf != 15) fprintf(stderr, "Group - ");
|
||||||
|
if (gi == 0 && dpf != 15) fprintf(stderr, "Indiv - ");
|
||||||
|
|
||||||
|
if (dpf == 0) fprintf (stderr, "Unified Data Transport (UDT) ");
|
||||||
|
else if (dpf == 1) fprintf (stderr, "Response Packet ");
|
||||||
|
else if (dpf == 2) fprintf (stderr, "Unconfirmed Delivery ");
|
||||||
|
else if (dpf == 3) fprintf (stderr, "Confirmed Delivery ");
|
||||||
|
else if (dpf == 13) fprintf (stderr, "Short Data: Defined ");
|
||||||
|
else if (dpf == 14) fprintf (stderr, "Short Data: Raw or S/P ");
|
||||||
|
else if (dpf == 15) fprintf (stderr, "Proprietary Packet Data");
|
||||||
|
else fprintf (stderr, "Reserved/Unknown DPF %X ", dpf);
|
||||||
|
|
||||||
|
if (a == 1 && dpf != 15) fprintf(stderr, "- Response Requested ");
|
||||||
|
if (dpf != 15) fprintf (stderr, "- Source: %d Target: %d ", source, target);
|
||||||
|
|
||||||
|
//include the hash value if this is an XPT and if its IND data
|
||||||
|
if (dpf != 15 && is_xpt == 1 && gi == 0) fprintf (stderr, "Hash: %d ", tg_hash);
|
||||||
|
|
||||||
|
//sap string handling
|
||||||
|
if (dpf == 15) sap = p_sap;
|
||||||
|
|
||||||
|
if (sap == 0) sprintf (sap_string, "%s", "UDT Data"); //apparently, both dpf and sap for UDT is 0
|
||||||
|
else if (sap == 2) sprintf (sap_string, "%s", "TCP Comp"); //TCP/IP header compression
|
||||||
|
else if (sap == 3) sprintf (sap_string, "%s", "UDP Comp"); //UDP/IP header compression
|
||||||
|
else if (sap == 4) sprintf (sap_string, "%s", "IP Based"); //IP based Packet Data
|
||||||
|
else if (sap == 5) sprintf (sap_string, "%s", "ARP Prot"); //Address Resoution Protocol (ARP)
|
||||||
|
else if (sap == 9) sprintf (sap_string, "%s", "Prop PDU"); //Proprietary Packet Data
|
||||||
|
else if (sap == 10) sprintf (sap_string, "%s", "Short DT"); //Short Data
|
||||||
|
else sprintf (sap_string, "%s", "Reserved"); //reserved, or err/unk
|
||||||
|
|
||||||
|
//mfid string handling
|
||||||
|
if (dpf == 15)
|
||||||
|
{
|
||||||
|
if (p_mfid == 0x10) sprintf (mfid_string, "%s", "Motorola");
|
||||||
|
else if (p_mfid == 0x58) sprintf (mfid_string, "%s", "Tait");
|
||||||
|
else if (p_mfid == 0x68) sprintf (mfid_string, "%s", "Hytera");
|
||||||
|
else if (p_mfid == 0x08) sprintf (mfid_string, "%s", "Hytera");
|
||||||
|
else if (p_mfid == 0x06) sprintf (mfid_string, "%s", "Trid/Mot");
|
||||||
|
else if (p_mfid == 0x00) sprintf (mfid_string, "%s", "Standard");
|
||||||
|
else sprintf (mfid_string, "%s", "Other");
|
||||||
|
}
|
||||||
|
|
||||||
|
//udt format string handling
|
||||||
|
if (dpf == 0)
|
||||||
|
{
|
||||||
|
if (udt_format == 0x00) sprintf (udtf_string, "%s", "Binary");
|
||||||
|
else if (udt_format == 0x01) sprintf (udtf_string, "%s", "MS/TG Adr");
|
||||||
|
else if (udt_format == 0x02) sprintf (udtf_string, "%s", "4-bit BCD");
|
||||||
|
else if (udt_format == 0x03) sprintf (udtf_string, "%s", "ISO7 Char");
|
||||||
|
else if (udt_format == 0x04) sprintf (udtf_string, "%s", "ISO8 Char");
|
||||||
|
else if (udt_format == 0x05) sprintf (udtf_string, "%s", "NMEA LOCN");
|
||||||
|
else if (udt_format == 0x06) sprintf (udtf_string, "%s", "IP Addr");
|
||||||
|
else if (udt_format == 0x07) sprintf (udtf_string, "%s", "UTF-16"); //16-bit Unicode Chars
|
||||||
|
else if (udt_format == 0x08) sprintf (udtf_string, "%s", "Manu Spec"); //Manufacturer Specific
|
||||||
|
else if (udt_format == 0x09) sprintf (udtf_string, "%s", "Manu Spec"); //Manufacturer Specific
|
||||||
|
else if (udt_format == 0x0A) sprintf (udtf_string, "%s", "Mixed UTF"); //Appended block contains addr and 16-bit UTF-16BE
|
||||||
|
else sprintf (udtf_string, "%s", "Reserved");
|
||||||
|
}
|
||||||
|
|
||||||
|
//short data dd_head format string
|
||||||
|
if (dpf == 13)
|
||||||
|
{
|
||||||
|
if (dd_format == 0x00) sprintf (sddd_string, "%s", "Binary");
|
||||||
|
else if (dd_format == 0x01) sprintf (sddd_string, "%s", "BCD ");
|
||||||
|
else if (dd_format == 0x02) sprintf (sddd_string, "%s", "7-bit Char");
|
||||||
|
else if (dd_format == 0x03) sprintf (sddd_string, "%s", "IEC 8859-1");
|
||||||
|
else if (dd_format == 0x04) sprintf (sddd_string, "%s", "IEC 8859-2");
|
||||||
|
else if (dd_format == 0x05) sprintf (sddd_string, "%s", "IEC 8859-3");
|
||||||
|
else if (dd_format == 0x06) sprintf (sddd_string, "%s", "IEC 8859-4");
|
||||||
|
else if (dd_format == 0x07) sprintf (sddd_string, "%s", "IEC 8859-5");
|
||||||
|
else if (dd_format == 0x08) sprintf (sddd_string, "%s", "IEC 8859-6");
|
||||||
|
else if (dd_format == 0x09) sprintf (sddd_string, "%s", "IEC 8859-7");
|
||||||
|
else if (dd_format == 0x0A) sprintf (sddd_string, "%s", "IEC 8859-8");
|
||||||
|
else if (dd_format == 0x0B) sprintf (sddd_string, "%s", "IEC 8859-9");
|
||||||
|
else if (dd_format == 0x0C) sprintf (sddd_string, "%s", "IEC 8859-10");
|
||||||
|
else if (dd_format == 0x0D) sprintf (sddd_string, "%s", "IEC 8859-11");
|
||||||
|
else if (dd_format == 0x0E) sprintf (sddd_string, "%s", "IEC 8859-13"); //there is no 8059-12
|
||||||
|
else if (dd_format == 0x0F) sprintf (sddd_string, "%s", "IEC 8859-14");
|
||||||
|
else if (dd_format == 0x10) sprintf (sddd_string, "%s", "IEC 8859-15");
|
||||||
|
else if (dd_format == 0x11) sprintf (sddd_string, "%s", "IEC 8859-16");
|
||||||
|
else if (dd_format == 0x12) sprintf (sddd_string, "%s", "UTF-8 ");
|
||||||
|
else if (dd_format == 0x13) sprintf (sddd_string, "%s", "UTF-16 ");
|
||||||
|
else if (dd_format == 0x14) sprintf (sddd_string, "%s", "UTF-16BE");
|
||||||
|
else if (dd_format == 0x15) sprintf (sddd_string, "%s", "UTF-16LE");
|
||||||
|
else if (dd_format == 0x16) sprintf (sddd_string, "%s", "UTF-32 ");
|
||||||
|
else if (dd_format == 0x17) sprintf (sddd_string, "%s", "UTF-32BE");
|
||||||
|
else if (dd_format == 0x18) sprintf (sddd_string, "%s", "UTF-32LE");
|
||||||
|
else sprintf (sddd_string, "%s", "Reserved");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dpf == 0) //UDT
|
||||||
|
{
|
||||||
|
//UDT packet info -- samples needed for testing
|
||||||
|
//NOTE: This format's completed message has a CRC16 - like MBC - but has number of blocks (appended blocks) like R 1/2, etc.
|
||||||
|
fprintf (stderr, "\n SAP %02X [%s] - FMT %d [%s] - PDn %d - BLOCKS %d SF %d - PF %d OP %02X", sap, sap_string, udt_format, udtf_string, udt_padnib, udt_uab, udt_sf, udt_pf, udt_op);
|
||||||
|
|
||||||
|
//set number of blocks to follow (appended blocks) for block assembler
|
||||||
|
state->data_header_blocks[slot] = udt_uab;
|
||||||
|
|
||||||
|
//set data header to valid
|
||||||
|
state->data_header_valid[slot] = 1;
|
||||||
|
|
||||||
|
//send to assembler as type 3, rearrange into CSBK type PDU (to verify), and send to dmr_cspdu
|
||||||
|
dmr_block_assembler (opts, state, dheader, 12, 0x0B, 3);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dpf == 1) //response data packet header
|
||||||
|
{
|
||||||
|
//mostly fleshed out response packet info
|
||||||
|
fprintf (stderr, "\n SAP %02X [%s] - Class %d - Type %0d - ", sap, sap_string, r_class, r_type);
|
||||||
|
if (r_class == 0 && r_type == 1) fprintf (stderr, "ACK - Success");
|
||||||
|
if (r_class == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "NACK - ");
|
||||||
|
if (r_type == 0) fprintf (stderr, "Illegal Format");
|
||||||
|
if (r_type == 1) fprintf (stderr, "Illegal Format");
|
||||||
|
if (r_type == 2) fprintf (stderr, "Packet CRC ERR");
|
||||||
|
if (r_type == 3) fprintf (stderr, "Memory Full");
|
||||||
|
if (r_type == 4) fprintf (stderr, "FSN Out of Seq");
|
||||||
|
if (r_type == 5) fprintf (stderr, "Undeliverable");
|
||||||
|
if (r_type == 6) fprintf (stderr, "PKT Out of Seq");
|
||||||
|
if (r_type == 7) fprintf (stderr, "Invalid User");
|
||||||
|
}
|
||||||
|
if (r_class == 2) fprintf (stderr, "SACK - Retry");
|
||||||
|
if (r_status) fprintf (stderr, " - %d", r_status); //the object/value of the ack/nack/sack
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Confirmed or Unconfirmed Data Packets Header
|
||||||
|
if (dpf == 2 || dpf == 3)
|
||||||
|
{
|
||||||
|
if (dpf == 2) fprintf (stderr, "\n SAP %02X [%s] - FMF %d - BLOCKS %02d - PAD %02d - FSN %d", sap, sap_string, f, bf, poc, fsn);
|
||||||
|
if (dpf == 3) fprintf (stderr, "\n SAP %02X [%s] - FMF %d - BLOCKS %02d - PAD %02d - S %d - NS %d - FSN %d", sap, sap_string, f, bf, poc, s, ns, fsn);
|
||||||
|
state->data_header_blocks[slot] = bf;
|
||||||
|
if (dpf == 3) state->data_conf_data[slot] = 1; //set confirmed data delivery flag for additional CRC checks, block assembly, etc.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Short Data DD_Head (13), and R_Head or SP_Head (14)
|
||||||
|
if (dpf == 13 || dpf == 14)
|
||||||
|
{
|
||||||
|
//only set if not all zeroes
|
||||||
|
if (s_ab_fin) state->data_header_blocks[slot] = s_ab_fin;
|
||||||
|
|
||||||
|
//Short Data: Defined
|
||||||
|
if (dpf == 13) fprintf (stderr, "\n SD:D [DD_HEAD] - SAP %02d [%s] - BLOCKS %02d - DD %02X - PADb %d - FMT %02X [%s]", sap, sap_string, s_ab_fin, dd_format, sd_bp, dd_format, sddd_string);
|
||||||
|
//Short Data: Raw or S/P
|
||||||
|
if (dpf == 14)
|
||||||
|
{
|
||||||
|
//S/P has all appended block bits set to zero -- any other way to tell difference?
|
||||||
|
if (s_ab_fin == 0) fprintf (stderr, "\n SD:S/P [SP_HEAD] - SAP %02d [%s] - SP %02d - DP %02d - S/P %02X", sap, sap_string, s_source_port, s_dest_port, s_status_precoded);
|
||||||
|
|
||||||
|
//Raw
|
||||||
|
else fprintf (stderr, "\n SD:RAW [R_HEAD] - SAP %02d [%s] - BLOCKS %02d - SP %02d - DP %02d - SARQ %d - FMF %d - PDb %d", sap, sap_string, s_ab_fin, s_source_port, s_dest_port, sd_sarq, sd_f, sd_bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
//6.2.2 The Response Requested (A) information element of the header shall be set to 0
|
||||||
|
//for unconfirmed data and shall be set to 1 for confirmed data. DD_HEAD, R_HEAD, or SP_HEAD (double check)
|
||||||
|
if (a == 1)
|
||||||
|
{
|
||||||
|
state->data_conf_data[slot] = 1;
|
||||||
|
fprintf (stderr, " - Confirmed Data");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Proprietary Data Header
|
||||||
|
if (dpf == 15)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " - P_SAP %02d [%s] - MFID %02X [%s]", p_sap, sap_string, p_mfid, mfid_string);
|
||||||
|
|
||||||
|
//look for LRRP potential report 0x1F (may be others as well)
|
||||||
|
//this could just be a sap == 1, which is 'reserved', unsure (or maybe no actual correlation?)
|
||||||
|
if (p_sap != 1) state->data_header_blocks[slot]--; //decrement the blocks to follow count in order to pass CRC32 and properly assemble blocks
|
||||||
|
else //add block to start of the dmr_pdu_sf since it probably contains vital LRRP data (0x1F report) -- won't pass CRC32 either way
|
||||||
|
{
|
||||||
|
int blocks = state->data_header_blocks[slot]-1;
|
||||||
|
if (blocks < 1) blocks = 4; //safety value
|
||||||
|
if (blocks > 9) blocks = 9;
|
||||||
|
for(i = 0; i < 12; i++) state->dmr_pdu_sf[slot][i+(blocks*12)] = dheader[i];
|
||||||
|
state->data_block_counter[slot]++;
|
||||||
|
//no matter how you assemble this, with or without the p_head block(s), there is ALWAYS a CRC32 Failure
|
||||||
|
//even when it continues to pass on other SAP values, SAP 1 must have a different checksum?
|
||||||
|
//Users trying to get this will want to enable -F to bypass the CRC32 failure
|
||||||
|
|
||||||
|
//a recent discovery on a few systems and samples show the existence of multiple Prop Head Packets (how does that work)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//block storage sanity
|
||||||
|
if (state->data_header_blocks[slot] > 9) state->data_header_blocks[slot] = 9;
|
||||||
|
//assuming we didn't receive the initial data header block on a p_head and then decremented it
|
||||||
|
//3 or 4 seems to be the average safe value
|
||||||
|
if (state->data_header_blocks[slot] < 1) state->data_header_blocks[slot] = 4;
|
||||||
|
//set data header validity unless its a p_head (should be set prior, if received)
|
||||||
|
if (dpf != 15) state->data_header_valid[slot] = 1;
|
||||||
|
|
||||||
|
//REMUS! Uncomment these lines if desired
|
||||||
|
if (dpf != 15 && dpf != 1)
|
||||||
|
{
|
||||||
|
sprintf (state->dmr_lrrp_gps[slot], "Data Call - %s SRC [%d] DST [%d] ", sap_string, source, target);
|
||||||
|
if (a == 1) strcat (state->dmr_lrrp_gps[slot], "- RSP REQ ");
|
||||||
|
}
|
||||||
|
|
||||||
|
//REMUS! Uncomment these lines if desired -- if the data request data header is missing, though, this may keep accumulating
|
||||||
|
// if (dpf == 1)
|
||||||
|
// {
|
||||||
|
// strcat (state->dmr_lrrp_gps[slot], "- RSP ");
|
||||||
|
// if (r_class == 0) strcat (state->dmr_lrrp_gps[slot], "ACK - RX OK");
|
||||||
|
// if (r_class == 1) strcat (state->dmr_lrrp_gps[slot], "NACK - RX ERR");
|
||||||
|
// if (r_class == 2) strcat (state->dmr_lrrp_gps[slot], "SACK - RETRY");
|
||||||
|
|
||||||
|
// }
|
||||||
|
//End REMUS Data string
|
||||||
|
|
||||||
|
//store SAP value
|
||||||
|
state->data_header_sap[slot] = sap;
|
||||||
|
|
||||||
|
} //End Irrecoverable Errors
|
||||||
|
|
||||||
|
if (IrrecoverableErrors != 0)
|
||||||
|
{
|
||||||
|
state->data_header_valid[slot] = 0;
|
||||||
|
sprintf (state->dmr_lrrp_gps[slot], "%s", "");
|
||||||
|
state->data_p_head[slot] = 0;
|
||||||
|
state->data_conf_data[slot] = 0;
|
||||||
|
state->data_block_counter[slot] = 1;
|
||||||
|
state->data_header_blocks[slot] = 1;
|
||||||
|
state->data_header_format[slot] = 7;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//convert UDT to a CSBK friendly PDU format
|
||||||
|
void dmr_udt_cspdu_converter (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
//rearrange UDT data to a CSBK message format
|
||||||
|
uint8_t cs_byte[48]; //12 x 4, 2 bit AB should mean only 3 total appended blocks, so 4 all together (minus rearrangement)
|
||||||
|
memset (cs_byte, 0, sizeof (cs_byte));
|
||||||
|
uint8_t cs_bits[48*8];
|
||||||
|
memset (cs_bits, 0, sizeof (cs_bits));
|
||||||
|
|
||||||
|
cs_byte[0] = (uint8_t)ConvertBitIntoBytes(&block_bits[73], 7); //lb, pf and opcode byte
|
||||||
|
uint8_t udt_opcode = (uint8_t)ConvertBitIntoBytes(&block_bits[74], 6); //for observation testing
|
||||||
|
cs_byte[1] = 0; //set mfid to zero for now (may use custom value if needed later)
|
||||||
|
uint8_t udt_mfid = 0; //setting to zero
|
||||||
|
|
||||||
|
//should be 34 total (maximum) after removing tailing CRC16 (initial and last block)
|
||||||
|
for (i = 0; i < 34; i++) cs_byte[2+i] = block_bytes[12+i];
|
||||||
|
|
||||||
|
//bytes to bits
|
||||||
|
for(i = 0, j = 0; i < 36; i++, j+=8) //4 blocks max?
|
||||||
|
{
|
||||||
|
cs_bits[j + 0] = (cs_byte[i] >> 7) & 0x01;
|
||||||
|
cs_bits[j + 1] = (cs_byte[i] >> 6) & 0x01;
|
||||||
|
cs_bits[j + 2] = (cs_byte[i] >> 5) & 0x01;
|
||||||
|
cs_bits[j + 3] = (cs_byte[i] >> 4) & 0x01;
|
||||||
|
cs_bits[j + 4] = (cs_byte[i] >> 3) & 0x01;
|
||||||
|
cs_bits[j + 5] = (cs_byte[i] >> 2) & 0x01;
|
||||||
|
cs_bits[j + 6] = (cs_byte[i] >> 1) & 0x01;
|
||||||
|
cs_bits[j + 7] = (cs_byte[i] >> 0) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
dmr_cspdu (opts, state, cs_bits, cs_byte, CRCCorrect, IrrecoverableErrors);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//assemble the blocks as they come in, shuffle them into the unified dmr_pdu_sf
|
||||||
|
void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type)
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
uint8_t lb = 0; //mbc last block
|
||||||
|
uint8_t pf = 0; //mbc protect flag
|
||||||
|
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
int blocks;
|
||||||
|
uint8_t blockcounter = state->data_block_counter[slot];
|
||||||
|
uint8_t block_num = state->data_header_blocks[slot];
|
||||||
|
|
||||||
|
uint32_t CRCCorrect = 0;
|
||||||
|
uint32_t CRCComputed = 0;
|
||||||
|
uint32_t CRCExtracted = 0;
|
||||||
|
uint32_t IrrecoverableErrors = 0;
|
||||||
|
|
||||||
|
uint8_t dmr_pdu_sf_bits[8*24*50]; //give plenty of space so we don't go oob
|
||||||
|
|
||||||
|
//MBC Header and Block CRC
|
||||||
|
uint8_t mbc_crc_good[2]; //header and blocks crc pass/fail local storage
|
||||||
|
memset (mbc_crc_good, 0, sizeof (mbc_crc_good)); //init on 0 - bad crc
|
||||||
|
|
||||||
|
if (type == 1) blocks = state->data_header_blocks[slot] - 1;
|
||||||
|
if (type == 2) blocks = state->data_block_counter[slot];
|
||||||
|
|
||||||
|
//UDT Header and Block Check
|
||||||
|
uint8_t is_udt = 0;
|
||||||
|
if (type == 3)
|
||||||
|
{
|
||||||
|
blocks = state->data_block_counter[slot];
|
||||||
|
is_udt = 1;
|
||||||
|
type = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
//sanity check, setting block_len and block values to sane numbers in case of missing header, else could overload array (crash) or print out
|
||||||
|
if (blocks < 1) blocks = 1; //changed blocks from uint8_t to int
|
||||||
|
if (blocks > 9) blocks = 9;
|
||||||
|
if (block_len == 0) block_len = 18;
|
||||||
|
if (block_len > 24) block_len = 24;
|
||||||
|
|
||||||
|
if (type == 1)
|
||||||
|
{
|
||||||
|
//type 1 data block, shuffle method
|
||||||
|
for (j = 0; j < blocks; j++)
|
||||||
|
{
|
||||||
|
for (i = 0; i < block_len; i++)
|
||||||
|
{
|
||||||
|
state->dmr_pdu_sf[slot][i+block_len*j] = state->dmr_pdu_sf[slot][i+block_len*(j+1)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < block_len; i++)
|
||||||
|
{
|
||||||
|
state->dmr_pdu_sf[slot][i+(blocks*block_len)] = block_bytes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
//time to send the completed 'superframe' to the DMR PDU message handler
|
||||||
|
if (state->data_block_counter[slot] == state->data_header_blocks[slot] && state->data_header_valid[slot] == 1)
|
||||||
|
{
|
||||||
|
//CRC32 on completed messages
|
||||||
|
for(i = 0, j = 0; i < (block_num * block_len); i++, j+=8)
|
||||||
|
{
|
||||||
|
dmr_pdu_sf_bits[j + 0] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 1] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 2] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 3] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 4] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 5] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 6] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 7] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//sanity check to prevent a negative or zero block_num or sending a negative value into the bit array
|
||||||
|
if (block_num < 1 || block_num == 0) block_num = 1;
|
||||||
|
|
||||||
|
CRCExtracted = 0;
|
||||||
|
//extract crc from last block, apply to completed 'superframe' minus header
|
||||||
|
for(i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
CRCExtracted = CRCExtracted << 1;
|
||||||
|
CRCExtracted = CRCExtracted | (uint32_t)(dmr_pdu_sf_bits[i + (block_len * 8) * block_num - 32] & 1); //96
|
||||||
|
}
|
||||||
|
|
||||||
|
//rearrage for ridiculously stupid CRC32 LSO/MSO ordering
|
||||||
|
for(i = 0, j = 0; i < (block_num * block_len); i+=2, j+=16)
|
||||||
|
{
|
||||||
|
dmr_pdu_sf_bits[j + 0] = (state->dmr_pdu_sf[slot][i+1] >> 7) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 1] = (state->dmr_pdu_sf[slot][i+1] >> 6) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 2] = (state->dmr_pdu_sf[slot][i+1] >> 5) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 3] = (state->dmr_pdu_sf[slot][i+1] >> 4) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 4] = (state->dmr_pdu_sf[slot][i+1] >> 3) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 5] = (state->dmr_pdu_sf[slot][i+1] >> 2) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 6] = (state->dmr_pdu_sf[slot][i+1] >> 1) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 7] = (state->dmr_pdu_sf[slot][i+1] >> 0) & 0x01;
|
||||||
|
|
||||||
|
dmr_pdu_sf_bits[j + 8] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 9] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 10] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 11] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 12] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 13] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 14] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 15] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
|
||||||
|
|
||||||
|
//increment extra 2 bytes on each nth byte (depends on data type 1/2, 3/4, 1) if conf data
|
||||||
|
if ( i == (block_len - 1) )
|
||||||
|
{
|
||||||
|
if (state->data_conf_data[slot] == 0) i+=2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//confirmed working now!
|
||||||
|
CRCComputed = (uint32_t)ComputeCrc32Bit(dmr_pdu_sf_bits, ( block_num * block_len * 8) - 32);
|
||||||
|
|
||||||
|
//if the CRC32 is correct, I think its fair to assume we don't need to worry about if the
|
||||||
|
//individual CRC9s are correct on confirmed data blocks (but we can confirm now that they are all good)
|
||||||
|
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
||||||
|
|
||||||
|
//only run on good CRC or CRC/RAS bypass (aggressive_framesync == 0)
|
||||||
|
if (CRCCorrect || opts->aggressive_framesync == 0)
|
||||||
|
dmr_pdu (opts, state, block_len, state->dmr_pdu_sf[slot]);
|
||||||
|
|
||||||
|
if (CRCCorrect) ; //print nothing
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, "\n Slot %d - Multi Block PDU Message CRC32 ERR", slot+1);
|
||||||
|
|
||||||
|
//debug print
|
||||||
|
// fprintf (stderr, " %X - %X", CRCExtracted, CRCComputed);
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Full Super Frame Type 1 - Payload Output
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf (stderr, "\n Slot %d - Multi Block PDU Message\n ", slot+1);
|
||||||
|
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", state->dmr_pdu_sf[slot][i]);
|
||||||
|
if (i == 11 || i == 23 || i == 35 || i == 47 || i == 59 || i == 71 || i == 83 || i == 95)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug print
|
||||||
|
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed);
|
||||||
|
|
||||||
|
if (state->data_header_format[slot] == 13 || state->data_header_format[slot] == 14 || state->data_header_sap[slot] == 10)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, "\n Short Data: ASCII \n" );
|
||||||
|
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||||
|
{
|
||||||
|
if (state->dmr_pdu_sf[slot][i] <= 0x7E && state->dmr_pdu_sf[slot][i] >=0x20)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%c", state->dmr_pdu_sf[slot][i]);
|
||||||
|
}
|
||||||
|
else fprintf (stderr, " ");
|
||||||
|
|
||||||
|
if (i == 47 || i == 95) fprintf (stderr, "\n ");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
//reset data header format storage
|
||||||
|
state->data_header_format[slot] = 7;
|
||||||
|
//flag off data header validity
|
||||||
|
state->data_header_valid[slot] = 0;
|
||||||
|
//flag off conf data flag
|
||||||
|
state->data_conf_data[slot] = 0;
|
||||||
|
|
||||||
|
} //end completed sf
|
||||||
|
|
||||||
|
} //end type 1 blocks
|
||||||
|
|
||||||
|
|
||||||
|
//type 2 - MBC and UDT header, MBC and UDT continuation blocks
|
||||||
|
if (type == 2)
|
||||||
|
{
|
||||||
|
//sanity check (marginal signal, bad decodes, etc) -- may go a little lower (find out max number of MBC blocks supported)
|
||||||
|
if (state->data_block_counter[slot] > 4) state->data_block_counter[slot] = 4;
|
||||||
|
|
||||||
|
//Type 2 data block, additive method
|
||||||
|
for (i = 0; i < block_len; i++)
|
||||||
|
{
|
||||||
|
state->dmr_pdu_sf[slot][i+(blockcounter*block_len)] = block_bytes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
memset (dmr_pdu_sf_bits, 0, sizeof(dmr_pdu_sf_bits));
|
||||||
|
|
||||||
|
lb = block_bytes[0] >> 7; //last block flag
|
||||||
|
pf = (block_bytes[0] >> 6) & 1;
|
||||||
|
|
||||||
|
if (is_udt)
|
||||||
|
{
|
||||||
|
blocks = state->data_header_blocks[slot];
|
||||||
|
if (blocks == blockcounter) lb = 1; //double check
|
||||||
|
pf = 0; //flag off is set from bit in error
|
||||||
|
}
|
||||||
|
|
||||||
|
//last block arrived and we have a valid data header, time to send to cspdu decoder
|
||||||
|
if (lb == 1 && state->data_header_valid[slot] == 1)
|
||||||
|
{
|
||||||
|
for(i = 0, j = 0; i < 12*4; i++, j+=8) //4 blocks max?
|
||||||
|
{
|
||||||
|
dmr_pdu_sf_bits[j + 0] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 1] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 2] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 3] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 4] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 5] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 6] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
|
||||||
|
dmr_pdu_sf_bits[j + 7] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check UDT PF
|
||||||
|
if (is_udt) pf = dmr_pdu_sf_bits[73];
|
||||||
|
|
||||||
|
//CRC check on Header and full frames as appropriate (header crc already stored)
|
||||||
|
//The 16 bit CRC in the header shall include the data carried by the header.
|
||||||
|
//The 16 bit CRC in the last block shall be performed on
|
||||||
|
//all MBC blocks (conmbined) except the header block.
|
||||||
|
|
||||||
|
mbc_crc_good[0] = state->data_block_crc_valid[slot][0];
|
||||||
|
|
||||||
|
CRCExtracted = 0;
|
||||||
|
//extract crc from last block, apply to completed 'superframe' minus header
|
||||||
|
for(i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
CRCExtracted = CRCExtracted << 1;
|
||||||
|
CRCExtracted = CRCExtracted | (uint32_t)(dmr_pdu_sf_bits[i + 96*(1+blocks) - 16] & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mbc_block_bits[12*8*3];
|
||||||
|
memset (mbc_block_bits, 0, sizeof(mbc_block_bits));
|
||||||
|
//shift continuation blocks and last block into seperate array for crc check
|
||||||
|
for (i = 0; i < 12*8*3; i++) //only doing 3 blocks (4 minus the header)
|
||||||
|
{
|
||||||
|
mbc_block_bits[i] = dmr_pdu_sf_bits[i+96]; //skip mbc header
|
||||||
|
}
|
||||||
|
|
||||||
|
CRCComputed = ComputeCrcCCITT16d (mbc_block_bits, ((blocks+0)*96)-16 );
|
||||||
|
|
||||||
|
if (CRCComputed == CRCExtracted) mbc_crc_good[1] = 1;
|
||||||
|
|
||||||
|
CRCCorrect = 0;
|
||||||
|
IrrecoverableErrors = 1;
|
||||||
|
|
||||||
|
//set good on good header and good blocks
|
||||||
|
if (mbc_crc_good[0] == 1 && mbc_crc_good[1] == 1)
|
||||||
|
{
|
||||||
|
CRCCorrect = 1;
|
||||||
|
IrrecoverableErrors = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, "\n Slot %d - Multi Block Control Message CRC16 ERR", slot+1);
|
||||||
|
|
||||||
|
//debug print
|
||||||
|
fprintf (stderr, " %X - %X", CRCExtracted, CRCComputed);
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
//cspdu will only act on any fid/opcodes if good CRC to prevent falsing on control signalling
|
||||||
|
if (!is_udt && !pf) dmr_cspdu (opts, state, dmr_pdu_sf_bits, state->dmr_pdu_sf[slot], CRCCorrect, IrrecoverableErrors);
|
||||||
|
|
||||||
|
//will need to reassemble partially before sending to dmr_pdu or dmr_cspdu for handling -- needs testing
|
||||||
|
if (is_udt && !pf) dmr_udt_cspdu_converter (opts, state, state->dmr_pdu_sf[slot], dmr_pdu_sf_bits, CRCCorrect, IrrecoverableErrors);
|
||||||
|
|
||||||
|
//Full Super Frame MBC/UDT - Debug Output
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf (stderr, "\n Slot %d - Multi Block Control Message\n ", slot+1);
|
||||||
|
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", state->dmr_pdu_sf[slot][i]);
|
||||||
|
if (i == 11 || i == 23 || i == 35 || i == 47 || i == 59 || i == 71 || i == 83 || i == 95)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
if (mbc_crc_good[0] == 0) fprintf (stderr, "MBC/UDT Header CRC ERR ");
|
||||||
|
if (mbc_crc_good[1] == 0) fprintf (stderr, "MBC/UDT Blocks CRC ERR ");
|
||||||
|
if (pf) fprintf (stderr, "MBC/UDT Header/Blocks Protected ");
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
} //end last block flag on MBC
|
||||||
|
|
||||||
|
} //end type 2 (MBC Header and Continuation)
|
||||||
|
|
||||||
|
//leave this seperate so we can reset/zero stuff in case the data header isn't valid, etc
|
||||||
|
//switched to an if-elseif-else so we could get the block counter increment on the end
|
||||||
|
//without having it in an akward position
|
||||||
|
|
||||||
|
//if the end of normal data header and blocks
|
||||||
|
if (type == 1 && state->data_block_counter[slot] == state->data_header_blocks[slot])
|
||||||
|
{
|
||||||
|
|
||||||
|
//clear out unified pdu 'superframe' slot
|
||||||
|
for (short int i = 0; i < 288; i++)
|
||||||
|
{
|
||||||
|
state->dmr_pdu_sf[slot][i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Zero Out MBC Header Block CRC Valid
|
||||||
|
state->data_block_crc_valid[slot][0] = 0;
|
||||||
|
//reset the block counter (data blocks)
|
||||||
|
state->data_block_counter[slot] = 1;
|
||||||
|
//reset data header format storage
|
||||||
|
state->data_header_format[slot] = 7;
|
||||||
|
//flag off data header validity
|
||||||
|
state->data_header_valid[slot] = 0;
|
||||||
|
//flag off conf data flag
|
||||||
|
state->data_conf_data[slot] = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//else if the end of MBC Header and Blocks
|
||||||
|
else if (type == 2 && lb == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
//clear out unified pdu 'superframe' slot
|
||||||
|
for (short int i = 0; i < 288; i++)
|
||||||
|
{
|
||||||
|
state->dmr_pdu_sf[slot][i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Zero Out MBC Header Block CRC Valid
|
||||||
|
state->data_block_crc_valid[slot][0] = 0;
|
||||||
|
//reset the block counter (data blocks)
|
||||||
|
state->data_block_counter[slot] = 1;
|
||||||
|
//reset data header format storage
|
||||||
|
state->data_header_format[slot] = 7;
|
||||||
|
//flag off data header validity
|
||||||
|
state->data_header_valid[slot] = 0;
|
||||||
|
//flag off conf data flag
|
||||||
|
state->data_conf_data[slot] = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//else increment block counter after sorting/shuffling blocks
|
||||||
|
else state->data_block_counter[slot]++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//failsafe to clear old data header, block info, cach, in case of tact/emb/slottype failures
|
||||||
|
//or tuning away and we can no longer verify accurate data block reporting
|
||||||
|
void dmr_reset_blocks (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
memset (state->data_p_head, 0, sizeof(state->data_p_head));
|
||||||
|
memset (state->data_conf_data, 0, sizeof(state->data_conf_data));
|
||||||
|
memset (state->dmr_pdu_sf, 0, sizeof(state->dmr_pdu_sf));
|
||||||
|
memset (state->data_block_counter, 1, sizeof(state->data_block_counter));
|
||||||
|
memset (state->data_header_blocks, 1, sizeof(state->data_header_blocks));
|
||||||
|
memset (state->data_block_crc_valid, 0, sizeof(state->data_block_crc_valid));
|
||||||
|
memset (state->dmr_lrrp_source, 0, sizeof(state->dmr_lrrp_source));
|
||||||
|
memset (state->dmr_cach_fragment, 1, sizeof (state->dmr_cach_fragment));
|
||||||
|
memset (state->cap_plus_csbk_bits, 0, sizeof(state->cap_plus_csbk_bits));
|
||||||
|
memset (state->cap_plus_block_num, 0, sizeof(state->cap_plus_block_num));
|
||||||
|
memset (state->data_header_valid, 0, sizeof(state->data_header_valid));
|
||||||
|
memset (state->data_header_format, 7, sizeof(state->data_header_format));
|
||||||
|
//reset some strings -- resetting call string here causes random blink on ncurses terminal (cap+)
|
||||||
|
// sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||||
|
// sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->dmr_lrrp_gps[0], "%s", "");
|
||||||
|
sprintf (state->dmr_lrrp_gps[1], "%s", "");
|
||||||
|
}
|
||||||
766
src/dmr_bs.c
Normal file
766
src/dmr_bs.c
Normal file
@@ -0,0 +1,766 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_bs.c
|
||||||
|
* DMR BS Voice Handling and Data Gathering Routines - "DMR STEREO"
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#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];
|
||||||
|
char ambe_fr2[4][24];
|
||||||
|
char ambe_fr3[4][24];
|
||||||
|
|
||||||
|
//redundancy check (carrier signal loss event)
|
||||||
|
char redundancyA[36];
|
||||||
|
char redundancyB[36];
|
||||||
|
|
||||||
|
//memcpy of ambe_fr for late entry
|
||||||
|
char m1[4][24];
|
||||||
|
char m2[4][24];
|
||||||
|
char m3[4][24];
|
||||||
|
|
||||||
|
const int *w, *x, *y, *z;
|
||||||
|
char sync[25];
|
||||||
|
char syncdata[48];
|
||||||
|
char EmbeddedSignalling[16];
|
||||||
|
|
||||||
|
uint8_t emb_ok = 0;
|
||||||
|
uint8_t tact_okay = 0;
|
||||||
|
uint8_t cach_err = 0;
|
||||||
|
|
||||||
|
uint8_t internalslot;
|
||||||
|
uint8_t vc1;
|
||||||
|
uint8_t vc2;
|
||||||
|
|
||||||
|
//assign as nonsensical numbers
|
||||||
|
uint8_t cc = 25;
|
||||||
|
uint8_t power = 9; //power and pre-emption indicator
|
||||||
|
uint8_t lcss = 9;
|
||||||
|
|
||||||
|
//would be ideal to grab all dibits and break them into bits to pass to new data handler?
|
||||||
|
uint8_t dummy_bits[196];
|
||||||
|
memset (dummy_bits, 0, sizeof(dummy_bits));
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
|
//cach
|
||||||
|
char cachdata[25];
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
//cach tact bits
|
||||||
|
uint8_t tact_bits[7];
|
||||||
|
|
||||||
|
//Run Loop while the getting is good
|
||||||
|
while (loop == 1) {
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
tact_bits[i] = cachdata[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
//disabled for dmr_cach testing
|
||||||
|
tact_okay = 0;
|
||||||
|
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
||||||
|
if (tact_okay != 1) goto END;
|
||||||
|
|
||||||
|
|
||||||
|
internalslot = state->currentslot = tact_bits[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 = 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
|
||||||
|
memcpy (redundancyB, redundancyA, sizeof (redundancyA));
|
||||||
|
|
||||||
|
//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++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//signaling data or sync
|
||||||
|
for(i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i+66] = dibit;
|
||||||
|
sync[i] = (dibit | 1) + 48; //how does this work again?
|
||||||
|
|
||||||
|
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
//embedded link control
|
||||||
|
if(internalslot == 0 && vc1 > 1 && vc1 < 7) //grab on vc1 values 2-5 B C D and E
|
||||||
|
{
|
||||||
|
state->dmr_embedded_signalling[internalslot][vc1-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->dmr_embedded_signalling[internalslot][vc1-1][i*2+1] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if(internalslot == 1 && vc2 > 1 && vc2 < 7) //grab on vc2 values 2-5 B C D and E
|
||||||
|
{
|
||||||
|
state->dmr_embedded_signalling[internalslot][vc2-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->dmr_embedded_signalling[internalslot][vc2-1][i*2+1] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
sync[24] = 0;
|
||||||
|
|
||||||
|
for(i = 0; i < 8; i++) EmbeddedSignalling[i] = syncdata[i];
|
||||||
|
for(i = 0; i < 8; i++) EmbeddedSignalling[i + 8] = syncdata[i + 40];
|
||||||
|
|
||||||
|
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||||
|
for(i = 0; i < 18; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
if (internalslot == 1) vc2 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//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)
|
||||||
|
{
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: +DMR ");
|
||||||
|
}
|
||||||
|
else fprintf (stderr,"Sync: -DMR ");
|
||||||
|
|
||||||
|
vc1 = 1;
|
||||||
|
|
||||||
|
//close MBEout file - slot 1
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
}
|
||||||
|
if (internalslot == 1)
|
||||||
|
{
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: +DMR ");
|
||||||
|
}
|
||||||
|
else fprintf (stderr,"Sync: -DMR ");
|
||||||
|
|
||||||
|
vc2 = 1;
|
||||||
|
|
||||||
|
//close MBEout file - slot 2
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
|
||||||
|
}
|
||||||
|
dmr_data_sync (opts, state);
|
||||||
|
skipcount++;
|
||||||
|
goto SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
//only play voice on no data sync
|
||||||
|
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0) //we already have a tact ecc check, so we won't get here without that, see if there is any other eccs we can run just to make sure
|
||||||
|
{
|
||||||
|
|
||||||
|
//check the embedded signalling, if bad at this point, we probably aren't quite in sync
|
||||||
|
if(QR_16_7_6_decode(EmbeddedSignalling)) emb_ok = 1;
|
||||||
|
else emb_ok = 0;
|
||||||
|
|
||||||
|
//disable the goto END; if this causes more problems than fixing on late entry dual voices i.e., lots of forced resyncs
|
||||||
|
if ( (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) && emb_ok == 0) goto END; //fprintf (stderr, "EMB BAD? ");
|
||||||
|
else if (emb_ok == 1)
|
||||||
|
{
|
||||||
|
cc = ((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
|
||||||
|
power = EmbeddedSignalling[4];
|
||||||
|
lcss = ((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
skipcount = 0; //reset skip count if processing voice frames
|
||||||
|
fprintf (stderr,"%s ", getTime());
|
||||||
|
|
||||||
|
//simplifying things
|
||||||
|
char polarity[3];
|
||||||
|
char light[18];
|
||||||
|
uint8_t vc;
|
||||||
|
if (internalslot == 0)
|
||||||
|
{
|
||||||
|
state->dmrburstL = 16;
|
||||||
|
vc = vc1;
|
||||||
|
sprintf (light, "%s", " [SLOT1] slot2 ");
|
||||||
|
//open MBEout file - slot 1
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state->dmrburstR = 16;
|
||||||
|
vc = vc2;
|
||||||
|
sprintf (light, "%s", " slot1 [SLOT2] ");
|
||||||
|
//open MBEout file - slot 2
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_fR == NULL)) openMbeOutFileR (opts, state);
|
||||||
|
}
|
||||||
|
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
|
||||||
|
else sprintf (polarity, "%s", "-");
|
||||||
|
|
||||||
|
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC%d ", polarity, light, state->dmr_color_code, vc);
|
||||||
|
|
||||||
|
if (internalslot == 0 && vc1 == 6)
|
||||||
|
{
|
||||||
|
//process embedded link control
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
||||||
|
//check the single burst/reverse channel opportunity -- moved
|
||||||
|
// dmr_sbrc (opts, state, power);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (internalslot == 1 && vc2 == 6)
|
||||||
|
{
|
||||||
|
//process embedded link control
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
||||||
|
//check the single burst/reverse channel opportunity -- moved
|
||||||
|
// dmr_sbrc (opts, state, power);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
|
||||||
|
|
||||||
|
//copy ambe_fr frames first, running process mbe will correct them,
|
||||||
|
//but this also leads to issues extracting good le mi values when
|
||||||
|
//we go to do correction on them there too
|
||||||
|
memcpy (m1, ambe_fr, sizeof(m1));
|
||||||
|
memcpy (m2, ambe_fr2, sizeof(m2));
|
||||||
|
memcpy (m3, ambe_fr3, sizeof(m3));
|
||||||
|
|
||||||
|
// if ( opts->audio_in_type == 5 && internalslot == 0 && state->dmrburstR != 16 ) //(OSS shim)
|
||||||
|
// {
|
||||||
|
// processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
// processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
// processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
// }
|
||||||
|
// if ( opts->audio_in_type == 5 && internalslot == 1 && state->dmrburstL != 16 ) //(OSS shim)
|
||||||
|
// {
|
||||||
|
// processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
// processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
// processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
// }
|
||||||
|
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
|
||||||
|
cach_err = dmr_cach (opts, state, cachdata);
|
||||||
|
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
//run sbrc here to look for the late entry key and alg after we observe potential errors in VC6
|
||||||
|
if (internalslot == 0 && vc1 == 6) dmr_sbrc (opts, state, power);
|
||||||
|
if (internalslot == 1 && vc2 == 6) dmr_sbrc (opts, state, power);
|
||||||
|
|
||||||
|
// run alg refresh after vc6 ambe processing
|
||||||
|
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
|
||||||
|
if (internalslot == 1 && vc2 == 6) dmr_alg_refresh (opts, state);
|
||||||
|
|
||||||
|
dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
|
||||||
|
|
||||||
|
//increment the vc counters
|
||||||
|
if (internalslot == 0) vc1++;
|
||||||
|
if (internalslot == 1) vc2++;
|
||||||
|
|
||||||
|
//update cc amd vc sync time for trunking purposes (particularly Con+)
|
||||||
|
if (opts->p25_is_tuned == 1)
|
||||||
|
{
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
state->last_cc_sync_time = time(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
//'DSP' output to file
|
||||||
|
if (opts->use_dsp_output == 1)
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 98 ", internalslot+1); //'98' is CACH designation value
|
||||||
|
for (i = 0; i < 6; i++) //3 byte CACH
|
||||||
|
{
|
||||||
|
int cach_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", cach_byte);
|
||||||
|
}
|
||||||
|
fprintf (pFile, "\n%d 10 ", internalslot+1); //0x10 for voice burst
|
||||||
|
for (i = 6; i < 72; i++) //33 bytes, no CACH
|
||||||
|
{
|
||||||
|
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", dsp_byte);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
//reset err checks
|
||||||
|
cach_err = 1;
|
||||||
|
tact_okay = 0;
|
||||||
|
emb_ok = 0;
|
||||||
|
|
||||||
|
//reset emb components
|
||||||
|
cc = 25;
|
||||||
|
power = 9;
|
||||||
|
lcss = 9;
|
||||||
|
|
||||||
|
//Extra safeguards to break loop
|
||||||
|
// if ( (vc1 > 7 && vc2 > 7) ) goto END;
|
||||||
|
if ( (vc1 > 14 || vc2 > 14) ) goto END;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SKIP:
|
||||||
|
if (skipcount > 2) //after 2 consecutive data frames, drop back to getFrameSync and process with dmr_data_sync
|
||||||
|
{
|
||||||
|
//set tests to all good so we don't get a bogus/redundant voice error
|
||||||
|
cach_err = 0;
|
||||||
|
tact_okay = 1;
|
||||||
|
emb_ok = 1;
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
|
//since we are in a while loop, run ncursesPrinter here.
|
||||||
|
if (opts->use_ncurses_terminal == 1)
|
||||||
|
{
|
||||||
|
ncursesPrinter(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // while loop
|
||||||
|
|
||||||
|
END:
|
||||||
|
state->dmr_stereo = 0;
|
||||||
|
state->errs = 0;
|
||||||
|
state->errs2 = 0;
|
||||||
|
state->errs2R = 0;
|
||||||
|
state->errs2 = 0;
|
||||||
|
|
||||||
|
//close any open MBEout files
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
|
||||||
|
//if we have a tact or emb err, then produce sync pattern/err message
|
||||||
|
if (tact_okay != 1 || emb_ok != 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
fprintf (stderr,"%s ", getTime());
|
||||||
|
fprintf (stderr,"Sync: DMR ");
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, "| VOICE CACH/EMB ERR");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
//run refresh if either slot had an active MI in it.
|
||||||
|
if (state->payload_algid >= 0x21)
|
||||||
|
{
|
||||||
|
state->currentslot = 0;
|
||||||
|
dmr_alg_refresh (opts, state);
|
||||||
|
}
|
||||||
|
if (state->payload_algidR >= 0x21)
|
||||||
|
{
|
||||||
|
state->currentslot = 1;
|
||||||
|
dmr_alg_refresh (opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
//failsafe to reset all data header and blocks when bad tact or emb
|
||||||
|
dmr_reset_blocks (opts, state);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//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];
|
||||||
|
char ambe_fr2[4][24];
|
||||||
|
char ambe_fr3[4][24];
|
||||||
|
|
||||||
|
//memcpy of ambe_fr for late entry
|
||||||
|
char m1[4][24];
|
||||||
|
char m2[4][24];
|
||||||
|
char m3[4][24];
|
||||||
|
|
||||||
|
const int *w, *x, *y, *z;
|
||||||
|
char sync[25];
|
||||||
|
uint8_t tact_okay = 0;
|
||||||
|
uint8_t cach_err = 0;
|
||||||
|
uint8_t sync_okay = 1;
|
||||||
|
|
||||||
|
uint8_t internalslot;
|
||||||
|
|
||||||
|
char cachdata[25];
|
||||||
|
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) & 3;
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
//cach tact bits
|
||||||
|
uint8_t tact_bits[7];
|
||||||
|
for (i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
tact_bits[i] = cachdata[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
//decode and correct tact and compare
|
||||||
|
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
||||||
|
if (tact_okay != 1) goto END;
|
||||||
|
|
||||||
|
internalslot = state->currentslot = tact_bits[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;
|
||||||
|
|
||||||
|
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
|
||||||
|
{
|
||||||
|
sync_okay = 0;
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||||
|
for(i = 0; i < 18; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
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);
|
||||||
|
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++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//'DSP' output to file
|
||||||
|
if (opts->use_dsp_output == 1)
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 98 ", internalslot+1); //'98' is CACH designation value
|
||||||
|
for (i = 0; i < 6; i++) //3 byte CACH
|
||||||
|
{
|
||||||
|
int cach_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", cach_byte);
|
||||||
|
}
|
||||||
|
fprintf (pFile, "\n%d 10 ", internalslot+1); //0x10 for "voice burst"
|
||||||
|
for (i = 6; i < 72; i++) //33 bytes, no CACH
|
||||||
|
{
|
||||||
|
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", dsp_byte);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr,"%s ", getTime());
|
||||||
|
char polarity[3];
|
||||||
|
char light[18];
|
||||||
|
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
sprintf (light, "%s", " [SLOT1] slot2 ");
|
||||||
|
//open MBEout file - slot 1
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf (light, "%s", " slot1 [SLOT2] ");
|
||||||
|
//open MBEout file - slot 2
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_fR == NULL)) openMbeOutFileR (opts, state);
|
||||||
|
}
|
||||||
|
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
|
||||||
|
else sprintf (polarity, "%s", "-");
|
||||||
|
|
||||||
|
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC1*", polarity, light, state->dmr_color_code);
|
||||||
|
|
||||||
|
dmr_alg_reset (opts, state);
|
||||||
|
|
||||||
|
//copy ambe_fr frames first, running process mbe will correct them,
|
||||||
|
//but this also leads to issues extracting good le mi values when
|
||||||
|
//we go to do correction on them there too
|
||||||
|
memcpy (m1, ambe_fr, sizeof(m1));
|
||||||
|
memcpy (m2, ambe_fr2, sizeof(m2));
|
||||||
|
memcpy (m3, ambe_fr3, sizeof(m3));
|
||||||
|
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
|
||||||
|
//collect the mi fragment
|
||||||
|
dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
|
||||||
|
|
||||||
|
cach_err = dmr_cach (opts, state, cachdata);
|
||||||
|
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
//update voice sync time for trunking purposes (particularly Con+)
|
||||||
|
if (opts->p25_is_tuned == 1) state->last_vc_sync_time = time(NULL);
|
||||||
|
|
||||||
|
dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode
|
||||||
|
END:
|
||||||
|
//if we have a tact err, then produce sync pattern/err message
|
||||||
|
if (tact_okay != 1 || sync_okay != 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"%s ", getTime());
|
||||||
|
fprintf (stderr,"Sync: DMR ");
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, "| VOICE CACH/SYNC ERR");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
//run refresh if either slot had an active MI in it.
|
||||||
|
if (state->payload_algid >= 0x21)
|
||||||
|
{
|
||||||
|
state->currentslot = 0;
|
||||||
|
dmr_alg_refresh (opts, state);
|
||||||
|
}
|
||||||
|
if (state->payload_algidR >= 0x21)
|
||||||
|
{
|
||||||
|
state->currentslot = 1;
|
||||||
|
dmr_alg_refresh (opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
//failsafe to reset all data header and blocks when bad tact or emb
|
||||||
|
dmr_reset_blocks (opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
1482
src/dmr_csbk.c
Normal file
1482
src/dmr_csbk.c
Normal file
File diff suppressed because it is too large
Load Diff
353
src/dmr_data.c
Normal file
353
src/dmr_data.c
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i, dibit;
|
||||||
|
int *dibit_p;
|
||||||
|
char sync[25];
|
||||||
|
char syncdata[48];
|
||||||
|
char cachdata[25];
|
||||||
|
|
||||||
|
char bursttype[5];
|
||||||
|
uint8_t burst;
|
||||||
|
char info[196];
|
||||||
|
unsigned char SlotType[20];
|
||||||
|
unsigned int SlotTypeOk;
|
||||||
|
uint8_t cach_err = 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
|
||||||
|
};
|
||||||
|
|
||||||
|
dibit_p = state->dmr_payload_p - 90;
|
||||||
|
|
||||||
|
//collect cach and de-interleave
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
else state->dmr_stereo_payload[i] = dibit;
|
||||||
|
|
||||||
|
cachdata[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//seperate tact bits from cach
|
||||||
|
uint8_t tact_bits[7];
|
||||||
|
for (i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
tact_bits[i] = cachdata[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
//run hamming on tact bits
|
||||||
|
int cach_okay = -1;
|
||||||
|
if ( Hamming_7_4_decode (tact_bits) ) cach_okay = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cach_okay = -1;
|
||||||
|
SlotTypeOk = 0;
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->currentslot = tact_bits[1];
|
||||||
|
|
||||||
|
//in the future, maybe we will remove the hard set on this
|
||||||
|
if (state->dmr_ms_mode == 1)
|
||||||
|
{
|
||||||
|
state->currentslot = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
}
|
||||||
|
else state->dmr_stereo_payload[i+12] = 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];
|
||||||
|
}
|
||||||
|
else state->dmr_stereo_payload[61] = dibit;
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
else state->dmr_stereo_payload[62] = dibit;
|
||||||
|
|
||||||
|
SlotType[2] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
SlotType[3] = (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[63];
|
||||||
|
}
|
||||||
|
else state->dmr_stereo_payload[63] = dibit;
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
else state->dmr_stereo_payload[64] = dibit;
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
else state->dmr_stereo_payload[65] = dibit;
|
||||||
|
SlotType[8] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
SlotType[9] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
// signaling 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];
|
||||||
|
}
|
||||||
|
else 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;
|
||||||
|
}
|
||||||
|
sync[24] = 0;
|
||||||
|
|
||||||
|
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) )
|
||||||
|
{
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
sprintf(state->slot1light, "[slot1]");
|
||||||
|
sprintf(state->slot2light, " slot2 ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(state->slot1light, " slot1 ");
|
||||||
|
sprintf(state->slot2light, "[slot2]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0)
|
||||||
|
{
|
||||||
|
state->currentslot = 0;
|
||||||
|
sprintf(state->slot1light, "[sLoT1]");
|
||||||
|
sprintf(state->slot2light, "[DMODE]");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
|
||||||
|
{
|
||||||
|
state->currentslot = 1;
|
||||||
|
sprintf(state->slot1light, "[DMODE]");
|
||||||
|
sprintf(state->slot2light, "[sLoT2]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (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)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
|
}
|
||||||
|
if (state->dmr_stereo == 1)
|
||||||
|
{
|
||||||
|
dibit = (int)state->dmr_stereo_payload[i+90];
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SlotTypeOk = -1;
|
||||||
|
SlotTypeOk = 0;
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0);
|
||||||
|
state->color_code_ok = SlotTypeOk;
|
||||||
|
|
||||||
|
//not sure why I still have two variables for this, need to look and see what state->color_code still ties into
|
||||||
|
if (SlotTypeOk == 1) state->dmr_color_code = state->color_code;
|
||||||
|
|
||||||
|
/* Reconstitute the burst type */
|
||||||
|
burst = (uint8_t)((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;
|
||||||
|
|
||||||
|
|
||||||
|
// Current slot - Second Half - Data Payload - 2nd part
|
||||||
|
for (i = 0; i < 49; i++)
|
||||||
|
{
|
||||||
|
if (state->dmr_stereo == 0)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i+95] = dibit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->dmr_stereo == 1)
|
||||||
|
{
|
||||||
|
dibit = (int)state->dmr_stereo_payload[i+95];
|
||||||
|
}
|
||||||
|
|
||||||
|
info[(2*i) + 98] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
info[(2*i) + 99] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//don't run on MS Reverse Channel Data
|
||||||
|
if (state->dmr_ms_rc == 0) dmr_data_burst_handler(opts, state, (uint8_t *)info, burst);
|
||||||
|
|
||||||
|
//don't run cach on simplex or mono
|
||||||
|
if (state->dmr_ms_mode == 0 && opts->dmr_mono == 0)
|
||||||
|
{
|
||||||
|
cach_err = dmr_cach (opts, state, cachdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
//ending line break
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
END:
|
||||||
|
if (SlotTypeOk == 0 || cach_okay != 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, "| CACH/Burst FEC ERR");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
dmr_reset_blocks (opts, state); //failsafe to reset all data header and blocks when bad tact or slottype
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 using con+ trunking and last voice observed more than x seconds ago, snap back to con+ cc
|
||||||
|
//con+ voice channels can have extremely long idle periods without properly tearing down
|
||||||
|
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->is_con_plus == 1)
|
||||||
|
{
|
||||||
|
//bug fixed that caused hopping from CC to VC; decreased to 2 seconds.
|
||||||
|
if ( (time(NULL) - state->last_vc_sync_time > 2) )
|
||||||
|
{
|
||||||
|
if (opts->use_rigctl == 1) //rigctl tuning
|
||||||
|
{
|
||||||
|
if (opts->setmod_bw != 0) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
|
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (opts->audio_in_type == 3) //rtl_fm tuning
|
||||||
|
{
|
||||||
|
#ifdef USE_RTLSDR
|
||||||
|
rtl_dev_tune(opts, state->p25_cc_freq);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
opts->p25_is_tuned = 0;
|
||||||
|
//zero out vc frequencies
|
||||||
|
state->p25_vc_freq[0] = 0;
|
||||||
|
state->p25_vc_freq[1] = 0;
|
||||||
|
memset (state->active_channel, 0, sizeof(state->active_channel));
|
||||||
|
state->last_cc_sync_time = time(NULL);
|
||||||
|
state->is_con_plus = 0; //con+ flag off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
645
src/dmr_dburst.c
Normal file
645
src/dmr_dburst.c
Normal file
@@ -0,0 +1,645 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_dburst.c
|
||||||
|
* DMR Data Burst Handling and related BPTC/FEC/CRC Functions
|
||||||
|
*
|
||||||
|
* Portions of BPTC/FEC/CRC code from LouisErigHerve
|
||||||
|
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
//WIP: Unified Single Block Data - USBD -- TODO: USBD LIP Protocol Handling
|
||||||
|
//WIP: UDT Header and Blocks (coded - need samples)
|
||||||
|
//WIP: CRC9/CRC32 on Rate 1 Data (need samples)
|
||||||
|
//WIP: Reverse Channel Signalling - RC single burst BPTC/7-bit CRC (rc interleave) and when to run RC and not SB
|
||||||
|
//WIP: Single Burst Embedded LC - Non-RC single burst LC - Look for Late Entry Alg/Key
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t i, j, k;
|
||||||
|
uint32_t CRCExtracted = 0;
|
||||||
|
uint32_t CRCComputed = 0;
|
||||||
|
uint32_t CRCCorrect = 0;
|
||||||
|
uint32_t IrrecoverableErrors = 0;
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
uint8_t block = state->data_block_counter[slot];
|
||||||
|
|
||||||
|
//confirmed data
|
||||||
|
uint8_t dbsn = 0; //data block serial number for confirmed data blocks
|
||||||
|
uint8_t blockcounter = 0; //local block count
|
||||||
|
uint8_t confdatabits[250]; //array to reshuffle conf data block bits into sequence for crc9 check
|
||||||
|
memset (confdatabits, 0, sizeof(confdatabits));
|
||||||
|
|
||||||
|
//BPTC 196x96 Specific
|
||||||
|
uint8_t BPTCDeInteleavedData[196];
|
||||||
|
uint8_t BPTCDmrDataBit[96];
|
||||||
|
uint8_t BPTCDmrDataByte[12];
|
||||||
|
|
||||||
|
//Embedded Signalling Specific
|
||||||
|
uint8_t BptcDataMatrix[8][16];
|
||||||
|
uint8_t LC_DataBit[77];
|
||||||
|
uint8_t LC_DataBytes[10];
|
||||||
|
int Burst = -1;
|
||||||
|
|
||||||
|
//PDU Bytes and Bits
|
||||||
|
uint8_t DMR_PDU[25];
|
||||||
|
uint8_t DMR_PDU_bits[196];
|
||||||
|
memset (DMR_PDU, 0, sizeof (DMR_PDU));
|
||||||
|
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
|
||||||
|
|
||||||
|
uint8_t R[3];
|
||||||
|
uint8_t BPTCReservedBits = 0;
|
||||||
|
uint8_t is_ras = 0;
|
||||||
|
uint8_t crc_original_validity = 0;
|
||||||
|
|
||||||
|
uint32_t crcmask = 0;
|
||||||
|
uint8_t crclen = 0;
|
||||||
|
|
||||||
|
uint8_t is_bptc = 0;
|
||||||
|
uint8_t is_trellis = 0;
|
||||||
|
uint8_t is_emb = 0;
|
||||||
|
uint8_t is_lc = 0;
|
||||||
|
uint8_t is_full = 0;
|
||||||
|
uint8_t is_udt = 0;
|
||||||
|
uint8_t pdu_len = 0;
|
||||||
|
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
|
||||||
|
|
||||||
|
uint8_t usbd_st = 0; //usbd service type
|
||||||
|
|
||||||
|
switch(databurst){
|
||||||
|
case 0x00: //PI
|
||||||
|
is_bptc = 1;
|
||||||
|
crclen = 16;
|
||||||
|
crcmask = 0x6969; //insert pi and 69 jokes here
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " PI ");
|
||||||
|
break;
|
||||||
|
case 0x01: //VLC
|
||||||
|
is_bptc = 1;
|
||||||
|
is_lc = 1;
|
||||||
|
crclen = 24;
|
||||||
|
crcmask = 0x969696;
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " VLC ");
|
||||||
|
break;
|
||||||
|
case 0x02: //TLC
|
||||||
|
is_bptc = 1;
|
||||||
|
is_lc = 1;
|
||||||
|
crcmask = 0x999999;
|
||||||
|
crclen = 24;
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " TLC ");
|
||||||
|
break;
|
||||||
|
case 0x03: //CSBK
|
||||||
|
is_bptc = 1;
|
||||||
|
crclen = 16;
|
||||||
|
crcmask = 0xA5A5;
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " CSBK");
|
||||||
|
break;
|
||||||
|
case 0x04: //MBC Header
|
||||||
|
is_bptc = 1;
|
||||||
|
crclen = 16;
|
||||||
|
crcmask = 0xAAAA;
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " MBCH");
|
||||||
|
break;
|
||||||
|
case 0x05: //MBC Continuation
|
||||||
|
is_bptc = 1;
|
||||||
|
//let block assembler carry out crc on the completed message
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " MBCC");
|
||||||
|
break;
|
||||||
|
case 0x06: //Data Header
|
||||||
|
is_bptc = 1;
|
||||||
|
crclen = 16;
|
||||||
|
crcmask = 0xCCCC;
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " DATA");
|
||||||
|
break;
|
||||||
|
case 0x07: //1/2 Rate Data
|
||||||
|
is_bptc = 1;
|
||||||
|
crclen = 9; //confirmed data only
|
||||||
|
crcmask = 0x0F0;
|
||||||
|
pdu_len = 12; //12 bytes unconfirmed
|
||||||
|
sprintf(state->fsubtype, " R12U ");
|
||||||
|
if (state->data_conf_data[slot] == 1)
|
||||||
|
{
|
||||||
|
pdu_len = 10;
|
||||||
|
pdu_start = 2; //start at plus two when assembling
|
||||||
|
sprintf(state->fsubtype, " R12C ");
|
||||||
|
}
|
||||||
|
if (state->data_header_format[slot] == 0) //UDT 1/2 Encoded Blocks
|
||||||
|
{
|
||||||
|
is_udt = 1;
|
||||||
|
sprintf(state->fsubtype, " UDTC ");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x08: //3/4 Rate Data
|
||||||
|
is_trellis = 1;
|
||||||
|
crclen = 9; //confirmed data only
|
||||||
|
crcmask = 0x1FF;
|
||||||
|
pdu_len = 18; //18 bytes unconfirmed
|
||||||
|
sprintf(state->fsubtype, " R34U ");
|
||||||
|
if (state->data_conf_data[slot] == 1)
|
||||||
|
{
|
||||||
|
pdu_len = 16;
|
||||||
|
pdu_start = 2; //start at plus two when assembling
|
||||||
|
sprintf(state->fsubtype, " R34C ");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x09: //Idle
|
||||||
|
//pseudo-random data fill, no need to do anything with this
|
||||||
|
sprintf(state->fsubtype, " IDLE ");
|
||||||
|
break;
|
||||||
|
case 0x0A: //1 Rate Data
|
||||||
|
crclen = 9; //confirmed data only
|
||||||
|
crcmask = 0x10F;
|
||||||
|
is_full = 1;
|
||||||
|
pdu_len = 25; //196 bits - 24.5 bytes
|
||||||
|
sprintf(state->fsubtype, " R_1U ");
|
||||||
|
if (state->data_conf_data[slot] == 1)
|
||||||
|
{
|
||||||
|
pdu_len = 23;
|
||||||
|
pdu_start = 2; //start at plus two when assembling
|
||||||
|
sprintf(state->fsubtype, " R_1C ");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x0B: //Unified Single Block Data USBD
|
||||||
|
is_bptc = 1;
|
||||||
|
crclen = 16;
|
||||||
|
crcmask = 0x3333;
|
||||||
|
pdu_len = 12; //12 bytes
|
||||||
|
sprintf(state->fsubtype, " USBD ");
|
||||||
|
break;
|
||||||
|
|
||||||
|
//special types (not real data 'sync' bursts)
|
||||||
|
case 0xEB: //Embedded Signalling
|
||||||
|
crclen = 5;
|
||||||
|
is_emb = 1;
|
||||||
|
pdu_len = 9;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
//Slot Type FEC should catch this so we never see it,
|
||||||
|
//but if it doesn't, then we can still dump the entire 'packet'
|
||||||
|
//treat like rate 1 unconfirmed data
|
||||||
|
is_full = 1;
|
||||||
|
pdu_len = 25; //196 bits - 24.5 bytes
|
||||||
|
sprintf(state->fsubtype, " _UNK ");
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//flag off prop head when not looking at data blocks
|
||||||
|
if (databurst != 0x6 && databurst != 0x7 && databurst != 0x8 && databurst != 0xA && databurst != 0xB) state->data_p_head[slot] = 0;
|
||||||
|
|
||||||
|
if (databurst != 0xEB)
|
||||||
|
{
|
||||||
|
if (state->dmr_ms_mode == 0) fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
|
||||||
|
fprintf(stderr, "|%s", state->fsubtype);
|
||||||
|
|
||||||
|
//'DSP' output to file
|
||||||
|
if (opts->use_dsp_output == 1)
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 98 ", slot+1); //'98' is CACH designation value
|
||||||
|
for (i = 0; i < 6; i++) //3 byte CACH
|
||||||
|
{
|
||||||
|
int cach_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", cach_byte); //nibble, not a byte, next time I look at this and wonder why its not %02X
|
||||||
|
}
|
||||||
|
fprintf (pFile, "\n%d %02X ", slot+1, databurst); //use hex value of current data burst type
|
||||||
|
for (i = 6; i < 72; i++) //33 bytes, no CACH
|
||||||
|
{
|
||||||
|
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", dsp_byte);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Most Data Sync Burst types will use the bptc 196x96
|
||||||
|
if (is_bptc)
|
||||||
|
{
|
||||||
|
CRCExtracted = 0;
|
||||||
|
CRCComputed = 0;
|
||||||
|
IrrecoverableErrors = 0;
|
||||||
|
|
||||||
|
/* Deinterleave DMR data */
|
||||||
|
BPTCDeInterleaveDMRData(info, BPTCDeInteleavedData);
|
||||||
|
|
||||||
|
/* Extract the BPTC 196,96 DMR data */
|
||||||
|
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, R);
|
||||||
|
|
||||||
|
/* Fill the reserved bit (R(0)-R(2) of the BPTC(196,96) block) */
|
||||||
|
BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x04);
|
||||||
|
|
||||||
|
//debug print
|
||||||
|
//fprintf (stderr, " RAS? %X - %d %d %d", BPTCReservedBits, R[0], R[1], R[2]);
|
||||||
|
|
||||||
|
/* Convert the 96 bits BPTC data into 12 bytes */
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
BPTCDmrDataByte[i] = 0;
|
||||||
|
for(j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] << 1;
|
||||||
|
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] | (BPTCDmrDataBit[k] & 0x01);
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */
|
||||||
|
CRCExtracted = 0;
|
||||||
|
for(i = 0; i < crclen; i++)
|
||||||
|
{
|
||||||
|
CRCExtracted = CRCExtracted << 1;
|
||||||
|
CRCExtracted = CRCExtracted | (uint32_t)(BPTCDmrDataBit[i + 96 - crclen] & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply the CRC mask (see DMR standard B.3.12 Data Type CRC Mask) */
|
||||||
|
CRCExtracted = CRCExtracted ^ crcmask;
|
||||||
|
|
||||||
|
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
|
||||||
|
if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
|
||||||
|
|
||||||
|
//set CRC to correct on unconfirmed 1/2 data blocks (for reporting due to no CRC available on these)
|
||||||
|
else if (state->data_conf_data[slot] == 0 && databurst == 0x7)
|
||||||
|
{
|
||||||
|
CRCComputed = 0;
|
||||||
|
CRCExtracted = 0;
|
||||||
|
CRCCorrect = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//run CRC9 on intermediate and last 1/2 confirmed data blocks
|
||||||
|
else if (state->data_conf_data[slot] == 1 && databurst == 0x7)
|
||||||
|
{
|
||||||
|
blockcounter = state->data_block_counter[slot]; //current block number according to the counter
|
||||||
|
dbsn = (uint32_t)ConvertBitIntoBytes(&BPTCDmrDataBit[0], 7) + 1; //recover data block serial number
|
||||||
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&BPTCDmrDataBit[7], 9); //extract CRC from data
|
||||||
|
CRCExtracted = CRCExtracted ^ crcmask;
|
||||||
|
|
||||||
|
//reorganize the BPTCDmrDataBit array into confdatabits, just for CRC9 check
|
||||||
|
//need to find a sample to confirm this
|
||||||
|
for(i = 0; i < 80; i++) confdatabits[i] = BPTCDmrDataBit[i + 16];
|
||||||
|
for(i = 0; i < 7; i++) confdatabits[i + 80] = BPTCDmrDataBit[i];
|
||||||
|
|
||||||
|
CRCComputed = ComputeCrc9Bit(confdatabits, 87);
|
||||||
|
if (CRCExtracted == CRCComputed)
|
||||||
|
{
|
||||||
|
CRCCorrect = 1;
|
||||||
|
state->data_block_crc_valid[slot][blockcounter] = 1;
|
||||||
|
}
|
||||||
|
else state->data_block_crc_valid[slot][blockcounter] = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//run CCITT on other data forms
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
|
||||||
|
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
||||||
|
else CRCCorrect = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the 'RAS Flag', if no irrecoverable errors but bad crc, only when enabled by user (to prevent a lot of bad data CSBKs)
|
||||||
|
if (opts->aggressive_framesync == 0 && CRCCorrect == 0 && IrrecoverableErrors == 0 && BPTCReservedBits == 4) is_ras = 1;
|
||||||
|
|
||||||
|
//make sure the system type isn't Hytera, but could just be bad decodes on bad sample
|
||||||
|
if (BPTCDmrDataByte[1] == 0x68) is_ras = 0;
|
||||||
|
|
||||||
|
//if this is a RAS system, set the CRC to okay if irrecoverable errors are okay
|
||||||
|
//if we don't do this, then we can't view some data (CSBKs on RAS enabled systems)
|
||||||
|
if (is_ras == 1)
|
||||||
|
{
|
||||||
|
crc_original_validity = CRCCorrect;
|
||||||
|
CRCCorrect = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (databurst == 0x04 || databurst == 0x06) //MBC Header, Data Header
|
||||||
|
{
|
||||||
|
if (CRCCorrect)
|
||||||
|
{
|
||||||
|
state->data_block_crc_valid[slot][0] = 1;
|
||||||
|
}
|
||||||
|
else state->data_block_crc_valid[slot][0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert corrected x bytes into x*8 bits */
|
||||||
|
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
|
||||||
|
{
|
||||||
|
BPTCDmrDataBit[j + 0] = (BPTCDmrDataByte[i+pdu_start] >> 7) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 1] = (BPTCDmrDataByte[i+pdu_start] >> 6) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 2] = (BPTCDmrDataByte[i+pdu_start] >> 5) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 3] = (BPTCDmrDataByte[i+pdu_start] >> 4) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 4] = (BPTCDmrDataByte[i+pdu_start] >> 3) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 5] = (BPTCDmrDataByte[i+pdu_start] >> 2) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 6] = (BPTCDmrDataByte[i+pdu_start] >> 1) & 0x01;
|
||||||
|
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
//convert to DMR_PDU and DMR_PDU_bits
|
||||||
|
for (i = 0; i < pdu_len; i++)
|
||||||
|
{
|
||||||
|
DMR_PDU[i] = BPTCDmrDataByte[i+pdu_start];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < pdu_len * 8; i++)
|
||||||
|
{
|
||||||
|
DMR_PDU_bits[i] = BPTCDmrDataBit[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Embedded Signalling will use BPTC 128x77
|
||||||
|
if (is_emb)
|
||||||
|
{
|
||||||
|
|
||||||
|
CRCExtracted = 0;
|
||||||
|
CRCComputed = 0;
|
||||||
|
IrrecoverableErrors = 0;
|
||||||
|
|
||||||
|
/* First step : Reconstitute the BPTC 16x8 matrix */
|
||||||
|
Burst = 1; /* Burst B to E contains embedded signaling data */
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
for(j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
/* Only the LSBit of the byte is stored */
|
||||||
|
BptcDataMatrix[j][i] = state->dmr_embedded_signalling[slot][Burst][k+8];
|
||||||
|
k++;
|
||||||
|
|
||||||
|
/* Go on to the next burst once 32 bit
|
||||||
|
* of the SNYC have been stored */
|
||||||
|
if(k >= 32)
|
||||||
|
{
|
||||||
|
k = 0;
|
||||||
|
Burst++;
|
||||||
|
}
|
||||||
|
} /* End for(j = 0; j < 8; j++) */
|
||||||
|
} /* End for(i = 0; i < 16; i++) */
|
||||||
|
|
||||||
|
/* Extract the 72 LC bit (+ 5 CRC bit) of the matrix */
|
||||||
|
IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
|
||||||
|
|
||||||
|
/* Reconstitute the 5 bit CRC */
|
||||||
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&LC_DataBit[72], 5);
|
||||||
|
|
||||||
|
/* Compute the 5 bit CRC */
|
||||||
|
CRCComputed = ComputeCrc5Bit(LC_DataBit);
|
||||||
|
|
||||||
|
if(CRCExtracted == CRCComputed) CRCCorrect = 1;
|
||||||
|
else CRCCorrect = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 72; i++)
|
||||||
|
{
|
||||||
|
DMR_PDU_bits[i] = LC_DataBit[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&LC_DataBit[i*8], 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//the ugly one
|
||||||
|
if (is_trellis)
|
||||||
|
{
|
||||||
|
CRCExtracted = 0;
|
||||||
|
CRCComputed = 0;
|
||||||
|
IrrecoverableErrors = 1;
|
||||||
|
bool trellis_good = TRUE;
|
||||||
|
|
||||||
|
uint8_t tdibits[98];
|
||||||
|
memset (tdibits, 0, sizeof(tdibits));
|
||||||
|
|
||||||
|
//reconstitute info bits into reverse ordered dibits for the trellis decoder
|
||||||
|
for (i = 0; i < 98; i++)
|
||||||
|
{
|
||||||
|
tdibits[97-i] = (info[i*2] << 1) | info[i*2+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char TrellisReturn[18];
|
||||||
|
memset (TrellisReturn, 0, sizeof(TrellisReturn));
|
||||||
|
|
||||||
|
trellis_good = CDMRTrellisDecode(tdibits, TrellisReturn);
|
||||||
|
if (trellis_good == TRUE) IrrecoverableErrors = 0;
|
||||||
|
// else (fprintf (stderr, " Trellis Failure")); //debug print
|
||||||
|
|
||||||
|
for (i = 0; i < pdu_len; i++) //18
|
||||||
|
{
|
||||||
|
DMR_PDU[i] = (uint8_t)TrellisReturn[i+pdu_start];
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0, j = 0; i < 18; i++, j+=8)
|
||||||
|
{
|
||||||
|
DMR_PDU_bits[j + 0] = (TrellisReturn[i] >> 7) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 1] = (TrellisReturn[i] >> 6) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 2] = (TrellisReturn[i] >> 5) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 3] = (TrellisReturn[i] >> 4) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 4] = (TrellisReturn[i] >> 3) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 5] = (TrellisReturn[i] >> 2) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 6] = (TrellisReturn[i] >> 1) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 7] = (TrellisReturn[i] >> 0) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
//set CRC to correct on unconfirmed 3/4 data blocks (for reporting due to no CRC available on these)
|
||||||
|
if (state->data_conf_data[slot] == 0) CRCCorrect = 1;
|
||||||
|
|
||||||
|
//run CRC9 on intermediate and last 3/4 data blocks
|
||||||
|
else if (state->data_conf_data[slot] == 1)
|
||||||
|
{
|
||||||
|
blockcounter = state->data_block_counter[slot]; //current block number according to the counter
|
||||||
|
dbsn = (uint32_t)ConvertBitIntoBytes(&DMR_PDU_bits[0], 7) + 1; //recover data block serial number
|
||||||
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&DMR_PDU_bits[7], 9); //extract CRC from data
|
||||||
|
CRCExtracted = CRCExtracted ^ crcmask;
|
||||||
|
|
||||||
|
//reorganize the DMR_PDU_bits array into confdatabits, just for CRC9 check
|
||||||
|
//confirmed working now!
|
||||||
|
for(i = 0; i < 128; i++) confdatabits[i] = DMR_PDU_bits[i + 16];
|
||||||
|
for(i = 0; i < 7; i++) confdatabits[i + 128] = DMR_PDU_bits[i];
|
||||||
|
|
||||||
|
CRCComputed = ComputeCrc9Bit(confdatabits, 135);
|
||||||
|
if (CRCExtracted == CRCComputed)
|
||||||
|
{
|
||||||
|
CRCCorrect = 1;
|
||||||
|
state->data_block_crc_valid[slot][blockcounter] = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// IrrecoverableErrors = 9; //set as a shim since the trellis decoder is a pain to fix
|
||||||
|
state->data_block_crc_valid[slot][blockcounter] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//reorganize the DMR_PDU_bits into PDU friendly format (minus the dbsn and crc9)
|
||||||
|
memset (DMR_PDU_bits, 0, sizeof(DMR_PDU_bits));
|
||||||
|
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
|
||||||
|
{
|
||||||
|
DMR_PDU_bits[j + 0] = (TrellisReturn[i+pdu_start] >> 7) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 1] = (TrellisReturn[i+pdu_start] >> 6) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 2] = (TrellisReturn[i+pdu_start] >> 5) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 3] = (TrellisReturn[i+pdu_start] >> 4) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 4] = (TrellisReturn[i+pdu_start] >> 3) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 5] = (TrellisReturn[i+pdu_start] >> 2) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 6] = (TrellisReturn[i+pdu_start] >> 1) & 0x01;
|
||||||
|
DMR_PDU_bits[j + 7] = (TrellisReturn[i+pdu_start] >> 0) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_full) //Rate 1 Data
|
||||||
|
{
|
||||||
|
CRCExtracted = 0;
|
||||||
|
CRCComputed = 0;
|
||||||
|
IrrecoverableErrors = 0; //implicit, since there is no encoding
|
||||||
|
|
||||||
|
for (i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&info[i*8], 8);
|
||||||
|
}
|
||||||
|
//leftover 4 bits, manual doesn't say which 4 so assuming last 4
|
||||||
|
//pushing last 4 to 25 and shifting it
|
||||||
|
DMR_PDU[25] = (uint8_t)ConvertBitIntoBytes(&info[192], 4);
|
||||||
|
DMR_PDU[25] = DMR_PDU[25] << 4;
|
||||||
|
|
||||||
|
//set CRC to correct on unconfirmed 1 rate data blocks (for reporting due to no CRC available on these)
|
||||||
|
if (state->data_conf_data[slot] == 0) CRCCorrect = 1;
|
||||||
|
|
||||||
|
//run CRC9 on intermediate and last 1 rate data blocks
|
||||||
|
else if (state->data_conf_data[slot] == 1)
|
||||||
|
{
|
||||||
|
blockcounter = state->data_block_counter[slot]; //current block number according to the counter
|
||||||
|
dbsn = (uint32_t)ConvertBitIntoBytes(&info[0], 7) + 1; //recover data block serial number
|
||||||
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&info[7], 9); //extract CRC from data
|
||||||
|
CRCExtracted = CRCExtracted ^ crcmask;
|
||||||
|
|
||||||
|
//reorganize the info bit array into confdatabits, just for CRC9 check
|
||||||
|
//unconfirmed, haven't seen any samples with rate 1 data
|
||||||
|
for(i = 0; i < 176; i++) confdatabits[i] = info[i + 16];
|
||||||
|
for(i = 0; i < 7; i++) confdatabits[i + 176] = info[i];
|
||||||
|
|
||||||
|
CRCComputed = ComputeCrc9Bit(confdatabits, 183);
|
||||||
|
if (CRCExtracted == CRCComputed)
|
||||||
|
{
|
||||||
|
CRCCorrect = 1;
|
||||||
|
state->data_block_crc_valid[slot][blockcounter] = 1;
|
||||||
|
}
|
||||||
|
else state->data_block_crc_valid[slot][blockcounter] = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//copy info to dmr_pdu_bits
|
||||||
|
memcpy(DMR_PDU_bits, info, sizeof(DMR_PDU_bits));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//time for some pi
|
||||||
|
if (databurst == 0x00) dmr_pi (opts, state, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
||||||
|
|
||||||
|
//full link control
|
||||||
|
if (databurst == 0x01) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 1); //VLC
|
||||||
|
if (databurst == 0x02) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 2); //TLC
|
||||||
|
if (databurst == 0xEB) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 3); //EMB
|
||||||
|
|
||||||
|
//dmr data header and multi block types (header, 1/2, 3/4, 1, UDT) - type 1
|
||||||
|
if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors);
|
||||||
|
if (databurst == 0x08) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //3/4 Rate Data
|
||||||
|
if (databurst == 0x0A) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //Full Rate Data
|
||||||
|
if (databurst == 0x07 && is_udt == 0) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //1/2 Rate Data
|
||||||
|
if (databurst == 0x07 && is_udt == 1) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 3); //UDT with 1/2 Rate Encoding
|
||||||
|
|
||||||
|
//control signalling types (CSBK, MBC)
|
||||||
|
if (databurst == 0x03) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
||||||
|
|
||||||
|
//both MBC header and MBC continuation will go to the block_assembler - type 2, and then to dmr_cspdu
|
||||||
|
if (databurst == 0x04)
|
||||||
|
{
|
||||||
|
state->data_block_counter[slot] = 0; //zero block counter before running header
|
||||||
|
state->data_header_valid[slot] = 1; //set valid header since we received one
|
||||||
|
dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2);
|
||||||
|
}
|
||||||
|
if (databurst == 0x05) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 2);
|
||||||
|
|
||||||
|
//Unified Single Data Block (USBD) -- Not to be confused with Unified Data Transport (UDT)
|
||||||
|
if (databurst == 0x0B)
|
||||||
|
{
|
||||||
|
//TODO: provide more robust handling at a later date -- ETSI TS 102 361-4 V1.11.1 (2021-01) 6.6.11.3
|
||||||
|
usbd_st = (uint8_t)ConvertBitIntoBytes(&DMR_PDU_bits[0], 4);
|
||||||
|
fprintf (stderr, "%s\n", KYEL);
|
||||||
|
fprintf (stderr, " Unified Single Block Data - ");
|
||||||
|
if (usbd_st == 0) fprintf (stderr, "Location Information Protocol");
|
||||||
|
else if (usbd_st > 8) fprintf (stderr, "Manufacturer Specific Service %d ", usbd_st);
|
||||||
|
else fprintf (stderr, "Reserved %d ", usbd_st);
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the original CRCCorrect back to its original value if the RAS flag was tripped
|
||||||
|
if (is_ras == 1) CRCCorrect = crc_original_validity;
|
||||||
|
|
||||||
|
//start printing relevant fec/crc/ras messages, don't print on idle or MBC continuation blocks (handled in dmr_block.c)
|
||||||
|
if (IrrecoverableErrors == 0 && CRCCorrect == 1 && databurst != 0x09 && databurst != 0x05) ; //fprintf(stderr, "(CRC OK)");
|
||||||
|
|
||||||
|
// if (IrrecoverableErrors == 0 && CRCCorrect == 0 && databurst != 0x09 && databurst != 0x05)
|
||||||
|
// {
|
||||||
|
// fprintf (stderr, "%s", KYEL);
|
||||||
|
// fprintf(stderr, " (FEC OK)");
|
||||||
|
// fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (IrrecoverableErrors != 0 && databurst != 0x09) //&& databurst != 0x05
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf(stderr, " (FEC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
//print whether or not the 'RAS Field' bits are set to indicate RAS enabled (to be verified)
|
||||||
|
if (is_ras == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " -RAS ");
|
||||||
|
//the value of this field seems to always, or usually, be 4, or just R[2] bit is set
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "%X ", BPTCReservedBits);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IrrecoverableErrors == 0 && CRCCorrect == 0 && is_ras == 0 && databurst != 0x09 && databurst != 0x05)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " (CRC ERR) ");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
//print the unified PDU format here, if not slot idle
|
||||||
|
if (opts->payload == 1 && databurst != 0x09)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " DMR PDU Payload ");
|
||||||
|
for (i = 0; i < pdu_len; i++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", DMR_PDU[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug print
|
||||||
|
// if (dbsn) fprintf (stderr, " SN %X", dbsn);
|
||||||
|
// fprintf (stderr, " CRC - EXT %X CMP %X", CRCExtracted, CRCComputed);
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
1094
src/dmr_flco.c
Normal file
1094
src/dmr_flco.c
Normal file
File diff suppressed because it is too large
Load Diff
325
src/dmr_le.c
Normal file
325
src/dmr_le.c
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_le.c
|
||||||
|
* DMR Late Entry MI Fragment Assembly, Procesing, and related Alg functions
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
//gather ambe_fr mi fragments for processing
|
||||||
|
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, char ambe_fr[4][24], char ambe_fr2[4][24], char ambe_fr3[4][24])
|
||||||
|
{
|
||||||
|
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
|
||||||
|
//enforce RC4 due to missing PI header, but with valid SVC Opts
|
||||||
|
//ideally, this would be handled by VC-F single burst, but its not fully reliable compared to this
|
||||||
|
//due to multiple signalling occurring inside of it, depending on system type
|
||||||
|
if (state->M == 0x21)
|
||||||
|
{
|
||||||
|
if (slot == 0 && state->dmr_so & 0x40)
|
||||||
|
{
|
||||||
|
state->payload_algid = 0x21;
|
||||||
|
state->payload_keyid = 0xFF;
|
||||||
|
}
|
||||||
|
if (slot == 1 && state->dmr_soR & 0x40)
|
||||||
|
{
|
||||||
|
state->payload_algidR = 0x21;
|
||||||
|
state->payload_keyidR = 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//collect our fragments and place them into storage
|
||||||
|
state->late_entry_mi_fragment[slot][vc][0] = (uint64_t)ConvertBitIntoBytes(&ambe_fr[3][0], 4);
|
||||||
|
state->late_entry_mi_fragment[slot][vc][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4);
|
||||||
|
state->late_entry_mi_fragment[slot][vc][2] = (uint64_t)ConvertBitIntoBytes(&ambe_fr3[3][0], 4);
|
||||||
|
|
||||||
|
if (vc == 6) dmr_late_entry_mi (opts, state);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
int i, j;
|
||||||
|
int g[3];
|
||||||
|
unsigned char mi_go_bits[24];
|
||||||
|
|
||||||
|
uint64_t mi_test = 0;
|
||||||
|
uint64_t go_test = 0;
|
||||||
|
uint64_t mi_corrected = 0;
|
||||||
|
uint64_t go_corrected = 0;
|
||||||
|
|
||||||
|
mi_test = (state->late_entry_mi_fragment[slot][1][0] << 32L) | (state->late_entry_mi_fragment[slot][2][0] << 28) | (state->late_entry_mi_fragment[slot][3][0] << 24) |
|
||||||
|
(state->late_entry_mi_fragment[slot][1][1] << 20) | (state->late_entry_mi_fragment[slot][2][1] << 16) | (state->late_entry_mi_fragment[slot][3][1] << 12) |
|
||||||
|
(state->late_entry_mi_fragment[slot][1][2] << 8) | (state->late_entry_mi_fragment[slot][2][2] << 4) | (state->late_entry_mi_fragment[slot][3][2] << 0);
|
||||||
|
|
||||||
|
go_test = (state->late_entry_mi_fragment[slot][4][0] << 32L) | (state->late_entry_mi_fragment[slot][5][0] << 28) | (state->late_entry_mi_fragment[slot][6][0] << 24) |
|
||||||
|
(state->late_entry_mi_fragment[slot][4][1] << 20) | (state->late_entry_mi_fragment[slot][5][1] << 16) | (state->late_entry_mi_fragment[slot][6][1] << 12) |
|
||||||
|
(state->late_entry_mi_fragment[slot][4][2] << 8) | (state->late_entry_mi_fragment[slot][5][2] << 4) | (state->late_entry_mi_fragment[slot][6][2] << 0);
|
||||||
|
|
||||||
|
for (j = 0; j < 3; j++)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
mi_go_bits[i] = (( mi_test << (i+j*12) ) & 0x800000000) >> 35;
|
||||||
|
mi_go_bits[i+12] = (( go_test << (i+j*12) ) & 0x800000000) >> 35;
|
||||||
|
}
|
||||||
|
//execute golay decode and assign pass or fail to g
|
||||||
|
if ( Golay_24_12_decode(mi_go_bits) ) g[j] = 1;
|
||||||
|
else g[j] = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
mi_corrected = mi_corrected << 1;
|
||||||
|
mi_corrected |= mi_go_bits[i];
|
||||||
|
go_corrected = go_corrected << 1;
|
||||||
|
go_corrected |= mi_go_bits[i+12];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int mi_final = 0;
|
||||||
|
mi_final = (mi_corrected >> 4) & 0xFFFFFFFF;
|
||||||
|
|
||||||
|
if (g[0] && g[1] && g[2])
|
||||||
|
{
|
||||||
|
if (slot == 0 && state->payload_algid != 0)
|
||||||
|
{
|
||||||
|
if (state->payload_mi != mi_final)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_mi, mi_final);
|
||||||
|
state->payload_mi = mi_final;
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
//run afterwards, or le verification won't match up properly
|
||||||
|
if (state->payload_algid != 0x21)
|
||||||
|
{
|
||||||
|
LFSR64 (state);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (slot == 1 && state->payload_algidR != 0)
|
||||||
|
{
|
||||||
|
if (state->payload_miR != mi_final)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_miR, mi_final);
|
||||||
|
state->payload_miR = mi_final;
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
//run afterwards, or le verification won't match up properly
|
||||||
|
if (state->payload_algidR != 0x21)
|
||||||
|
{
|
||||||
|
LFSR64 (state);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
state->dropL = 256;
|
||||||
|
|
||||||
|
if (state->K1 != 0)
|
||||||
|
{
|
||||||
|
state->DMRvcL = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->payload_algid == 0x21)
|
||||||
|
{
|
||||||
|
LFSR (state);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
//LFSR64 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
|
||||||
|
|
||||||
|
}
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
{
|
||||||
|
state->dropR = 256;
|
||||||
|
|
||||||
|
if (state->K1 != 0)
|
||||||
|
{
|
||||||
|
state->DMRvcR = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->payload_algidR == 0x21)
|
||||||
|
{
|
||||||
|
LFSR (state);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
//LFSR64 carried out after LE verification in order to keep it from constantly resetting the MI to the previous value
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void dmr_alg_reset (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
state->dropL = 256;
|
||||||
|
state->dropR = 256;
|
||||||
|
state->DMRvcL = 0;
|
||||||
|
state->DMRvcR = 0;
|
||||||
|
state->payload_miP = 0;
|
||||||
|
state->payload_miN = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle Single Burst (Voice Burst F) or Reverse Channel Signalling
|
||||||
|
void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
uint8_t sbrc_interleaved[32];
|
||||||
|
uint8_t sbrc_return[32];
|
||||||
|
memset (sbrc_interleaved, 0, sizeof(sbrc_interleaved));
|
||||||
|
memset (sbrc_return, 0, sizeof(sbrc_return));
|
||||||
|
uint32_t irr_err = 0;
|
||||||
|
uint32_t sbrc_hex = 0;
|
||||||
|
uint16_t crc_extracted = 7777;
|
||||||
|
uint16_t crc_computed = 9999;
|
||||||
|
uint8_t crc_okay = 0;
|
||||||
|
|
||||||
|
// 9.3.2 Pre-emption and power control Indicator (PI)
|
||||||
|
// 0 - The embedded signalling carries information associated to the same logical channel or the Null embedded message
|
||||||
|
// 1 - The embedded signalling carries RC information associated to the other logical channel
|
||||||
|
|
||||||
|
for(i = 0; i < 32; i++) sbrc_interleaved[i] = state->dmr_embedded_signalling[slot][5][i + 8];
|
||||||
|
//power == 0 should be single burst
|
||||||
|
if (power == 0) irr_err = BPTC_16x2_Extract_Data(sbrc_interleaved, sbrc_return, 0);
|
||||||
|
//power == 1 should be reverse channel -- still need to check the interleave inside of BPTC
|
||||||
|
if (power == 1) irr_err = BPTC_16x2_Extract_Data(sbrc_interleaved, sbrc_return, 1);
|
||||||
|
//bad emb burst, never set a valid power indicator value (probably 9)
|
||||||
|
if (power > 1) goto SBRC_END;
|
||||||
|
|
||||||
|
//RC Channel CRC 7 Mask = 0x7A; CRC bits are used as privacy indicators on
|
||||||
|
//Single Voice Burst F (see below), other moto values seem to exist there as well
|
||||||
|
if (power == 1) //RC
|
||||||
|
{
|
||||||
|
crc_extracted = 0;
|
||||||
|
for (i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
crc_extracted = crc_extracted << 1;
|
||||||
|
crc_extracted = crc_extracted | sbrc_return[i+4];
|
||||||
|
}
|
||||||
|
crc_extracted = crc_extracted ^ 0x7A;
|
||||||
|
crc_computed = crc7((uint8_t *) sbrc_return, 11);
|
||||||
|
if (crc_extracted == crc_computed) crc_okay = 1;
|
||||||
|
}
|
||||||
|
else crc_okay = 1; //SB
|
||||||
|
|
||||||
|
for(i = 0; i < 11; i++)
|
||||||
|
{
|
||||||
|
sbrc_hex = sbrc_hex << 1;
|
||||||
|
sbrc_hex |= sbrc_return[i] & 1; //sbrc_return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->payload == 1) //hide the sb/rc behind the payload printer, won't be useful to most people
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n %s", KCYN);
|
||||||
|
if (power == 0) fprintf (stderr, " SB: ");
|
||||||
|
if (power == 1) fprintf (stderr, " RC: ");
|
||||||
|
for(i = 0; i < 11; i++)
|
||||||
|
fprintf (stderr, "%d", sbrc_return[i]);
|
||||||
|
fprintf (stderr, " - %03X", sbrc_hex);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
if (crc_okay == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " (CRC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
fprintf (stderr, " CRC EXT %02X, CRC CMP %02X", crc_extracted, crc_computed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t sbrc_opcode = sbrc_hex;
|
||||||
|
uint8_t alg = sbrc_hex & 0x7;
|
||||||
|
uint8_t key = (sbrc_hex >> 3) & 0xFF;
|
||||||
|
uint8_t cap_site = (sbrc_hex >> 4) & 0x7; //signalling on Cap+ when voice errors (non voice VC6 is present)
|
||||||
|
|
||||||
|
if (1 == 1) //opts->payload == 1
|
||||||
|
{
|
||||||
|
if (irr_err != 0) ; //fprintf (stderr, "\n %s SLOT %d SB/RC (FEC ERR) %d %s \n", KRED, slot, irr_err, KNRM);
|
||||||
|
if (irr_err == 0)
|
||||||
|
{
|
||||||
|
if (sbrc_hex == 0) ; //NULL
|
||||||
|
//else if (placeholder for future conditions)
|
||||||
|
// {
|
||||||
|
// placeholder for future conditions
|
||||||
|
// }
|
||||||
|
else //Finally have a consistent set-up for this
|
||||||
|
{
|
||||||
|
//the signalling here is always presents on good voice frames, if errs are [3][2] then its not a VC6 voice frame, but some other 'hidden' frame
|
||||||
|
if (slot == 0 && state->errs < 3)
|
||||||
|
{
|
||||||
|
if (state->dmr_so & 0x40 && key != 0 && state->payload_keyid == 0)
|
||||||
|
{
|
||||||
|
//if we aren't forcing a particular alg or privacy key set
|
||||||
|
if (state->M == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s ", KYEL);
|
||||||
|
fprintf (stderr, " Slot 1");
|
||||||
|
fprintf (stderr, " DMR LE SB ALG ID: %X KEY ID: %0X", alg + 0x20, key);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
|
||||||
|
state->payload_keyid = key;
|
||||||
|
state->payload_algid = alg + 0x20; //assuming DMRA approved alg values (moto patent)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//the signalling here is always presents on good voice frames, if errs are [3][2] then its not a VC6 voice frame, but some other 'hidden' frame
|
||||||
|
if (slot == 1 && state->errsR < 3)
|
||||||
|
{
|
||||||
|
if (state->dmr_soR & 0x40 && key != 0 && state->payload_keyidR == 0)
|
||||||
|
{
|
||||||
|
//if we aren't forcing a particular alg or privacy key set
|
||||||
|
if (state->M == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s ", KYEL);
|
||||||
|
fprintf (stderr, " Slot 1");
|
||||||
|
fprintf (stderr, " DMR LE SB ALG ID: %X KEY ID: %0X", alg + 0x20, key);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
|
||||||
|
state->payload_keyidR = key;
|
||||||
|
state->payload_algidR = alg + 0x20; //assuming DMRA approved alg values (moto patent)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SBRC_END:
|
||||||
|
|
||||||
|
//'DSP' output to file -- only RC, or RC and SB?
|
||||||
|
if (power == 1 && opts->use_dsp_output == 1 && sbrc_hex != 0) //if not NULL
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 99 ", slot+1); //'99' is RC designation value
|
||||||
|
int k = 0;
|
||||||
|
for (i = 0; i < 24; i++) //12 bytes, SB or RC
|
||||||
|
{
|
||||||
|
//check to see if k++ starts at zero, or at 1
|
||||||
|
int sbrc_nib = (state->dmr_embedded_signalling[slot][5][k++] << 3) | (state->dmr_embedded_signalling[slot][5][k++] << 2) | (state->dmr_embedded_signalling[slot][5][k++] << 1) | (state->dmr_embedded_signalling[slot][5][k++] << 0);
|
||||||
|
fprintf (pFile, "%X", sbrc_nib);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
679
src/dmr_ms.c
Normal file
679
src/dmr_ms.c
Normal file
@@ -0,0 +1,679 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_ms.c
|
||||||
|
* DMR MS/Simplex/Direct Mode Voice Handling and Data Gathering Routines
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
#include "dmr_const.h"
|
||||||
|
|
||||||
|
//A subroutine for processing MS voice
|
||||||
|
void dmrMS (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i, j, k, l, dibit;
|
||||||
|
int *dibit_p;
|
||||||
|
char ambe_fr[4][24];
|
||||||
|
char ambe_fr2[4][24];
|
||||||
|
char ambe_fr3[4][24];
|
||||||
|
char ambe_fr4[4][24];
|
||||||
|
|
||||||
|
//memcpy of ambe_fr for late entry
|
||||||
|
char m1[4][24];
|
||||||
|
char m2[4][24];
|
||||||
|
char m3[4][24];
|
||||||
|
|
||||||
|
const int *w, *x, *y, *z;
|
||||||
|
char sync[25];
|
||||||
|
char syncdata[48];
|
||||||
|
char EmbeddedSignalling[16];
|
||||||
|
|
||||||
|
//cach
|
||||||
|
char cachdata[25];
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
//cach tact bits
|
||||||
|
uint8_t tact_bits[7];
|
||||||
|
|
||||||
|
uint8_t emb_ok = 0;
|
||||||
|
uint8_t tact_okay = 0;
|
||||||
|
uint8_t cach_err = 0;
|
||||||
|
uint8_t EmbeddedSignallingOk = 0;
|
||||||
|
|
||||||
|
uint8_t internalslot;
|
||||||
|
uint8_t vc;
|
||||||
|
|
||||||
|
//assign as nonsensical numbers
|
||||||
|
uint8_t cc = 25;
|
||||||
|
uint8_t power = 9; //power and pre-emption indicator
|
||||||
|
uint8_t lcss = 9;
|
||||||
|
|
||||||
|
//dummy bits to pass to dburst for link control
|
||||||
|
uint8_t dummy_bits[196];
|
||||||
|
memset (dummy_bits, 0, sizeof (dummy_bits));
|
||||||
|
|
||||||
|
//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;
|
||||||
|
}
|
||||||
|
|
||||||
|
vc = 2;
|
||||||
|
|
||||||
|
short int skipcount = 0;
|
||||||
|
|
||||||
|
//Hardset variables for MS/Mono
|
||||||
|
state->currentslot = 0; //0
|
||||||
|
|
||||||
|
//Note: Manual dibit inversion required here since I didn't seperate inverted return from normal return in framesync,
|
||||||
|
//so getDibit doesn't know to invert it before it gets here
|
||||||
|
|
||||||
|
for (j = 0; j < 6; j++) {
|
||||||
|
state->dmrburstL = 16;
|
||||||
|
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
|
||||||
|
cachdata[i] = dibit;
|
||||||
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
tact_bits[i] = cachdata[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
tact_okay = 0;
|
||||||
|
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
||||||
|
if (tact_okay != 1)
|
||||||
|
{
|
||||||
|
//do nothing since we aren't loop locked forever.
|
||||||
|
}
|
||||||
|
|
||||||
|
//internalslot = tact_bits[1];
|
||||||
|
internalslot = 0;
|
||||||
|
|
||||||
|
//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) & 3;
|
||||||
|
|
||||||
|
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 = getDibit(opts, state);
|
||||||
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// load the superframe to do embedded signal processing
|
||||||
|
if(vc > 1) //grab on vc1 values 2-5 B C D and E
|
||||||
|
{
|
||||||
|
state->dmr_embedded_signalling[internalslot][vc-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
state->dmr_embedded_signalling[internalslot][vc-1][i*2+1] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sync[24] = 0;
|
||||||
|
|
||||||
|
//test for RC sync pattern in MS sourced audio
|
||||||
|
if ( strcmp (sync, DMR_RC_DATA_SYNC) == 0)
|
||||||
|
{
|
||||||
|
state->dmr_ms_rc = 1;
|
||||||
|
fprintf (stderr, "%s ", getTime());
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
|
||||||
|
}
|
||||||
|
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
|
||||||
|
|
||||||
|
fprintf (stderr, "| RC ");
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < 8; i++) EmbeddedSignalling[i] = syncdata[i];
|
||||||
|
for(i = 0; i < 8; i++) EmbeddedSignalling[i + 8] = syncdata[i + 40];
|
||||||
|
|
||||||
|
EmbeddedSignallingOk = -1;
|
||||||
|
if(QR_16_7_6_decode(EmbeddedSignalling))
|
||||||
|
{
|
||||||
|
EmbeddedSignallingOk = 1;
|
||||||
|
cc = ((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
|
||||||
|
power = EmbeddedSignalling[4];
|
||||||
|
lcss = ((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//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) & 3;
|
||||||
|
|
||||||
|
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) & 3;
|
||||||
|
|
||||||
|
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++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//'DSP' output to file
|
||||||
|
if (opts->use_dsp_output == 1)
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 10 ", state->currentslot+1); //0x10 for "voice burst", forced to slot 1
|
||||||
|
for (i = 6; i < 72; i++) //33 bytes, no CACH
|
||||||
|
{
|
||||||
|
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", dsp_byte);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
state->dmr_ms_mode = 1;
|
||||||
|
|
||||||
|
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
|
||||||
|
|
||||||
|
//copy ambe_fr frames first, running process mbe will correct them,
|
||||||
|
//but this also leads to issues extracting good le mi values when
|
||||||
|
//we go to do correction on them there too
|
||||||
|
memcpy (m1, ambe_fr, sizeof(m1));
|
||||||
|
memcpy (m2, ambe_fr2, sizeof(m2));
|
||||||
|
memcpy (m3, ambe_fr3, sizeof(m3));
|
||||||
|
|
||||||
|
if (state->dmr_ms_rc == 0)
|
||||||
|
{
|
||||||
|
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 on tdma direct
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vc == 6)
|
||||||
|
{
|
||||||
|
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
||||||
|
//check the single burst/reverse channel opportunity
|
||||||
|
dmr_sbrc (opts, state, power);
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
dmr_alg_refresh (opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
//collect the mi fragment
|
||||||
|
dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
|
||||||
|
|
||||||
|
//errors in ms/mono since we skip the other slot
|
||||||
|
// cach_err = dmr_cach (opts, state, cachdata);
|
||||||
|
|
||||||
|
//update voice sync time for trunking purposes (particularly Con+)
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
|
||||||
|
vc++;
|
||||||
|
|
||||||
|
//reset emb components
|
||||||
|
cc = 25;
|
||||||
|
power = 9; //power and pre-emption indicator
|
||||||
|
lcss = 9;
|
||||||
|
|
||||||
|
//this is necessary because we need to skip and collect dibits, not just skip them
|
||||||
|
if (vc > 6) goto END;
|
||||||
|
|
||||||
|
skipDibit (opts, state, 144); //skip to next tdma channel
|
||||||
|
state->dmr_ms_rc = 0;
|
||||||
|
|
||||||
|
//since we are in a loop, run ncursesPrinter here
|
||||||
|
if (opts->use_ncurses_terminal == 1)
|
||||||
|
{
|
||||||
|
ncursesPrinter(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end loop
|
||||||
|
|
||||||
|
END:
|
||||||
|
//get first half payload dibits and store them in the payload for the next repitition
|
||||||
|
skipDibit (opts, state, 144); //should we have two of these?
|
||||||
|
|
||||||
|
//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) & 3;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//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];
|
||||||
|
char ambe_fr2[4][24];
|
||||||
|
char ambe_fr3[4][24];
|
||||||
|
char ambe_fr4[4][24]; //shim for crappy tdma direct, need more samples to see if this is really a necessary shim or not
|
||||||
|
|
||||||
|
//memcpy of ambe_fr for late entry
|
||||||
|
char m1[4][24];
|
||||||
|
char m2[4][24];
|
||||||
|
char m3[4][24];
|
||||||
|
|
||||||
|
const int *w, *x, *y, *z;
|
||||||
|
char sync[25];
|
||||||
|
char syncdata[48];
|
||||||
|
|
||||||
|
//cach
|
||||||
|
char cachdata[25];
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
int mutecurrentslot;
|
||||||
|
int msMode;
|
||||||
|
char cc[4];
|
||||||
|
char EmbeddedSignalling[16];
|
||||||
|
|
||||||
|
uint8_t emb_ok = 0;
|
||||||
|
uint8_t tact_okay = 0;
|
||||||
|
uint8_t cach_err = 0;
|
||||||
|
uint8_t EmbeddedSignallingOk = 0;
|
||||||
|
|
||||||
|
//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;
|
||||||
|
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) & 3;
|
||||||
|
}
|
||||||
|
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) & 3;
|
||||||
|
}
|
||||||
|
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) & 3;
|
||||||
|
}
|
||||||
|
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) & 3;
|
||||||
|
}
|
||||||
|
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) & 3;
|
||||||
|
}
|
||||||
|
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++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//'DSP' output to file
|
||||||
|
if (opts->use_dsp_output == 1)
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 10 ", state->currentslot+1); //0x10 for "voice burst", force to slot 1
|
||||||
|
for (i = 6; i < 72; i++) //33 bytes, no CACH
|
||||||
|
{
|
||||||
|
int dsp_byte = (state->dmr_stereo_payload[i*2] << 2) | state->dmr_stereo_payload[i*2 + 1];
|
||||||
|
fprintf (pFile, "%X", dsp_byte);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s ", getTime());
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
|
||||||
|
}
|
||||||
|
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
|
||||||
|
|
||||||
|
fprintf (stderr, "| VC* ");
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
//alg reset
|
||||||
|
//dmr_alg_reset (opts, state);
|
||||||
|
|
||||||
|
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
|
||||||
|
|
||||||
|
//copy ambe_fr frames first, running process mbe will correct them,
|
||||||
|
//but this also leads to issues extracting good le mi values when
|
||||||
|
//we go to do correction on them there too
|
||||||
|
memcpy (m1, ambe_fr, sizeof(m1));
|
||||||
|
memcpy (m2, ambe_fr2, sizeof(m2));
|
||||||
|
memcpy (m3, ambe_fr3, sizeof(m3));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//collect the mi fragment
|
||||||
|
dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
|
||||||
|
|
||||||
|
//errors due to skipping other slot
|
||||||
|
// cach_err = dmr_cach (opts, state, cachdata);
|
||||||
|
|
||||||
|
skipDibit (opts, state, 144); //skip to next TDMA slot
|
||||||
|
dmrMS (opts, state); //bootstrap into full TDMA frame
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//simplied to a simple data collector, and then passed on to dmr_data_sync for the usual processing
|
||||||
|
void dmrMSData (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
int i, b, c;
|
||||||
|
int dibit;
|
||||||
|
int *dibit_p;
|
||||||
|
|
||||||
|
//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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//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) & 3;
|
||||||
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 54; i++)
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s ", getTime());
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
|
||||||
|
}
|
||||||
|
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
|
||||||
|
|
||||||
|
|
||||||
|
sprintf(state->slot1light, "%s", "");
|
||||||
|
sprintf(state->slot2light, "%s", "");
|
||||||
|
|
||||||
|
//process data
|
||||||
|
state->dmr_stereo = 1;
|
||||||
|
state->dmr_ms_mode = 1;
|
||||||
|
|
||||||
|
dmr_data_sync (opts, state);
|
||||||
|
|
||||||
|
state->dmr_stereo = 0;
|
||||||
|
state->dmr_ms_mode = 0;
|
||||||
|
state->directmode = 0; //flag off
|
||||||
|
|
||||||
|
//should just be loaded in the dmr_payload_buffer instead now
|
||||||
|
for (i = 0; i < 144; i++) //66
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
|
}
|
||||||
|
//CACH + First Half Payload = 12 + 54
|
||||||
|
for (i = 0; i < 66; i++) //66
|
||||||
|
{
|
||||||
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
534
src/dmr_pdu.c
Normal file
534
src/dmr_pdu.c
Normal file
@@ -0,0 +1,534 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_bs.c
|
||||||
|
* DMR Data (1/2, 3/4, 1) PDU Decoding
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
char * getTimeL(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 * getDateL(void) {
|
||||||
|
char datename[120];
|
||||||
|
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 dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||||
|
{
|
||||||
|
|
||||||
|
uint8_t message_len = 0;
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
uint8_t blocks = state->data_header_blocks[slot];
|
||||||
|
uint8_t padding = state->data_header_padding[slot];
|
||||||
|
uint8_t lrrp_conf = 0;
|
||||||
|
|
||||||
|
//check for more available flag info, etc on these prior to running
|
||||||
|
if (DMR_PDU[0] == 0x01) dmr_locn (opts, state, block_len, DMR_PDU);
|
||||||
|
else dmr_lrrp (opts, state, block_len, DMR_PDU);
|
||||||
|
|
||||||
|
//maybe one day we will have more things to do here
|
||||||
|
state->data_conf_data[slot] = 0; //flag off confirmed data after processing it
|
||||||
|
state->data_p_head[slot] = 0; //flag off prop_head data after processing it
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//The contents of this function are mostly my reversed engineered efforts by observing DSDPlus output and matching data bytes
|
||||||
|
//combined with a few external sources such as OK-DMR for some token values and extra data values (rad and alt)
|
||||||
|
//this is by no means an extensive LRRP list and is prone to error (unless somebody has the manual or something)
|
||||||
|
void dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
uint16_t message_len = 0;
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
uint8_t blocks = state->data_header_blocks[slot];
|
||||||
|
uint8_t padding = state->data_header_padding[slot];
|
||||||
|
uint8_t lrrp_confidence = 0; //variable to increment based on number of tokens found, the more, the higher the confidence level
|
||||||
|
|
||||||
|
//source/dest and ports
|
||||||
|
uint32_t source = 0;
|
||||||
|
uint32_t dest = 0;
|
||||||
|
uint16_t port_s = 0;
|
||||||
|
uint16_t port_d = 0;
|
||||||
|
|
||||||
|
//time
|
||||||
|
uint16_t year = 0;
|
||||||
|
uint16_t month = 0;
|
||||||
|
uint16_t day = 0;
|
||||||
|
uint16_t hour = 0;
|
||||||
|
uint16_t minute = 0;
|
||||||
|
uint16_t second = 0;
|
||||||
|
|
||||||
|
//lat-long-radius-alt
|
||||||
|
uint32_t lat = 0;
|
||||||
|
uint32_t lon = 0;
|
||||||
|
uint16_t rad = 0; //or Azimuth in Circle 3D?
|
||||||
|
uint8_t alt = 0;
|
||||||
|
double lat_unit = (double)180/(double)4294967295;
|
||||||
|
double lon_unit = (double)360/(double)4294967295;
|
||||||
|
double lat_fin = 0.0; //calculated values
|
||||||
|
double lon_fin = 0.0; //calculated values
|
||||||
|
int lat_sign = 1; //positive 1, or negative 1
|
||||||
|
int lon_sign = 1; //positive 1, or negative 1
|
||||||
|
|
||||||
|
//speed/velocity
|
||||||
|
uint16_t vel = 0;
|
||||||
|
double velocity = 0;
|
||||||
|
uint8_t vel_set = 0;
|
||||||
|
|
||||||
|
//track, direction, degrees
|
||||||
|
uint8_t degrees = 0;
|
||||||
|
uint8_t deg_set = 0;
|
||||||
|
|
||||||
|
char deg_glyph[4];
|
||||||
|
sprintf (deg_glyph, "%s", "°");
|
||||||
|
// sprintf (deg_glyph, "%s", "d");
|
||||||
|
|
||||||
|
//triggered information report
|
||||||
|
uint8_t report = 0;
|
||||||
|
uint8_t pot_report = 0; //potential report by finding 0x0D and backtracking a few bytes
|
||||||
|
|
||||||
|
|
||||||
|
//start looking for tokens (using my best understanding of the ok-dmr library xml and python files)
|
||||||
|
for (i = 0; i < ( (blocks*block_len) - (padding+4) ); i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
switch(DMR_PDU[i]){
|
||||||
|
case 0x0C: //LRRP_TriggeredLocationReport
|
||||||
|
if (source == 0)
|
||||||
|
{
|
||||||
|
source = (DMR_PDU[i+1] << 16 ) + (DMR_PDU[i+2] << 8) + DMR_PDU[i+3];
|
||||||
|
dest = (DMR_PDU[i+5] << 16 ) + (DMR_PDU[i+6] << 8) + DMR_PDU[i+7];
|
||||||
|
port_s = (DMR_PDU[i+8] << 8) + DMR_PDU[i+9];
|
||||||
|
port_d = (DMR_PDU[i+10] << 8) + DMR_PDU[i+11];
|
||||||
|
i += 11;
|
||||||
|
lrrp_confidence++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//may need more 'report' types to trigger various scenarios
|
||||||
|
//disable all but 0x1F if issues or falsing happens often
|
||||||
|
//case 0x1D: //ARRP_TriggeredInformationAnswer_NCDT
|
||||||
|
//case 0x1E: //ARRP_TriggeredInformationReport = FALSE?
|
||||||
|
//case 0x25: //ARRP_UnsolicitedInformationReport_NCDT = (0x25, True, "")
|
||||||
|
//case 0x26: //ARRP_UnsolicitedInformationReport_NCDT
|
||||||
|
//case 0x13: //LRRP_UnsolicitedLocationReport_NCDT
|
||||||
|
//case 0x15: //LRRP_LocationProtocolReport_NCDT
|
||||||
|
//case 0x21: //ARRP_TriggeredInformationStopRequest_NCDT
|
||||||
|
case 0x1F: //ARRP_TriggeredInformationReport_NCDT
|
||||||
|
if (report == 0)
|
||||||
|
{
|
||||||
|
report = DMR_PDU[i];
|
||||||
|
i += 1;
|
||||||
|
lrrp_confidence++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//0x0D always seems to follow 0x1F or other 'report' types two/three bytes later
|
||||||
|
case 0x0D: //message len indicator //LRRP_TriggeredLocationReport_NCDT
|
||||||
|
if (report > 0 && message_len == 0)
|
||||||
|
{
|
||||||
|
message_len = DMR_PDU[i+1];
|
||||||
|
i += 1;
|
||||||
|
lrrp_confidence++;
|
||||||
|
}
|
||||||
|
else if (message_len == 0)
|
||||||
|
{
|
||||||
|
if (i > 3)
|
||||||
|
{
|
||||||
|
if (DMR_PDU[i-3] > 0) pot_report = DMR_PDU[i-3];
|
||||||
|
if (pot_report < 0x27)
|
||||||
|
{
|
||||||
|
report = pot_report;
|
||||||
|
message_len = DMR_PDU[i+1];
|
||||||
|
i += 1;
|
||||||
|
//no lrrp_confidence on speculative reporting, but will print it as such.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//RESULT TOKEN or Request ID!
|
||||||
|
case 0x22: //same comment as below, observed followed the message len
|
||||||
|
case 0x23: //this value has been seen after the 0x0D message len, appears to be a 2-byte value, was tripping a false on time stamp
|
||||||
|
i += 2;
|
||||||
|
break;
|
||||||
|
//answer and report tokens
|
||||||
|
case 0x51: //circle-2d
|
||||||
|
case 0x54: //circle-3d
|
||||||
|
case 0x55: //circle-3d
|
||||||
|
if (message_len > 0 && lat == 0)
|
||||||
|
{
|
||||||
|
// lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
|
// lon = ( ( ((DMR_PDU[i+5] & 0x7F ) << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
|
lat = ( ( (DMR_PDU[i+1] << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
|
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
|
rad = (DMR_PDU[i+9] << 8) + DMR_PDU[i+10];
|
||||||
|
i += 10;
|
||||||
|
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
||||||
|
else lat = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x34: //Time Interval Periodic Trigger (timestamp)
|
||||||
|
case 0x35: //Time Interval Periodic Trigger (timestamp)
|
||||||
|
if (message_len > 0 && year == 0)
|
||||||
|
{
|
||||||
|
year = (DMR_PDU[i+1] << 6) + (DMR_PDU[i+2] >> 2);
|
||||||
|
month = ((DMR_PDU[i+2] & 0x3) << 2) + ((DMR_PDU[i+3] & 0xC0) >> 6);
|
||||||
|
day = ((DMR_PDU[i+3] & 0x30) >> 1) + ((DMR_PDU[i+3] & 0x0E) >> 1);
|
||||||
|
hour = ((DMR_PDU[i+3] & 0x01) << 4) + ((DMR_PDU[i+4] & 0xF0) >> 4);
|
||||||
|
minute = ((DMR_PDU[i+4] & 0x0F) << 2) + ((DMR_PDU[i+5] & 0xC0) >> 6);
|
||||||
|
second = (DMR_PDU[i+5] & 0x3F);
|
||||||
|
i += 5;
|
||||||
|
//sanity check
|
||||||
|
if (year > 2000 && year <= 2025) lrrp_confidence++;
|
||||||
|
if (year > 2025 || year < 2000) year = 0; //needs future proofing
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x66: //point-2d
|
||||||
|
if (message_len > 0 && lat == 0)
|
||||||
|
{
|
||||||
|
// lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
|
// lon = ( ( ((DMR_PDU[i+5] & 0x7F ) << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
|
lat = ( ( (DMR_PDU[i+1] << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
|
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
|
i += 8;
|
||||||
|
lrrp_confidence++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x69: //point-3d
|
||||||
|
case 0x6A: //point-3d
|
||||||
|
if (message_len > 0 && lat == 0)
|
||||||
|
{
|
||||||
|
// lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
|
// lon = ( ( ((DMR_PDU[i+5] & 0x7F ) << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
|
lat = ( ( (DMR_PDU[i+1] << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
|
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
|
alt = DMR_PDU[i+9];
|
||||||
|
i += 9;
|
||||||
|
if (lat > 0 && lon > 0) lrrp_confidence++;
|
||||||
|
else lat = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x36: //protocol-version
|
||||||
|
i += 1;
|
||||||
|
//lrrp_confidence++;
|
||||||
|
break;
|
||||||
|
//speed/velocity
|
||||||
|
case 0x70: //speed-virt
|
||||||
|
case 0x6C: //speed-hor
|
||||||
|
if (message_len > 0 && vel_set == 0)
|
||||||
|
{
|
||||||
|
vel = (DMR_PDU[i+1] << 8) + DMR_PDU[i+2]; //don't think this is meant to be shifted, but doing it anyways for comparison
|
||||||
|
velocity = ( ((double)( (DMR_PDU[i+1] ) + DMR_PDU[i+2] )) / ( (double)128));
|
||||||
|
vel_set = 1;
|
||||||
|
i += 2;
|
||||||
|
lrrp_confidence++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x56: //direction-hor
|
||||||
|
if (message_len > 0 && deg_set == 0)
|
||||||
|
{
|
||||||
|
degrees = DMR_PDU[i+1] * 2;
|
||||||
|
deg_set = 1;
|
||||||
|
i += 1;
|
||||||
|
lrrp_confidence++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//below all unknown
|
||||||
|
case 0x37: //result
|
||||||
|
case 0x38: //result
|
||||||
|
case 0x39: //result 'operations error'? attributes 0x22?
|
||||||
|
case 0x6B: //unknown-uint8
|
||||||
|
case 0x65: //lev-conf
|
||||||
|
default:
|
||||||
|
//do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (report && message_len > 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d Octets", lrrp_confidence, message_len);
|
||||||
|
if (lrrp_confidence >= 3) //find the sweet magical number
|
||||||
|
{
|
||||||
|
//now we can open our lrrp file and write to it as well
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
if (opts->lrrp_file_output == 1)
|
||||||
|
{
|
||||||
|
//open file by name that is supplied in the ncurses terminal, or cli
|
||||||
|
pFile = fopen (opts->lrrp_out_file, "a");
|
||||||
|
//write current date/time if not present in LRRP data
|
||||||
|
if (!year) fprintf (pFile, "%s\t", getDateL() ); //current date, only add this IF no included timestamp in LRRP data?
|
||||||
|
if (!year) fprintf (pFile, "%s\t", getTimeL() ); //current timestamp, only add this IF no included timestamp in LRRP data?
|
||||||
|
if (year) fprintf (pFile, "%04d/%02d/%02d\t%02d:%02d:%02d\t", year, month, day, hour, minute, second); //add timestamp from decoded audio if available
|
||||||
|
//write data header source if not available in lrrp data
|
||||||
|
if (!source) fprintf (pFile, "%08lld\t", state->dmr_lrrp_source[state->currentslot]); //source address from data header
|
||||||
|
if (source) fprintf (pFile, "%08d\t", source); //add source form decoded audio if available, else its from the header
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pot_report)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
||||||
|
}
|
||||||
|
if (report)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " Report: 0x%02X", report);
|
||||||
|
if (report == 0x1F) fprintf (stderr, " ARRP_TriggeredInformationReport_NCDT "); //customize later when more is learned
|
||||||
|
if (report == 0x21) fprintf (stderr, " ARRP_TriggeredInformationStopRequest_NCDT ");
|
||||||
|
if (report == 0x22) fprintf (stderr, " ARRP_TriggeredInformationStopAnswer ");
|
||||||
|
if (report == 0x25) fprintf (stderr, " ARRP_UnsolicitedInformationReport_NCDT ");
|
||||||
|
if (report == 0x26) fprintf (stderr, " ARRP_UnsolicitedInformationReport_NCDT ");
|
||||||
|
if (report == 0x27) fprintf (stderr, " ARRP_InformationProtocolRequest_NCDT ");
|
||||||
|
if (report == 0x13) fprintf (stderr, " LRRP_UnsolicitedLocationReport_NCDT ");
|
||||||
|
if (report == 0x15) fprintf (stderr, " LRRP_UnsolicitedLocationReport_NCDT ");
|
||||||
|
|
||||||
|
}
|
||||||
|
if (source)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " Source: %08d - %04d", source, port_s);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " Destination: %08d - %04d", dest, port_d);
|
||||||
|
|
||||||
|
}
|
||||||
|
if (year)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " LRRP - Time: ");
|
||||||
|
fprintf (stderr, " %04d.%02d.%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
|
||||||
|
|
||||||
|
}
|
||||||
|
if (lat)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
//need to check these new calcs for accuracy accross the globe, both lat and lon
|
||||||
|
|
||||||
|
//two's compliment-ish testing on these bytes
|
||||||
|
if (lat & 0x80000000) //8
|
||||||
|
{
|
||||||
|
lat = lat & 0x7FFFFFFF;
|
||||||
|
lat_sign = -1;
|
||||||
|
// lat = 0x80000000 - lat; //not sure why this doesn't work here like it does on lon, extra bit?
|
||||||
|
}
|
||||||
|
if (lon & 0x80000000)
|
||||||
|
{
|
||||||
|
lon = lon & 0x7FFFFFFF;
|
||||||
|
lon_sign = -1;
|
||||||
|
lon = 0x80000000 - lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
lat_fin = (double)lat * lat_unit * lat_sign;
|
||||||
|
lon_fin = (double)lon * lon_unit * lon_sign;
|
||||||
|
|
||||||
|
fprintf (stderr, " LRRP - Lat: %.5lf", lat_fin);
|
||||||
|
fprintf (stderr, " Lon: %.5lf", lon_fin);
|
||||||
|
fprintf (stderr, " (%.5lf, %.5lf)", lat_fin , lon_fin);
|
||||||
|
|
||||||
|
if (opts->lrrp_file_output == 1)
|
||||||
|
{
|
||||||
|
fprintf (pFile, "%.5lf\t", lat_fin);
|
||||||
|
fprintf (pFile, "%.5lf\t", lon_fin);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (rad)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " LRRP - Radius: %dm", rad); //unsure of 'units' or calculation for radius (meters?)
|
||||||
|
}
|
||||||
|
if (alt)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " LRRP - Altitude: %dm", alt); //unsure of 'units' or calculation for alt (meters?)
|
||||||
|
}
|
||||||
|
if (vel_set)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " LRRP - Velocity: %.4lf m/s %.4lf km/h %.4lf mph", velocity, (3.6 * velocity), (2.2369 * velocity));
|
||||||
|
if (opts->lrrp_file_output == 1) fprintf (pFile, "%.3lf\t ", (velocity * 3.6) );
|
||||||
|
}
|
||||||
|
if (deg_set)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " LRRP - Track: %d%s", degrees, deg_glyph);
|
||||||
|
if (opts->lrrp_file_output == 1) fprintf (pFile, "%d\t",degrees);
|
||||||
|
}
|
||||||
|
|
||||||
|
//close open file
|
||||||
|
if (opts->lrrp_file_output == 1)
|
||||||
|
{
|
||||||
|
fprintf (pFile, "\n");
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
//save to string for ncurses
|
||||||
|
if (!source) source = state->dmr_lrrp_source[state->currentslot];
|
||||||
|
char velstr[20];
|
||||||
|
char degstr[20];
|
||||||
|
char lrrpstr[100];
|
||||||
|
sprintf (lrrpstr, "%s", "");
|
||||||
|
sprintf (velstr, "%s", "");
|
||||||
|
sprintf (degstr, "%s", "");
|
||||||
|
if (lat) sprintf (lrrpstr, "LRRP %0d (%lf, %lf)", source, lat_fin, lon_fin);
|
||||||
|
if (vel_set) sprintf (velstr, " %.4lf km/h", velocity * 3.6);
|
||||||
|
if (deg_set) sprintf (degstr, " %d%s ", degrees, deg_glyph);
|
||||||
|
sprintf (state->dmr_lrrp_gps[slot], "%s%s%s", lrrpstr, velstr, degstr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (pot_report)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
||||||
|
}
|
||||||
|
|
||||||
|
LRRP_END:
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void dmr_locn (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
uint8_t blocks = state->data_header_blocks[slot];
|
||||||
|
uint8_t padding = state->data_header_padding[slot];
|
||||||
|
uint8_t source = state->dmr_lrrp_source[slot];
|
||||||
|
|
||||||
|
//flags if certain data type is present
|
||||||
|
uint8_t time = 0;
|
||||||
|
uint8_t lat = 0;
|
||||||
|
uint8_t lon = 0;
|
||||||
|
|
||||||
|
//date-time variables
|
||||||
|
uint8_t hour = 0;
|
||||||
|
uint8_t minute = 0;
|
||||||
|
uint8_t second = 0;
|
||||||
|
uint8_t year = 0;
|
||||||
|
uint8_t month = 0;
|
||||||
|
uint8_t day = 0;
|
||||||
|
|
||||||
|
//need to experiment best way to do this portion
|
||||||
|
uint8_t lat_deg = 0;
|
||||||
|
uint8_t lat_min = 0;
|
||||||
|
uint16_t lat_sec = 0;
|
||||||
|
|
||||||
|
uint8_t lon_deg = 0;
|
||||||
|
uint8_t lon_min = 0;
|
||||||
|
uint16_t lon_sec = 0;
|
||||||
|
|
||||||
|
char lat_ord[2];
|
||||||
|
char lon_ord[2];
|
||||||
|
sprintf (lat_ord, "%s", "N");
|
||||||
|
sprintf (lon_ord, "%s", "E");
|
||||||
|
|
||||||
|
char deg_glyph[4];
|
||||||
|
sprintf (deg_glyph, "%s", "°");
|
||||||
|
// sprintf (deg_glyph, "%s", "d");
|
||||||
|
|
||||||
|
//more strings...
|
||||||
|
char locnstr[50];
|
||||||
|
char latstr[75];
|
||||||
|
char lonstr[75];
|
||||||
|
sprintf (locnstr, "%s", " ");
|
||||||
|
sprintf (latstr, "%s", " ");
|
||||||
|
sprintf (lonstr, "%s", " ");
|
||||||
|
|
||||||
|
//TODO: conversion to decimal format
|
||||||
|
//DD = d + (min/60) + (sec/3600)
|
||||||
|
//N is positive, E is positive?? Assuming its like a 2D plane
|
||||||
|
int lat_sign = 1; //positive 1 or negative 1
|
||||||
|
int lon_sign = 1; //positive 1 or negative 1
|
||||||
|
double lat_fin = 0;
|
||||||
|
double lon_fin = 0;
|
||||||
|
|
||||||
|
//start looking for specific bytes corresponding to 'letters' A (time), NSEW (ordinal directions), etc
|
||||||
|
for (i = 0; i < ( (blocks*block_len) - (padding+4) ); i++)
|
||||||
|
{
|
||||||
|
switch(DMR_PDU[i]){
|
||||||
|
case 0x41: //A -- time and date
|
||||||
|
time = 1;
|
||||||
|
hour = ((DMR_PDU[i+1] - 0x30) << 4) | (DMR_PDU[i+2] - 0x30);
|
||||||
|
minute = ((DMR_PDU[i+3] - 0x30) << 4) | (DMR_PDU[i+4] - 0x30);
|
||||||
|
second = ((DMR_PDU[i+5] - 0x30) << 4) | (DMR_PDU[i+6] - 0x30);
|
||||||
|
//think this is in day, mon, year format
|
||||||
|
day = ((DMR_PDU[i+7] - 0x30) << 4) | (DMR_PDU[i+8] - 0x30);
|
||||||
|
month = ((DMR_PDU[i+9] - 0x30) << 4) | (DMR_PDU[i+10] - 0x30);
|
||||||
|
year = ((DMR_PDU[i+11] - 0x30) << 4) | (DMR_PDU[i+12] - 0x30);
|
||||||
|
i += 12;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x53: //S -- South
|
||||||
|
sprintf (lat_ord, "%s", "S");
|
||||||
|
lat_sign = -1;
|
||||||
|
case 0x4E: //N -- North
|
||||||
|
lat = 1;
|
||||||
|
lat_deg = ((DMR_PDU[i+1] - 0x30) << 4) | (DMR_PDU[i+2] - 0x30);
|
||||||
|
lat_min = ((DMR_PDU[i+3] - 0x30) << 4) | (DMR_PDU[i+4] - 0x30);
|
||||||
|
lat_sec = ((DMR_PDU[i+6] - 0x30) << 12) | ((DMR_PDU[i+7] - 0x30) << 8) | ((DMR_PDU[i+8] - 0x30) << 4) | ((DMR_PDU[i+9] - 0x30) << 0);
|
||||||
|
i += 8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x57: //W -- West
|
||||||
|
sprintf (lon_ord, "%s", "W");
|
||||||
|
lon_sign = -1;
|
||||||
|
case 0x45: //E -- East
|
||||||
|
lon = 1;
|
||||||
|
lon_deg = ((DMR_PDU[i+1] - 0x30) << 8) | ((DMR_PDU[i+2] - 0x30) << 4) | ((DMR_PDU[i+3] - 0x30) << 0);
|
||||||
|
lon_min = ((DMR_PDU[i+4] - 0x30) << 4) | (DMR_PDU[i+5] - 0x30);
|
||||||
|
lon_sec = ((DMR_PDU[i+7] - 0x30) << 12) | ((DMR_PDU[i+8] - 0x30) << 8) | ((DMR_PDU[i+9] - 0x30) << 4) | ((DMR_PDU[i+10] - 0x30) << 0);
|
||||||
|
i += 8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
//do nothing
|
||||||
|
break;
|
||||||
|
|
||||||
|
} //end switch
|
||||||
|
|
||||||
|
} //for i
|
||||||
|
|
||||||
|
if (lat && lon)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, "\n LOCN Report - Source: [%d]\n", source);
|
||||||
|
if (time) fprintf (stderr, " 20%02X/%02X/%02X %02X:%02X:%02X ", year, month, day, hour, minute, second);
|
||||||
|
fprintf (stderr, "Lat: %s %02X%s%02X\"%04X' Lon: %s %02X%s%02X\"%04X' ", lat_ord, lat_deg, deg_glyph, lat_min, lat_sec, lon_ord, lon_deg, deg_glyph, lon_min, lon_sec);
|
||||||
|
|
||||||
|
//string manip for ncurses terminal display
|
||||||
|
sprintf (locnstr, "LOCN %d ", source);
|
||||||
|
sprintf (latstr, "%s %02X%s%02X\"%04X' ", lat_ord, lat_deg, deg_glyph, lat_min, lat_sec);
|
||||||
|
sprintf (lonstr, "%s %02X%s%02X\"%04X' ", lon_ord, lon_deg, deg_glyph, lon_min, lon_sec);
|
||||||
|
sprintf (state->dmr_lrrp_gps[slot], "%s%s%s", locnstr, latstr, lonstr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
156
src/dmr_pi.c
Normal file
156
src/dmr_pi.c
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_pi.c
|
||||||
|
* DMR Privacy Indicator and LFSR Function
|
||||||
|
*
|
||||||
|
* LFSR code courtesy of https://github.com/mattames/LFSR/
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||||
|
{
|
||||||
|
|
||||||
|
if((IrrecoverableErrors == 0))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
state->payload_algid = PI_BYTE[0];
|
||||||
|
state->payload_keyid = PI_BYTE[2];
|
||||||
|
state->payload_mi = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
||||||
|
if (state->payload_algid < 0x26)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s ", KYEL);
|
||||||
|
fprintf (stderr, "\n Slot 1");
|
||||||
|
fprintf (stderr, " DMR PI H- ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algid, state->payload_keyid, state->payload_mi);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
if (state->payload_algid != 0x21)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
LFSR64 (state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->payload_algid >= 0x26)
|
||||||
|
{
|
||||||
|
state->payload_algid = 0;
|
||||||
|
state->payload_keyid = 0;
|
||||||
|
state->payload_mi = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
state->payload_algidR = PI_BYTE[0];
|
||||||
|
state->payload_keyidR = PI_BYTE[2];
|
||||||
|
state->payload_miR = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
||||||
|
if (state->payload_algidR < 0x26)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s ", KYEL);
|
||||||
|
fprintf (stderr, "\n Slot 2");
|
||||||
|
fprintf (stderr, " DMR PI H- ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algidR, state->payload_keyidR, state->payload_miR);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
if (state->payload_algidR != 0x21)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
LFSR64 (state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->payload_algidR >= 0x26)
|
||||||
|
{
|
||||||
|
state->payload_algidR = 0;
|
||||||
|
state->payload_keyidR = 0;
|
||||||
|
state->payload_miR = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LFSR(dsd_state * state)
|
||||||
|
{
|
||||||
|
int lfsr = 0;
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
lfsr = state->payload_mi;
|
||||||
|
}
|
||||||
|
else lfsr = state->payload_miR;
|
||||||
|
|
||||||
|
uint8_t cnt = 0;
|
||||||
|
|
||||||
|
for(cnt=0;cnt<32;cnt++)
|
||||||
|
{
|
||||||
|
// Polynomial is C(x) = x^32 + x^4 + x^2 + 1
|
||||||
|
int bit = ((lfsr >> 31) ^ (lfsr >> 3) ^ (lfsr >> 1)) & 0x1;
|
||||||
|
lfsr = (lfsr << 1) | (bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " Slot 1");
|
||||||
|
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
|
||||||
|
fprintf(stderr, " MI: 0x%08X", lfsr);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
state->payload_mi = lfsr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " Slot 2");
|
||||||
|
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
|
||||||
|
fprintf(stderr, " MI: 0x%08X", lfsr);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
state->payload_miR = lfsr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LFSR64(dsd_state * state)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
unsigned long long int lfsr = 0;
|
||||||
|
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
lfsr = (uint64_t) state->payload_mi;
|
||||||
|
}
|
||||||
|
else lfsr = (uint64_t) state->payload_miR;
|
||||||
|
|
||||||
|
uint8_t cnt = 0;
|
||||||
|
|
||||||
|
for(cnt=0;cnt<32;cnt++)
|
||||||
|
{
|
||||||
|
unsigned long long int bit = ( (lfsr >> 31) ^ (lfsr >> 21) ^ (lfsr >> 1) ^ (lfsr >> 0) ) & 0x1;
|
||||||
|
lfsr = (lfsr << 1) | bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " Slot 1");
|
||||||
|
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
|
||||||
|
fprintf (stderr, " MI: 0x%016llX", lfsr);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
state->payload_mi = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " Slot 2");
|
||||||
|
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
|
||||||
|
fprintf (stderr, " MI: 0x%016llX", lfsr);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
state->payload_miR = lfsr & 0xFFFFFFFF; //truncate for next repitition and le verification
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
440
src/dmr_utils.c
Normal file
440
src/dmr_utils.c
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
//DMR CRC/Utility Functions
|
||||||
|
//Original File - dmr_sync.c
|
||||||
|
//ConvertBitIntoBytes, ComputeCrcCCITT, ComputeCrc5Bit, ComputeAndCorrectFullLinkControlCrc, CRC32, CRC9
|
||||||
|
//Original Source - https://github.com/LouisErigHerve/dsd
|
||||||
|
|
||||||
|
//Additional Functions
|
||||||
|
//Hamming17123, crc7, crc8, crc8ok functions
|
||||||
|
//Original Souce - https://github.com/boatbod/op25
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
//modified to accept variable payload size and len
|
||||||
|
uint16_t ComputeCrcCCITT16d(const uint8_t buf[], uint8_t len)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */
|
||||||
|
/* Polynomial x^16 + x^12 + x^5 + 1
|
||||||
|
* Normal = 0x1021
|
||||||
|
* Reciprocal = 0x0811
|
||||||
|
* Reversed = 0x8408
|
||||||
|
* Reversed reciprocal = 0x8810 */
|
||||||
|
uint16_t Polynome = 0x1021;
|
||||||
|
for(i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if(((CRC >> 15) & 1) ^ (buf[i] & 1))
|
||||||
|
{
|
||||||
|
CRC = (CRC << 1) ^ Polynome;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CRC <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invert the CRC */
|
||||||
|
CRC ^= 0xFFFF;
|
||||||
|
|
||||||
|
/* Return the CRC */
|
||||||
|
return CRC;
|
||||||
|
} /* End ComputeCrcCCITTd() */
|
||||||
|
|
||||||
|
// A Hamming (17,12,3) Check for completed SLC message
|
||||||
|
bool Hamming17123(uint8_t* d)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Calculate the checksum this column should have
|
||||||
|
bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9];
|
||||||
|
bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10];
|
||||||
|
bool c2 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11];
|
||||||
|
bool c3 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10];
|
||||||
|
bool c4 = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11];
|
||||||
|
|
||||||
|
// Compare these with the actual bits
|
||||||
|
unsigned char n = 0x00U;
|
||||||
|
n |= (c0 != d[12]) ? 0x01U : 0x00U;
|
||||||
|
n |= (c1 != d[13]) ? 0x02U : 0x00U;
|
||||||
|
n |= (c2 != d[14]) ? 0x04U : 0x00U;
|
||||||
|
n |= (c3 != d[15]) ? 0x08U : 0x00U;
|
||||||
|
n |= (c4 != d[16]) ? 0x10U : 0x00U;
|
||||||
|
|
||||||
|
switch (n) {
|
||||||
|
// Parity bit errors
|
||||||
|
case 0x01U: d[12] = !d[12]; return true;
|
||||||
|
case 0x02U: d[13] = !d[13]; return true;
|
||||||
|
case 0x04U: d[14] = !d[14]; return true;
|
||||||
|
case 0x08U: d[15] = !d[15]; return true;
|
||||||
|
case 0x10U: d[16] = !d[16]; return true;
|
||||||
|
|
||||||
|
// Data bit errors
|
||||||
|
case 0x1BU: d[0] = !d[0]; return true;
|
||||||
|
case 0x1FU: d[1] = !d[1]; return true;
|
||||||
|
case 0x17U: d[2] = !d[2]; return true;
|
||||||
|
case 0x07U: d[3] = !d[3]; return true;
|
||||||
|
case 0x0EU: d[4] = !d[4]; return true;
|
||||||
|
case 0x1CU: d[5] = !d[5]; return true;
|
||||||
|
case 0x11U: d[6] = !d[6]; return true;
|
||||||
|
case 0x0BU: d[7] = !d[7]; return true;
|
||||||
|
case 0x16U: d[8] = !d[8]; return true;
|
||||||
|
case 0x05U: d[9] = !d[9]; return true;
|
||||||
|
case 0x0AU: d[10] = !d[10]; return true;
|
||||||
|
case 0x14U: d[11] = !d[11]; return true;
|
||||||
|
|
||||||
|
// No bit errors
|
||||||
|
case 0x00U: return true;
|
||||||
|
|
||||||
|
// Unrecoverable errors
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t crc8(uint8_t bits[], unsigned int len)
|
||||||
|
{
|
||||||
|
uint8_t crc=0;
|
||||||
|
unsigned int K = 8;
|
||||||
|
uint8_t poly[9] = {1,0,0,0,0,0,1,1,1}; // crc8 poly
|
||||||
|
uint8_t buf[256];
|
||||||
|
if (len+K > sizeof(buf)) {
|
||||||
|
//fprintf (stderr, "crc8: buffer length %u exceeds maximum %lu\n", len+K, sizeof(buf));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memset (buf, 0, sizeof(buf));
|
||||||
|
for (unsigned int i=0; i<len; i++){
|
||||||
|
buf[i] = bits[i];
|
||||||
|
}
|
||||||
|
for (unsigned int i=0; i<len; i++)
|
||||||
|
if (buf[i])
|
||||||
|
for (unsigned int j=0; j<K+1; j++)
|
||||||
|
buf[i+j] ^= poly[j];
|
||||||
|
for (unsigned int i=0; i<K; i++){
|
||||||
|
crc = (crc << 1) + buf[len + i];
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool crc8_ok(uint8_t bits[], unsigned int len)
|
||||||
|
{
|
||||||
|
uint16_t crc = 0;
|
||||||
|
for (unsigned int i=0; i < 8; i++) {
|
||||||
|
crc = (crc << 1) + bits[len+i];
|
||||||
|
}
|
||||||
|
return (crc == crc8(bits,len));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t crc7(uint8_t bits[], unsigned int len)
|
||||||
|
{
|
||||||
|
uint8_t crc=0;
|
||||||
|
unsigned int K = 7;
|
||||||
|
//G7(x) = x7 + x5 + x2 + x + 1 check poly below for correct (dmr rc crc7)
|
||||||
|
uint8_t poly[8] = {1,0,1,0,0,1,1,1}; // crc7 poly
|
||||||
|
uint8_t buf[256];
|
||||||
|
if (len+K > sizeof(buf)) {
|
||||||
|
// fprintf (stderr, "crc8: buffer length %u exceeds maximum %lu\n", len+K, sizeof(buf));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memset (buf, 0, sizeof(buf));
|
||||||
|
for (unsigned int i=0; i<len; i++){
|
||||||
|
buf[i] = bits[i];
|
||||||
|
}
|
||||||
|
for (unsigned int i=0; i<len; i++)
|
||||||
|
if (buf[i])
|
||||||
|
for (unsigned int j=0; j<K+1; j++)
|
||||||
|
buf[i+j] ^= poly[j];
|
||||||
|
for (unsigned int i=0; i<K; i++){
|
||||||
|
crc = (crc << 1) + buf[len + i];
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief : This function compute the CRC-CCITT of the DMR data
|
||||||
|
* by using the polynomial x^16 + x^12 + x^5 + 1
|
||||||
|
*
|
||||||
|
* @param Input : A buffer pointer of the DMR data (80 bytes)
|
||||||
|
*
|
||||||
|
* @return The 16 bit CRC
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint16_t ComputeCrcCCITT(uint8_t * DMRData)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */
|
||||||
|
/* Polynomial x^16 + x^12 + x^5 + 1
|
||||||
|
* Normal = 0x1021
|
||||||
|
* Reciprocal = 0x0811
|
||||||
|
* Reversed = 0x8408
|
||||||
|
* Reversed reciprocal = 0x8810 */
|
||||||
|
uint16_t Polynome = 0x1021;
|
||||||
|
for(i = 0; i < 80; i++)
|
||||||
|
{
|
||||||
|
if(((CRC >> 15) & 1) ^ (DMRData[i] & 1))
|
||||||
|
{
|
||||||
|
CRC = (CRC << 1) ^ Polynome;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CRC <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invert the CRC */
|
||||||
|
CRC ^= 0xFFFF;
|
||||||
|
|
||||||
|
/* Return the CRC */
|
||||||
|
return CRC;
|
||||||
|
} /* End ComputeCrcCCITT() */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief : This function compute the CRC-24 bit of the full
|
||||||
|
* link control by using the Reed-Solomon(12,9) FEC
|
||||||
|
*
|
||||||
|
* @param FullLinkControlDataBytes : A buffer pointer of the DMR data bytes (12 bytes)
|
||||||
|
*
|
||||||
|
* @param CRCComputed : A 32 bit pointer where the computed CRC 24-bit will be stored
|
||||||
|
*
|
||||||
|
* @param CRCMask : The 24 bit CRC mask to apply
|
||||||
|
*
|
||||||
|
* @return 0 = CRC error
|
||||||
|
* 1 = CRC is correct
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
rs_12_9_codeword_t VoiceLCHeaderStr;
|
||||||
|
rs_12_9_poly_t syndrome;
|
||||||
|
uint8_t errors_found = 0;
|
||||||
|
rs_12_9_correct_errors_result_t result = RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND;
|
||||||
|
uint32_t CrcIsCorrect = 0;
|
||||||
|
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
VoiceLCHeaderStr.data[i] = FullLinkControlDataBytes[i];
|
||||||
|
|
||||||
|
/* Apply CRC mask on each 3 last bytes
|
||||||
|
* of the full link control */
|
||||||
|
if(i == 9)
|
||||||
|
{
|
||||||
|
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask >> 16);
|
||||||
|
}
|
||||||
|
else if(i == 10)
|
||||||
|
{
|
||||||
|
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask >> 8);
|
||||||
|
}
|
||||||
|
else if(i == 11)
|
||||||
|
{
|
||||||
|
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Nothing to do */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check and correct the full link LC control with Reed Solomon (12,9) FEC */
|
||||||
|
rs_12_9_calc_syndrome(&VoiceLCHeaderStr, &syndrome);
|
||||||
|
if(rs_12_9_check_syndrome(&syndrome) != 0) result = rs_12_9_correct_errors(&VoiceLCHeaderStr, &syndrome, &errors_found);
|
||||||
|
|
||||||
|
/* Reconstitue the CRC */
|
||||||
|
*CRCComputed = (uint32_t)((VoiceLCHeaderStr.data[9] << 16) & 0xFF0000);
|
||||||
|
*CRCComputed |= (uint32_t)((VoiceLCHeaderStr.data[10] << 8) & 0x00FF00);
|
||||||
|
*CRCComputed |= (uint32_t)((VoiceLCHeaderStr.data[11] << 0) & 0x0000FF);
|
||||||
|
|
||||||
|
if((result == RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND) ||
|
||||||
|
(result == RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CORRECTED))
|
||||||
|
{
|
||||||
|
//fprintf(stderr, "CRC OK : 0x%06X\n", *CRCComputed);
|
||||||
|
CrcIsCorrect = 1;
|
||||||
|
|
||||||
|
/* Reconstitue full link control data after FEC correction */
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
FullLinkControlDataBytes[i] = VoiceLCHeaderStr.data[i];
|
||||||
|
|
||||||
|
/* Apply CRC mask on each 3 last bytes
|
||||||
|
* of the full link control */
|
||||||
|
if(i == 9)
|
||||||
|
{
|
||||||
|
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask >> 16);
|
||||||
|
}
|
||||||
|
else if(i == 10)
|
||||||
|
{
|
||||||
|
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask >> 8);
|
||||||
|
}
|
||||||
|
else if(i == 11)
|
||||||
|
{
|
||||||
|
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Nothing to do */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//fprintf(stderr, "CRC ERROR : 0x%06X\n", *CRCComputed);
|
||||||
|
CrcIsCorrect = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the CRC status */
|
||||||
|
return CrcIsCorrect;
|
||||||
|
} /* End ComputeAndCorrectFullLinkControlCrc() */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief : This function compute the 5 bit CRC of the DMR voice burst data
|
||||||
|
* See ETSI TS 102 361-1 chapter B.3.11
|
||||||
|
*
|
||||||
|
* @param Input : A buffer pointer of the DMR data (72 bytes)
|
||||||
|
*
|
||||||
|
* @return The 5 bit CRC
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8_t ComputeCrc5Bit(uint8_t * DMRData)
|
||||||
|
{
|
||||||
|
uint32_t i, j, k;
|
||||||
|
uint8_t Buffer[9];
|
||||||
|
uint32_t Sum;
|
||||||
|
uint8_t CRC = 0;
|
||||||
|
|
||||||
|
/* Convert the 72 bit into 9 bytes */
|
||||||
|
k = 0;
|
||||||
|
for(i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
Buffer[i] = 0;
|
||||||
|
for(j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
Buffer[i] = Buffer[i] << 1;
|
||||||
|
Buffer[i] = Buffer[i] | DMRData[k++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add all 9 bytes */
|
||||||
|
Sum = 0;
|
||||||
|
for(i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
Sum += (uint32_t)Buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sum MOD 31 = CRC */
|
||||||
|
CRC = (uint8_t)(Sum % 31);
|
||||||
|
|
||||||
|
/* Return the CRC */
|
||||||
|
return CRC;
|
||||||
|
} /* End ComputeCrc5Bit() */
|
||||||
|
|
||||||
|
uint64_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength)
|
||||||
|
{
|
||||||
|
uint64_t Output = 0;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for(i = 0; i < BitLength; i++)
|
||||||
|
{
|
||||||
|
Output <<= 1;
|
||||||
|
Output |= (uint64_t)(BufferIn[i] & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Output;
|
||||||
|
} /* End ConvertBitIntoBytes() */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief : This function compute the CRC-9 of the DMR data
|
||||||
|
* by using the polynomial x^9 + x^6 + x^4 + x^3 + 1
|
||||||
|
*
|
||||||
|
* @param Input : A buffer pointer of the DMR data (80 bytes)
|
||||||
|
* Rate 1/2 coded confirmed (10 data octets): 80 bit sequence (80 bytes)
|
||||||
|
* Rate 3/4 coded confirmed (16 data octets): 128 bit sequence (120 bytes)
|
||||||
|
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
|
||||||
|
*
|
||||||
|
* @param NbData : The number of bit to compute
|
||||||
|
* Rate 1/2 coded confirmed (10 data octets): 80 bit sequence (80 bytes)
|
||||||
|
* Rate 3/4 coded confirmed (16 data octets): 128 bit sequence (120 bytes)
|
||||||
|
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
|
||||||
|
*
|
||||||
|
* @return The 9 bit CRC
|
||||||
|
*/
|
||||||
|
uint16_t ComputeCrc9Bit(uint8_t * DMRData, uint32_t NbData)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */
|
||||||
|
/* Polynomial x^9 + x^6 + x^4 + x^3 + 1
|
||||||
|
* Normal = 0x059
|
||||||
|
* Reciprocal = 0x134
|
||||||
|
* Reversed reciprocal = 0x12C */
|
||||||
|
uint16_t Polynome = 0x059;
|
||||||
|
for(i = 0; i < NbData; i++)
|
||||||
|
{
|
||||||
|
if(((CRC >> 8) & 1) ^ (DMRData[i] & 1))
|
||||||
|
{
|
||||||
|
CRC = (CRC << 1) ^ Polynome;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CRC <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Conserve only the 9 LSBs */
|
||||||
|
CRC &= 0x01FF;
|
||||||
|
|
||||||
|
/* Invert the CRC */
|
||||||
|
CRC ^= 0x01FF;
|
||||||
|
|
||||||
|
/* Return the CRC */
|
||||||
|
return CRC;
|
||||||
|
} /* End ComputeCrc9Bit() */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief : This function compute the CRC-32 of the DMR data
|
||||||
|
* by using the polynomial x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
|
||||||
|
*
|
||||||
|
* @param Input : A buffer pointer of the DMR data (80 bytes)
|
||||||
|
* Rate 1/2 coded confirmed (10 data octets): 80 bit sequence (80 bytes)
|
||||||
|
* Rate 3/4 coded confirmed (16 data octets): 128 bit sequence (120 bytes)
|
||||||
|
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
|
||||||
|
*
|
||||||
|
* @param NbData : The number of bit to compute
|
||||||
|
* Rate 1/2 coded confirmed (10 data octets): 80 bit sequence (80 bytes)
|
||||||
|
* Rate 3/4 coded confirmed (16 data octets): 128 bit sequence (120 bytes)
|
||||||
|
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
|
||||||
|
*
|
||||||
|
* @return The 32 bit CRC
|
||||||
|
*/
|
||||||
|
uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint32_t CRC = 0x00000000; /* Initialization value = 0x00000000 */
|
||||||
|
/* Polynomial x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
|
||||||
|
* Normal = 0x04C11DB7
|
||||||
|
* Reversed = 0xEDB88320
|
||||||
|
* Reciprocal = 0xDB710641
|
||||||
|
* Reversed reciprocal = 0x82608EDB */
|
||||||
|
uint32_t Polynome = 0x04C11DB7;
|
||||||
|
for(i = 0; i < NbData; i++)
|
||||||
|
{
|
||||||
|
if(((CRC >> 31) & 1) ^ (DMRData[i] & 1))
|
||||||
|
{
|
||||||
|
CRC = (CRC << 1) ^ Polynome;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CRC <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//for whatever reason, we get the CRC returned in a reversed byte order (MSO LSO b***s***)
|
||||||
|
uint32_t a, b, c, d;
|
||||||
|
a = b = c = d = 0;
|
||||||
|
a = (CRC & 0xFF) << 24;
|
||||||
|
b = (CRC & 0xFF00) >> 8;
|
||||||
|
b = b << 16;
|
||||||
|
c = (CRC & 0xFF0000) >> 16;
|
||||||
|
c = c << 8;
|
||||||
|
d = (CRC & 0xFF000000) >> 24;
|
||||||
|
|
||||||
|
CRC = a + b + c + d;
|
||||||
|
/* Return the CRC */
|
||||||
|
return CRC;
|
||||||
|
} /* End ComputeCrc32Bit() */
|
||||||
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 */
|
||||||
704
src/dpmr_voice.c
Normal file
704
src/dpmr_voice.c
Normal file
@@ -0,0 +1,704 @@
|
|||||||
|
/*
|
||||||
|
* 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];
|
||||||
|
uint8_t CCHDataCRC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||||
|
|
||||||
|
memset (CCHDataHammingCorrected, 1, sizeof(CCHDataHammingCorrected));
|
||||||
|
memset (CCHDataCRC, 1, sizeof(CCHDataCRC));
|
||||||
|
|
||||||
|
uint8_t CCHDataCRCComputed[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||||
|
memset (CCHDataCRCComputed, 0, sizeof(CCHDataCRCComputed));
|
||||||
|
|
||||||
|
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];
|
||||||
|
memset (ambe_fr, 0, sizeof (ambe_fr));
|
||||||
|
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};
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
|
||||||
|
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++;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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) HammingOk[i] = 1;
|
||||||
|
else 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]) CrcOk[i] = 1;
|
||||||
|
else 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';
|
||||||
|
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
/* Set the next part as the second part */
|
||||||
|
opts->dPMR_next_part_of_superframe = 2;
|
||||||
|
}
|
||||||
|
else if(opts->dPMR_next_part_of_superframe == 2)
|
||||||
|
{
|
||||||
|
/* Set the next part as the first part */
|
||||||
|
opts->dPMR_next_part_of_superframe = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Unknown current part, set to 0 */
|
||||||
|
opts->dPMR_next_part_of_superframe = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Display the destination ID (talk group - TG) */
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
if(state->dPMRVoiceFS2Frame.CalledIDOk)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf(stderr, " TG=%s", CalledID);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
//check other as well before assigning
|
||||||
|
if(state->dPMRVoiceFS2Frame.CallingIDOk) sprintf (state->dpmr_target_id, "%s", CalledID);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf(stderr, " TG=(CRC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Display the source ID */
|
||||||
|
if(state->dPMRVoiceFS2Frame.CallingIDOk)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf(stderr, " Src=%s", CallingID);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
//check other as well before assigning
|
||||||
|
if(state->dPMRVoiceFS2Frame.CalledIDOk) sprintf (state->dpmr_caller_id, "%s", CallingID);
|
||||||
|
|
||||||
|
//stashed channel code in here, more likely to be correct, but no guarantee
|
||||||
|
if(state->dPMRVoiceFS2Frame.ColorCode[0] != (unsigned int)(-1))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf(stderr, " Channel Code=%02d", (int)state->dPMRVoiceFS2Frame.ColorCode[0]);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
//check other as well before assigning
|
||||||
|
if(state->dPMRVoiceFS2Frame.CalledIDOk) state->dpmr_color_code = (int)state->dPMRVoiceFS2Frame.ColorCode[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf(stderr, " Src=(CRC ERR) Channel Code =(CRC ERR)");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->dPMRVoiceFS2Frame.Version[0] == 3)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " Scrambler");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
if (state->R != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " Key %05lld ", state->R );
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Play only voice frames by first looking to see if there is a TCH voice in CommunicationMode,
|
||||||
|
short start = 0;
|
||||||
|
short end = 4;
|
||||||
|
|
||||||
|
//not really even sure why I made this loop
|
||||||
|
//I probably should actually read the manual again
|
||||||
|
for (short o = 0; o < 2; o++)
|
||||||
|
{
|
||||||
|
|
||||||
|
//reset scrambler key seed on frame 0
|
||||||
|
if (state->dPMRVoiceFS2Frame.FrameNumbering[o] == 0) state->payload_miN = 0;
|
||||||
|
|
||||||
|
//depending on first or second TCH, set start and end variables appropriately
|
||||||
|
if (o == 1)
|
||||||
|
{
|
||||||
|
start = 4;
|
||||||
|
end = 8;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//this is used so we can simply pass the voice here to the
|
||||||
|
//nxdn scrambler by telling mbe these are nxdn frames and to
|
||||||
|
//apply the same descramble method to them if necessary
|
||||||
|
int realsynctype = state->synctype;
|
||||||
|
|
||||||
|
// fprintf (stderr, "\nCommunication Mode: %d\n", state->dPMRVoiceFS2Frame.CommunicationMode[o]);
|
||||||
|
// fprintf (stderr, "Encryption Mode: %d\n", state->dPMRVoiceFS2Frame.Version[o]); //shows as 3 (manufacturer specific, 5.16) on the dPMR scrambler values received
|
||||||
|
// fprintf (stderr, "Superframe Number: %d", state->dPMRVoiceFS2Frame.FrameNumbering[o]);
|
||||||
|
|
||||||
|
if((state->dPMRVoiceFS2Frame.CommunicationMode[o] == 0) ||
|
||||||
|
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 1) ||
|
||||||
|
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 5))
|
||||||
|
{
|
||||||
|
|
||||||
|
//check to see if we are using scrambler
|
||||||
|
if (state->dPMRVoiceFS2Frame.Version[o] == 3) //!= 0
|
||||||
|
{
|
||||||
|
state->synctype = 28; //fake it as nxdn
|
||||||
|
state->nxdn_cipher_type = 0x01; //turn on nxdn scrambler cipher
|
||||||
|
state->dmr_encL = 1; //flag on enc bit here so we can mute if no key provided
|
||||||
|
}
|
||||||
|
|
||||||
|
//flag back off if key is provided
|
||||||
|
if (state->R != 0) state->dmr_encL = 0;
|
||||||
|
|
||||||
|
//There are 4 AMBE voice frames per TCH
|
||||||
|
for(i = start; i < end; i++)
|
||||||
|
{
|
||||||
|
processMbeFrame (opts, state, NULL, ambe_fr[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the correct sync type again and flag off the cipher
|
||||||
|
state->synctype = realsynctype;
|
||||||
|
state->nxdn_cipher_type = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
702
src/dsd_audio.c
Normal file
702
src/dsd_audio.c
Normal file
@@ -0,0 +1,702 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (opts->audio_out_type == 5) //OSS
|
||||||
|
{
|
||||||
|
//OSS
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
else if (opts->audio_out_type == 0)
|
||||||
|
{
|
||||||
|
pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL);
|
||||||
|
state->audio_out_idx = 0;
|
||||||
|
}
|
||||||
|
else state->audio_out_idx = 0; //failsafe for audio_out == 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 == 5) //OSS
|
||||||
|
{
|
||||||
|
//OSS
|
||||||
|
result = write (opts->audio_out_fdR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2));
|
||||||
|
state->audio_out_idxR = 0;
|
||||||
|
}
|
||||||
|
else if (opts->audio_out_type == 0)
|
||||||
|
{
|
||||||
|
pa_simple_write(opts->pulse_digi_dev_outR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2), NULL);
|
||||||
|
state->audio_out_idxR = 0;
|
||||||
|
}
|
||||||
|
else state->audio_out_idxR = 0; //failsafe for audio_out == 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)
|
||||||
|
{
|
||||||
|
char * extension;
|
||||||
|
const char ch = '.';
|
||||||
|
extension = strrchr(opts->audio_in_dev, ch); //return extension if this is a .wav or .bin file
|
||||||
|
|
||||||
|
//if no extension set, give default of .wav -- bugfix for github issue #105
|
||||||
|
// if (extension == NULL) extension = ".wav";
|
||||||
|
|
||||||
|
// 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=opts->wav_sample_rate;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (strncmp(opts->audio_in_dev, "tcp", 3) == 0)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 8;
|
||||||
|
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
|
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||||
|
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->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||||
|
if(opts->tcp_file_in == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error, couldn't open TCP with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (strncmp(opts->audio_in_dev, "udp", 3) == 0)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 6;
|
||||||
|
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
|
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||||
|
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->udp_file_in = sf_open_fd(opts->udp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||||
|
|
||||||
|
if(opts->udp_file_in == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error, couldn't open UDP with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0)
|
||||||
|
{
|
||||||
|
#ifdef USE_RTLSDR
|
||||||
|
opts->audio_in_type = 3;
|
||||||
|
#elif AERO_BUILD
|
||||||
|
opts->audio_in_type = 5;
|
||||||
|
sprintf (opts->audio_in_dev, "/dev/dsp");
|
||||||
|
#else
|
||||||
|
opts->audio_in_type = 0;
|
||||||
|
sprintf (opts->audio_in_dev, "pulse");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if(strncmp(opts->audio_in_dev, "pulse", 5) == 0)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 0;
|
||||||
|
}
|
||||||
|
else if((strncmp(opts->audio_in_dev, "/dev/dsp", 8) == 0))
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if no extension set, treat as named pipe or extensionless wav file -- bugfix for github issue #105
|
||||||
|
else if (extension == NULL)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 2;
|
||||||
|
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
|
opts->audio_in_file_info->samplerate = opts->wav_sample_rate;
|
||||||
|
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(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
|
||||||
|
|
||||||
|
if (opts->audio_in_file == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error, couldn't open file/pipe with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (strncmp(extension, ".bin", 3) == 0)
|
||||||
|
{
|
||||||
|
struct stat stat_buf;
|
||||||
|
if (stat(opts->audio_in_dev, &stat_buf) != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Error, couldn't open bin file %s\n", opts->audio_in_dev);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
opts->symbolfile = fopen(opts->audio_in_dev, "r");
|
||||||
|
opts->audio_in_type = 4; //symbol capture bin files
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//open as wav file as last resort, wav files subseptible to sample rate issues if not 48000
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct stat stat_buf;
|
||||||
|
if (stat(opts->audio_in_dev, &stat_buf) != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"Error, couldn't open wav file %s\n", opts->audio_in_dev);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (S_ISREG(stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 2; //two now, seperating STDIN and wav files
|
||||||
|
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
|
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||||
|
opts->audio_in_file_info->channels=1;
|
||||||
|
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(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
|
||||||
|
|
||||||
|
if(opts->audio_in_file == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error, couldn't open wav file %s\n", opts->audio_in_dev);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//open pulse audio if no bin or wav
|
||||||
|
else //seems this condition is never met
|
||||||
|
{
|
||||||
|
//opts->audio_in_type = 5; //not sure if this works or needs to openPulse here
|
||||||
|
fprintf(stderr, "Error, couldn't open input file.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
490
src/dsd_dibit.c
Normal file
490
src/dsd_dibit.c
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
/*
|
||||||
|
* 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) || (state->synctype == 37))
|
||||||
|
|
||||||
|
{
|
||||||
|
// 6 +D-STAR
|
||||||
|
// 14 +ProVoice
|
||||||
|
// 18 +D-STAR_HD
|
||||||
|
// 37 +EDACS
|
||||||
|
|
||||||
|
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) || (state->synctype == 38))
|
||||||
|
{
|
||||||
|
// 7 -D-STAR
|
||||||
|
// 15 -ProVoice
|
||||||
|
// 19 -D-STAR_HD
|
||||||
|
// 38 -EDACS
|
||||||
|
|
||||||
|
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 == 29) || (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)
|
||||||
|
// 29 -NXDN (inverted FSW)
|
||||||
|
// 36 -P25p2
|
||||||
|
|
||||||
|
int valid;
|
||||||
|
int dibit;
|
||||||
|
|
||||||
|
valid = 0;
|
||||||
|
|
||||||
|
//testing again, either on Voice channels only (when tuned) or with trunk disabled
|
||||||
|
if (state->synctype == 1 && (opts->p25_is_tuned == 1 || opts->p25_trunk == 0) && opts->use_heuristics == 1)
|
||||||
|
{
|
||||||
|
// Use the P25p1 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)
|
||||||
|
// 28 +NXND (FSW)
|
||||||
|
// 35 +p25p2
|
||||||
|
|
||||||
|
int valid;
|
||||||
|
int dibit;
|
||||||
|
|
||||||
|
valid = 0;
|
||||||
|
|
||||||
|
//testing again, either on Voice channels only (when tuned) or with trunk disabled
|
||||||
|
if (state->synctype == 0 && (opts->p25_is_tuned == 1 || opts->p25_trunk == 0) && opts->use_heuristics == 1)
|
||||||
|
{
|
||||||
|
// Use the P25p1 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
587
src/dsd_file.c
Normal file
587
src/dsd_file.c
Normal file
@@ -0,0 +1,587 @@
|
|||||||
|
/*
|
||||||
|
* 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[80]; //32-bit needs this to be 80
|
||||||
|
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;
|
||||||
|
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 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;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveAmbe2450DataR (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
unsigned char b;
|
||||||
|
unsigned char err;
|
||||||
|
|
||||||
|
err = (unsigned char) state->errs2R;
|
||||||
|
fputc (err, opts->mbe_out_fR);
|
||||||
|
|
||||||
|
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_fR);
|
||||||
|
}
|
||||||
|
b = ambe_d[48];
|
||||||
|
fputc (b, opts->mbe_out_fR);
|
||||||
|
fflush (opts->mbe_out_fR);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
unsigned char b;
|
||||||
|
unsigned char err;
|
||||||
|
|
||||||
|
err = (unsigned char) state->errs2;
|
||||||
|
k = 0;
|
||||||
|
if (opts->dmr_stereo == 0 && opts->dmr_mono == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
if (opts->payload == 1) //print AMBE info from DMR Stereo method
|
||||||
|
{
|
||||||
|
fprintf(stderr, " AMBE ");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
b = 0;
|
||||||
|
for (j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
b = b << 1;
|
||||||
|
b = b + ambe_d[k];
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
if (opts->payload == 1 && i < 6)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%02X", b);
|
||||||
|
}
|
||||||
|
if (opts->payload == 1 && i == 6)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%02X", b & 0x80);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 || opts->dmr_mono == 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)
|
||||||
|
{
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%02X", x);
|
||||||
|
}
|
||||||
|
if (opts->payload == 1 && i == 6)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%02X", x & 0x80);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
//ambe+2
|
||||||
|
if (strstr (cookie, ".amb") != NULL)
|
||||||
|
{
|
||||||
|
state->mbe_file_type = 1;
|
||||||
|
}
|
||||||
|
//p1 and pv
|
||||||
|
else if (strstr (cookie, ".imb") != NULL)
|
||||||
|
{
|
||||||
|
state->mbe_file_type = 0;
|
||||||
|
}
|
||||||
|
//d-star ambe
|
||||||
|
else if (strstr (cookie, ".dmb") != NULL)
|
||||||
|
{
|
||||||
|
state->mbe_file_type = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state->mbe_file_type = -1;
|
||||||
|
fprintf (stderr,"Error - unrecognized file type\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//slot 1
|
||||||
|
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 1.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//slot 2
|
||||||
|
void closeMbeOutFileR (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
if (opts->mbe_outR == 1)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_fR != NULL)
|
||||||
|
{
|
||||||
|
fflush (opts->mbe_out_fR);
|
||||||
|
fclose (opts->mbe_out_fR);
|
||||||
|
opts->mbe_out_fR = NULL;
|
||||||
|
opts->mbe_outR = 0;
|
||||||
|
fprintf (stderr, "\nClosing MBE out file 2.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
char ext[5];
|
||||||
|
|
||||||
|
//phase 1 and provoice
|
||||||
|
if ( (state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15) )
|
||||||
|
{
|
||||||
|
sprintf (ext, ".imb");
|
||||||
|
}
|
||||||
|
//d-star
|
||||||
|
else if ( (state->synctype == 6) || (state->synctype == 7) || (state->synctype == 18) || (state->synctype == 19) )
|
||||||
|
{
|
||||||
|
sprintf (ext, ".dmb"); //new dstar file extension to make it read in and process properly
|
||||||
|
}
|
||||||
|
//dmr, nxdn, phase 2, x2-tdma
|
||||||
|
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;
|
||||||
|
|
||||||
|
sprintf (opts->mbe_out_file, "%s %s S1%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,"\nError, couldn't open %s for slot 1\n", opts->mbe_out_path);
|
||||||
|
}
|
||||||
|
else opts->mbe_out = 1;
|
||||||
|
|
||||||
|
//
|
||||||
|
fprintf (opts->mbe_out_f, "%s", ext);
|
||||||
|
|
||||||
|
fflush (opts->mbe_out_f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void openMbeOutFileR (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
char ext[5];
|
||||||
|
|
||||||
|
//phase 1 and provoice
|
||||||
|
if ( (state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15) )
|
||||||
|
{
|
||||||
|
sprintf (ext, ".imb");
|
||||||
|
}
|
||||||
|
//d-star
|
||||||
|
else if ( (state->synctype == 6) || (state->synctype == 7) || (state->synctype == 18) || (state->synctype == 19) )
|
||||||
|
{
|
||||||
|
sprintf (ext, ".dmb"); //new dstar file extension to make it read in and process properly
|
||||||
|
}
|
||||||
|
//dmr, nxdn, phase 2, x2-tdma
|
||||||
|
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;
|
||||||
|
|
||||||
|
sprintf (opts->mbe_out_fileR, "%s %s S2%s", getDateF(), getTimeF(), ext);
|
||||||
|
|
||||||
|
sprintf (opts->mbe_out_path, "%s%s", opts->mbe_out_dir, opts->mbe_out_fileR);
|
||||||
|
|
||||||
|
opts->mbe_out_fR = fopen (opts->mbe_out_path, "w");
|
||||||
|
if (opts->mbe_out_fR == NULL)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"\nError, couldn't open %s for slot 2\n", opts->mbe_out_path);
|
||||||
|
}
|
||||||
|
else opts->mbe_outR = 1;
|
||||||
|
|
||||||
|
//
|
||||||
|
fprintf (opts->mbe_out_fR, "%s", ext);
|
||||||
|
|
||||||
|
fflush (opts->mbe_out_fR);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void openWavOutFileL (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
|
||||||
|
SF_INFO info;
|
||||||
|
info.samplerate = 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_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
fclose(opts->symbol_out_f);
|
||||||
|
}
|
||||||
|
|
||||||
|
opts->symbol_out = 0; //set flag to 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user