Compare commits
753 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ad9f4107c | ||
|
|
bc140ec081 | ||
|
|
d904289060 | ||
|
|
e13bd4f323 | ||
|
|
5752f1b84a | ||
|
|
5e516ed6d1 | ||
|
|
2ceeeb82af | ||
|
|
2883e44a9d | ||
|
|
20747db2cc | ||
|
|
c2f22ac399 | ||
|
|
b8beede921 | ||
|
|
cd32158a73 | ||
|
|
a8ec6dbe37 | ||
|
|
f22af7c5fc | ||
|
|
a4b53f88a1 | ||
|
|
4567e2f4a4 | ||
|
|
8a3fd87784 | ||
|
|
c428ce32b2 | ||
|
|
2ae9c50d47 | ||
|
|
dfcc8d513d | ||
|
|
7c9d032c99 | ||
|
|
f6fc91a051 | ||
|
|
8b76daa35f | ||
|
|
8b883f556f | ||
|
|
beefaae0fa | ||
|
|
13e778b123 | ||
|
|
7f7c13a72a | ||
|
|
f2312e63eb | ||
|
|
d6adaa87c7 | ||
|
|
20e35a00ba | ||
|
|
8610ee8956 | ||
|
|
92fbdabbe5 | ||
|
|
25659ba920 | ||
|
|
b2ce60b843 | ||
|
|
c87ad08371 | ||
|
|
94688fddd4 | ||
|
|
e29eba9943 | ||
|
|
920dce96fe | ||
|
|
e2747525a6 | ||
|
|
a5989486ae | ||
|
|
9c58273832 | ||
|
|
9a9fa0c182 | ||
|
|
319d0b9866 | ||
|
|
5dbccaadf6 | ||
|
|
28a6777d83 | ||
|
|
21afa4ba97 | ||
|
|
d60dfa7ff3 | ||
|
|
5d3b2ec56f | ||
|
|
2c55a9caab | ||
|
|
6daedf198f | ||
|
|
8d52ce2ac9 | ||
|
|
63e484cc91 | ||
|
|
e3ecace5db | ||
|
|
f9add0b997 | ||
|
|
d3c942d475 | ||
|
|
3d1e28fb3a | ||
|
|
c07fa3a0bc | ||
|
|
ef94b311fa | ||
|
|
79d5b795e3 | ||
|
|
d0c20be7f3 | ||
|
|
6833275960 | ||
|
|
601a1ad2d1 | ||
|
|
db036f4dfc | ||
|
|
7bcd29abec | ||
|
|
c5b36af672 | ||
|
|
9da8544408 | ||
|
|
ab9a9ddab3 | ||
|
|
ceb695af01 | ||
|
|
1ffddf823f | ||
|
|
499134bc2d | ||
|
|
7a9763c6c7 | ||
|
|
db7bc75739 | ||
|
|
bd1dcd2a7f | ||
|
|
bc205cfe00 | ||
|
|
0f3dcf47ea | ||
|
|
3f165571d2 | ||
|
|
b2450b1924 | ||
|
|
061703db7b | ||
|
|
50f4327306 | ||
|
|
90206eac92 | ||
|
|
f1a5d6f870 | ||
|
|
ca506672a8 | ||
|
|
3041713eb3 | ||
|
|
dbc645e7b0 | ||
|
|
5d205800aa | ||
|
|
e5d11c81f3 | ||
|
|
d86c520509 | ||
|
|
854b421235 | ||
|
|
8206a5f3f4 | ||
|
|
0568475d59 | ||
|
|
b5a43031f9 | ||
|
|
ba41ba57f6 | ||
|
|
d84136dc92 | ||
|
|
ad78373b43 | ||
|
|
8980207fd6 | ||
|
|
33d8ee70d9 | ||
|
|
b86cd1bc3c | ||
|
|
57c22b484d | ||
|
|
834bd57ea6 | ||
|
|
7e26b7b902 | ||
|
|
4eb2e770c8 | ||
|
|
0208f6240c | ||
|
|
db88bfff8d | ||
|
|
a78b88cb90 | ||
|
|
a42dd9db83 | ||
|
|
a7d7cf1c2e | ||
|
|
1ec8bb5c8b | ||
|
|
d8e672f796 | ||
|
|
b577397d22 | ||
|
|
832afa6c5a | ||
|
|
b667d25f2a | ||
|
|
e237b5541b | ||
|
|
bf62106953 | ||
|
|
09b181449f | ||
|
|
c073bc49c1 | ||
|
|
7b2a2031a2 | ||
|
|
a90a2dadd4 | ||
|
|
c21aa98034 | ||
|
|
c967e1500c | ||
|
|
99212cc139 | ||
|
|
caab348a39 | ||
|
|
f799374183 | ||
|
|
1f8a129137 | ||
|
|
ba11725087 | ||
|
|
79f8503c09 | ||
|
|
5c7f285b9d | ||
|
|
b7415e1855 | ||
|
|
3def1f59e5 | ||
|
|
7b8a79b8ea | ||
|
|
9695438fcd | ||
|
|
49cd974dcb | ||
|
|
739d373204 | ||
|
|
896cb83eb2 | ||
|
|
22f1261efa | ||
|
|
4648b9c8eb | ||
|
|
e77770f608 | ||
|
|
bfa2ae8bc4 | ||
|
|
9c13d21f69 | ||
|
|
16ce501582 | ||
|
|
61617db239 | ||
|
|
ac74a9c80e | ||
|
|
c8812db585 | ||
|
|
95aaa8fc2e | ||
|
|
deb9dbed61 | ||
|
|
0feb8dc73c | ||
|
|
6bfbdbd9df | ||
|
|
2160b369bb | ||
|
|
de91defc20 | ||
|
|
3fbff95b2b | ||
|
|
6facababd7 | ||
|
|
dc3dc80d8f | ||
|
|
202fbf3dbc | ||
|
|
f6283f4807 | ||
|
|
1f7ddbecdf | ||
|
|
129af0ba9e | ||
|
|
eed267ed3f | ||
|
|
e195770181 | ||
|
|
cbddea701a | ||
|
|
3eefa3365f | ||
|
|
d22f9ddfe3 | ||
|
|
5c9416313a | ||
|
|
4b063aaf97 | ||
|
|
bbfd4967cf | ||
|
|
c5742350d1 | ||
|
|
753286cdf6 | ||
|
|
22028b37ed | ||
|
|
3483c736ba | ||
|
|
20989c31c8 | ||
|
|
cee797017b | ||
|
|
bceac916fe | ||
|
|
7d8a081e3f | ||
|
|
4bd36ece9a | ||
|
|
b95644d5e5 | ||
|
|
791cc17b27 | ||
|
|
40b1b67b29 | ||
|
|
b8cb4ad521 | ||
|
|
6b50ca034b | ||
|
|
43df9c3ff5 | ||
|
|
dff48692ca | ||
|
|
44a8085463 | ||
|
|
934eb8264e | ||
|
|
dc1bb6a55e | ||
|
|
fe9b5b51e8 | ||
|
|
f35aa046c1 | ||
|
|
a708fa5f6e | ||
|
|
bb78b1bf27 | ||
|
|
931fae62c6 | ||
|
|
c1e657a5e3 | ||
|
|
1e2d42ce23 | ||
|
|
5d367a5c27 | ||
|
|
1d03f7efbf | ||
|
|
5125fd4d6c | ||
|
|
d079035adf | ||
|
|
06bfcdacc2 | ||
|
|
ab844202bc | ||
|
|
f05ddf8fcd | ||
|
|
f36df3ea09 | ||
|
|
df28ebe573 | ||
|
|
3a44affc63 | ||
|
|
19be1db46c | ||
|
|
7101c3588a | ||
|
|
5573244583 | ||
|
|
ad1db34c86 | ||
|
|
379cc25099 | ||
|
|
df6a6ce066 | ||
|
|
27735ccc01 | ||
|
|
d614eb088e | ||
|
|
f1088e9471 | ||
|
|
f6b8cc71cc | ||
|
|
d35144e0e9 | ||
|
|
f7fe1e066c | ||
|
|
18366dd294 | ||
|
|
106d717157 | ||
|
|
296775d231 | ||
|
|
114d8d7dfc | ||
|
|
2392f57d62 | ||
|
|
a0b199d208 | ||
|
|
6c4584a00a | ||
|
|
5d565c55a7 | ||
|
|
fbc902d16e | ||
|
|
051a65f0bb | ||
|
|
ffc311e0e9 | ||
|
|
d738eee5d9 | ||
|
|
58a00106d3 | ||
|
|
d0617714ca | ||
|
|
6ae3316cd1 | ||
|
|
c1577905cd | ||
|
|
d12f6a1191 | ||
|
|
0f5612fdd4 | ||
|
|
a77fa83c80 | ||
|
|
75bc2b7b35 | ||
|
|
ac6284819a | ||
|
|
2e09e5fdfa | ||
|
|
5c09130782 | ||
|
|
677b064e97 | ||
|
|
a30512f4e4 | ||
|
|
e20229208f | ||
|
|
f67bb81de2 | ||
|
|
f98cdfeb0f | ||
|
|
ba264c1557 | ||
|
|
11a08298ff | ||
|
|
72ac2f628b | ||
|
|
dd87bf77f8 | ||
|
|
d730ca2c25 | ||
|
|
0d09baee3c | ||
|
|
7ea26b68e7 | ||
|
|
e7334a6c4f | ||
|
|
404dc772d4 | ||
|
|
7754123e30 | ||
|
|
16b950d5a0 | ||
|
|
0ba3fee968 | ||
|
|
a2113fcdcc | ||
|
|
ec7fbeeb22 | ||
|
|
5f969bf4aa | ||
|
|
8a197aea45 | ||
|
|
c6bc10f1ba | ||
|
|
5830dfa991 | ||
|
|
fc44edb0fc | ||
|
|
cb02c0328c | ||
|
|
71d79ff45b | ||
|
|
c4e2730ced | ||
|
|
65fdd7c5b4 | ||
|
|
42fee3bd3e | ||
|
|
b4d45af268 | ||
|
|
7125f2fa5e | ||
|
|
4993ed00a2 | ||
|
|
a2b622a343 | ||
|
|
c401035209 | ||
|
|
4324afc593 | ||
|
|
de5b99dbb9 | ||
|
|
374999df1f | ||
|
|
ba7455c99e | ||
|
|
ad592cdc59 | ||
|
|
78b2dac71c | ||
|
|
5d5f224f66 | ||
|
|
c70762fbee | ||
|
|
166bbbbc72 | ||
|
|
8bdcaa047a | ||
|
|
ee557cc552 | ||
|
|
d7b99be51f | ||
|
|
ed85d508ed | ||
|
|
ffa8b053d7 | ||
|
|
c39ddbc7a7 | ||
|
|
fa7fd116a7 | ||
|
|
199913ce53 | ||
|
|
1ac2d84dc0 | ||
|
|
c55128fc21 | ||
|
|
a6cb8b6989 | ||
|
|
676c5f0f09 | ||
|
|
9acc014274 | ||
|
|
2237d16696 | ||
|
|
180fd1bfb0 | ||
|
|
77bcff0dcc | ||
|
|
b43e566f98 | ||
|
|
f4e0412368 | ||
|
|
18ec24f0e8 | ||
|
|
bf10289223 | ||
|
|
e3c3d15dce | ||
|
|
b726cc8faf | ||
|
|
133746d3a3 | ||
|
|
56adbfe6fd | ||
|
|
c2e5b919d6 | ||
|
|
b675a8fbcd | ||
|
|
d5d84d598e | ||
|
|
aac17ca47d | ||
|
|
ead9bb77ec | ||
|
|
91aac9b61a | ||
|
|
d0aa9e66ca | ||
|
|
b8f92e9661 | ||
|
|
78cb583a4d | ||
|
|
69f6710898 | ||
|
|
ca3c648e3a | ||
|
|
cc581637c5 | ||
|
|
5a05011992 | ||
|
|
daa200d655 | ||
|
|
34b0040b04 | ||
|
|
24c08dfbbd | ||
|
|
390c7939f9 | ||
|
|
fa6fe06068 | ||
|
|
7193bca136 | ||
|
|
67b7272b05 | ||
|
|
88c9959e8e | ||
|
|
e4dcad7873 | ||
|
|
bd1f6881ce | ||
|
|
4b7b5d5a29 | ||
|
|
42c5bd16cd | ||
|
|
501e7c762c | ||
|
|
a01a01b533 | ||
|
|
6d56db17f4 | ||
|
|
d0040fd2a1 | ||
|
|
e5dd5eed5d | ||
|
|
dda84e74bc | ||
|
|
19712bf401 | ||
|
|
76b6aa46bd | ||
|
|
20ca710523 | ||
|
|
f98bad18b9 | ||
|
|
0ecc9765dd | ||
|
|
5d26333676 | ||
|
|
2d2af754c5 | ||
|
|
3939a5b1cf | ||
|
|
fc99153c80 | ||
|
|
076c7d669b | ||
|
|
c8020c76ae | ||
|
|
696c9663dd | ||
|
|
92144ee482 | ||
|
|
bd0b3f2856 | ||
|
|
529148ee18 | ||
|
|
810aa04eb0 | ||
|
|
20abfd4b8e | ||
|
|
bb76449560 | ||
|
|
fc6b35c7f5 | ||
|
|
e6da485117 | ||
|
|
d6160f7b7a | ||
|
|
2aedef7e77 | ||
|
|
11fd605d6b | ||
|
|
7b87ecca87 | ||
|
|
ea996f6442 | ||
|
|
e0182068b2 | ||
|
|
cd01902023 | ||
|
|
5e386e90e0 | ||
|
|
e28121f5b3 | ||
|
|
7a25e5d998 | ||
|
|
c6488a4a26 | ||
|
|
f32d18a5a3 | ||
|
|
3297bd1e9e | ||
|
|
6f9ce1f7ad | ||
|
|
a21d0ac05e | ||
|
|
584ca9e17a | ||
|
|
fb1abfbd26 | ||
|
|
e6dd71f338 | ||
|
|
46bf41fe15 | ||
|
|
01b6eccf4c | ||
|
|
e1924db1ff | ||
|
|
76309399b0 | ||
|
|
9cbb4ec36b | ||
|
|
973af860cf | ||
|
|
bc90ade337 | ||
|
|
ab54b2f6f8 | ||
|
|
72626546c5 | ||
|
|
c08dcb064c | ||
|
|
7525735df1 | ||
|
|
f28559ae6b | ||
|
|
e8957aa90f | ||
|
|
4fdc302809 | ||
|
|
c32b2bd7aa | ||
|
|
a121f33be8 | ||
|
|
f65e65fb47 | ||
|
|
a4ddcd4220 | ||
|
|
481dd0adb5 | ||
|
|
001d28aaf8 | ||
|
|
86e0a3ad77 | ||
|
|
a6a300e7c5 | ||
|
|
55a1d5a9f3 | ||
|
|
81f9cd6ee6 | ||
|
|
459151b4bf | ||
|
|
fcad1789dc | ||
|
|
1c3204e5d7 | ||
|
|
84df9bbca7 | ||
|
|
d073e7cae8 | ||
|
|
3c2ce0bb18 | ||
|
|
9aee34a501 | ||
|
|
cc5248e958 | ||
|
|
d880d05ea5 | ||
|
|
d882962b9c | ||
|
|
241cc05f80 | ||
|
|
cdcdc106ef | ||
|
|
a6bb5ed28d | ||
|
|
e1348b838b | ||
|
|
677ad50f79 | ||
|
|
6903d230fa | ||
|
|
b2cc2d4c11 | ||
|
|
763d28981c | ||
|
|
f274f74f44 | ||
|
|
9dfbcc886f | ||
|
|
5e0f64a7c2 | ||
|
|
18f24215e5 | ||
|
|
23e1c23789 | ||
|
|
a46523ee84 | ||
|
|
dc9aa270e5 | ||
|
|
ae528f844f | ||
|
|
d52c2b6426 | ||
|
|
29cd61fdcd | ||
|
|
daecac1612 | ||
|
|
abbf153785 | ||
|
|
1667019662 | ||
|
|
e2fbece993 | ||
|
|
d157dee956 | ||
|
|
9506bd5f77 | ||
|
|
2222d86e02 | ||
|
|
2161c3d762 | ||
|
|
eb5de342ba | ||
|
|
2ca9117783 | ||
|
|
b1809e9403 | ||
|
|
d1b11bc53c | ||
|
|
c61d2dd35d | ||
|
|
d8958662d9 | ||
|
|
801cdead80 | ||
|
|
5098cdb445 | ||
|
|
f229f0d648 | ||
|
|
cbaed9337b | ||
|
|
66ff2551e9 | ||
|
|
0a7e86a8fa | ||
|
|
e3c96f2d5d | ||
|
|
24741ed420 | ||
|
|
1f6f8f194b | ||
|
|
102966482b | ||
|
|
eb7929e758 | ||
|
|
8bbfdf2671 | ||
|
|
958400eaf6 | ||
|
|
20e862a6a0 | ||
|
|
8cbb30b036 | ||
|
|
9696042156 | ||
|
|
56b27dd055 | ||
|
|
a388e9ad85 | ||
|
|
2e93cca73a | ||
|
|
6a694444c5 | ||
|
|
8227b49b59 | ||
|
|
dd483c3300 | ||
|
|
f217f17c52 | ||
|
|
be3f69a2d0 | ||
|
|
bf96b96fdf | ||
|
|
c7537cca96 | ||
|
|
14a87465ce | ||
|
|
8c8df5b1b4 | ||
|
|
a56c736c06 | ||
|
|
7ff3e3e79b | ||
|
|
c5713c52da | ||
|
|
5bea494193 | ||
|
|
e48b715f4f | ||
|
|
12ac9460b9 | ||
|
|
c8c72eadab | ||
|
|
cd476bb463 | ||
|
|
53617bd366 | ||
|
|
99f2606d4d | ||
|
|
2aebdf79f3 | ||
|
|
c840544d25 | ||
|
|
927df66e9a | ||
|
|
7f4443c77f | ||
|
|
1600e6c0d4 | ||
|
|
0c86ba29fc | ||
|
|
b2f6b158e3 | ||
|
|
6ab2f69111 | ||
|
|
0097942732 | ||
|
|
3686e7fdd2 | ||
|
|
0e8f3362b9 | ||
|
|
078142bd3f | ||
|
|
304817b68e | ||
|
|
e48c05a0ab | ||
|
|
4cdcefb980 | ||
|
|
dcc818e9c3 | ||
|
|
0037064958 | ||
|
|
c0ccbf73d0 | ||
|
|
500d0387fd | ||
|
|
0f077832cf | ||
|
|
449468f566 | ||
|
|
fd2444f2cd | ||
|
|
d6968dffdd | ||
|
|
b50119e0bc | ||
|
|
58e90298eb | ||
|
|
224dc1e68c | ||
|
|
e9262f18db | ||
|
|
14b4af8b33 | ||
|
|
af271f62f3 | ||
|
|
6f9fba9fbf | ||
|
|
afa329b5e4 | ||
|
|
d8395fff59 | ||
|
|
81abd65da1 | ||
|
|
a48c197ecc | ||
|
|
85d53896de | ||
|
|
8b844aa6e3 | ||
|
|
578117d82a | ||
|
|
363c99ceca | ||
|
|
a93177eb61 | ||
|
|
d6181e2dcd | ||
|
|
f9b74cf7e2 | ||
|
|
d4740550e4 | ||
|
|
d91c44f6f3 | ||
|
|
e59b46448a | ||
|
|
d826391d16 | ||
|
|
5406960f26 | ||
|
|
8f1d1e4934 | ||
|
|
54b6fd67e7 | ||
|
|
1568ee6176 | ||
|
|
6978ac9cd3 | ||
|
|
1961eb832a | ||
|
|
c38423c251 | ||
|
|
ecd3ec1c61 | ||
|
|
66e8bcd86a | ||
|
|
23b1036c6b | ||
|
|
0d4ab17d0a | ||
|
|
b38a692fc5 | ||
|
|
2ff96a6b91 | ||
|
|
9089d6568a | ||
|
|
73c43b030e | ||
|
|
42fa737844 | ||
|
|
e4f61115c6 | ||
|
|
3bb9994589 | ||
|
|
23f6c75257 | ||
|
|
3bac7ba99b | ||
|
|
56a5df86a0 | ||
|
|
50d707a8e3 | ||
|
|
aa49e5869b | ||
|
|
d982a2b91e | ||
|
|
a92736bb3a | ||
|
|
54386285cc | ||
|
|
676027a56f | ||
|
|
cdc0d9e012 | ||
|
|
32d4c0ed72 | ||
|
|
16959cac7a | ||
|
|
f2f20f8f94 | ||
|
|
1a8efd5835 | ||
|
|
5a01c7a4db | ||
|
|
1b18139e41 | ||
|
|
263b3bbeda | ||
|
|
cba01aaa59 | ||
|
|
d4599b05a0 | ||
|
|
200287316d | ||
|
|
1d05f3d851 | ||
|
|
8e8d987686 | ||
|
|
343512e9bf | ||
|
|
af612c058b | ||
|
|
d81e8be3cf | ||
|
|
fb93289538 | ||
|
|
92a2217e80 | ||
|
|
b6d41260e7 | ||
|
|
bb6b54990f | ||
|
|
016d27e22d | ||
|
|
17a336e27d | ||
|
|
c3f3573a99 | ||
|
|
81dab2f0b6 | ||
|
|
a80e534179 | ||
|
|
8310f87504 | ||
|
|
f27057c790 | ||
|
|
649d267fa5 | ||
|
|
d8a7e5d366 | ||
|
|
8ccc241743 | ||
|
|
af922d209d | ||
|
|
958d0debbb | ||
|
|
2016891bd5 | ||
|
|
412cb26c0f | ||
|
|
3fc983f273 | ||
|
|
b5d24f9c00 | ||
|
|
a432173cd6 | ||
|
|
5440161ff9 | ||
|
|
bb542c475c | ||
|
|
aef834e914 | ||
|
|
fd44120874 | ||
|
|
0e9b2b245d | ||
|
|
eacbd21c50 | ||
|
|
deca8e74a8 | ||
|
|
9eefbb26d8 | ||
|
|
b637462ee8 | ||
|
|
7fc513e09e | ||
|
|
802e1c96bd | ||
|
|
0bc75f6f78 | ||
|
|
32a3ba535b | ||
|
|
2e898d997b | ||
|
|
817b335349 | ||
|
|
e9d551f4cb | ||
|
|
33933e914e | ||
|
|
8e230a08b5 | ||
|
|
dfc4f6f304 | ||
|
|
5a3061f942 | ||
|
|
b40281fda3 | ||
|
|
966fbf8c6a | ||
|
|
ca65f1b209 | ||
|
|
a5d73f06d2 | ||
|
|
56b0e8440e | ||
|
|
b6a12b6ceb | ||
|
|
15a12acfd3 | ||
|
|
c534e0f1fe | ||
|
|
5e68580fa6 | ||
|
|
e6d83e7305 | ||
|
|
d56cf106b4 | ||
|
|
4279ccf292 | ||
|
|
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 |
5
.gitignore
vendored
@@ -5,3 +5,8 @@ build
|
|||||||
*.sh
|
*.sh
|
||||||
*.csv
|
*.csv
|
||||||
examples
|
examples
|
||||||
|
*.bin
|
||||||
|
*.ans
|
||||||
|
*.patch
|
||||||
|
*.zip
|
||||||
|
result
|
||||||
@@ -1,11 +1,52 @@
|
|||||||
|
#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)
|
project(dsd-fme)
|
||||||
cmake_minimum_required(VERSION 2.8.11)
|
|
||||||
|
|
||||||
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
|
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
|
||||||
#Set curses to ncurses, and wide true for ascii
|
#Set curses to ncurses, and wide true for ascii
|
||||||
set(CURSES_NEED_NCURSES TRUE)
|
set(CURSES_NEED_NCURSES TRUE)
|
||||||
set(CURSES_NEED_WIDE 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 -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 -DSID=ON to enable P25p1 Soft ID decoding -- Experimental
|
||||||
|
option(SID
|
||||||
|
"Build with P25p1 LSD/Soft ID Enabled" OFF)
|
||||||
|
if (SID)
|
||||||
|
add_definitions(-DSOFTID)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
|
||||||
include(git_revision)
|
include(git_revision)
|
||||||
git_describe(GIT_TAG)
|
git_describe(GIT_TAG)
|
||||||
|
|
||||||
@@ -14,10 +55,11 @@ find_package(MBE REQUIRED)
|
|||||||
find_package(ITPP REQUIRED)
|
find_package(ITPP REQUIRED)
|
||||||
find_package(RTLSDR)
|
find_package(RTLSDR)
|
||||||
find_package(Curses REQUIRED)
|
find_package(Curses REQUIRED)
|
||||||
find_package(PULSEAUDIO REQUIRED)
|
find_package(PulseAudio REQUIRED)
|
||||||
|
find_package(CODEC2)
|
||||||
|
|
||||||
include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${ITPP_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIR})
|
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})
|
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${PULSEAUDIO_LIBRARY} ${CURSES_LIBRARY})
|
||||||
|
|
||||||
if(RTLSDR_FOUND)
|
if(RTLSDR_FOUND)
|
||||||
find_package(Threads)
|
find_package(Threads)
|
||||||
@@ -26,6 +68,12 @@ if(RTLSDR_FOUND)
|
|||||||
add_definitions(-DUSE_RTLSDR)
|
add_definitions(-DUSE_RTLSDR)
|
||||||
endif(RTLSDR_FOUND)
|
endif(RTLSDR_FOUND)
|
||||||
|
|
||||||
|
if(CODEC2_FOUND)
|
||||||
|
include_directories(SYSTEM ${CODEC2_INCLUDE_DIRS})
|
||||||
|
list(APPEND LIBS ${CODEC2_LIBRARIES})
|
||||||
|
add_definitions(-DUSE_CODEC2)
|
||||||
|
endif(CODEC2_FOUND)
|
||||||
|
|
||||||
FILE(GLOB SRCS src/*.c src/*.cpp)
|
FILE(GLOB SRCS src/*.c src/*.cpp)
|
||||||
FILE(GLOB HEADERS include/*.h include/*.hpp)
|
FILE(GLOB HEADERS include/*.h include/*.hpp)
|
||||||
|
|
||||||
@@ -41,6 +89,11 @@ include_directories("${PROJECT_SOURCE_DIR}/include")
|
|||||||
ADD_EXECUTABLE(dsd-fme ${SRCS} ${HEADERS})
|
ADD_EXECUTABLE(dsd-fme ${SRCS} ${HEADERS})
|
||||||
TARGET_LINK_LIBRARIES(dsd-fme ${LIBS})
|
TARGET_LINK_LIBRARIES(dsd-fme ${LIBS})
|
||||||
|
|
||||||
|
target_compile_options(dsd-fme PRIVATE -Wunused-but-set-variable -Wunused-variable -Wunused-parameter
|
||||||
|
-Wempty-body -Wunused-label $<$<COMPILE_LANGUAGE:C>:-Wpointer-sign>
|
||||||
|
-Wmisleading-indentation -Wparentheses -Wunused-value -Wreturn-type
|
||||||
|
-Wtautological-compare)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
install(TARGETS dsd-fme DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS dsd-fme DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
||||||
|
|||||||
19
README.md
@@ -2,25 +2,17 @@
|
|||||||
# Digital Speech Decoder - Florida Man Edition
|
# 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
|
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"), [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"), 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, etc, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users, so please understand that this version may not compile or run correctly in other environments.
|
[Osmocom OP25](https://gitea.osmocom.org/op25/op25 "Osmocom OP25"), along with other snippets of code, information, and inspirations from other projects including [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDRTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), [OK-DMRlib](https://github.com/OK-DMR/ok-dmrlib "OK-DMRlib"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"), Eric Cottrell, SP5WWP and others. Finally, this is all brought together with original code to extend the fuctionality and add new features including NCurses Terminal and Menu system, Pulse Audio, TCP Direct Link Audio, RIGCTL, Trunking Features, LRRP/GPS Mapping, P25 Phase 2, EDACS, YSF, M17, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users in mind, so please understand that this version may not compile, compile easily, or run correctly in other environments.
|
||||||
|
|
||||||
This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu and others for the many hours of wav samples submitted 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. Thank you everybody.
|
This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu, Forts, thewraithe2008, RayAir, Cretu, ilyacodes, and others for the many hours of wav samples and information provided by them. Most importantly, HRH17, whose insight, information, samples, and willingness to let me remote into a computer half-way across the globe in order to test trunking features are what make DSD-FME what it has become. I'd also like to thank mrscanner2008 for providing an additional remote where additional NXDN Type-C, 'Idas' Type-D, and XPT decoding and trunking could be sorted out. Thanks to volo-zyko for cleaning up a lot of code. Thank you everybody.
|
||||||
|
|
||||||
Click image below for video of current DMR/P25 trunking capabilities.
|

|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=hGIijBbbUxg "DSD-FME Trunking Demo")
|

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

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

|
|
||||||
|
|
||||||
## Information
|
## Information
|
||||||
|
|
||||||
See the example folder for information on cloning, installing, example usage, and trunking features.
|
See the [examples](https://github.com/lwvmobile/dsd-fme/tree/audio_work/examples "examples") folder for information on [cloning and installing](https://github.com/lwvmobile/dsd-fme/blob/audio_work/examples/Install_Notes.md "cloning and installing"), [example usage](https://github.com/lwvmobile/dsd-fme/blob/audio_work/examples/Example_Usage.md "example usage"), and [trunking examples](https://github.com/lwvmobile/dsd-fme/blob/audio_work/examples/trunking.sh "trunking examples").
|
||||||
|
|
||||||
## Notice
|
|
||||||
|
|
||||||
The pulseaudio branch will hearby be for legacy use as the promotion of this version to main branch will obsolete that branch. Any pre-compiled windows versions found in the release link will be considered legacy as well, and do not feature trunking support and have older handling for some other modes. Users are free to use those, but keep in mind the use case syntax may and will be different between versions. Furthermore, any wiki articles made for windows users may contain outdated information for building the pulseaudio branch, please adjust any information found in those accordingly.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Copyright (C) 2010 DSD Author
|
Copyright (C) 2010 DSD Author
|
||||||
@@ -38,4 +30,3 @@ GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|||||||
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.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
31
cmake/FindCODEC2.cmake
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# - Try to find CODEC2
|
||||||
|
# Once done this will define
|
||||||
|
|
||||||
|
#Seems to work on Cygwin as well now -- more testing for may be required
|
||||||
|
# Not sure what causes the 'Package 'codec2', required by 'virtual:world', not found' message, but seems to work anyways
|
||||||
|
# -- Checking for module 'codec2'
|
||||||
|
# -- Package 'codec2', required by 'virtual:world', not found
|
||||||
|
# -- Found CODEC2: /usr/local/lib/libcodec2.dll.a
|
||||||
|
|
||||||
|
if (NOT CODEC2_FOUND)
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(CODEC2_PKG codec2)
|
||||||
|
set(CODEC2_DEFINITIONS ${PC_CODEC2_CFLAGS_OTHER})
|
||||||
|
|
||||||
|
find_path(CODEC2_INCLUDE_DIR
|
||||||
|
NAMES codec2/codec2.h codec2.h
|
||||||
|
HINTS ${CODEC2_PKG_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
find_library(CODEC2_LIBRARY
|
||||||
|
NAMES codec2/codec2 codec2
|
||||||
|
HINTS ${CODEC2_PKG_LIBRARY_DIRS})
|
||||||
|
|
||||||
|
set(CODEC2_LIBRARIES ${CODEC2_LIBRARY})
|
||||||
|
set(CODEC2_INCLUDE_DIRS ${CODEC2_INCLUDE_DIR})
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(CODEC2 DEFAULT_MSG CODEC2_LIBRARY CODEC2_INCLUDE_DIR)
|
||||||
|
mark_as_advanced(CODEC2_INCLUDE_DIR CODEC2_LIBRARY)
|
||||||
|
|
||||||
|
endif (NOT CODEC2_FOUND)
|
||||||
52
download-and-install-Arch.sh
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
#
|
||||||
|
cdir=$(pwd)
|
||||||
|
clear
|
||||||
|
printf "Digital Speech Decoder: Florida Man Edition - Auto Installer For Arch Linux\n
|
||||||
|
This will install the required packages, clone, build, and install DSD-FME only.
|
||||||
|
This has been tested on Arch 2023.08.01 and Manjaro XFCE 22.1.3 Minimal.\n
|
||||||
|
MBELib is considered a requirement on this build.
|
||||||
|
You must view the Patent Notice prior to continuing.
|
||||||
|
The Patent Notice can be found at the site below.
|
||||||
|
https://github.com/lwvmobile/mbelib#readme
|
||||||
|
Please confirm that you have viewed the patent notice by entering y below.\n\n"
|
||||||
|
read -p "Have you viewed the patent notice? y/N " ANSWER
|
||||||
|
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
|
||||||
|
if [ "$ANSWER" = "y" ]; then
|
||||||
|
|
||||||
|
sudo pacman -Syu #always run a full update first, partial upgrades aren't supported in Arch -- including downloading dependencies, that may require an updated dependency, and breaks same dependency on another package, a.k.a, dependency hell
|
||||||
|
sudo pacman -S libpulse cmake ncurses lapack perl fftw rtl-sdr codec2 base-devel libsndfile git wget
|
||||||
|
|
||||||
|
printf "Installing itpp 4.3.1 from Arch Strike --Tested on Arch 2023.08.01 and Manjaro XFCE 22.1.3 Minimal\n"
|
||||||
|
wget https://mirror.archstrike.org/x86_64/archstrike/itpp-4.3.1-3-x86_64.pkg.tar.xz
|
||||||
|
sudo pacman -U itpp-4.3.1-3-x86_64.pkg.tar.xz
|
||||||
|
sudo rm -R itpp-4.3.1-3-x86_64.pkg.tar.xz
|
||||||
|
|
||||||
|
git clone https://github.com/lwvmobile/mbelib
|
||||||
|
cd mbelib
|
||||||
|
git checkout ambe_tones
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j $(nproc)
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
sudo cp /usr/local/lib/libmbe.so /usr/lib/libmbe.so
|
||||||
|
cd $cdir
|
||||||
|
|
||||||
|
git clone https://github.com/lwvmobile/dsd-fme
|
||||||
|
### For branch audio_work - git clone --branch audio_work https://github.com/lwvmobile/dsd-fme
|
||||||
|
cd dsd-fme
|
||||||
|
#git checkout audio_work
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j $(nproc)
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
|
||||||
|
printf "Any issues, Please report to either:\nhttps://github.com/lwvmobile/dsd-fme/issues or\nhttps://forums.radioreference.com/threads/dsd-fme.438137/\n\n"
|
||||||
|
|
||||||
|
else
|
||||||
|
printf "Sorry, you cannot build DSD-FME without acknowledging the Patent Notice.\n\n"
|
||||||
|
fi
|
||||||
@@ -1,23 +1,17 @@
|
|||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
clear
|
clear
|
||||||
echo DSD-FME Digital Speech Decoder - Florida Man Edition Auto Installer
|
printf "\nDigital Speech Decoder: Florida Man Edition - Auto Installer\n
|
||||||
echo This will clone, build, and install DSD-FME only.
|
This will clone, build, and install DSD-FME only.
|
||||||
echo If you need dependencies installed, please run download-and-install.sh instead.
|
If DSD-FME has never been installed on this machine,
|
||||||
echo
|
then you will need to install the dependencies first!
|
||||||
|
Please run download-and-install.sh instead.\n\n"
|
||||||
read -p "Press enter to continue"
|
read -p "Press Enter to continue..." x
|
||||||
|
|
||||||
git clone https://github.com/lwvmobile/dsd-fme
|
git clone https://github.com/lwvmobile/dsd-fme
|
||||||
cd dsd-fme
|
cd dsd-fme
|
||||||
sudo cp tone8.wav /usr/share/
|
#git checkout audio_work
|
||||||
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
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
make -j `nproc`
|
make -j $(nproc)
|
||||||
sudo make install
|
sudo make install
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
44
download-and-install-ubuntu2404lts.sh
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
#
|
||||||
|
cdir=$(pwd)
|
||||||
|
clear
|
||||||
|
printf "Digital Speech Decoder: Florida Man Edition - Auto Installer\n
|
||||||
|
MBELib is considered a requirement on this build.
|
||||||
|
You must view the Patent Notice prior to continuing.
|
||||||
|
The Patent Notice can be found at the site below.
|
||||||
|
https://github.com/lwvmobile/mbelib#readme
|
||||||
|
Please confirm that you have viewed the patent notice by entering y below:\n\n"
|
||||||
|
read -p "Have you viewed the patent notice? y/N " ANSWER
|
||||||
|
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
|
||||||
|
if [ "$ANSWER" = "y" ]; then
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncurses-dev libncurses6 libcodec2-dev
|
||||||
|
chkitpp=$(sudo apt list libitpp-dev 2>&1|grep -ci "installed")
|
||||||
|
if [ "$chkitpp" -lt "1" ];then
|
||||||
|
printf "\nFor some reason, ITPP did NOT install!\nYou may have to manually install it.\n\n"
|
||||||
|
read -p "Press Enter to exit..." x
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
git clone https://github.com/lwvmobile/mbelib
|
||||||
|
cd mbelib
|
||||||
|
git checkout ambe_tones
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j $(nproc)
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
cd $cdir
|
||||||
|
git clone https://github.com/lwvmobile/dsd-fme
|
||||||
|
cd dsd-fme
|
||||||
|
#git checkout audio_work
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j $(nproc)
|
||||||
|
sudo make install
|
||||||
|
sudo ldconfig
|
||||||
|
else
|
||||||
|
printf "\nSorry, you cannot build DSD-FME without acknowledging the Patent Notice.\nExiting...\n\n"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
@@ -1,67 +1,44 @@
|
|||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
|
#
|
||||||
|
cdir=$(pwd)
|
||||||
clear
|
clear
|
||||||
echo DSD-FME Digital Speech Decoder - Florida Man Edition Auto Installer
|
printf "Digital Speech Decoder: Florida Man Edition - Auto Installer\n
|
||||||
echo MBELib is considered a requirement on this build.
|
MBELib is considered a requirement on this build.
|
||||||
echo You must view this notice prior to continuing.
|
You must view the Patent Notice prior to continuing.
|
||||||
echo The Patent Notice can be found at the site below.
|
The Patent Notice can be found at the site below.
|
||||||
echo https://github.com/lwvmobile/mbelib#readme
|
https://github.com/lwvmobile/mbelib#readme
|
||||||
echo Please confirm that you have viewed the patent notice by entering y below.
|
Please confirm that you have viewed the patent notice by entering y below:\n\n"
|
||||||
echo
|
read -p "Have you viewed the patent notice? y/N " ANSWER
|
||||||
echo y/N
|
ANSWER=$(printf "$ANSWER"|tr '[:upper:]' '[:lower:]')
|
||||||
read ANSWER
|
if [ "$ANSWER" = "y" ]; then
|
||||||
Y='y'
|
sudo apt update
|
||||||
if [[ $Y == $ANSWER ]]; then
|
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 libcodec2-dev
|
||||||
|
chkitpp=$(sudo apt list libitpp-dev 2>&1|grep -ci "installed")
|
||||||
sudo apt update
|
if [ "$chkitpp" -lt "1" ];then
|
||||||
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
|
printf "\nFor some reason, ITPP did NOT install!\nYou may have to manually install it.\n\n"
|
||||||
|
read -p "Press Enter to exit..." x
|
||||||
echo ITPP Manual Build and Install has been temporarily removed from this script due to issues with older versions of Ubuntu.
|
exit 1
|
||||||
echo Please check to see that libitpp-dev has successfully been downloaded and installed from the repo.
|
fi
|
||||||
read -p "Press enter to continue"
|
git clone https://github.com/lwvmobile/mbelib
|
||||||
|
cd mbelib
|
||||||
#wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
|
git checkout ambe_tones
|
||||||
#tar xjf itpp*
|
mkdir build
|
||||||
#if you can't cd into this folder, double check folder name first
|
cd build
|
||||||
#cd itpp-4.3.1
|
cmake ..
|
||||||
#mkdir build
|
make -j $(nproc)
|
||||||
#cd build
|
sudo make install
|
||||||
#cmake ..
|
sudo ldconfig
|
||||||
#make -j `nproc`
|
cd $cdir
|
||||||
#sudo make install
|
git clone https://github.com/lwvmobile/dsd-fme
|
||||||
#sudo ldconfig
|
cd dsd-fme
|
||||||
#cd ..
|
#git checkout audio_work
|
||||||
#cd ..
|
mkdir build
|
||||||
|
cd build
|
||||||
git clone https://github.com/lwvmobile/mbelib
|
cmake ..
|
||||||
cd mbelib
|
make -j $(nproc)
|
||||||
mkdir build
|
sudo make install
|
||||||
cd build
|
sudo ldconfig
|
||||||
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
|
else
|
||||||
echo
|
printf "\nSorry, you cannot build DSD-FME without acknowledging the Patent Notice.\nExiting...\n\n"
|
||||||
echo Sorry, you cannot build DSD-FME without acknowledging the Patent Notice.
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
dsd-fme.png
|
Before Width: | Height: | Size: 706 KiB |
BIN
dsd-fme2.png
|
Before Width: | Height: | Size: 315 KiB After Width: | Height: | Size: 393 KiB |
BIN
dsd-fme3.png
|
Before Width: | Height: | Size: 692 KiB After Width: | Height: | Size: 721 KiB |
74
examples/Audio_Plumbing.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
|
||||||
|
|
||||||
|
## Audio Plumbing
|
||||||
|
|
||||||
|
While using DSD-FME to demodulate and decode RF Audio, you will find it necessary to properly plumb (or route) audio from each input source and output sink properly. This mini tutorial aims to help you do such when using Pulse Audio.
|
||||||
|
|
||||||
|
You will first want to locate and run the virtualsink.sh script file via `sh virtualsink.sh` command. This will create two null-sink, or 'virtual sinks' for us to plumb audio from point A to point B.
|
||||||
|
|
||||||
|
Here are screenshots of a proper setup using pavucontrol, or "Pulse Audio Volume Control", having voice input from the encoder listening to your microphone, the RF Output going into the "Virtual_Sink" null-sink, and on the playback side, playing back voice to our speakers, with RF modulated audio being monitored on "Virtual-Sink" null-sink.
|
||||||
|
|
||||||
|

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

|
||||||
|
|
||||||
|
The general idea is that we want to route RF modulated audio into our Virtual_Sink and have our decoder listen to the same sink. Voice Output should always be to Audio Hardware (speakers, headphones, etc).
|
||||||
|
|
||||||
|
Note, that using the virtualsink.sh will create these sinks for us for the duration of your computers boot. If you reboot, they will not persist. Configuration files can be modified to always have these sinks available, or the `virtualsink.sh` script can be executed on login, but you must do so at your own risk and own research, it is out of the scope of this tutorial to create persistent sinks.
|
||||||
|
|
||||||
|
In case of the usage of a headless environment and pavucontrol is not viable, you may use alternatives such as pulsemixer which is similar in functionality, just as a CLI tool and not a GUI tool.
|
||||||
|
|
||||||
|
Users can now use the `-O` CLI argument to list out all pulse audio input sources and output sinks and pass an optional argument alongside `pulse`, appending the name or index number to select the device each time. This method will work in both Linux environments as well as Cygwin builds (making sure to start the pulseaudio.exe server in the back first with the specified .bat file or command, see below).
|
||||||
|
|
||||||
|
For Example:
|
||||||
|
|
||||||
|
The Output (truncated) of `dsd-fme -O`
|
||||||
|
|
||||||
|
```
|
||||||
|
=======[ Output Device #1 ]=======
|
||||||
|
Description: Family 17h (Models 00h-0fh) HD Audio Controller Analog Stereo
|
||||||
|
Name: alsa_output.pci-0000_0d_00.3.analog-stereo
|
||||||
|
Index: 1
|
||||||
|
|
||||||
|
=======[ Output Device #4 ]=======
|
||||||
|
Description: Virtual_Sink1
|
||||||
|
Name: virtual_sink1
|
||||||
|
Index: 4
|
||||||
|
|
||||||
|
=======[ Output Device #5 ]=======
|
||||||
|
Description: Virtual_Sink2
|
||||||
|
Name: virtual_sink2
|
||||||
|
Index: 5
|
||||||
|
|
||||||
|
=======[ Input Device #2 ]=======
|
||||||
|
Description: Family 17h (Models 00h-0fh) HD Audio Controller Analog Stereo
|
||||||
|
Name: alsa_input.pci-0000_0d_00.3.analog-stereo
|
||||||
|
Index: 2
|
||||||
|
|
||||||
|
=======[ Input Device #5 ]=======
|
||||||
|
Description: Monitor of Virtual_Sink1
|
||||||
|
Name: virtual_sink1.monitor
|
||||||
|
Index: 5
|
||||||
|
|
||||||
|
=======[ Input Device #6 ]=======
|
||||||
|
Description: Monitor of Virtual_Sink2
|
||||||
|
Name: virtual_sink2.monitor
|
||||||
|
Index: 6
|
||||||
|
```
|
||||||
|
|
||||||
|
You might use any of the following for input and output options:
|
||||||
|
|
||||||
|
```
|
||||||
|
-i pulse
|
||||||
|
-i pulse:6
|
||||||
|
-i pulse:virtual_sink2.monitor
|
||||||
|
|
||||||
|
-o pulse
|
||||||
|
-o pulse:1
|
||||||
|
-o pulse:alsa_output.pci-0000_0d_00.3.analog-stereo
|
||||||
|
```
|
||||||
|
|
||||||
|
Something to keep in mind is that often times, you only need to set these values once, as a pulse audio server (or plugin) can often times remember these settings, even across reboots, as long as the same hardware is present for future use (real or null-sink).
|
||||||
|
|
||||||
|
<!-- TODO: Add This later -->
|
||||||
|
<!-- Cygwin Note: Running the supplied `0p - start-pulse-audio-backend.bat` file will not only start up the pulse audio server for you, but also setup your null-sinks, and enumerate all devices for you to use, making it an easy selection for your start up .bat files. See any notes in the precomplied .zip file for more information. -->
|
||||||
@@ -1,21 +1,44 @@
|
|||||||
### Example Usage and Notes!
|
### 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 (1 and 2) . 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.
|
`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.
|
||||||
|
|
||||||
```
|
```
|
||||||
-ft XDMA decoder class (P25 1 and 2, and DMR Stereo)
|
-ft XDMA decoder class (P25p1 and p2, YSF, and DMR Stereo)
|
||||||
-fs DMR Stereo, also may need to use -xr if inverted DMR
|
-fs DMR Stereo, also may need to use -xr if inverted DMR
|
||||||
-fa Legacy Auto (not recommended)
|
-fa Legacy Auto (not recommended)
|
||||||
-fi NXDN48
|
-fi NXDN48
|
||||||
-fn NXDN96
|
-fn NXDN96
|
||||||
-fp ProVoice
|
-fp Provoice
|
||||||
|
-fh EDACS Standard / Network
|
||||||
|
-fH EDACS Standard / Network with ESK
|
||||||
|
-fe EDACS Extended Addresssing
|
||||||
|
-fE EDACS Extended Addressing with ESK
|
||||||
-fm dPMR, also may need to use -xd if inverted dPMR.
|
-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
|
-f1 P25P1
|
||||||
-f2 P25P2 (will need to specify wacn/sys/nac manually)
|
-f2 P25P2 (may need to specify wacn/sys/nac manually if traffic channel)
|
||||||
-fx X2-TDMA
|
-fx X2-TDMA
|
||||||
|
-fy YSF
|
||||||
|
-fz M17 Audio
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Conventional Frequency Scanner
|
||||||
|
This feature is used to allow DSD-FME to use RIGCTL or RTL input and cycle through frequencies 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 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 scan switch. Note: This method will NOT perform at a level similar to a real hardware scanner, SDR tuning is not nearly as fast or reliable at locking onto signal.
|
||||||
|
|
||||||
|
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.~~ Using `-t 0` will forego the one second wait time, and this can work in some situations, but is not advised as it may skip during the middle of transmissions, or skip over them entirely without a framesync.
|
||||||
|
|
||||||
|
Please Note: DMR Simplex may not behave well on 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
|
## Trunking Examples
|
||||||
Please see trunking.sh for sample start up scripts or scroll down to the bottom for more explanation.
|
Please see trunking.sh for sample start up scripts or scroll down to the bottom for more explanation.
|
||||||
|
|
||||||
@@ -26,7 +49,9 @@ and in a second terminal tab, same folder, run
|
|||||||
|
|
||||||
`tail -n 40 -f voice.log`
|
`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)
|
### Audio Plumbing
|
||||||
|
|
||||||
|
Please see [Audio Plumbing](https://github.com/lwvmobile/dsd-fme/blob/aw_dev/examples/Audio_Plumbing.md "Audio Plumbing") for general help with audio plumbing (routing from A to B).
|
||||||
|
|
||||||
### Input ###
|
### Input ###
|
||||||
|
|
||||||
@@ -45,26 +70,33 @@ Then you can open up your pavucontrol "Pulse Audio Volume Control" or "Volume Co
|
|||||||
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.
|
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
|
## 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 `
|
`dsd-fme -ft -i rtl:0:154.9875M:26:-2:8:0:6020 `
|
||||||
|
|
||||||
```
|
```
|
||||||
RTL-SDR options:
|
RTL-SDR options:
|
||||||
WARNING! Old CLI Switch Handling has been depreciated in favor of rtl:<parms>
|
Usage: rtl:dev:freq:gain:ppm:bw:sq:vol
|
||||||
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
|
dev <num> RTL-SDR Device Index Number
|
||||||
freq <num> RTL-SDR Frequency (851800000 or 851.8M)
|
freq <num> RTL-SDR Frequency (851800000 or 851.8M)
|
||||||
gain <num> RTL-SDR Device Gain (0-49) (default = 26)(0 = Hardware AGC, not recommended)
|
gain <num> RTL-SDR Device Gain (0-49)(default = 0; Hardware AGC recommended)
|
||||||
ppm <num> RTL-SDR PPM Error (default = 0)
|
ppm <num> RTL-SDR PPM Error (default = 0)
|
||||||
bw <num> RTL-SDR VFO Bandwidth kHz (default = 12)(6, 8, 12, 24)
|
bw <num> RTL-SDR Bandwidth kHz (default = 12)(8, 12, 16, 24)
|
||||||
sq <num> RTL-SDR Squelch Level (0 - Open, 25 - Little, 50 - Higher)
|
sq <num> RTL-SDR Squelch Level vs RMS Value (Optional)
|
||||||
udp <num> RTL-SDR UDP Remote Port (default = 6020)
|
vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3)
|
||||||
Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:12:0:6021
|
Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T
|
||||||
|
Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example Usage - Extra Payload/PDU Logging
|
## Example Usage - Extra Payload/PDU Logging
|
||||||
|
|
||||||
`dsd-fme -Z -N 2>> log.ans`
|
`dsd-fme -Z -N 2> log.ans`
|
||||||
|
|
||||||
and in a second terminal tab, same folder, run
|
and in a second terminal tab, same folder, run
|
||||||
|
|
||||||
@@ -74,11 +106,11 @@ and in a second terminal tab, same folder, run
|
|||||||
|
|
||||||
EDACS Trunking (w/ channel map import)
|
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)
|
--EDACS/PV Trunking using RIGCTL and TCP Direct Link Audio inside of SDR++ (see switches above for STD/NET, EA, and ESK modes)
|
||||||
|
|
||||||
`dsd-fme -i tcp -fp -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans`
|
`dsd-fme -i tcp -fE -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
--NXDN48 Trunking (standard band plan) with SDR++ (untested for frequency accuracy)
|
--NXDN48 Trunking (Direct Frequency Assignment) with SDR++ (untested for frequency accuracy)
|
||||||
|
|
||||||
`dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans`
|
`dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
@@ -94,11 +126,11 @@ EDACS Trunking (w/ channel map import)
|
|||||||
|
|
||||||
`dsd-fme -i tcp -T -U 4532 -N 2> log.ans`
|
`dsd-fme -i tcp -T -U 4532 -N 2> log.ans`
|
||||||
|
|
||||||
--P25 Trunking (CQPSK) with P1 Control Channel (Should switch symbol rate and center on Phase 2 audio channels)
|
--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`
|
`dsd-fme -i tcp -T -U 4532 -N -mq 2> log.ans`
|
||||||
|
|
||||||
--P25 Trunking Phase 2 TDMA Control Channel systems with CQPSK (non Phase 1 systems)
|
--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`
|
`dsd-fme -i tcp -T -U 4532 -N -f2 -m2 2> log.ans`
|
||||||
|
|
||||||
@@ -106,25 +138,31 @@ Trunking Note1: All samples above can also be run with the RTL input method and
|
|||||||
|
|
||||||
`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`
|
`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: CQPSK Phase 1 and Phase 2 Systems are 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 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 using channel 0 as the control channel will hardset that frequency to the control channel. 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) 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.
|
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 style LSN-esque numbering 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 lpcn (12-bit logical physical channel number - decimal format) values from the log, and not the LPCN+TS (13-bit DSDPlus LSN) values. Notice: TIII Site ID value needs work to determine proper DMRLA values for system area and sub area.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
00:43:00 Sync: +DMR [slot1] slot2 | Color Code=00 | CSBK
|
||||||
Talkgroup Voice Channel Grant (TV_GRANT) - Logical
|
Talkgroup Voice Channel Grant (TV_GRANT) - Logical
|
||||||
Ch [036] Cd [0054] C+ [0110] - TS [1] - Target [01900500] - Source [01900505]
|
LPCN: 0075; TS: 2; LPCN+TS: 0152; Target: 16518173 - Source: 16533625
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Use channel 54 in your import file, which would correspond to dsdplus channels 109 and 110 (109 = TS0 / 110 = TS1).
|
Use channel 75 in your import file, which would correspond to dsdplus channels 151 and 152 (151 = TS1 / 152 = TS2).
|
||||||
|
|
||||||
For Connect Plus, enumerate your list from 1 to the last channel and add the frequency. For Capacity Plus, Rest Channels 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 RF frequency as two seperate channels, 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 will lose the rest channel and will have to hunt through all frequencies to find it again.
|
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.
|
||||||
|
|
||||||
Currently uncoded/unknown DMR trunking systems include Hytera XPT.
|
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.
|
||||||
|
|
||||||
Trunking Note4: NXDN Trunking may also require a channel map file, depending on the system. If it uses a custom range (above 800), then channels will need to be mapped. If channels do not require mapping (have channels below 800) but you are not tuning properly, then please use a channel_map and please report the issue in the issues along with the channels you are tuning and the actual rf frequency so corrections can be made.
|
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.
|
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 ##
|
## 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:
|
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:
|
||||||
@@ -136,20 +174,203 @@ c - toggle compact mode
|
|||||||
h - toggle call history
|
h - toggle call history
|
||||||
z - toggle console payloads
|
z - toggle console payloads
|
||||||
a - toggle call alert beep
|
a - toggle call alert beep
|
||||||
4 - force dmr privacy key assertion over fid and svc bits
|
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)
|
i - toggle signal inversion on types that can't auto detect (dmr, dpmr)
|
||||||
m - toggle c4fm/cqpsk 10/4 (everything but phase 2 signal)
|
m - toggle c4fm/qpsk 10/4 (everything but phase 2 signal)
|
||||||
M - toggle c4fm/cqpsk 8/3 (phase 2 tdma control channel)
|
M - toggle c4fm/qpsk 8/3 (phase 2 tdma control channel)
|
||||||
t - toggle trunking (needs either rtl input, or rigctl setup at CLI)
|
|
||||||
R - start capturing symbol capture bin (date/time name file)
|
R - start capturing symbol capture bin (date/time name file)
|
||||||
r - stop capturing symbol capture bin
|
r - stop capturing symbol capture bin
|
||||||
spacebar - replay last symbol capture bin (captures must be stopped first)
|
spacebar - replay last symbol capture bin (captures must be stopped first)
|
||||||
s - stop playing symbol capture bin or wav input file
|
s - stop playing symbol capture bin or wav input file (lower s)
|
||||||
P - start per call decoded wav files
|
P - start per call decoded wav files (Capital P)
|
||||||
p - stop per call decoded wav files
|
p - stop per call decoded wav files (lower p)
|
||||||
1 - Lockout Tuning/Playback of TG in Slot 1 or Conventional (Current Session Only)
|
t - toggle trunking (needs either rtl input, or rigctl connection)
|
||||||
2 - Lockout Tuning/Playback of TG in Slot 2 (Current Session Only)
|
y - toggle scanner (needs either rtl input, or rigctl connection)
|
||||||
0 - Toggle Audio Smoothing - May produce crackling if enabled on RTL/TCP or wav/bin files
|
1 - Toggle Voice Synthesis in TDMA Slot 1 or FDMA Conventional Voice
|
||||||
|
2 - Toggle Voice Synthesis in TDMA Slot 2
|
||||||
w - Toggle Trunking/Playback White List (Allow A Groups Only) / Black List (Block B or DE groups only) Mode
|
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)
|
||||||
|
|
||||||
|
! - Lockout Tuning/Playback of TG in Slot 1 or Conventional -- Current Session Only if no group.csv file specified
|
||||||
|
@ - Lockout Tuning/Playback of TG in Slot 2 -- Current Session Only if no group.csv file specified
|
||||||
|
|
||||||
|
k - Hold TG in Slot 1 or Conventional, or clear current hold
|
||||||
|
l - Hold TG in Slot 2 on TDMA Systems, or clear current hold
|
||||||
|
|
||||||
|
C - Drop Call and Return to CC during trunking operation
|
||||||
|
L - Manual Cycle Forward Channel Tuned when RIGCTL or using RTL input and channel csv loaded
|
||||||
|
|
||||||
|
S - Toggle between EDACS Standard/Network and Extended Addressing mode (Capital S)
|
||||||
|
A - Toggle EDACS ESK Mask (none vs A0)
|
||||||
|
|
||||||
|
H - Toggle High Pass Filter on Digital Sythesized Voice
|
||||||
|
V - Toggle Low Pass Filter on Analog Voice or Raw Signal Monitoring
|
||||||
|
B - Toggle High Pass Filter on Analog Voice or Raw Signal Monitoring
|
||||||
|
N - Toggle Passband Filter on Analog Voice or Raw Signal Monitoring
|
||||||
|
|
||||||
|
v - Cycle RTL Input Volume Multiplier 1X, 2X, 3X (lower v)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Sending Audio to a Icecast 2 Server via FFmpeg (Windows)
|
||||||
|
|
||||||
|
Make sure to enable "Stereo Mix" from Volume Control Panel and then disable Windows Sounds.
|
||||||
|
|
||||||
|
```
|
||||||
|
ffmpeg -f dshow -i audio="Stereo Mix (Realtek High Definition Audio)" -c:a aac -b:a 64k -content_type 'audio/aac' -vn -f adts icecast://source:password@192.168.5.251:8844/DSDFME
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sending Audio to a Icecast 2 Server via FFmpeg (Linux w/ Pulse Audio)
|
||||||
|
|
||||||
|
`pactl list short sources`
|
||||||
|
|
||||||
|
Choose Desired Device to Cast:
|
||||||
|
|
||||||
|
```
|
||||||
|
0 auto_null.monitor module-null-sink.c s16le 2ch 48000Hz IDLE
|
||||||
|
1 alsa_output.pci-0000_0d_00.3.analog-stereo.monitor module-alsa-card.c s16le 2ch 44100Hz RUNNING
|
||||||
|
2 alsa_input.pci-0000_0d_00.3.analog-stereo module-alsa-card.c s16le 2ch 44100Hz RUNNING
|
||||||
|
3 virtual_sink.monitor module-null-sink.c s16le 2ch 44100Hz RUNNING
|
||||||
|
4 virtual_sink2.monitor module-null-sink.c s16le 2ch 44100Hz RUNNING
|
||||||
|
7 alsa_output.pci-0000_0b_00.1.hdmi-stereo-extra1.monitor module-alsa-card.c s16le 2ch 44100Hz RUNNING
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
francis@12cores:~$ pactl list short sources
|
||||||
|
0 auto_null.monitor module-null-sink.c s16le 2ch 48000Hz IDLE
|
||||||
|
francis@12coresx:~$ ffmpeg -f pulse -i 0 -c:a libmp3lame -ab 64k -f mp3 icecast://source:password@XXXXXXXXX:8000
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Do not run this command for ffmpeg/icecast piped to the main dsd-fme command, make sure these are running in seperate terminals.
|
||||||
|
|
||||||
|
## Sending Audio to UDP Target/Port
|
||||||
|
|
||||||
|
Audio can be sent to a target address on your local area network with the UDP blaster using the `-o udp:targetaddress:port` option.
|
||||||
|
Note: Any analog audio monitoring will not function properly over UDP port (rate difference issues)
|
||||||
|
|
||||||
|
Example (Short 8k/2):
|
||||||
|
`dsd-fme -fs -o udp:192.168.7.8:23470`
|
||||||
|
|
||||||
|
Receiving End (Short 8k/2):
|
||||||
|
`socat stdio udp-listen:23470 | play --buffer 640 -q -b 16 -r 8000 -c2 -t s16 -`
|
||||||
|
|
||||||
|
Example (Short 8k/1):
|
||||||
|
`dsd-fme -f1 -o udp:192.168.7.8:23469`
|
||||||
|
|
||||||
|
Receiving End (Short 8k/1):
|
||||||
|
`socat stdio udp-listen:23469 | play --buffer 320 -q -b 16 -r 8000 -c1 -t s16 -`
|
||||||
|
|
||||||
|
Example (Float 8k/2):
|
||||||
|
`dsd-fme -ft -y -o udp:192.168.7.8:23468`
|
||||||
|
|
||||||
|
Receiving End (Float 8k/2):
|
||||||
|
`socat stdio udp-listen:23468 | play --buffer 1280 -q -b 16 -r 8000 -c2 -t f32 -`
|
||||||
|
|
||||||
|
Example (Float 8k/1):
|
||||||
|
`dsd-fme -fi -y -o udp:192.168.7.8:23467`
|
||||||
|
|
||||||
|
Receiving End (Float 8k/1):
|
||||||
|
`socat stdio udp-listen:23467 | play --buffer 640 -q -b 16 -r 8000 -c1 -t f32 -`
|
||||||
|
|
||||||
|
Note: --buffer option in play is calculated by (channels * samples * sizeof(samples)),
|
||||||
|
where samples is 160 for mono, 320 for stereo, and short samples are 2 and float samples are 4.
|
||||||
|
This is vital to prevent 'lag' or cut-off audio when the UDP audio doesn't fill the default buffer size.
|
||||||
|
|
||||||
|
Note2: Be sure to start the receiving end AFTER starting DSD-FME. If DSD-FME is restarted, make sure to restart the receiving end as well, as socat/UDP closes the UDP port listening when DSD-FME closes the UDP socket.
|
||||||
|
|
||||||
|
Note3: socat version 1.8 may have a `E xioopen_ipdgram_listen(): unknown address family 0` problem, downgrading to 1.7.4 is recommended if this occurs. `sudo downgrade socat`.
|
||||||
|
|
||||||
|
## Record 48k/1 audio with rtl_fm
|
||||||
|
|
||||||
|
You can record suitable audio to feed as input to dsd-fme with `-i` using rtl_fm:
|
||||||
|
|
||||||
|
`rtl_fm -M nfm -f [FREQ] -s 48k -g 42 -l 20 > input.wav`
|
||||||
|
|
||||||
|
You can also combine that with `socat` to send the audio to a remote dsd-fme instance in real time while keeping a local copy on the host where the RTL-SDR dongle is:
|
||||||
|
|
||||||
|
`rtl_fm -M fm -f [FREQ] -s 48k -g 42 -l 20 | tee input.wav | socat -u - TCP-LISTEN:1234,forever,reuseaddr`
|
||||||
|
|
||||||
|
On the remote side where dsd-fme is, use the TCP input:
|
||||||
|
|
||||||
|
`dsd-fme -i tcp:192.168.7.8:1234 -N`
|
||||||
|
|
||||||
|
Note: As always, when using rtl_fm, play with `-l` and `-g` to find a good squelch and gain values for your environment - you will know if the squelch is the right value when `input.wav` does not increase in size in the absence of useful signal on the monitored frequency.
|
||||||
|
|
||||||
|
## Using the Experimental M17 Encoder (and Decoder)
|
||||||
|
|
||||||
|
You can use the experimental M17 Voice Stream and Packet encoder with the following examples.
|
||||||
|
|
||||||
|
Encode/Decode Local Feedback Voice Stream (Single Session):
|
||||||
|
|
||||||
|
`dsd-fme -fZ -i pulse -o pulse -M M17:3:SP5WWP:BROADCAST:48000`
|
||||||
|
|
||||||
|
Using the `-8` option (raw audio monitor) will instead send encoded raw audio to output instead of decoded audio. For example,
|
||||||
|
|
||||||
|
To send a voice stream out of stdout into SOCAT TCP to a second/remote DSD-FME session to decode:
|
||||||
|
|
||||||
|
Encode (Upper Z):
|
||||||
|
|
||||||
|
`dsd-fme -fZ -8 -i pulse -M M17:9:LWVMOBILE:DSD-FME:48000 -o - | socat stdio tcp-listen:7355,forever,reuseaddr`
|
||||||
|
|
||||||
|
Decode (Lower z):
|
||||||
|
|
||||||
|
`dsd-fme -fz -i tcp:192.168.7.8:7355`
|
||||||
|
|
||||||
|
NOTE: When using tcp input for M17 decoding, DSD-FME will keep the TCP connection alive with constant retries, so no need to restart the decoding session on connection failure.
|
||||||
|
|
||||||
|
SMS Packet Encoder (Text Only):
|
||||||
|
|
||||||
|
`dsd-fme -fP -8 -S "This is a sample message sent over M17 Packet Data using DSD-FME to encode it."`
|
||||||
|
|
||||||
|
SMS Packet Encoder (over SOCAT TCP):
|
||||||
|
|
||||||
|
`dsd-fme -fP -8 -S "This is a sample message sent over M17 Packet Data using DSD-FME to encode it and SOCAT to send it over TCP." -o - | socat stdio tcp-listen:7355,forever,reuseaddr`
|
||||||
|
|
||||||
|
Many combinations of input and output can be used as well, and using features like raw audio saving with `-6 m17str.wav` will save a wav file of your encoded audio to be replayed later with `-i m17str.wav`
|
||||||
|
Also note, features function similarly, like -Z for payload, and input options include pulse, oss, stdin, rtl, tcp, etc, using those as the mic in for voice encoding. With the right setup of input/output/input using SOCAT TCP pipes, it is conceivable to use DSD-FME to convert analog audio from NFM into M17 audio, or even change another digital format supported by DSD-FME into M17 audio, if desired (purely for research and fun purposes). Ncurses terminal will function with internal encoder/decoder and wav file creation, with toggle encode/tx with `\` key, but will not function with stdout properly.
|
||||||
|
|
||||||
|
The `-M` switch is used to specify user M17 variables, the string is in the format of M17:CAN:SRC:DST:INPUT_RATE.
|
||||||
|
CAN 1-15; SRC and DST have to be no more than 9 UPPER base40 characters.
|
||||||
|
BASE40: ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.'
|
||||||
|
Input Rate Default is 48000; Use Multiples of 8000 up to 48000.
|
||||||
|
Values not entered into the M17: string are set to default values. M17:7:DSD-FME:DSD-FME:48000
|
||||||
|
|
||||||
|
Fun Example Transmuting OTA Analog Things into M17:
|
||||||
|
|
||||||
|
Start up in sequence.
|
||||||
|
|
||||||
|
Terminal #1 (RTL Input):
|
||||||
|
`dsd-fme -fZ -8 -M M17:1:ANALOG:M17:48000 -i rtl:0:162.425M -o - | socat stdio tcp-listen:7310,forever,reuseaddr`
|
||||||
|
|
||||||
|
Terminal #2:
|
||||||
|
`dsd-fme -fz -i tcp:127.0.0.1:7310`
|
||||||
|
|
||||||
|
Fun Example Transmuting OTA Digital Things into M17:
|
||||||
|
|
||||||
|
Start up all three of these in sequence.
|
||||||
|
|
||||||
|
Terminal #1 (connecting to SDR++):
|
||||||
|
`dsd-fme -fi -i tcp:127.0.0.1:7355 -o - | socat stdio tcp-listen:7300,forever,reuseaddr`
|
||||||
|
|
||||||
|
Terminal #2:
|
||||||
|
`dsd-fme -fZ -8 -M M17:2:NXDN:M17:8000 -i tcp:127.0.0.1:7300 -o - | socat stdio tcp-listen:7301,forever,reuseaddr`
|
||||||
|
|
||||||
|
Terminal #3:
|
||||||
|
`dsd-fme -fz -i tcp:127.0.0.1:7301`
|
||||||
|
|
||||||
|
Note: Transmuting only carries over voice when audio is output, and will not carry any ID values, or any other data payloads.
|
||||||
|
This mode is only meant for educational or fun purposes, please do not retransmit stuff that doesn't belong to you.
|
||||||
|
|
||||||
|
Information on this section will be updated as features are added or modified, etc. If you need help with a particular function and can't find a suitable answer, always open a new issue here, or make contact on the radio reference forum for additional examples, guidance, ideas, help, etc.
|
||||||
@@ -1,42 +1,75 @@
|
|||||||
## How to clone and build this branch
|
## How to clone and build this branch
|
||||||
|
|
||||||
### Ubuntu 22.04/20.04/LM20/Debian Bullseye or Newer:
|
### Ubuntu 22.04/20.04/LM20/Debian Bullseye:
|
||||||
|
|
||||||
Using the included download-and-install.sh should make for a simple and painless clone, build, and install on newer Debian/Ubuntu/Mint/Pi systems. Simply acquire or copy the script, and run it. Update: Ubuntu 22.04 and RPi Bullseye 64-bit has been tested working with the installer script and functions appropriately.
|
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:
|
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
|
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/audio_work/download-and-install.sh
|
||||||
chmod +x download-and-install.sh
|
sh download-and-install.sh
|
||||||
./download-and-install.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If you have dependencies already installed (i.e. need a fresh clean install on a system with DSD-FME already or using system other than Debian/Ubuntu, etc), please run this instead:
|
### Debian 12 (SID) / Ubuntu 24.04 LTS (Updated Ncurses Dependencies)
|
||||||
|
|
||||||
```
|
```
|
||||||
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/main/download-and-install-nodeps.sh
|
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/audio_work/download-and-install-ubuntu2404lts.sh
|
||||||
chmod +x download-and-install-nodeps.sh
|
sh download-and-install-ubuntu2404lts.sh
|
||||||
./download-and-install-nodeps.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ubuntu 18.04/LM19/Buster Note:
|
### Arch Based Distros:
|
||||||
The above install.sh should now function on older system types. You can elect to manually follow the steps down below. Do NOT Manually build and install ITPP 4.3.1 on older systems, it is currently not wanting to build on Ubuntu 18.04 and Linux Mint 19. Install it from the repository instead.
|
|
||||||
|
```
|
||||||
|
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/audio_work/download-and-install-Arch.sh
|
||||||
|
sh download-and-install-Arch.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Other:
|
||||||
|
|
||||||
|
If you have dependencies already installed, please run this instead:
|
||||||
|
|
||||||
|
```
|
||||||
|
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/audio_work/download-and-install-nodeps.sh
|
||||||
|
sh download-and-install-nodeps.sh
|
||||||
|
```
|
||||||
|
|
||||||
## Manual Install
|
## 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.
|
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 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
|
#Ubuntu 22.04 lts / Debian 11 and lower
|
||||||
|
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 libcodec2-dev
|
||||||
|
#Ubuntu 24.04 lts / Debian 12
|
||||||
|
#sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncurses-dev libncurses6 libcodec2-dev
|
||||||
```
|
```
|
||||||
## Headless
|
|
||||||
|
Fedora 36/37 -- from https://github.com/lwvmobile/dsd-fme/issues/99
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo dnf update
|
||||||
|
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++ codec2-devel
|
||||||
|
```
|
||||||
|
|
||||||
|
Arch -- https://github.com/lwvmobile/dsd-fme/issues/153 and https://github.com/lwvmobile/dsd-fme/issues/153
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo pacman -Syu
|
||||||
|
sudo pacman -S libpulse cmake ncurses lapack perl fftw rtl-sdr codec2 base-devel libsndfile git wget rtl-sdr
|
||||||
|
wget https://mirror.archstrike.org/x86_64/archstrike/itpp-4.3.1-3-x86_64.pkg.tar.xz
|
||||||
|
sudo pacman -U itpp-4.3.1-3-x86_64.pkg.tar.xz
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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.
|
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
|
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 libcodec2-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
### Build and Install ITPP - ONLY IF NOT IN REPO!!
|
### Build and Install ITPP - ONLY IF NOT IN REPO!!
|
||||||
@@ -56,10 +89,11 @@ cd ..
|
|||||||
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.
|
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")
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone https://github.com/lwvmobile/mbelib
|
git clone https://github.com/lwvmobile/mbelib
|
||||||
|
git checkout ambe_tones
|
||||||
cd mbelib
|
cd mbelib
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
@@ -71,17 +105,27 @@ cd ..
|
|||||||
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.
|
||||||
|
|
||||||
|
```
|
||||||
|
cp libmbe* /usr/lib
|
||||||
|
cp cygmbe* /usr/lib
|
||||||
|
cp cygmbe* /bin
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo cp libmbe* /usr/lib
|
||||||
|
sudo cp cygmbe* /usr/lib
|
||||||
|
sudo cp cygmbe* /bin
|
||||||
|
```
|
||||||
|
|
||||||
Finish by running these steps to clone and build DSD-FME.
|
Finish by running these steps to clone and build DSD-FME.
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone https://github.com/lwvmobile/dsd-fme
|
git clone https://github.com/lwvmobile/dsd-fme
|
||||||
cd 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
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
@@ -90,13 +134,39 @@ sudo make install
|
|||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
|
|
||||||
```
|
```
|
||||||
Optional 'Virtual Sinks' for routing audio from SDR++ or GQRX, Media Players, etc. into DSD-FME
|
|
||||||
|
|
||||||
You may wish to direct sound into DSD-FME via Virtual Sinks. You may set up a Virtual Sink or two on your machine for routing audio in and out of applications to other applications using the following command, and opening up pavucontrol "PulseAudio Volume Control" in the menu (or `pulsemixer` in headless mode) to change line out of application to virtual sink, and line in of DSD-FME to monitor of virtual sink. This command will not persist past a reboot, so you will need to invoke them each time you reboot, or search for how to add this to your conf files for persistency if desired.
|
### 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:
|
||||||
|
|
||||||
```
|
```
|
||||||
pacmd load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink
|
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
|
||||||
pacmd load-module module-null-sink sink_name=virtual_sink2 sink_properties=device.description=Virtual_Sink2
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
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:
|
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:
|
||||||
@@ -112,13 +182,31 @@ sudo make install
|
|||||||
sudo ldconfig
|
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.
|
### Applying Patches -- Recommended For Advanced Users Only
|
||||||
|
|
||||||
|
More experimental or potentially functionality changing or temperamental features may be included as seperate .patch files inside of the patch folder, and are not included in the source code by default.
|
||||||
|
If users wish to try or use these patches, then they can apply patches and build/rebuild with the following procedure. Make sure you have changed to the dsd-fme directory first, either by the top
|
||||||
|
command, or by otherwise opening the terminal to the clone folder.
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo cp tone8.wav /usr/share/
|
cd dsd-fme
|
||||||
sudo cp tone24.wav /usr/share/
|
git apply patch/patchname.patch
|
||||||
sudo cp tone48.wav /usr/share/
|
cd build
|
||||||
sudo chmod 777 /usr/share/tone8.wav
|
make
|
||||||
sudo chmod 777 /usr/share/tone24.wav
|
sudo make install
|
||||||
sudo chmod 777 /usr/share/tone48.wav
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you later wish to update to the latest version via `git pull`, you may first need to reset any patches, perform the `git pull`, and then reapply patches.
|
||||||
|
A procedure for this is similar to, but not limited to:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd dsd-fme
|
||||||
|
git reset --hard
|
||||||
|
git pull
|
||||||
|
git apply patch/xyz.patch
|
||||||
|
cd build
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
NOTE: Patches can potential break over time with further changes to code, requiring merge resolution or other manual intervention to re-apply patches.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
LSN(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||||
1,858606250
|
1,858606250
|
||||||
2,858606250
|
2,858606250
|
||||||
3,859606250
|
3,859606250
|
||||||
|
|||||||
|
@@ -1,4 +1,4 @@
|
|||||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
LSN(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||||
1,851375000
|
1,851375000
|
||||||
2,851800000
|
2,851800000
|
||||||
3,855987500
|
3,855987500
|
||||||
|
|||||||
|
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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
BIN
examples/pavucontrol_plumbing1.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
examples/pavucontrol_plumbing2.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
@@ -1,33 +1,49 @@
|
|||||||
#example trunking sh scripts (SDR++ and RTL Input) -T = trunking
|
#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
|
#make sure to put a # in front of all lines except the one you wish to use
|
||||||
|
|
||||||
#P25 P1 FDMA CC with C4FM
|
#P25 P1 FDMA CC with C4FM
|
||||||
#dsd-fme -i tcp -U 4532 -T -N 2> log.ans
|
#dsd-fme -i tcp -U 4532 -T -N 2> log.ans
|
||||||
#dsd-fme -i rtl:0:850M:26:-2:8:0:6020 -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)
|
#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 tcp:192.168.7.8:7355 -U 4532 -T -mq -N 2> log.ans
|
||||||
#dsd-fme -i rtl:0:855.625M:26:-2:12:0:6020 -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)
|
#P25 P2 TDMA CC with C4FM
|
||||||
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -f2 -m2 -N 2> log.ans
|
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -f2 -N 2> log.ans
|
||||||
#dsd-fme -i rtl:0:855.625M:26:-2:12:0:6020 -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+
|
#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 tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans
|
||||||
#dsd-fme -fs -i rtl:0:450M:26:-2:8:0:6020 -T -C connect_plus_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
|
#EDACS Standard Network Analog and Digital
|
||||||
#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 -fh -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||||
#dsd-fme -fp -i rtl:0:850M:26:-2:24:0:6020 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
#dsd-fme -fh -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||||
|
|
||||||
#NXDN48 Trunking with Standard Band Plan (Channels 0-800)
|
#EDACS Standard Network Analog and Digital (with ESK)
|
||||||
|
#dsd-fme -fH -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||||
|
#dsd-fme -fH -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||||
|
|
||||||
|
#EDACS Extended Address Analog and Digital
|
||||||
|
#dsd-fme -fe -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||||
|
#dsd-fme -fe -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||||
|
|
||||||
|
#EDACS Extended Address Analog and Digital (with ESK)
|
||||||
|
#dsd-fme -fE -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||||
|
#dsd-fme -fE -i rtl:0:850M:44:-2:24 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||||
|
|
||||||
|
#NXDN48 Type-C or Type-D Trunking with Channel Map
|
||||||
|
#dsd-fme -fi -i tcp -T -U 4532 -C nxdn_channel_map.csv -N 2> log.ans
|
||||||
|
#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 tcp -T -U 4532 -N 2> log.ans
|
||||||
#dsd-fme -fi -i rtl:0:450M:26:-2:8:75:6020 -T -G group.csv -N 2> log.ans
|
#dsd-fme -fi -i rtl:0:855.625M:0:-2:12:100 -T -N 2> log.ans
|
||||||
|
|
||||||
#NXDN48 Trunking with Channel Map (Custom Channels > 800)
|
#NXDN96 Trunking with Channel Map
|
||||||
#dsd-fme -fi -i tcp -T -U 4532 -C channel_map.csv -N 2> log.ans
|
#dsd-fme -fn -i tcp -T -U 4532 -C nxdn_channel_map.csv -N 2> log.ans
|
||||||
#dsd-fme -fi -i rtl:0:450M:26:-2:8:75:6020 -T -G group.csv -C 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
|
#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
|
#and 7355 as the default port number. -U is the port number for RIGCTL and will mirror the tcp hostname
|
||||||
@@ -36,5 +52,6 @@ dsd-fme -fs -i tcp -U 4532 -T -C dmr_t3_chan.csv -G group.csv -N 2> log.ans
|
|||||||
#inside of SDR++ match, i.e., change in SDR++ from 'localhost' to an IP to bind to (machine's IP address)
|
#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
|
#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
|
#Having a -G group.csv is purely optional, but useful if you want to block groups, or give groups names
|
||||||
|
|||||||
64
flake.lock
generated
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1709336216,
|
||||||
|
"narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1711163522,
|
||||||
|
"narHash": "sha256-YN/Ciidm+A0fmJPWlHBGvVkcarYWSC+s3NTPk/P+q3c=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "44d0940ea560dee511026a53f0e2e2cde489b4d4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"dir": "lib",
|
||||||
|
"lastModified": 1709237383,
|
||||||
|
"narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "lib",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
18
flake.nix
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
description = "Digital Speech Decoder - Florida Man Edition ";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = inputs@{ flake-parts, ... }:
|
||||||
|
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||||
|
systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ];
|
||||||
|
perSystem = { config, pkgs, ... }: {
|
||||||
|
packages.default = config.packages.dsd-fme;
|
||||||
|
|
||||||
|
packages.dsd-fme = pkgs.callPackage ./package.nix { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@ int traceBack (int * out, int * m_pathMemory0, int * m_pathMemory1, int * m_path
|
|||||||
state = S2; // lower path
|
state = S2; // lower path
|
||||||
} else {
|
} else {
|
||||||
state = S0; // upper path
|
state = S0; // upper path
|
||||||
}; // end else - if
|
} // end else - if
|
||||||
out[loop]=0;
|
out[loop]=0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ int traceBack (int * out, int * m_pathMemory0, int * m_pathMemory1, int * m_path
|
|||||||
state = S2; // lower path
|
state = S2; // lower path
|
||||||
} else {
|
} else {
|
||||||
state = S0; // upper path
|
state = S0; // upper path
|
||||||
}; // end else - if
|
} // end else - if
|
||||||
out[loop]=1;
|
out[loop]=1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ int traceBack (int * out, int * m_pathMemory0, int * m_pathMemory1, int * m_path
|
|||||||
state = S3; // lower path
|
state = S3; // lower path
|
||||||
} else {
|
} else {
|
||||||
state = S1; // upper path
|
state = S1; // upper path
|
||||||
}; // end else - if
|
} // end else - if
|
||||||
out[loop]=0;
|
out[loop]=0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -79,12 +79,12 @@ int traceBack (int * out, int * m_pathMemory0, int * m_pathMemory1, int * m_path
|
|||||||
state = S3; // lower path
|
state = S3; // lower path
|
||||||
} else {
|
} else {
|
||||||
state = S1; // upper path
|
state = S1; // upper path
|
||||||
}; // end else - if
|
} // end else - if
|
||||||
out[loop]=1;
|
out[loop]=1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}; // end switch
|
} // end switch
|
||||||
}; // end for
|
} // end for
|
||||||
|
|
||||||
return(length);
|
return(length);
|
||||||
}; // end function
|
}; // end function
|
||||||
|
|||||||
575
include/dsd.h
@@ -17,7 +17,8 @@
|
|||||||
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//pretty pretty colors
|
//defined by CMakeLists.txt -- Disable by using cmake -DCOLORS=OFF ..
|
||||||
|
#ifdef PRETTY_COLORS
|
||||||
#define KNRM "\x1B[0m"
|
#define KNRM "\x1B[0m"
|
||||||
#define KRED "\x1B[31m"
|
#define KRED "\x1B[31m"
|
||||||
#define KGRN "\x1B[32m"
|
#define KGRN "\x1B[32m"
|
||||||
@@ -26,6 +27,16 @@
|
|||||||
#define KMAG "\x1B[35m"
|
#define KMAG "\x1B[35m"
|
||||||
#define KCYN "\x1B[36m"
|
#define KCYN "\x1B[36m"
|
||||||
#define KWHT "\x1B[37m"
|
#define KWHT "\x1B[37m"
|
||||||
|
#else
|
||||||
|
#define KNRM ""
|
||||||
|
#define KRED ""
|
||||||
|
#define KGRN ""
|
||||||
|
#define KYEL ""
|
||||||
|
#define KBLU ""
|
||||||
|
#define KMAG ""
|
||||||
|
#define KCYN ""
|
||||||
|
#define KWHT ""
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -47,11 +58,16 @@
|
|||||||
|
|
||||||
#include "p25p1_heuristics.h"
|
#include "p25p1_heuristics.h"
|
||||||
|
|
||||||
|
//OSS support
|
||||||
|
#include <sys/soundcard.h>
|
||||||
|
|
||||||
|
#include <pulse/pulseaudio.h> //PULSE AUDIO
|
||||||
#include <pulse/simple.h> //PULSE AUDIO
|
#include <pulse/simple.h> //PULSE AUDIO
|
||||||
#include <pulse/error.h> //PULSE AUDIO
|
#include <pulse/error.h> //PULSE AUDIO
|
||||||
|
#include <pulse/introspect.h> //PULSE AUDIO
|
||||||
|
|
||||||
#define SAMPLE_RATE_IN 48000 //48000
|
#define SAMPLE_RATE_IN 48000 //48000
|
||||||
#define SAMPLE_RATE_OUT 8000 //8000,
|
#define SAMPLE_RATE_OUT 8000 //8000
|
||||||
|
|
||||||
#ifdef USE_RTLSDR
|
#ifdef USE_RTLSDR
|
||||||
#include <rtl-sdr.h>
|
#include <rtl-sdr.h>
|
||||||
@@ -60,7 +76,55 @@
|
|||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
|
|
||||||
static volatile int exitflag;
|
#define UNUSED(x) ((void)x)
|
||||||
|
#define UNUSED2(x1, x2) (UNUSED(x1), UNUSED(x2))
|
||||||
|
#define UNUSED3(x1, x2, x3) (UNUSED(x1), UNUSED(x2), UNUSED(x3))
|
||||||
|
#define UNUSED4(x1, x2, x3, x4) (UNUSED(x1), UNUSED(x2), UNUSED(x3), UNUSED(x4))
|
||||||
|
#define UNUSED5(x1, x2, x3, x4, x5) (UNUSED(x1), UNUSED(x2), UNUSED(x3), UNUSED(x4), UNUSED(x5))
|
||||||
|
|
||||||
|
#ifdef USE_CODEC2
|
||||||
|
#include <codec2/codec2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern volatile uint8_t exitflag; //fix for issue #136
|
||||||
|
|
||||||
|
//new audio filter stuff from: https://github.com/NedSimao/FilteringLibrary
|
||||||
|
typedef struct {
|
||||||
|
float coef[2];
|
||||||
|
float v_out[2];
|
||||||
|
}LPFilter;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float coef;
|
||||||
|
float v_out[2];
|
||||||
|
float v_in[2];
|
||||||
|
|
||||||
|
}HPFilter;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
LPFilter lpf;
|
||||||
|
HPFilter hpf;
|
||||||
|
float out_in;
|
||||||
|
}PBFilter;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float alpha;
|
||||||
|
float beta;
|
||||||
|
|
||||||
|
float vin[3];
|
||||||
|
float vout[3];
|
||||||
|
|
||||||
|
}NOTCHFilter;
|
||||||
|
//end new filters
|
||||||
|
|
||||||
|
// Field list is here: http://0pointer.de/lennart/projects/pulseaudio/doxygen/structpa__sink__info.html
|
||||||
|
typedef struct pa_devicelist
|
||||||
|
{
|
||||||
|
uint8_t initialized;
|
||||||
|
char name[512];
|
||||||
|
uint32_t index;
|
||||||
|
char description[256];
|
||||||
|
} pa_devicelist_t;
|
||||||
|
|
||||||
//group csv import struct
|
//group csv import struct
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -190,6 +254,7 @@ typedef struct
|
|||||||
int audio_in_type;
|
int audio_in_type;
|
||||||
char audio_out_dev[1024];
|
char audio_out_dev[1024];
|
||||||
int audio_out_fd;
|
int audio_out_fd;
|
||||||
|
int audio_out_fdR; //right channel audio for OSS hack
|
||||||
SNDFILE *audio_out_file;
|
SNDFILE *audio_out_file;
|
||||||
SF_INFO *audio_out_file_info;
|
SF_INFO *audio_out_file_info;
|
||||||
|
|
||||||
@@ -199,21 +264,25 @@ typedef struct
|
|||||||
int playoffsetR;
|
int playoffsetR;
|
||||||
char mbe_out_dir[1024];
|
char mbe_out_dir[1024];
|
||||||
char mbe_out_file[1024];
|
char mbe_out_file[1024];
|
||||||
|
char mbe_out_fileR[1024]; //second slot on a TDMA system
|
||||||
char mbe_out_path[2048]; //1024
|
char mbe_out_path[2048]; //1024
|
||||||
FILE *mbe_out_f;
|
FILE *mbe_out_f;
|
||||||
|
FILE *mbe_out_fR; //second slot on a TDMA system
|
||||||
FILE *symbol_out_f;
|
FILE *symbol_out_f;
|
||||||
float audio_gain;
|
float audio_gain;
|
||||||
float audio_gainR;
|
float audio_gainR;
|
||||||
|
float audio_gainA;
|
||||||
int audio_out;
|
int audio_out;
|
||||||
int dmr_stereo_wav;
|
int dmr_stereo_wav;
|
||||||
|
char wav_out_dir[512];
|
||||||
char wav_out_file[1024];
|
char wav_out_file[1024];
|
||||||
char wav_out_fileR[1024];
|
char wav_out_fileR[1024];
|
||||||
char wav_out_file_raw[1024];
|
char wav_out_file_raw[1024];
|
||||||
char symbol_out_file[1024];
|
char symbol_out_file[1024];
|
||||||
char lrrp_out_file[1024];
|
char lrrp_out_file[1024];
|
||||||
char szNumbers[1024]; //**tera 10/32/64 char str
|
char szNumbers[1024]; //**tera 10/32/64 char str
|
||||||
short int symbol_out;
|
|
||||||
short int mbe_out; //flag for mbe out, don't attempt fclose more than once
|
short int mbe_out; //flag for mbe out, don't attempt fclose more than once
|
||||||
|
short int mbe_outR; //flag for mbe out, don't attempt fclose more than once
|
||||||
SNDFILE *wav_out_f;
|
SNDFILE *wav_out_f;
|
||||||
SNDFILE *wav_out_fR;
|
SNDFILE *wav_out_fR;
|
||||||
SNDFILE *wav_out_raw;
|
SNDFILE *wav_out_raw;
|
||||||
@@ -242,6 +311,10 @@ typedef struct
|
|||||||
int ssize;
|
int ssize;
|
||||||
int msize;
|
int msize;
|
||||||
int playfiles;
|
int playfiles;
|
||||||
|
int m17encoder;
|
||||||
|
int m17encoderbrt;
|
||||||
|
int m17encoderpkt;
|
||||||
|
int m17decoderip;
|
||||||
int delay;
|
int delay;
|
||||||
int use_cosine_filter;
|
int use_cosine_filter;
|
||||||
int unmute_encrypted_p25;
|
int unmute_encrypted_p25;
|
||||||
@@ -252,7 +325,9 @@ typedef struct
|
|||||||
int rtl_udp_port;
|
int rtl_udp_port;
|
||||||
int rtl_bandwidth;
|
int rtl_bandwidth;
|
||||||
int rtl_started;
|
int rtl_started;
|
||||||
|
long int rtl_rms;
|
||||||
int monitor_input_audio;
|
int monitor_input_audio;
|
||||||
|
int analog_only;
|
||||||
int pulse_raw_rate_in;
|
int pulse_raw_rate_in;
|
||||||
int pulse_raw_rate_out;
|
int pulse_raw_rate_out;
|
||||||
int pulse_digi_rate_in;
|
int pulse_digi_rate_in;
|
||||||
@@ -267,6 +342,8 @@ typedef struct
|
|||||||
pa_simple *pulse_digi_dev_in;
|
pa_simple *pulse_digi_dev_in;
|
||||||
pa_simple *pulse_digi_dev_out;
|
pa_simple *pulse_digi_dev_out;
|
||||||
pa_simple *pulse_digi_dev_outR;
|
pa_simple *pulse_digi_dev_outR;
|
||||||
|
char pa_input_idx[100];
|
||||||
|
char pa_output_idx[100];
|
||||||
int use_ncurses_terminal;
|
int use_ncurses_terminal;
|
||||||
int ncurses_compact;
|
int ncurses_compact;
|
||||||
int ncurses_history;
|
int ncurses_history;
|
||||||
@@ -290,6 +367,9 @@ typedef struct
|
|||||||
int inverted_ysf;
|
int inverted_ysf;
|
||||||
short int aggressive_framesync;
|
short int aggressive_framesync;
|
||||||
|
|
||||||
|
int frame_m17;
|
||||||
|
int inverted_m17;
|
||||||
|
|
||||||
FILE *symbolfile;
|
FILE *symbolfile;
|
||||||
int call_alert;
|
int call_alert;
|
||||||
|
|
||||||
@@ -299,11 +379,17 @@ typedef struct
|
|||||||
int rigctlportno;
|
int rigctlportno;
|
||||||
char rigctlhostname[1024];
|
char rigctlhostname[1024];
|
||||||
|
|
||||||
//udp socket for GQRX, SDR++, etc
|
//UDP Socket Blaster Audio
|
||||||
int udp_sockfd;
|
int udp_sockfd; //digital
|
||||||
|
int udp_sockfdA; //analog 48k1
|
||||||
int udp_portno;
|
int udp_portno;
|
||||||
char * udp_hostname;
|
char udp_hostname[1024];
|
||||||
SNDFILE *udp_file_in;
|
|
||||||
|
//M17 UDP for IP frame output
|
||||||
|
int m17_use_ip; //if enabled, open UDP and broadcast IP frame
|
||||||
|
int m17_portno; //default is 17000
|
||||||
|
int m17_udp_sock; //actual UDP socket for M17 to send to
|
||||||
|
char m17_hostname[1024];
|
||||||
|
|
||||||
//tcp socket for SDR++, etc
|
//tcp socket for SDR++, etc
|
||||||
int tcp_sockfd;
|
int tcp_sockfd;
|
||||||
@@ -318,12 +404,15 @@ typedef struct
|
|||||||
|
|
||||||
int p25_trunk; //experimental P25 trunking with RIGCTL (or RTLFM)
|
int p25_trunk; //experimental P25 trunking with RIGCTL (or RTLFM)
|
||||||
int p25_is_tuned; //set to 1 if currently on VC, set back to 0 if on CC
|
int p25_is_tuned; //set to 1 if currently on VC, set back to 0 if on CC
|
||||||
int trunk_hangtime; //hangtime in seconds before tuning back to CC
|
float trunk_hangtime; //hangtime in seconds before tuning back to CC
|
||||||
|
|
||||||
|
int scanner_mode; //experimental -- use the channel map as a conventional scanner, quicker tuning, but no CC
|
||||||
|
|
||||||
//csv import filenames
|
//csv import filenames
|
||||||
char group_in_file[1024];
|
char group_in_file[1024];
|
||||||
char lcn_in_file[1024];
|
char lcn_in_file[1024];
|
||||||
char chan_in_file[1024];
|
char chan_in_file[1024];
|
||||||
|
char key_in_file[1024];
|
||||||
//end import filenames
|
//end import filenames
|
||||||
|
|
||||||
//reverse mute
|
//reverse mute
|
||||||
@@ -336,15 +425,47 @@ typedef struct
|
|||||||
uint8_t dmr_dmrla_is_set; //flag to tell us dmrla is set by the user
|
uint8_t dmr_dmrla_is_set; //flag to tell us dmrla is set by the user
|
||||||
uint8_t dmr_dmrla_n; //n value for dmrla
|
uint8_t dmr_dmrla_n; //n value for dmrla
|
||||||
|
|
||||||
|
//DMR Late Entry
|
||||||
|
uint8_t dmr_le; //user option to turn on or turn off late entry for enc identifiers
|
||||||
|
|
||||||
//Trunking - Use Group List as Allow List
|
//Trunking - Use Group List as Allow List
|
||||||
uint8_t trunk_use_allow_list;
|
uint8_t trunk_use_allow_list;
|
||||||
|
|
||||||
|
//Trunking - Tune Group Calls
|
||||||
|
uint8_t trunk_tune_group_calls;
|
||||||
|
|
||||||
//Trunking - Tune Private Calls
|
//Trunking - Tune Private Calls
|
||||||
uint8_t trunk_tune_private_calls;
|
uint8_t trunk_tune_private_calls;
|
||||||
|
|
||||||
//Trunking - Tune Data Calls...why?
|
//Trunking - Tune Data Calls
|
||||||
uint8_t trunk_tune_data_calls;
|
uint8_t trunk_tune_data_calls;
|
||||||
|
|
||||||
|
//Trunking - Tune Enc Calls (P25 only on applicable grants with svc opts)
|
||||||
|
uint8_t trunk_tune_enc_calls;
|
||||||
|
|
||||||
|
//OSS audio 48k/1 - slot preference
|
||||||
|
int slot_preference;
|
||||||
|
|
||||||
|
//hard set slots to synthesize
|
||||||
|
int slot1_on;
|
||||||
|
int slot2_on;
|
||||||
|
|
||||||
|
//enable filter options
|
||||||
|
int use_lpf;
|
||||||
|
int use_hpf;
|
||||||
|
int use_pbf;
|
||||||
|
int use_hpf_d;
|
||||||
|
|
||||||
|
//'DSP' Format Output
|
||||||
|
uint8_t use_dsp_output;
|
||||||
|
char dsp_out_file[2048];
|
||||||
|
|
||||||
|
//Use P25p1 heuristics
|
||||||
|
uint8_t use_heuristics;
|
||||||
|
|
||||||
|
//Use floating point audio output
|
||||||
|
int floating_point;
|
||||||
|
|
||||||
} dsd_opts;
|
} dsd_opts;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -366,6 +487,25 @@ typedef struct
|
|||||||
float *audio_out_temp_buf_p;
|
float *audio_out_temp_buf_p;
|
||||||
float audio_out_temp_bufR[160];
|
float audio_out_temp_bufR[160];
|
||||||
float *audio_out_temp_buf_pR;
|
float *audio_out_temp_buf_pR;
|
||||||
|
//analog/raw signal audio buffers
|
||||||
|
short analog_out[960];
|
||||||
|
int analog_sample_counter;
|
||||||
|
//new stereo float sample storage
|
||||||
|
float f_l[160]; //single sample left
|
||||||
|
float f_r[160]; //single sample right
|
||||||
|
float f_l4[4][160]; //quad sample for up to a P25p2 4V
|
||||||
|
float f_r4[4][160]; //quad sample for up to a P25p2 4V
|
||||||
|
//new stereo short sample storage
|
||||||
|
short s_l[160]; //single sample left
|
||||||
|
short s_r[160]; //single sample right
|
||||||
|
short s_l4[18][160]; //quad sample for up to a P25p2 4V
|
||||||
|
short s_r4[18][160]; //quad sample for up to a P25p2 4V
|
||||||
|
//new stereo short sample storage tapped from 48_k internal upsampling
|
||||||
|
short s_lu[160*6]; //single sample left
|
||||||
|
short s_ru[160*6]; //single sample right
|
||||||
|
short s_l4u[4][160*6]; //quad sample for up to a P25p2 4V
|
||||||
|
short s_r4u[4][160*6]; //quad sample for up to a P25p2 4V
|
||||||
|
//end
|
||||||
int audio_out_idx;
|
int audio_out_idx;
|
||||||
int audio_out_idx2;
|
int audio_out_idx2;
|
||||||
int audio_out_idxR;
|
int audio_out_idxR;
|
||||||
@@ -418,6 +558,7 @@ typedef struct
|
|||||||
char slot0light[8];
|
char slot0light[8];
|
||||||
float aout_gain;
|
float aout_gain;
|
||||||
float aout_gainR;
|
float aout_gainR;
|
||||||
|
float aout_gainA;
|
||||||
float aout_max_buf[200];
|
float aout_max_buf[200];
|
||||||
float aout_max_bufR[200];
|
float aout_max_bufR[200];
|
||||||
float *aout_max_buf_p;
|
float *aout_max_buf_p;
|
||||||
@@ -460,6 +601,7 @@ typedef struct
|
|||||||
unsigned int debug_audio_errorsR;
|
unsigned int debug_audio_errorsR;
|
||||||
unsigned int debug_header_errors;
|
unsigned int debug_header_errors;
|
||||||
unsigned int debug_header_critical_errors;
|
unsigned int debug_header_critical_errors;
|
||||||
|
int debug_mode; //debug misc things
|
||||||
|
|
||||||
// Last dibit read
|
// Last dibit read
|
||||||
int last_dibit;
|
int last_dibit;
|
||||||
@@ -498,28 +640,32 @@ typedef struct
|
|||||||
|
|
||||||
unsigned int dmr_fid;
|
unsigned int dmr_fid;
|
||||||
unsigned int dmr_so;
|
unsigned int dmr_so;
|
||||||
|
unsigned int dmr_flco;
|
||||||
|
|
||||||
unsigned int dmr_fidR;
|
unsigned int dmr_fidR;
|
||||||
unsigned int dmr_soR;
|
unsigned int dmr_soR;
|
||||||
|
unsigned int dmr_flcoR;
|
||||||
|
|
||||||
char slot1light[8];
|
char slot1light[8];
|
||||||
char slot2light[8];
|
char slot2light[8];
|
||||||
int directmode;
|
int directmode;
|
||||||
|
|
||||||
char dmr_branding[99];
|
|
||||||
int dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame
|
int dmr_stereo_payload[144]; //load up 144 dibit buffer for every single DMR TDMA frame
|
||||||
uint8_t data_header_blocks[2]; //collect number of blocks to follow from data header per slot
|
int data_header_blocks[2]; //collect number of blocks to follow from data header per slot
|
||||||
|
int data_block_counter[2]; //counter for number of data blocks collected
|
||||||
|
uint8_t data_header_valid[2]; //flag for verifying the data header if still valid (in case of tact/burst fec errs)
|
||||||
uint8_t data_header_padding[2]; //collect number of padding octets in last block per slot
|
uint8_t data_header_padding[2]; //collect number of padding octets in last block per slot
|
||||||
uint8_t data_header_format[2]; //collect format of data header (conf or unconf) per slot
|
uint8_t data_header_format[2]; //collect format of data header (conf or unconf) per slot
|
||||||
uint8_t data_header_sap[2]; //collect sap info per slot
|
uint8_t data_header_sap[2]; //collect sap info per slot
|
||||||
uint8_t data_block_counter[2]; //counter for number of data blocks collected
|
|
||||||
uint8_t data_p_head[2]; //flag for dmr proprietary header to follow
|
uint8_t data_p_head[2]; //flag for dmr proprietary header to follow
|
||||||
|
|
||||||
//new stuff below here
|
//new stuff below here
|
||||||
uint8_t data_conf_data[2]; //flag for confirmed data blocks per slot
|
uint8_t data_conf_data[2]; //flag for confirmed data blocks per slot
|
||||||
uint8_t dmr_pdu_sf[2][288]; //unified pdu 'superframe' //[slot][byte]
|
uint8_t dmr_pdu_sf[2][24*128]; //unified pdu 'superframe' //[slot][byte] -- increased capacity to 127(+1) full rate blocks
|
||||||
uint8_t data_block_crc_valid[2][25]; //flag each individual block as good crc on confirmed data
|
uint8_t cap_plus_csbk_bits[2][12*8*8]; //CSBK Cap+ FL initial and appended block bit storage, by slot
|
||||||
char dmr_embedded_signalling[2][6][48]; //embedded signalling 2 slots by 6 vc by 48 bits (replacing TS1SuperFrame.TimeSlotRawVoiceFrame.Sync structure)
|
uint8_t cap_plus_block_num[2]; //received block number storage -- per timeslot
|
||||||
|
uint8_t data_block_crc_valid[2][127]; //flag each individual block as good crc on confirmed data
|
||||||
|
char dmr_embedded_signalling[2][7][48]; //embedded signalling 2 slots by 6 vc by 48 bits (replacing TS1SuperFrame.TimeSlotRawVoiceFrame.Sync structure)
|
||||||
|
|
||||||
char dmr_cach_fragment[4][17]; //unsure of size, will need to check/verify
|
char dmr_cach_fragment[4][17]; //unsure of size, will need to check/verify
|
||||||
int dmr_cach_counter; //counter for dmr_cach_fragments 0-3; not sure if needed yet.
|
int dmr_cach_counter; //counter for dmr_cach_fragments 0-3; not sure if needed yet.
|
||||||
@@ -532,16 +678,27 @@ typedef struct
|
|||||||
char dmr_lrrp_gps[2][200]; //2 slots by 99 char string for string lrrp gps
|
char dmr_lrrp_gps[2][200]; //2 slots by 99 char string for string lrrp gps
|
||||||
char dmr_site_parms[200]; //string for site/net info depending on type of DMR system (TIII or Con+)
|
char dmr_site_parms[200]; //string for site/net info depending on type of DMR system (TIII or Con+)
|
||||||
char call_string[2][200]; //string for call information
|
char call_string[2][200]; //string for call information
|
||||||
|
char active_channel[31][200]; //string for storing and displaying active trunking channels
|
||||||
|
|
||||||
|
|
||||||
dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame;
|
dPMRVoiceFS2Frame_t dPMRVoiceFS2Frame;
|
||||||
|
|
||||||
unsigned char * dpmr_caller_id;
|
//new audio filter structs
|
||||||
unsigned char * dpmr_target_id;
|
LPFilter RCFilter;
|
||||||
|
HPFilter HRCFilter;
|
||||||
|
PBFilter PBF;
|
||||||
|
NOTCHFilter NF;
|
||||||
|
LPFilter RCFilterL;
|
||||||
|
HPFilter HRCFilterL;
|
||||||
|
LPFilter RCFilterR;
|
||||||
|
HPFilter HRCFilterR;
|
||||||
|
|
||||||
|
char dpmr_caller_id[20];
|
||||||
|
char dpmr_target_id[20];
|
||||||
|
|
||||||
int dpmr_color_code;
|
int dpmr_color_code;
|
||||||
|
|
||||||
short int dmr_stereo; //need state variable for upsample function
|
short int dmr_stereo; //need state variable for upsample function
|
||||||
short int dmr_ms_rc;
|
|
||||||
short int dmr_ms_mode;
|
short int dmr_ms_mode;
|
||||||
unsigned int dmrburstL;
|
unsigned int dmrburstL;
|
||||||
unsigned int dmrburstR;
|
unsigned int dmrburstR;
|
||||||
@@ -562,14 +719,18 @@ typedef struct
|
|||||||
unsigned long long int p2_wacn;
|
unsigned long long int p2_wacn;
|
||||||
unsigned long long int p2_sysid;
|
unsigned long long int p2_sysid;
|
||||||
unsigned long long int p2_cc; //p1 NAC
|
unsigned long long int p2_cc; //p1 NAC
|
||||||
|
unsigned long long int p2_siteid;
|
||||||
|
unsigned long long int p2_rfssid;
|
||||||
int p2_hardset; //flag for checking whether or not P2 wacn and sysid are hard set by user
|
int p2_hardset; //flag for checking whether or not P2 wacn and sysid are hard set by user
|
||||||
int p2_scramble_offset; //offset counter for scrambling application
|
int p2_scramble_offset; //offset counter for scrambling application
|
||||||
int p2_vch_chan_num; //vch channel number (0 or 1, not the 0-11 TS)
|
int p2_vch_chan_num; //vch channel number (0 or 1, not the 0-11 TS)
|
||||||
int ess_b[2][96]; //external storage for ESS_B fragments
|
int ess_b[2][96]; //external storage for ESS_B fragments
|
||||||
int fourv_counter[2]; //external reference counter for ESS_B fragment collection
|
int fourv_counter[2]; //external reference counter for ESS_B fragment collection
|
||||||
|
int voice_counter[2]; //external reference counter for 18V x 2 P25p2 Superframe
|
||||||
int p2_is_lcch; //flag to tell us when a frame is lcch and not sacch
|
int p2_is_lcch; //flag to tell us when a frame is lcch and not sacch
|
||||||
|
|
||||||
//iden freq storage for frequency calculations
|
//iden freq storage for frequency calculations
|
||||||
|
int p25_chan_tdma[16]; //set from iden_up vs iden_up_tdma
|
||||||
int p25_chan_iden;
|
int p25_chan_iden;
|
||||||
int p25_chan_type[16];
|
int p25_chan_type[16];
|
||||||
int p25_trans_off[16];
|
int p25_trans_off[16];
|
||||||
@@ -586,25 +747,45 @@ typedef struct
|
|||||||
int symbol_throttle; //throttle speed
|
int symbol_throttle; //throttle speed
|
||||||
int use_throttle; //only use throttle if set to 1
|
int use_throttle; //only use throttle if set to 1
|
||||||
|
|
||||||
//dstar header for ncurses
|
|
||||||
unsigned char dstarradioheader[41];
|
|
||||||
|
|
||||||
//dmr trunking stuff
|
//dmr trunking stuff
|
||||||
int dmr_rest_channel;
|
int dmr_rest_channel;
|
||||||
int dmr_mfid; //just when 'fid' is used as a manufacturer ID and not a feature set id
|
int dmr_mfid; //just when 'fid' is used as a manufacturer ID and not a feature set id
|
||||||
int dmr_vc_lcn;
|
int dmr_vc_lcn;
|
||||||
int dmr_vc_lsn;
|
int dmr_vc_lsn;
|
||||||
int dmr_tuned_lcn;
|
int dmr_tuned_lcn;
|
||||||
|
uint16_t dmr_cc_lpcn; //dmr t3 logical physical channel number
|
||||||
|
uint32_t tg_hold; //single TG to hold on when enabled
|
||||||
|
|
||||||
//edacs
|
//edacs
|
||||||
int ea_mode;
|
int ea_mode;
|
||||||
int esk_mode;
|
|
||||||
unsigned short esk_mask;
|
unsigned short esk_mask;
|
||||||
unsigned long long int edacs_site_id;
|
unsigned long long int edacs_site_id;
|
||||||
int edacs_lcn_count; //running tally of lcn's observed on edacs system
|
int edacs_lcn_count; //running tally of lcn's observed on edacs system
|
||||||
int edacs_cc_lcn; //current lcn for the edacs control channel
|
int edacs_cc_lcn; //current lcn for the edacs control channel
|
||||||
int edacs_vc_lcn; //current lcn for any active vc (not the one we are tuned/tuning to)
|
int edacs_vc_lcn; //current lcn for any active vc (not the one we are tuned/tuning to)
|
||||||
int edacs_tuned_lcn; //the vc we are currently tuned to...above variable is for updating all in the matrix
|
int edacs_tuned_lcn; //the vc we are currently tuned to...above variable is for updating all in the matrix
|
||||||
|
int edacs_vc_call_type; //the type of call on the given VC - see defines below
|
||||||
|
int edacs_a_bits; // Agency Significant Bits
|
||||||
|
int edacs_f_bits; // Fleet Significant Bits
|
||||||
|
int edacs_s_bits; //Subfleet Significant Bits
|
||||||
|
int edacs_a_shift; //Calculated Shift for A Bits
|
||||||
|
int edacs_f_shift; //Calculated Shift for F Bits
|
||||||
|
int edacs_a_mask; //Calculated Mask for A Bits
|
||||||
|
int edacs_f_mask; //Calculated Mask for F Bits
|
||||||
|
int edacs_s_mask; //Calculated Mask for S Bits
|
||||||
|
|
||||||
|
//flags for EDACS call type
|
||||||
|
#define EDACS_IS_VOICE 0x01
|
||||||
|
#define EDACS_IS_DIGITAL 0x02
|
||||||
|
#define EDACS_IS_EMERGENCY 0x04
|
||||||
|
#define EDACS_IS_GROUP 0x08
|
||||||
|
#define EDACS_IS_INDIVIDUAL 0x10
|
||||||
|
#define EDACS_IS_ALL_CALL 0x20
|
||||||
|
#define EDACS_IS_INTERCONNECT 0x40
|
||||||
|
#define EDACS_IS_TEST_CALL 0x80
|
||||||
|
#define EDACS_IS_AGENCY_CALL 0x100
|
||||||
|
#define EDACS_IS_FLEET_CALL 0x200
|
||||||
|
|
||||||
//trunking group and lcn freq list
|
//trunking group and lcn freq list
|
||||||
long int trunk_lcn_freq[26]; //max number on an EDACS system, should be enough on DMR too hopefully
|
long int trunk_lcn_freq[26]; //max number on an EDACS system, should be enough on DMR too hopefully
|
||||||
@@ -615,6 +796,8 @@ typedef struct
|
|||||||
int lcn_freq_roll; //number we have 'rolled' to in search of the CC
|
int lcn_freq_roll; //number we have 'rolled' to in search of the CC
|
||||||
time_t last_cc_sync_time; //use this to start hunting for CC after signal lost
|
time_t last_cc_sync_time; //use this to start hunting for CC after signal lost
|
||||||
time_t last_vc_sync_time; //flag for voice activity bursts, tune back on con+ after more than x seconds no voice
|
time_t last_vc_sync_time; //flag for voice activity bursts, tune back on con+ after more than x seconds no voice
|
||||||
|
time_t last_active_time; //time the a 'call grant' was received, used to clear the active_channel strings after x seconds
|
||||||
|
time_t last_t3_tune_time; //last time a DMR T3 grant was received, this is used to prevent a rogue p_clear condition from immediately sending back to CC
|
||||||
int is_con_plus; //con_plus flag for knowing its safe to skip payload channel after x seconds of no voice sync
|
int is_con_plus; //con_plus flag for knowing its safe to skip payload channel after x seconds of no voice sync
|
||||||
|
|
||||||
//new nxdn stuff
|
//new nxdn stuff
|
||||||
@@ -626,22 +809,94 @@ typedef struct
|
|||||||
uint8_t nxdn_sacch_frame_segcrc[4];
|
uint8_t nxdn_sacch_frame_segcrc[4];
|
||||||
uint8_t nxdn_alias_block_number;
|
uint8_t nxdn_alias_block_number;
|
||||||
char nxdn_alias_block_segment[4][4][8];
|
char nxdn_alias_block_segment[4][4][8];
|
||||||
|
|
||||||
//site/srv/cch info
|
//site/srv/cch info
|
||||||
char nxdn_location_category[14];
|
char nxdn_location_category[14];
|
||||||
uint32_t nxdn_location_sys_code;
|
uint32_t nxdn_location_sys_code;
|
||||||
uint16_t nxdn_location_site_code;
|
uint16_t nxdn_location_site_code;
|
||||||
|
|
||||||
//dmr late entry mi
|
//channel access information
|
||||||
uint64_t late_entry_mi_fragment[2][7][3];
|
uint8_t nxdn_rcn;
|
||||||
|
uint8_t nxdn_base_freq;
|
||||||
|
uint8_t nxdn_step;
|
||||||
|
uint8_t nxdn_bw;
|
||||||
|
|
||||||
//char dmr_branding[25]; //may need higher value?
|
//multi-key array
|
||||||
char dmr_branding_sub[25];
|
unsigned long long int rkey_array[0xFFFF];
|
||||||
|
int keyloader; //let us know the keyloader is active
|
||||||
|
|
||||||
|
//dmr late entry mi
|
||||||
|
uint64_t late_entry_mi_fragment[2][8][3];
|
||||||
|
|
||||||
|
//dmr manufacturer branding and sub_branding (i.e., Motorola and Con+)
|
||||||
|
char dmr_branding[20];
|
||||||
|
char dmr_branding_sub[80];
|
||||||
|
|
||||||
//Remus DMR End Call Alert Beep
|
//Remus DMR End Call Alert Beep
|
||||||
int dmr_end_alert[2]; //dmr TLC end call alert beep has already played once flag
|
int dmr_end_alert[2]; //dmr TLC end call alert beep has already played once flag
|
||||||
|
|
||||||
//Upsampled Audio Smoothing
|
//Bitmap Filtering Options
|
||||||
uint8_t audio_smoothing;
|
int audio_smoothing;
|
||||||
|
|
||||||
|
//YSF Fusion Call Strings and Info
|
||||||
|
uint8_t ysf_dt; //data type -- VD1, VD2, Full Rate, etc.
|
||||||
|
uint8_t ysf_fi; //frame information -- HC, CC, TC
|
||||||
|
uint8_t ysf_cm; //group or private call
|
||||||
|
char ysf_tgt[11];
|
||||||
|
char ysf_src[11];
|
||||||
|
char ysf_upl[11];
|
||||||
|
char ysf_dnl[11];
|
||||||
|
char ysf_rm1[6];
|
||||||
|
char ysf_rm2[6];
|
||||||
|
char ysf_rm3[6];
|
||||||
|
char ysf_rm4[6];
|
||||||
|
char ysf_txt[21][21]; //text storage blocks
|
||||||
|
|
||||||
|
//DSTAR Call Strings and Info
|
||||||
|
char dstar_rpt1[9];
|
||||||
|
char dstar_rpt2[9];
|
||||||
|
char dstar_dst[9];
|
||||||
|
char dstar_src[13];
|
||||||
|
char dstar_txt[60];
|
||||||
|
char dstar_gps[60];
|
||||||
|
|
||||||
|
//M17 Storage
|
||||||
|
uint8_t m17_lsf[360];
|
||||||
|
uint8_t m17_pkt[850];
|
||||||
|
uint8_t m17_pbc_ct; //pbc packet counter
|
||||||
|
uint8_t m17_str_dt; //stream contents
|
||||||
|
|
||||||
|
unsigned long long int m17_dst;
|
||||||
|
unsigned long long int m17_src;
|
||||||
|
uint8_t m17_can; //can value that was decoded from signal
|
||||||
|
int m17_can_en; //can value supplied to the encoding side
|
||||||
|
int m17_rate; //sampling rate for audio input
|
||||||
|
int m17_vox; //vox enabled via RMS value
|
||||||
|
|
||||||
|
char m17_dst_csd[20];
|
||||||
|
char m17_src_csd[20];
|
||||||
|
|
||||||
|
char m17_src_str[50];
|
||||||
|
char m17_dst_str[50];
|
||||||
|
|
||||||
|
uint8_t m17_meta[16]; //packed meta
|
||||||
|
uint8_t m17_enc; //enc type
|
||||||
|
uint8_t m17_enc_st; //scrambler or data subtye
|
||||||
|
int m17encoder_tx; //if TX (encode + decode) M17 Stream is enabled
|
||||||
|
int m17encoder_eot; //signal if we need to send the EOT frame
|
||||||
|
|
||||||
|
//misc str storage
|
||||||
|
char str50a[50];
|
||||||
|
char str50b[50];
|
||||||
|
char str50c[50];
|
||||||
|
char m17dat[50]; //user supplied m17 data input string
|
||||||
|
char m17sms[800]; //user supplied sms text string
|
||||||
|
|
||||||
|
//Codec2
|
||||||
|
#ifdef USE_CODEC2
|
||||||
|
struct CODEC2 *codec2_3200; //M17 fullrate
|
||||||
|
struct CODEC2 *codec2_1600; //M17 halfrate
|
||||||
|
#endif
|
||||||
|
|
||||||
} dsd_state;
|
} dsd_state;
|
||||||
|
|
||||||
@@ -649,8 +904,19 @@ typedef struct
|
|||||||
* Frame sync patterns
|
* Frame sync patterns
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define FUSION_SYNC "31111311313113131131" //HA!
|
//M17 Sync Patterns
|
||||||
#define INV_FUSION_SYNC "13333133131331313313" //HA!
|
#define M17_LSF "11113313"
|
||||||
|
#define M17_STR "33331131"
|
||||||
|
//alternating with last symbol opposite of first symbol of LSF
|
||||||
|
#define M17_PRE "31313131"
|
||||||
|
#define M17_PIV "13131313"
|
||||||
|
#define M17_PRE_LSF "3131313133331131" //Preamble + LSF
|
||||||
|
#define M17_PIV_LSF "1313131311113313" //Preamble + LSF
|
||||||
|
#define M17_BRT "31331111"
|
||||||
|
#define M17_PKT "13113333"
|
||||||
|
|
||||||
|
#define FUSION_SYNC "31111311313113131131"
|
||||||
|
#define INV_FUSION_SYNC "13333133131331313313"
|
||||||
|
|
||||||
#define INV_P25P1_SYNC "333331331133111131311111"
|
#define INV_P25P1_SYNC "333331331133111131311111"
|
||||||
#define P25P1_SYNC "111113113311333313133333"
|
#define P25P1_SYNC "111113113311333313133333"
|
||||||
@@ -689,7 +955,7 @@ typedef struct
|
|||||||
#define NXDN_PANDFSW "3131133313131331131" //19 symbols
|
#define NXDN_PANDFSW "3131133313131331131" //19 symbols
|
||||||
#define INV_NXDN_PANDFSW "1313311131313113313" //19 symbols
|
#define INV_NXDN_PANDFSW "1313311131313113313" //19 symbols
|
||||||
|
|
||||||
#define DMR_RC_DATA_SYNC "131331111133133133311313"
|
#define DMR_RESERVED_SYNC "131331111133133133311313"
|
||||||
|
|
||||||
#define DMR_DIRECT_MODE_TS1_DATA_SYNC "331333313111313133311111"
|
#define DMR_DIRECT_MODE_TS1_DATA_SYNC "331333313111313133311111"
|
||||||
#define DMR_DIRECT_MODE_TS1_VOICE_SYNC "113111131333131311133333"
|
#define DMR_DIRECT_MODE_TS1_VOICE_SYNC "113111131333131311133333"
|
||||||
@@ -701,6 +967,30 @@ typedef struct
|
|||||||
#define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311"
|
#define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311"
|
||||||
#define PROVOICE_EA_SYNC "31131311331331111133131311311133"
|
#define PROVOICE_EA_SYNC "31131311331331111133131311311133"
|
||||||
|
|
||||||
|
//EDACS/PV EOT dotting sequence
|
||||||
|
#define DOTTING_SEQUENCE_A "131313131313131313131313131313131313131313131313" //0xAAAA...
|
||||||
|
#define DOTTING_SEQUENCE_B "313131313131313131313131313131313131313131313131" //0x5555...
|
||||||
|
|
||||||
|
//define the provoice conventional string pattern to default 85/85 if not enabled, else mute it so we won't double sync on accident in frame_sync
|
||||||
|
#ifdef PVCONVENTIONAL
|
||||||
|
#define PROVOICE_CONV "00000000000000000000000000000000" //all zeroes should be unobtainable string in the frame_sync synctests
|
||||||
|
#define INV_PROVOICE_CONV "00000000000000000000000000000000" //all zeroes should be unobtainable string in the frame_sync synctests
|
||||||
|
#else
|
||||||
|
#define PROVOICE_CONV "13131333111311311313131313131313" //TX 85 RX 85 (default programming value)
|
||||||
|
#define INV_PROVOICE_CONV "31313111333133133131313131313131" //TX 85 RX 85 (default programming value)
|
||||||
|
#endif
|
||||||
|
//we use the short sync instead of the default 85/85 when PVCONVENTIONAL is defined by cmake
|
||||||
|
#define PROVOICE_CONV_SHORT "1313133311131131" //16-bit short pattern, last 16-bits change based on TX an RX values
|
||||||
|
#define INV_PROVOICE_CONV_SHORT "3131311133313313"
|
||||||
|
//In this pattern (inverted polarity, the norm for PV) 3 is bit 0, and 1 is bit 1 (2 level GFSK)
|
||||||
|
//same pattern //TX //RX
|
||||||
|
// Sync Pattern = 3131311133313313 31331131 31331131 TX/RX 77 -- 31331131 symbol = 01001101 binary = 77 decimal
|
||||||
|
// Sync Pattern = 3131311133313313 33333333 33333333 TX/RX 0 -- 33333333 symbol = 00000000 binary = 0 decimal
|
||||||
|
// Sync Pattern = 3131311133313313 33333331 33333331 TX/RX 1 -- 33333331 symbol = 00000001 binary = 1 decimal
|
||||||
|
// Sync Pattern = 3131311133313313 13131133 13131133 TX/RX 172 -- 13131133 symbol = 10101100 binary = 172 decimal
|
||||||
|
// Sync Pattern = 3131311133313313 11333111 11333111 TX/RX 199 -- 11333111 symbol = 11000111 binary = 199 decimal
|
||||||
|
// Sync Pattern = 3131311133313313 31313131 31313131 TX/RX 85 -- 31313131 symbol = 01010101 binary = 85 decimal
|
||||||
|
|
||||||
#define EDACS_SYNC "313131313131313131313111333133133131313131313131"
|
#define EDACS_SYNC "313131313131313131313111333133133131313131313131"
|
||||||
#define INV_EDACS_SYNC "131313131313131313131333111311311313131313131313"
|
#define INV_EDACS_SYNC "131313131313131313131333111311311313131313131313"
|
||||||
|
|
||||||
@@ -722,37 +1012,67 @@ typedef struct
|
|||||||
void processdPMRvoice (dsd_opts * opts, dsd_state * state);
|
void processdPMRvoice (dsd_opts * opts, dsd_state * state);
|
||||||
void processAudio (dsd_opts * opts, dsd_state * state);
|
void processAudio (dsd_opts * opts, dsd_state * state);
|
||||||
void processAudioR (dsd_opts * opts, dsd_state * state);
|
void processAudioR (dsd_opts * opts, dsd_state * state);
|
||||||
void openPulseInput (dsd_opts * opts); //not sure if we need to just pass opts, or opts and state yet
|
void openPulseInput (dsd_opts * opts);
|
||||||
void openPulseOutput (dsd_opts * opts); //not sure if we need to just pass opts, or opts and state yet
|
void openPulseOutput (dsd_opts * opts);
|
||||||
|
void openOSSOutput (dsd_opts * opts);
|
||||||
void closePulseInput (dsd_opts * opts);
|
void closePulseInput (dsd_opts * opts);
|
||||||
void closePulseOutput (dsd_opts * opts);
|
void closePulseOutput (dsd_opts * opts);
|
||||||
void writeSynthesizedVoice (dsd_opts * opts, dsd_state * state);
|
void writeSynthesizedVoice (dsd_opts * opts, dsd_state * state);
|
||||||
void writeSynthesizedVoiceR (dsd_opts * opts, dsd_state * state);
|
void writeSynthesizedVoiceR (dsd_opts * opts, dsd_state * state);
|
||||||
void playSynthesizedVoice (dsd_opts * opts, dsd_state * state);
|
void playSynthesizedVoice (dsd_opts * opts, dsd_state * state); //short mono output slot 1
|
||||||
void playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state);
|
void playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state); //short mono output slot 2
|
||||||
|
//new float stuff
|
||||||
|
void playSynthesizedVoiceFS (dsd_opts * opts, dsd_state * state); //float stereo mix
|
||||||
|
void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state); //float stereo mix 3v2 DMR
|
||||||
|
void playSynthesizedVoiceFS4 (dsd_opts * opts, dsd_state * state); //float stereo mix 4v2 P25p2
|
||||||
|
void playSynthesizedVoiceFM (dsd_opts * opts, dsd_state * state); //float mono
|
||||||
|
void agf (dsd_opts * opts, dsd_state * state, float samp[160], int slot); //float gain control
|
||||||
|
void agsm (dsd_opts * opts, dsd_state * state, short * input, int len); //short gain control for analog things
|
||||||
|
void analog_gain (dsd_opts * opts, dsd_state * state, short * input, int len); //manual gain handling for analong things
|
||||||
|
//new short stuff
|
||||||
|
void playSynthesizedVoiceMS (dsd_opts * opts, dsd_state * state); //short mono mix
|
||||||
|
void playSynthesizedVoiceMSR (dsd_opts * opts, dsd_state * state); //short mono mix R (needed for OSS 48k input/output)
|
||||||
|
void playSynthesizedVoiceSS (dsd_opts * opts, dsd_state * state); //short stereo mix
|
||||||
|
void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state); //short stereo mix 3v2 DMR
|
||||||
|
void playSynthesizedVoiceSS4 (dsd_opts * opts, dsd_state * state); //short stereo mix 4v2 P25p2
|
||||||
|
void playSynthesizedVoiceSS18 (dsd_opts * opts, dsd_state * state); //short stereo mix 18V Superframe
|
||||||
|
void upsampleS (short invalue, short prev, short outbuf[6]); //upsample 8k to 48k short
|
||||||
|
//
|
||||||
void openAudioOutDevice (dsd_opts * opts, int speed);
|
void openAudioOutDevice (dsd_opts * opts, int speed);
|
||||||
void openAudioInDevice (dsd_opts * opts);
|
void openAudioInDevice (dsd_opts * opts);
|
||||||
|
|
||||||
|
//pulse sources and sinks
|
||||||
|
void parse_pulse_input_string (dsd_opts * opts, char * input);
|
||||||
|
void parse_pulse_output_string (dsd_opts * opts, char * input);
|
||||||
|
void pa_state_cb (pa_context *c, void *userdata);
|
||||||
|
void pa_sinklist_cb (pa_context *c, const pa_sink_info *l, int eol, void *userdata);
|
||||||
|
void pa_sourcelist_cb (pa_context *c, const pa_source_info *l, int eol, void *userdata);
|
||||||
|
int pa_get_devicelist (pa_devicelist_t *input, pa_devicelist_t *output);
|
||||||
|
int pulse_list();
|
||||||
|
|
||||||
int getDibit (dsd_opts * opts, dsd_state * state);
|
int getDibit (dsd_opts * opts, dsd_state * state);
|
||||||
int get_dibit_and_analog_signal (dsd_opts * opts, dsd_state * state, int * out_analog_signal);
|
int get_dibit_and_analog_signal (dsd_opts * opts, dsd_state * state, int * out_analog_signal);
|
||||||
|
int digitize (dsd_opts* opts, dsd_state* state, int symbol);
|
||||||
|
|
||||||
void skipDibit (dsd_opts * opts, dsd_state * state, int count);
|
void skipDibit (dsd_opts * opts, dsd_state * state, int count);
|
||||||
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
||||||
void saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
void saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
||||||
|
void saveAmbe2450DataR (dsd_opts * opts, dsd_state * state, char *ambe_d); //tdma slot 2
|
||||||
void PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
void PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
||||||
void PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
void PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
||||||
int readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
int readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
||||||
int readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
int readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
||||||
void openMbeInFile (dsd_opts * opts, dsd_state * state);
|
void openMbeInFile (dsd_opts * opts, dsd_state * state);
|
||||||
void closeMbeOutFile (dsd_opts * opts, dsd_state * state);
|
void closeMbeOutFile (dsd_opts * opts, dsd_state * state);
|
||||||
|
void closeMbeOutFileR (dsd_opts * opts, dsd_state * state); //tdma slot 2
|
||||||
void openMbeOutFile (dsd_opts * opts, dsd_state * state);
|
void openMbeOutFile (dsd_opts * opts, dsd_state * state);
|
||||||
|
void openMbeOutFileR (dsd_opts * opts, dsd_state * state); //tdma slot 2
|
||||||
void openWavOutFile (dsd_opts * opts, dsd_state * state);
|
void openWavOutFile (dsd_opts * opts, dsd_state * state);
|
||||||
void openWavOutFileL (dsd_opts * opts, dsd_state * state);
|
void openWavOutFileL (dsd_opts * opts, dsd_state * state);
|
||||||
void openWavOutFileR (dsd_opts * opts, dsd_state * state);
|
void openWavOutFileR (dsd_opts * opts, dsd_state * state);
|
||||||
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state);
|
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state);
|
||||||
void openSymbolOutFile (dsd_opts * opts, dsd_state * state);
|
void openSymbolOutFile (dsd_opts * opts, dsd_state * state);
|
||||||
void closeSymbolOutFile (dsd_opts * opts, dsd_state * state);
|
void closeSymbolOutFile (dsd_opts * opts, dsd_state * state);
|
||||||
void writeSymbolOutFile (dsd_opts * opts, dsd_state * state);
|
|
||||||
void writeRawSample (dsd_opts * opts, dsd_state * state, short sample);
|
void writeRawSample (dsd_opts * opts, dsd_state * state, short sample);
|
||||||
void closeWavOutFile (dsd_opts * opts, dsd_state * state);
|
void closeWavOutFile (dsd_opts * opts, dsd_state * state);
|
||||||
void closeWavOutFileL (dsd_opts * opts, dsd_state * state);
|
void closeWavOutFileL (dsd_opts * opts, dsd_state * state);
|
||||||
@@ -769,7 +1089,6 @@ void initState (dsd_state * state);
|
|||||||
void usage ();
|
void usage ();
|
||||||
void liveScanner (dsd_opts * opts, dsd_state * state);
|
void liveScanner (dsd_opts * opts, dsd_state * state);
|
||||||
void cleanupAndExit (dsd_opts * opts, dsd_state * state);
|
void cleanupAndExit (dsd_opts * opts, dsd_state * state);
|
||||||
// void sigfun (int sig); //not necesary
|
|
||||||
int main (int argc, char **argv);
|
int main (int argc, char **argv);
|
||||||
void playMbeFiles (dsd_opts * opts, dsd_state * state, 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 processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24]);
|
||||||
@@ -779,6 +1098,17 @@ int getSymbol (dsd_opts * opts, dsd_state * state, int have_sync);
|
|||||||
void upsample (dsd_state * state, float invalue);
|
void upsample (dsd_state * state, float invalue);
|
||||||
void processDSTAR (dsd_opts * opts, dsd_state * state);
|
void processDSTAR (dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
|
//new cleaner, sleaker, nicer mbe handler...maybe -- wrap around ifdef later on with cmake options
|
||||||
|
void soft_mbe (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24]);
|
||||||
|
void soft_tonef (float samp[160], int n, int ID, int AD);
|
||||||
|
|
||||||
|
//new p25lcw
|
||||||
|
void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t irrecoverable_errors);
|
||||||
|
//new p25 1/2 rate decoder
|
||||||
|
int p25_12(uint8_t * input, uint8_t treturn[12]);
|
||||||
|
//new p25 lsd fec function
|
||||||
|
int p25p1_lsd_fec(uint8_t * input);
|
||||||
|
|
||||||
void processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo);
|
void processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo);
|
||||||
void processHDU (dsd_opts * opts, dsd_state * state);
|
void processHDU (dsd_opts * opts, dsd_state * state);
|
||||||
void processLDU1 (dsd_opts * opts, dsd_state * state);
|
void processLDU1 (dsd_opts * opts, dsd_state * state);
|
||||||
@@ -788,17 +1118,35 @@ void processTDULC (dsd_opts * opts, dsd_state * state);
|
|||||||
void processProVoice (dsd_opts * opts, dsd_state * state);
|
void processProVoice (dsd_opts * opts, dsd_state * state);
|
||||||
void processX2TDMAdata (dsd_opts * opts, dsd_state * state);
|
void processX2TDMAdata (dsd_opts * opts, dsd_state * state);
|
||||||
void processX2TDMAvoice (dsd_opts * opts, dsd_state * state);
|
void processX2TDMAvoice (dsd_opts * opts, dsd_state * state);
|
||||||
void processDSTAR_HD (dsd_opts * opts, dsd_state * state);
|
void processDSTAR_HD (dsd_opts * opts, dsd_state * state); //DSTAR Header
|
||||||
|
void processDSTAR_SD (dsd_opts * opts, dsd_state * state, uint8_t * sd); //DSTAR Slow Data
|
||||||
void processYSF(dsd_opts * opts, dsd_state * state); //YSF
|
void processYSF(dsd_opts * opts, dsd_state * state); //YSF
|
||||||
|
void processM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR)
|
||||||
|
void processM17PKT(dsd_opts * opts, dsd_state * state); //M17 (PKT)
|
||||||
|
void processM17LSF(dsd_opts * opts, dsd_state * state); //M17 (LSF)
|
||||||
|
void processM17IPF(dsd_opts * opts, dsd_state * state); //M17 (IPF)
|
||||||
|
void encodeM17STR(dsd_opts * opts, dsd_state * state); //M17 (STR) encoder
|
||||||
|
void encodeM17BRT(dsd_opts * opts, dsd_state * state); //M17 (BRT) encoder
|
||||||
|
void encodeM17PKT(dsd_opts * opts, dsd_state * state); //M17 (PKT) encoder
|
||||||
|
void decodeM17PKT(dsd_opts * opts, dsd_state * state, uint8_t * input, int len); //M17 (PKT) decoder
|
||||||
void processP2(dsd_opts * opts, dsd_state * state); //P2
|
void processP2(dsd_opts * opts, dsd_state * state); //P2
|
||||||
void processTSBK(dsd_opts * opts, dsd_state * state);
|
void processTSBK(dsd_opts * opts, dsd_state * state); //P25 Trunking Single Block
|
||||||
|
void processMPDU(dsd_opts * opts, dsd_state * state); //P25 Multi Block PDU (SAP 0x61 FMT 0x15 or 0x17 for Trunking Blocks)
|
||||||
short dmr_filter(short sample);
|
short dmr_filter(short sample);
|
||||||
short nxdn_filter(short sample);
|
short nxdn_filter(short sample);
|
||||||
short dpmr_filter(short sample);
|
short dpmr_filter(short sample);
|
||||||
|
short m17_filter(short sample);
|
||||||
|
|
||||||
int strncmperr(const char *s1, const char *s2, size_t size, int MaxErr);
|
//utility functions
|
||||||
/* Global functions */
|
uint64_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength);
|
||||||
uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength);
|
uint64_t convert_bits_into_output (uint8_t * input, int len);
|
||||||
|
void pack_bit_array_into_byte_array (uint8_t * input, uint8_t * output, int len);
|
||||||
|
void pack_bit_array_into_byte_array_asym (uint8_t * input, uint8_t * output, int len);
|
||||||
|
void unpack_byte_array_into_bit_array (uint8_t * input, uint8_t * output, int len);
|
||||||
|
|
||||||
|
//ambe pack and unpack functions
|
||||||
|
void pack_ambe (char * input, uint8_t * output, int len);
|
||||||
|
void unpack_ambe (uint8_t * input, char * ambe);
|
||||||
|
|
||||||
void ncursesOpen (dsd_opts * opts, dsd_state * state);
|
void ncursesOpen (dsd_opts * opts, dsd_state * state);
|
||||||
void ncursesPrinter (dsd_opts * opts, dsd_state * state);
|
void ncursesPrinter (dsd_opts * opts, dsd_state * state);
|
||||||
@@ -807,25 +1155,42 @@ void ncursesClose ();
|
|||||||
//new NXDN Functions start here!
|
//new NXDN Functions start here!
|
||||||
void nxdn_frame (dsd_opts * opts, dsd_state * state);
|
void nxdn_frame (dsd_opts * opts, dsd_state * state);
|
||||||
void nxdn_descramble (uint8_t dibits[], int len);
|
void nxdn_descramble (uint8_t dibits[], int len);
|
||||||
|
//nxdn deinterleaving/depuncturing functions
|
||||||
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
|
void nxdn_deperm_facch (dsd_opts * opts, dsd_state * state, uint8_t bits[144]);
|
||||||
void nxdn_deperm_sacch (dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
|
void nxdn_deperm_sacch (dsd_opts * opts, dsd_state * state, uint8_t bits[60]);
|
||||||
void nxdn_deperm_cac (dsd_opts * opts, dsd_state * state, uint8_t bits[300]);
|
void nxdn_deperm_cac (dsd_opts * opts, dsd_state * state, uint8_t bits[300]);
|
||||||
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348]);
|
void nxdn_deperm_facch2_udch (dsd_opts * opts, dsd_state * state, uint8_t bits[348], uint8_t type);
|
||||||
|
//type-d 'idas' deinterleaving/depuncturing functions
|
||||||
|
void nxdn_deperm_scch(dsd_opts * opts, dsd_state * state, uint8_t bits[60], uint8_t direction);
|
||||||
|
void nxdn_deperm_facch3_udch2(dsd_opts * opts, dsd_state * state, uint8_t bits[288], uint8_t type);
|
||||||
|
//end
|
||||||
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType);
|
void nxdn_message_type (dsd_opts * opts, dsd_state * state, uint8_t MessageType);
|
||||||
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
|
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182]);
|
||||||
|
//Osmocom OP25 12 Rate Trellis Decoder (for NXDN, M17, YSF, etc)
|
||||||
|
void trellis_decode(uint8_t result[], const uint8_t source[], int result_len);
|
||||||
|
|
||||||
//OP25 NXDN CRC functions
|
//OP25 NXDN CRC functions
|
||||||
static inline int load_i(const uint8_t val[], int len);
|
int load_i(const uint8_t val[], int len);
|
||||||
static uint8_t crc6(const uint8_t buf[], int len);
|
uint8_t crc6(const uint8_t buf[], int len);
|
||||||
static uint16_t crc12f(const uint8_t buf[], int len);
|
uint16_t crc12f(const uint8_t buf[], int len);
|
||||||
static uint16_t crc15(const uint8_t buf[], int len);
|
uint16_t crc15(const uint8_t buf[], int len);
|
||||||
static uint16_t crc16cac(const uint8_t buf[], int len);
|
uint16_t crc16cac(const uint8_t buf[], int len);
|
||||||
|
uint8_t crc7_scch(uint8_t bits[], int len); //converted from op25 crc6
|
||||||
|
|
||||||
/* NXDN Convolution functions */
|
/* NXDN Convolution functions */
|
||||||
void CNXDNConvolution_start(void);
|
void CNXDNConvolution_start(void);
|
||||||
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
|
void CNXDNConvolution_decode(uint8_t s0, uint8_t s1);
|
||||||
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
|
void CNXDNConvolution_chainback(unsigned char* out, unsigned int nBits);
|
||||||
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
|
void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsigned int nBits);
|
||||||
|
void CNXDNConvolution_init();
|
||||||
|
|
||||||
|
//libM17 viterbi decoder
|
||||||
|
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len);
|
||||||
|
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len);
|
||||||
|
void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos);
|
||||||
|
uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len);
|
||||||
|
void viterbi_reset(void);
|
||||||
|
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2);
|
||||||
|
|
||||||
//keeping these
|
//keeping these
|
||||||
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state);
|
void NXDN_SACCH_Full_decode(dsd_opts * opts, dsd_state * state);
|
||||||
@@ -842,7 +1207,9 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa
|
|||||||
void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
void NXDN_decode_cch_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||||
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
void NXDN_decode_srv_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||||
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
void NXDN_decode_site_info(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||||
void nxdn_location_id_handler (dsd_state * state, uint32_t location_id);
|
void NXDN_decode_adj_site(dsd_opts * opts, dsd_state * state, uint8_t * Message);
|
||||||
|
//Type-D SCCH Message Decoder
|
||||||
|
void NXDN_decode_scch(dsd_opts * opts, dsd_state * state, uint8_t * Message, uint8_t direction);
|
||||||
|
|
||||||
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
|
void dPMRVoiceFrameProcess(dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
@@ -858,7 +1225,7 @@ int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24]);
|
|||||||
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output);
|
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output);
|
||||||
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3]);
|
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3]);
|
||||||
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77]);
|
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77]);
|
||||||
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32]);
|
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32], uint32_t ParityCheckTypeOdd);
|
||||||
|
|
||||||
//Reed Solomon (12,9) functions
|
//Reed Solomon (12,9) functions
|
||||||
void rs_12_9_calc_syndrome(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome);
|
void rs_12_9_calc_syndrome(rs_12_9_codeword_t *codeword, rs_12_9_poly_t *syndrome);
|
||||||
@@ -871,6 +1238,8 @@ uint16_t ComputeCrcCCITT(uint8_t * DMRData);
|
|||||||
uint16_t ComputeCrcCCITT16d(const uint8_t buf[], uint8_t len);
|
uint16_t ComputeCrcCCITT16d(const uint8_t buf[], uint8_t len);
|
||||||
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask);
|
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask);
|
||||||
uint8_t ComputeCrc5Bit(uint8_t * DMRData);
|
uint8_t ComputeCrc5Bit(uint8_t * DMRData);
|
||||||
|
uint16_t ComputeCrc9Bit(uint8_t * DMRData, uint32_t NbData);
|
||||||
|
uint32_t ComputeCrc32Bit(uint8_t * DMRData, uint32_t NbData);
|
||||||
|
|
||||||
//new simplified dmr functions
|
//new simplified dmr functions
|
||||||
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst);
|
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst);
|
||||||
@@ -880,11 +1249,19 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
|||||||
void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8_t cs_pdu[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8_t cs_pdu[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
||||||
void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[]);
|
void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[]);
|
||||||
uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25]);
|
uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25]);
|
||||||
|
uint32_t dmr_34(uint8_t * input, uint8_t treturn[18]); //simplier trellis decoder
|
||||||
|
void beeper (dsd_opts * opts, dsd_state * state, int lr); //the tone beeper function
|
||||||
|
void dmr_gateway_identifier (uint32_t source, uint32_t target); //translate special addresses
|
||||||
|
|
||||||
//Embedded Alias and GPS
|
//Embedded Alias and GPS reports
|
||||||
void dmr_embedded_alias_header (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
void dmr_embedded_alias_header (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
||||||
void dmr_embedded_alias_blocks (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
void dmr_embedded_alias_blocks (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
||||||
void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
||||||
|
void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]);
|
||||||
|
void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input);
|
||||||
|
void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int type);
|
||||||
|
void nmea_harris (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot);
|
||||||
|
void harris_gps(dsd_opts * opts, dsd_state * state, int slot, uint8_t * input);
|
||||||
|
|
||||||
//"DMR STEREO"
|
//"DMR STEREO"
|
||||||
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
|
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state);
|
||||||
@@ -894,26 +1271,46 @@ void dmrMSData (dsd_opts * opts, dsd_state * state);
|
|||||||
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state);
|
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
//dmr data header and multi block types (header, 1/2, 3/4, 1, Unified)
|
//dmr data header and multi block types (header, 1/2, 3/4, 1, Unified)
|
||||||
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint8_t dheader_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors);
|
||||||
void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type);
|
void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type);
|
||||||
void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
||||||
void dmr_reset_blocks (dsd_opts * opts, dsd_state * state);
|
void dmr_reset_blocks (dsd_opts * opts, dsd_state * state);
|
||||||
uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
void dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
||||||
|
void dmr_locn (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[]);
|
||||||
|
|
||||||
|
//Time and Date Functions
|
||||||
|
char * getTime();
|
||||||
|
char * getTimeC();
|
||||||
|
char * getTimeN(time_t t);
|
||||||
|
char * getDate();
|
||||||
|
char * getDateH();
|
||||||
|
char * getDateN(time_t t);
|
||||||
|
|
||||||
//dmr alg stuff
|
//dmr alg stuff
|
||||||
void dmr_alg_reset (dsd_opts * opts, dsd_state * state);
|
void dmr_alg_reset (dsd_opts * opts, dsd_state * state);
|
||||||
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state);
|
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state);
|
||||||
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]);
|
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, uint8_t ambe_fr[4][24], uint8_t ambe_fr2[4][24], uint8_t ambe_fr3[4][24]);
|
||||||
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state);
|
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state);
|
||||||
|
void dmr_decode_syscode(dsd_opts * opts, dsd_state * state, uint8_t * cs_pdu_bits, int csbk_fid, int type);
|
||||||
|
|
||||||
|
//handle Single Burst (Voice Burst F) or Reverse Channel Signalling
|
||||||
|
void dmr_sbrc (dsd_opts * opts, dsd_state * state, uint8_t power);
|
||||||
|
|
||||||
//DMR FEC/CRC from Boatbod - OP25
|
//DMR FEC/CRC from Boatbod - OP25
|
||||||
bool Hamming17123(uint8_t* d);
|
bool Hamming17123(uint8_t* d);
|
||||||
uint8_t crc8(uint8_t bits[], unsigned int len);
|
uint8_t crc8(uint8_t bits[], unsigned int len);
|
||||||
bool crc8_ok(uint8_t bits[], unsigned int len);
|
bool crc8_ok(uint8_t bits[], unsigned int len);
|
||||||
|
//modified CRC functions for SB/RC
|
||||||
|
uint8_t crc7(uint8_t bits[], unsigned int len);
|
||||||
|
uint8_t crc3(uint8_t bits[], unsigned int len);
|
||||||
|
uint8_t crc4(uint8_t bits[], unsigned int len);
|
||||||
|
|
||||||
//LFSR code courtesy of https://github.com/mattames/LFSR/
|
//LFSR and LFSRP code courtesy of https://github.com/mattames/LFSR/
|
||||||
void LFSR(dsd_state * state);
|
void LFSR(dsd_state * state);
|
||||||
|
void LFSRP(dsd_state * state);
|
||||||
|
|
||||||
void LFSRN (char * BufferIn, char * BufferOut, dsd_state * state);
|
void LFSRN (char * BufferIn, char * BufferOut, dsd_state * state);
|
||||||
|
void LFSR64(dsd_state * state);
|
||||||
|
|
||||||
void Hamming_7_4_init();
|
void Hamming_7_4_init();
|
||||||
void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits);
|
void Hamming_7_4_encode(unsigned char *origBits, unsigned char *encodedBits);
|
||||||
@@ -953,6 +1350,7 @@ bool QR_16_7_6_decode(unsigned char *rxBits);
|
|||||||
|
|
||||||
void InitAllFecFunction(void);
|
void InitAllFecFunction(void);
|
||||||
void resetState (dsd_state * state);
|
void resetState (dsd_state * state);
|
||||||
|
void reset_dibit_buffer(dsd_state * state);
|
||||||
void dstar_header_decode(dsd_state * state, int radioheaderbuffer[660]);
|
void dstar_header_decode(dsd_state * state, int radioheaderbuffer[660]);
|
||||||
|
|
||||||
//P25 PDU Handler
|
//P25 PDU Handler
|
||||||
@@ -965,6 +1363,10 @@ void process_FACCH_MAC_PDU (dsd_opts * opts, dsd_state * state, int payload[156]
|
|||||||
//P25 Channel to Frequency
|
//P25 Channel to Frequency
|
||||||
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel);
|
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel);
|
||||||
|
|
||||||
|
//P25 CRC Functions
|
||||||
|
int crc16_lb_bridge (int payload[190], int len);
|
||||||
|
int crc12_xb_bridge (int payload[190], int len);
|
||||||
|
|
||||||
//NXDN Channel to Frequency, Courtesy of IcomIcR20 on RR Forums
|
//NXDN Channel to Frequency, Courtesy of IcomIcR20 on RR Forums
|
||||||
long int nxdn_channel_to_frequency (dsd_opts * opts, dsd_state * state, uint16_t channel);
|
long int nxdn_channel_to_frequency (dsd_opts * opts, dsd_state * state, uint16_t channel);
|
||||||
|
|
||||||
@@ -993,11 +1395,48 @@ int UDPBind (char *hostname, int portno);
|
|||||||
//EDACS
|
//EDACS
|
||||||
void edacs(dsd_opts * opts, dsd_state * state);
|
void edacs(dsd_opts * opts, dsd_state * state);
|
||||||
unsigned long long int edacs_bch (unsigned long long int message);
|
unsigned long long int edacs_bch (unsigned long long int message);
|
||||||
|
void eot_cc(dsd_opts * opts, dsd_state * state); //end of TX return to CC
|
||||||
|
|
||||||
|
//Generic Tuning Functions
|
||||||
|
void return_to_cc (dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
|
//initialize static float filter memory
|
||||||
|
void init_rrc_filter_memory();
|
||||||
|
|
||||||
|
//misc audio filtering for analog
|
||||||
|
long int raw_rms(short *samples, int len, int step);
|
||||||
|
void init_audio_filters(dsd_state * state);
|
||||||
|
void lpf(dsd_state * state, short * input, int len);
|
||||||
|
void hpf(dsd_state * state, short * input, int len);
|
||||||
|
void pbf(dsd_state * state, short * input, int len);
|
||||||
|
void nf(dsd_state * state, short * input, int len);
|
||||||
|
void hpf_dL(dsd_state * state, short * input, int len);
|
||||||
|
void hpf_dR(dsd_state * state, short * input, int len);
|
||||||
|
//from: https://github.com/NedSimao/FilteringLibrary
|
||||||
|
void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS);
|
||||||
|
float LPFilter_Update(LPFilter *filter, float v_in);
|
||||||
|
void HPFilter_Init(HPFilter *filter, float cutoffFreqHz, float sampleTimeS);
|
||||||
|
float HPFilter_Update(HPFilter *filter, float v_in);
|
||||||
|
void PBFilter_Init(PBFilter *filter, float HPF_cutoffFreqHz, float LPF_cutoffFreqHz, float sampleTimeS);
|
||||||
|
float PBFilter_Update(PBFilter *filter, float v_in);
|
||||||
|
void NOTCHFilter_Init(NOTCHFilter *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS);
|
||||||
|
float NOTCHFilter_Update(NOTCHFilter *filter, float vin);
|
||||||
|
|
||||||
//csv imports
|
//csv imports
|
||||||
int csvGroupImport(dsd_opts * opts, dsd_state * state);
|
int csvGroupImport(dsd_opts * opts, dsd_state * state);
|
||||||
int csvLCNImport(dsd_opts * opts, dsd_state * state);
|
int csvLCNImport(dsd_opts * opts, dsd_state * state);
|
||||||
int csvChanImport(dsd_opts * opts, dsd_state * state);
|
int csvChanImport(dsd_opts * opts, dsd_state * state);
|
||||||
|
int csvKeyImportDec(dsd_opts * opts, dsd_state * state);
|
||||||
|
int csvKeyImportHex(dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
|
//UDP Socket Connect and UDP Socket Blaster (audio output)
|
||||||
|
int udp_socket_connect(dsd_opts * opts, dsd_state * state);
|
||||||
|
int udp_socket_connectA(dsd_opts * opts, dsd_state * state);
|
||||||
|
void udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
|
||||||
|
void udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
|
||||||
|
int m17_socket_receiver(dsd_opts * opts, void * data);
|
||||||
|
int udp_socket_connectM17(dsd_opts * opts, dsd_state * state);
|
||||||
|
int m17_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -1006,27 +1445,19 @@ extern "C" {
|
|||||||
#ifdef USE_RTLSDR
|
#ifdef USE_RTLSDR
|
||||||
void open_rtlsdr_stream(dsd_opts *opts);
|
void open_rtlsdr_stream(dsd_opts *opts);
|
||||||
void cleanup_rtlsdr_stream();
|
void cleanup_rtlsdr_stream();
|
||||||
void get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state);
|
int get_rtlsdr_sample(int16_t *sample, dsd_opts * opts, dsd_state * state);
|
||||||
void rtlsdr_sighandler();
|
void rtlsdr_sighandler();
|
||||||
|
void rtl_dev_tune(dsd_opts * opts, long int frequency);
|
||||||
|
long int rtl_return_rms();
|
||||||
|
void rtl_clean_queue();
|
||||||
#endif
|
#endif
|
||||||
//DMR TRELLIS
|
|
||||||
void CDMRTrellisTribitsToBits(const unsigned char* tribits, unsigned char* payload);
|
|
||||||
unsigned int CDMRTrellisCheckCode(const unsigned char* points, unsigned char* tribits);
|
|
||||||
bool CDMRTrellisFixCode(unsigned char* points, unsigned int failPos, unsigned char* payload);
|
|
||||||
void CDMRTrellisDeinterleave(const unsigned char* data, signed char* dibits);
|
|
||||||
void CDMRTrellisDibitsToPoints(const signed char* dibits, unsigned char* points);
|
|
||||||
void CDMRTrellisPointsToDibits(const unsigned char* points, signed char* dibits);
|
|
||||||
void CDMRTrellisBitsToTribits(const unsigned char* payload, unsigned char* tribits);
|
|
||||||
bool CDMRTrellisDecode(const unsigned char* data, unsigned char* payload);
|
|
||||||
|
|
||||||
//Phase 2 Helper Functions
|
|
||||||
|
//Phase 2 RS/FEC Functions
|
||||||
int ez_rs28_ess (int payload[96], int parity[168]); //ezpwd bridge for FME
|
int ez_rs28_ess (int payload[96], int parity[168]); //ezpwd bridge for FME
|
||||||
int ez_rs28_facch (int payload[156], int parity[114]); //ezpwd bridge for FME
|
int ez_rs28_facch (int payload[156], int parity[114]); //ezpwd bridge for FME
|
||||||
int ez_rs28_sacch (int payload[180], int parity[132]); //ezpwd bridge for FME
|
int ez_rs28_sacch (int payload[180], int parity[132]); //ezpwd bridge for FME
|
||||||
unsigned long long int isch_lookup (unsigned long long int isch); //isch map lookup
|
int isch_lookup (uint64_t isch); //isch map lookup
|
||||||
int bd_bridge (int payload[196], uint8_t decoded[12]); //bridge to Michael Ossmann Block De-interleaver and 1/2 rate trellis decoder
|
|
||||||
int crc16_lb_bridge (int payload[190], int len);
|
|
||||||
int crc12_xb_bridge (int payload[190], int len);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ for (loop=0; loop < size; loop++) {
|
|||||||
|
|
||||||
m_crc = (m_crc >> 8) ^ ccittTab[tmp];
|
m_crc = (m_crc >> 8) ^ ccittTab[tmp];
|
||||||
|
|
||||||
}; // end for
|
} // end for
|
||||||
|
|
||||||
// calculate and save crc-value in fields 54 and 55 of dvframe
|
// calculate and save crc-value in fields 54 and 55 of dvframe
|
||||||
m_crc = ~m_crc;
|
m_crc = ~m_crc;
|
||||||
|
|||||||
1448
include/fec.h
10
install.sh
@@ -1,12 +1,8 @@
|
|||||||
sudo cp tone8.wav /usr/share/
|
#!/bin/bash
|
||||||
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
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
make -j `nproc`
|
make -j $(nproc)
|
||||||
sudo make install
|
sudo make install
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
46
package.nix
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{ lib
|
||||||
|
, stdenv
|
||||||
|
, cmake
|
||||||
|
, mbelib
|
||||||
|
, libsndfile
|
||||||
|
, ncurses
|
||||||
|
, pulseaudio
|
||||||
|
, rtl-sdr
|
||||||
|
, itpp
|
||||||
|
, codec2
|
||||||
|
, portaudioSupport ? true
|
||||||
|
, portaudio ? null
|
||||||
|
}:
|
||||||
|
|
||||||
|
assert portaudioSupport -> portaudio != null;
|
||||||
|
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
name = "dsd-fme";
|
||||||
|
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
nativeBuildInputs = [ cmake ];
|
||||||
|
buildInputs = [
|
||||||
|
mbelib
|
||||||
|
libsndfile
|
||||||
|
itpp
|
||||||
|
rtl-sdr
|
||||||
|
ncurses.dev
|
||||||
|
pulseaudio.dev
|
||||||
|
codec2
|
||||||
|
] ++ lib.optionals portaudioSupport [ portaudio ];
|
||||||
|
|
||||||
|
doCheck = true;
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "Digital Speech Decoder - Florida Man Edition";
|
||||||
|
longDescription = ''
|
||||||
|
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.
|
||||||
|
'';
|
||||||
|
homepage = "https://github.com/szechyjs/dsd";
|
||||||
|
license = licenses.mit;
|
||||||
|
platforms = platforms.unix;
|
||||||
|
};
|
||||||
|
}
|
||||||
51
patch/DMR_MONO_MODE_20240201.patch
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
diff --git a/src/dmr_pi.c b/src/dmr_pi.c
|
||||||
|
index 55dfb83..33fb3ba 100644
|
||||||
|
--- a/src/dmr_pi.c
|
||||||
|
+++ b/src/dmr_pi.c
|
||||||
|
@@ -14,6 +14,9 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
|
||||||
|
{
|
||||||
|
UNUSED2(opts, CRCCorrect);
|
||||||
|
|
||||||
|
+ //force slot to 0 if using dmr mono handling
|
||||||
|
+ if (opts->dmr_mono == 1) state->currentslot = 0;
|
||||||
|
+
|
||||||
|
if((IrrecoverableErrors == 0))
|
||||||
|
{
|
||||||
|
|
||||||
|
diff --git a/src/dsd_frame.c b/src/dsd_frame.c
|
||||||
|
index 3b2a3e1..ba32618 100644
|
||||||
|
--- a/src/dsd_frame.c
|
||||||
|
+++ b/src/dsd_frame.c
|
||||||
|
@@ -160,7 +160,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||||
|
sprintf (state->slot2light, " slot2 ");
|
||||||
|
//we can safely open MBE on any MS or mono handling
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
|
- if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state);
|
||||||
|
+ // if (opts->p25_trunk == 0) //line disabled to allow mono mode while trunking
|
||||||
|
+ dmrMSBootstrap (opts, state);
|
||||||
|
}
|
||||||
|
if (opts->dmr_mono == 1 && state->synctype == 32)
|
||||||
|
{
|
||||||
|
diff --git a/src/dsd_main.c b/src/dsd_main.c
|
||||||
|
index 7be5912..8db6dc3 100644
|
||||||
|
--- a/src/dsd_main.c
|
||||||
|
+++ b/src/dsd_main.c
|
||||||
|
@@ -2302,13 +2302,13 @@ main (int argc, char **argv)
|
||||||
|
state.rf_mod = 0; //
|
||||||
|
opts.pulse_digi_rate_out = 8000;
|
||||||
|
opts.pulse_digi_out_channels = 2;
|
||||||
|
- opts.dmr_mono = 0;
|
||||||
|
- opts.dmr_stereo = 1;
|
||||||
|
+ opts.dmr_mono = 1;
|
||||||
|
+ opts.dmr_stereo = 0; //not sure if this will break anything
|
||||||
|
state.dmr_stereo = 0; //0
|
||||||
|
// opts.setmod_bw = 7000;
|
||||||
|
- sprintf (opts.output_name, "DMR Stereo");
|
||||||
|
- fprintf (stderr,"-fr / DMR Mono switch has been deprecated.\n");
|
||||||
|
- fprintf (stderr,"Decoding DMR Stereo BS/MS Simplex\n");
|
||||||
|
+ sprintf (opts.output_name, "DMR Mono");
|
||||||
|
+ // fprintf (stderr,"-fr / DMR Mono switch enabled. \n NOTE: Some ENC options in Slot 2 may not work properly.\n");
|
||||||
|
+ fprintf (stderr,"\n Decoding DMR Stereo BS/MS Simplex in Mono Single Slot Only Configuration.\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (optarg[0] == 'm')
|
||||||
152
patch/MTL_MONO_MODE_20240328.patch
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
diff --git a/src/dmr_pi.c b/src/dmr_pi.c
|
||||||
|
index 7542c61..d1c8e39 100644
|
||||||
|
--- a/src/dmr_pi.c
|
||||||
|
+++ b/src/dmr_pi.c
|
||||||
|
@@ -14,6 +14,9 @@ void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRC
|
||||||
|
{
|
||||||
|
UNUSED2(opts, CRCCorrect);
|
||||||
|
|
||||||
|
+ //force slot to 0 if using dmr mono handling
|
||||||
|
+ if (opts->dmr_mono == 1) state->currentslot = 0;
|
||||||
|
+
|
||||||
|
if((IrrecoverableErrors == 0))
|
||||||
|
{
|
||||||
|
|
||||||
|
diff --git a/src/dsd_audio2.c b/src/dsd_audio2.c
|
||||||
|
index 756e468..13316df 100644
|
||||||
|
--- a/src/dsd_audio2.c
|
||||||
|
+++ b/src/dsd_audio2.c
|
||||||
|
@@ -988,6 +988,9 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
|
||||||
|
memset (stereo_samp2, 0, sizeof(stereo_samp2));
|
||||||
|
memset (stereo_samp3, 0, sizeof(stereo_samp3));
|
||||||
|
|
||||||
|
+ if (opts->dmr_mono == 1)
|
||||||
|
+ memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4));
|
||||||
|
+
|
||||||
|
//dmr enc checkdown for whether or not to fill the stereo sample or not for playback or writing
|
||||||
|
encL = encR = 0;
|
||||||
|
encL = (state->dmr_so >> 6) & 0x1;
|
||||||
|
@@ -1070,8 +1073,16 @@ void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state)
|
||||||
|
}
|
||||||
|
|
||||||
|
//flag either left or right as 'enc' to mute if B
|
||||||
|
- if (strcmp(modeL, "B") == 0) encL = 1;
|
||||||
|
- if (strcmp(modeR, "B") == 0) encR = 1;
|
||||||
|
+ if (strcmp(modeL, "B") == 0)
|
||||||
|
+ {
|
||||||
|
+ encL = 1;
|
||||||
|
+ encR = 1;
|
||||||
|
+ }
|
||||||
|
+ if (strcmp(modeR, "B") == 0)
|
||||||
|
+ {
|
||||||
|
+ encR = 1;
|
||||||
|
+ encL = 1;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
//check to see if we need to enable slot and toggle slot preference here
|
||||||
|
//this method will always favor slot 2 (this is a patch anyways, so....meh)
|
||||||
|
diff --git a/src/dsd_frame.c b/src/dsd_frame.c
|
||||||
|
index 577169c..275abb6 100644
|
||||||
|
--- a/src/dsd_frame.c
|
||||||
|
+++ b/src/dsd_frame.c
|
||||||
|
@@ -160,7 +160,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||||
|
sprintf (state->slot2light, " slot2 ");
|
||||||
|
//we can safely open MBE on any MS or mono handling
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
|
- if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state);
|
||||||
|
+ // if (opts->p25_trunk == 0) //line disabled to allow mono mode while trunking
|
||||||
|
+ dmrMSBootstrap (opts, state);
|
||||||
|
}
|
||||||
|
if (opts->dmr_mono == 1 && state->synctype == 32)
|
||||||
|
{
|
||||||
|
diff --git a/src/dsd_main.c b/src/dsd_main.c
|
||||||
|
index 3ca7788..22462a0 100644
|
||||||
|
--- a/src/dsd_main.c
|
||||||
|
+++ b/src/dsd_main.c
|
||||||
|
@@ -1645,9 +1645,9 @@ main (int argc, char **argv)
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AERO_BUILD
|
||||||
|
- fprintf (stderr, "Build Version: AW (20231015) \n");
|
||||||
|
+ fprintf (stderr, "Build Version: AW MTL (20240201) \n");
|
||||||
|
#else
|
||||||
|
- fprintf (stderr, "Build Version: AW %s \n", GIT_TAG);
|
||||||
|
+ fprintf (stderr, "Build Version: AW MTL %s \n", GIT_TAG);
|
||||||
|
#endif
|
||||||
|
fprintf (stderr,"MBElib Version: %s\n", versionstr);
|
||||||
|
|
||||||
|
@@ -2474,14 +2474,18 @@ main (int argc, char **argv)
|
||||||
|
opts.mod_qpsk = 0;
|
||||||
|
opts.mod_gfsk = 0;
|
||||||
|
state.rf_mod = 0;
|
||||||
|
- opts.dmr_stereo = 1;
|
||||||
|
- opts.dmr_mono = 0;
|
||||||
|
+ // opts.dmr_stereo = 1;
|
||||||
|
+ // opts.dmr_mono = 0;
|
||||||
|
// opts.setmod_bw = 7000;
|
||||||
|
opts.pulse_digi_rate_out = 8000;
|
||||||
|
opts.pulse_digi_out_channels = 2;
|
||||||
|
- sprintf (opts.output_name, "DMR");
|
||||||
|
-
|
||||||
|
- fprintf (stderr,"Decoding DMR Stereo BS/MS Simplex\n");
|
||||||
|
+ opts.dmr_mono = 1;
|
||||||
|
+ opts.dmr_stereo = 0; //not sure if this will break anything
|
||||||
|
+ state.dmr_stereo = 0; //0
|
||||||
|
+ // opts.setmod_bw = 7000;
|
||||||
|
+ sprintf (opts.output_name, "DMR Mono");
|
||||||
|
+ // fprintf (stderr,"-fr / DMR Mono switch enabled. \n NOTE: Some ENC options in Slot 2 may not work properly.\n");
|
||||||
|
+ fprintf (stderr,"\n Decoding DMR Stereo BS/MS Simplex in Mono Single Slot Only Configuration.\n");
|
||||||
|
}
|
||||||
|
//change ft to only do P25 and DMR (TDMA trunking modes)
|
||||||
|
else if (optarg[0] == 't')
|
||||||
|
@@ -2558,13 +2562,13 @@ main (int argc, char **argv)
|
||||||
|
state.rf_mod = 0; //
|
||||||
|
opts.pulse_digi_rate_out = 8000;
|
||||||
|
opts.pulse_digi_out_channels = 2;
|
||||||
|
- opts.dmr_mono = 0;
|
||||||
|
- opts.dmr_stereo = 1;
|
||||||
|
+ opts.dmr_mono = 1;
|
||||||
|
+ opts.dmr_stereo = 0; //not sure if this will break anything
|
||||||
|
state.dmr_stereo = 0; //0
|
||||||
|
// opts.setmod_bw = 7000;
|
||||||
|
- sprintf (opts.output_name, "DMR Stereo");
|
||||||
|
- fprintf (stderr,"-fr / DMR Mono switch has been deprecated.\n");
|
||||||
|
- fprintf (stderr,"Decoding DMR Stereo BS/MS Simplex\n");
|
||||||
|
+ sprintf (opts.output_name, "DMR Mono");
|
||||||
|
+ // fprintf (stderr,"-fr / DMR Mono switch enabled. \n NOTE: Some ENC options in Slot 2 may not work properly.\n");
|
||||||
|
+ fprintf (stderr,"\n Decoding DMR Stereo BS/MS Simplex in Mono Single Slot Only Configuration.\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (optarg[0] == 'm')
|
||||||
|
diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c
|
||||||
|
index 73965dc..e5b38aa 100644
|
||||||
|
--- a/src/dsd_ncurses.c
|
||||||
|
+++ b/src/dsd_ncurses.c
|
||||||
|
@@ -2297,8 +2297,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
if (opts->ncurses_compact == 1)
|
||||||
|
{
|
||||||
|
printw ("------------------------------------------------------------------------------\n");
|
||||||
|
- printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20231015)");
|
||||||
|
- printw ("------------------------------------------------------------------------------\n");
|
||||||
|
+ printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW MTL (20240328)");
|
||||||
|
+ printw ("------------------------------------------------------------------------------\n");
|
||||||
|
}
|
||||||
|
#elif LIMAZULUTWEAKS
|
||||||
|
if (opts->ncurses_compact == 1)
|
||||||
|
@@ -2336,12 +2336,12 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
#endif
|
||||||
|
#ifdef AERO_BUILD
|
||||||
|
if (i == 5) printw (" %s ", "Aero Build");
|
||||||
|
- if (i == 6) printw (" AW (20231015) \n");
|
||||||
|
+ if (i == 6) printw (" AW MTL (20240328) \n");
|
||||||
|
#elif ZDEV_BUILD
|
||||||
|
- if (i == 5) printw (" %s ", "AW ");
|
||||||
|
+ if (i == 5) printw (" %s ", "AW MTL");
|
||||||
|
if (i == 6) printw (" %s \n", GIT_TAG);
|
||||||
|
- #else
|
||||||
|
- if (i == 5) printw (" %s ", "AW ");
|
||||||
|
+ #else
|
||||||
|
+ if (i == 5) printw (" %s ", "AW MTL");
|
||||||
|
if (i == 6) printw (" %s \n", GIT_TAG);
|
||||||
|
#endif
|
||||||
|
else printw ("\n");
|
||||||
277
patch/cretu_no_dmr_idle.patch
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
diff --git a/include/dsd.h b/include/dsd.h
|
||||||
|
index 9ebf3f1..c5ac68a 100644
|
||||||
|
--- a/include/dsd.h
|
||||||
|
+++ b/include/dsd.h
|
||||||
|
@@ -18,16 +18,16 @@
|
||||||
|
*/
|
||||||
|
|
||||||
|
//defined by CMakeLists.txt -- Disable by using cmake -DCOLORS=OFF ..
|
||||||
|
-#ifdef PRETTY_COLORS
|
||||||
|
-#define KNRM "\x1B[0m"
|
||||||
|
-#define KRED "\x1B[31m"
|
||||||
|
-#define KGRN "\x1B[32m"
|
||||||
|
-#define KYEL "\x1B[33m"
|
||||||
|
-#define KBLU "\x1B[34m"
|
||||||
|
-#define KMAG "\x1B[35m"
|
||||||
|
-#define KCYN "\x1B[36m"
|
||||||
|
-#define KWHT "\x1B[37m"
|
||||||
|
-#else
|
||||||
|
+// #ifdef PRETTY_COLORS
|
||||||
|
+// #define KNRM "\x1B[0m"
|
||||||
|
+// #define KRED "\x1B[31m"
|
||||||
|
+// #define KGRN "\x1B[32m"
|
||||||
|
+// #define KYEL "\x1B[33m"
|
||||||
|
+// #define KBLU "\x1B[34m"
|
||||||
|
+// #define KMAG "\x1B[35m"
|
||||||
|
+// #define KCYN "\x1B[36m"
|
||||||
|
+// #define KWHT "\x1B[37m"
|
||||||
|
+// #else
|
||||||
|
#define KNRM ""
|
||||||
|
#define KRED ""
|
||||||
|
#define KGRN ""
|
||||||
|
@@ -36,7 +36,7 @@
|
||||||
|
#define KMAG ""
|
||||||
|
#define KCYN ""
|
||||||
|
#define KWHT ""
|
||||||
|
-#endif
|
||||||
|
+// #endif
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
diff --git a/src/dmr_bs.c b/src/dmr_bs.c
|
||||||
|
index bdab262..17a9e70 100644
|
||||||
|
--- a/src/dmr_bs.c
|
||||||
|
+++ b/src/dmr_bs.c
|
||||||
|
@@ -266,14 +266,14 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||||
|
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 )
|
||||||
|
{
|
||||||
|
|
||||||
|
- fprintf (stderr,"%s ", timestr);
|
||||||
|
+ // fprintf (stderr,"%s ", timestr);
|
||||||
|
if (internalslot == 0)
|
||||||
|
{
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
- fprintf (stderr,"Sync: +DMR ");
|
||||||
|
+ // fprintf (stderr,"Sync: +DMR ");
|
||||||
|
}
|
||||||
|
- else fprintf (stderr,"Sync: -DMR ");
|
||||||
|
+ else {}//fprintf (stderr,"Sync: -DMR ");
|
||||||
|
|
||||||
|
vc1 = 7; //set to 7 so we can see that we should not be on a VC unless a framesync comes in for it first
|
||||||
|
|
||||||
|
@@ -284,9 +284,9 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
{
|
||||||
|
- fprintf (stderr,"Sync: +DMR ");
|
||||||
|
+ // fprintf (stderr,"Sync: +DMR ");
|
||||||
|
}
|
||||||
|
- else fprintf (stderr,"Sync: -DMR ");
|
||||||
|
+ else {}//fprintf (stderr,"Sync: -DMR ");
|
||||||
|
|
||||||
|
vc2 = 7; //set to 7 so we can see that we should not be on a VC unless a framesync comes in for it first
|
||||||
|
|
||||||
|
diff --git a/src/dmr_data.c b/src/dmr_data.c
|
||||||
|
index 87f4dfd..57e5647 100644
|
||||||
|
--- a/src/dmr_data.c
|
||||||
|
+++ b/src/dmr_data.c
|
||||||
|
@@ -232,10 +232,10 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
||||||
|
sprintf(state->slot2light, "[sLoT2]");
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (state->dmr_ms_mode == 0)
|
||||||
|
- {
|
||||||
|
- fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
|
||||||
|
- }
|
||||||
|
+ // 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++)
|
||||||
|
@@ -292,6 +292,13 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
||||||
|
info[(2*i) + 98] = (1 & (dibit >> 1)); // bit 1
|
||||||
|
info[(2*i) + 99] = (1 & dibit); // bit 0
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ // if (state->dmr_ms_mode == 0 && burst != 0x09)
|
||||||
|
+ if (burst != 0x09)
|
||||||
|
+ {
|
||||||
|
+ printFrameSync (opts, state, "+DMR ", 90, NULL); //don't think the values matter
|
||||||
|
+ fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
//don't run on MS Reverse Channel Data
|
||||||
|
if (state->dmr_ms_rc == 0) dmr_data_burst_handler(opts, state, (uint8_t *)info, burst);
|
||||||
|
@@ -303,15 +310,16 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
||||||
|
}
|
||||||
|
|
||||||
|
//ending line break
|
||||||
|
- fprintf(stderr, "\n");
|
||||||
|
+ if (burst != 0x09)
|
||||||
|
+ 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");
|
||||||
|
+ // 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
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/src/dmr_dburst.c b/src/dmr_dburst.c
|
||||||
|
index fe81158..078524d 100644
|
||||||
|
--- a/src/dmr_dburst.c
|
||||||
|
+++ b/src/dmr_dburst.c
|
||||||
|
@@ -205,7 +205,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
||||||
|
//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 (databurst != 0xEB && databurst != 0x09)
|
||||||
|
{
|
||||||
|
if (state->dmr_ms_mode == 0)
|
||||||
|
{
|
||||||
|
diff --git a/src/dmr_flco.c b/src/dmr_flco.c
|
||||||
|
index 839b085..e43b568 100644
|
||||||
|
--- a/src/dmr_flco.c
|
||||||
|
+++ b/src/dmr_flco.c
|
||||||
|
@@ -790,21 +790,23 @@ uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25])
|
||||||
|
crc = crc8_ok(slco_bits, 36);
|
||||||
|
|
||||||
|
//only run SLCO on good everything
|
||||||
|
- if (h1 && h2 && h3 && crc) dmr_slco (opts, state, slco_bits);
|
||||||
|
- else
|
||||||
|
- {
|
||||||
|
- //this line break issue is wracking on my OCD for clean line breaks
|
||||||
|
- if (opts->payload == 1 && state->dmrburstL == 16 && state->currentslot == 0) ; //no line break if current slot is voice with payload enabled
|
||||||
|
- else if (opts->payload == 1 && state->dmrburstR == 16 && state->currentslot == 1) ; //no line break if current slot is voice with payload enabled
|
||||||
|
- else fprintf (stderr, "\n");
|
||||||
|
- fprintf (stderr, "%s", KRED);
|
||||||
|
- fprintf (stderr, " SLCO CRC ERR");
|
||||||
|
- fprintf (stderr, "%s", KNRM);
|
||||||
|
- if (opts->payload == 1 && state->dmrburstL == 16 && state->currentslot == 0) //if current slot is voice with payload enabled
|
||||||
|
- fprintf (stderr, "\n");
|
||||||
|
- else if (opts->payload == 1 && state->dmrburstR == 16 && state->currentslot == 1) //if current slot is voice with payload enabled
|
||||||
|
- fprintf (stderr, "\n");
|
||||||
|
- }
|
||||||
|
+ if (h1 && h2 && h3 && crc) {} //dmr_slco (opts, state, slco_bits);
|
||||||
|
+ // else
|
||||||
|
+ // {
|
||||||
|
+ // //this line break issue is wracking on my OCD for clean line breaks
|
||||||
|
+ // if (opts->payload == 1 && state->dmrburstL == 16 && state->currentslot == 0) {} //no line break if current slot is voice with payload enabled
|
||||||
|
+ // else if (opts->payload == 1 && state->dmrburstR == 16 && state->currentslot == 1) {} //no line break if current slot is voice with payload enabled
|
||||||
|
+ // else {} //fprintf (stderr, "\n");
|
||||||
|
+ // // fprintf (stderr, "%s", KRED);
|
||||||
|
+ // // fprintf (stderr, " SLCO CRC ERR");
|
||||||
|
+ // // fprintf (stderr, "%s", KNRM);
|
||||||
|
+ // if (opts->payload == 1 && state->dmrburstL == 16 && state->currentslot == 0) //if current slot is voice with payload enabled
|
||||||
|
+ // fprintf (stderr, "\n");
|
||||||
|
+ // else if (opts->payload == 1 && state->dmrburstR == 16 && state->currentslot == 1) //if current slot is voice with payload enabled
|
||||||
|
+ // fprintf (stderr, "\n");
|
||||||
|
+ // }
|
||||||
|
+
|
||||||
|
+ UNUSED(opts);
|
||||||
|
|
||||||
|
}
|
||||||
|
return (err); //return err value based on success or failure, even if we aren't checking it
|
||||||
|
@@ -935,8 +937,11 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
||||||
|
uint8_t xpt_hash = (uint8_t)ConvertBitIntoBytes(&slco_bits[20], 8); //priority TG hash
|
||||||
|
|
||||||
|
//initial line break
|
||||||
|
- fprintf (stderr, "\n");
|
||||||
|
- fprintf (stderr, "%s", KYEL);
|
||||||
|
+ if (slco != 0)
|
||||||
|
+ {
|
||||||
|
+ fprintf (stderr, "\n");
|
||||||
|
+ fprintf (stderr, "%s", KYEL);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if (slco == 0x2) //C_SYS_Parms
|
||||||
|
{
|
||||||
|
@@ -973,7 +978,8 @@ void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
||||||
|
else sprintf (state->dmr_site_parms, "TIII %s:%d-%d;%04X; ", model_str, net, site, syscode);
|
||||||
|
}
|
||||||
|
else if (slco == 0x0) //null
|
||||||
|
- fprintf (stderr, " SLCO NULL ");
|
||||||
|
+ // fprintf (stderr, " SLCO NULL ");
|
||||||
|
+ {}
|
||||||
|
else if (slco == 0x1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Activity Update"); //102 361-2 7.1.3.2
|
||||||
|
diff --git a/src/dsd_frame_sync.c b/src/dsd_frame_sync.c
|
||||||
|
index 062bb20..f47a447 100644
|
||||||
|
--- a/src/dsd_frame_sync.c
|
||||||
|
+++ b/src/dsd_frame_sync.c
|
||||||
|
@@ -940,7 +940,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||||
|
sprintf(state->ftype, "DMR ");
|
||||||
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
- printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation);
|
||||||
|
+ // printFrameSync (opts, state, "+DMR ", synctest_pos + 1, modulation);
|
||||||
|
}
|
||||||
|
state->lastsynctype = 10;
|
||||||
|
state->last_cc_sync_time = time(NULL);
|
||||||
|
@@ -1068,7 +1068,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||||
|
sprintf(state->ftype, "DMR ");
|
||||||
|
if (opts->errorbars == 1) //&& opts->dmr_stereo == 0
|
||||||
|
{
|
||||||
|
- printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation);
|
||||||
|
+ // printFrameSync (opts, state, "-DMR ", synctest_pos + 1, modulation);
|
||||||
|
}
|
||||||
|
state->lastsynctype = 13;
|
||||||
|
state->last_cc_sync_time = time(NULL);
|
||||||
|
@@ -1443,7 +1443,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
if ((opts->errorbars == 1) && (opts->verbose > 1) && (state->carrier == 1))
|
||||||
|
{
|
||||||
|
- fprintf (stderr,"Sync: no sync\n");
|
||||||
|
+ // fprintf (stderr,"Sync: no sync\n");
|
||||||
|
// fprintf (stderr,"Press CTRL + C to close.\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
diff --git a/src/dsd_main.c b/src/dsd_main.c
|
||||||
|
index 98f2ead..b09cb81 100644
|
||||||
|
--- a/src/dsd_main.c
|
||||||
|
+++ b/src/dsd_main.c
|
||||||
|
@@ -1687,7 +1687,7 @@ main (int argc, char **argv)
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AERO_BUILD
|
||||||
|
- fprintf (stderr, "Build Version: AW (20231015) \n");
|
||||||
|
+ fprintf (stderr, "Build Version: AW (20240523) \n");
|
||||||
|
#else
|
||||||
|
fprintf (stderr, "Build Version: AW %s \n", GIT_TAG);
|
||||||
|
#endif
|
||||||
|
diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c
|
||||||
|
index a5442af..514b9ae 100644
|
||||||
|
--- a/src/dsd_ncurses.c
|
||||||
|
+++ b/src/dsd_ncurses.c
|
||||||
|
@@ -2423,7 +2423,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
if (opts->ncurses_compact == 1)
|
||||||
|
{
|
||||||
|
printw ("------------------------------------------------------------------------------\n");
|
||||||
|
- printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20231015)");
|
||||||
|
+ printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20240523)");
|
||||||
|
printw ("------------------------------------------------------------------------------\n");
|
||||||
|
}
|
||||||
|
#elif LIMAZULUTWEAKS
|
||||||
|
@@ -2462,8 +2462,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
if (i == 4 && opts->frame_m17 == 1) printw (" CODEC2");
|
||||||
|
#endif
|
||||||
|
#ifdef AERO_BUILD
|
||||||
|
- if (i == 5) printw (" %s ", "Aero Build");
|
||||||
|
- if (i == 6) printw (" AW (20231015) \n");
|
||||||
|
+ if (i == 5) printw (" %s ", "Cretu Build");
|
||||||
|
+ if (i == 6) printw (" AW (20240523) \n");
|
||||||
|
#elif ZDEV_BUILD
|
||||||
|
if (i == 5) printw (" %s ", "AW ");
|
||||||
|
if (i == 6) printw (" %s \n", GIT_TAG);
|
||||||
120
patch/dmr_data_voice_call_tg_src_persistency_ncurses2.patch
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
diff --git a/src/dmr_block.c b/src/dmr_block.c
|
||||||
|
index 893b0e8..e7f9de7 100644
|
||||||
|
--- a/src/dmr_block.c
|
||||||
|
+++ b/src/dmr_block.c
|
||||||
|
@@ -783,6 +783,6 @@ void dmr_reset_blocks (dsd_opts * opts, dsd_state * state)
|
||||||
|
//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", "");
|
||||||
|
+ // sprintf (state->dmr_lrrp_gps[0], "%s", ""); //disabled for dmr data call persistence #174
|
||||||
|
+ // sprintf (state->dmr_lrrp_gps[1], "%s", ""); //disabled for dmr data call persistence #174
|
||||||
|
}
|
||||||
|
\ No newline at end of file
|
||||||
|
diff --git a/src/dmr_data.c b/src/dmr_data.c
|
||||||
|
index 3541152..3cae906 100644
|
||||||
|
--- a/src/dmr_data.c
|
||||||
|
+++ b/src/dmr_data.c
|
||||||
|
@@ -267,7 +267,20 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
||||||
|
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;
|
||||||
|
+ if (SlotTypeOk == 1)
|
||||||
|
+ {
|
||||||
|
+ if (state->dmr_color_code != state->color_code)
|
||||||
|
+ {
|
||||||
|
+ //Zero out values on color code change #174
|
||||||
|
+ state->lasttg = 0;
|
||||||
|
+ state->lasttgR =0;
|
||||||
|
+ state->lastsrc = 0;
|
||||||
|
+ state->lastsrcR = 0;
|
||||||
|
+ sprintf (state->dmr_lrrp_gps[0], "%s", "");
|
||||||
|
+ sprintf (state->dmr_lrrp_gps[1], "%s", "");
|
||||||
|
+ 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]);
|
||||||
|
diff --git a/src/dsd_main.c b/src/dsd_main.c
|
||||||
|
index 5531ef3..5598793 100644
|
||||||
|
--- a/src/dsd_main.c
|
||||||
|
+++ b/src/dsd_main.c
|
||||||
|
@@ -257,10 +257,10 @@ if(opts->frame_m17 == 1) //&& opts->audio_in_type == 5
|
||||||
|
//zero out right away if not trunking
|
||||||
|
if (opts->p25_trunk == 0)
|
||||||
|
{
|
||||||
|
- state->lasttg = 0;
|
||||||
|
- state->lastsrc = 0;
|
||||||
|
- state->lasttgR = 0;
|
||||||
|
- state->lastsrcR = 0;
|
||||||
|
+ // state->lasttg = 0; //disabled for dmr voice call persistence #174
|
||||||
|
+ // state->lastsrc = 0; //disabled for dmr voice call persistence #174
|
||||||
|
+ // state->lasttgR = 0; //disabled for dmr voice call persistence #174
|
||||||
|
+ // state->lastsrcR = 0; //disabled for dmr voice call persistence #174
|
||||||
|
|
||||||
|
//zero out vc frequencies?
|
||||||
|
state->p25_vc_freq[0] = 0;
|
||||||
|
@@ -290,10 +290,10 @@ if(opts->frame_m17 == 1) //&& opts->audio_in_type == 5
|
||||||
|
//mainly bugfix for ncurses and per call wavs (edacs) and also signal fade, etc
|
||||||
|
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && time(NULL) - state->last_cc_sync_time > opts->trunk_hangtime)
|
||||||
|
{
|
||||||
|
- state->lasttg = 0;
|
||||||
|
- state->lastsrc = 0;
|
||||||
|
- state->lasttgR = 0;
|
||||||
|
- state->lastsrcR = 0;
|
||||||
|
+ // state->lasttg = 0; //disabled for dmr voice call persistence #174
|
||||||
|
+ // state->lastsrc = 0; //disabled for dmr voice call persistence #174
|
||||||
|
+ // state->lasttgR = 0; //disabled for dmr voice call persistence #174
|
||||||
|
+ // state->lastsrcR = 0; //disabled for dmr voice call persistence #174
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -431,7 +431,7 @@ if(opts->frame_m17 == 1) //&& opts->audio_in_type == 5
|
||||||
|
memset(state->dmr_alias_len, 0, sizeof(state->dmr_alias_len));
|
||||||
|
memset(state->dmr_alias_block_segment, 0, sizeof(state->dmr_alias_block_segment));
|
||||||
|
memset(state->dmr_embedded_gps, 0, sizeof(state->dmr_embedded_gps));
|
||||||
|
- memset(state->dmr_lrrp_gps, 0, sizeof(state->dmr_lrrp_gps));
|
||||||
|
+ // memset(state->dmr_lrrp_gps, 0, sizeof(state->dmr_lrrp_gps));
|
||||||
|
|
||||||
|
// memset(state->active_channel, 0, sizeof(state->active_channel));
|
||||||
|
|
||||||
|
diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c
|
||||||
|
index 4646e77..e9ecab1 100644
|
||||||
|
--- a/src/dsd_ncurses.c
|
||||||
|
+++ b/src/dsd_ncurses.c
|
||||||
|
@@ -3104,7 +3104,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
}
|
||||||
|
|
||||||
|
//LRRP
|
||||||
|
- if(state->dmrburstL != 16) //only during data and no trunking
|
||||||
|
+ // if(state->dmrburstL != 16) //only during data and no trunking //disabled for dmr data call persistence #174
|
||||||
|
{
|
||||||
|
attron(COLOR_PAIR(4));
|
||||||
|
printw ("%s", state->dmr_lrrp_gps[0]);
|
||||||
|
@@ -3298,7 +3298,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
}
|
||||||
|
|
||||||
|
//LRRP
|
||||||
|
- if(state->dmrburstR != 16) //only during data and no trunking
|
||||||
|
+ // if(state->dmrburstR != 16) //only during data and no trunking //disabled for dmr data call persistence #174
|
||||||
|
{
|
||||||
|
attron(COLOR_PAIR(4));
|
||||||
|
printw ("%s", state->dmr_lrrp_gps[1]);
|
||||||
|
@@ -3978,10 +3978,10 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
dmr_reset_blocks (opts, state);
|
||||||
|
|
||||||
|
//zero out additional items
|
||||||
|
- state->lasttg = 0;
|
||||||
|
- state->lasttgR = 0;
|
||||||
|
- state->lastsrc = 0;
|
||||||
|
- state->lastsrcR = 0;
|
||||||
|
+ // state->lasttg = 0; //disabled for dmr voice call persistence #174
|
||||||
|
+ // state->lasttgR = 0; //disabled for dmr voice call persistence #174
|
||||||
|
+ // state->lastsrc = 0; //disabled for dmr voice call persistence #174
|
||||||
|
+ // state->lastsrcR = 0; //disabled for dmr voice call persistence #174
|
||||||
|
state->payload_algid = 0;
|
||||||
|
state->payload_algidR = 0;
|
||||||
|
state->payload_keyid = 0;
|
||||||
330
patch/extra/ghost_box_wip3_awd.patch
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
diff --git a/include/dsd.h b/include/dsd.h
|
||||||
|
index 4a75702..3cf085e 100644
|
||||||
|
--- a/include/dsd.h
|
||||||
|
+++ b/include/dsd.h
|
||||||
|
@@ -317,6 +317,7 @@ typedef struct
|
||||||
|
long int rtl_rms;
|
||||||
|
int monitor_input_audio;
|
||||||
|
int analog_only;
|
||||||
|
+ int ghosts_only;
|
||||||
|
int pulse_raw_rate_in;
|
||||||
|
int pulse_raw_rate_out;
|
||||||
|
int pulse_digi_rate_in;
|
||||||
|
@@ -1342,6 +1343,8 @@ void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency);
|
||||||
|
long int GetCurrentFreq(int sockfd);
|
||||||
|
bool SetFreq(int sockfd, long int freq);
|
||||||
|
bool SetModulation(int sockfd, int bandwidth);
|
||||||
|
+bool SetModulationWFM(int sockfd, int bandwidth);
|
||||||
|
+void death (dsd_opts * opts, dsd_state * state);
|
||||||
|
//commands below unique to GQRX only, not usable on SDR++
|
||||||
|
bool GetSignalLevel(int sockfd, double *dBFS);
|
||||||
|
bool GetSquelchLevel(int sockfd, double *dBFS);
|
||||||
|
diff --git a/src/3.c b/src/3.c
|
||||||
|
index e104993..b9033bd 100644
|
||||||
|
--- a/src/3.c
|
||||||
|
+++ b/src/3.c
|
||||||
|
@@ -591,4 +591,176 @@ long int raw_rms(int16_t *samples, int len, int step) //use samplespersymbol as
|
||||||
|
rms = (long int)sqrt((p-err) / len);
|
||||||
|
if (rms < 0) rms = 150;
|
||||||
|
return rms;
|
||||||
|
-}
|
||||||
|
\ No newline at end of file
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+//Ghosts
|
||||||
|
+void death (dsd_opts * opts, dsd_state * state)
|
||||||
|
+{
|
||||||
|
+ int i, x, result = 0;
|
||||||
|
+ short ghosts[960];
|
||||||
|
+ short spooks[960];
|
||||||
|
+ short demons[960];
|
||||||
|
+ short sample = 0;
|
||||||
|
+
|
||||||
|
+ memset (ghosts, 0, 960*sizeof(short));
|
||||||
|
+ memset (spooks, 0, 960*sizeof(short));
|
||||||
|
+ memset (demons, 0, 960*sizeof(short));
|
||||||
|
+
|
||||||
|
+ while (!exitflag) //while the software is running
|
||||||
|
+ {
|
||||||
|
+ //death on speaks at 48k/1 short output
|
||||||
|
+ if (opts->audio_in_type == 0)
|
||||||
|
+ {
|
||||||
|
+ for (i = 0; i < 960; i++)
|
||||||
|
+ {
|
||||||
|
+ pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
|
||||||
|
+ ghosts[i] = sample;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < 960; i++)
|
||||||
|
+ {
|
||||||
|
+ pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
|
||||||
|
+ spooks[i] = sample;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < 960; i++)
|
||||||
|
+ {
|
||||||
|
+ pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
|
||||||
|
+ demons[i] = sample;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (opts->audio_in_type == 8)
|
||||||
|
+ {
|
||||||
|
+ for (i = 0; i < 960; i++)
|
||||||
|
+ {
|
||||||
|
+ result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
+ if (result == 0)
|
||||||
|
+ {
|
||||||
|
+ sf_close(opts->tcp_file_in);
|
||||||
|
+ fprintf (stderr, "Connection to the Underworld Severed.\n");
|
||||||
|
+ fprintf (stderr, "Closing Death Speech Decoder.\n");
|
||||||
|
+ cleanupAndExit(opts, state);
|
||||||
|
+ }
|
||||||
|
+ ghosts[i] = sample;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < 960; i++)
|
||||||
|
+ {
|
||||||
|
+ result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
+ if (result == 0)
|
||||||
|
+ {
|
||||||
|
+ sf_close(opts->tcp_file_in);
|
||||||
|
+ fprintf (stderr, "Connection to the Underworld Severed.\n");
|
||||||
|
+ fprintf (stderr, "Closing Death Speech Decoder.\n");
|
||||||
|
+ cleanupAndExit(opts, state);
|
||||||
|
+ }
|
||||||
|
+ spooks[i] = sample;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < 960; i++)
|
||||||
|
+ {
|
||||||
|
+ result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
+ if (result == 0)
|
||||||
|
+ {
|
||||||
|
+ sf_close(opts->tcp_file_in);
|
||||||
|
+ fprintf (stderr, "Connection to the Underworld Severed.\n");
|
||||||
|
+ fprintf (stderr, "Closing Death Speech Decoder.\n");
|
||||||
|
+ cleanupAndExit(opts, state);
|
||||||
|
+ }
|
||||||
|
+ demons[i] = sample;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ //RTL Input
|
||||||
|
+ #ifdef USE_RTLSDR
|
||||||
|
+ if (opts->audio_in_type == 3)
|
||||||
|
+ {
|
||||||
|
+ for (i = 0; i < 960; i++)
|
||||||
|
+ {
|
||||||
|
+ get_rtlsdr_sample(&sample, opts, state);
|
||||||
|
+ ghosts[i] = sample;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < 960; i++)
|
||||||
|
+ {
|
||||||
|
+ get_rtlsdr_sample(&sample, opts, state);
|
||||||
|
+ spooks[i] = sample;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < 960; i++)
|
||||||
|
+ {
|
||||||
|
+ get_rtlsdr_sample(&sample, opts, state);
|
||||||
|
+ demons[i] = sample;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ #endif
|
||||||
|
+
|
||||||
|
+ //The Dead Don't Like OSS input yet
|
||||||
|
+
|
||||||
|
+ //Listen to the Howls of the Deceased
|
||||||
|
+ if (opts->audio_out_type == 0 && opts->slot1_on == 1)
|
||||||
|
+ {
|
||||||
|
+ pa_simple_write(opts->pulse_raw_dev_out, ghosts, 960*2, NULL);
|
||||||
|
+ pa_simple_write(opts->pulse_raw_dev_out, spooks, 960*2, NULL);
|
||||||
|
+ pa_simple_write(opts->pulse_raw_dev_out, demons, 960*2, NULL);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (opts->audio_out_type == 8) //UDP Audio
|
||||||
|
+ {
|
||||||
|
+ udp_socket_blasterA (opts, state, 960*2, ghosts);
|
||||||
|
+ udp_socket_blasterA (opts, state, 960*2, spooks);
|
||||||
|
+ udp_socket_blasterA (opts, state, 960*2, demons);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ //added a condition check so that if OSS output and 8K, switches to 48K when opening OSS
|
||||||
|
+ if (opts->audio_out_type == 5 && opts->floating_point == 0 && opts->slot1_on == 1)
|
||||||
|
+ {
|
||||||
|
+ write (opts->audio_out_fd, ghosts, 960*2);
|
||||||
|
+ write (opts->audio_out_fd, spooks, 960*2);
|
||||||
|
+ write (opts->audio_out_fd, demons, 960*2);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ //STDOUT -- I don't see the harm of adding this here, will be fine for analog only or digital only (non-mixed analog and digital)
|
||||||
|
+ if (opts->audio_out_type == 1 && opts->floating_point == 0 && opts->slot1_on == 1)
|
||||||
|
+ {
|
||||||
|
+ write (opts->audio_out_fd, ghosts, 960*2);
|
||||||
|
+ write (opts->audio_out_fd, spooks, 960*2);
|
||||||
|
+ write (opts->audio_out_fd, demons, 960*2);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ //Undulate the Crystal Ball
|
||||||
|
+ if (x >= opts->trunk_hangtime)
|
||||||
|
+ {
|
||||||
|
+ long int spook_channel = state->trunk_lcn_freq[0];
|
||||||
|
+
|
||||||
|
+ //rigctl
|
||||||
|
+ if (opts->use_rigctl == 1)
|
||||||
|
+ {
|
||||||
|
+ if (opts->setmod_bw != 0 ) SetModulationWFM(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
|
+ SetFreq(opts->rigctl_sockfd, spook_channel);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ //rtl
|
||||||
|
+ if (opts->audio_in_type == 3)
|
||||||
|
+ {
|
||||||
|
+ #ifdef USE_RTLSDR
|
||||||
|
+ rtl_dev_tune(opts, spook_channel);
|
||||||
|
+ #endif
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ //
|
||||||
|
+ state->trunk_lcn_freq[0] += 200000;
|
||||||
|
+
|
||||||
|
+ if (state->trunk_lcn_freq[0] > 106100000)
|
||||||
|
+ state->trunk_lcn_freq[0] = 87900000;
|
||||||
|
+
|
||||||
|
+ x = 0;
|
||||||
|
+
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ x++;
|
||||||
|
+
|
||||||
|
+ } //end while loop
|
||||||
|
+} //end ghosts
|
||||||
|
\ No newline at end of file
|
||||||
|
diff --git a/src/dsd_main.c b/src/dsd_main.c
|
||||||
|
index ca878e9..f14995d 100644
|
||||||
|
--- a/src/dsd_main.c
|
||||||
|
+++ b/src/dsd_main.c
|
||||||
|
@@ -696,6 +696,7 @@ initOpts (dsd_opts * opts)
|
||||||
|
|
||||||
|
opts->monitor_input_audio = 0; //enable with -8
|
||||||
|
opts->analog_only = 0; //only turned on with -fA
|
||||||
|
+ opts->ghosts_only = 0; //ghostbox mode with -fG
|
||||||
|
|
||||||
|
opts->inverted_p2 = 0;
|
||||||
|
opts->p2counter = 0;
|
||||||
|
@@ -1358,6 +1359,7 @@ usage ()
|
||||||
|
printf ("Decoder options:\n");
|
||||||
|
printf (" -fa Auto Detection\n");
|
||||||
|
printf (" -fA Passive Analog Audio Monitor\n");
|
||||||
|
+ printf (" -fG Ghosts, Demons, Specters, Ghouls, and Skeletons (Ghost Box)\n");
|
||||||
|
printf (" -ft TDMA Trunking P25p1 Control and Voice, P25p2 Trunked Channels, and DMR\n");
|
||||||
|
printf (" -fs DMR TDMA BS and MS Simplex\n");
|
||||||
|
printf (" -f1 Decode only P25 Phase 1\n");
|
||||||
|
@@ -2172,6 +2174,35 @@ main (int argc, char **argv)
|
||||||
|
sprintf (opts.output_name, "Analog Monitor");
|
||||||
|
fprintf (stderr,"Only Monitoring Passive Analog Signal\n");
|
||||||
|
}
|
||||||
|
+ else if (optarg[0] == 'G') //listen to spooky scary skeletons
|
||||||
|
+ {
|
||||||
|
+ opts.frame_dstar = 0;
|
||||||
|
+ opts.frame_x2tdma = 0;
|
||||||
|
+ opts.frame_p25p1 = 0;
|
||||||
|
+ opts.frame_p25p2 = 0;
|
||||||
|
+ opts.frame_nxdn48 = 0;
|
||||||
|
+ opts.frame_nxdn96 = 0;
|
||||||
|
+ opts.frame_dmr = 0;
|
||||||
|
+ opts.frame_dpmr = 0;
|
||||||
|
+ opts.frame_provoice = 0;
|
||||||
|
+ opts.frame_ysf = 0;
|
||||||
|
+ opts.frame_m17 = 0;
|
||||||
|
+ opts.pulse_digi_rate_out = 8000;
|
||||||
|
+ opts.pulse_digi_out_channels = 1;
|
||||||
|
+ opts.dmr_stereo = 0;
|
||||||
|
+ state.dmr_stereo = 0;
|
||||||
|
+ opts.dmr_mono = 0;
|
||||||
|
+ state.rf_mod = 0;
|
||||||
|
+ opts.monitor_input_audio = 1;
|
||||||
|
+ opts.ghosts_only = 1;
|
||||||
|
+ state.trunk_lcn_freq[0] = 87900000; //everybody knows ghosts live on the Broadcast FM band
|
||||||
|
+ opts.setmod_bw = 150000; //Ghosts annunciate clearly with about 150,000 PB
|
||||||
|
+ opts.trunk_hangtime = 0;
|
||||||
|
+ opts.rtlsdr_center_freq = 850000000;
|
||||||
|
+ opts.rtl_bandwidth = 48;
|
||||||
|
+ sprintf (opts.output_name, "Ghost Box");
|
||||||
|
+ fprintf (stderr,"Sounds of the Dead.\n");
|
||||||
|
+ }
|
||||||
|
else if (optarg[0] == 'd')
|
||||||
|
{
|
||||||
|
opts.frame_dstar = 1;
|
||||||
|
@@ -3462,6 +3493,22 @@ main (int argc, char **argv)
|
||||||
|
processM17IPF(&opts, &state);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ else if (opts.ghosts_only == 1)
|
||||||
|
+ {
|
||||||
|
+ #ifdef USE_RTLSDR
|
||||||
|
+ if(opts.audio_in_type == 3)
|
||||||
|
+ open_rtlsdr_stream(&opts);
|
||||||
|
+ #endif
|
||||||
|
+
|
||||||
|
+ if (opts.audio_in_type == 0)
|
||||||
|
+ openPulseInput(&opts);
|
||||||
|
+
|
||||||
|
+ if (opts.audio_out_type == 0)
|
||||||
|
+ openPulseOutput(&opts);
|
||||||
|
+
|
||||||
|
+ death(&opts, &state);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
diff --git a/src/dsd_rigctl.c b/src/dsd_rigctl.c
|
||||||
|
index 820ac7e..f12bacc 100644
|
||||||
|
--- a/src/dsd_rigctl.c
|
||||||
|
+++ b/src/dsd_rigctl.c
|
||||||
|
@@ -163,6 +163,28 @@ bool SetModulation(int sockfd, int bandwidth)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
+bool SetModulationWFM(int sockfd, int bandwidth)
|
||||||
|
+{
|
||||||
|
+ char buf[BUFSIZE];
|
||||||
|
+ //the bandwidth is now a user/system based configurable variable
|
||||||
|
+ sprintf (buf, "M WFM %d\n", bandwidth); //SDR++ has changed the token from FM to NFM, even if Ryzerth fixes it later, users may still have an older version
|
||||||
|
+ Send(sockfd, buf);
|
||||||
|
+ Recv(sockfd, buf);
|
||||||
|
+
|
||||||
|
+ //if it fails the first time, send the other token instead
|
||||||
|
+ if (strcmp(buf, "RPRT 1\n") == 0 ) //sdr++ has a linebreak here, is that in all versions of the protocol?
|
||||||
|
+ {
|
||||||
|
+ sprintf (buf, "M WFM %d\n", bandwidth); //anything not SDR++
|
||||||
|
+ Send(sockfd, buf);
|
||||||
|
+ Recv(sockfd, buf);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (strcmp(buf, "RPRT 1\n") == 0 )
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ return true;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
bool GetSignalLevel(int sockfd, double *dBFS)
|
||||||
|
{
|
||||||
|
char buf[BUFSIZE];
|
||||||
|
diff --git a/src/rtl_sdr_fm.cpp b/src/rtl_sdr_fm.cpp
|
||||||
|
index ad04667..dd6566c 100644
|
||||||
|
--- a/src/rtl_sdr_fm.cpp
|
||||||
|
+++ b/src/rtl_sdr_fm.cpp
|
||||||
|
@@ -1034,7 +1034,7 @@ void open_rtlsdr_stream(dsd_opts *opts)
|
||||||
|
//init with low pass if decoding P25 or EDACS/Provoice
|
||||||
|
if (opts->frame_p25p1 == 1 || opts->frame_p25p2 == 1 || opts->frame_provoice == 1)
|
||||||
|
demod_init_ro2(&demod);
|
||||||
|
- else if (opts->analog_only == 1 || opts->m17encoder == 1)
|
||||||
|
+ else if (opts->analog_only == 1 || opts->m17encoder == 1 || opts->ghosts_only == 1)
|
||||||
|
demod_init_analog(&demod);
|
||||||
|
else demod_init(&demod);
|
||||||
|
output_init(&output);
|
||||||
@@ -0,0 +1,499 @@
|
|||||||
|
diff --git a/src/dmr_csbk.c b/src/dmr_csbk.c
|
||||||
|
index 6ee44d6..45502a4 100644
|
||||||
|
--- a/src/dmr_csbk.c
|
||||||
|
+++ b/src/dmr_csbk.c
|
||||||
|
@@ -520,6 +520,26 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
|
//rigctl
|
||||||
|
if (opts->use_rigctl == 1)
|
||||||
|
{
|
||||||
|
+
|
||||||
|
+ if (state->currentslot == 0)
|
||||||
|
+ {
|
||||||
|
+ //close any perCall wav files that are open
|
||||||
|
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_f != NULL)
|
||||||
|
+ {
|
||||||
|
+ closeWavOutFileL (opts, state);
|
||||||
|
+ opts->wav_out_f = NULL;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ else if (state->currentslot == 1)
|
||||||
|
+ {
|
||||||
|
+ //close any perCall wav files that are open
|
||||||
|
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_fR != NULL)
|
||||||
|
+ {
|
||||||
|
+ closeWavOutFileR (opts, state);
|
||||||
|
+ opts->wav_out_fR = NULL;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
//Guess I forgot to add this condition here
|
||||||
|
if (GetCurrentFreq(opts->rigctl_sockfd) != state->p25_cc_freq)
|
||||||
|
dmr_reset_blocks (opts, state); //reset all block gathering since we are tuning away from current frequency
|
||||||
|
@@ -554,6 +574,26 @@ void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8
|
||||||
|
else if (opts->audio_in_type == 3)
|
||||||
|
{
|
||||||
|
#ifdef USE_RTLSDR
|
||||||
|
+
|
||||||
|
+ if (state->currentslot == 0)
|
||||||
|
+ {
|
||||||
|
+ //close any perCall wav files that are open
|
||||||
|
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_f != NULL)
|
||||||
|
+ {
|
||||||
|
+ closeWavOutFileL (opts, state);
|
||||||
|
+ opts->wav_out_f = NULL;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ else if (state->currentslot == 1)
|
||||||
|
+ {
|
||||||
|
+ //close any perCall wav files that are open
|
||||||
|
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_fR != NULL)
|
||||||
|
+ {
|
||||||
|
+ closeWavOutFileR (opts, state);
|
||||||
|
+ opts->wav_out_fR = NULL;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
//Guess I forgot to add this condition here
|
||||||
|
uint32_t tempf = (uint32_t)state->p25_cc_freq;
|
||||||
|
if (opts->rtlsdr_center_freq != tempf)
|
||||||
|
diff --git a/src/dmr_flco.c b/src/dmr_flco.c
|
||||||
|
index 839b085..637debf 100644
|
||||||
|
--- a/src/dmr_flco.c
|
||||||
|
+++ b/src/dmr_flco.c
|
||||||
|
@@ -298,20 +298,7 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||||
|
|
||||||
|
if (type != 2) //VLC and EMB, set our target, source, so, and fid per channel
|
||||||
|
{
|
||||||
|
- if (state->currentslot == 0)
|
||||||
|
- {
|
||||||
|
- state->dmr_fid = fid;
|
||||||
|
- state->dmr_so = so;
|
||||||
|
- state->lasttg = target;
|
||||||
|
- state->lastsrc = source;
|
||||||
|
- }
|
||||||
|
- if (state->currentslot == 1)
|
||||||
|
- {
|
||||||
|
- state->dmr_fidR = fid;
|
||||||
|
- state->dmr_soR = so;
|
||||||
|
- state->lasttgR = target;
|
||||||
|
- state->lastsrcR = source;
|
||||||
|
- }
|
||||||
|
+ //moved per call further down past the call_string stuff
|
||||||
|
|
||||||
|
//update cc amd vc sync time for trunking purposes (particularly Con+)
|
||||||
|
if (opts->p25_is_tuned == 1)
|
||||||
|
@@ -337,6 +324,12 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||||
|
//reset gain
|
||||||
|
if (opts->floating_point == 1)
|
||||||
|
state->aout_gain = opts->audio_gain;
|
||||||
|
+ //close any perCall wav files that are open
|
||||||
|
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_f != NULL)
|
||||||
|
+ {
|
||||||
|
+ closeWavOutFileL (opts, state);
|
||||||
|
+ opts->wav_out_f = NULL;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
{
|
||||||
|
@@ -350,6 +343,12 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||||
|
//reset gain
|
||||||
|
if (opts->floating_point == 1)
|
||||||
|
state->aout_gainR = opts->audio_gain;
|
||||||
|
+ //close any perCall wav files that are open
|
||||||
|
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_fR != NULL)
|
||||||
|
+ {
|
||||||
|
+ closeWavOutFileR (opts, state);
|
||||||
|
+ opts->wav_out_fR = NULL;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@@ -612,6 +611,69 @@ void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t C
|
||||||
|
fprintf (stderr, "Key %010llX ", state->RR);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ if (type != 2)
|
||||||
|
+ {
|
||||||
|
+ if (state->currentslot == 0)
|
||||||
|
+ {
|
||||||
|
+ state->dmr_fid = fid;
|
||||||
|
+ state->dmr_so = so;
|
||||||
|
+ state->lasttg = target;
|
||||||
|
+ state->lastsrc = source;
|
||||||
|
+ char * timestr = NULL;
|
||||||
|
+ char * datestr = NULL;
|
||||||
|
+ timestr = getTime();
|
||||||
|
+ datestr = getDate();
|
||||||
|
+
|
||||||
|
+ //setup a call string for the per call (group/private and/or emergency)
|
||||||
|
+ char cs[200]; memset (cs, 0, 200*sizeof(char));
|
||||||
|
+ int x = 0;
|
||||||
|
+ for (int i = 0; i < 200; i++)
|
||||||
|
+ {
|
||||||
|
+ //copy over non space and non termination values from call string
|
||||||
|
+ if (i != 0 && state->call_string[0][i] != ' ' && state->call_string[0][i] != 0)
|
||||||
|
+ cs[x++] = state->call_string[0][i];
|
||||||
|
+ }
|
||||||
|
+ cs[x] = 0; //terminate
|
||||||
|
+
|
||||||
|
+ //open wav file here if not already opened
|
||||||
|
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_f == NULL)
|
||||||
|
+ {
|
||||||
|
+ sprintf (opts->wav_out_file, "%s/%s %s CC %d - %s - TG %d - RD %d", opts->wav_out_dir, datestr, timestr, state->dmr_color_code, cs, target, source);
|
||||||
|
+ openWavOutFileL (opts, state);
|
||||||
|
+ }
|
||||||
|
+ free (timestr); free(datestr);
|
||||||
|
+ }
|
||||||
|
+ if (state->currentslot == 1)
|
||||||
|
+ {
|
||||||
|
+ state->dmr_fidR = fid;
|
||||||
|
+ state->dmr_soR = so;
|
||||||
|
+ state->lasttgR = target;
|
||||||
|
+ state->lastsrcR = source;
|
||||||
|
+ char * timestr = NULL;
|
||||||
|
+ char * datestr = NULL;
|
||||||
|
+ timestr = getTime();
|
||||||
|
+ datestr = getDate();
|
||||||
|
+
|
||||||
|
+ //setup a call string for the per call (group/private and/or emergency)
|
||||||
|
+ char cs[200]; memset (cs, 0, 200*sizeof(char));
|
||||||
|
+ int x = 0;
|
||||||
|
+ for (int i = 0; i < 200; i++)
|
||||||
|
+ {
|
||||||
|
+ //copy over non space and non termination values from call string
|
||||||
|
+ if (i != 0 && state->call_string[1][i] != ' ' && state->call_string[1][i] != 0)
|
||||||
|
+ cs[x++] = state->call_string[1][i];
|
||||||
|
+ }
|
||||||
|
+ cs[x] = 0; //terminate
|
||||||
|
+
|
||||||
|
+ //open wav file here if not already opened
|
||||||
|
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_fR == NULL)
|
||||||
|
+ {
|
||||||
|
+ sprintf (opts->wav_out_fileR, "%s/%s %s CC %d - %s - TG %d - RD %d", opts->wav_out_dir, datestr, timestr, state->dmr_color_code, cs, target, source);
|
||||||
|
+ openWavOutFileR (opts, state);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/src/dsd_file.c b/src/dsd_file.c
|
||||||
|
index 6f4ecaa..f4b201d 100644
|
||||||
|
--- a/src/dsd_file.c
|
||||||
|
+++ b/src/dsd_file.c
|
||||||
|
@@ -555,6 +555,16 @@ void closeWavOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
|
sf_close(opts->wav_out_f);
|
||||||
|
+ char newfilename[1032];
|
||||||
|
+ int result; UNUSED(result);
|
||||||
|
+ sprintf (newfilename, "%s.wav", opts->wav_out_file);
|
||||||
|
+ result = rename (opts->wav_out_file, newfilename);
|
||||||
|
+ // fprintf (stderr, "\n Close WAV FileF %s; Result: %d \n", newfilename, result);
|
||||||
|
+
|
||||||
|
+ //copy back to wav_out_file string for ncurses display
|
||||||
|
+ memcpy (opts->wav_out_file, newfilename, 1023);
|
||||||
|
+ opts->wav_out_file[1023] = 0;
|
||||||
|
+
|
||||||
|
}
|
||||||
|
|
||||||
|
void closeWavOutFileL (dsd_opts * opts, dsd_state * state)
|
||||||
|
@@ -562,6 +572,16 @@ void closeWavOutFileL (dsd_opts * opts, dsd_state * state)
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
|
sf_close(opts->wav_out_f);
|
||||||
|
+ char newfilename[1032];
|
||||||
|
+ int result; UNUSED(result);
|
||||||
|
+ sprintf (newfilename, "%s.wav", opts->wav_out_file);
|
||||||
|
+ result = rename (opts->wav_out_file, newfilename);
|
||||||
|
+ // fprintf (stderr, "\n Close WAV FileL %s; Result: %d \n", newfilename, result);
|
||||||
|
+
|
||||||
|
+ //copy back to wav_out_file string for ncurses display
|
||||||
|
+ memcpy (opts->wav_out_file, newfilename, 1023);
|
||||||
|
+ opts->wav_out_file[1023] = 0;
|
||||||
|
+
|
||||||
|
}
|
||||||
|
|
||||||
|
void closeWavOutFileR (dsd_opts * opts, dsd_state * state)
|
||||||
|
@@ -569,6 +589,15 @@ void closeWavOutFileR (dsd_opts * opts, dsd_state * state)
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
|
sf_close(opts->wav_out_fR);
|
||||||
|
+ char newfilename[1032];
|
||||||
|
+ int result; UNUSED(result);
|
||||||
|
+ sprintf (newfilename, "%s.wav", opts->wav_out_fileR);
|
||||||
|
+ result = rename (opts->wav_out_fileR, newfilename);
|
||||||
|
+ // fprintf (stderr, "\n Close WAV FileR %s; Result: %d \n", newfilename, result);
|
||||||
|
+
|
||||||
|
+ //copy back to wav_out_file string for ncurses display
|
||||||
|
+ memcpy (opts->wav_out_fileR, newfilename, 1023);
|
||||||
|
+ opts->wav_out_fileR[1023] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void closeWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
||||||
|
diff --git a/src/dsd_main.c b/src/dsd_main.c
|
||||||
|
index 3fb0893..a6d45e5 100644
|
||||||
|
--- a/src/dsd_main.c
|
||||||
|
+++ b/src/dsd_main.c
|
||||||
|
@@ -107,6 +107,32 @@ void
|
||||||
|
noCarrier (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
|
||||||
|
+ //close any perCall wav files that are open
|
||||||
|
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_f != NULL)
|
||||||
|
+ {
|
||||||
|
+ closeWavOutFileL (opts, state);
|
||||||
|
+ opts->wav_out_f = NULL;
|
||||||
|
+ }
|
||||||
|
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_fR != NULL)
|
||||||
|
+ {
|
||||||
|
+ closeWavOutFileR (opts, state);
|
||||||
|
+ opts->wav_out_fR = NULL;
|
||||||
|
+ }
|
||||||
|
+ //end perCall wav file close
|
||||||
|
+
|
||||||
|
+ //close any perCall wav files that are open
|
||||||
|
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_f != NULL)
|
||||||
|
+ {
|
||||||
|
+ closeWavOutFileL (opts, state);
|
||||||
|
+ opts->wav_out_f = NULL;
|
||||||
|
+ }
|
||||||
|
+ if (opts->dmr_stereo_wav == 1 && opts->wav_out_fR != NULL)
|
||||||
|
+ {
|
||||||
|
+ closeWavOutFileR (opts, state);
|
||||||
|
+ opts->wav_out_fR = NULL;
|
||||||
|
+ }
|
||||||
|
+ //end perCall wav file close
|
||||||
|
+
|
||||||
|
// #ifdef AERO_BUILD //NOTE: Blame seems to be synctest8 not being initialized (will continue to test)
|
||||||
|
// //TODO: Investigate why getSymbol needs to be run first in this context...truly confused here
|
||||||
|
// if(opts->frame_m17 == 1) //&& opts->audio_in_type == 5
|
||||||
|
@@ -1692,9 +1718,9 @@ main (int argc, char **argv)
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AERO_BUILD
|
||||||
|
- fprintf (stderr, "Build Version: AW (20231015) \n");
|
||||||
|
+ fprintf (stderr, "Build Version: AW (20240601) G Build\n");
|
||||||
|
#else
|
||||||
|
- fprintf (stderr, "Build Version: AW %s \n", GIT_TAG);
|
||||||
|
+ fprintf (stderr, "Build Version: AW %s G Build\n", GIT_TAG);
|
||||||
|
#endif
|
||||||
|
fprintf (stderr,"MBElib Version: %s\n", versionstr);
|
||||||
|
|
||||||
|
diff --git a/src/dsd_mbe.c b/src/dsd_mbe.c
|
||||||
|
index fe71539..2702b5d 100644
|
||||||
|
--- a/src/dsd_mbe.c
|
||||||
|
+++ b/src/dsd_mbe.c
|
||||||
|
@@ -1197,7 +1197,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||||
|
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
|
||||||
|
{
|
||||||
|
//write wav to per call on left channel Slot 1
|
||||||
|
- writeSynthesizedVoice (opts, state);
|
||||||
|
+ if (opts->wav_out_f != NULL)
|
||||||
|
+ writeSynthesizedVoice (opts, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1207,7 +1208,8 @@ processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char a
|
||||||
|
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
|
||||||
|
{
|
||||||
|
//write wav to per call on right channel Slot 2
|
||||||
|
- writeSynthesizedVoiceR (opts, state);
|
||||||
|
+ if (opts->wav_out_fR != NULL)
|
||||||
|
+ writeSynthesizedVoiceR (opts, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c
|
||||||
|
index a5442af..0cd538d 100644
|
||||||
|
--- a/src/dsd_ncurses.c
|
||||||
|
+++ b/src/dsd_ncurses.c
|
||||||
|
@@ -1071,7 +1071,7 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state)
|
||||||
|
if (choicec == 12)
|
||||||
|
{
|
||||||
|
//flesh out all closewavs and sprint "" wav filenames
|
||||||
|
- closeWavOutFile (opts, state);
|
||||||
|
+ // closeWavOutFile (opts, state);
|
||||||
|
closeWavOutFileL (opts, state);
|
||||||
|
closeWavOutFileR (opts, state);
|
||||||
|
//closeWavOutFileRaw (opts, state);
|
||||||
|
@@ -2153,7 +2153,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
char cs[200]; memcpy (cs, state->call_string[0], 200*sizeof(char));
|
||||||
|
|
||||||
|
//close old first, assign name based on time and radio, open wav file
|
||||||
|
- closeWavOutFileL (opts, state);
|
||||||
|
+ if (opts->wav_out_f != NULL)
|
||||||
|
+ closeWavOutFileL (opts, state);
|
||||||
|
|
||||||
|
// sprintf (opts->wav_out_file, "%s/%s %s NXDN - RAN %d - DST %d - SRC %d.wav", opts->wav_out_dir, datestr, timestr, rn, tgn, src); //original
|
||||||
|
sprintf (opts->wav_out_file, "%s/%s %s NXDN - RAN %d - %s - DST %d - SRC %d.wav", opts->wav_out_dir, datestr, timestr, rn, cs, tgn, src); //with call string
|
||||||
|
@@ -2198,27 +2199,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
call_matrix[9][5] = time(NULL);
|
||||||
|
|
||||||
|
//open wav file if enabled and both rd and tg are not 0
|
||||||
|
- if (opts->dmr_stereo_wav == 1 && rd != 0 && tg != 0)
|
||||||
|
- {
|
||||||
|
- //setup a call string for the per call (group/private and/or emergency)
|
||||||
|
- char cs[200]; memset (cs, 0, 200*sizeof(char));
|
||||||
|
- int x = 0;
|
||||||
|
- for (int i = 0; i < 200; i++)
|
||||||
|
- {
|
||||||
|
- //copy over non space and non termination values from call string
|
||||||
|
- if (i != 0 && state->call_string[0][i] != ' ' && state->call_string[0][i] != 0)
|
||||||
|
- cs[x++] = state->call_string[0][i];
|
||||||
|
- }
|
||||||
|
- cs[x] = 0; //terminate
|
||||||
|
-
|
||||||
|
- //close old first, assign name based on time and radio, open wav file
|
||||||
|
- closeWavOutFileL (opts, state);
|
||||||
|
-
|
||||||
|
- // sprintf (opts->wav_out_file, "%s/%s %s MS - CC %d - TG %d - RD %d.wav", opts->wav_out_dir, datestr, timestr, dcc, tg, rd); //original
|
||||||
|
- sprintf (opts->wav_out_file, "%s/%s %s MS - CC %d - %s - DST %d - SRC %d.wav", opts->wav_out_dir, datestr, timestr, dcc, cs, tg, rd); //with cs call string
|
||||||
|
-
|
||||||
|
- openWavOutFileL (opts, state); //testing for now, will want to move to per call later
|
||||||
|
- }
|
||||||
|
+ // if (opts->dmr_stereo_wav == 1 && rd != 0 && tg != 0)
|
||||||
|
+ // {
|
||||||
|
+ // //close old first, assign name based on time and radio, open wav file
|
||||||
|
+ // if (opts->wav_out_f != NULL)
|
||||||
|
+ // closeWavOutFileL (opts, state);
|
||||||
|
+ // sprintf (opts->wav_out_file, "./%s/%s %s MS - CC %d - TG %d - RD %d.wav", opts->wav_out_dir, getDateN(), timestr, dcc, tg, rd);
|
||||||
|
+ // openWavOutFileL (opts, state); //testing for now, will want to move to per call later
|
||||||
|
+ // }
|
||||||
|
|
||||||
|
if (opts->call_alert == 1 && rd != 0 && tg != 0)
|
||||||
|
{
|
||||||
|
@@ -2233,7 +2221,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
}
|
||||||
|
|
||||||
|
//DMR BS Slot 1 - matrix 0-4
|
||||||
|
- if ( call_matrix[4][2] != rd && (lls == 11 || lls == 12 || lls == 10 || lls == 13 || lls == 35 || lls == 36) )
|
||||||
|
+ if ( call_matrix[4][2] != rd && (lls == 11 || lls == 12 || lls == 10 || lls == 13 ) ) //|| lls == 35 || lls == 36
|
||||||
|
{
|
||||||
|
|
||||||
|
for (short int k = 0; k < 4; k++)
|
||||||
|
@@ -2254,27 +2242,14 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
call_matrix[4][5] = time(NULL);
|
||||||
|
|
||||||
|
//open wav file if enabled and both rd and tg are not 0
|
||||||
|
- if (opts->dmr_stereo_wav == 1 && rd != 0 && tg != 0)
|
||||||
|
- {
|
||||||
|
- //setup a call string for the per call (group/private and/or emergency)
|
||||||
|
- char cs[200]; memset (cs, 0, 200*sizeof(char));
|
||||||
|
- int x = 0;
|
||||||
|
- for (int i = 0; i < 200; i++)
|
||||||
|
- {
|
||||||
|
- //copy over non space and non termination values from call string
|
||||||
|
- if (i != 0 && state->call_string[0][i] != ' ' && state->call_string[0][i] != 0)
|
||||||
|
- cs[x++] = state->call_string[0][i];
|
||||||
|
- }
|
||||||
|
- cs[x] = 0; //terminate
|
||||||
|
-
|
||||||
|
- //close old first, assign name based on time and radio, open wav file
|
||||||
|
- closeWavOutFileL (opts, state);
|
||||||
|
-
|
||||||
|
- // sprintf (opts->wav_out_file, "%s/%s %s CC %d - TG %d - RD %d.wav", opts->wav_out_dir, datestr, timestr, dcc, tg, rd); //original
|
||||||
|
- sprintf (opts->wav_out_file, "%s/%s %s CC %d - %s - DST %d - SRC %d.wav", opts->wav_out_dir, datestr, timestr, dcc, cs, tg, rd); //with call string
|
||||||
|
-
|
||||||
|
- openWavOutFileL (opts, state); //testing for now, will want to move to per call later
|
||||||
|
- }
|
||||||
|
+ // if (opts->dmr_stereo_wav == 1 && rd != 0 && tg != 0)
|
||||||
|
+ // {
|
||||||
|
+ // //close old first, assign name based on time and radio, open wav file
|
||||||
|
+ // if (opts->wav_out_f != NULL)
|
||||||
|
+ // closeWavOutFileL (opts, state);
|
||||||
|
+ // sprintf (opts->wav_out_file, "./%s/%s %s CC %d - TG %d - RD %d", opts->wav_out_dir, getDateN(), timestr, dcc, tg, rd);
|
||||||
|
+ // openWavOutFileL (opts, state); //testing for now, will want to move to per call later
|
||||||
|
+ // }
|
||||||
|
|
||||||
|
if (opts->call_alert == 1 && rd != 0 && tg != 0)
|
||||||
|
{
|
||||||
|
@@ -2310,34 +2285,21 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
call_matrix[9][5] = time(NULL);
|
||||||
|
|
||||||
|
//open wav file if enabled and both rdR and tgR are not 0
|
||||||
|
- if (opts->dmr_stereo_wav == 1 && rdR != 0 && tgR != 0)
|
||||||
|
- {
|
||||||
|
- //setup a call string for the per call (group/private and/or emergency)
|
||||||
|
- char cs[200]; memset (cs, 0, 200*sizeof(char));
|
||||||
|
- int x = 0;
|
||||||
|
- for (int i = 0; i < 200; i++)
|
||||||
|
- {
|
||||||
|
- //copy over non space and non termination values from call string
|
||||||
|
- if (i != 0 && state->call_string[1][i] != ' ' && state->call_string[1][i] != 0)
|
||||||
|
- cs[x++] = state->call_string[1][i];
|
||||||
|
- }
|
||||||
|
- cs[x] = 0; //terminate
|
||||||
|
-
|
||||||
|
- //close old first, assign name based on time and radio, open wav file
|
||||||
|
- closeWavOutFileR (opts, state);
|
||||||
|
-
|
||||||
|
- // sprintf (opts->wav_out_fileR, "%s/%s %s CC %d - TG %d - RD %d.wav", opts->wav_out_dir, datestr, timestr, dcc, tgR, rdR); //original
|
||||||
|
- sprintf (opts->wav_out_fileR, "%s/%s %s CC %d - %s - DST %d - SRC %d.wav", opts->wav_out_dir, datestr, timestr, dcc, cs, tgR, rdR); //with call string
|
||||||
|
-
|
||||||
|
- openWavOutFileR (opts, state); //testing for now, will want to move to per call later
|
||||||
|
- }
|
||||||
|
+ // if (opts->dmr_stereo_wav == 1 && rdR != 0 && tgR != 0)
|
||||||
|
+ // {
|
||||||
|
+ // //close old first, assign name based on time and radio, open wav file
|
||||||
|
+ // if (opts->wav_out_fR != NULL)
|
||||||
|
+ // closeWavOutFileR (opts, state);
|
||||||
|
+ // sprintf (opts->wav_out_fileR, "./%s/%s %s CC %d - TG %d - RD %d", opts->wav_out_dir, getDateN(), timestr, dcc, tgR, rdR);
|
||||||
|
+ // openWavOutFileR (opts, state); //testing for now, will want to move to per call later
|
||||||
|
+ // }
|
||||||
|
|
||||||
|
- if (opts->call_alert == 1 && rdR != 0 && tgR != 0)
|
||||||
|
- {
|
||||||
|
- //fprintf (stderr, "BEEP 1 BS RIGHT\n");
|
||||||
|
- beeper (opts, state, 1);
|
||||||
|
- state->dmr_end_alert[1] = 0; //new voice frame, okay to beep at the end of it
|
||||||
|
- }
|
||||||
|
+ // if (opts->call_alert == 1 && rdR != 0 && tgR != 0)
|
||||||
|
+ // {
|
||||||
|
+ // //fprintf (stderr, "BEEP 1 BS RIGHT\n");
|
||||||
|
+ // beeper (opts, state, 1);
|
||||||
|
+ // state->dmr_end_alert[1] = 0; //new voice frame, okay to beep at the end of it
|
||||||
|
+ // }
|
||||||
|
|
||||||
|
memset(state->dmr_alias_block_segment[1], 0, sizeof(state->dmr_alias_block_segment[1]));
|
||||||
|
sprintf (state->dmr_embedded_gps[1], "%s", "");
|
||||||
|
@@ -2423,8 +2385,8 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
if (opts->ncurses_compact == 1)
|
||||||
|
{
|
||||||
|
printw ("------------------------------------------------------------------------------\n");
|
||||||
|
- printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20231015)");
|
||||||
|
- printw ("------------------------------------------------------------------------------\n");
|
||||||
|
+ printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20240601)");
|
||||||
|
+ printw ("------------------------------------------------------------------------------\n");
|
||||||
|
}
|
||||||
|
#elif LIMAZULUTWEAKS
|
||||||
|
if (opts->ncurses_compact == 1)
|
||||||
|
@@ -2462,13 +2424,13 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
if (i == 4 && opts->frame_m17 == 1) printw (" CODEC2");
|
||||||
|
#endif
|
||||||
|
#ifdef AERO_BUILD
|
||||||
|
- if (i == 5) printw (" %s ", "Aero Build");
|
||||||
|
- if (i == 6) printw (" AW (20231015) \n");
|
||||||
|
+ if (i == 5) printw (" %s ", "G Build");
|
||||||
|
+ if (i == 6) printw (" AW (20240601) \n");
|
||||||
|
#elif ZDEV_BUILD
|
||||||
|
- if (i == 5) printw (" %s ", "AW ");
|
||||||
|
+ if (i == 5) printw (" %s ", "AW G Build");
|
||||||
|
if (i == 6) printw (" %s \n", GIT_TAG);
|
||||||
|
- #else
|
||||||
|
- if (i == 5) printw (" %s ", "AW ");
|
||||||
|
+ #else
|
||||||
|
+ if (i == 5) printw (" %s ", "AW G Build");
|
||||||
|
if (i == 6) printw (" %s \n", GIT_TAG);
|
||||||
|
#endif
|
||||||
|
else printw ("\n");
|
||||||
|
@@ -4426,7 +4388,7 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state)
|
||||||
|
if (c == 112) //'p' key - stop all per call wav files
|
||||||
|
{
|
||||||
|
//hope this one doesn't cause random crashing or garbage writing
|
||||||
|
- closeWavOutFile (opts, state);
|
||||||
|
+ // closeWavOutFile (opts, state);
|
||||||
|
closeWavOutFileL (opts, state);
|
||||||
|
closeWavOutFileR (opts, state);
|
||||||
|
sprintf (opts->wav_out_file, "%s", "");
|
||||||
257
patch/moto_type2_starter_pack.patch
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
diff --git a/include/dsd.h b/include/dsd.h
|
||||||
|
index 4a75702..355837f 100644
|
||||||
|
--- a/include/dsd.h
|
||||||
|
+++ b/include/dsd.h
|
||||||
|
@@ -357,6 +357,8 @@ typedef struct
|
||||||
|
int frame_m17;
|
||||||
|
int inverted_m17;
|
||||||
|
|
||||||
|
+ int frame_typeii;
|
||||||
|
+
|
||||||
|
FILE *symbolfile;
|
||||||
|
int call_alert;
|
||||||
|
|
||||||
|
@@ -989,6 +991,10 @@ typedef struct
|
||||||
|
#define INV_DPMR_FRAME_SYNC_3 "311313111133"
|
||||||
|
#define INV_DPMR_FRAME_SYNC_4 "111333331133131131111313"
|
||||||
|
|
||||||
|
+//Motorola Type II Sync Pattern 0xAC binary 1010 1100
|
||||||
|
+#define TYPE_II_SYNC "13131133" //baesd on what getDibit returns
|
||||||
|
+#define TYPE_II_SINV "31313311" //baesd on what getDibit returns
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* function prototypes
|
||||||
|
*/
|
||||||
|
@@ -1357,6 +1363,9 @@ void edacs(dsd_opts * opts, dsd_state * state);
|
||||||
|
unsigned long long int edacs_bch (unsigned long long int message);
|
||||||
|
void eot_cc(dsd_opts * opts, dsd_state * state); //end of TX return to CC
|
||||||
|
|
||||||
|
+//Type II
|
||||||
|
+void type_ii(dsd_opts * opts, dsd_state * state);
|
||||||
|
+
|
||||||
|
//Generic Tuning Functions
|
||||||
|
void return_to_cc (dsd_opts * opts, dsd_state * state);
|
||||||
|
|
||||||
|
diff --git a/src/dsd_dibit.c b/src/dsd_dibit.c
|
||||||
|
index 5424f7a..9b403cf 100644
|
||||||
|
--- a/src/dsd_dibit.c
|
||||||
|
+++ b/src/dsd_dibit.c
|
||||||
|
@@ -222,13 +222,14 @@ invert_dibit(int dibit)
|
||||||
|
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))
|
||||||
|
+ if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18) || (state->synctype == 37) || (state->synctype == 40))
|
||||||
|
|
||||||
|
{
|
||||||
|
// 6 +D-STAR
|
||||||
|
// 14 +ProVoice
|
||||||
|
// 18 +D-STAR_HD
|
||||||
|
// 37 +EDACS
|
||||||
|
+ // 41 +TypeII
|
||||||
|
|
||||||
|
if (symbol > state->center)
|
||||||
|
{
|
||||||
|
@@ -243,12 +244,13 @@ int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
||||||
|
return (1); // +3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- else if ((state->synctype == 7) || (state->synctype == 15)|| (state->synctype == 19) || (state->synctype == 38))
|
||||||
|
+ else if ((state->synctype == 7) || (state->synctype == 15)|| (state->synctype == 19) || (state->synctype == 38) || (state->synctype == 41))
|
||||||
|
{
|
||||||
|
// 7 -D-STAR
|
||||||
|
// 15 -ProVoice
|
||||||
|
// 19 -D-STAR_HD
|
||||||
|
// 38 -EDACS
|
||||||
|
+ // 41 -TypeII
|
||||||
|
|
||||||
|
if (symbol > state->center)
|
||||||
|
{
|
||||||
|
diff --git a/src/dsd_frame.c b/src/dsd_frame.c
|
||||||
|
index 577169c..58fa07c 100644
|
||||||
|
--- a/src/dsd_frame.c
|
||||||
|
+++ b/src/dsd_frame.c
|
||||||
|
@@ -269,6 +269,12 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
||||||
|
processYSF(opts, state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
+ //TypeII
|
||||||
|
+ else if (state->synctype == 40 || state->synctype == 41)
|
||||||
|
+ {
|
||||||
|
+ type_ii(opts, state);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
//M17
|
||||||
|
else if ((state->synctype == 16) || (state->synctype == 9) || (state->synctype == 17) || (state->synctype == 8) ||
|
||||||
|
(state->synctype == 76) || (state->synctype == 77) || (state->synctype == 86) || (state->synctype == 87) ||
|
||||||
|
diff --git a/src/dsd_frame_sync.c b/src/dsd_frame_sync.c
|
||||||
|
index e10f3cc..7f5fbf5 100644
|
||||||
|
--- a/src/dsd_frame_sync.c
|
||||||
|
+++ b/src/dsd_frame_sync.c
|
||||||
|
@@ -117,6 +117,8 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||||
|
* 36 = -P25 P2
|
||||||
|
* 37 = +EDACS
|
||||||
|
* 38 = -EDACS
|
||||||
|
+ * 40 = +Type2
|
||||||
|
+ * 41 = -Type2
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@@ -205,7 +207,7 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
t_max = 12; //based on Frame_Sync_2 pattern
|
||||||
|
}
|
||||||
|
- else if (opts->frame_m17 == 1)
|
||||||
|
+ else if (opts->frame_m17 == 1 || opts->frame_typeii == 1)
|
||||||
|
{
|
||||||
|
t_max = 8;
|
||||||
|
}
|
||||||
|
@@ -581,6 +583,40 @@ getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||||
|
}
|
||||||
|
//end YSF sync
|
||||||
|
|
||||||
|
+ //Motorola Type II
|
||||||
|
+ strncpy(synctest8, (synctest_p - 7), 8);
|
||||||
|
+ if (opts->frame_typeii == 1)
|
||||||
|
+ {
|
||||||
|
+ if (strcmp(synctest8, TYPE_II_SYNC) == 0)
|
||||||
|
+ {
|
||||||
|
+ if (state->lastsynctype == 40)
|
||||||
|
+ {
|
||||||
|
+ printFrameSync (opts, state, "+Type II", synctest_pos + 1, modulation);
|
||||||
|
+ state->carrier = 1;
|
||||||
|
+ state->offset = synctest_pos;
|
||||||
|
+ state->max = ((state->max) + lmax) / 2;
|
||||||
|
+ state->min = ((state->min) + lmin) / 2;
|
||||||
|
+ state->lastsynctype = 40;
|
||||||
|
+ return (40);
|
||||||
|
+ }
|
||||||
|
+ else state->lastsynctype = 40;
|
||||||
|
+ }
|
||||||
|
+ else if (strcmp(synctest8, TYPE_II_SINV) == 0)
|
||||||
|
+ {
|
||||||
|
+ if (state->lastsynctype == 41)
|
||||||
|
+ {
|
||||||
|
+ printFrameSync (opts, state, "-Type II", synctest_pos + 1, modulation);
|
||||||
|
+ state->carrier = 1;
|
||||||
|
+ state->offset = synctest_pos;
|
||||||
|
+ state->max = ((state->max) + lmax) / 2;
|
||||||
|
+ state->min = ((state->min) + lmin) / 2;
|
||||||
|
+ state->lastsynctype = 41;
|
||||||
|
+ return (41);
|
||||||
|
+ }
|
||||||
|
+ else state->lastsynctype = 41;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
//M17 Sync -- Just STR and LSF for now
|
||||||
|
strncpy(synctest16, (synctest_p - 15), 16);
|
||||||
|
strncpy(synctest8, (synctest_p - 7), 8);
|
||||||
|
diff --git a/src/dsd_main.c b/src/dsd_main.c
|
||||||
|
index ca878e9..c5a3e95 100644
|
||||||
|
--- a/src/dsd_main.c
|
||||||
|
+++ b/src/dsd_main.c
|
||||||
|
@@ -622,6 +622,7 @@ initOpts (dsd_opts * opts)
|
||||||
|
opts->frame_provoice = 0;
|
||||||
|
opts->frame_ysf = 1;
|
||||||
|
opts->frame_m17 = 0;
|
||||||
|
+ opts->frame_typeii = 0;
|
||||||
|
opts->mod_c4fm = 1;
|
||||||
|
opts->mod_qpsk = 0;
|
||||||
|
opts->mod_gfsk = 0;
|
||||||
|
@@ -1377,6 +1378,7 @@ usage ()
|
||||||
|
printf (" -fe Decode only EDACS EA/ProVoice*\n");
|
||||||
|
printf (" -fE Decode only EDACS EA/ProVoice with ESK 0xA0*\n");
|
||||||
|
printf (" -fm Decode only dPMR*\n");
|
||||||
|
+ printf (" -fT Decode only Type II (Smartnet/Smartzone)*\n");
|
||||||
|
printf (" -l Disable DMR, dPMR, NXDN, M17 input filtering\n");
|
||||||
|
printf (" -u <num> Unvoiced speech quality (default=3)\n");
|
||||||
|
printf (" -xx Expect non-inverted X2-TDMA signal\n");
|
||||||
|
@@ -2431,6 +2433,39 @@ main (int argc, char **argv)
|
||||||
|
opts.rtl_bandwidth = 24;
|
||||||
|
// opts.rtl_gain_value = 36;
|
||||||
|
}
|
||||||
|
+ else if (optarg[0] == 'T') //Motorola Type II
|
||||||
|
+ {
|
||||||
|
+ opts.frame_dstar = 0;
|
||||||
|
+ opts.frame_x2tdma = 0;
|
||||||
|
+ opts.frame_p25p1 = 0;
|
||||||
|
+ opts.frame_p25p2 = 0;
|
||||||
|
+ opts.frame_nxdn48 = 0;
|
||||||
|
+ opts.frame_nxdn96 = 0;
|
||||||
|
+ opts.frame_dmr = 0;
|
||||||
|
+ opts.frame_dpmr = 0;
|
||||||
|
+ opts.frame_provoice = 0;
|
||||||
|
+ opts.frame_ysf = 0;
|
||||||
|
+ opts.frame_m17 = 0;
|
||||||
|
+ opts.frame_typeii = 1;
|
||||||
|
+ ////to maintain 10/4 sample per symbol rate, we need to run at 36000
|
||||||
|
+ //to hit TypeII's 3600 bps rating. BW values in rtl_fm are also modified
|
||||||
|
+ //similarly, but testing will need to be done, this MAY NOT be viable in DSD-FME.
|
||||||
|
+ opts.pulse_digi_rate_in = 36000;
|
||||||
|
+ opts.wav_sample_rate = 36000;
|
||||||
|
+ opts.mod_c4fm = 1;
|
||||||
|
+ opts.mod_qpsk = 0;
|
||||||
|
+ opts.mod_gfsk = 0; //might still be better as gfsk
|
||||||
|
+ state.rf_mod = 2;
|
||||||
|
+ opts.pulse_digi_rate_out = 8000;
|
||||||
|
+ opts.pulse_digi_out_channels = 1;
|
||||||
|
+ opts.dmr_stereo = 0;
|
||||||
|
+ opts.dmr_mono = 0;
|
||||||
|
+ state.dmr_stereo = 0;
|
||||||
|
+ // opts.setmod_bw = 12500;
|
||||||
|
+ sprintf (opts.output_name, "Type II");
|
||||||
|
+ // fprintf (stderr,"Setting symbol rate to 4800 / second\n");
|
||||||
|
+ fprintf (stderr,"Decoding Motorola Type II frames.\n");
|
||||||
|
+ }
|
||||||
|
else if (optarg[0] == '1')
|
||||||
|
{
|
||||||
|
opts.frame_dstar = 0;
|
||||||
|
diff --git a/src/rtl_sdr_fm.cpp b/src/rtl_sdr_fm.cpp
|
||||||
|
index ad04667..954c6dc 100644
|
||||||
|
--- a/src/rtl_sdr_fm.cpp
|
||||||
|
+++ b/src/rtl_sdr_fm.cpp
|
||||||
|
@@ -1025,6 +1025,7 @@ void rtlsdr_sighandler()
|
||||||
|
void open_rtlsdr_stream(dsd_opts *opts)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
+ if (opts->frame_typeii == 1) bandwidth_divisor = 36000;
|
||||||
|
rtl_bandwidth = opts->rtl_bandwidth * 1000; //reverted back to straight value
|
||||||
|
bandwidth_multiplier = (bandwidth_divisor / rtl_bandwidth);
|
||||||
|
volume_multiplier = 1; //moved to external handling to be more dynamic
|
||||||
|
diff --git a/src/typeII.c b/src/typeII.c
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..eb0cd56
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/typeII.c
|
||||||
|
@@ -0,0 +1,29 @@
|
||||||
|
+/*-------------------------------------------------------------------------------
|
||||||
|
+ * typeII.c
|
||||||
|
+ * Motorola Type II Decoder (WIP)
|
||||||
|
+ *
|
||||||
|
+ *
|
||||||
|
+ *
|
||||||
|
+ *
|
||||||
|
+ * LWVMOBILE
|
||||||
|
+ * 2024-03 DSD-FME Florida Man Edition
|
||||||
|
+ *-----------------------------------------------------------------------------*/
|
||||||
|
+
|
||||||
|
+#include "dsd.h"
|
||||||
|
+
|
||||||
|
+//Type II
|
||||||
|
+void type_ii(dsd_opts * opts, dsd_state * state)
|
||||||
|
+{
|
||||||
|
+ int i;
|
||||||
|
+ uint8_t t2[80]; memset(t2, 0, sizeof(t2));
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < 76; i++)
|
||||||
|
+ t2[i] = getDibit(opts, state);
|
||||||
|
+
|
||||||
|
+ //debug dump collected bits
|
||||||
|
+ fprintf (stderr, "Frame:");
|
||||||
|
+ for (i = 0; i < 10; i++)
|
||||||
|
+ fprintf (stderr, " %02X", (uint8_t)ConvertBitIntoBytes(&t2[i*8], 8));
|
||||||
|
+
|
||||||
|
+ fprintf (stderr, "\n");
|
||||||
|
+}
|
||||||
|
\ No newline at end of file
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
|
#
|
||||||
clear
|
clear
|
||||||
echo DSD-FME Digital Speech Decoder - Florida Man Edition
|
printf "DSD-FME Digital Speech Decoder - Florida Man Edition
|
||||||
echo Automatic Git Pull and Rebuild
|
Automatic Git Pull and Rebuild\n\n"
|
||||||
echo
|
|
||||||
sleep 1
|
sleep 1
|
||||||
##Open your clone folder##
|
##Open your clone folder##
|
||||||
git pull
|
git pull
|
||||||
@@ -10,7 +10,6 @@ sleep 2
|
|||||||
##cd into your build folder##
|
##cd into your build folder##
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
make -j `nproc`
|
make -j $(nproc)
|
||||||
sudo make install
|
sudo make install
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
|
|
||||||
|
|||||||
7
src/1.c
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* 1.c
|
||||||
|
* Placeholder File 1 -- This file is for temporary code storage
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
7
src/2.c
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* 2.c
|
||||||
|
* Placeholder File 2 -- This file is for temporary code storage
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
594
src/3.c
Normal file
@@ -0,0 +1,594 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* 3.c
|
||||||
|
* Placeholder File 3 -- This file is for temporary code storage
|
||||||
|
*
|
||||||
|
* Multiple 1/2 Rate Convolutional Decoders for NXDN/M17/YSF
|
||||||
|
* Used as a secondary decoder in case convolutional decoder fails (second opinion)
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
static const int PARITY[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1};
|
||||||
|
|
||||||
|
// trellis_1_2 encode: source is in bits, result in bits
|
||||||
|
void trellis_encode(uint8_t result[], const uint8_t source[], int result_len, int reg)
|
||||||
|
{
|
||||||
|
for (int i=0; i<result_len; i+=2) {
|
||||||
|
reg = (reg << 1) | source[i>>1];
|
||||||
|
result[i] = PARITY[reg & 0x19];
|
||||||
|
result[i+1] = PARITY[reg & 0x17];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// simplified trellis 2:1 decode; source and result in bits
|
||||||
|
// assumes that encoding was done with NTEST trailing zero bits
|
||||||
|
// result_len should be set to the actual number of data bits
|
||||||
|
// in the original unencoded message (excl. these trailing bits)
|
||||||
|
void trellis_decode(uint8_t result[], const uint8_t source[], int result_len)
|
||||||
|
{
|
||||||
|
int reg = 0;
|
||||||
|
int min_d;
|
||||||
|
int min_bt;
|
||||||
|
static const int NTEST = 4;
|
||||||
|
static const int NTESTC = 1 << NTEST;
|
||||||
|
uint8_t bt[NTEST];
|
||||||
|
uint8_t tt[NTEST*2];
|
||||||
|
int dstats[4];
|
||||||
|
int sum;
|
||||||
|
for (int p=0; p < 4; p++)
|
||||||
|
dstats[p] = 0;
|
||||||
|
for (int p=0; p < result_len; p++) {
|
||||||
|
for (int i=0; i<NTESTC; i++) {
|
||||||
|
bt[0] = (i&8)>>3;
|
||||||
|
bt[1] = (i&4)>>2;
|
||||||
|
bt[2] = (i&2)>>1;
|
||||||
|
bt[3] = (i&1);
|
||||||
|
trellis_encode(tt, bt, NTEST*2, reg);
|
||||||
|
sum=0;
|
||||||
|
for (int j=0; j<NTEST*2; j++) {
|
||||||
|
sum += tt[j] ^ source[p*2+j];
|
||||||
|
}
|
||||||
|
if (i == 0 || sum < min_d) {
|
||||||
|
min_d = sum;
|
||||||
|
min_bt = bt[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result[p] = min_bt;
|
||||||
|
reg = (reg << 1) | min_bt;
|
||||||
|
dstats[(min_d > 3) ? 3 : min_d] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug output
|
||||||
|
// fprintf (stderr, "\n stats\t%d %d %d %d\n", dstats[0], dstats[1], dstats[2], dstats[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Original Copyright/License
|
||||||
|
|
||||||
|
/* -*- c++ -*- */
|
||||||
|
/*
|
||||||
|
* NXDN Encoder/Decoder (C) Copyright 2019 Max H. Parke KA1RBI
|
||||||
|
*
|
||||||
|
* This file is part of OP25
|
||||||
|
*
|
||||||
|
* This 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.
|
||||||
|
*
|
||||||
|
* This software 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 this software; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Ripped from libM17
|
||||||
|
#define K 5 //constraint length
|
||||||
|
#define NUM_STATES (1 << (K - 1)) //number of states
|
||||||
|
|
||||||
|
static uint32_t prevMetrics[NUM_STATES];
|
||||||
|
static uint32_t currMetrics[NUM_STATES];
|
||||||
|
static uint32_t prevMetricsData[NUM_STATES];
|
||||||
|
static uint32_t currMetricsData[NUM_STATES];
|
||||||
|
static uint16_t viterbi_history[244];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode unpunctured convolutionally encoded data.
|
||||||
|
*
|
||||||
|
* @param out Destination array where decoded data is written.
|
||||||
|
* @param in Input data.
|
||||||
|
* @param len Input length in bits.
|
||||||
|
* @return Number of bit errors corrected.
|
||||||
|
*/
|
||||||
|
uint32_t viterbi_decode(uint8_t* out, const uint16_t* in, const uint16_t len)
|
||||||
|
{
|
||||||
|
if(len > 244*2)
|
||||||
|
fprintf(stderr, "Input size exceeds max history\n");
|
||||||
|
|
||||||
|
viterbi_reset();
|
||||||
|
|
||||||
|
size_t pos = 0;
|
||||||
|
for(size_t i = 0; i < len; i += 2)
|
||||||
|
{
|
||||||
|
uint16_t s0 = in[i];
|
||||||
|
uint16_t s1 = in[i + 1];
|
||||||
|
|
||||||
|
viterbi_decode_bit(s0, s1, pos);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
uint32_t err = viterbi_chainback(out, pos, len/2);
|
||||||
|
|
||||||
|
//debug
|
||||||
|
// fprintf (stderr, "\n vcb: \n");
|
||||||
|
// for (size_t i = 0; i < len; i++)
|
||||||
|
// fprintf (stderr, "%02X", out[i]);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode punctured convolutionally encoded data.
|
||||||
|
*
|
||||||
|
* @param out Destination array where decoded data is written.
|
||||||
|
* @param in Input data.
|
||||||
|
* @param punct Puncturing matrix.
|
||||||
|
* @param in_len Input data length.
|
||||||
|
* @param p_len Puncturing matrix length (entries).
|
||||||
|
* @return Number of bit errors corrected.
|
||||||
|
*/
|
||||||
|
uint32_t viterbi_decode_punctured(uint8_t* out, const uint16_t* in, const uint8_t* punct, const uint16_t in_len, const uint16_t p_len)
|
||||||
|
{
|
||||||
|
if(in_len > 244*2)
|
||||||
|
fprintf(stderr, "Input size exceeds max history\n");
|
||||||
|
|
||||||
|
uint16_t umsg[244*2]; //unpunctured message
|
||||||
|
uint8_t p=0; //puncturer matrix entry
|
||||||
|
uint16_t u=0; //bits count - unpunctured message
|
||||||
|
uint16_t i=0; //bits read from the input message
|
||||||
|
|
||||||
|
while(i<in_len)
|
||||||
|
{
|
||||||
|
if(punct[p])
|
||||||
|
{
|
||||||
|
umsg[u]=in[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
umsg[u]=0x7FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
u++;
|
||||||
|
p++;
|
||||||
|
p%=p_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug
|
||||||
|
// fprintf (stderr, " p: %d, u: %d; p_len: %d; len: %d;", p, u, p_len, (u-in_len)*0x7FFF);
|
||||||
|
|
||||||
|
return viterbi_decode(out, umsg, u) - (u-in_len)*0x7FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode one bit and update trellis.
|
||||||
|
*
|
||||||
|
* @param s0 Cost of the first symbol.
|
||||||
|
* @param s1 Cost of the second symbol.
|
||||||
|
* @param pos Bit position in history.
|
||||||
|
*/
|
||||||
|
void viterbi_decode_bit(uint16_t s0, uint16_t s1, const size_t pos)
|
||||||
|
{
|
||||||
|
static const uint16_t COST_TABLE_0[] = {0, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
|
||||||
|
static const uint16_t COST_TABLE_1[] = {0, 0xFFFF, 0xFFFF, 0, 0, 0xFFFF, 0xFFFF, 0};
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < NUM_STATES/2; i++)
|
||||||
|
{
|
||||||
|
uint32_t metric = q_abs_diff(COST_TABLE_0[i], s0)
|
||||||
|
+ q_abs_diff(COST_TABLE_1[i], s1);
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t m0 = prevMetrics[i] + metric;
|
||||||
|
uint32_t m1 = prevMetrics[i + NUM_STATES/2] + (0x1FFFE - metric);
|
||||||
|
|
||||||
|
uint32_t m2 = prevMetrics[i] + (0x1FFFE - metric);
|
||||||
|
uint32_t m3 = prevMetrics[i + NUM_STATES/2] + metric;
|
||||||
|
|
||||||
|
uint8_t i0 = 2 * i;
|
||||||
|
uint8_t i1 = i0 + 1;
|
||||||
|
|
||||||
|
if(m0 >= m1)
|
||||||
|
{
|
||||||
|
viterbi_history[pos]|=(1<<i0);
|
||||||
|
currMetrics[i0] = m1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
viterbi_history[pos]&=~(1<<i0);
|
||||||
|
currMetrics[i0] = m0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m2 >= m3)
|
||||||
|
{
|
||||||
|
viterbi_history[pos]|=(1<<i1);
|
||||||
|
currMetrics[i1] = m3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
viterbi_history[pos]&=~(1<<i1);
|
||||||
|
currMetrics[i1] = m2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//swap
|
||||||
|
uint32_t tmp[NUM_STATES];
|
||||||
|
for(uint8_t i=0; i<NUM_STATES; i++)
|
||||||
|
{
|
||||||
|
tmp[i]=currMetrics[i];
|
||||||
|
}
|
||||||
|
for(uint8_t i=0; i<NUM_STATES; i++)
|
||||||
|
{
|
||||||
|
currMetrics[i]=prevMetrics[i];
|
||||||
|
prevMetrics[i]=tmp[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief History chainback to obtain final byte array.
|
||||||
|
*
|
||||||
|
* @param out Destination byte array for decoded data.
|
||||||
|
* @param pos Starting position for the chainback.
|
||||||
|
* @param len Length of the output in bits.
|
||||||
|
* @return Minimum Viterbi cost at the end of the decode sequence.
|
||||||
|
*/
|
||||||
|
uint32_t viterbi_chainback(uint8_t* out, size_t pos, uint16_t len)
|
||||||
|
{
|
||||||
|
uint8_t state = 0;
|
||||||
|
size_t bitPos = len+4;
|
||||||
|
|
||||||
|
memset(out, 0, (len-1)/8+1);
|
||||||
|
|
||||||
|
while(pos > 0)
|
||||||
|
{
|
||||||
|
bitPos--;
|
||||||
|
pos--;
|
||||||
|
uint16_t bit = viterbi_history[pos]&((1<<(state>>4)));
|
||||||
|
state >>= 1;
|
||||||
|
if(bit)
|
||||||
|
{
|
||||||
|
state |= 0x80;
|
||||||
|
out[bitPos/8]|=1<<(7-(bitPos%8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t cost = prevMetrics[0];
|
||||||
|
|
||||||
|
for(size_t i = 0; i < NUM_STATES; i++)
|
||||||
|
{
|
||||||
|
uint32_t m = prevMetrics[i];
|
||||||
|
if(m < cost) cost = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug
|
||||||
|
// fprintf (stderr, "\n cb: \n");
|
||||||
|
// for (size_t i = 0; i < len; i++)
|
||||||
|
// fprintf (stderr, "%02X", out[i]);
|
||||||
|
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the decoder state. No args.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void viterbi_reset(void)
|
||||||
|
{
|
||||||
|
memset((uint8_t*)viterbi_history, 0, 2*244);
|
||||||
|
memset((uint8_t*)currMetrics, 0, 4*NUM_STATES);
|
||||||
|
memset((uint8_t*)prevMetrics, 0, 4*NUM_STATES);
|
||||||
|
memset((uint8_t*)currMetricsData, 0, 4*NUM_STATES);
|
||||||
|
memset((uint8_t*)prevMetricsData, 0, 4*NUM_STATES);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t q_abs_diff(const uint16_t v1, const uint16_t v2)
|
||||||
|
{
|
||||||
|
if(v2 > v1) return v2 - v1;
|
||||||
|
return v1 - v2;
|
||||||
|
}
|
||||||
|
|
||||||
|
//original sources
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// M17 C library - decode/viterbi.c
|
||||||
|
//
|
||||||
|
// This file contains:
|
||||||
|
// - the Viterbi decoder
|
||||||
|
//
|
||||||
|
// Wojciech Kaczmarski, SP5WWP
|
||||||
|
// M17 Project, 29 December 2023
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// M17 C library - math/math.c
|
||||||
|
//
|
||||||
|
// This file contains:
|
||||||
|
// - absolute difference value
|
||||||
|
// - Euclidean norm (L2) calculation for n-dimensional vectors (float)
|
||||||
|
// - soft-valued arrays to integer conversion (and vice-versa)
|
||||||
|
// - fixed-valued multiplication and division
|
||||||
|
//
|
||||||
|
// Wojciech Kaczmarski, SP5WWP
|
||||||
|
// M17 Project, 29 December 2023
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
//audio filter stuff sourced from: https://github.com/NedSimao/FilteringLibrary
|
||||||
|
//no license / information provided in source code
|
||||||
|
#define PI 3.141592653
|
||||||
|
|
||||||
|
//do we need this for test input sthit?
|
||||||
|
// #define PLOT_POINTS 100
|
||||||
|
// #define SAMPLE_TIME_S 0.001
|
||||||
|
// #define CUTOFFFREQ_HZ 50
|
||||||
|
// #define sine_freq 40
|
||||||
|
// #define sine_freq_1 500
|
||||||
|
// #define HFC 50
|
||||||
|
// #define LFC 100
|
||||||
|
|
||||||
|
|
||||||
|
void LPFilter_Init(LPFilter *filter, float cutoffFreqHz, float sampleTimeS)
|
||||||
|
{
|
||||||
|
|
||||||
|
float RC=0.0;
|
||||||
|
RC=1.0/(2*PI*cutoffFreqHz);
|
||||||
|
filter->coef[0]=sampleTimeS/(sampleTimeS+RC);
|
||||||
|
filter->coef[1]=RC/(sampleTimeS+RC);
|
||||||
|
|
||||||
|
filter->v_out[0]=0.0;
|
||||||
|
filter->v_out[1]=0.0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float LPFilter_Update(LPFilter *filter, float v_in)
|
||||||
|
{
|
||||||
|
|
||||||
|
filter->v_out[1]=filter->v_out[0];
|
||||||
|
filter->v_out[0]=(filter->coef[0]*v_in) + (filter->coef[1]*filter->v_out[1]);
|
||||||
|
|
||||||
|
return (filter->v_out[0]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* HIGH PASS FILTER
|
||||||
|
********************************************************************************************************/
|
||||||
|
void HPFilter_Init(HPFilter *filter, float cutoffFreqHz, float sampleTimeS)
|
||||||
|
{
|
||||||
|
|
||||||
|
float RC=0.0;
|
||||||
|
RC=1.0/(2*PI*cutoffFreqHz);
|
||||||
|
|
||||||
|
filter->coef=RC/(sampleTimeS+RC);
|
||||||
|
|
||||||
|
filter->v_in[0]=0.0;
|
||||||
|
filter->v_in[1]=0.0;
|
||||||
|
|
||||||
|
filter->v_out[0]=0.0;
|
||||||
|
filter->v_out[1]=0.0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float HPFilter_Update(HPFilter *filter, float v_in)
|
||||||
|
{
|
||||||
|
|
||||||
|
filter->v_in[1]=filter->v_in[0];
|
||||||
|
filter->v_in[0]=v_in;
|
||||||
|
|
||||||
|
filter->v_out[1]=filter->v_out[0];
|
||||||
|
filter->v_out[0]=filter->coef * (filter->v_in[0] - filter->v_in[1]+filter->v_out[1]);
|
||||||
|
|
||||||
|
return (filter->v_out[0]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* BAND PASS FILTER
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
void PBFilter_Init(PBFilter *filter, float HPF_cutoffFreqHz, float LPF_cutoffFreqHz, float sampleTimeS)
|
||||||
|
{
|
||||||
|
|
||||||
|
LPFilter_Init(&filter->lpf, LPF_cutoffFreqHz, sampleTimeS);
|
||||||
|
HPFilter_Init(&filter->hpf, HPF_cutoffFreqHz, sampleTimeS);
|
||||||
|
|
||||||
|
filter->out_in=0.0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float PBFilter_Update(PBFilter *filter, float v_in)
|
||||||
|
{
|
||||||
|
|
||||||
|
filter->out_in=HPFilter_Update(&filter->hpf, v_in);
|
||||||
|
|
||||||
|
filter->out_in=LPFilter_Update(&filter->lpf, filter->out_in);
|
||||||
|
|
||||||
|
return (filter->out_in);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* NOTCH FILTER
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
void NOTCHFilter_Init(NOTCHFilter *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS)
|
||||||
|
{
|
||||||
|
|
||||||
|
//filter frequency to angular (rad/s)
|
||||||
|
float w0_rps=2.0 * PI *centerFreqHz;
|
||||||
|
float ww_rps=2.0 * PI *notchWidthHz;
|
||||||
|
|
||||||
|
//pre warp center frequency
|
||||||
|
float w0_pw_rps=(2.0/sampleTimeS) * tanf(0.5 * w0_rps * sampleTimeS);
|
||||||
|
|
||||||
|
//computing filter coefficients
|
||||||
|
|
||||||
|
filter->alpha=4.0 + w0_rps*w0_pw_rps*sampleTimeS*sampleTimeS;
|
||||||
|
filter->beta=2.0*ww_rps*sampleTimeS;
|
||||||
|
|
||||||
|
//clearing input and output buffers
|
||||||
|
|
||||||
|
for (uint8_t n=0; n<3; n++)
|
||||||
|
{
|
||||||
|
filter->vin[n]=0;
|
||||||
|
filter->vout[n]=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float NOTCHFilter_Update(NOTCHFilter *filter, float vin)
|
||||||
|
{
|
||||||
|
|
||||||
|
//shifting samples
|
||||||
|
filter->vin[2]=filter->vin[1];
|
||||||
|
filter->vin[1]=filter->vin[0];
|
||||||
|
|
||||||
|
filter->vout[2]=filter->vout[1];
|
||||||
|
filter->vout[1]=filter->vout[0];
|
||||||
|
|
||||||
|
filter->vin[0]=vin;
|
||||||
|
|
||||||
|
//compute new output
|
||||||
|
filter->vout[0]=(filter->alpha*filter->vin[0] + 2.0 *(filter->alpha -8.0)*filter->vin[1] + filter->alpha*filter->vin[2]
|
||||||
|
-(2.0f*(filter->alpha-8.0)*filter->vout[1]+(filter->alpha-filter->beta)*filter->vout[2]))/(filter->alpha+filter->beta);
|
||||||
|
|
||||||
|
|
||||||
|
return (filter->vout[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_audio_filters (dsd_state * state)
|
||||||
|
{
|
||||||
|
//still not sure if this is even correct or not, but 48k sounds good now
|
||||||
|
LPFilter_Init(&state->RCFilter, 960, (float)1/(float)48000);
|
||||||
|
HPFilter_Init(&state->HRCFilter, 960, (float)1/(float)48000);
|
||||||
|
|
||||||
|
//left and right variants for stereo output testing on digital voice samples
|
||||||
|
LPFilter_Init(&state->RCFilterL, 960, (float)1/(float)16000);
|
||||||
|
HPFilter_Init(&state->HRCFilterL, 960, (float)1/(float)16000);
|
||||||
|
LPFilter_Init(&state->RCFilterR, 960, (float)1/(float)16000);
|
||||||
|
HPFilter_Init(&state->HRCFilterR, 960, (float)1/(float)16000);
|
||||||
|
|
||||||
|
//PBFilter_Init(PBFilter *filter, float HPF_cutoffFreqHz, float LPF_cutoffFreqHz, float sampleTimeS);
|
||||||
|
//NOTCHFilter_Init(NOTCHFilter *filter, float centerFreqHz, float notchWidthHz, float sampleTimeS);
|
||||||
|
|
||||||
|
//NOTE: PBFilter_init also inits a LPF and HPF, but on another set of filters, might be worth
|
||||||
|
//testing just using the PBFilter by itself and see how it does without the hpf used
|
||||||
|
|
||||||
|
//passband filter working (seems to be), notch filter unsure which values to use, doesn't have any appreciable affect when used as is
|
||||||
|
PBFilter_Init(&state->PBF, 8000, 12000, (float)1/(float)1536000); //RTL Sampling at 1536000 S/s.
|
||||||
|
NOTCHFilter_Init(&state->NF, 1000, 4000, (float)1/(float)1536000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//FUNCTIONS for handing use of above filters
|
||||||
|
|
||||||
|
//lpf
|
||||||
|
void lpf(dsd_state * state, short * input, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
// fprintf (stderr, "\n in: %05d", input[i]);
|
||||||
|
input[i] = LPFilter_Update(&state->RCFilter, input[i]);
|
||||||
|
// fprintf (stderr, "\n out: %05d", input[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//hpf
|
||||||
|
void hpf(dsd_state * state, short * input, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
// fprintf (stderr, "\n in: %05d", input[i]);
|
||||||
|
input[i] = HPFilter_Update(&state->HRCFilter, input[i]);
|
||||||
|
// fprintf (stderr, "\n out: %05d", input[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//hpf digital left
|
||||||
|
void hpf_dL(dsd_state * state, short * input, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
// fprintf (stderr, "\n in: %05d", input[i]);
|
||||||
|
input[i] = HPFilter_Update(&state->HRCFilterL, input[i]);
|
||||||
|
// fprintf (stderr, "\n out: %05d", input[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//hpf digital right
|
||||||
|
void hpf_dR(dsd_state * state, short * input, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
// fprintf (stderr, "\n in: %05d", input[i]);
|
||||||
|
input[i] = HPFilter_Update(&state->HRCFilterR, input[i]);
|
||||||
|
// fprintf (stderr, "\n out: %05d", input[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//nf
|
||||||
|
void nf(dsd_state * state, short * input, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
// fprintf (stderr, "\n in: %05d", input[i]);
|
||||||
|
input[i] = NOTCHFilter_Update(&state->NF, input[i]);
|
||||||
|
// fprintf (stderr, "\n out: %05d", input[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//pbf
|
||||||
|
void pbf(dsd_state * state, short * input, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
// fprintf (stderr, "\n in: %05d", input[i]);
|
||||||
|
input[i] = PBFilter_Update(&state->PBF, input[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Generic RMS function derived from RTL_FM (RTL_SDR) RMS code (doesnt' really work correctly outside of RTL)
|
||||||
|
long int raw_rms(int16_t *samples, int len, int step) //use samplespersymbol as len
|
||||||
|
{
|
||||||
|
|
||||||
|
int i;
|
||||||
|
long int rms;
|
||||||
|
long p, t, s;
|
||||||
|
double dc, err;
|
||||||
|
|
||||||
|
p = t = 0L;
|
||||||
|
for (i=0; i<len; i+=step) {
|
||||||
|
s = (long)samples[i];
|
||||||
|
t += s;
|
||||||
|
p += s * s;
|
||||||
|
}
|
||||||
|
/* correct for dc offset in squares */
|
||||||
|
dc = (double)(t*step) / (double)len;
|
||||||
|
err = t * 2 * dc - dc * dc * len;
|
||||||
|
|
||||||
|
rms = (long int)sqrt((p-err) / len);
|
||||||
|
if (rms < 0) rms = 150;
|
||||||
|
return rms;
|
||||||
|
}
|
||||||
7
src/4.c
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* 4.c
|
||||||
|
* Placeholder File 4 -- This file is for temporary code storage
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
7
src/5.c
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* 5.c
|
||||||
|
* Placeholder File 5 -- This file is for temporary code storage
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
62
src/bptc.c
@@ -134,6 +134,13 @@ const uint8_t DeInterleaveReverseChannelBptc[32] =
|
|||||||
24, 9, 26, 11, 28, 13, 30, 15
|
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 ----------------------------------------------------------------*/
|
/* Functions ----------------------------------------------------------------*/
|
||||||
|
|
||||||
@@ -192,18 +199,10 @@ uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* R(0) to R(2) may be used to transport some
|
|
||||||
* Restricted Access System (RAS) information,
|
|
||||||
* So save these three bytes before reset all of them
|
|
||||||
* See patent US 2013/0288643 A1 */
|
|
||||||
R[0] = DataMatrix[0][2]; /* Save R(0) */
|
|
||||||
R[1] = DataMatrix[0][1]; /* Save R(1) */
|
|
||||||
R[2] = DataMatrix[0][0]; /* Save R(2) */
|
|
||||||
|
|
||||||
/* Set to 0 R(0) to R(2) - See DMR standard chapter B1.1 BPTC (196,96) */
|
/* 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][0] = 0; /* R(2) */
|
||||||
DataMatrix[0][1] = 0; /* R(1) */
|
// DataMatrix[0][1] = 0; /* R(1) */
|
||||||
DataMatrix[0][2] = 0; /* R(0) */
|
// DataMatrix[0][2] = 0; /* R(0) */
|
||||||
|
|
||||||
/* Init the Hamming (15,11,3) library
|
/* Init the Hamming (15,11,3) library
|
||||||
* Not needed because it has already been done
|
* Not needed because it has already been done
|
||||||
@@ -356,6 +355,14 @@ uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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 the number of irrecoverable Hamming errors */
|
||||||
return HammingIrrecoverableErrorNb;
|
return HammingIrrecoverableErrorNb;
|
||||||
} /* End BPTC_196x96_Extract_Data() */
|
} /* End BPTC_196x96_Extract_Data() */
|
||||||
@@ -498,33 +505,38 @@ uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDat
|
|||||||
* DMRDataExtracted[11..15] = Hamming data
|
* DMRDataExtracted[11..15] = Hamming data
|
||||||
* DMRDataExtracted[16..31] = 16 bits of odd parity (to be checked)
|
* 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
|
* @return 0 = All is OK
|
||||||
* Other = Error - Irrecoverable Hamming parity check error number
|
* Other = Error - Irrecoverable Hamming parity check error number
|
||||||
*/
|
*/
|
||||||
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32])
|
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32], uint32_t ParityCheckTypeOdd)
|
||||||
{
|
{
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
uint8_t DataMatrix[32];
|
uint8_t DataMatrix[32];
|
||||||
uint8_t LineUncorrected[16];
|
uint8_t LineUncorrected[16];
|
||||||
uint8_t LineCorrected[11];
|
uint8_t LineCorrected[11];
|
||||||
uint32_t HammingIrrecoverableErrorNb = 0;
|
uint32_t HammingIrrecoverableErrorNb = 0;
|
||||||
uint32_t ParityCheckErrorNb = 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++)
|
for(i = 0; i < 32; i++)
|
||||||
{
|
{
|
||||||
DataMatrix[DeInterleaveReverseChannelBptc[i]] = InputInterleavedData[i] & 1;
|
DataMatrix[DeInterleaveReverseChannelBptcPlacement[DeInterleaveReverseChannelBptc[i]]] = InputInterleavedData[i] & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the entire input buffer to the output buffer */
|
/* Copy the entire input buffer to the output buffer */
|
||||||
for(i = 0; i < 32; i++)
|
for(i = 0; i < 32; i++)
|
||||||
{
|
{
|
||||||
DMRDataExtracted[i] = DataMatrix[i];
|
DMRDataExtracted[i] = DataMatrix[i] & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare the line to be corrected */
|
/* Prepare the line to be corrected */
|
||||||
for(i = 0; i < 16; i++)
|
for(i = 0; i < 16; i++)
|
||||||
{
|
{
|
||||||
LineUncorrected[i] = DataMatrix[i];
|
LineUncorrected[i] = DataMatrix[i] & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Apply Hamming (16,11,4) code correction */
|
/* Apply Hamming (16,11,4) code correction */
|
||||||
@@ -536,7 +548,7 @@ uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDat
|
|||||||
/* Copy the corrected content to the output buffer */
|
/* Copy the corrected content to the output buffer */
|
||||||
for(i = 0; i < 11; i++)
|
for(i = 0; i < 11; i++)
|
||||||
{
|
{
|
||||||
DMRDataExtracted[i] = LineCorrected[i];
|
DMRDataExtracted[i] = LineCorrected[i] & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check Parity bits */
|
/* Check Parity bits */
|
||||||
@@ -544,14 +556,24 @@ uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDat
|
|||||||
{
|
{
|
||||||
/* Odd parity ==> If data = 1 then parity = 0
|
/* Odd parity ==> If data = 1 then parity = 0
|
||||||
* If data = 0 then parity = 1 */
|
* If data = 0 then parity = 1 */
|
||||||
if(DMRDataExtracted[i] == DMRDataExtracted[i + 16]) ParityCheckErrorNb++;
|
if((DMRDataExtracted[i] & 1) == (DMRDataExtracted[i + 16] & 1)) ParityCheckOddErrorNb++;
|
||||||
|
if((DMRDataExtracted[i] & 1) != (DMRDataExtracted[i + 16] & 1)) ParityCheckEvenErrorNb++;
|
||||||
}
|
}
|
||||||
|
|
||||||
//fprintf(stderr, "Hamming ERR=%u ; Parity ERR=%u", HammingIrrecoverableErrorNb, ParityCheckErrorNb);
|
//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 +
|
/* Return the number of irrecoverable Hamming errors +
|
||||||
* the number of parity check error */
|
* the number of parity check error */
|
||||||
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
|
if(ParityCheckTypeOdd) return (HammingIrrecoverableErrorNb + ParityCheckOddErrorNb);
|
||||||
|
else return (HammingIrrecoverableErrorNb + ParityCheckEvenErrorNb);
|
||||||
|
|
||||||
} /* End BPTC_16x2_Extract_Data() */
|
} /* End BPTC_16x2_Extract_Data() */
|
||||||
|
|
||||||
|
|||||||
214
src/dmr_34.c
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dmr_34.c
|
||||||
|
* DMR (and P25) 3/4 Rate Simple Trellis Decoder
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2023-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
uint8_t interleave[98] = {
|
||||||
|
0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97,
|
||||||
|
2, 3, 10, 11, 18, 19, 26, 27, 34, 35, 42, 43, 50, 51, 58, 59, 66, 67, 74, 75, 82, 83, 90, 91,
|
||||||
|
4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93,
|
||||||
|
6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95};
|
||||||
|
|
||||||
|
//this is a convertion table for converting the dibit pairs into constellation points
|
||||||
|
uint8_t constellation_map[16] = {
|
||||||
|
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
|
||||||
|
};
|
||||||
|
|
||||||
|
//digitized dibit to OTA symbol conversion for reference
|
||||||
|
//0 = +1; 1 = +3;
|
||||||
|
//2 = -1; 3 = -3;
|
||||||
|
|
||||||
|
//finite state machine values
|
||||||
|
uint8_t fsm[64] = {
|
||||||
|
0, 8, 4, 12, 2, 10, 6, 14,
|
||||||
|
4, 12, 2, 10, 6, 14, 0, 8,
|
||||||
|
1, 9, 5, 13, 3, 11, 7, 15,
|
||||||
|
5, 13, 3, 11, 7, 15, 1, 9,
|
||||||
|
3, 11, 7, 15, 1, 9, 5, 13,
|
||||||
|
7, 15, 1, 9, 5, 13, 3, 11,
|
||||||
|
2, 10, 6, 14, 0, 8, 4, 12,
|
||||||
|
6, 14, 0, 8, 4, 12, 2, 10};
|
||||||
|
|
||||||
|
//attempt to find the surviving path, or the 'best' path available (most positions gained)
|
||||||
|
uint8_t fix_34(uint8_t * p, uint8_t state, int position)
|
||||||
|
{
|
||||||
|
int i, j, k, counter, best_p, best_v, survivors;
|
||||||
|
uint8_t temp_s, tri, t;
|
||||||
|
|
||||||
|
//status of surviving paths -- debug
|
||||||
|
int s[8];
|
||||||
|
memset (s, 0, 8*sizeof(int));
|
||||||
|
|
||||||
|
//assign all potentially correct points to temporary storage
|
||||||
|
uint8_t temp_p[8];
|
||||||
|
temp_p[0] = (p[position] ^ 1) & 0xF;
|
||||||
|
temp_p[1] = (p[position] ^ 3) & 0xF;
|
||||||
|
temp_p[2] = (p[position] ^ 5) & 0xF;
|
||||||
|
temp_p[3] = (p[position] ^ 7) & 0xF;
|
||||||
|
temp_p[4] = (p[position] ^ 9) & 0xF;
|
||||||
|
temp_p[5] = (p[position] ^ 11) & 0xF;
|
||||||
|
temp_p[6] = (p[position] ^ 13) & 0xF;
|
||||||
|
temp_p[7] = (p[position] ^ 15) & 0xF;
|
||||||
|
|
||||||
|
best_p = 0; //best position
|
||||||
|
best_v = 0; //best path value
|
||||||
|
|
||||||
|
for (k = 0; k < 8; k++)
|
||||||
|
{
|
||||||
|
temp_s = state;
|
||||||
|
counter = 0;
|
||||||
|
tri = 0;
|
||||||
|
for (i = position; i < 49; i++)
|
||||||
|
{
|
||||||
|
//assign temp t either as our temp_p, or next point of i
|
||||||
|
if (i == position) t = temp_p[k];
|
||||||
|
else t = p[i];
|
||||||
|
|
||||||
|
if (tri != 0xFF) //while the path survives
|
||||||
|
{
|
||||||
|
tri = 0xFF;
|
||||||
|
for (j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
if ( fsm[(temp_s*8)+j] == t )
|
||||||
|
{
|
||||||
|
//return our tribit value and state for the next point
|
||||||
|
tri = temp_s = (uint8_t)j;
|
||||||
|
counter++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter > best_p)
|
||||||
|
{
|
||||||
|
best_p = counter;
|
||||||
|
best_v = k; //if we make it further on current path, assign as best path
|
||||||
|
}
|
||||||
|
|
||||||
|
// surviving path -- made it to 49
|
||||||
|
if (i == 48) s[k] = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//NOTE: If there are no surviving paths, then that means there is another bit/point err
|
||||||
|
//Ideally, at the end of a single or multiple fixes, there should only be one survivor, but in practice,
|
||||||
|
//I've seen up to 4 surviving paths and somehow, get a good CRC and a good LRRP or LOCN decode
|
||||||
|
//multiple survivors seems common when the starting err position is very close to 49
|
||||||
|
|
||||||
|
//tally number of surviving paths
|
||||||
|
survivors = 0;
|
||||||
|
for (k = 0; k < 8; k++)
|
||||||
|
{
|
||||||
|
if (s[k] == 1) survivors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug
|
||||||
|
// fprintf (stderr, "START: %d; BEST_P: %d; BEST_V: %d; Survivors: %d; Point: %d; ", position, best_p, best_v, survivors, temp_p[best_v]);
|
||||||
|
|
||||||
|
return temp_p[best_v]; //return the point value of the best path's starting point value
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t dmr_34(uint8_t * input, uint8_t treturn[18])
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
uint32_t irr_err = 0; //irrecoverable errors
|
||||||
|
|
||||||
|
uint8_t deinterleaved_dibits[98];
|
||||||
|
memset (deinterleaved_dibits, 0, sizeof(deinterleaved_dibits));
|
||||||
|
|
||||||
|
//deinterleave our input dibits
|
||||||
|
for (i = 0; i < 98; i++)
|
||||||
|
deinterleaved_dibits[interleave[i]] = input[i];
|
||||||
|
|
||||||
|
//pack the input into nibbles (dibit pairs)
|
||||||
|
uint8_t nibs[49];
|
||||||
|
memset (nibs, 0, sizeof(nibs));
|
||||||
|
|
||||||
|
for (i = 0; i < 49; i++)
|
||||||
|
nibs[i] = (deinterleaved_dibits[i*2+0] << 2) | (deinterleaved_dibits[i*2+1] << 0);
|
||||||
|
|
||||||
|
//convert our dibit pairs into constellation point values
|
||||||
|
uint8_t point[49];
|
||||||
|
memset (point, 0xFF, sizeof(point));
|
||||||
|
|
||||||
|
for (i = 0; i < 49; i++)
|
||||||
|
point[i] = constellation_map[nibs[i]];
|
||||||
|
|
||||||
|
//debug view points
|
||||||
|
// fprintf (stderr, "\n P =");
|
||||||
|
// for (i = 0; i < 49; i++)
|
||||||
|
// fprintf (stderr, " %02d", point[i]);
|
||||||
|
|
||||||
|
//convert constellation points into tribit values using the FSM
|
||||||
|
uint8_t state = 0;
|
||||||
|
uint32_t tribits[49];
|
||||||
|
memset (tribits, 0xF, sizeof(tribits));
|
||||||
|
|
||||||
|
for (i = 0; i < 49; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
for (j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
if ( fsm[(state*8)+j] == point[i] )
|
||||||
|
{
|
||||||
|
//return our tribit value and state for the next point
|
||||||
|
tribits[i] = state = (uint8_t)j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if tribit value is greater than 7, then we have a decoding error
|
||||||
|
if (tribits[i] > 7)
|
||||||
|
{
|
||||||
|
irr_err++; //tally number of errors
|
||||||
|
|
||||||
|
//debug point, position of error, and state value
|
||||||
|
// fprintf (stderr, "\n P: %d, %d:%d; ", point[i], i, state);
|
||||||
|
|
||||||
|
//attempt to fix point by looking for the best path from here
|
||||||
|
point[i] = fix_34(point, state, i);
|
||||||
|
|
||||||
|
//Make a hard decision and flip point to fit in current state
|
||||||
|
// point[i] ^= 7; //lucky number 7 (0111) //fallback
|
||||||
|
|
||||||
|
//decrement one and resume decoding
|
||||||
|
i--;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug view tribits/states
|
||||||
|
// fprintf (stderr, "\n T =");
|
||||||
|
// for (i = 0; i < 49; i++)
|
||||||
|
// fprintf (stderr, " %02d", tribits[i]);
|
||||||
|
|
||||||
|
//convert tribits into a return payload
|
||||||
|
uint32_t temp = 0;
|
||||||
|
|
||||||
|
//break into chunks of 24 bit values and shuffle into 8-bit (byte) treturn values
|
||||||
|
for (i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
temp = (tribits[(i*8)+0] << 21) + (tribits[(i*8)+1] << 18) + (tribits[(i*8)+2] << 15) + (tribits[(i*8)+3] << 12) +
|
||||||
|
(tribits[(i*8)+4] << 9) + (tribits[(i*8)+5] << 6) + (tribits[(i*8)+6] << 3) + (tribits[(i*8)+7] << 0);
|
||||||
|
|
||||||
|
treturn[(i*3)+0] = (temp >> 16) & 0xFF;
|
||||||
|
treturn[(i*3)+1] = (temp >> 8) & 0xFF;
|
||||||
|
treturn[(i*3)+2] = (temp >> 0) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
//trellis point/state err tally
|
||||||
|
// if (irr_err != 0)
|
||||||
|
// fprintf (stderr, " P_ERR = %d", irr_err);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
1190
src/dmr_block.c
488
src/dmr_bs.c
@@ -13,9 +13,9 @@
|
|||||||
//processing voice and/or data on both BS slots (channels) simultaneously
|
//processing voice and/or data on both BS slots (channels) simultaneously
|
||||||
void dmrBS (dsd_opts * opts, dsd_state * state)
|
void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
int i, j, k, l, dibit;
|
char * timestr = NULL;
|
||||||
int *dibit_p;
|
|
||||||
|
|
||||||
|
int i, dibit;
|
||||||
char ambe_fr[4][24];
|
char ambe_fr[4][24];
|
||||||
char ambe_fr2[4][24];
|
char ambe_fr2[4][24];
|
||||||
char ambe_fr3[4][24];
|
char ambe_fr3[4][24];
|
||||||
@@ -25,44 +25,37 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
char redundancyB[36];
|
char redundancyB[36];
|
||||||
|
|
||||||
//memcpy of ambe_fr for late entry
|
//memcpy of ambe_fr for late entry
|
||||||
char m1[4][24];
|
uint8_t m1[4][24];
|
||||||
char m2[4][24];
|
uint8_t m2[4][24];
|
||||||
char m3[4][24];
|
uint8_t m3[4][24];
|
||||||
|
|
||||||
const int *w, *x, *y, *z;
|
const int *w, *x, *y, *z;
|
||||||
char sync[25];
|
char sync[25];
|
||||||
char syncdata[48];
|
uint8_t syncdata[48];
|
||||||
char EmbeddedSignalling[16];
|
memset (syncdata, 0, sizeof(syncdata));
|
||||||
|
|
||||||
|
uint8_t emb_pdu[16];
|
||||||
|
memset (emb_pdu, 0, sizeof(emb_pdu));
|
||||||
|
|
||||||
uint8_t emb_ok = 0;
|
uint8_t emb_ok = 0;
|
||||||
uint8_t tact_okay = 0;
|
uint8_t tact_okay = 0;
|
||||||
uint8_t cach_err = 0;
|
uint8_t cach_err = 0;
|
||||||
|
UNUSED(cach_err);
|
||||||
|
|
||||||
uint8_t internalslot;
|
uint8_t internalslot;
|
||||||
uint8_t vc1;
|
uint8_t vc1;
|
||||||
uint8_t vc2;
|
uint8_t vc2;
|
||||||
|
|
||||||
|
//assign as nonsensical numbers
|
||||||
|
uint8_t cc = 25;
|
||||||
|
uint8_t power = 9; //power and pre-emption indicator
|
||||||
|
uint8_t lcss = 9;
|
||||||
|
UNUSED2(cc, lcss);
|
||||||
|
|
||||||
//would be ideal to grab all dibits and break them into bits to pass to new data handler?
|
//would be ideal to grab all dibits and break them into bits to pass to new data handler?
|
||||||
uint8_t dummy_bits[196];
|
uint8_t dummy_bits[196];
|
||||||
memset (dummy_bits, 0, sizeof(dummy_bits));
|
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
|
//Init slot lights
|
||||||
sprintf (state->slot1light, " slot1 ");
|
sprintf (state->slot1light, " slot1 ");
|
||||||
sprintf (state->slot2light, " slot2 ");
|
sprintf (state->slot2light, " slot2 ");
|
||||||
@@ -70,14 +63,24 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
//Init the color code status
|
//Init the color code status
|
||||||
state->color_code_ok = 0;
|
state->color_code_ok = 0;
|
||||||
|
|
||||||
vc1 = 2;
|
//if coming from the bootsrap, then the slot will still be assigned the last value
|
||||||
vc2 = 2;
|
//we want to set only that vc value to 2, the other to 7
|
||||||
|
if (state->currentslot == 0)
|
||||||
|
{
|
||||||
|
vc1 = 2;
|
||||||
|
vc2 = 7;
|
||||||
|
}
|
||||||
|
if (state->currentslot == 1)
|
||||||
|
{
|
||||||
|
vc1 = 7;
|
||||||
|
vc2 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
short int loop = 1;
|
short int loop = 1;
|
||||||
short int skipcount = 0;
|
short int skipcount = 0;
|
||||||
|
|
||||||
//cach
|
//cach
|
||||||
char cachdata[25];
|
uint8_t cachdata[25];
|
||||||
int cachInterleave[24] =
|
int cachInterleave[24] =
|
||||||
{0, 7, 8, 9, 1, 10,
|
{0, 7, 8, 9, 1, 10,
|
||||||
11, 12, 2, 13, 14,
|
11, 12, 2, 13, 14,
|
||||||
@@ -91,6 +94,19 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
//Run Loop while the getting is good
|
//Run Loop while the getting is good
|
||||||
while (loop == 1) {
|
while (loop == 1) {
|
||||||
|
|
||||||
|
if (exitflag == 1)
|
||||||
|
{
|
||||||
|
cleanupAndExit (opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
timestr = getTimeC();
|
||||||
|
|
||||||
|
memset (ambe_fr, 0, sizeof(ambe_fr));
|
||||||
|
memset (ambe_fr2, 0, sizeof(ambe_fr2));
|
||||||
|
memset (ambe_fr3, 0, sizeof(ambe_fr3));
|
||||||
|
memset (emb_pdu, 0, sizeof(emb_pdu));
|
||||||
|
memset (syncdata, 0, sizeof(syncdata));
|
||||||
|
|
||||||
internalslot = -1; //reset here so we know if this value is being set properly
|
internalslot = -1; //reset here so we know if this value is being set properly
|
||||||
for(i = 0; i < 12; i++)
|
for(i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
@@ -109,13 +125,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
//disabled for dmr_cach testing
|
//disabled for dmr_cach testing
|
||||||
tact_okay = 0;
|
tact_okay = 0;
|
||||||
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
||||||
if (tact_okay != 1)
|
if (tact_okay != 1) goto END;
|
||||||
{
|
|
||||||
if (opts->aggressive_framesync == 1)
|
|
||||||
{
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internalslot = state->currentslot = tact_bits[1];
|
internalslot = state->currentslot = tact_bits[1];
|
||||||
|
|
||||||
@@ -183,19 +194,19 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
state->dmr_stereo_payload[i+66] = dibit;
|
state->dmr_stereo_payload[i+66] = dibit;
|
||||||
sync[i] = (dibit | 1) + 48; //how does this work again?
|
sync[i] = (dibit | 1) + 48; //Sync Burst String as ones and threes
|
||||||
|
|
||||||
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
||||||
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
//embedded link control
|
//embedded link control
|
||||||
if(internalslot == 0 && vc1 > 1 && vc1 < 6) //grab on vc1 values 2-5 B C D and E
|
if(internalslot == 0 && vc1 > 1 && vc1 < 7) //grab on vc1 values 2-5 B C D E, and F
|
||||||
{
|
{
|
||||||
state->dmr_embedded_signalling[internalslot][vc1-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
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
|
state->dmr_embedded_signalling[internalslot][vc1-1][i*2+1] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if(internalslot == 1 && vc2 > 1 && vc2 < 6) //grab on vc2 values 2-5 B C D and E
|
if(internalslot == 1 && vc2 > 1 && vc2 < 7) //grab on vc2 values 2-5 B C D E, and F
|
||||||
{
|
{
|
||||||
state->dmr_embedded_signalling[internalslot][vc2-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
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
|
state->dmr_embedded_signalling[internalslot][vc2-1][i*2+1] = (1 & dibit); // bit 0
|
||||||
@@ -204,8 +215,8 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
sync[24] = 0;
|
sync[24] = 0;
|
||||||
|
|
||||||
for(i = 0; i < 8; i++) EmbeddedSignalling[i] = syncdata[i];
|
for(i = 0; i < 8; i++) emb_pdu[i + 0] = syncdata[i];
|
||||||
for(i = 0; i < 8; i++) EmbeddedSignalling[i + 8] = syncdata[i + 40];
|
for(i = 0; i < 8; i++) emb_pdu[i + 8] = syncdata[i + 40];
|
||||||
|
|
||||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||||
for(i = 0; i < 18; i++)
|
for(i = 0; i < 18; i++)
|
||||||
@@ -255,7 +266,7 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 )
|
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 )
|
||||||
{
|
{
|
||||||
|
|
||||||
fprintf (stderr,"%s ", getTime());
|
fprintf (stderr,"%s ", timestr);
|
||||||
if (internalslot == 0)
|
if (internalslot == 0)
|
||||||
{
|
{
|
||||||
if (opts->inverted_dmr == 0)
|
if (opts->inverted_dmr == 0)
|
||||||
@@ -264,7 +275,10 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
else fprintf (stderr,"Sync: -DMR ");
|
else fprintf (stderr,"Sync: -DMR ");
|
||||||
|
|
||||||
vc1 = 1;
|
vc1 = 7; //set to 7 so we can see that we should not be on a VC unless a framesync comes in for it first
|
||||||
|
|
||||||
|
//close MBEout file - slot 1
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
if (internalslot == 1)
|
if (internalslot == 1)
|
||||||
{
|
{
|
||||||
@@ -274,7 +288,10 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
else fprintf (stderr,"Sync: -DMR ");
|
else fprintf (stderr,"Sync: -DMR ");
|
||||||
|
|
||||||
vc2 = 1;
|
vc2 = 7; //set to 7 so we can see that we should not be on a VC unless a framesync comes in for it first
|
||||||
|
|
||||||
|
//close MBEout file - slot 2
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
|
||||||
}
|
}
|
||||||
dmr_data_sync (opts, state);
|
dmr_data_sync (opts, state);
|
||||||
@@ -282,26 +299,103 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
goto SKIP;
|
goto SKIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
//only play voice on no data sync
|
//ETSI TS 102 361-1 V2.6.1 Table E.2 and E.4
|
||||||
|
//check for a rc burst with bptc or 34 rate data in it (testing only)
|
||||||
|
// #define RC_TESTING //disable if not in use
|
||||||
|
#ifdef RC_TESTING
|
||||||
|
if ( (strcmp (sync, DMR_BS_DATA_SYNC) != 0) && (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) &&
|
||||||
|
( (internalslot == 0 && vc1 == 6) || (internalslot == 1 && vc2 == 6) ) )
|
||||||
|
{
|
||||||
|
|
||||||
|
//if the QR FEC is good, and the P/PI bit is on for RC
|
||||||
|
if (QR_16_7_6_decode(emb_pdu) && emb_pdu[4])
|
||||||
|
{
|
||||||
|
fprintf (stderr,"%s ", timestr);
|
||||||
|
|
||||||
|
if (opts->inverted_dmr == 0)
|
||||||
|
fprintf (stderr,"Sync: +RC ");
|
||||||
|
else fprintf (stderr,"Sync: -RC ");
|
||||||
|
|
||||||
|
dmr_data_sync (opts, state);
|
||||||
|
|
||||||
|
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
||||||
|
|
||||||
|
dmr_sbrc (opts, state, emb_pdu[4]);
|
||||||
|
|
||||||
|
if (internalslot == 0)
|
||||||
|
vc1 = 7;
|
||||||
|
else vc2 = 7;
|
||||||
|
|
||||||
|
goto SKIP;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //RC_TESTING
|
||||||
|
|
||||||
|
//check to see if we are expecting a VC at this point vc > 7
|
||||||
|
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0 && internalslot == 0 && vc1 > 6)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"%s ", timestr);
|
||||||
|
|
||||||
|
//simplifying things
|
||||||
|
// char polarity[3];
|
||||||
|
char light[18];
|
||||||
|
|
||||||
|
sprintf (light, "%s", " [SLOT1] slot2 ");
|
||||||
|
fprintf (stderr,"Sync: DMR %s", light);
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, "| Frame Sync Err: %d", vc1);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
vc1++;
|
||||||
|
//this should give it enough time to find the next frame sync pattern, if it exists, if not, then trigger a resync
|
||||||
|
if ( vc1 > 13 ) goto END;
|
||||||
|
else goto SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check to see if we are expecting a VC at this point vc > 7
|
||||||
|
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0 && internalslot == 1 && vc2 > 6)
|
||||||
|
{
|
||||||
|
fprintf (stderr,"%s ", timestr);
|
||||||
|
|
||||||
|
//simplifying things
|
||||||
|
// char polarity[3];
|
||||||
|
char light[18];
|
||||||
|
|
||||||
|
sprintf (light, "%s", " slot1 [SLOT2] ");
|
||||||
|
fprintf (stderr,"Sync: DMR %s", light);
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, "| Frame Sync Err: %d", vc2);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
vc2++;
|
||||||
|
//this should give it enough time to find the next frame sync pattern, if it exists, if not, then trigger a resync
|
||||||
|
if ( vc2 > 13 ) goto END;
|
||||||
|
else goto SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
//only play voice on no data sync, and VC values are within expected values 1-6
|
||||||
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
|
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
|
//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;
|
if(QR_16_7_6_decode(emb_pdu)) emb_ok = 1;
|
||||||
else emb_ok = 0;
|
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
|
//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? ");
|
if ( (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) && emb_ok == 0) goto END; //fprintf (stderr, "EMB BAD? ");
|
||||||
else if (emb_ok == 1)
|
else if (emb_ok == 1)
|
||||||
{
|
{
|
||||||
uint8_t cc = ((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
|
cc = ((emb_pdu[0] << 3) + (emb_pdu[1] << 2) + (emb_pdu[2] << 1) + emb_pdu[3]);
|
||||||
uint8_t power = EmbeddedSignalling[4];
|
power = emb_pdu[4];
|
||||||
uint8_t lcss = ((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
|
lcss = ((emb_pdu[5] << 1) + emb_pdu[6]);
|
||||||
|
state->dmr_color_code = state->color_code = cc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
skipcount = 0; //reset skip count if processing voice frames
|
skipcount = 0; //reset skip count if processing voice frames
|
||||||
fprintf (stderr,"%s ", getTime());
|
fprintf (stderr,"%s ", timestr);
|
||||||
|
|
||||||
//simplifying things
|
//simplifying things
|
||||||
char polarity[3];
|
char polarity[3];
|
||||||
@@ -312,23 +406,31 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
state->dmrburstL = 16;
|
state->dmrburstL = 16;
|
||||||
vc = vc1;
|
vc = vc1;
|
||||||
sprintf (light, "%s", " [SLOT1] slot2 ");
|
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
|
else
|
||||||
{
|
{
|
||||||
state->dmrburstR = 16;
|
state->dmrburstR = 16;
|
||||||
vc = vc2;
|
vc = vc2;
|
||||||
sprintf (light, "%s", " slot1 [SLOT2] ");
|
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", "+");
|
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
|
||||||
else sprintf (polarity, "%s", "-");
|
else sprintf (polarity, "%s", "-");
|
||||||
|
if (state->dmr_color_code != 16)
|
||||||
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC%d ", polarity, light, state->dmr_color_code, vc);
|
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC%d ", polarity, light, state->dmr_color_code, vc);
|
||||||
|
else fprintf (stderr,"Sync: %sDMR %s| Color Code=XX | VC%d ", polarity, light, vc);
|
||||||
|
|
||||||
if (internalslot == 0 && vc1 == 6)
|
if (internalslot == 0 && vc1 == 6)
|
||||||
{
|
{
|
||||||
//process embedded link control
|
//process embedded link control
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
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)
|
if (internalslot == 1 && vc2 == 6)
|
||||||
@@ -336,6 +438,9 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
//process embedded link control
|
//process embedded link control
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
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
|
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
|
||||||
@@ -348,17 +453,84 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
memcpy (m3, ambe_fr3, sizeof(m3));
|
memcpy (m3, ambe_fr3, sizeof(m3));
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
if(internalslot == 0)
|
||||||
|
{
|
||||||
|
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu));
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(state->f_r4[0], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR));
|
||||||
|
memcpy(state->s_r4[0], state->s_r, sizeof(state->s_r));
|
||||||
|
memcpy(state->s_r4u[0], state->s_ru, sizeof(state->s_ru));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
if(internalslot == 0)
|
||||||
|
{
|
||||||
|
memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu));
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(state->f_r4[1], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR));
|
||||||
|
memcpy(state->s_r4[1], state->s_r, sizeof(state->s_r));
|
||||||
|
memcpy(state->s_r4u[1], state->s_ru, sizeof(state->s_ru));
|
||||||
|
}
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
if(internalslot == 0)
|
||||||
|
{
|
||||||
|
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(state->f_r4[2], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR));
|
||||||
|
memcpy(state->s_r4[2], state->s_r, sizeof(state->s_r));
|
||||||
|
memcpy(state->s_r4u[2], state->s_ru, sizeof(state->s_ru));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//'DSP' output to file -- run before sbrc
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
cach_err = dmr_cach (opts, state, cachdata);
|
cach_err = dmr_cach (opts, state, cachdata);
|
||||||
fprintf (stderr, "\n");
|
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||||
|
|
||||||
// run alg refresh after vc6 ambe processing
|
// run alg refresh after vc6 ambe processing
|
||||||
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
|
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
|
||||||
if (internalslot == 1 && vc2 == 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);
|
dmr_late_entry_mi_fragment (opts, state, vc%7, m1, m2, m3);
|
||||||
|
|
||||||
//increment the vc counters
|
//increment the vc counters
|
||||||
if (internalslot == 0) vc1++;
|
if (internalslot == 0) vc1++;
|
||||||
@@ -376,6 +548,11 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
tact_okay = 0;
|
tact_okay = 0;
|
||||||
emb_ok = 0;
|
emb_ok = 0;
|
||||||
|
|
||||||
|
//reset emb components
|
||||||
|
cc = 25;
|
||||||
|
power = 9;
|
||||||
|
lcss = 9;
|
||||||
|
|
||||||
//Extra safeguards to break loop
|
//Extra safeguards to break loop
|
||||||
// if ( (vc1 > 7 && vc2 > 7) ) goto END;
|
// if ( (vc1 > 7 && vc2 > 7) ) goto END;
|
||||||
if ( (vc1 > 14 || vc2 > 14) ) goto END;
|
if ( (vc1 > 14 || vc2 > 14) ) goto END;
|
||||||
@@ -383,7 +560,15 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SKIP:
|
SKIP:
|
||||||
if (skipcount > 2) //after 2 consecutive data frames, drop back to getFrameSync and process with dmr_data_sync
|
|
||||||
|
//both working now, will need support added for ENC audio and no key
|
||||||
|
//NOTE: We want this to play regardless of whether the slot is voice or data, to play silence in one slot and voice in the second, or voices in both
|
||||||
|
if (internalslot == 1 && opts->floating_point == 1 && opts->pulse_digi_rate_out == 8000)
|
||||||
|
playSynthesizedVoiceFS3 (opts, state); //Float Stereo Mix 3v2
|
||||||
|
if (internalslot == 1 && opts->floating_point == 0 && opts->pulse_digi_rate_out == 8000)
|
||||||
|
playSynthesizedVoiceSS3 (opts, state); //Short Stereo Mix 3v2
|
||||||
|
|
||||||
|
if (skipcount > 3) //after 3 onsecutive data frames, drop back to getFrameSync and process with dmr_data_sync (need one more in order to push last voice on slot 2 only voice)
|
||||||
{
|
{
|
||||||
//set tests to all good so we don't get a bogus/redundant voice error
|
//set tests to all good so we don't get a bogus/redundant voice error
|
||||||
cach_err = 0;
|
cach_err = 0;
|
||||||
@@ -398,6 +583,13 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
ncursesPrinter(opts, state);
|
ncursesPrinter(opts, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
} // while loop
|
} // while loop
|
||||||
|
|
||||||
END:
|
END:
|
||||||
@@ -407,28 +599,36 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
state->errs2R = 0;
|
state->errs2R = 0;
|
||||||
state->errs2 = 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);
|
||||||
|
|
||||||
|
//clear out any stale audio storage buffers
|
||||||
|
memset (state->f_l4, 0.0f, sizeof(state->f_l4));
|
||||||
|
memset (state->f_r4, 0.0f, sizeof(state->f_r4));
|
||||||
|
memset (state->s_l4, 0, sizeof(state->s_l4));
|
||||||
|
memset (state->s_r4, 0, sizeof(state->s_r4));
|
||||||
|
|
||||||
//if we have a tact or emb err, then produce sync pattern/err message
|
//if we have a tact or emb err, then produce sync pattern/err message
|
||||||
if (tact_okay != 1 || emb_ok != 1)
|
if (tact_okay != 1 || emb_ok != 1)
|
||||||
{
|
{
|
||||||
|
|
||||||
fprintf (stderr,"%s ", getTime());
|
fprintf (stderr,"%s ", timestr);
|
||||||
fprintf (stderr,"Sync: DMR ");
|
fprintf (stderr,"Sync: DMR ");
|
||||||
fprintf (stderr, "%s", KRED);
|
fprintf (stderr, "%s", KRED);
|
||||||
fprintf (stderr, "| VOICE CACH/EMB ERR");
|
fprintf (stderr, "| VOICE CACH/EMB ERR");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
//run LFSR if either slot had an active MI in it.
|
//run refresh if either slot had an active MI in it.
|
||||||
if (state->payload_algid >= 0x21)
|
if (state->payload_algid >= 0x21)
|
||||||
{
|
{
|
||||||
state->currentslot = 0;
|
state->currentslot = 0;
|
||||||
LFSR(state);
|
dmr_alg_refresh (opts, state);
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
}
|
||||||
if (state->payload_algidR >= 0x21)
|
if (state->payload_algidR >= 0x21)
|
||||||
{
|
{
|
||||||
state->currentslot = 1;
|
state->currentslot = 1;
|
||||||
LFSR(state);
|
dmr_alg_refresh (opts, state);
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//failsafe to reset all data header and blocks when bad tact or emb
|
//failsafe to reset all data header and blocks when bad tact or emb
|
||||||
@@ -436,52 +636,51 @@ void dmrBS (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Process buffered half frame and 2nd half and then jump to full BS decoding
|
//Process buffered half frame and 2nd half and then jump to full BS decoding
|
||||||
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
int i, j, k, l, dibit;
|
int i, dibit;
|
||||||
int *dibit_p;
|
int *dibit_p;
|
||||||
char ambe_fr[4][24];
|
char ambe_fr[4][24];
|
||||||
char ambe_fr2[4][24];
|
char ambe_fr2[4][24];
|
||||||
char ambe_fr3[4][24];
|
char ambe_fr3[4][24];
|
||||||
|
|
||||||
|
memset (ambe_fr, 0, sizeof(ambe_fr));
|
||||||
|
memset (ambe_fr2, 0, sizeof(ambe_fr2));
|
||||||
|
memset (ambe_fr3, 0, sizeof(ambe_fr3));
|
||||||
|
|
||||||
//memcpy of ambe_fr for late entry
|
//memcpy of ambe_fr for late entry
|
||||||
char m1[4][24];
|
uint8_t m1[4][24];
|
||||||
char m2[4][24];
|
uint8_t m2[4][24];
|
||||||
char m3[4][24];
|
uint8_t m3[4][24];
|
||||||
|
|
||||||
const int *w, *x, *y, *z;
|
const int *w, *x, *y, *z;
|
||||||
char sync[25];
|
char sync[25];
|
||||||
uint8_t tact_okay = 0;
|
uint8_t tact_okay = 0;
|
||||||
uint8_t cach_err = 0;
|
uint8_t cach_err = 0;
|
||||||
uint8_t sync_okay = 1;
|
uint8_t sync_okay = 1;
|
||||||
|
UNUSED(cach_err);
|
||||||
|
|
||||||
|
uint8_t internalslot;
|
||||||
|
|
||||||
char cachdata[25];
|
uint8_t cachdata[25];
|
||||||
int cachInterleave[24] =
|
int cachInterleave[24] =
|
||||||
{0, 7, 8, 9, 1, 10,
|
{0, 7, 8, 9, 1, 10,
|
||||||
11, 12, 2, 13, 14,
|
11, 12, 2, 13, 14,
|
||||||
15, 3, 16, 4, 17, 18,
|
15, 3, 16, 4, 17, 18,
|
||||||
19, 5, 20, 21, 22, 6, 23
|
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 * timestr = getTimeC();
|
||||||
char * stamp = asctime(localtime( & t));
|
|
||||||
|
|
||||||
curr = strtok(stamp, " ");
|
|
||||||
curr = strtok(NULL, " ");
|
|
||||||
curr = strtok(NULL, " ");
|
|
||||||
curr = strtok(NULL, " ");
|
|
||||||
|
|
||||||
return curr;
|
|
||||||
}
|
|
||||||
|
|
||||||
//payload buffer
|
//payload buffer
|
||||||
//CACH + First Half Payload + Sync = 12 + 54 + 24
|
//CACH + First Half Payload + Sync = 12 + 54 + 24
|
||||||
@@ -490,10 +689,7 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = *dibit_p;
|
dibit = *dibit_p;
|
||||||
dibit_p++;
|
dibit_p++;
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
{
|
|
||||||
dibit = (dibit ^ 2);
|
|
||||||
}
|
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,15 +709,9 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
//decode and correct tact and compare
|
//decode and correct tact and compare
|
||||||
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
||||||
if (tact_okay != 1)
|
if (tact_okay != 1) goto END;
|
||||||
{
|
|
||||||
if (opts->aggressive_framesync == 1)
|
|
||||||
{
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state->currentslot = tact_bits[1];
|
internalslot = state->currentslot = tact_bits[1];
|
||||||
|
|
||||||
//Setup for first AMBE Frame
|
//Setup for first AMBE Frame
|
||||||
|
|
||||||
@@ -577,17 +767,15 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
|
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
|
||||||
{
|
{
|
||||||
if (opts->aggressive_framesync == 1)
|
sync_okay = 0;
|
||||||
{
|
goto END;
|
||||||
sync_okay = 0;
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||||
for(i = 0; i < 18; i++)
|
for(i = 0; i < 18; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -610,6 +798,7 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 36; i++)
|
for(i = 0; i < 36; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i+108] = dibit;
|
||||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -620,22 +809,47 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf (stderr,"%s ", getTime());
|
//'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 ", timestr);
|
||||||
char polarity[3];
|
char polarity[3];
|
||||||
char light[18];
|
char light[18];
|
||||||
|
|
||||||
if (state->currentslot == 0)
|
if (state->currentslot == 0)
|
||||||
{
|
{
|
||||||
sprintf (light, "%s", " [SLOT1] slot2 ");
|
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
|
else
|
||||||
{
|
{
|
||||||
sprintf (light, "%s", " slot1 [SLOT2] ");
|
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", "+");
|
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
|
||||||
else sprintf (polarity, "%s", "-");
|
else sprintf (polarity, "%s", "-");
|
||||||
|
if (state->dmr_color_code != 16)
|
||||||
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC1*", polarity, light, state->dmr_color_code);
|
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC1*", polarity, light, state->dmr_color_code);
|
||||||
|
else fprintf (stderr,"Sync: %sDMR %s| Color Code=XX | VC1*", polarity, light);
|
||||||
|
|
||||||
dmr_alg_reset (opts, state);
|
dmr_alg_reset (opts, state);
|
||||||
|
|
||||||
@@ -647,46 +861,102 @@ void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
memcpy (m3, ambe_fr3, sizeof(m3));
|
memcpy (m3, ambe_fr3, sizeof(m3));
|
||||||
|
|
||||||
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
|
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);
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
if(internalslot == 0)
|
||||||
|
{
|
||||||
|
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu));
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(state->f_r4[0], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR));
|
||||||
|
memcpy(state->s_r4[0], state->s_r, sizeof(state->s_r));
|
||||||
|
memcpy(state->s_r4u[0], state->s_ru, sizeof(state->s_ru));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
if(internalslot == 0)
|
||||||
|
{
|
||||||
|
memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu));
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(state->f_r4[1], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR));
|
||||||
|
memcpy(state->s_r4[1], state->s_r, sizeof(state->s_r));
|
||||||
|
memcpy(state->s_r4u[1], state->s_ru, sizeof(state->s_ru));
|
||||||
|
}
|
||||||
|
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
if(internalslot == 0)
|
||||||
|
{
|
||||||
|
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(state->f_r4[2], state->audio_out_temp_bufR, sizeof(state->audio_out_temp_bufR));
|
||||||
|
memcpy(state->s_r4[2], state->s_r, sizeof(state->s_r));
|
||||||
|
memcpy(state->s_r4u[2], state->s_ru, sizeof(state->s_ru));
|
||||||
|
}
|
||||||
|
|
||||||
//collect the mi fragment
|
//collect the mi fragment
|
||||||
dmr_late_entry_mi_fragment (opts, state, 1, ambe_fr, ambe_fr2, ambe_fr3);
|
dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
|
||||||
|
|
||||||
cach_err = dmr_cach (opts, state, cachdata);
|
cach_err = dmr_cach (opts, state, cachdata);
|
||||||
fprintf (stderr, "\n");
|
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||||
|
|
||||||
//update voice sync time for trunking purposes (particularly Con+)
|
//update voice sync time for trunking purposes (particularly Con+)
|
||||||
if (opts->p25_is_tuned == 1) state->last_vc_sync_time = time(NULL);
|
if (opts->p25_is_tuned == 1) state->last_vc_sync_time = time(NULL);
|
||||||
|
|
||||||
|
//NOTE: Only play on slot 1, if slot 0, then it'll play after the next TDMA frame in the BS loop instead
|
||||||
|
if (internalslot == 1 && opts->floating_point == 1 && opts->pulse_digi_rate_out == 8000)
|
||||||
|
playSynthesizedVoiceFS3 (opts, state); //Float Stereo Mix 3v2
|
||||||
|
if (internalslot == 1 && opts->floating_point == 0 && opts->pulse_digi_rate_out == 8000)
|
||||||
|
playSynthesizedVoiceSS3 (opts, state); //Short Stereo Mix 3v2
|
||||||
|
|
||||||
dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode
|
dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode
|
||||||
END:
|
END:
|
||||||
//if we have a tact err, then produce sync pattern/err message
|
//if we have a tact err, then produce sync pattern/err message
|
||||||
if (tact_okay != 1 || sync_okay != 1)
|
if (tact_okay != 1 || sync_okay != 1)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"%s ", getTime());
|
fprintf (stderr,"%s ", timestr);
|
||||||
fprintf (stderr,"Sync: DMR ");
|
fprintf (stderr,"Sync: DMR ");
|
||||||
fprintf (stderr, "%s", KRED);
|
fprintf (stderr, "%s", KRED);
|
||||||
fprintf (stderr, "| VOICE CACH/SYNC ERR");
|
fprintf (stderr, "| VOICE CACH/SYNC ERR");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
//run LFSR if either slot had an active MI in it.
|
fprintf (stderr, "\n");
|
||||||
|
//run refresh if either slot had an active MI in it.
|
||||||
if (state->payload_algid >= 0x21)
|
if (state->payload_algid >= 0x21)
|
||||||
{
|
{
|
||||||
state->currentslot = 0;
|
state->currentslot = 0;
|
||||||
LFSR(state);
|
dmr_alg_refresh (opts, state);
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
}
|
||||||
if (state->payload_algidR >= 0x21)
|
if (state->payload_algidR >= 0x21)
|
||||||
{
|
{
|
||||||
state->currentslot = 1;
|
state->currentslot = 1;
|
||||||
LFSR(state);
|
dmr_alg_refresh (opts, state);
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//failsafe to reset all data header and blocks when bad tact or emb
|
//failsafe to reset all data header and blocks when bad tact or emb
|
||||||
dmr_reset_blocks (opts, state);
|
dmr_reset_blocks (opts, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
2505
src/dmr_csbk.c
@@ -25,14 +25,15 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
int *dibit_p;
|
int *dibit_p;
|
||||||
char sync[25];
|
char sync[25];
|
||||||
char syncdata[48];
|
char syncdata[48];
|
||||||
char cachdata[25];
|
uint8_t cachdata[25];
|
||||||
|
UNUSED(syncdata);
|
||||||
|
|
||||||
char bursttype[5];
|
|
||||||
uint8_t burst;
|
uint8_t burst;
|
||||||
char info[196];
|
char info[196];
|
||||||
unsigned char SlotType[20];
|
unsigned char SlotType[20];
|
||||||
unsigned int SlotTypeOk;
|
unsigned int SlotTypeOk;
|
||||||
uint8_t cach_err = 0;
|
uint8_t cach_err = 0;
|
||||||
|
UNUSED(cach_err);
|
||||||
|
|
||||||
int cachInterleave[24] =
|
int cachInterleave[24] =
|
||||||
{0, 7, 8, 9, 1, 10,
|
{0, 7, 8, 9, 1, 10,
|
||||||
@@ -56,6 +57,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[i];
|
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 & (dibit >> 1)); // bit 1
|
||||||
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
||||||
@@ -75,11 +77,8 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
cach_okay = -1;
|
cach_okay = -1;
|
||||||
if (opts->aggressive_framesync == 1)
|
SlotTypeOk = 0;
|
||||||
{
|
goto END;
|
||||||
SlotTypeOk = 0;
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state->currentslot = tact_bits[1];
|
state->currentslot = tact_bits[1];
|
||||||
@@ -103,6 +102,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[i+12];
|
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 & (dibit >> 1)); // bit 1
|
||||||
info[(2*i)+1] = (1 & dibit); // bit 0
|
info[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
@@ -118,6 +118,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[61];
|
dibit = (int)state->dmr_stereo_payload[61];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[61] = dibit;
|
||||||
|
|
||||||
SlotType[0] = (1 & (dibit >> 1)); // bit 1
|
SlotType[0] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[1] = (1 & dibit); // bit 0
|
SlotType[1] = (1 & dibit); // bit 0
|
||||||
@@ -132,6 +133,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[62];
|
dibit = (int)state->dmr_stereo_payload[62];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[62] = dibit;
|
||||||
|
|
||||||
SlotType[2] = (1 & (dibit >> 1)); // bit 1
|
SlotType[2] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[3] = (1 & dibit); // bit 0
|
SlotType[3] = (1 & dibit); // bit 0
|
||||||
@@ -146,6 +148,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[63];
|
dibit = (int)state->dmr_stereo_payload[63];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[63] = dibit;
|
||||||
|
|
||||||
SlotType[4] = (1 & (dibit >> 1)); // bit 1
|
SlotType[4] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[5] = (1 & dibit); // bit 0
|
SlotType[5] = (1 & dibit); // bit 0
|
||||||
@@ -160,6 +163,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[64];
|
dibit = (int)state->dmr_stereo_payload[64];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[64] = dibit;
|
||||||
|
|
||||||
SlotType[6] = (1 & (dibit >> 1)); // bit 1
|
SlotType[6] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[7] = (1 & dibit); // bit 0
|
SlotType[7] = (1 & dibit); // bit 0
|
||||||
@@ -175,6 +179,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[65];
|
dibit = (int)state->dmr_stereo_payload[65];
|
||||||
}
|
}
|
||||||
|
else state->dmr_stereo_payload[65] = dibit;
|
||||||
SlotType[8] = (1 & (dibit >> 1)); // bit 1
|
SlotType[8] = (1 & (dibit >> 1)); // bit 1
|
||||||
SlotType[9] = (1 & dibit); // bit 0
|
SlotType[9] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -191,6 +196,8 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
dibit = (int)state->dmr_stereo_payload[i+66];
|
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 & (dibit >> 1)); // bit 1
|
||||||
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
sync[i] = (dibit | 1) + 48;
|
sync[i] = (dibit | 1) + 48;
|
||||||
@@ -236,6 +243,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->dmr_stereo == 0)
|
if (state->dmr_stereo == 0)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
}
|
}
|
||||||
if (state->dmr_stereo == 1)
|
if (state->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
@@ -251,12 +259,8 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
if( Golay_20_8_decode(SlotType) ) SlotTypeOk = 1;
|
if( Golay_20_8_decode(SlotType) ) SlotTypeOk = 1;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SlotTypeOk = -1;
|
SlotTypeOk = 0;
|
||||||
if (opts->aggressive_framesync == 1)
|
goto END;
|
||||||
{
|
|
||||||
SlotTypeOk = 0;
|
|
||||||
goto END;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0);
|
state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0);
|
||||||
@@ -277,6 +281,7 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->dmr_stereo == 0)
|
if (state->dmr_stereo == 0)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
|
state->dmr_stereo_payload[i+95] = dibit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->dmr_stereo == 1)
|
if (state->dmr_stereo == 1)
|
||||||
@@ -288,10 +293,11 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
info[(2*i) + 99] = (1 & dibit); // bit 0
|
info[(2*i) + 99] = (1 & dibit); // bit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
dmr_data_burst_handler(opts, state, (uint8_t *)info, burst);
|
dmr_data_burst_handler(opts, state, (uint8_t *)info, burst);
|
||||||
|
|
||||||
//don't run cach on simplex or mono
|
//don't run cach on simplex or mono
|
||||||
if (state->dmr_ms_mode == 0 || opts->dmr_mono == 0)
|
if (state->dmr_ms_mode == 0 && opts->dmr_mono == 0)
|
||||||
{
|
{
|
||||||
cach_err = dmr_cach (opts, state, cachdata);
|
cach_err = dmr_cach (opts, state, cachdata);
|
||||||
}
|
}
|
||||||
@@ -315,31 +321,25 @@ dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
|||||||
skipDibit (opts, state, 12 + 49 + 5);
|
skipDibit (opts, state, 12 + 49 + 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if using con+ trunking and last voice observed more than x seconds ago, snap back to con+ cc
|
#define CON_TUNEAWAY //disable if any unlock issues noted on the logic
|
||||||
//con+ voice channels can have extremely long idle periods without properly tearing down
|
|
||||||
|
#ifdef CON_TUNEAWAY
|
||||||
|
//simplified, if IDLE condition, drop cc and vc sync time so when RF channel tears down, we go back much quicker
|
||||||
|
//NOTE: This was adopted, because going back to the CC early and then to be sent back to the channel grant,
|
||||||
|
//only for it to tear down and being sent back to the CC again isn't very efficient for trunk tracking
|
||||||
|
//NOTE: This will still leave the tuner in the 'locked' state when tuning a voice channel grant on the CC,
|
||||||
|
//and will remain locked until a new voice channel grant is received, but its just asthetic, trying to fix it
|
||||||
|
//is too much of a hassle and causes other issues like CC hunting, etc.
|
||||||
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->is_con_plus == 1)
|
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->is_con_plus == 1)
|
||||||
{
|
{
|
||||||
//at 3 seconds, the CC was sending back to the VC even without voice present, increased to 5
|
int clear = 0;
|
||||||
if ( (time(NULL) - state->last_vc_sync_time > 5) )
|
//IF both slots currently signalling IDLE
|
||||||
|
if (state->dmrburstL == 9 && state->dmrburstR == 9) clear = 1;
|
||||||
|
if (clear == 1)
|
||||||
{
|
{
|
||||||
if (opts->use_rigctl == 1) //rigctl tuning
|
state->last_cc_sync_time = 0;
|
||||||
{
|
state->last_vc_sync_time = 0;
|
||||||
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
|
|
||||||
{
|
|
||||||
//UDP command to tune the RTL dongle
|
|
||||||
rtl_udp_tune(opts, state, state->p25_cc_freq);
|
|
||||||
}
|
|
||||||
opts->p25_is_tuned = 0;
|
|
||||||
//zero out vc frequencies
|
|
||||||
state->p25_vc_freq[0] = 0;
|
|
||||||
state->p25_vc_freq[1] = 0;
|
|
||||||
state->last_cc_sync_time = time(NULL);
|
|
||||||
state->is_con_plus = 0; //con+ flag off
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
348
src/dmr_dburst.c
@@ -6,12 +6,14 @@
|
|||||||
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
|
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
|
||||||
*
|
*
|
||||||
* LWVMOBILE
|
* LWVMOBILE
|
||||||
* 2022-12 DSD-FME Florida Man Edition
|
* 2023-12 DSD-FME Florida Man Edition
|
||||||
*-----------------------------------------------------------------------------*/
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
//TODO: CRC9 on confirmed data blocks; CRC32 on completed data sf;
|
//TODO: Test USBD LIP Decoder with Real World Samples (if/when available)
|
||||||
//TODO: Reverse Channel Signalling - RC single burst BPTC/7-bit CRC
|
//TODO: Test UDT NMEA and LIP Decoders with Real World Samples (if/when available)
|
||||||
//TODO: Single Burst Embedded LC - Non-RC single burst LC - Look for Late Entry Alg/Key
|
//WIP: Move all extra decoders for location, etc, to a new file and add prototypes to dsd.h
|
||||||
|
//TODO: Test CRC9/CRC32 on Rate 1 Data with Real World Samples (if/when available)
|
||||||
|
//TODO: Address areas that require reading of ISO7, ISO8, and UTF-16 string formats
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
@@ -24,7 +26,13 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
uint32_t CRCCorrect = 0;
|
uint32_t CRCCorrect = 0;
|
||||||
uint32_t IrrecoverableErrors = 0;
|
uint32_t IrrecoverableErrors = 0;
|
||||||
uint8_t slot = state->currentslot;
|
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));
|
||||||
|
UNUSED(dbsn);
|
||||||
|
|
||||||
//BPTC 196x96 Specific
|
//BPTC 196x96 Specific
|
||||||
uint8_t BPTCDeInteleavedData[196];
|
uint8_t BPTCDeInteleavedData[196];
|
||||||
@@ -37,26 +45,38 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
uint8_t LC_DataBytes[10];
|
uint8_t LC_DataBytes[10];
|
||||||
int Burst = -1;
|
int Burst = -1;
|
||||||
|
|
||||||
//Unified PDU (is this the best nomenclature) Bytes
|
memset (BPTCDeInteleavedData, 0, sizeof(BPTCDeInteleavedData));
|
||||||
uint8_t DMR_PDU[25]; //using 25 bytes as the max for now
|
memset (BPTCDmrDataBit, 0, sizeof(BPTCDmrDataBit));
|
||||||
uint8_t DMR_PDU_bits[196]; //bitwise version of unified payload, will probably be a lot easier to use this w/ manual
|
memset (BPTCDmrDataByte, 0, sizeof(BPTCDmrDataByte));
|
||||||
|
memset (BptcDataMatrix, 0, sizeof(BptcDataMatrix));
|
||||||
|
memset (LC_DataBit, 0, sizeof(LC_DataBit));
|
||||||
|
memset (LC_DataBytes, 0, sizeof(LC_DataBytes));
|
||||||
|
|
||||||
|
//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, 0, sizeof (DMR_PDU));
|
||||||
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
|
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
|
||||||
|
|
||||||
uint8_t R[3];
|
uint8_t R[3];
|
||||||
uint8_t BPTCReservedBits = 0;
|
uint8_t BPTCReservedBits = 0;
|
||||||
|
uint8_t is_ras = 0;
|
||||||
|
uint8_t crc_original_validity = 0;
|
||||||
|
|
||||||
uint32_t crcmask = 0;
|
uint32_t crcmask = 0;
|
||||||
uint8_t crclen = 0;
|
uint8_t crclen = 0;
|
||||||
|
|
||||||
uint8_t is_bptc = 0;
|
uint8_t is_bptc = 0;
|
||||||
uint8_t is_trellis = 0;
|
uint8_t is_trellis = 0;
|
||||||
uint8_t is_emb = 0;
|
uint8_t is_emb = 0;
|
||||||
uint8_t is_lc = 0;
|
uint8_t is_lc = 0;
|
||||||
uint8_t is_full = 0;
|
uint8_t is_full = 0;
|
||||||
|
uint8_t is_udt = 0;
|
||||||
uint8_t pdu_len = 0;
|
uint8_t pdu_len = 0;
|
||||||
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
|
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
|
||||||
|
|
||||||
|
uint8_t usbd_st = 0; //usbd service type
|
||||||
|
|
||||||
switch(databurst){
|
switch(databurst){
|
||||||
case 0x00: //PI
|
case 0x00: //PI
|
||||||
is_bptc = 1;
|
is_bptc = 1;
|
||||||
@@ -108,32 +128,36 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
pdu_len = 12; //12 bytes
|
pdu_len = 12; //12 bytes
|
||||||
sprintf(state->fsubtype, " DATA");
|
sprintf(state->fsubtype, " DATA");
|
||||||
break;
|
break;
|
||||||
//TODO - 1/2 0x0F0 - 3/4 0x1FF - Rate 1 0x10F
|
|
||||||
//going to set crclen and mask according to confirmed data here,
|
|
||||||
//use block assmebler to carry out completed message crc
|
|
||||||
case 0x07: //1/2 Rate Data
|
case 0x07: //1/2 Rate Data
|
||||||
is_bptc = 1;
|
is_bptc = 1;
|
||||||
crclen = 9; //confirmed data only
|
crclen = 9; //confirmed data only
|
||||||
crcmask = 0x0F0;
|
crcmask = 0x0F0;
|
||||||
pdu_len = 12; //12 bytes unconfirmed
|
pdu_len = 12; //12 bytes unconfirmed
|
||||||
|
sprintf(state->fsubtype, " R12U ");
|
||||||
if (state->data_conf_data[slot] == 1)
|
if (state->data_conf_data[slot] == 1)
|
||||||
{
|
{
|
||||||
pdu_len = 10;
|
pdu_len = 10;
|
||||||
pdu_start = 2; //start at plus two when assembling
|
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 ");
|
||||||
}
|
}
|
||||||
sprintf(state->fsubtype, " R12D ");
|
|
||||||
break;
|
break;
|
||||||
case 0x08: //3/4 Rate Data
|
case 0x08: //3/4 Rate Data
|
||||||
is_trellis = 1;
|
is_trellis = 1;
|
||||||
crclen = 9; //confirmed data only
|
crclen = 9; //confirmed data only
|
||||||
crcmask = 0x1FF;
|
crcmask = 0x1FF;
|
||||||
pdu_len = 18; //18 bytes unconfirmed
|
pdu_len = 18; //18 bytes unconfirmed
|
||||||
|
sprintf(state->fsubtype, " R34U ");
|
||||||
if (state->data_conf_data[slot] == 1)
|
if (state->data_conf_data[slot] == 1)
|
||||||
{
|
{
|
||||||
pdu_len = 16;
|
pdu_len = 16;
|
||||||
pdu_start = 2; //start at plus two when assembling
|
pdu_start = 2; //start at plus two when assembling
|
||||||
|
sprintf(state->fsubtype, " R34C ");
|
||||||
}
|
}
|
||||||
sprintf(state->fsubtype, " R34D ");
|
|
||||||
break;
|
break;
|
||||||
case 0x09: //Idle
|
case 0x09: //Idle
|
||||||
//pseudo-random data fill, no need to do anything with this
|
//pseudo-random data fill, no need to do anything with this
|
||||||
@@ -141,33 +165,75 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
break;
|
break;
|
||||||
case 0x0A: //1 Rate Data
|
case 0x0A: //1 Rate Data
|
||||||
crclen = 9; //confirmed data only
|
crclen = 9; //confirmed data only
|
||||||
crcmask = 0x1FF;
|
crcmask = 0x10F;
|
||||||
is_full = 1;
|
is_full = 1;
|
||||||
pdu_len = 25; //196 bits - 24.5 bytes
|
pdu_len = 25; //196 bits - 24.5 bytes
|
||||||
sprintf(state->fsubtype, " R_1D ");
|
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;
|
break;
|
||||||
case 0x0B: //Unified Data
|
case 0x0B: //Unified Single Block Data USBD
|
||||||
is_bptc = 1;
|
is_bptc = 1;
|
||||||
crclen = 16;
|
crclen = 16;
|
||||||
crcmask = 0x3333;
|
crcmask = 0x3333;
|
||||||
pdu_len = 12; //12 bytes
|
pdu_len = 12; //12 bytes
|
||||||
sprintf(state->fsubtype, " UDAT ");
|
sprintf(state->fsubtype, " USBD ");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//special types (not real data 'sync' bursts)
|
//special types (not real data 'sync' bursts)
|
||||||
case 0xEB: //Embedded Signalling
|
case 0xEB: //Embedded Signalling
|
||||||
|
crclen = 5;
|
||||||
is_emb = 1;
|
is_emb = 1;
|
||||||
pdu_len = 9;
|
pdu_len = 9;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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;
|
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 (databurst != 0xEB)
|
||||||
{
|
{
|
||||||
if (state->dmr_ms_mode == 0) fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
|
if (state->dmr_ms_mode == 0)
|
||||||
|
{
|
||||||
|
if (state->dmr_color_code != 16)
|
||||||
|
fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
|
||||||
|
else fprintf(stderr, "| Color Code=XX ");
|
||||||
|
}
|
||||||
fprintf(stderr, "|%s", state->fsubtype);
|
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
|
//Most Data Sync Burst types will use the bptc 196x96
|
||||||
@@ -184,7 +250,10 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, R);
|
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, R);
|
||||||
|
|
||||||
/* Fill the reserved bit (R(0)-R(2) of the BPTC(196,96) block) */
|
/* 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) & 0x08);
|
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 */
|
/* Convert the 96 bits BPTC data into 12 bytes */
|
||||||
k = 0;
|
k = 0;
|
||||||
@@ -213,20 +282,59 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
|
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
|
||||||
if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
|
if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
|
||||||
|
|
||||||
//TODO: run CRC9 on confirmed 1/2 data blocks
|
//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] == 1)
|
else if (state->data_conf_data[slot] == 0 && databurst == 0x7)
|
||||||
{
|
{
|
||||||
//run CRC on individual blocks according to type and validate each individual block
|
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
|
//run CCITT on other data forms
|
||||||
else //will still run on unconfirmed data (shouldn't matter other than a possible bogus warning message)
|
else
|
||||||
{
|
{
|
||||||
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
|
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
|
||||||
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
||||||
else CRCCorrect = 0;
|
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 (databurst == 0x04 || databurst == 0x06) //MBC Header, Data Header
|
||||||
{
|
{
|
||||||
if (CRCCorrect)
|
if (CRCCorrect)
|
||||||
@@ -249,7 +357,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
|
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
//convert to unified DMR_PDU and DMR_PDU_bits
|
//convert to DMR_PDU and DMR_PDU_bits
|
||||||
for (i = 0; i < pdu_len; i++)
|
for (i = 0; i < pdu_len; i++)
|
||||||
{
|
{
|
||||||
DMR_PDU[i] = BPTCDmrDataByte[i+pdu_start];
|
DMR_PDU[i] = BPTCDmrDataByte[i+pdu_start];
|
||||||
@@ -295,11 +403,7 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
|
IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
|
||||||
|
|
||||||
/* Reconstitute the 5 bit CRC */
|
/* Reconstitute the 5 bit CRC */
|
||||||
CRCExtracted = (LC_DataBit[72] & 1) << 4;
|
CRCExtracted = (uint32_t)ConvertBitIntoBytes(&LC_DataBit[72], 5);
|
||||||
CRCExtracted |= (LC_DataBit[73] & 1) << 3;
|
|
||||||
CRCExtracted |= (LC_DataBit[74] & 1) << 2;
|
|
||||||
CRCExtracted |= (LC_DataBit[75] & 1) << 1;
|
|
||||||
CRCExtracted |= (LC_DataBit[76] & 1) << 0;
|
|
||||||
|
|
||||||
/* Compute the 5 bit CRC */
|
/* Compute the 5 bit CRC */
|
||||||
CRCComputed = ComputeCrc5Bit(LC_DataBit);
|
CRCComputed = ComputeCrc5Bit(LC_DataBit);
|
||||||
@@ -319,35 +423,71 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//the ugly one
|
//the sexy one
|
||||||
if (is_trellis)
|
if (is_trellis)
|
||||||
{
|
{
|
||||||
CRCExtracted = 0;
|
CRCExtracted = 0;
|
||||||
CRCComputed = 0;
|
CRCComputed = 0;
|
||||||
IrrecoverableErrors = 1;
|
IrrecoverableErrors = 1;
|
||||||
bool trellis_good = TRUE;
|
|
||||||
|
|
||||||
uint8_t tdibits[98];
|
uint8_t tdibits[98];
|
||||||
memset (tdibits, 0, sizeof(tdibits));
|
memset (tdibits, 0, sizeof(tdibits));
|
||||||
|
|
||||||
//reconstitute info bits into reverse ordered dibits for the trellis decoder
|
//reconstitute info bits into dibits for the trellis decoder
|
||||||
for (i = 0; i < 98; i++)
|
for (i = 0; i < 98; i++)
|
||||||
{
|
tdibits[i] = (info[i*2] << 1) | info[i*2+1];
|
||||||
tdibits[97-i] = (info[i*2] << 1) | info[i*2+1];
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char TrellisReturn[18];
|
uint8_t TrellisReturn[18];
|
||||||
memset (TrellisReturn, 0, sizeof(TrellisReturn));
|
memset (TrellisReturn, 0, sizeof(TrellisReturn));
|
||||||
|
IrrecoverableErrors = dmr_34(tdibits, TrellisReturn);
|
||||||
|
|
||||||
trellis_good = CDMRTrellisDecode(tdibits, TrellisReturn);
|
//NOTE: IrrecoverableErrors in this context are a tally of errors from trellis
|
||||||
if (trellis_good == TRUE) IrrecoverableErrors = 0;
|
//they may have been successfully corrected, the CRC will reveal as much
|
||||||
// else (fprintf (stderr, " Trellis Failure")); //debug print
|
|
||||||
|
|
||||||
for (i = 0; i < pdu_len; i++) //18
|
for (i = 0; i < pdu_len; i++)
|
||||||
|
DMR_PDU[i] = TrellisReturn[i+pdu_start];
|
||||||
|
|
||||||
|
for(i = 0, j = 0; i < 18; i++, j+=8)
|
||||||
{
|
{
|
||||||
DMR_PDU[i] = (uint8_t)TrellisReturn[i+pdu_start];
|
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
|
||||||
|
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
|
||||||
|
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)
|
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 + 0] = (TrellisReturn[i+pdu_start] >> 7) & 0x01;
|
||||||
@@ -360,26 +500,46 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
DMR_PDU_bits[j + 7] = (TrellisReturn[i+pdu_start] >> 0) & 0x01;
|
DMR_PDU_bits[j + 7] = (TrellisReturn[i+pdu_start] >> 0) & 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: run CRC9 on confirmed 3/4 data blocks
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_full) //Rate 1 Data
|
if (is_full) //Rate 1 Data
|
||||||
{
|
{
|
||||||
CRCExtracted = 0;
|
CRCExtracted = 0;
|
||||||
CRCComputed = 0;
|
CRCComputed = 0;
|
||||||
IrrecoverableErrors = 0;
|
IrrecoverableErrors = 0; //implicit, since there is no encoding
|
||||||
|
|
||||||
//TODO: run CRC9 on confirmed data
|
for (i = 0; i < 12; i++)
|
||||||
|
|
||||||
for (i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&info[i*8], 8);
|
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&info[i*8], 8);
|
||||||
|
|
||||||
|
//Skip Padding Bits (96,97,98,99)
|
||||||
|
for (i = 0; i < 12; i++)
|
||||||
|
DMR_PDU[i+12] = (uint8_t)ConvertBitIntoBytes(&info[(i*8)+100], 8);
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
}
|
}
|
||||||
//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;
|
|
||||||
|
|
||||||
//copy info to dmr_pdu_bits
|
//copy info to dmr_pdu_bits
|
||||||
memcpy(DMR_PDU_bits, info, sizeof(DMR_PDU_bits));
|
memcpy(DMR_PDU_bits, info, sizeof(DMR_PDU_bits));
|
||||||
@@ -395,27 +555,75 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
if (databurst == 0x02) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 2); //TLC
|
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
|
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, Unified) - type 1
|
//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, CRCCorrect, IrrecoverableErrors);
|
if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors);
|
||||||
if (databurst == 0x07) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //1/2 Rate Data
|
|
||||||
if (databurst == 0x08) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //3/4 Rate Data
|
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 - Disabled until further testing can be done
|
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
|
||||||
|
|
||||||
//is unified data handled like normal data, or control signalling data?
|
//control signalling types (CSBK, MBC)
|
||||||
if (databurst == 0x0B) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //Unified Data
|
|
||||||
//if (databurst == 0x0B) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
|
||||||
|
|
||||||
//control signalling types (CSBK, MBC, UDT)
|
|
||||||
if (databurst == 0x03) dmr_cspdu (opts, state, DMR_PDU_bits, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
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
|
//both MBC header and MBC continuation will go to the block_assembler - type 2, and then to dmr_cspdu
|
||||||
if (databurst == 0x04)
|
if (databurst == 0x04)
|
||||||
{
|
{
|
||||||
state->data_block_counter[slot] = 0; //zero block counter before running header
|
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);
|
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);
|
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, " USBD - ");
|
||||||
|
if (usbd_st == 0) lip_protocol_decoder (opts, state, DMR_PDU_bits);
|
||||||
|
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 != 0x08 && 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
|
//print the unified PDU format here, if not slot idle
|
||||||
if (opts->payload == 1 && databurst != 0x09)
|
if (opts->payload == 1 && databurst != 0x09)
|
||||||
{
|
{
|
||||||
@@ -427,20 +635,12 @@ void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196
|
|||||||
fprintf (stderr, "[%02X]", DMR_PDU[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);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
// fprintf (stderr, "%s", KCYN);
|
|
||||||
// fprintf (stderr, "\n Hex to Ascii - ");
|
|
||||||
// for (i = 0; i < 12; i++)
|
|
||||||
// {
|
|
||||||
// if (DMR_PDU[i] <= 0x7E && DMR_PDU[i] >=0x20)
|
|
||||||
// {
|
|
||||||
// fprintf (stderr, "%c", DMR_PDU[i]);
|
|
||||||
// }
|
|
||||||
// else fprintf (stderr, ".");
|
|
||||||
// }
|
|
||||||
|
|
||||||
fprintf (stderr,"%s", KNRM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
843
src/dmr_flco.c
369
src/dmr_le.c
@@ -9,11 +9,28 @@
|
|||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
//gather ambe_fr mi fragments for processing
|
//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])
|
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, uint8_t ambe_fr[4][24], uint8_t ambe_fr2[4][24], uint8_t ambe_fr3[4][24])
|
||||||
{
|
{
|
||||||
|
|
||||||
uint8_t slot = state->currentslot;
|
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
|
//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][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][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4);
|
||||||
@@ -25,6 +42,8 @@ void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc,
|
|||||||
|
|
||||||
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
UNUSED(opts);
|
||||||
|
|
||||||
uint8_t slot = state->currentslot;
|
uint8_t slot = state->currentslot;
|
||||||
int i, j;
|
int i, j;
|
||||||
int g[3];
|
int g[3];
|
||||||
@@ -35,6 +54,12 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
|||||||
uint64_t mi_corrected = 0;
|
uint64_t mi_corrected = 0;
|
||||||
uint64_t go_corrected = 0;
|
uint64_t go_corrected = 0;
|
||||||
|
|
||||||
|
uint8_t mi_bits[36];
|
||||||
|
memset (mi_bits, 0, sizeof(mi_bits));
|
||||||
|
uint8_t mi_crc_cmp = 0;
|
||||||
|
uint8_t mi_crc_ext = 1;
|
||||||
|
uint8_t mi_crc_ok = 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) |
|
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][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);
|
(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);
|
||||||
@@ -60,12 +85,22 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
|||||||
mi_corrected |= mi_go_bits[i];
|
mi_corrected |= mi_go_bits[i];
|
||||||
go_corrected = go_corrected << 1;
|
go_corrected = go_corrected << 1;
|
||||||
go_corrected |= mi_go_bits[i+12];
|
go_corrected |= mi_go_bits[i+12];
|
||||||
|
//manipulate for crc check
|
||||||
|
mi_bits[i+(j*12)] = mi_go_bits[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int mi_final = 0;
|
int mi_final = 0;
|
||||||
mi_final = (mi_corrected >> 4) & 0xFFFFFFFF;
|
mi_final = (mi_corrected >> 4) & 0xFFFFFFFF;
|
||||||
|
|
||||||
|
mi_crc_ext = (uint8_t)ConvertBitIntoBytes(&mi_bits[32], 4);
|
||||||
|
mi_crc_cmp = crc4(mi_bits, 32);
|
||||||
|
if (mi_crc_ext == mi_crc_cmp)
|
||||||
|
mi_crc_ok = 1;
|
||||||
|
|
||||||
|
//debug -- working now
|
||||||
|
// fprintf (stderr, " LE MI: %09llX; CRC EXT: %X; CRC CMP: %X; \n", mi_corrected, mi_crc_ext, mi_crc_cmp);
|
||||||
|
|
||||||
if (g[0] && g[1] && g[2])
|
if (g[0] && g[1] && g[2])
|
||||||
{
|
{
|
||||||
if (slot == 0 && state->payload_algid != 0)
|
if (slot == 0 && state->payload_algid != 0)
|
||||||
@@ -73,20 +108,38 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->payload_mi != mi_final)
|
if (state->payload_mi != mi_final)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KCYN);
|
fprintf (stderr, "%s", KCYN);
|
||||||
fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_mi, mi_final);
|
fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08X : %08X ", state->payload_mi, mi_final);
|
||||||
state->payload_mi = mi_final;
|
if (mi_crc_ok == 1) state->payload_mi = mi_final;
|
||||||
|
if (mi_crc_ok == 1) fprintf (stderr, "(CRC OK)");
|
||||||
|
else fprintf (stderr, "(CRC ERR)");
|
||||||
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, "%s", KNRM);
|
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 (slot == 1 && state->payload_algidR != 0)
|
||||||
{
|
{
|
||||||
if (state->payload_miR != mi_final)
|
if (state->payload_miR != mi_final)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KCYN);
|
fprintf (stderr, "%s", KCYN);
|
||||||
fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_miR, mi_final);
|
fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08X : %08X ", state->payload_miR, mi_final);
|
||||||
state->payload_miR = mi_final;
|
if (mi_crc_ok == 1) state->payload_miR = mi_final;
|
||||||
|
if (mi_crc_ok == 1) fprintf (stderr, "(CRC OK)");
|
||||||
|
else fprintf (stderr, "(CRC ERR)");
|
||||||
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
|
//run afterwards, or le verification won't match up properly
|
||||||
|
if (state->payload_algidR != 0x21)
|
||||||
|
{
|
||||||
|
LFSR64 (state);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -95,6 +148,8 @@ void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
UNUSED(opts);
|
||||||
|
|
||||||
if (state->currentslot == 0)
|
if (state->currentslot == 0)
|
||||||
{
|
{
|
||||||
state->dropL = 256;
|
state->dropL = 256;
|
||||||
@@ -104,11 +159,13 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
|||||||
state->DMRvcL = 0;
|
state->DMRvcL = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->payload_algid >= 0x21)
|
if (state->payload_algid == 0x21)
|
||||||
{
|
{
|
||||||
LFSR (state);
|
LFSR (state);
|
||||||
fprintf (stderr, "\n");
|
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)
|
if (state->currentslot == 1)
|
||||||
{
|
{
|
||||||
@@ -119,21 +176,319 @@ void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
|||||||
state->DMRvcR = 0;
|
state->DMRvcR = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->payload_algidR >= 0x21)
|
if (state->payload_algidR == 0x21)
|
||||||
{
|
{
|
||||||
LFSR (state);
|
LFSR (state);
|
||||||
fprintf (stderr, "\n");
|
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)
|
void dmr_alg_reset (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
UNUSED(opts);
|
||||||
|
|
||||||
state->dropL = 256;
|
state->dropL = 256;
|
||||||
state->dropR = 256;
|
state->dropR = 256;
|
||||||
state->DMRvcL = 0;
|
state->DMRvcL = 0;
|
||||||
state->DMRvcR = 0;
|
state->DMRvcR = 0;
|
||||||
state->payload_miP = 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];
|
||||||
|
uint8_t sbrc_retcrc[32]; //crc significant bits of the return for SB
|
||||||
|
memset (sbrc_interleaved, 0, sizeof(sbrc_interleaved));
|
||||||
|
memset (sbrc_return, 0, sizeof(sbrc_return));
|
||||||
|
memset (sbrc_retcrc, 0, sizeof(sbrc_retcrc));
|
||||||
|
uint32_t irr_err = 0;
|
||||||
|
uint32_t sbrc_hex = 0;
|
||||||
|
uint16_t crc_extracted = 7777;
|
||||||
|
uint16_t crc_computed = 9999;
|
||||||
|
uint8_t crc7_okay = 0; //RC
|
||||||
|
uint8_t crc3_okay = 0; //TXI
|
||||||
|
uint8_t txi = 0; //SEE: https://patents.google.com/patent/US8271009B2
|
||||||
|
UNUSED(txi);
|
||||||
|
|
||||||
|
//NOTE: Any previous mentions to Cap+ in this area may have been in error,
|
||||||
|
//The signalling observed here was actually TXI information, not Cap+ Specifically
|
||||||
|
|
||||||
|
//check to see if this a TXI system
|
||||||
|
if (slot == 0 && state->dmr_so & 0x20) txi = 1;
|
||||||
|
if (slot == 1 && state->dmr_soR & 0x20) txi = 1;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
//arrange the return for proper order for the crc3 check
|
||||||
|
for (i = 0; i < 8; i++) sbrc_retcrc[i] = sbrc_return[i+3];
|
||||||
|
|
||||||
|
//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 -- See TXI patent
|
||||||
|
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, 4); //#187 fix
|
||||||
|
if (crc_extracted == crc_computed) crc7_okay = 1;
|
||||||
|
}
|
||||||
|
else //if (txi == 1) //if TXI -- but TXI systems also carry the non-crc protected ENC identifiers
|
||||||
|
{
|
||||||
|
crc_extracted = 0;
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
crc_extracted = crc_extracted << 1;
|
||||||
|
crc_extracted = crc_extracted | sbrc_return[i]; //first 3 most significant bits
|
||||||
|
}
|
||||||
|
crc_computed = crc3((uint8_t *) sbrc_retcrc, 8); //working now seems consistent as well
|
||||||
|
if (crc_extracted == crc_computed) crc3_okay = 1;
|
||||||
|
// fprintf (stderr, " CRC EXT %02X, CRC CMP %02X", crc_extracted, crc_computed);
|
||||||
|
}
|
||||||
|
// else //crc_okay = 0; //CRC invalid / not available when is ENC Identifiers (I really hate that)
|
||||||
|
|
||||||
|
for(i = 0; i < 11; i++)
|
||||||
|
{
|
||||||
|
sbrc_hex = sbrc_hex << 1;
|
||||||
|
sbrc_hex |= sbrc_return[i] & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->payload == 1) //hide the sb/rc behind the payload printer, won't be useful to most people
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%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) //forego this since the crc can vary or not be used at all
|
||||||
|
// {
|
||||||
|
// fprintf (stderr, "%s", KRED);
|
||||||
|
// fprintf (stderr, " (CRC ERR)");
|
||||||
|
// fprintf (stderr, "%s", KNRM);
|
||||||
|
// fprintf (stderr, " CRC EXT %02X, CRC CMP %02X", crc_extracted, crc_computed);
|
||||||
|
// }
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t sbrc_opcode = sbrc_hex & 0x7; //opcode and alg the same bits, but the alg is present when CRC is bad (I know they are limited on bits, but I hate that idea)
|
||||||
|
uint8_t alg = sbrc_hex & 0x7; //SEE: https://patents.google.com/patent/EP2347540B1/en
|
||||||
|
uint8_t key = (sbrc_hex >> 3) & 0xFF;
|
||||||
|
uint8_t txi_delay = (sbrc_hex >> 3) & 0x1F; //middle five are the 'delay' value on a TXI system
|
||||||
|
|
||||||
|
//Note: AES-256 Key 1 will pass a CRC3 due to its bit arrangement vs the CRC poly 1101
|
||||||
|
// if (irr_err == 0 && sbrc_hex = 0xD) crc3_okay = 0; //SB: 00000001101
|
||||||
|
|
||||||
|
//NOTE: on above, I belive that we need to check by opcode as well, as a CRC3 can have multiple collisions
|
||||||
|
//so we need to exclude op/alg 0 and 3 from the check (does algID 0x03/0x23 even exist?)
|
||||||
|
|
||||||
|
if (opts->dmr_le == 1)
|
||||||
|
{
|
||||||
|
if (irr_err != 0)
|
||||||
|
{
|
||||||
|
uint32_t sbrcpl = 0;
|
||||||
|
for(i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
sbrcpl = sbrcpl << 1;
|
||||||
|
sbrcpl |= sbrc_interleaved[i] & 1;
|
||||||
|
}
|
||||||
|
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s SLOT %d SB/RC (FEC ERR) E:%d; I:%08X D:%03X; %s ", KRED, slot+1, irr_err, sbrcpl, sbrc_hex, KNRM);
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
if (irr_err == 0)
|
||||||
|
{
|
||||||
|
if (sbrc_hex == 0 && crc7_okay == 0) ; //NULL Single Burst, do nothing (changed to allow the 0 value of RC below to trigger)
|
||||||
|
|
||||||
|
//else if (placeholder for future conditions)
|
||||||
|
// {
|
||||||
|
// placeholder for future conditions
|
||||||
|
// }
|
||||||
|
|
||||||
|
else if (crc7_okay == 1)
|
||||||
|
{
|
||||||
|
//decode the reverse channel information ETSI TS 102 361-4 V1.12.1 (2023-07) p 103 Table 6.32: MS Reverse Channel (RC) Command Information Elements
|
||||||
|
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
sbrc_hex = sbrc_hex >> 7; //set value to its 4-bit form
|
||||||
|
if (sbrc_hex == 0) fprintf (stderr, " RC: Increase Power By One Step;");
|
||||||
|
else if (sbrc_hex == 1) fprintf (stderr, " RC: Decrease Power By One Step;");
|
||||||
|
else if (sbrc_hex == 2) fprintf (stderr, " RC: Set Power To Highest;");
|
||||||
|
else if (sbrc_hex == 3) fprintf (stderr, " RC: Set Power To Lowest;");
|
||||||
|
else if (sbrc_hex == 4) fprintf (stderr, " RC: Cease Transmission Command;");
|
||||||
|
else if (sbrc_hex == 5) fprintf (stderr, " RC: Cease Transmission Request;");
|
||||||
|
else fprintf (stderr, " RC: Reserved %02X;", sbrc_hex);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//if the call is interruptable (TXI) and the crc3 is okay and TXI Opcode
|
||||||
|
else if (crc3_okay == 1 && (sbrc_opcode == 0 || sbrc_opcode == 3) )
|
||||||
|
{
|
||||||
|
//opcodes -- 0 (NULL), BR Delay (3)
|
||||||
|
if (opts->payload == 0) fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " TXI Op: %X -", sbrc_opcode);
|
||||||
|
if (sbrc_opcode == 0) fprintf (stderr, " Null; ");
|
||||||
|
else if (sbrc_opcode == 3)
|
||||||
|
{
|
||||||
|
if (txi_delay != 0)
|
||||||
|
fprintf (stderr, " BR Delay: %d - %d ms;", txi_delay, txi_delay * 30); //could also indicate number of superframes until next VC6 pre-emption
|
||||||
|
else fprintf (stderr, "BR Delay: Irrelevant / Send at any time;");
|
||||||
|
|
||||||
|
//alignment of inbound backwards channel and outbound burst
|
||||||
|
if (txi_delay == 2) fprintf (stderr, " SF3, Burst E;"); //E is inbound
|
||||||
|
if (txi_delay == 4) fprintf (stderr, " SF3, Burst D;"); //D is inbound
|
||||||
|
if (txi_delay == 6) fprintf (stderr, " SF3, Burst C;"); //C is inbound
|
||||||
|
if (txi_delay == 8) fprintf (stderr, " SF3, Burst B;"); //B is inbound
|
||||||
|
}
|
||||||
|
else fprintf (stderr, " Unk; ");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "\n"); //only during payload
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (sbrc_opcode != 0 && sbrc_opcode != 3) //all that should be left in this field is the potential ENC identifiers
|
||||||
|
{
|
||||||
|
|
||||||
|
if (slot == 0) //may not need the state->errs anymore //&& state->errs < 3
|
||||||
|
{
|
||||||
|
if (state->dmr_so & 0x40 && key != 0 && alg != 0)
|
||||||
|
{
|
||||||
|
//if we aren't forcing a particular alg or privacy key set
|
||||||
|
if (state->M == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " Slot 1");
|
||||||
|
fprintf (stderr, " DMR LE SB ALG ID: 0x%02X KEY ID: 0x%02X", alg + 0x20, key);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "\n");
|
||||||
|
if (state->payload_keyid != key)
|
||||||
|
state->payload_keyid = key;
|
||||||
|
if (state->payload_algid != alg)
|
||||||
|
state->payload_algid = alg + 0x20; //assuming DMRA approved alg values (moto patent)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot == 1) //may not need the state->errs anymore //&& state->errsR < 3
|
||||||
|
{
|
||||||
|
if (state->dmr_soR & 0x40 && key != 0 && alg != 0)
|
||||||
|
{
|
||||||
|
//if we aren't forcing a particular alg or privacy key set
|
||||||
|
if (state->M == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "%s", KCYN);
|
||||||
|
fprintf (stderr, " Slot 2");
|
||||||
|
fprintf (stderr, " DMR LE SB ALG ID: 0x%02X KEY ID: 0x%02X", alg + 0x20, key);
|
||||||
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
if (opts->payload == 1) fprintf (stderr, "\n");
|
||||||
|
if (state->payload_keyidR != key)
|
||||||
|
state->payload_keyidR = key;
|
||||||
|
if (state->payload_algidR != alg)
|
||||||
|
state->payload_algidR = alg + 0x20; //assuming DMRA approved alg values (moto patent)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SBRC_END:
|
||||||
|
|
||||||
|
//'DSP' output to file -- SB and RC
|
||||||
|
if (opts->use_dsp_output == 1)
|
||||||
|
{
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
pFile = fopen (opts->dsp_out_file, "a");
|
||||||
|
fprintf (pFile, "\n%d 99 ", slot+1); //'99' is SB and RC designation value
|
||||||
|
for (i = 0; i < 12; i++) //48 bits (includes CC, PPI, LCSS, and QR)
|
||||||
|
// for (i = 2; i < 10; i++) //32 bits (only SB/RC Data and its PC/H)
|
||||||
|
{
|
||||||
|
uint8_t sbrc_nib = (state->dmr_embedded_signalling[slot][5][(i*4)+0] << 3) | (state->dmr_embedded_signalling[slot][5][(i*4)+1] << 2) | (state->dmr_embedded_signalling[slot][5][(i*4)+2] << 1) | (state->dmr_embedded_signalling[slot][5][(i*4)+3] << 0);
|
||||||
|
fprintf (pFile, "%X", sbrc_nib);
|
||||||
|
}
|
||||||
|
fclose (pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t crc3(uint8_t bits[], unsigned int len)
|
||||||
|
{
|
||||||
|
uint8_t crc=0;
|
||||||
|
unsigned int K = 3;
|
||||||
|
//x^3+x+1
|
||||||
|
uint8_t poly[4] = {1,1,0,1};
|
||||||
|
uint8_t buf[256];
|
||||||
|
if (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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t crc4(uint8_t bits[], unsigned int len)
|
||||||
|
{
|
||||||
|
uint8_t crc=0;
|
||||||
|
unsigned int K = 4;
|
||||||
|
//x^4+x+1
|
||||||
|
uint8_t poly[5] = {1,0,0,1,1};
|
||||||
|
uint8_t buf[256];
|
||||||
|
if (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 ^ 0xF; //invert
|
||||||
|
}
|
||||||
|
|||||||
327
src/dmr_ms.c
@@ -13,68 +13,52 @@
|
|||||||
void dmrMS (dsd_opts * opts, dsd_state * state)
|
void dmrMS (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
|
||||||
int i, j, k, l, dibit;
|
char * timestr = getTimeC();
|
||||||
int *dibit_p;
|
|
||||||
|
int i, j, dibit;
|
||||||
char ambe_fr[4][24];
|
char ambe_fr[4][24];
|
||||||
char ambe_fr2[4][24];
|
char ambe_fr2[4][24];
|
||||||
char ambe_fr3[4][24];
|
char ambe_fr3[4][24];
|
||||||
char ambe_fr4[4][24];
|
char ambe_fr4[4][24];
|
||||||
|
|
||||||
//memcpy of ambe_fr for late entry
|
//memcpy of ambe_fr for late entry
|
||||||
char m1[4][24];
|
uint8_t m1[4][24];
|
||||||
char m2[4][24];
|
uint8_t m2[4][24];
|
||||||
char m3[4][24];
|
uint8_t m3[4][24];
|
||||||
|
|
||||||
const int *w, *x, *y, *z;
|
const int *w, *x, *y, *z;
|
||||||
char sync[25];
|
|
||||||
char syncdata[48];
|
uint8_t syncdata[48];
|
||||||
char EmbeddedSignalling[16];
|
memset (syncdata, 0, sizeof(syncdata));
|
||||||
|
|
||||||
|
uint8_t emb_pdu[16];
|
||||||
|
memset (emb_pdu, 0, sizeof(emb_pdu));
|
||||||
|
|
||||||
//cach
|
//cach
|
||||||
char cachdata[25];
|
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
|
//cach tact bits
|
||||||
uint8_t tact_bits[7];
|
uint8_t tact_bits[7];
|
||||||
|
|
||||||
uint8_t emb_ok = 0;
|
|
||||||
uint8_t tact_okay = 0;
|
uint8_t tact_okay = 0;
|
||||||
uint8_t cach_err = 0;
|
uint8_t emb_ok = 0;
|
||||||
uint8_t EmbeddedSignallingOk = 0;
|
UNUSED(emb_ok);
|
||||||
|
|
||||||
uint8_t internalslot;
|
uint8_t internalslot;
|
||||||
uint8_t vc;
|
uint8_t vc;
|
||||||
|
|
||||||
|
//assign as nonsensical numbers
|
||||||
|
uint8_t cc = 25;
|
||||||
|
uint8_t power = 9; //power and pre-emption indicator
|
||||||
|
uint8_t lcss = 9;
|
||||||
|
UNUSED2(cc, lcss);
|
||||||
|
|
||||||
//dummy bits to pass to dburst for link control
|
//dummy bits to pass to dburst for link control
|
||||||
uint8_t dummy_bits[196];
|
uint8_t dummy_bits[196];
|
||||||
memset (dummy_bits, 0, sizeof (dummy_bits));
|
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;
|
vc = 2;
|
||||||
|
|
||||||
short int skipcount = 0;
|
|
||||||
|
|
||||||
//Hardset variables for MS/Mono
|
//Hardset variables for MS/Mono
|
||||||
state->currentslot = 0; //0
|
state->currentslot = 0; //0
|
||||||
|
|
||||||
@@ -84,10 +68,14 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
for (j = 0; j < 6; j++) {
|
for (j = 0; j < 6; j++) {
|
||||||
state->dmrburstL = 16;
|
state->dmrburstL = 16;
|
||||||
|
|
||||||
|
memset (ambe_fr, 0, sizeof(ambe_fr));
|
||||||
|
memset (ambe_fr2, 0, sizeof(ambe_fr2));
|
||||||
|
memset (ambe_fr3, 0, sizeof(ambe_fr3));
|
||||||
|
|
||||||
for(i = 0; i < 12; i++)
|
for(i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
|
||||||
cachdata[i] = dibit;
|
cachdata[i] = dibit;
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
@@ -119,7 +107,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 36; i++)
|
for(i = 0; i < 36; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
|
||||||
state->dmr_stereo_payload[i+12] = dibit;
|
state->dmr_stereo_payload[i+12] = dibit;
|
||||||
|
|
||||||
@@ -144,7 +132,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 18; i++)
|
for(i = 0; i < 18; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
|
||||||
state->dmr_stereo_payload[i+48] = dibit;
|
state->dmr_stereo_payload[i+48] = dibit;
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
@@ -168,10 +156,8 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
||||||
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
sync[i] = (dibit | 1) + 48;
|
|
||||||
|
|
||||||
// load the superframe to do embedded signal processing
|
// load the superframe to do embedded signal processing
|
||||||
if(vc > 1 && vc < 6) //grab on vc1 values 2-5 B C D and E
|
if(vc > 1) //grab on vc2 values 2-5 B C D E, and F
|
||||||
{
|
{
|
||||||
state->dmr_embedded_signalling[internalslot][vc-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
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
|
state->dmr_embedded_signalling[internalslot][vc-1][i*2+1] = (1 & dibit); // bit 0
|
||||||
@@ -179,18 +165,24 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sync[24] = 0;
|
for(i = 0; i < 8; i++) emb_pdu[i + 0] = syncdata[i];
|
||||||
EmbeddedSignallingOk = -1;
|
for(i = 0; i < 8; i++) emb_pdu[i + 8] = syncdata[i + 40];
|
||||||
if(QR_16_7_6_decode(EmbeddedSignalling))
|
|
||||||
|
emb_ok = -1;
|
||||||
|
if(QR_16_7_6_decode(emb_pdu))
|
||||||
{
|
{
|
||||||
EmbeddedSignallingOk = 1;
|
emb_ok = 1;
|
||||||
|
cc = ((emb_pdu[0] << 3) + (emb_pdu[1] << 2) + (emb_pdu[2] << 1) + emb_pdu[3]);
|
||||||
|
power = emb_pdu[4];
|
||||||
|
lcss = ((emb_pdu[5] << 1) + emb_pdu[6]);
|
||||||
|
state->dmr_color_code = state->color_code = cc;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||||
for(i = 0; i < 18; i++)
|
for(i = 0; i < 18; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
|
||||||
state->dmr_stereo_payload[i+90] = dibit;
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
@@ -214,7 +206,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
for(i = 0; i < 36; i++)
|
for(i = 0; i < 36; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
|
|
||||||
state->dmr_stereo_payload[i+108] = dibit;
|
state->dmr_stereo_payload[i+108] = dibit;
|
||||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
@@ -227,6 +219,20 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//'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;
|
state->dmr_ms_mode = 1;
|
||||||
|
|
||||||
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
|
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
|
||||||
@@ -241,19 +247,55 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->directmode == 0)
|
if (state->directmode == 0)
|
||||||
{
|
{
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu));
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu));
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio on tdma direct
|
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio on tdma direct
|
||||||
|
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu));
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu));
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Consider copying f_l to f_r for left and right channel saturation on MS mode
|
||||||
|
if (opts->floating_point == 0)
|
||||||
|
{
|
||||||
|
// memcpy (state->s_r4, state->s_l4, sizeof(state->s_l4));
|
||||||
|
if(opts->pulse_digi_out_channels == 2)
|
||||||
|
playSynthesizedVoiceSS3(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->floating_point == 1)
|
||||||
|
{
|
||||||
|
// memcpy (state->f_r4, state->f_l4, sizeof(state->f_l4));
|
||||||
|
if(opts->pulse_digi_out_channels == 2)
|
||||||
|
playSynthesizedVoiceFS3(opts, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vc == 6)
|
if (vc == 6)
|
||||||
{
|
{
|
||||||
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
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");
|
fprintf (stderr, "\n");
|
||||||
dmr_alg_refresh (opts, state);
|
dmr_alg_refresh (opts, state);
|
||||||
}
|
}
|
||||||
@@ -268,11 +310,16 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
state->last_vc_sync_time = time(NULL);
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
|
||||||
vc++;
|
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
|
//this is necessary because we need to skip and collect dibits, not just skip them
|
||||||
if (vc > 6) goto END;
|
if (vc > 6) goto END;
|
||||||
|
|
||||||
skipDibit (opts, state, 144); //skip to next tdma channel
|
skipDibit (opts, state, 144); //skip to next tdma channel
|
||||||
state->dmr_ms_rc = 0;
|
|
||||||
|
|
||||||
//since we are in a loop, run ncursesPrinter here
|
//since we are in a loop, run ncursesPrinter here
|
||||||
if (opts->use_ncurses_terminal == 1)
|
if (opts->use_ncurses_terminal == 1)
|
||||||
@@ -292,7 +339,7 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if (opts->inverted_dmr == 1)
|
if (opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2) & 3;
|
||||||
}
|
}
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
|
|
||||||
@@ -300,67 +347,44 @@ void dmrMS (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
state->dmr_stereo = 0;
|
state->dmr_stereo = 0;
|
||||||
state->dmr_ms_mode = 0;
|
state->dmr_ms_mode = 0;
|
||||||
state->dmr_ms_rc = 0;
|
|
||||||
state->directmode = 0; //flag off
|
state->directmode = 0; //flag off
|
||||||
|
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//collect buffered 1st half and get 2nd half voice payload and then jump to full MS Voice decoding.
|
//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)
|
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
int i, j, k, l, dibit;
|
|
||||||
|
char * timestr = getTimeC();
|
||||||
|
|
||||||
|
int i, dibit;
|
||||||
int *dibit_p;
|
int *dibit_p;
|
||||||
|
|
||||||
char ambe_fr[4][24];
|
char ambe_fr[4][24];
|
||||||
char ambe_fr2[4][24];
|
char ambe_fr2[4][24];
|
||||||
char ambe_fr3[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
|
char ambe_fr4[4][24];
|
||||||
|
|
||||||
|
memset (ambe_fr, 0, sizeof(ambe_fr));
|
||||||
|
memset (ambe_fr2, 0, sizeof(ambe_fr2));
|
||||||
|
memset (ambe_fr3, 0, sizeof(ambe_fr3));
|
||||||
|
|
||||||
//memcpy of ambe_fr for late entry
|
//memcpy of ambe_fr for late entry
|
||||||
char m1[4][24];
|
uint8_t m1[4][24];
|
||||||
char m2[4][24];
|
uint8_t m2[4][24];
|
||||||
char m3[4][24];
|
uint8_t m3[4][24];
|
||||||
|
|
||||||
const int *w, *x, *y, *z;
|
const int *w, *x, *y, *z;
|
||||||
char sync[25];
|
|
||||||
char syncdata[48];
|
|
||||||
|
|
||||||
//cach
|
//cach
|
||||||
char cachdata[25];
|
char cachdata[25];
|
||||||
int cachInterleave[24] =
|
UNUSED(cachdata);
|
||||||
{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->dmrburstL = 16;
|
||||||
state->currentslot = 0; //force to slot 0
|
state->currentslot = 0; //force to slot 0
|
||||||
@@ -379,7 +403,7 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = state->dmr_stereo_payload[i];
|
dibit = state->dmr_stereo_payload[i];
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2) & 3;
|
||||||
}
|
}
|
||||||
cachdata[i] = dibit;
|
cachdata[i] = dibit;
|
||||||
}
|
}
|
||||||
@@ -398,7 +422,7 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = state->dmr_stereo_payload[i+12];
|
dibit = state->dmr_stereo_payload[i+12];
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2) & 3;
|
||||||
}
|
}
|
||||||
state->dmr_stereo_payload[i+12] = dibit;
|
state->dmr_stereo_payload[i+12] = dibit;
|
||||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
@@ -425,7 +449,7 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = state->dmr_stereo_payload[i+48];
|
dibit = state->dmr_stereo_payload[i+48];
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2) & 3;
|
||||||
}
|
}
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||||
@@ -443,8 +467,9 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2) & 3;
|
||||||
}
|
}
|
||||||
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -469,8 +494,9 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1)
|
if(opts->inverted_dmr == 1)
|
||||||
{
|
{
|
||||||
dibit = (dibit ^ 2);
|
dibit = (dibit ^ 2) & 3;
|
||||||
}
|
}
|
||||||
|
state->dmr_stereo_payload[i+108] = dibit;
|
||||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
@@ -481,13 +507,29 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf (stderr, "%s ", getTime());
|
//'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 ", timestr);
|
||||||
if (opts->inverted_dmr == 0)
|
if (opts->inverted_dmr == 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
|
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
|
||||||
}
|
}
|
||||||
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
|
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
|
||||||
|
if (state->dmr_color_code != 16)
|
||||||
|
fprintf (stderr, "| Color Code=%02d ", state->dmr_color_code);
|
||||||
|
else fprintf (stderr, "| Color Code=XX ");
|
||||||
fprintf (stderr, "| VC* ");
|
fprintf (stderr, "| VC* ");
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
@@ -506,21 +548,60 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->directmode == 0)
|
if (state->directmode == 0)
|
||||||
{
|
{
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu));
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu));
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio
|
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio on tdma direct
|
||||||
|
memcpy(state->f_l4[0], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[0], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[0], state->s_lu, sizeof(state->s_lu));
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||||
|
memcpy(state->f_l4[1], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[1], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[1], state->s_lu, sizeof(state->s_lu));
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||||
|
memcpy(state->f_l4[2], state->audio_out_temp_buf, sizeof(state->audio_out_temp_buf));
|
||||||
|
memcpy(state->s_l4[2], state->s_l, sizeof(state->s_l));
|
||||||
|
memcpy(state->s_l4u[2], state->s_lu, sizeof(state->s_lu));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: Consider copying f_l to f_r for left and right channel saturation on MS mode
|
||||||
|
if (opts->floating_point == 0)
|
||||||
|
{
|
||||||
|
// memcpy (state->s_r4, state->s_l4, sizeof(state->s_l4));
|
||||||
|
if(opts->pulse_digi_out_channels == 2)
|
||||||
|
playSynthesizedVoiceSS3(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->floating_point == 1)
|
||||||
|
{
|
||||||
|
// memcpy (state->f_r4, state->f_l4, sizeof(state->f_l4));
|
||||||
|
if(opts->pulse_digi_out_channels == 2)
|
||||||
|
playSynthesizedVoiceFS3(opts, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
//collect the mi fragment
|
//collect the mi fragment
|
||||||
dmr_late_entry_mi_fragment (opts, state, 1, ambe_fr, ambe_fr2, ambe_fr3);
|
dmr_late_entry_mi_fragment (opts, state, 1, m1, m2, m3);
|
||||||
|
|
||||||
//errors due to skipping other slot
|
//errors due to skipping other slot
|
||||||
// cach_err = dmr_cach (opts, state, cachdata);
|
// cach_err = dmr_cach (opts, state, cachdata);
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
skipDibit (opts, state, 144); //skip to next TDMA slot
|
skipDibit (opts, state, 144); //skip to next TDMA slot
|
||||||
dmrMS (opts, state); //bootstrap into full TDMA frame
|
dmrMS (opts, state); //bootstrap into full TDMA frame
|
||||||
@@ -530,54 +611,39 @@ void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
|||||||
//simplied to a simple data collector, and then passed on to dmr_data_sync for the usual processing
|
//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)
|
void dmrMSData (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
int i, b, c;
|
|
||||||
|
char * timestr = getTimeC();
|
||||||
|
|
||||||
|
int i;
|
||||||
int dibit;
|
int dibit;
|
||||||
int *dibit_p;
|
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
|
//CACH + First Half Payload + Sync = 12 + 54 + 24
|
||||||
dibit_p = state->dmr_payload_p - 90;
|
dibit_p = state->dmr_payload_p - 90;
|
||||||
for (i = 0; i < 90; i++) //90
|
for (i = 0; i < 90; i++) //90
|
||||||
{
|
{
|
||||||
dibit = *dibit_p;
|
dibit = *dibit_p;
|
||||||
dibit_p++;
|
dibit_p++;
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i] = dibit;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 54; i++)
|
for (i = 0; i < 54; i++)
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2) & 3;
|
||||||
state->dmr_stereo_payload[i+90] = dibit;
|
state->dmr_stereo_payload[i+90] = dibit;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf (stderr, "%s ", getTime());
|
fprintf (stderr, "%s ", timestr);
|
||||||
if (opts->inverted_dmr == 0)
|
if (opts->inverted_dmr == 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
|
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
|
||||||
}
|
}
|
||||||
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
|
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
|
||||||
|
if (state->dmr_color_code != 16)
|
||||||
|
fprintf (stderr, "| Color Code=%02d ", state->dmr_color_code);
|
||||||
|
else fprintf (stderr, "| Color Code=XX ");
|
||||||
|
|
||||||
sprintf(state->slot1light, "%s", "");
|
sprintf(state->slot1light, "%s", "");
|
||||||
sprintf(state->slot2light, "%s", "");
|
sprintf(state->slot2light, "%s", "");
|
||||||
@@ -593,16 +659,23 @@ void dmrMSData (dsd_opts * opts, dsd_state * state)
|
|||||||
state->directmode = 0; //flag off
|
state->directmode = 0; //flag off
|
||||||
|
|
||||||
//should just be loaded in the dmr_payload_buffer instead now
|
//should just be loaded in the dmr_payload_buffer instead now
|
||||||
|
//but we want to run getDibit so the buffer has actual good values in it
|
||||||
for (i = 0; i < 144; i++) //66
|
for (i = 0; i < 144; i++) //66
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i] = 1; //set to one so first frame will fail intentionally instead of zero fill
|
||||||
}
|
}
|
||||||
//CACH + First Half Payload = 12 + 54
|
//CACH + First Half Payload = 12 + 54
|
||||||
for (i = 0; i < 66; i++) //66
|
for (i = 0; i < 66; i++) //66
|
||||||
{
|
{
|
||||||
dibit = getDibit(opts, state);
|
dibit = getDibit(opts, state);
|
||||||
state->dmr_stereo_payload[i] = dibit;
|
state->dmr_stereo_payload[i+66] = 1; ////set to one so first frame will fail intentionally instead of zero fill
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
320
src/dmr_pdu.c
@@ -8,45 +8,14 @@
|
|||||||
|
|
||||||
#include "dsd.h"
|
#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[])
|
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 slot = state->currentslot;
|
||||||
uint8_t blocks = state->data_header_blocks[slot];
|
|
||||||
uint8_t padding = state->data_header_padding[slot];
|
|
||||||
uint8_t lrrp_conf = 0;
|
|
||||||
|
|
||||||
//consider disabling on prop_head data blocks if bad/false decodes
|
//check for more available flag info, etc on these prior to running
|
||||||
//if (state->data_p_head[slot] == 0)
|
if (DMR_PDU[0] == 0x01) dmr_locn (opts, state, block_len, DMR_PDU);
|
||||||
lrrp_conf = dmr_lrrp (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
|
//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_conf_data[slot] = 0; //flag off confirmed data after processing it
|
||||||
@@ -57,9 +26,9 @@ void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR
|
|||||||
//The contents of this function are mostly my reversed engineered efforts by observing DSDPlus output and matching data bytes
|
//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)
|
//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)
|
//this is by no means an extensive LRRP list and is prone to error (unless somebody has the manual or something)
|
||||||
uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
void dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||||
{
|
{
|
||||||
int i, j;
|
int i;
|
||||||
uint16_t message_len = 0;
|
uint16_t message_len = 0;
|
||||||
uint8_t slot = state->currentslot;
|
uint8_t slot = state->currentslot;
|
||||||
uint8_t blocks = state->data_header_blocks[slot];
|
uint8_t blocks = state->data_header_blocks[slot];
|
||||||
@@ -82,42 +51,37 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
|
|
||||||
//lat-long-radius-alt
|
//lat-long-radius-alt
|
||||||
uint32_t lat = 0;
|
uint32_t lat = 0;
|
||||||
uint8_t lat_sign = 0;
|
|
||||||
uint32_t lon = 0;
|
uint32_t lon = 0;
|
||||||
uint16_t rad = 0;
|
uint16_t rad = 0; //or Azimuth in Circle 3D?
|
||||||
uint8_t alt = 0;
|
uint8_t alt = 0;
|
||||||
double lat_unit = (double)180/(double)4294967295;
|
double lat_unit = (double)180/(double)4294967295;
|
||||||
double lon_unit = (double)360/(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
|
//speed -- NOTE: Velocity is Speed + Direction
|
||||||
uint16_t vel = 0;
|
uint16_t vel = 0;
|
||||||
double velocity = 0;
|
double velocity = 0;
|
||||||
uint8_t vel_set = 0;
|
uint8_t vel_set = 0;
|
||||||
|
UNUSED(vel);
|
||||||
|
|
||||||
//track, direction, degrees
|
//track, direction, degrees
|
||||||
uint8_t degrees = 0;
|
uint8_t degrees = 0;
|
||||||
uint8_t deg_set = 0;
|
uint8_t deg_set = 0;
|
||||||
|
|
||||||
|
char deg_glyph[4];
|
||||||
|
sprintf (deg_glyph, "%s", "°");
|
||||||
|
// sprintf (deg_glyph, "%s", "d");
|
||||||
|
|
||||||
//triggered information report
|
//triggered information report
|
||||||
uint8_t report = 0;
|
uint8_t report = 0;
|
||||||
uint8_t pot_report = 0; //potential report by finding 0x0D and backtracking a few bytes
|
uint8_t pot_report = 0; //potential report by finding 0x0D and backtracking a few bytes
|
||||||
|
|
||||||
uint8_t is_p_head = 0;
|
|
||||||
|
|
||||||
//shim for getting LRRP out of some prop head data blocks
|
|
||||||
if (state->data_p_head[slot] == 1)
|
|
||||||
{
|
|
||||||
if (DMR_PDU[1] != 0x10) goto LRRP_END; //check to see if FID is 0x10
|
|
||||||
i = 0;
|
|
||||||
message_len = blocks * block_len;
|
|
||||||
source = state->dmr_lrrp_source[state->currentslot];
|
|
||||||
is_p_head = 1;
|
|
||||||
}
|
|
||||||
else i = 12;
|
|
||||||
|
|
||||||
|
|
||||||
//start looking for tokens (using my best understanding of the ok-dmr library xml and python files)
|
//start looking for tokens (using my best understanding of the ok-dmr library xml and python files)
|
||||||
for (i; i < ( (blocks*block_len) - (padding+4) ); i++)
|
for (i = 0; i < ( (blocks*block_len) - (padding+4) ); i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
switch(DMR_PDU[i]){
|
switch(DMR_PDU[i]){
|
||||||
@@ -174,20 +138,22 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
//RESULT TOKEN or Request ID!
|
//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
|
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;
|
i += 2;
|
||||||
break;
|
break;
|
||||||
//answer and report tokens
|
//answer and report tokens
|
||||||
case 0x51: //circle-2d
|
case 0x51: //circle-2d
|
||||||
case 0x54: //circle-3d
|
case 0x54: //circle-3d
|
||||||
case 0x55: //circle-3d <- false positives on one sample, so either an earlier token occurred (that we don't have) or just random data?
|
case 0x55: //circle-3d
|
||||||
if (message_len > 0 && lat == 0)
|
if (message_len > 0 && lat == 0)
|
||||||
{
|
{
|
||||||
if (DMR_PDU[i+1] & 0x80) lat_sign = 1;
|
// lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
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 );
|
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];
|
rad = (DMR_PDU[i+9] << 8) + DMR_PDU[i+10];
|
||||||
i += 10; //double check
|
i += 10;
|
||||||
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
||||||
else lat = 0;
|
else lat = 0;
|
||||||
}
|
}
|
||||||
@@ -213,10 +179,11 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
case 0x66: //point-2d
|
case 0x66: //point-2d
|
||||||
if (message_len > 0 && lat == 0)
|
if (message_len > 0 && lat == 0)
|
||||||
{
|
{
|
||||||
if (DMR_PDU[i+1] & 0x80) lat_sign = 1;
|
// lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
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 );
|
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||||
i += 8; //check
|
i += 8;
|
||||||
lrrp_confidence++;
|
lrrp_confidence++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -224,12 +191,13 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
case 0x6A: //point-3d
|
case 0x6A: //point-3d
|
||||||
if (message_len > 0 && lat == 0)
|
if (message_len > 0 && lat == 0)
|
||||||
{
|
{
|
||||||
if (DMR_PDU[i+1] & 0x80) lat_sign = 1;
|
// lat = ( ( ((DMR_PDU[i+1] & 0x7F ) << 24 ) + (DMR_PDU[i+2] << 16) + (DMR_PDU[i+3] << 8) + DMR_PDU[i+4]) * 1 );
|
||||||
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 );
|
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];
|
alt = DMR_PDU[i+9];
|
||||||
i += 9; //check
|
i += 9;
|
||||||
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
if (lat > 0 && lon > 0) lrrp_confidence++;
|
||||||
else lat = 0;
|
else lat = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -242,8 +210,8 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
case 0x6C: //speed-hor
|
case 0x6C: //speed-hor
|
||||||
if (message_len > 0 && vel_set == 0)
|
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
|
vel = (DMR_PDU[i+1] << 8) + DMR_PDU[i+2]; //raw value
|
||||||
velocity = ( ((double)( (DMR_PDU[i+1] ) + DMR_PDU[i+2] )) / ( (double)128));
|
velocity = ( ((double)( (DMR_PDU[i+1] << 8) + DMR_PDU[i+2] )) / ( (double)255.0f)); //mps
|
||||||
vel_set = 1;
|
vel_set = 1;
|
||||||
i += 2;
|
i += 2;
|
||||||
lrrp_confidence++;
|
lrrp_confidence++;
|
||||||
@@ -273,21 +241,35 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d Octets", lrrp_confidence, message_len);
|
fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d Octets", lrrp_confidence, message_len);
|
||||||
if (lrrp_confidence >= 4) //find the sweet magical number
|
if (lrrp_confidence >= 3) //find the sweet magical number
|
||||||
{
|
{
|
||||||
//now we can open our lrrp file and write to it as well
|
//now we can open our lrrp file and write to it as well
|
||||||
FILE * pFile; //file pointer
|
FILE * pFile; //file pointer
|
||||||
if (opts->lrrp_file_output == 1)
|
if (opts->lrrp_file_output == 1)
|
||||||
{
|
{
|
||||||
|
char * timestr = getTime();
|
||||||
|
char * datestr = getDate();
|
||||||
|
|
||||||
//open file by name that is supplied in the ncurses terminal, or cli
|
//open file by name that is supplied in the ncurses terminal, or cli
|
||||||
pFile = fopen (opts->lrrp_out_file, "a");
|
pFile = fopen (opts->lrrp_out_file, "a");
|
||||||
//write current date/time if not present in LRRP data
|
//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", datestr ); //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, "%s\t", timestr ); //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
|
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 (or destination?) if not available in lrrp data
|
//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, "%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 (source) fprintf (pFile, "%08d\t", source); //add source form decoded audio if available, else its from the header
|
||||||
|
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
if (datestr != NULL)
|
||||||
|
{
|
||||||
|
free (datestr);
|
||||||
|
datestr = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pot_report)
|
if (pot_report)
|
||||||
@@ -327,44 +309,70 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
if (lat)
|
if (lat)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
if (lat_sign) //print a '-' in front of the value
|
//need to check these new calcs for accuracy accross the globe, both lat and lon
|
||||||
fprintf (stderr, " LRRP - Lat: -%.5lf", (double)lat * lat_unit);
|
|
||||||
else fprintf (stderr, " LRRP - Lat: %.5lf", (double)lat * lat_unit);
|
|
||||||
fprintf (stderr, " Lon: %.5lf", (double)lon * lon_unit);
|
|
||||||
if (lat_sign)
|
|
||||||
fprintf (stderr, " (-%.5lf, %.5lf)", (double)lat * lat_unit , (double)lon * lon_unit);
|
|
||||||
else fprintf (stderr, " (%.5lf, %.5lf)", (double)lat * lat_unit , (double)lon * lon_unit);
|
|
||||||
|
|
||||||
if (opts->lrrp_file_output == 1)
|
//two's compliment-ish testing on these bytes
|
||||||
|
if (lat & 0x80000000) //8
|
||||||
{
|
{
|
||||||
if (lat_sign) fprintf (pFile, "-");
|
lat = lat & 0x7FFFFFFF;
|
||||||
fprintf (pFile, "%.5lf\t", (double)lat * lat_unit);
|
lat_sign = -1;
|
||||||
fprintf (pFile, "%.5lf\t", (double)lon * lon_unit);
|
// 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);
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
//always print into the lrrp file, even if zeroes, keep alignment correct
|
||||||
|
if (opts->lrrp_file_output == 1)
|
||||||
|
{
|
||||||
|
fprintf (pFile, "%.5lf\t", lat_fin);
|
||||||
|
fprintf (pFile, "%.5lf\t", lon_fin);
|
||||||
|
//
|
||||||
|
//
|
||||||
}
|
}
|
||||||
if (rad)
|
if (rad)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, " LRRP - Radius: %d", rad); //unsure of 'units' or calculation for radius (meters?)
|
fprintf (stderr, " LRRP - Radius: %dm", rad); //unsure of 'units' or calculation for radius (meters?)
|
||||||
}
|
}
|
||||||
if (alt)
|
if (alt)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, " LRRP - Altitude: %d", alt); //unsure of 'units' or calculation for alt (meters?)
|
fprintf (stderr, " LRRP - Altitude: %dm", alt); //unsure of 'units' or calculation for alt (meters?)
|
||||||
}
|
}
|
||||||
if (vel_set)
|
if (vel_set)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, " LRRP - Velocity: %.4lf m/s %.4lf km/h %.4lf mph", velocity, (3.6 * velocity), (2.2369 * velocity));
|
fprintf (stderr, " LRRP - Speed: %.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 (opts->lrrp_file_output == 1) fprintf (pFile, "%.3lf\t ", (velocity * 3.6) );
|
||||||
}
|
}
|
||||||
|
//always print into the lrrp file, even if zeroes, keep alignment correct
|
||||||
|
if (opts->lrrp_file_output == 1) fprintf (pFile, "%.3lf\t ", (velocity * 3.6) );
|
||||||
if (deg_set)
|
if (deg_set)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, " LRRP - Track: %d Degrees", degrees);
|
fprintf (stderr, " LRRP - Track: %d%s", degrees, deg_glyph);
|
||||||
if (opts->lrrp_file_output == 1) fprintf (pFile, "%d\t",degrees);
|
// if (opts->lrrp_file_output == 1) fprintf (pFile, "%d\t",degrees);
|
||||||
}
|
}
|
||||||
|
//always print into the lrrp file, even if zeroes, keep alignment correct
|
||||||
|
if (opts->lrrp_file_output == 1) fprintf (pFile, "%d\t",degrees);
|
||||||
|
|
||||||
//close open file
|
//close open file
|
||||||
if (opts->lrrp_file_output == 1)
|
if (opts->lrrp_file_output == 1)
|
||||||
@@ -373,20 +381,17 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
fclose (pFile);
|
fclose (pFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
//save to array for ncurses
|
//save to string for ncurses
|
||||||
if (!source) source = state->dmr_lrrp_source[state->currentslot];
|
if (!source) source = state->dmr_lrrp_source[state->currentslot];
|
||||||
char sign[8];
|
|
||||||
char velstr[20];
|
char velstr[20];
|
||||||
char degstr[20];
|
char degstr[20];
|
||||||
char lrrpstr[100];
|
char lrrpstr[100];
|
||||||
sprintf (lrrpstr, "%s", "");
|
sprintf (lrrpstr, "%s", "");
|
||||||
sprintf (velstr, "%s", "");
|
sprintf (velstr, "%s", "");
|
||||||
sprintf (degstr, "%s", "");
|
sprintf (degstr, "%s", "");
|
||||||
if (lat_sign) sprintf (sign, "%s", "-");
|
if (lat) sprintf (lrrpstr, "LRRP %0d (%lf, %lf)", source, lat_fin, lon_fin);
|
||||||
else sprintf (sign, "%s", "");
|
|
||||||
if (lat) sprintf (lrrpstr, "LRRP %0d (%s%lf, %lf)", source, sign, (double)lat * lat_unit, (double)lon * lon_unit);
|
|
||||||
if (vel_set) sprintf (velstr, " %.4lf km/h", velocity * 3.6);
|
if (vel_set) sprintf (velstr, " %.4lf km/h", velocity * 3.6);
|
||||||
if (deg_set) sprintf (degstr, " %d deg", degrees);
|
if (deg_set) sprintf (degstr, " %d%s ", degrees, deg_glyph);
|
||||||
sprintf (state->dmr_lrrp_gps[slot], "%s%s%s", lrrpstr, velstr, degstr);
|
sprintf (state->dmr_lrrp_gps[slot], "%s%s%s", lrrpstr, velstr, degstr);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -399,16 +404,125 @@ uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t
|
|||||||
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
||||||
}
|
}
|
||||||
|
|
||||||
// else //debug only, disabled
|
|
||||||
// {
|
|
||||||
// fprintf (stderr, "\n");
|
|
||||||
// fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d Octets", lrrp_confidence, message_len);
|
|
||||||
// }
|
|
||||||
|
|
||||||
LRRP_END:
|
|
||||||
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
}
|
||||||
return (lrrp_confidence);
|
|
||||||
|
void dmr_locn (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||||
|
{
|
||||||
|
UNUSED(opts);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
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
|
||||||
|
UNUSED2(lat_sign, lon_sign);
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
158
src/dmr_pi.c
@@ -12,82 +12,89 @@
|
|||||||
|
|
||||||
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||||
{
|
{
|
||||||
|
UNUSED2(opts, CRCCorrect);
|
||||||
|
|
||||||
if((IrrecoverableErrors == 0))
|
if((IrrecoverableErrors == 0))
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//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);
|
||||||
|
}
|
||||||
|
|
||||||
if (state->currentslot == 0)
|
if (state->currentslot == 0)
|
||||||
{
|
{
|
||||||
|
state->payload_algid = PI_BYTE[0];
|
||||||
if (0 == 0)
|
state->payload_keyid = PI_BYTE[2];
|
||||||
{
|
|
||||||
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]) );
|
state->payload_mi = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
||||||
if (state->payload_algid < 0x26)
|
if (state->payload_algid < 0x26)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s ", KYEL);
|
fprintf (stderr, "%s ", KYEL);
|
||||||
fprintf (stderr, "\n Slot 1");
|
fprintf (stderr, "\n Slot 1");
|
||||||
fprintf (stderr, " DMR PI Header ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algid, state->payload_keyid, state->payload_mi);
|
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);
|
||||||
|
|
||||||
|
//Anytone RC4 Shim
|
||||||
|
if (state->payload_algid == 0x01)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Anytone (0x01)");
|
||||||
|
state->payload_algid = 0x21;
|
||||||
|
}
|
||||||
|
|
||||||
fprintf (stderr, "%s ", KNRM);
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
if (state->payload_algid != 0x21)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
LFSR64 (state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->payload_algid >= 0x26)
|
if (state->payload_algid >= 0x26)
|
||||||
{
|
{
|
||||||
if (0 == 0)
|
state->payload_algid = 0;
|
||||||
{
|
state->payload_keyid = 0;
|
||||||
state->payload_algid = 0;
|
state->payload_mi = 0;
|
||||||
state->payload_keyid = 0;
|
|
||||||
state->payload_mi = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->currentslot == 1)
|
if (state->currentslot == 1)
|
||||||
{
|
{
|
||||||
if (0 == 0)
|
|
||||||
{
|
|
||||||
state->payload_algidR = PI_BYTE[0];
|
|
||||||
state->payload_keyidR = PI_BYTE[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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]) );
|
state->payload_miR = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
||||||
if (state->payload_algidR < 0x26)
|
if (state->payload_algidR < 0x26)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s ", KYEL);
|
fprintf (stderr, "%s ", KYEL);
|
||||||
fprintf (stderr, "\n Slot 2");
|
fprintf (stderr, "\n Slot 2");
|
||||||
fprintf (stderr, " DMR PI Header ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algidR, state->payload_keyidR, state->payload_miR);
|
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);
|
||||||
|
|
||||||
|
//Anytone RC4 Shim
|
||||||
|
if (state->payload_algidR == 0x01)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Anytone (0x01)");
|
||||||
|
state->payload_algidR = 0x21;
|
||||||
|
}
|
||||||
|
|
||||||
fprintf (stderr, "%s ", KNRM);
|
fprintf (stderr, "%s ", KNRM);
|
||||||
|
if (state->payload_algidR != 0x21)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
LFSR64 (state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->payload_algidR >= 0x26)
|
if (state->payload_algidR >= 0x26)
|
||||||
{
|
{
|
||||||
if (0== 0)
|
state->payload_algidR = 0;
|
||||||
{
|
state->payload_keyidR = 0;
|
||||||
state->payload_algidR = 0;
|
state->payload_miR = 0;
|
||||||
state->payload_keyidR = 0;
|
|
||||||
state->payload_miR = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if((IrrecoverableErrors == 0) && CRCCorrect)
|
|
||||||
{
|
|
||||||
//fprintf (stderr, " (PI CRC Okay)");
|
|
||||||
}
|
}
|
||||||
else if((IrrecoverableErrors == 0))
|
|
||||||
{
|
|
||||||
//fprintf (stderr, " (PI FEC Okay)");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf (stderr, (" (PI FEC ERR)"));
|
|
||||||
fprintf (stderr, "%s ", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LFSR(dsd_state * state)
|
void LFSR(dsd_state * state)
|
||||||
@@ -110,27 +117,64 @@ void LFSR(dsd_state * state)
|
|||||||
|
|
||||||
if (state->currentslot == 0)
|
if (state->currentslot == 0)
|
||||||
{
|
{
|
||||||
if (1 == 1)
|
fprintf (stderr, "%s", KYEL);
|
||||||
{
|
fprintf (stderr, " Slot 1");
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, " DMR PI C- ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
|
||||||
fprintf (stderr, " Slot 1");
|
fprintf(stderr, " MI: 0x%08X", lfsr);
|
||||||
fprintf (stderr, " DMR PI Continuation ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
|
fprintf (stderr, "%s", KNRM);
|
||||||
fprintf(stderr, " Next MI: 0x%08X", lfsr);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
state->payload_mi = lfsr;
|
state->payload_mi = lfsr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->currentslot == 1)
|
if (state->currentslot == 1)
|
||||||
{
|
{
|
||||||
if (1 == 1)
|
|
||||||
{
|
fprintf (stderr, "%s", KYEL);
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, " Slot 2");
|
||||||
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, " DMR PI Continuation ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
|
fprintf(stderr, " MI: 0x%08X", lfsr);
|
||||||
fprintf(stderr, " Next MI: 0x%08X", lfsr);
|
fprintf (stderr, "%s", KNRM);
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
state->payload_miR = lfsr;
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
116
src/dmr_utils.c
@@ -4,7 +4,7 @@
|
|||||||
//Original Source - https://github.com/LouisErigHerve/dsd
|
//Original Source - https://github.com/LouisErigHerve/dsd
|
||||||
|
|
||||||
//Additional Functions
|
//Additional Functions
|
||||||
//Hamming17123, crc8, crc8ok functions
|
//Hamming17123, crc7, crc8, crc8ok functions
|
||||||
//Original Souce - https://github.com/boatbod/op25
|
//Original Souce - https://github.com/boatbod/op25
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
@@ -121,6 +121,31 @@ bool crc8_ok(uint8_t bits[], unsigned int len)
|
|||||||
return (crc == crc8(bits,len));
|
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
|
* @brief : This function compute the CRC-CCITT of the DMR data
|
||||||
* by using the polynomial x^16 + x^12 + x^5 + 1
|
* by using the polynomial x^16 + x^12 + x^5 + 1
|
||||||
@@ -300,15 +325,15 @@ uint8_t ComputeCrc5Bit(uint8_t * DMRData)
|
|||||||
return CRC;
|
return CRC;
|
||||||
} /* End ComputeCrc5Bit() */
|
} /* End ComputeCrc5Bit() */
|
||||||
|
|
||||||
uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength)
|
uint64_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength)
|
||||||
{
|
{
|
||||||
uint32_t Output = 0;
|
uint64_t Output = 0;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
for(i = 0; i < BitLength; i++)
|
for(i = 0; i < BitLength; i++)
|
||||||
{
|
{
|
||||||
Output <<= 1;
|
Output <<= 1;
|
||||||
Output |= (uint32_t)(BufferIn[i] & 1);
|
Output |= (uint64_t)(BufferIn[i] & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Output;
|
return Output;
|
||||||
@@ -330,3 +355,86 @@ uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength)
|
|||||||
*
|
*
|
||||||
* @return The 9 bit CRC
|
* @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() */
|
||||||
|
|||||||
272
src/dpmr_voice.c
@@ -24,11 +24,18 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
uint8_t CCH[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
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 CCHDescrambled[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
||||||
uint8_t CCHDeInterleaved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
uint8_t CCHDeInterleaved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
||||||
uint8_t CCHDataHammingCorrected[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][48] = {0};
|
uint8_t CCHDataHammingCorrected[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][48];
|
||||||
uint8_t CCHDataCRC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
|
uint8_t CCHDataCRC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||||
uint8_t CCHDataCRCComputed[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
|
|
||||||
|
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};
|
uint8_t CC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE / 2][24] = {0};
|
||||||
char ambe_fr[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4][4][24] = {0};
|
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;
|
const int *w, *x, *y, *z;
|
||||||
uint32_t ScramblerLFSR = 0;
|
uint32_t ScramblerLFSR = 0;
|
||||||
bool correctable = true;
|
bool correctable = true;
|
||||||
@@ -46,20 +53,10 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
uint32_t CCH_EmergencyPriority[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_Reserved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||||
uint32_t CCH_SlowData[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 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 CalledID[8] = {0};
|
||||||
uint8_t CallingID[8] = {0};
|
uint8_t CallingID[8] = {0};
|
||||||
|
UNUSED(PartOfSuperFrame);
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
//fprintf(stderr, "VOICE e:");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* First CCH (Control CHannel) - 72 bit */
|
/* First CCH (Control CHannel) - 72 bit */
|
||||||
k = 0;
|
k = 0;
|
||||||
@@ -76,7 +73,6 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
CCH[0][(i * 2)] = (1 & (dibit >> 1)); // bit 1
|
CCH[0][(i * 2)] = (1 & (dibit >> 1)); // bit 1
|
||||||
CCH[0][(i * 2) + 1] = (1 & dibit); // bit 0
|
CCH[0][(i * 2) + 1] = (1 & dibit); // bit 0
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */
|
/* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */
|
||||||
@@ -148,9 +144,6 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
/* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */
|
/* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */
|
||||||
|
|
||||||
//Sync issues occur when FS2 occurs immediatly after the 4th rep, so cutting of 4 early to gain constant resync
|
|
||||||
//probably can't resync fast enough when the next sync starts immediately after the 4th rep of 36 dibits
|
|
||||||
//consider writing a method that will loop this entire process based on the frame numbering found inside the CCH info, is that possible?
|
|
||||||
k = 0;
|
k = 0;
|
||||||
for (j = 0; j < 4; j++)
|
for (j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
@@ -177,16 +170,10 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
x++;
|
x++;
|
||||||
y++;
|
y++;
|
||||||
z++;
|
z++;
|
||||||
//should be fixed now with the frame_sync t_max setup
|
|
||||||
//no more early breaking required
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the Hamming (12,8) error code detection
|
|
||||||
* Not needed because it has already been done
|
|
||||||
* in the "InitAllFecFunction()" function */
|
|
||||||
//Hamming_12_8_init();
|
|
||||||
|
|
||||||
/* Decoding all CCH (Control CHannel) */
|
/* Decoding all CCH (Control CHannel) */
|
||||||
for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++)
|
for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++)
|
||||||
{
|
{
|
||||||
@@ -207,22 +194,8 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
if(HammingCorrectable[i][j] == false) correctable = false;
|
if(HammingCorrectable[i][j] == false) correctable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(correctable)
|
if(correctable) HammingOk[i] = 1;
|
||||||
{
|
else HammingOk[i] = 0;
|
||||||
|
|
||||||
//fprintf(stderr, "CCH data corrected successfully\n");
|
|
||||||
|
|
||||||
|
|
||||||
HammingOk[i] = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
//fprintf(stderr, "ERROR !!! CCH data contains uncorrectable bit\n");
|
|
||||||
|
|
||||||
|
|
||||||
HammingOk[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reconstitute the 7 bit CRC */
|
/* Reconstitute the 7 bit CRC */
|
||||||
CCHDataCRC[i] = 0;
|
CCHDataCRC[i] = 0;
|
||||||
@@ -237,24 +210,8 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
/* Compute the 7 bit CRC */
|
/* Compute the 7 bit CRC */
|
||||||
CCHDataCRCComputed[i] = CRC7BitdPMR(CCHDataHammingCorrected[i], 41);
|
CCHDataCRCComputed[i] = CRC7BitdPMR(CCHDataHammingCorrected[i], 41);
|
||||||
|
|
||||||
if(CCHDataCRC[i] == CCHDataCRCComputed[i])
|
if(CCHDataCRC[i] == CCHDataCRCComputed[i]) CrcOk[i] = 1;
|
||||||
{
|
else CrcOk[i] = 0;
|
||||||
|
|
||||||
//fprintf(stderr, "Good CCH CRCs ! CRC received = 0x%02X, CRC computed = 0x%02X\n",
|
|
||||||
// CCHDataCRC[i], CCHDataCRCComputed[i]);
|
|
||||||
|
|
||||||
|
|
||||||
CrcOk[i] = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
//fprintf(stderr, "ERROR !!! CCH CRCs are different ! CRC received = 0x%02X, CRC computed = 0x%02X\n",
|
|
||||||
// CCHDataCRC[i], CCHDataCRCComputed[i]);
|
|
||||||
|
|
||||||
|
|
||||||
CrcOk[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill the frame number */
|
/* Fill the frame number */
|
||||||
CCH_FrameNumber[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][0], 2);
|
CCH_FrameNumber[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][0], 2);
|
||||||
@@ -300,45 +257,18 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
CallingID[7] = '\0';
|
CallingID[7] = '\0';
|
||||||
|
|
||||||
|
|
||||||
/* Print Hamming debug info */
|
|
||||||
//fprintf(stderr, " | ");
|
|
||||||
for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++)
|
|
||||||
{
|
|
||||||
//fprintf(stderr, "Hamming[%u]=", i);
|
|
||||||
for(j = 0; j < 6; j++)
|
|
||||||
{
|
|
||||||
//if(HammingCorrectable[i][j]) fprintf(stderr, "1");
|
|
||||||
//else fprintf(stderr, "0");
|
|
||||||
}
|
|
||||||
//fprintf(stderr, " HammingOk[%u]=%u", i, HammingOk[i]);
|
|
||||||
//fprintf(stderr, " CrcOk[%u]=%u", i, CrcOk[i]);
|
|
||||||
//fprintf(stderr, " Bit[%u]=", i);
|
|
||||||
//for(j = 0; j < 7; j++)
|
|
||||||
//{
|
|
||||||
// if(CCHDataHammingCorrected[i][j + 41]) fprintf(stderr, "1");
|
|
||||||
// else fprintf(stderr, "0");
|
|
||||||
//}
|
|
||||||
//fprintf(stderr, " CRCrec=0x%02X CRCcomp=0x%02X", CCHDataCRC[i], CCHDataCRCComputed[i]);
|
|
||||||
//if(i < (NB_OF_DPMR_VOICE_FRAME_TO_DECODE - 1)) fprintf(stderr, " - ");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if (NB_OF_DPMR_VOICE_FRAME_TO_DECODE == 2)
|
|
||||||
/* To determine the part of the payload frame, we need to check the
|
/* To determine the part of the payload frame, we need to check the
|
||||||
* Hamming code result, the CRC and the frame number */
|
* Hamming code result, the CRC and the frame number */
|
||||||
|
|
||||||
// if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 0)) ||
|
// if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 0)) ||
|
||||||
// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 1)))
|
// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 1)))
|
||||||
|
|
||||||
if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 0)) ||
|
if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 0)) ||
|
||||||
((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 1)))
|
((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 1)))
|
||||||
{
|
{
|
||||||
/* First part of the super frame */
|
/* First part of the super frame */
|
||||||
PartOfSuperFrame = 1;
|
PartOfSuperFrame = 1;
|
||||||
|
|
||||||
|
|
||||||
//fprintf(stderr, "\nFirst part of the Super Frame");
|
|
||||||
|
|
||||||
|
|
||||||
/* The next part will normally be the second part */
|
/* The next part will normally be the second part */
|
||||||
opts->dPMR_next_part_of_superframe = 2;
|
opts->dPMR_next_part_of_superframe = 2;
|
||||||
|
|
||||||
@@ -366,20 +296,19 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
/* BUGFIX : Copy in all case the TG ID */
|
/* BUGFIX : Copy in all case the TG ID */
|
||||||
strcpy((char *)state->dPMRVoiceFS2Frame.CalledID, (char *)CalledID);
|
strcpy((char *)state->dPMRVoiceFS2Frame.CalledID, (char *)CalledID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* To determine the part of the payload frame, we need to check the
|
/* To determine the part of the payload frame, we need to check the
|
||||||
* Hamming code result, the CRC and the frame number */
|
* Hamming code result, the CRC and the frame number */
|
||||||
|
|
||||||
// else if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 2)) ||
|
// else if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 2)) ||
|
||||||
// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 3)))
|
// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 3)))
|
||||||
|
|
||||||
else if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 2)) ||
|
else if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 2)) ||
|
||||||
((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 3)))
|
((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 3)))
|
||||||
{
|
{
|
||||||
/* Second part of the super frame */
|
/* Second part of the super frame */
|
||||||
PartOfSuperFrame = 2;
|
PartOfSuperFrame = 2;
|
||||||
|
|
||||||
|
|
||||||
//fprintf(stderr, "\nSecond part of the Super Frame");
|
|
||||||
|
|
||||||
|
|
||||||
/* The next part will normally be the first part */
|
/* The next part will normally be the first part */
|
||||||
opts->dPMR_next_part_of_superframe = 1;
|
opts->dPMR_next_part_of_superframe = 1;
|
||||||
|
|
||||||
@@ -392,7 +321,9 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
CallingID[7] = '\0';
|
CallingID[7] = '\0';
|
||||||
|
|
||||||
/* Determine if the calling SRC ID is correct */
|
/* Determine if the calling SRC ID is correct */
|
||||||
|
|
||||||
//if(HammingOk[0] && CrcOk[0] && HammingOk[1] && CrcOk[1])
|
//if(HammingOk[0] && CrcOk[0] && HammingOk[1] && CrcOk[1])
|
||||||
|
|
||||||
if((CrcOk[0] || (HammingCorrectable[0][0] && HammingCorrectable[0][1])) &&
|
if((CrcOk[0] || (HammingCorrectable[0][0] && HammingCorrectable[0][1])) &&
|
||||||
(CrcOk[1] || (HammingCorrectable[1][0] && HammingCorrectable[1][1])))
|
(CrcOk[1] || (HammingCorrectable[1][0] && HammingCorrectable[1][1])))
|
||||||
{
|
{
|
||||||
@@ -420,133 +351,152 @@ void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
|||||||
/* Set the next part of the superframe to receive */
|
/* Set the next part of the superframe to receive */
|
||||||
if(opts->dPMR_next_part_of_superframe == 1)
|
if(opts->dPMR_next_part_of_superframe == 1)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
//fprintf(stderr, "Supposed first part of the Super Frame\n");
|
|
||||||
|
|
||||||
|
|
||||||
/* Set the next part as the second part */
|
/* Set the next part as the second part */
|
||||||
opts->dPMR_next_part_of_superframe = 2;
|
opts->dPMR_next_part_of_superframe = 2;
|
||||||
}
|
}
|
||||||
else if(opts->dPMR_next_part_of_superframe == 2)
|
else if(opts->dPMR_next_part_of_superframe == 2)
|
||||||
{
|
{
|
||||||
|
|
||||||
//fprintf(stderr, "Supposed second part of the Super Frame\n");
|
|
||||||
|
|
||||||
|
|
||||||
/* Set the next part as the first part */
|
/* Set the next part as the first part */
|
||||||
opts->dPMR_next_part_of_superframe = 1;
|
opts->dPMR_next_part_of_superframe = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
//fprintf(stderr, "Unknown part of the Super Frame\n");
|
|
||||||
|
|
||||||
|
|
||||||
/* Unknown current part, set to 0 */
|
/* Unknown current part, set to 0 */
|
||||||
opts->dPMR_next_part_of_superframe = 0;
|
opts->dPMR_next_part_of_superframe = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(state->dPMRVoiceFS2Frame.ColorCode[0] != (unsigned int)(-1))
|
|
||||||
{
|
|
||||||
//only print the CC and src/tg info if CRC is good
|
|
||||||
/* Display the color code */
|
|
||||||
//fprintf(stderr, " | Color Code=%02d ", (int)state->dPMRVoiceFS2Frame.ColorCode[0]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Unknown color code, display "??" */
|
|
||||||
//fprintf(stderr, " | Color Code=?? ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Display the destination ID (talk group - TG) */
|
/* Display the destination ID (talk group - TG) */
|
||||||
//fprintf(stderr, "| TG=%s", CalledID);
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
if(state->dPMRVoiceFS2Frame.CalledIDOk)
|
if(state->dPMRVoiceFS2Frame.CalledIDOk)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KGRN);
|
fprintf (stderr, "%s", KGRN);
|
||||||
fprintf(stderr, "\n| TG=%s", CalledID);
|
fprintf(stderr, " TG=%s", CalledID);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
//strcpy (state->dpmr_target_id, CalledID); //HERE HERE
|
|
||||||
state->dpmr_target_id = CalledID;
|
//check other as well before assigning
|
||||||
//fprintf(stderr, " (CRC OK) ");
|
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))
|
if(state->dPMRVoiceFS2Frame.ColorCode[0] != (unsigned int)(-1))
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KGRN);
|
fprintf (stderr, "%s", KGRN);
|
||||||
fprintf(stderr, " | Color Code=%02d ", (int)state->dPMRVoiceFS2Frame.ColorCode[0]);
|
fprintf(stderr, " Channel Code=%02d", (int)state->dPMRVoiceFS2Frame.ColorCode[0]);
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
state->dpmr_color_code = (int)state->dPMRVoiceFS2Frame.ColorCode[0];
|
|
||||||
|
|
||||||
|
//check other as well before assigning
|
||||||
|
if(state->dPMRVoiceFS2Frame.CalledIDOk) state->dpmr_color_code = (int)state->dPMRVoiceFS2Frame.ColorCode[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
//fprintf(stderr, " (CRC ERR) ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Display the source ID */
|
|
||||||
//fprintf(stderr, "| Src=%s", CallingID);
|
|
||||||
if(state->dPMRVoiceFS2Frame.CallingIDOk)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KGRN);
|
|
||||||
fprintf(stderr, "| Src=%s", CallingID);
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
//strcpy (state->dpmr_caller_id, CallingID);
|
|
||||||
state->dpmr_caller_id = CallingID;
|
|
||||||
|
|
||||||
//fprintf(stderr, " (CRC OK) \n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KRED);
|
fprintf (stderr, "%s", KRED);
|
||||||
fprintf(stderr, " (CRC ERR) \n");
|
fprintf(stderr, " Src=(CRC ERR) Channel Code =(CRC ERR)");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* (NB_OF_DPMR_VOICE_FRAME_TO_DECODE == 2) */
|
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,
|
//Play only voice frames by first looking to see if there is a TCH voice in CommunicationMode,
|
||||||
//then sending it to be processed by processMbeFrame, its much cleaner this way
|
|
||||||
short start = 0;
|
short start = 0;
|
||||||
short end = 4;
|
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++)
|
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
|
//depending on first or second TCH, set start and end variables appropriately
|
||||||
if (o == 1)
|
if (o == 1)
|
||||||
{
|
{
|
||||||
start = 4;
|
start = 4;
|
||||||
end = 8;
|
end = 8;
|
||||||
//add extra line break on second repitition only when dumping payload to console
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
//fprintf (stderr, "\nCommunication Mode: %d\n", state->dPMRVoiceFS2Frame.CommunicationMode[o]);
|
|
||||||
//fprintf (stderr, "Encryption Mode: %d\n", state->dPMRVoiceFS2Frame.Version[o]);
|
//this is used so we can simply pass the voice here to the
|
||||||
//fprintf (stderr, "Superframe Number: %d", state->dPMRVoiceFS2Frame.FrameNumbering[o]); //should be 1-4, but may be same number on TCH 1 or TCH 2
|
//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) ||
|
if((state->dPMRVoiceFS2Frame.CommunicationMode[o] == 0) ||
|
||||||
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 1) ||
|
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 1) ||
|
||||||
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 5))
|
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 5))
|
||||||
{
|
{
|
||||||
|
|
||||||
//There are 4 AMBE voice samples per TCH
|
//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;
|
||||||
|
|
||||||
|
//print Current Frame Number if payload verbosity enabled
|
||||||
|
if (opts->payload == 1)
|
||||||
|
fprintf (stderr, "\n FN %d/4", state->dPMRVoiceFS2Frame.FrameNumbering[o]+1);
|
||||||
|
|
||||||
|
//There are 4 AMBE voice frames per TCH
|
||||||
for(i = start; i < end; i++)
|
for(i = start; i < end; i++)
|
||||||
{
|
{
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr[i], NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr[i], NULL);
|
||||||
} //End for(i = start; i < end); i++)
|
if (opts->floating_point == 0)
|
||||||
} //if comms and not encrypted
|
playSynthesizedVoiceMS(opts, state);
|
||||||
}//End for TCH for o loop
|
if (opts->floating_point == 1)
|
||||||
|
playSynthesizedVoiceFM(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the correct sync type again and flag off the cipher
|
||||||
|
state->synctype = realsynctype;
|
||||||
|
state->nxdn_cipher_type = 0;
|
||||||
|
|
||||||
if (opts->errorbars == 1)
|
}
|
||||||
{
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
|
||||||
//keep code below, could be handy cheat sheet for other info
|
//keep code below, could be handy cheat sheet for other info
|
||||||
#ifdef dPMR_PRINT_DEBUG_INFO
|
#ifdef dPMR_PRINT_DEBUG_INFO
|
||||||
//Display the CCH content
|
//Display the CCH content
|
||||||
|
|||||||
461
src/dsd_audio.c
@@ -21,14 +21,16 @@ pa_sample_spec ss;
|
|||||||
pa_sample_spec tt;
|
pa_sample_spec tt;
|
||||||
pa_sample_spec zz;
|
pa_sample_spec zz;
|
||||||
pa_sample_spec cc;
|
pa_sample_spec cc;
|
||||||
|
pa_sample_spec ff; //float
|
||||||
|
|
||||||
|
//pulse audio buffer attributes (input latency)
|
||||||
|
pa_buffer_attr lt;
|
||||||
|
|
||||||
void closePulseOutput (dsd_opts * opts)
|
void closePulseOutput (dsd_opts * opts)
|
||||||
{
|
{
|
||||||
pa_simple_free (opts->pulse_digi_dev_out);
|
pa_simple_free (opts->pulse_digi_dev_out);
|
||||||
if (opts->dmr_stereo == 1)
|
if (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) //EDACS analog calls and/or monitoring source analog audio
|
||||||
{
|
pa_simple_free (opts->pulse_raw_dev_out);
|
||||||
pa_simple_free (opts->pulse_digi_dev_outR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void closePulseInput (dsd_opts * opts)
|
void closePulseInput (dsd_opts * opts)
|
||||||
@@ -39,33 +41,66 @@ void closePulseInput (dsd_opts * opts)
|
|||||||
void openPulseOutput(dsd_opts * opts)
|
void openPulseOutput(dsd_opts * opts)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
char * dev = NULL;
|
||||||
|
if (opts->pa_output_idx[0] != 0)
|
||||||
|
dev = opts->pa_output_idx;
|
||||||
|
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
ss.format = PA_SAMPLE_S16NE;
|
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.channels = opts->pulse_raw_out_channels;
|
||||||
ss.rate = opts->pulse_raw_rate_out; //48000
|
ss.rate = opts->pulse_raw_rate_out;
|
||||||
|
|
||||||
tt.format = PA_SAMPLE_S16NE;
|
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.channels = opts->pulse_digi_out_channels;
|
||||||
tt.rate = opts->pulse_digi_rate_out; //48000, switches to 8000 when using RTL dongle
|
tt.rate = opts->pulse_digi_rate_out;
|
||||||
|
|
||||||
if (opts->monitor_input_audio == 1)
|
ff.format = PA_SAMPLE_FLOAT32NE;
|
||||||
|
ff.channels = opts->pulse_digi_out_channels;
|
||||||
|
ff.rate = opts->pulse_digi_rate_out;
|
||||||
|
|
||||||
|
//reconfigured to open when using edacs or raw analog monitor so we can have a analog audio out that runs at 48k1 and not 8k1 float/short
|
||||||
|
if (opts->frame_provoice == 1 || opts->monitor_input_audio == 1)
|
||||||
|
opts->pulse_raw_dev_out = pa_simple_new(NULL, "DSD-FME3", PA_STREAM_PLAYBACK, dev, "Analog", &ss, 0, NULL, &err);
|
||||||
|
|
||||||
|
if (err != 0)
|
||||||
{
|
{
|
||||||
opts->pulse_raw_dev_out = pa_simple_new(NULL, "DSD-FME3", PA_STREAM_PLAYBACK, NULL, "Raw Audio Out", &ss, NULL, NULL, NULL);
|
fprintf (stderr, "Err: %d; %s; ", err, pa_strerror(err));
|
||||||
|
#ifdef __CYGWIN__
|
||||||
|
fprintf (stderr, "Please make sure the Pulse Audio Server Backend is running first.");
|
||||||
|
#endif
|
||||||
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//tt
|
pa_channel_map* fl = 0; //NULL and 0 are same in this context
|
||||||
pa_channel_map* left = 0; //NULL and 0 are same in this context
|
pa_channel_map* ss = 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)
|
if (opts->floating_point == 0)
|
||||||
{
|
{
|
||||||
opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, NULL, opts->output_name, &tt, left, NULL, NULL);
|
opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, dev, opts->output_name, &tt, ss, NULL, &err);
|
||||||
|
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Err: %d; %s; ", err, pa_strerror(err));
|
||||||
|
#ifdef __CYGWIN__
|
||||||
|
fprintf (stderr, "Please make sure the Pulse Audio Server Backend is running first.");
|
||||||
|
#endif
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->dmr_stereo == 1)
|
if (opts->floating_point == 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_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, dev, opts->output_name, &ff, fl, NULL, &err);
|
||||||
opts->pulse_digi_dev_outR = pa_simple_new(NULL, "DSD-FME2", PA_STREAM_PLAYBACK, NULL, "XDMA SLOT 2", &tt, right, NULL, NULL);
|
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Err: %d; %s; ", err, pa_strerror(err));
|
||||||
|
#ifdef __CYGWIN__
|
||||||
|
fprintf (stderr, "Please make sure the Pulse Audio Server Backend is running first.");
|
||||||
|
#endif
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -73,12 +108,198 @@ void openPulseOutput(dsd_opts * opts)
|
|||||||
void openPulseInput(dsd_opts * opts)
|
void openPulseInput(dsd_opts * opts)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
char * dev = NULL;
|
||||||
|
if (opts->pa_input_idx[0] != 0)
|
||||||
|
dev = opts->pa_input_idx;
|
||||||
|
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
cc.format = PA_SAMPLE_S16NE;
|
cc.format = PA_SAMPLE_S16NE;
|
||||||
cc.channels = opts->pulse_digi_in_channels;
|
cc.channels = opts->pulse_digi_in_channels;
|
||||||
cc.rate = opts->pulse_digi_rate_in; //48000
|
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);
|
//adjust input latency settings (defaults are all -1 to let server set these automatically, but without pavucontrol open, this is approx 2s)
|
||||||
|
//setting fragsize to 960 for 48000 input seems to do the trick to allow a much faster latency without underrun, may need adjusting if
|
||||||
|
//modifying the input rate (will only apply IF using pulse audio input, and not any other input method)
|
||||||
|
|
||||||
|
//TODO: set fragsize vs expected input rate if required in the future
|
||||||
|
//NOTE: If users report any underrun conditions, then either change 960 back to -1, or pass NULL instead of <
|
||||||
|
|
||||||
|
//https://freedesktop.org/software/pulseaudio/doxygen/structpa__buffer__attr.html
|
||||||
|
|
||||||
|
//for now, only going to modify the fragsize if using the encoder, else, let the pa server set it automatically
|
||||||
|
// if (opts->m17encoder == 1)
|
||||||
|
// lt.fragsize = 960*5;
|
||||||
|
// else lt.fragsize = -1;
|
||||||
|
|
||||||
|
//test doing it universally, fall back to above if needed
|
||||||
|
lt.fragsize = 960*5;
|
||||||
|
lt.maxlength = -1;
|
||||||
|
lt.prebuf = -1;
|
||||||
|
lt.tlength = -1;
|
||||||
|
if (opts->m17encoder == 1)
|
||||||
|
opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME4", PA_STREAM_RECORD, dev, "M17 Voice Input", &cc, NULL, <, &err);
|
||||||
|
else opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, dev, opts->output_name, &cc, NULL, <, &err);
|
||||||
|
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Err: %d; %s; ", err, pa_strerror(err));
|
||||||
|
#ifdef __CYGWIN__
|
||||||
|
fprintf (stderr, "Please make sure the Pulse Audio Server Backend is running first.");
|
||||||
|
#endif
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug
|
||||||
|
// if (opts->m17encoder == 1)
|
||||||
|
// {
|
||||||
|
// unsigned long long int latency = pa_simple_get_latency (opts->pulse_digi_dev_in, NULL);
|
||||||
|
// fprintf (stderr, "Pulse Audio Input Latency: %05lld;", latency);
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_pulse_input_string (dsd_opts * opts, char * input)
|
||||||
|
{
|
||||||
|
char * curr;
|
||||||
|
curr = strtok(input, ":");
|
||||||
|
if (curr != NULL)
|
||||||
|
{
|
||||||
|
strncpy (opts->pa_input_idx, curr, 99);
|
||||||
|
opts->pa_input_idx[99] = 0;
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "Pulse Input Device: %s; ", opts->pa_input_idx);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_pulse_output_string (dsd_opts * opts, char * input)
|
||||||
|
{
|
||||||
|
char * curr;
|
||||||
|
curr = strtok(input, ":");
|
||||||
|
if (curr != NULL)
|
||||||
|
{
|
||||||
|
strncpy (opts->pa_output_idx, curr, 99);
|
||||||
|
opts->pa_output_idx[99] = 0;
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, "Pulse Output Device: %s; ", opts->pa_output_idx);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void openOSSOutput (dsd_opts * opts)
|
||||||
|
{
|
||||||
|
int fmt;
|
||||||
|
int speed = 48000;
|
||||||
|
if (opts->audio_in_type == 5) //if((strncmp(opts->audio_in_dev, "/dev/dsp", 8) == 0)) or 'split' == 0
|
||||||
|
{
|
||||||
|
|
||||||
|
if((strncmp(opts->audio_out_dev, "/dev/dsp", 8) == 0))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "OSS Output %s.\n", opts->audio_out_dev);
|
||||||
|
opts->audio_out_fd = open (opts->audio_out_dev, O_RDWR);
|
||||||
|
if (opts->audio_out_fd == -1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Error, couldn't open #1 %s\n", opts->audio_out_dev);
|
||||||
|
opts->audio_out = 0;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt = 0;
|
||||||
|
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "ioctl reset error \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt = speed;
|
||||||
|
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SPEED, &fmt) < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "ioctl speed error \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt = 0; //this seems okay to be 1 or 0, not sure what the difference really is (works in stereo on 0)
|
||||||
|
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_STEREO, &fmt) < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "ioctl stereo error \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt = AFMT_S16_LE;
|
||||||
|
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SETFMT, &fmt) < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "ioctl setfmt error \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
opts->audio_out_type = 5; //5 for 1 channel - 48k OSS 16-bit short output (matching with input)
|
||||||
|
opts->pulse_digi_rate_out = 48000; //this is used to force to upsample and also allow source audio monitor conditional check
|
||||||
|
opts->pulse_digi_out_channels = 1; //this is used to allow source audio monitor conditional check
|
||||||
|
opts->audio_gain = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->audio_in_type != 5) //split == 1
|
||||||
|
{
|
||||||
|
|
||||||
|
if((strncmp(opts->audio_out_dev, "/dev/dsp", 8) == 0))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "OSS Output %s.\n", opts->audio_out_dev);
|
||||||
|
opts->audio_out_fd = open (opts->audio_out_dev, O_WRONLY);
|
||||||
|
if (opts->audio_out_fd == -1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Error, couldn't open %s\n", opts->audio_out_dev);
|
||||||
|
opts->audio_out = 0;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Setup the device. Note that it's important to set the sample format, number of channels and sample rate exactly in this order. Some devices depend on the order.
|
||||||
|
|
||||||
|
fmt = 0;
|
||||||
|
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "ioctl reset error \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt = AFMT_S16_LE; //Sample Format
|
||||||
|
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SETFMT, &fmt) < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "ioctl setfmt error \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt = opts->pulse_digi_out_channels; //number of channels //was 2
|
||||||
|
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_CHANNELS, &fmt) < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "ioctl channel error \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//if using split with OSS output, and using EDACS w/ Analog, we have to use 48k
|
||||||
|
if (opts->frame_provoice == 1)
|
||||||
|
opts->pulse_digi_rate_out = 48000;
|
||||||
|
|
||||||
|
speed = opts->pulse_digi_rate_out; //since we have split input/output, we want to mirror pulse rate out
|
||||||
|
fmt = speed; //output rate
|
||||||
|
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SPEED, &fmt) < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "ioctl speed error \n");
|
||||||
|
}
|
||||||
|
if (opts->pulse_digi_out_channels == 2)
|
||||||
|
fmt = 1;
|
||||||
|
else fmt = 0;
|
||||||
|
|
||||||
|
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_STEREO, &fmt) < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "ioctl stereo error \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Multiple output returns based on 8k/1, 8k/2, or maybe 48k/1? (2,3,5)??
|
||||||
|
if (opts->pulse_digi_out_channels == 2)
|
||||||
|
opts->audio_out_type = 2; //2 for 2 channel 8k OSS 16-bit short output
|
||||||
|
else if (opts->frame_m17 == 1)
|
||||||
|
opts->audio_out_type = 2;
|
||||||
|
else opts->audio_out_type = 5;
|
||||||
|
|
||||||
|
//debug
|
||||||
|
fprintf (stderr, "Using OSS Output with %dk/%d channel configuration.\n", opts->pulse_digi_rate_out, opts->pulse_digi_out_channels);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -172,8 +393,7 @@ processAudio (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
// copy audio data to output buffer and upsample if necessary
|
// copy audio data to output buffer and upsample if necessary
|
||||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
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,
|
//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)
|
if (opts->pulse_digi_rate_out > 8000)
|
||||||
{
|
{
|
||||||
for (n = 0; n < 160; n++)
|
for (n = 0; n < 160; n++)
|
||||||
@@ -197,6 +417,8 @@ processAudio (dsd_opts * opts, dsd_state * state)
|
|||||||
*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 = (short) *state->audio_out_float_buf_p;
|
||||||
|
//tap the pointer here and store the short upsample buffer samples
|
||||||
|
state->s_lu[n] = (short) *state->audio_out_float_buf_p;
|
||||||
state->audio_out_buf_p++;
|
state->audio_out_buf_p++;
|
||||||
state->audio_out_float_buf_p++;
|
state->audio_out_float_buf_p++;
|
||||||
}
|
}
|
||||||
@@ -215,6 +437,10 @@ processAudio (dsd_opts * opts, dsd_state * state)
|
|||||||
*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 = (short) *state->audio_out_temp_buf_p;
|
||||||
|
//tap the pointer here and store the short buffer samples
|
||||||
|
state->s_l[n] = (short) *state->audio_out_temp_buf_p;
|
||||||
|
//debug
|
||||||
|
// fprintf (stderr, " %d", state->s_l[n]);
|
||||||
state->audio_out_buf_p++;
|
state->audio_out_buf_p++;
|
||||||
state->audio_out_temp_buf_p++;
|
state->audio_out_temp_buf_p++;
|
||||||
state->audio_out_idx++;
|
state->audio_out_idx++;
|
||||||
@@ -315,7 +541,6 @@ processAudioR (dsd_opts * opts, dsd_state * state)
|
|||||||
// copy audio data to output buffer and upsample if necessary
|
// copy audio data to output buffer and upsample if necessary
|
||||||
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
|
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,
|
//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)
|
if (opts->pulse_digi_rate_out > 8000)
|
||||||
{
|
{
|
||||||
for (n = 0; n < 160; n++)
|
for (n = 0; n < 160; n++)
|
||||||
@@ -339,6 +564,8 @@ processAudioR (dsd_opts * opts, dsd_state * state)
|
|||||||
*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 = (short) *state->audio_out_float_buf_pR;
|
||||||
|
//tap the pointer here and store the short upsample buffer samples
|
||||||
|
state->s_ru[n] = (short) *state->audio_out_float_buf_pR;
|
||||||
state->audio_out_buf_pR++;
|
state->audio_out_buf_pR++;
|
||||||
state->audio_out_float_buf_pR++;
|
state->audio_out_float_buf_pR++;
|
||||||
}
|
}
|
||||||
@@ -357,6 +584,8 @@ processAudioR (dsd_opts * opts, dsd_state * state)
|
|||||||
*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 = (short) *state->audio_out_temp_buf_pR;
|
||||||
|
//tap the pointer here and store the short buffer samples
|
||||||
|
state->s_r[n] = (short) *state->audio_out_temp_buf_pR;
|
||||||
state->audio_out_buf_pR++;
|
state->audio_out_buf_pR++;
|
||||||
state->audio_out_temp_buf_pR++;
|
state->audio_out_temp_buf_pR++;
|
||||||
state->audio_out_idxR++;
|
state->audio_out_idxR++;
|
||||||
@@ -423,10 +652,9 @@ void writeSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
void writeRawSample (dsd_opts * opts, dsd_state * state, short sample)
|
void writeRawSample (dsd_opts * opts, dsd_state * state, short sample)
|
||||||
{
|
{
|
||||||
int n;
|
UNUSED(state);
|
||||||
short aout_buf[160];
|
|
||||||
short *aout_buf_p;
|
|
||||||
|
|
||||||
|
//short aout_buf[160];
|
||||||
//sf_write_short(opts->wav_out_raw, aout_buf, 160);
|
//sf_write_short(opts->wav_out_raw, aout_buf, 160);
|
||||||
|
|
||||||
//only write if actual audio, truncate silence
|
//only write if actual audio, truncate silence
|
||||||
@@ -441,24 +669,46 @@ void
|
|||||||
playSynthesizedVoice (dsd_opts * opts, dsd_state * state)
|
playSynthesizedVoice (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
ssize_t result;
|
ssize_t result;
|
||||||
|
UNUSED(result);
|
||||||
|
|
||||||
|
//don't synthesize voice if slot is turned off
|
||||||
|
if (opts->slot1_on == 0)
|
||||||
|
{
|
||||||
|
//clear any previously buffered audio
|
||||||
|
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;
|
||||||
|
state->audio_out_idx = 0;
|
||||||
|
goto end_psv;
|
||||||
|
}
|
||||||
|
|
||||||
if (state->audio_out_idx > opts->delay)
|
if (state->audio_out_idx > opts->delay)
|
||||||
{
|
{
|
||||||
// output synthesized speech to sound card
|
if (opts->audio_out_type == 5 || opts->audio_out_type == 1) //OSS
|
||||||
if(opts->audio_out_type == 2)
|
|
||||||
{
|
|
||||||
//go F yourself PA
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
//Test just sending it straight on since I think I've figured out the STDIN and RTL for Stereo
|
//OSS 48k/1
|
||||||
pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL); //Yay! It works.
|
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;
|
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 if (opts->audio_out_type == 8) //UDP Audio Out -- Forgot some things still use this for now
|
||||||
|
{
|
||||||
|
udp_socket_blaster (opts, state, (state->audio_out_idx * 2), (state->audio_out_buf_p - state->audio_out_idx));
|
||||||
|
state->audio_out_idx = 0;
|
||||||
|
}
|
||||||
|
else state->audio_out_idx = 0; //failsafe for audio_out == 0
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
end_psv:
|
||||||
|
|
||||||
if (state->audio_out_idx2 >= 800000)
|
if (state->audio_out_idx2 >= 800000)
|
||||||
{
|
{
|
||||||
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
|
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
|
||||||
@@ -474,19 +724,28 @@ void
|
|||||||
playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
|
playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
ssize_t result;
|
ssize_t result;
|
||||||
|
UNUSED(result);
|
||||||
|
|
||||||
if (state->audio_out_idxR > opts->delay)
|
if (state->audio_out_idxR > opts->delay)
|
||||||
{
|
{
|
||||||
// output synthesized speech to sound card
|
// output synthesized speech to sound card
|
||||||
if(opts->audio_out_type == 2)
|
if (opts->audio_out_type == 5) //OSS
|
||||||
{
|
|
||||||
//go F yourself PA
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
pa_simple_write(opts->pulse_digi_dev_outR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2), NULL); //Yay! It works.
|
//OSS 48k/1
|
||||||
|
result = write (opts->audio_out_fd, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2));
|
||||||
state->audio_out_idxR = 0;
|
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 if (opts->audio_out_type == 8) //UDP Audio Out -- Not sure how this would handle, but R never gets called anymore, so just here for symmetry
|
||||||
|
{
|
||||||
|
udp_socket_blaster (opts, state, (state->audio_out_idxR * 2), (state->audio_out_buf_pR - state->audio_out_idxR));
|
||||||
|
state->audio_out_idxR = 0;
|
||||||
|
}
|
||||||
|
else state->audio_out_idxR = 0; //failsafe for audio_out == 0
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,6 +762,8 @@ playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
|
|||||||
void
|
void
|
||||||
openAudioOutDevice (dsd_opts * opts, int speed)
|
openAudioOutDevice (dsd_opts * opts, int speed)
|
||||||
{
|
{
|
||||||
|
UNUSED(speed);
|
||||||
|
|
||||||
//converted to handle any calls to use portaudio
|
//converted to handle any calls to use portaudio
|
||||||
if(strncmp(opts->audio_out_dev, "pa:", 3) == 0)
|
if(strncmp(opts->audio_out_dev, "pa:", 3) == 0)
|
||||||
{
|
{
|
||||||
@@ -517,7 +778,7 @@ openAudioOutDevice (dsd_opts * opts, int speed)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
struct stat stat_buf;
|
// struct stat stat_buf;
|
||||||
// if(stat(opts->audio_out_dev, &stat_buf) != 0 && strncmp(opts->audio_out_dev, "pulse", 5 != 0)) //HERE
|
// 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);
|
// fprintf (stderr,"Error, couldn't open %s\n", opts->audio_out_dev);
|
||||||
@@ -541,6 +802,9 @@ openAudioInDevice (dsd_opts * opts)
|
|||||||
const char ch = '.';
|
const char ch = '.';
|
||||||
extension = strrchr(opts->audio_in_dev, ch); //return extension if this is a .wav or .bin file
|
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
|
// get info of device/file
|
||||||
if (strncmp(opts->audio_in_dev, "-", 1) == 0)
|
if (strncmp(opts->audio_in_dev, "-", 1) == 0)
|
||||||
{
|
{
|
||||||
@@ -558,6 +822,12 @@ openAudioInDevice (dsd_opts * opts)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (strncmp(opts->audio_in_dev, "m17udp", 6) == 0)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 9; //NULL audio device
|
||||||
|
}
|
||||||
|
|
||||||
else if (strncmp(opts->audio_in_dev, "tcp", 3) == 0)
|
else if (strncmp(opts->audio_in_dev, "tcp", 3) == 0)
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 8;
|
opts->audio_in_type = 8;
|
||||||
@@ -567,37 +837,110 @@ openAudioInDevice (dsd_opts * opts)
|
|||||||
opts->audio_in_file_info->seekable=0;
|
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_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);
|
opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||||
|
|
||||||
if(opts->tcp_file_in == NULL)
|
if(opts->tcp_file_in == NULL)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error, couldn't open TCP with libsndfile: %s\n", sf_strerror(NULL));
|
fprintf(stderr, "Error, couldn't open TCP with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
exit(1);
|
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)
|
// else if (strncmp(opts->audio_in_dev, "udp", 3) == 0)
|
||||||
{
|
// {
|
||||||
fprintf(stderr, "Error, couldn't open UDP with libsndfile: %s\n", sf_strerror(NULL));
|
// opts->audio_in_type = 6;
|
||||||
exit(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->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)
|
else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_RTLSDR
|
||||||
opts->audio_in_type = 3;
|
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)
|
else if(strncmp(opts->audio_in_dev, "pulse", 5) == 0)
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//test .rrc files with hardset wav file settings
|
||||||
|
else if (strncmp(extension, ".rrc", 3) == 0)
|
||||||
|
{
|
||||||
|
//debug
|
||||||
|
fprintf (stderr, "Opening M17 .rrc headless wav file\n");
|
||||||
|
|
||||||
|
opts->audio_in_type = 2;
|
||||||
|
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||||
|
opts->audio_in_file_info->samplerate = 48000;
|
||||||
|
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 %s with libsndfile: %s\n", opts->audio_in_dev, sf_strerror(NULL));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: test .sym files as symbol capture .bin files
|
||||||
|
else if (strncmp(extension, ".sym", 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else if (strncmp(extension, ".bin", 3) == 0)
|
else if (strncmp(extension, ".bin", 3) == 0)
|
||||||
{
|
{
|
||||||
struct stat stat_buf;
|
struct stat stat_buf;
|
||||||
@@ -645,9 +988,11 @@ openAudioInDevice (dsd_opts * opts)
|
|||||||
|
|
||||||
}
|
}
|
||||||
//open pulse audio if no bin or wav
|
//open pulse audio if no bin or wav
|
||||||
else
|
else //seems this condition is never met
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 0; //not sure if this works or needs to openPulse here
|
//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)
|
if (opts->split == 1)
|
||||||
|
|||||||
1988
src/dsd_audio2.c
Normal file
@@ -118,6 +118,8 @@ print_datascope(dsd_opts* opts, dsd_state* state, int* sbuf2)
|
|||||||
static void
|
static void
|
||||||
use_symbol (dsd_opts* opts, dsd_state* state, int symbol)
|
use_symbol (dsd_opts* opts, dsd_state* state, int symbol)
|
||||||
{
|
{
|
||||||
|
UNUSED(symbol);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
int sbuf2[128];
|
int sbuf2[128];
|
||||||
int lmin, lmax, lsum;
|
int lmin, lmax, lsum;
|
||||||
@@ -217,8 +219,7 @@ invert_dibit(int dibit)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
||||||
//int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|
||||||
{
|
{
|
||||||
// determine dibit state
|
// determine dibit state
|
||||||
if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18) || (state->synctype == 37))
|
if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18) || (state->synctype == 37))
|
||||||
@@ -265,31 +266,39 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
|
|
||||||
else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) ||
|
else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) ||
|
||||||
(state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) ||
|
(state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) ||
|
||||||
(state->synctype == 17) || (state->synctype == 29) || (state->synctype == 36) )
|
(state->synctype == 17) || (state->synctype == 29) || (state->synctype == 31) ||
|
||||||
|
(state->synctype == 77) || (state->synctype == 87) ||(state->synctype == 36) ||
|
||||||
|
(state->synctype == 99) )
|
||||||
|
|
||||||
{
|
{
|
||||||
// 1 -P25p1
|
// 1 -P25p1
|
||||||
// 3 -X2-TDMA (inverted signal voice frame)
|
// 3 -X2-TDMA (inverted signal voice frame)
|
||||||
// 5 -X2-TDMA (inverted signal data frame)
|
// 5 -X2-TDMA (inverted signal data frame)
|
||||||
// 9 -NXDN (inverted voice frame)
|
// 9 -M17 LSR
|
||||||
// 11 -DMR (inverted signal voice frame)
|
// 11 -DMR (inverted signal voice frame)
|
||||||
// 13 -DMR (inverted signal data frame)
|
// 13 -DMR (inverted signal data frame)
|
||||||
// 17 -NXDN (inverted data frame)
|
// 17 -M17 STR
|
||||||
// 29 -NXDN (inverted FSW)
|
// 29 -NXDN (inverted FSW)
|
||||||
|
// 31 -YSF
|
||||||
// 36 -P25p2
|
// 36 -P25p2
|
||||||
|
// 77 -M17 BRT
|
||||||
|
// 87 -M17 PKT
|
||||||
|
// 99 -M17 Preamble
|
||||||
|
|
||||||
int valid;
|
int valid;
|
||||||
int dibit;
|
int dibit;
|
||||||
|
|
||||||
valid = 0;
|
valid = 0;
|
||||||
|
|
||||||
//only run estimate_symbol when we aren't trunking, otherwise noise/gain levels on other rf channels may impact performance severely
|
UNUSED(opts);
|
||||||
//honestly can't say I've seen any improvement when using heuristics, but is detrimental to playing back multiple p25 samples
|
|
||||||
if (state->synctype == 1 && opts->p25_trunk == 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 P25 heuristics if available
|
if (state->synctype == 1 && opts->use_heuristics == 1)
|
||||||
//valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit);
|
{
|
||||||
}
|
// Use the P25p1 heuristics if available
|
||||||
|
valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||||
|
}
|
||||||
|
|
||||||
if (valid == 0)
|
if (valid == 0)
|
||||||
{
|
{
|
||||||
@@ -336,25 +345,29 @@ static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
|||||||
// 0 +P25p1
|
// 0 +P25p1
|
||||||
// 2 +X2-TDMA (non inverted signal data frame)
|
// 2 +X2-TDMA (non inverted signal data frame)
|
||||||
// 4 +X2-TDMA (non inverted signal voice frame)
|
// 4 +X2-TDMA (non inverted signal voice frame)
|
||||||
// 8 +NXDN (non inverted voice frame)
|
// 8 +M17 LSF
|
||||||
// 10 +DMR (non inverted signal data frame)
|
// 10 +DMR (non inverted signal data frame)
|
||||||
// 12 +DMR (non inverted signal voice frame)
|
// 12 +DMR (non inverted signal voice frame)
|
||||||
// 16 +NXDN (non inverted data frame)
|
// 16 +M17 STR
|
||||||
// 28 +NXND (FSW)
|
// 28 +NXND (FSW)
|
||||||
|
// 30 +YSF
|
||||||
// 35 +p25p2
|
// 35 +p25p2
|
||||||
|
// 76 +M17 BRT
|
||||||
|
// 86 +M17 PKT
|
||||||
|
// 98 +M17 Preamble
|
||||||
|
|
||||||
int valid;
|
int valid;
|
||||||
int dibit;
|
int dibit;
|
||||||
|
|
||||||
valid = 0;
|
valid = 0;
|
||||||
|
|
||||||
//only run estimate_symbol when we aren't trunking, otherwise noise/gain levels on other rf channels may impact performance severely
|
//testing again, either on Voice channels only (when tuned) or with trunk disabled
|
||||||
//honestly can't say I've seen any improvement when using heuristics, but is detrimental to playing back multiple p25 samples
|
// if (state->synctype == 0 && (opts->p25_is_tuned == 1 || opts->p25_trunk == 0) && opts->use_heuristics == 1)
|
||||||
if (state->synctype == 0 && opts->p25_trunk == 0)
|
if (state->synctype == 0 && opts->use_heuristics == 1)
|
||||||
{
|
{
|
||||||
// Use the P25 heuristics if available
|
// Use the P25p1 heuristics if available
|
||||||
//valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit);
|
valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid == 0)
|
if (valid == 0)
|
||||||
{
|
{
|
||||||
@@ -447,7 +460,7 @@ get_dibit_and_analog_signal (dsd_opts* opts, dsd_state* state, int* out_analog_s
|
|||||||
}
|
}
|
||||||
|
|
||||||
//symbol/dibit file capture/writing
|
//symbol/dibit file capture/writing
|
||||||
if (opts->symbol_out == 1)
|
if (opts->symbol_out_f)
|
||||||
{
|
{
|
||||||
//fprintf (stderr, "%d", dibit);
|
//fprintf (stderr, "%d", dibit);
|
||||||
fputc (dibit, opts->symbol_out_f);
|
fputc (dibit, opts->symbol_out_f);
|
||||||
@@ -484,6 +497,7 @@ skipDibit (dsd_opts * opts, dsd_state * state, int count)
|
|||||||
|
|
||||||
short sample;
|
short sample;
|
||||||
int i;
|
int i;
|
||||||
|
UNUSED(sample);
|
||||||
|
|
||||||
for (i = 0; i < (count); i++)
|
for (i = 0; i < (count); i++)
|
||||||
{
|
{
|
||||||
|
|||||||
501
src/dsd_file.c
@@ -17,36 +17,7 @@
|
|||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
time_t nowF;
|
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
||||||
char * getTimeF(void) //get pretty hh:mm:ss timestamp
|
|
||||||
{
|
|
||||||
time_t t = time(NULL);
|
|
||||||
|
|
||||||
char * curr;
|
|
||||||
char * stamp = asctime(localtime( & t));
|
|
||||||
|
|
||||||
curr = strtok(stamp, " ");
|
|
||||||
curr = strtok(NULL, " ");
|
|
||||||
curr = strtok(NULL, " ");
|
|
||||||
curr = strtok(NULL, " ");
|
|
||||||
|
|
||||||
return curr;
|
|
||||||
}
|
|
||||||
|
|
||||||
char * getDateF(void) {
|
|
||||||
char datename[120]; //bug in 32-bit Ubuntu when using date in filename, date is garbage text
|
|
||||||
char * curr2;
|
|
||||||
struct tm * to;
|
|
||||||
time_t t;
|
|
||||||
t = time(NULL);
|
|
||||||
to = localtime( & t);
|
|
||||||
strftime(datename, sizeof(datename), "%Y-%m-%d", to);
|
|
||||||
curr2 = strtok(datename, " ");
|
|
||||||
return curr2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
|
||||||
{
|
{
|
||||||
int i, j, k;
|
int i, j, k;
|
||||||
unsigned char b;
|
unsigned char b;
|
||||||
@@ -56,36 +27,22 @@ saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
|||||||
fputc (err, opts->mbe_out_f);
|
fputc (err, opts->mbe_out_f);
|
||||||
|
|
||||||
k = 0;
|
k = 0;
|
||||||
if (opts->payload == 1) //make opt variable later on to toggle this
|
|
||||||
{
|
|
||||||
//fprintf(stderr, "\n IMBE ");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 11; i++)
|
for (i = 0; i < 11; i++)
|
||||||
{
|
{
|
||||||
b = 0;
|
b = 0;
|
||||||
|
|
||||||
for (j = 0; j < 8; j++)
|
for (j = 0; j < 8; j++)
|
||||||
{
|
{
|
||||||
b = b << 1;
|
b = b << 1;
|
||||||
b = b + imbe_d[k];
|
b = b + imbe_d[k];
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
if (opts->payload == 1) //make opt variable later on to toggle this
|
fputc (b, opts->mbe_out_f);
|
||||||
{
|
}
|
||||||
//fprintf (stderr, "%02X", b);
|
|
||||||
}
|
|
||||||
fputc (b, opts->mbe_out_f);
|
|
||||||
}
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
//fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
|
||||||
}
|
|
||||||
fflush (opts->mbe_out_f);
|
fflush (opts->mbe_out_f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||||
saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|
||||||
{
|
{
|
||||||
int i, j, k;
|
int i, j, k;
|
||||||
unsigned char b;
|
unsigned char b;
|
||||||
@@ -95,122 +52,86 @@ saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|||||||
fputc (err, opts->mbe_out_f);
|
fputc (err, opts->mbe_out_f);
|
||||||
|
|
||||||
k = 0;
|
k = 0;
|
||||||
if (opts->payload == 1) //make opt variable later on to toggle this
|
for (i = 0; i < 6; i++)
|
||||||
{
|
|
||||||
//fprintf(stderr, "\n AMBE ");
|
|
||||||
}
|
|
||||||
//for (i = 0; i < 6; i++)
|
|
||||||
for (i = 0; i < 6; i++) //using 7 seems to break older amb files where it was 6, need to test 7 on 7 some more
|
|
||||||
{
|
|
||||||
b = 0;
|
|
||||||
for (j = 0; j < 8; j++)
|
|
||||||
{
|
|
||||||
b = b << 1;
|
|
||||||
b = b + ambe_d[k];
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
if (opts->payload == 1 && i < 6) //make opt variable later on to toggle this
|
|
||||||
{
|
|
||||||
//fprintf (stderr, "%02X", b);
|
|
||||||
}
|
|
||||||
if (opts->payload == 1 && i == 6) //7th octet should only contain 1 bit? value will be either 0x00 or 0x80?
|
|
||||||
{
|
|
||||||
//fprintf (stderr, "%02X", b & 0x80); //7th octet should only contain 1 bit?
|
|
||||||
}
|
|
||||||
|
|
||||||
fputc (b, opts->mbe_out_f);
|
|
||||||
}
|
|
||||||
if (opts->payload == 1)
|
|
||||||
{
|
|
||||||
//fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
|
||||||
}
|
|
||||||
b = ambe_d[48];
|
|
||||||
fputc (b, opts->mbe_out_f);
|
|
||||||
fflush (opts->mbe_out_f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d) //for P25P1 and ProVoice
|
|
||||||
{
|
|
||||||
int i, j, k;
|
|
||||||
unsigned char b;
|
|
||||||
unsigned char err;
|
|
||||||
|
|
||||||
err = (unsigned char) state->errs2;
|
|
||||||
k = 0;
|
|
||||||
if (opts->payload == 1) //print IMBE info
|
|
||||||
{
|
|
||||||
fprintf(stderr, "\n IMBE ");
|
|
||||||
}
|
|
||||||
for (i = 0; i < 11; i++)
|
|
||||||
{
|
{
|
||||||
b = 0;
|
b = 0;
|
||||||
for (j = 0; j < 8; j++)
|
for (j = 0; j < 8; j++)
|
||||||
{
|
{
|
||||||
b = b << 1;
|
b = b << 1;
|
||||||
b = b + imbe_d[k];
|
b = b + ambe_d[k];
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
if (opts->payload == 1)
|
fputc (b, opts->mbe_out_f);
|
||||||
{
|
|
||||||
fprintf (stderr, "%02X", b);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
b = ambe_d[48];
|
||||||
if (opts->payload == 1)
|
fputc (b, opts->mbe_out_f);
|
||||||
{
|
fflush (opts->mbe_out_f);
|
||||||
fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
|
||||||
}
|
|
||||||
|
|
||||||
//fprintf (stderr, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void saveAmbe2450DataR (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||||
PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
|
||||||
{
|
{
|
||||||
int i, j, k;
|
int i, j, k;
|
||||||
unsigned char b;
|
unsigned char b;
|
||||||
unsigned char err;
|
unsigned char err;
|
||||||
|
|
||||||
err = (unsigned char) state->errs2;
|
err = (unsigned char) state->errs2R;
|
||||||
|
fputc (err, opts->mbe_out_fR);
|
||||||
|
|
||||||
k = 0;
|
k = 0;
|
||||||
if (opts->dmr_stereo == 0 && opts->dmr_mono == 0)
|
for (i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
b = 0;
|
||||||
|
for (j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
b = b << 1;
|
||||||
|
b = b + ambe_d[k];
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
fputc (b, opts->mbe_out_fR);
|
||||||
}
|
}
|
||||||
if (opts->payload == 1) //print AMBE info from DMR Stereo method
|
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
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\n IMBE ");
|
||||||
|
uint8_t imbe[88];
|
||||||
|
for (int i = 0; i < 11; i++)
|
||||||
{
|
{
|
||||||
fprintf(stderr, " AMBE ");
|
imbe[i] = convert_bits_into_output((uint8_t *)imbe_d+(i*8), 8);
|
||||||
|
fprintf(stderr, "%02X", imbe[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 7; i++)
|
|
||||||
{
|
fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
||||||
b = 0;
|
UNUSED(opts);
|
||||||
for (j = 0; j < 8; j++)
|
}
|
||||||
{
|
|
||||||
b = b << 1;
|
void PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||||
b = b + ambe_d[k];
|
{
|
||||||
k++;
|
|
||||||
}
|
//cast as unsigned long long int and not uint64_t
|
||||||
if (opts->payload == 1 && i < 6)
|
//to avoid the %lx vs %llx warning on 32 or 64 bit
|
||||||
{
|
unsigned long long int ambe = 0;
|
||||||
fprintf (stderr, "%02X", b);
|
|
||||||
}
|
//preceeding line break, if required
|
||||||
if (opts->payload == 1 && i == 6)
|
if (opts->dmr_stereo == 0 && opts->dmr_mono == 0)
|
||||||
{
|
fprintf (stderr, "\n");
|
||||||
fprintf (stderr, "%02X", b & 0x80);
|
|
||||||
}
|
ambe = convert_bits_into_output ((uint8_t *)ambe_d, 49);
|
||||||
}
|
ambe = ambe << 7; //shift to final position
|
||||||
if (state->currentslot == 0)
|
|
||||||
{
|
fprintf(stderr, " AMBE %014llX", ambe);
|
||||||
fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
|
||||||
}
|
if (state->currentslot == 0)
|
||||||
else fprintf(stderr, " err = [%X] [%X] ", state->errsR, state->errs2R);
|
fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
||||||
b = ambe_d[48];
|
else fprintf(stderr, " err = [%X] [%X] ", state->errsR, state->errs2R);
|
||||||
if (opts->dmr_stereo == 1 || opts->dmr_mono == 1) //need to fix the printouts again
|
|
||||||
{
|
//trailing line break, if required
|
||||||
|
if (opts->dmr_stereo == 1 || opts->dmr_mono == 1)
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,14 +254,21 @@ openMbeInFile (dsd_opts * opts, dsd_state * state)
|
|||||||
cookie[2] = fgetc (opts->mbe_in_f);
|
cookie[2] = fgetc (opts->mbe_in_f);
|
||||||
cookie[3] = fgetc (opts->mbe_in_f);
|
cookie[3] = fgetc (opts->mbe_in_f);
|
||||||
cookie[4] = 0;
|
cookie[4] = 0;
|
||||||
|
//ambe+2
|
||||||
if (strstr (cookie, ".amb") != NULL)
|
if (strstr (cookie, ".amb") != NULL)
|
||||||
{
|
{
|
||||||
state->mbe_file_type = 1;
|
state->mbe_file_type = 1;
|
||||||
}
|
}
|
||||||
|
//p1 and pv
|
||||||
else if (strstr (cookie, ".imb") != NULL)
|
else if (strstr (cookie, ".imb") != NULL)
|
||||||
{
|
{
|
||||||
state->mbe_file_type = 0;
|
state->mbe_file_type = 0;
|
||||||
}
|
}
|
||||||
|
//d-star ambe
|
||||||
|
else if (strstr (cookie, ".dmb") != NULL)
|
||||||
|
{
|
||||||
|
state->mbe_file_type = 2;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state->mbe_file_type = -1;
|
state->mbe_file_type = -1;
|
||||||
@@ -349,9 +277,11 @@ openMbeInFile (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//much simpler version
|
//slot 1
|
||||||
void closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
void closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
if (opts->mbe_out == 1)
|
if (opts->mbe_out == 1)
|
||||||
{
|
{
|
||||||
if (opts->mbe_out_f != NULL)
|
if (opts->mbe_out_f != NULL)
|
||||||
@@ -360,48 +290,74 @@ void closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
|||||||
fclose (opts->mbe_out_f);
|
fclose (opts->mbe_out_f);
|
||||||
opts->mbe_out_f = NULL;
|
opts->mbe_out_f = NULL;
|
||||||
opts->mbe_out = 0;
|
opts->mbe_out = 0;
|
||||||
fprintf (stderr, "\nClosing MBE out file.");
|
fprintf (stderr, "\nClosing MBE out file 1.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
//slot 2
|
||||||
openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
void closeMbeOutFileR (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
UNUSED(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;
|
int i, j;
|
||||||
char ext[5];
|
char ext[5];
|
||||||
|
char * timestr; //add timestr here, so we can assign it and also free it to prevent memory leak
|
||||||
|
char * datestr;
|
||||||
|
|
||||||
if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
|
timestr = getTime();
|
||||||
{
|
datestr = getDate();
|
||||||
sprintf (ext, ".imb");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf (ext, ".amb");
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset talkgroup id buffer
|
//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 (i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
for (j = 0; j < 25; j++)
|
||||||
{
|
{
|
||||||
for (j = 0; j < 25; j++)
|
state->tg[j][i] = 0;
|
||||||
{
|
|
||||||
state->tg[j][i] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state->tgcount = 0;
|
state->tgcount = 0;
|
||||||
|
|
||||||
sprintf (opts->mbe_out_file, "%s %s%s", getDateF(), getTimeF(), ext);
|
sprintf (opts->mbe_out_file, "%s %s S1%s", datestr, timestr, ext);
|
||||||
|
|
||||||
sprintf (opts->mbe_out_path, "%s%s", opts->mbe_out_dir, opts->mbe_out_file);
|
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");
|
opts->mbe_out_f = fopen (opts->mbe_out_path, "w");
|
||||||
if (opts->mbe_out_f == NULL)
|
if (opts->mbe_out_f == NULL)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Error, couldn't open %s\n", opts->mbe_out_path);
|
fprintf (stderr,"\nError, couldn't open %s for slot 1\n", opts->mbe_out_path);
|
||||||
}
|
}
|
||||||
else opts->mbe_out = 1;
|
else opts->mbe_out = 1;
|
||||||
|
|
||||||
@@ -409,12 +365,84 @@ openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
|||||||
fprintf (opts->mbe_out_f, "%s", ext);
|
fprintf (opts->mbe_out_f, "%s", ext);
|
||||||
|
|
||||||
fflush (opts->mbe_out_f);
|
fflush (opts->mbe_out_f);
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
if (datestr != NULL)
|
||||||
|
{
|
||||||
|
free (datestr);
|
||||||
|
datestr = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void openMbeOutFileR (dsd_opts * opts, dsd_state * state)
|
||||||
openWavOutFile (dsd_opts * opts, dsd_state * state)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
char ext[5];
|
||||||
|
char * timestr; //add timestr here, so we can assign it and also free it to prevent memory leak
|
||||||
|
char * datestr;
|
||||||
|
|
||||||
|
timestr = getTime();
|
||||||
|
datestr = getDate();
|
||||||
|
|
||||||
|
//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", datestr, timestr, 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);
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
if (datestr != NULL)
|
||||||
|
{
|
||||||
|
free (datestr);
|
||||||
|
datestr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void openWavOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
SF_INFO info;
|
SF_INFO info;
|
||||||
info.samplerate = 8000; //8000
|
info.samplerate = 8000; //8000
|
||||||
info.channels = 1;
|
info.channels = 1;
|
||||||
@@ -430,6 +458,7 @@ openWavOutFile (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
void openWavOutFileL (dsd_opts * opts, dsd_state * state)
|
void openWavOutFileL (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
SF_INFO info;
|
SF_INFO info;
|
||||||
info.samplerate = 8000;
|
info.samplerate = 8000;
|
||||||
@@ -446,6 +475,7 @@ void openWavOutFileL (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
void openWavOutFileR (dsd_opts * opts, dsd_state * state)
|
void openWavOutFileR (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
SF_INFO info;
|
SF_INFO info;
|
||||||
info.samplerate = 8000; //8000
|
info.samplerate = 8000; //8000
|
||||||
@@ -462,12 +492,13 @@ void openWavOutFileR (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
SF_INFO info;
|
SF_INFO info;
|
||||||
info.samplerate = 48000; //8000
|
info.samplerate = 48000; //8000
|
||||||
info.channels = 1;
|
info.channels = 1;
|
||||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
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);
|
opts->wav_out_raw = sf_open (opts->wav_out_file_raw, SFM_WRITE, &info);
|
||||||
if (opts->wav_out_raw == NULL)
|
if (opts->wav_out_raw == NULL)
|
||||||
{
|
{
|
||||||
fprintf (stderr,"Error - could not open raw wav output file %s\n", opts->wav_out_file_raw);
|
fprintf (stderr,"Error - could not open raw wav output file %s\n", opts->wav_out_file_raw);
|
||||||
@@ -477,48 +508,128 @@ void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
void closeWavOutFile (dsd_opts * opts, dsd_state * state)
|
void closeWavOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
sf_close(opts->wav_out_f);
|
sf_close(opts->wav_out_f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeWavOutFileL (dsd_opts * opts, dsd_state * state)
|
void closeWavOutFileL (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
sf_close(opts->wav_out_f);
|
sf_close(opts->wav_out_f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeWavOutFileR (dsd_opts * opts, dsd_state * state)
|
void closeWavOutFileR (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
sf_close(opts->wav_out_fR);
|
sf_close(opts->wav_out_fR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
void closeWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
sf_close(opts->wav_out_raw);
|
sf_close(opts->wav_out_raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
void openSymbolOutFile (dsd_opts * opts, dsd_state * state)
|
void openSymbolOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
//Do something
|
closeSymbolOutFile(opts, state);
|
||||||
opts->symbol_out_f = fopen (opts->symbol_out_file, "w");
|
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)
|
void closeSymbolOutFile (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
//Do something
|
UNUSED(state);
|
||||||
if (opts->symbol_out == 1)
|
|
||||||
|
if (opts->symbol_out_f)
|
||||||
{
|
{
|
||||||
if (opts->symbol_out_file != NULL)
|
fclose(opts->symbol_out_f);
|
||||||
{
|
opts->symbol_out_f = NULL;
|
||||||
fclose(opts->symbol_out_f);
|
|
||||||
}
|
|
||||||
|
|
||||||
opts->symbol_out = 0; //set flag to 1
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//input bit array, return output as up to a 64-bit value
|
||||||
|
uint64_t convert_bits_into_output(uint8_t * input, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint64_t output = 0;
|
||||||
|
for(i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
output <<= 1;
|
||||||
|
output |= (uint64_t)(input[i] & 1);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pack_bit_array_into_byte_array (uint8_t * input, uint8_t * output, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
output[i] = (uint8_t)convert_bits_into_output(&input[i*8], 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
//take len amount of bits and pack into x amount of bytes (asymmetrical)
|
||||||
|
void pack_bit_array_into_byte_array_asym (uint8_t * input, uint8_t * output, int len)
|
||||||
|
{
|
||||||
|
int i = 0; int k = len % 8;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
output[i/8] <<= 1;
|
||||||
|
output[i/8] |= input[i];
|
||||||
|
}
|
||||||
|
//if any leftover bits that don't flush the last byte fully packed, shift them over left
|
||||||
|
if (k)
|
||||||
|
output[i/8] <<= 8-k;
|
||||||
|
}
|
||||||
|
|
||||||
|
//take len amount of bytes and unpack back into a bit array
|
||||||
|
void unpack_byte_array_into_bit_array (uint8_t * input, uint8_t * output, int len)
|
||||||
|
{
|
||||||
|
int i = 0, k = 0;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
output[k++] = (input[i] >> 7) & 1;
|
||||||
|
output[k++] = (input[i] >> 6) & 1;
|
||||||
|
output[k++] = (input[i] >> 5) & 1;
|
||||||
|
output[k++] = (input[i] >> 4) & 1;
|
||||||
|
output[k++] = (input[i] >> 3) & 1;
|
||||||
|
output[k++] = (input[i] >> 2) & 1;
|
||||||
|
output[k++] = (input[i] >> 1) & 1;
|
||||||
|
output[k++] = (input[i] >> 0) & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//take len amount of bits and pack into x amount of bytes (asymmetrical)
|
||||||
|
void pack_ambe (char * input, uint8_t * output, int len)
|
||||||
|
{
|
||||||
|
int i = 0; int k = len % 8;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
output[i/8] <<= 1;
|
||||||
|
output[i/8] |= (uint8_t)input[i];
|
||||||
|
}
|
||||||
|
//if any leftover bits that don't flush the last byte fully packed, shift them over left
|
||||||
|
if (k)
|
||||||
|
output[i/8] <<= 8-k;
|
||||||
|
}
|
||||||
|
|
||||||
|
//unpack byte array with ambe data into a 49-bit bitwise array
|
||||||
|
void unpack_ambe (uint8_t * input, char * ambe)
|
||||||
|
{
|
||||||
|
int i = 0, k = 0;
|
||||||
|
for (i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
ambe[k++] = (input[i] >> 7) & 1;
|
||||||
|
ambe[k++] = (input[i] >> 6) & 1;
|
||||||
|
ambe[k++] = (input[i] >> 5) & 1;
|
||||||
|
ambe[k++] = (input[i] >> 4) & 1;
|
||||||
|
ambe[k++] = (input[i] >> 3) & 1;
|
||||||
|
ambe[k++] = (input[i] >> 2) & 1;
|
||||||
|
ambe[k++] = (input[i] >> 1) & 1;
|
||||||
|
ambe[k++] = (input[i] >> 0) & 1;
|
||||||
|
}
|
||||||
|
ambe[48] = input[6] >> 7;
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,48 @@
|
|||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
//M17 Filter -- RRC Alpha = 0.5 S/s 48000 with 81 taps
|
||||||
|
#define M17ZEROS 80
|
||||||
|
float m17gain = 3.16227766f; //sqrt of 10.0f
|
||||||
|
static float m17xv[81];
|
||||||
|
|
||||||
|
//sample RRC filter for 48kHz sample rate
|
||||||
|
//alpha=0.5, span=8, sps=10, gain=sqrt(sps)
|
||||||
|
float m17coeffs[81] = //replaced old ones with ones from libm17
|
||||||
|
{
|
||||||
|
-0.003195702904062073f, -0.002930279157647190f, -0.001940667871554463f,
|
||||||
|
-0.000356087678023658f, 0.001547011339077758f, 0.003389554791179751f,
|
||||||
|
0.004761898604225673f, 0.005310860846138910f, 0.004824746306020221f,
|
||||||
|
0.003297923526848786f, 0.000958710871218619f, -0.001749908029791816f,
|
||||||
|
-0.004238694106631223f, -0.005881783042101693f, -0.006150256456781309f,
|
||||||
|
-0.004745376707651645f, -0.001704189656473565f, 0.002547854551539951f,
|
||||||
|
0.007215575568844704f, 0.011231038205363532f, 0.013421952197060707f,
|
||||||
|
0.012730475385624438f, 0.008449554307303753f, 0.000436744366018287f,
|
||||||
|
-0.010735380379191660f, -0.023726883538258272f, -0.036498030780605324f,
|
||||||
|
-0.046500883189991064f, -0.050979050575999614f, -0.047340680079891187f,
|
||||||
|
-0.033554880492651755f, -0.008513823955725943f, 0.027696543159614194f,
|
||||||
|
0.073664520037517042f, 0.126689053778116234f, 0.182990955139333916f,
|
||||||
|
0.238080025892859704f, 0.287235637987091563f, 0.326040247765297220f,
|
||||||
|
0.350895727088112619f, 0.359452932027607974f, 0.350895727088112619f,
|
||||||
|
0.326040247765297220f, 0.287235637987091563f, 0.238080025892859704f,
|
||||||
|
0.182990955139333916f, 0.126689053778116234f, 0.073664520037517042f,
|
||||||
|
0.027696543159614194f, -0.008513823955725943f, -0.033554880492651755f,
|
||||||
|
-0.047340680079891187f, -0.050979050575999614f, -0.046500883189991064f,
|
||||||
|
-0.036498030780605324f, -0.023726883538258272f, -0.010735380379191660f,
|
||||||
|
0.000436744366018287f, 0.008449554307303753f, 0.012730475385624438f,
|
||||||
|
0.013421952197060707f, 0.011231038205363532f, 0.007215575568844704f,
|
||||||
|
0.002547854551539951f, -0.001704189656473565f, -0.004745376707651645f,
|
||||||
|
-0.006150256456781309f, -0.005881783042101693f, -0.004238694106631223f,
|
||||||
|
-0.001749908029791816f, 0.000958710871218619f, 0.003297923526848786f,
|
||||||
|
0.004824746306020221f, 0.005310860846138910f, 0.004761898604225673f,
|
||||||
|
0.003389554791179751f, 0.001547011339077758f, -0.000356087678023658f,
|
||||||
|
-0.001940667871554463f, -0.002930279157647190f, -0.003195702904062073f
|
||||||
|
};
|
||||||
|
|
||||||
// Original DMR filter
|
// Original DMR filter
|
||||||
#define NZEROS 60
|
#define NZEROS 60
|
||||||
float ngain = 7.423339364f;
|
float ngain = 7.423339364f;
|
||||||
static float xv[NZEROS+1];
|
static float xv[NZEROS+1];
|
||||||
float xcoeffs[] =
|
float xcoeffs[61] =
|
||||||
{ -0.0083649323f, -0.0265444850f, -0.0428141462f, -0.0537571943f,
|
{ -0.0083649323f, -0.0265444850f, -0.0428141462f, -0.0537571943f,
|
||||||
-0.0564141052f, -0.0489161045f, -0.0310068662f, -0.0043393881f,
|
-0.0564141052f, -0.0489161045f, -0.0310068662f, -0.0043393881f,
|
||||||
+0.0275375106f, +0.0595423283f, +0.0857543325f, +0.1003565948f,
|
+0.0275375106f, +0.0595423283f, +0.0857543325f, +0.1003565948f,
|
||||||
@@ -27,7 +67,7 @@ float xcoeffs[] =
|
|||||||
float nxgain = 15.95930463f;
|
float nxgain = 15.95930463f;
|
||||||
static float nxv[NXZEROS+1];
|
static float nxv[NXZEROS+1];
|
||||||
|
|
||||||
float nxcoeffs[] =
|
float nxcoeffs[135] =
|
||||||
{ +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f,
|
{ +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f,
|
||||||
+0.022653298f, +0.016379869f, +0.008737200f, +0.000003302f,
|
+0.022653298f, +0.016379869f, +0.008737200f, +0.000003302f,
|
||||||
-0.009468531f, -0.019262057f, -0.028914291f, -0.037935027f,
|
-0.009468531f, -0.019262057f, -0.028914291f, -0.037935027f,
|
||||||
@@ -74,7 +114,7 @@ const float dmrgain = 6.82973073748f;
|
|||||||
static float dxv[DMRNZEROS+1];
|
static float dxv[DMRNZEROS+1];
|
||||||
|
|
||||||
// DMR filter F4EXB - root raised cosine alpha=0.7 Ts = 6650 S/s Fc = 48kHz
|
// DMR filter F4EXB - root raised cosine alpha=0.7 Ts = 6650 S/s Fc = 48kHz
|
||||||
const float dmrcoeffs[] =
|
const float dmrcoeffs[61] =
|
||||||
{0.0301506278, 0.0269200615, 0.0159662432, -0.0013114705, -0.0216605133,
|
{0.0301506278, 0.0269200615, 0.0159662432, -0.0013114705, -0.0216605133,
|
||||||
-0.0404938748, -0.0528141756, -0.0543747957, -0.0428325003, -0.0186176083,
|
-0.0404938748, -0.0528141756, -0.0543747957, -0.0428325003, -0.0186176083,
|
||||||
0.0147202645, 0.0508418571, 0.0816392577, 0.0988113688, 0.0957187780,
|
0.0147202645, 0.0508418571, 0.0816392577, 0.0988113688, 0.0957187780,
|
||||||
@@ -98,7 +138,7 @@ static float dpmrxv[NXZEROS+1];
|
|||||||
const float dpmrgain = 14.6083498224f;
|
const float dpmrgain = 14.6083498224f;
|
||||||
|
|
||||||
// dPMR filter - root raised cosine alpha=0.2 Ts = 3325 S/s Fc = 48 kHz - zero at boundaries appears to be slightly better
|
// dPMR filter - root raised cosine alpha=0.2 Ts = 3325 S/s Fc = 48 kHz - zero at boundaries appears to be slightly better
|
||||||
const float dpmrcoeffs[] =
|
const float dpmrcoeffs[135] =
|
||||||
{-0.0000983004, 0.0058388841, 0.0119748846, 0.0179185547, 0.0232592816,
|
{-0.0000983004, 0.0058388841, 0.0119748846, 0.0179185547, 0.0232592816,
|
||||||
0.0275919612, 0.0305433586, 0.0317982965, 0.0311240307,
|
0.0275919612, 0.0305433586, 0.0317982965, 0.0311240307,
|
||||||
0.0283911865, 0.0235897433, 0.0168387650, 0.0083888763,
|
0.0283911865, 0.0235897433, 0.0168387650, 0.0083888763,
|
||||||
@@ -147,7 +187,7 @@ dmr_filter(short sample)
|
|||||||
short
|
short
|
||||||
nxdn_filter(short sample)
|
nxdn_filter(short sample)
|
||||||
{
|
{
|
||||||
return dsd_input_filter(sample, 2);
|
return dsd_input_filter(sample, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
short
|
short
|
||||||
@@ -156,6 +196,11 @@ dpmr_filter(short sample)
|
|||||||
return dsd_input_filter(sample, 4);
|
return dsd_input_filter(sample, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
short m17_filter (short sample)
|
||||||
|
{
|
||||||
|
return dsd_input_filter(sample, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
short
|
short
|
||||||
dsd_input_filter(short sample, int mode)
|
dsd_input_filter(short sample, int mode)
|
||||||
@@ -189,6 +234,14 @@ dsd_input_filter(short sample, int mode)
|
|||||||
coeffs = (float *)dpmrcoeffs;
|
coeffs = (float *)dpmrcoeffs;
|
||||||
zeros = DPMRNZEROS;
|
zeros = DPMRNZEROS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
gain = m17gain;
|
||||||
|
v = m17xv;
|
||||||
|
coeffs = (float *)m17coeffs;
|
||||||
|
zeros = M17ZEROS;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
@@ -204,3 +257,12 @@ dsd_input_filter(short sample, int mode)
|
|||||||
|
|
||||||
return (short)(sum / gain); // filtered sample out
|
return (short)(sum / gain); // filtered sample out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void init_rrc_filter_memory()
|
||||||
|
{
|
||||||
|
memset (m17xv, 0, 81*sizeof(float));
|
||||||
|
memset (xv, 0, 61*sizeof(float));
|
||||||
|
memset (dxv, 0, 61*sizeof(float));
|
||||||
|
memset (nxv, 0, 135*sizeof(float));
|
||||||
|
memset (dpmrxv, 0, 135*sizeof(float));
|
||||||
|
}
|
||||||
481
src/dsd_frame.c
@@ -31,7 +31,8 @@ printFrameInfo (dsd_opts * opts, dsd_state * state)
|
|||||||
level = (int) state->max / 164;
|
level = (int) state->max / 164;
|
||||||
if (opts->verbose > 0)
|
if (opts->verbose > 0)
|
||||||
{
|
{
|
||||||
//fprintf (stderr,"inlvl: %2i%% ", level);
|
// fprintf (stderr,"inlvl: %2i%% ", level);
|
||||||
|
UNUSED(level);
|
||||||
}
|
}
|
||||||
if (state->nac != 0)
|
if (state->nac != 0)
|
||||||
{
|
{
|
||||||
@@ -59,8 +60,10 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
char duid[3];
|
char duid[3];
|
||||||
char nac[13];
|
char nac[13];
|
||||||
int level;
|
int level;
|
||||||
|
UNUSED2(nac, level);
|
||||||
|
|
||||||
char status_0;
|
char status_0;
|
||||||
|
UNUSED(status_0);
|
||||||
char bch_code[63];
|
char bch_code[63];
|
||||||
int index_bch_code;
|
int index_bch_code;
|
||||||
unsigned char parity;
|
unsigned char parity;
|
||||||
@@ -87,63 +90,37 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
//NXDN FSW
|
//NXDN FSW
|
||||||
if ((state->synctype == 28) || (state->synctype == 29))
|
if ((state->synctype == 28) || (state->synctype == 29))
|
||||||
{
|
{
|
||||||
|
//MBEout restored, is not handled internally by nxdn_frame.c
|
||||||
nxdn_frame (opts, state);
|
nxdn_frame (opts, state);
|
||||||
//no MBEout...use symbol capture bin instead!
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//DSTAR
|
||||||
else if ((state->synctype == 6) || (state->synctype == 7))
|
else if ((state->synctype == 6) || (state->synctype == 7))
|
||||||
{
|
{
|
||||||
state->nac = 0;
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||||
state->lastsrc = 0;
|
openMbeOutFile (opts, state);
|
||||||
state->lasttg = 0;
|
sprintf (state->fsubtype, " VOICE ");
|
||||||
if (opts->errorbars == 1)
|
processDSTAR (opts, state);
|
||||||
{
|
return;
|
||||||
if (opts->verbose > 0)
|
}
|
||||||
{
|
|
||||||
level = (int) state->max / 164;
|
|
||||||
//fprintf (stderr,"inlvl: %2i%% ", level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->nac = 0;
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
|
||||||
processDSTAR (opts, state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ((state->synctype == 18) || (state->synctype == 19))
|
else if ((state->synctype == 18) || (state->synctype == 19))
|
||||||
{
|
{
|
||||||
state->nac = 0;
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||||
state->lastsrc = 0;
|
openMbeOutFile (opts, state);
|
||||||
state->lasttg = 0;
|
sprintf (state->fsubtype, " DATA ");
|
||||||
if (opts->errorbars == 1)
|
processDSTAR_HD (opts, state);
|
||||||
{
|
return;
|
||||||
if (opts->verbose > 0)
|
}
|
||||||
{
|
|
||||||
level = (int) state->max / 164;
|
|
||||||
//fprintf (stderr,"inlvl: %2i%% ", level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->nac = 0;
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
sprintf (state->fsubtype, " DATA ");
|
|
||||||
processDSTAR_HD (opts, state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//Start DMR Types
|
//Start DMR Types
|
||||||
else if ((state->synctype >= 10) && (state->synctype <= 13) || (state->synctype == 32) || (state->synctype == 33) || (state->synctype == 34) ) //32-34 DMR MS and RC
|
else if ((state->synctype >= 10 && state->synctype <= 13) || (state->synctype == 32) || (state->synctype == 33) || (state->synctype == 34) ) //32-34 DMR MS and RC
|
||||||
{
|
{
|
||||||
|
|
||||||
//print manufacturer strings to branding, disabled 0x10 moto other systems can use that fid set
|
//print manufacturer strings to branding, disabled 0x10 moto other systems can use that fid set
|
||||||
//0x06 is trident, but when searching, apparently, they developed con+, but was bought by moto?
|
//0x06 is trident, but when searching, apparently, they developed con+, but was bought by moto?
|
||||||
if (state->dmr_mfid == 0x10) ; //sprintf (state->dmr_branding, "%s", "Motorola");
|
if (state->dmr_mfid == 0x10) ; //sprintf (state->dmr_branding, "%s", "Motorola");
|
||||||
else if (state->dmr_mfid == 0x68) sprintf (state->dmr_branding, "%s", "Hytera");
|
else if (state->dmr_mfid == 0x68) sprintf (state->dmr_branding, "%s", " Hytera");
|
||||||
else if (state->dmr_mfid == 0x58) sprintf (state->dmr_branding, "%s", "Tait");
|
else if (state->dmr_mfid == 0x58) sprintf (state->dmr_branding, "%s", " Tait");
|
||||||
|
|
||||||
//disabling these due to random data decodes setting an odd mfid, could be legit, but only for that one packet?
|
//disabling these due to random data decodes setting an odd mfid, could be legit, but only for that one packet?
|
||||||
//or, its just a decode error somewhere
|
//or, its just a decode error somewhere
|
||||||
@@ -164,89 +141,79 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
//disable so radio id doesn't blink in and out during ncurses and aggressive_framesync
|
//disable so radio id doesn't blink in and out during ncurses and aggressive_framesync
|
||||||
state->nac = 0;
|
state->nac = 0;
|
||||||
//state->lastsrc = 0;
|
|
||||||
//state->lasttg = 0;
|
|
||||||
//state->lastsrcR = 0;
|
|
||||||
//state->lasttgR = 0;
|
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
if (opts->verbose > 0)
|
||||||
{
|
{
|
||||||
if (opts->verbose > 0)
|
level = (int) state->max / 164;
|
||||||
{
|
//fprintf (stderr,"inlvl: %2i%% ", level);
|
||||||
level = (int) state->max / 164;
|
|
||||||
//fprintf (stderr,"inlvl: %2i%% ", level);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ( (state->synctype == 11) || (state->synctype == 12) || (state->synctype == 32) ) //DMR Voice Modes
|
if ( (state->synctype == 11) || (state->synctype == 12) || (state->synctype == 32) ) //DMR Voice Modes
|
||||||
|
{
|
||||||
|
|
||||||
|
sprintf (state->fsubtype, " VOICE ");
|
||||||
|
if (opts->dmr_stereo == 0 && state->synctype < 32)
|
||||||
{
|
{
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL) && opts->dmr_stereo == 0)
|
sprintf (state->slot1light, " slot1 ");
|
||||||
{
|
sprintf (state->slot2light, " slot2 ");
|
||||||
openMbeOutFile (opts, state);
|
//we can safely open MBE on any MS or mono handling
|
||||||
}
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state);
|
||||||
if (opts->dmr_stereo == 0 && state->synctype < 32) // -T option for DMR (TDMA) stereo
|
|
||||||
{
|
|
||||||
sprintf (state->slot1light, " slot1 ");
|
|
||||||
sprintf (state->slot2light, " slot2 ");
|
|
||||||
dmrMSBootstrap (opts, state);
|
|
||||||
}
|
|
||||||
if (opts->dmr_mono == 1 && state->synctype == 32)
|
|
||||||
{
|
|
||||||
// fprintf (stderr, "%s", KRED);
|
|
||||||
// fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
|
|
||||||
// fprintf (stderr, "%s", KNRM);
|
|
||||||
dmrMSBootstrap (opts, state);
|
|
||||||
}
|
|
||||||
if (opts->dmr_stereo == 1) //opts->dmr_stereo == 1
|
|
||||||
{
|
|
||||||
state->dmr_stereo = 1; //set the state to 1 when handling pure voice frames
|
|
||||||
if (state->synctype > 31 )
|
|
||||||
{
|
|
||||||
dmrMSBootstrap (opts, state); //bootstrap into MS Bootstrap (voice only)
|
|
||||||
}
|
|
||||||
else dmrBSBootstrap (opts, state); //bootstrap into BS Bootstrap
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (opts->dmr_mono == 1 && state->synctype == 32)
|
||||||
|
{
|
||||||
|
//we can safely open MBE on any MS or mono handling
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
|
if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state);
|
||||||
|
}
|
||||||
|
if (opts->dmr_stereo == 1) //opts->dmr_stereo == 1
|
||||||
|
{
|
||||||
|
state->dmr_stereo = 1; //set the state to 1 when handling pure voice frames
|
||||||
|
if (state->synctype > 31 )
|
||||||
|
{
|
||||||
|
//we can safely open MBE on any MS or mono handling
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
|
if (opts->p25_trunk == 0) dmrMSBootstrap (opts, state); //bootstrap into MS Bootstrap (voice only)
|
||||||
|
}
|
||||||
|
else dmrBSBootstrap (opts, state); //bootstrap into BS Bootstrap
|
||||||
|
}
|
||||||
|
}
|
||||||
else if ( (state->synctype == 33) || (state->synctype == 34) ) //MS Data and RC data
|
else if ( (state->synctype == 33) || (state->synctype == 34) ) //MS Data and RC data
|
||||||
{
|
{
|
||||||
dmrMSData (opts, state);
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
// if (opts->dmr_stereo == 0)
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
// {
|
if (opts->p25_trunk == 0) dmrMSData (opts, state);
|
||||||
// closeMbeOutFile (opts, state);
|
|
||||||
// state->err_str[0] = 0;
|
|
||||||
// fprintf (stderr, "%s", KRED);
|
|
||||||
// fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
|
|
||||||
// fprintf (stderr, "%s", KNRM);
|
|
||||||
// }
|
|
||||||
// if (opts->dmr_stereo == 1)
|
|
||||||
// {
|
|
||||||
// dmrMSData (opts, state);
|
|
||||||
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (opts->dmr_stereo == 0)
|
if (opts->dmr_stereo == 0)
|
||||||
{
|
{
|
||||||
closeMbeOutFile (opts, state);
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
|
||||||
state->err_str[0] = 0;
|
state->err_str[0] = 0;
|
||||||
sprintf (state->slot1light, " slot1 ");
|
sprintf (state->slot1light, " slot1 ");
|
||||||
sprintf (state->slot2light, " slot2 ");
|
sprintf (state->slot2light, " slot2 ");
|
||||||
state->payload_miP = 0; //zero out when switching back to data sync
|
|
||||||
dmr_data_sync (opts, state);
|
dmr_data_sync (opts, state);
|
||||||
}
|
}
|
||||||
//switch dmr_stereo to 0 when handling BS data frame syncs with processDMRdata
|
//switch dmr_stereo to 0 when handling BS data frame syncs with processDMRdata
|
||||||
if (opts->dmr_stereo == 1)
|
if (opts->dmr_stereo == 1)
|
||||||
{
|
{
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
|
||||||
state->dmr_stereo = 0; //set the state to zero for handling pure data frames
|
state->dmr_stereo = 0; //set the state to zero for handling pure data frames
|
||||||
sprintf (state->slot1light, " slot1 ");
|
sprintf (state->slot1light, " slot1 ");
|
||||||
sprintf (state->slot2light, " slot2 ");
|
sprintf (state->slot2light, " slot2 ");
|
||||||
state->payload_miP = 0; //zero out when switching back to data sync
|
|
||||||
dmr_data_sync (opts, state);
|
dmr_data_sync (opts, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if ((state->synctype >= 2) && (state->synctype <= 5))
|
//X2-TDMA
|
||||||
|
else if ((state->synctype >= 2) && (state->synctype <= 5))
|
||||||
{
|
{
|
||||||
state->nac = 0;
|
state->nac = 0;
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
@@ -256,15 +223,15 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
if ((state->synctype == 3) || (state->synctype == 4))
|
if ((state->synctype == 3) || (state->synctype == 4))
|
||||||
{
|
{
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||||
{
|
{
|
||||||
openMbeOutFile (opts, state);
|
openMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
sprintf (state->fsubtype, " VOICE ");
|
||||||
processX2TDMAvoice (opts, state);
|
processX2TDMAvoice (opts, state);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
closeMbeOutFile (opts, state);
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
state->err_str[0] = 0;
|
state->err_str[0] = 0;
|
||||||
processX2TDMAdata (opts, state);
|
processX2TDMAdata (opts, state);
|
||||||
}
|
}
|
||||||
@@ -272,21 +239,18 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
else if ((state->synctype == 14) || (state->synctype == 15))
|
else if ((state->synctype == 14) || (state->synctype == 15))
|
||||||
{
|
{
|
||||||
//state->nac = 0;
|
|
||||||
//state->lastsrc = 0;
|
|
||||||
//state->lasttg = 0;
|
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
|
{
|
||||||
|
if (opts->verbose > 0)
|
||||||
{
|
{
|
||||||
if (opts->verbose > 0)
|
level = (int) state->max / 164;
|
||||||
{
|
//fprintf (stderr,"inlvl: %2i%% ", level);
|
||||||
level = (int) state->max / 164;
|
|
||||||
//fprintf (stderr,"inlvl: %2i%% ", level);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||||
{
|
{
|
||||||
openMbeOutFile (opts, state);
|
openMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
sprintf (state->fsubtype, " VOICE ");
|
sprintf (state->fsubtype, " VOICE ");
|
||||||
processProVoice (opts, state);
|
processProVoice (opts, state);
|
||||||
return;
|
return;
|
||||||
@@ -294,32 +258,38 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
//edacs
|
//edacs
|
||||||
else if ((state->synctype == 37) || (state->synctype == 38))
|
else if ((state->synctype == 37) || (state->synctype == 38))
|
||||||
{
|
{
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
edacs (opts, state);
|
edacs (opts, state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//ysf
|
//ysf
|
||||||
else if ((state->synctype == 30) || (state->synctype == 31))
|
else if ((state->synctype == 30) || (state->synctype == 31))
|
||||||
{
|
{
|
||||||
//Do stuff
|
//relocate MBEout to inside frame handling -- Not Working Currently for YSF
|
||||||
//fprintf(stderr, "YSF Sync! \n");
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
//openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
//sprintf(state->fsubtype, " VOICE ");
|
|
||||||
processYSF(opts, state);
|
processYSF(opts, state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//M17
|
||||||
|
else if ((state->synctype == 16) || (state->synctype == 9) || (state->synctype == 17) || (state->synctype == 8) ||
|
||||||
|
(state->synctype == 76) || (state->synctype == 77) || (state->synctype == 86) || (state->synctype == 87) ||
|
||||||
|
(state->synctype == 99) || (state->synctype == 98) )
|
||||||
|
{
|
||||||
|
if (state->synctype == 98 || state->synctype == 99) //preamble only
|
||||||
|
skipDibit(opts, state, 8); //skip dibits to prime the demodulator
|
||||||
|
else if (state->synctype == 16 || state->synctype == 17)
|
||||||
|
processM17STR(opts, state);
|
||||||
|
else if (state->synctype == 76 || state->synctype == 77) {} //Not available yet
|
||||||
|
// processM17BRT(opts, state); //Not available yet
|
||||||
|
else if (state->synctype == 86 || state->synctype == 87)
|
||||||
|
processM17PKT(opts, state);
|
||||||
|
else
|
||||||
|
processM17LSF(opts, state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
//P25 P2
|
//P25 P2
|
||||||
else if ((state->synctype == 35) || (state->synctype == 36))
|
else if ((state->synctype == 35) || (state->synctype == 36))
|
||||||
{
|
{
|
||||||
//Do stuff
|
//relocate MBEout to inside frame handling
|
||||||
//fprintf(stderr, "YSF Sync! \n");
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
|
||||||
{
|
|
||||||
//openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
//sprintf(state->fsubtype, " VOICE ");
|
|
||||||
processP2(opts, state);
|
processP2(opts, state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -328,6 +298,7 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
/* dPMR Frame Sync 1 */
|
/* dPMR Frame Sync 1 */
|
||||||
fprintf(stderr, "dPMR Frame Sync 1 ");
|
fprintf(stderr, "dPMR Frame Sync 1 ");
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
else if ((state->synctype == 21) || (state->synctype == 25))
|
else if ((state->synctype == 21) || (state->synctype == 25))
|
||||||
{
|
{
|
||||||
@@ -346,12 +317,12 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
}
|
}
|
||||||
state->nac = 0;
|
state->nac = 0;
|
||||||
|
|
||||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||||
{
|
{
|
||||||
openMbeOutFile (opts, state);
|
openMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
sprintf(state->fsubtype, " VOICE ");
|
sprintf(state->fsubtype, " VOICE ");
|
||||||
processdPMRvoice (opts, state);
|
processdPMRvoice (opts, state);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -360,11 +331,13 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
/* dPMR Frame Sync 3 */
|
/* dPMR Frame Sync 3 */
|
||||||
fprintf(stderr, "dPMR Frame Sync 3 ");
|
fprintf(stderr, "dPMR Frame Sync 3 ");
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
else if ((state->synctype == 23) || (state->synctype == 27))
|
else if ((state->synctype == 23) || (state->synctype == 27))
|
||||||
{
|
{
|
||||||
/* dPMR Frame Sync 4 */
|
/* dPMR Frame Sync 4 */
|
||||||
fprintf(stderr, "dPMR Frame Sync 4 ");
|
fprintf(stderr, "dPMR Frame Sync 4 ");
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
//dPMR
|
//dPMR
|
||||||
else //P25
|
else //P25
|
||||||
@@ -437,7 +410,8 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
if (new_nac != state->nac) {
|
if (new_nac != state->nac) {
|
||||||
// NAC fixed by error correction
|
// NAC fixed by error correction
|
||||||
state->nac = new_nac;
|
state->nac = new_nac;
|
||||||
if (state->p2_hardset == 0 && new_nac != 0)
|
//apparently, both 0 and 0xFFF can the BCH code on signal drop
|
||||||
|
if (state->p2_hardset == 0 && new_nac != 0 && new_nac != 0xFFF)
|
||||||
{
|
{
|
||||||
state->p2_cc = new_nac;
|
state->p2_cc = new_nac;
|
||||||
}
|
}
|
||||||
@@ -463,15 +437,15 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
// Header Data Unit
|
// Header Data Unit
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," HDU\n");
|
fprintf (stderr," HDU\n");
|
||||||
}
|
}
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
{
|
{
|
||||||
closeMbeOutFile (opts, state);
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
openMbeOutFile (opts, state);
|
if (opts->mbe_out_f == NULL) openMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||||
state->lastp25type = 2;
|
state->lastp25type = 2;
|
||||||
state->dmrburstL = 25;
|
state->dmrburstL = 25;
|
||||||
@@ -483,26 +457,22 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
// Logical Link Data Unit 1
|
// Logical Link Data Unit 1
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," LDU1 ");
|
fprintf (stderr," LDU1 ");
|
||||||
}
|
}
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f == NULL)
|
||||||
{
|
{
|
||||||
if (opts->mbe_out_f == NULL)
|
openMbeOutFile (opts, state);
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
state->lastp25type = 1;
|
state->lastp25type = 1;
|
||||||
state->dmrburstL = 26;
|
state->dmrburstL = 26;
|
||||||
state->currentslot = 0;
|
state->currentslot = 0;
|
||||||
sprintf (state->fsubtype, " LDU1 ");
|
sprintf (state->fsubtype, " LDU1 ");
|
||||||
state->numtdulc = 0;
|
state->numtdulc = 0;
|
||||||
if (state->payload_algid == 0x81)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
processLDU1 (opts, state);
|
processLDU1 (opts, state);
|
||||||
}
|
}
|
||||||
@@ -514,27 +484,27 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
if (state->lastp25type != 1)
|
if (state->lastp25type != 1)
|
||||||
{
|
{
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr,"\n Ignoring LDU2 not preceeded by LDU1\n");
|
fprintf (stderr,"\n Ignoring LDU2 not preceeded by LDU1\n");
|
||||||
}
|
}
|
||||||
state->lastp25type = 0;
|
state->lastp25type = 0;
|
||||||
sprintf (state->fsubtype, " ");
|
sprintf (state->fsubtype, " ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," LDU2 ");
|
fprintf (stderr," LDU2 ");
|
||||||
}
|
}
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f == NULL)
|
||||||
{
|
{
|
||||||
if (opts->mbe_out_f == NULL)
|
openMbeOutFile (opts, state);
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
state->lastp25type = 2;
|
state->lastp25type = 2;
|
||||||
sprintf (state->fsubtype, " LDU2 ");
|
sprintf (state->fsubtype, " LDU2 ");
|
||||||
state->numtdulc = 0;
|
state->numtdulc = 0;
|
||||||
@@ -546,25 +516,25 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
// Terminator with subsequent Link Control
|
// Terminator with subsequent Link Control
|
||||||
state->dmrburstL = 28;
|
state->dmrburstL = 28;
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," TDULC\n");
|
fprintf (stderr," TDULC\n");
|
||||||
}
|
}
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
{
|
{
|
||||||
closeMbeOutFile (opts, state);
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||||
state->lasttg = 0;
|
// state->lasttg = 0;
|
||||||
state->lastsrc = 0;
|
// state->lastsrc = 0;
|
||||||
state->lastp25type = 0;
|
state->lastp25type = 0;
|
||||||
state->err_str[0] = 0;
|
state->err_str[0] = 0;
|
||||||
sprintf (state->fsubtype, " TDULC ");
|
sprintf (state->fsubtype, " TDULC ");
|
||||||
state->numtdulc++;
|
state->numtdulc++;
|
||||||
if ((opts->resume > 0) && (state->numtdulc > opts->resume))
|
if ((opts->resume > 0) && (state->numtdulc > opts->resume))
|
||||||
{
|
{
|
||||||
resumeScan (opts, state);
|
resumeScan (opts, state);
|
||||||
}
|
}
|
||||||
processTDULC (opts, state);
|
processTDULC (opts, state);
|
||||||
state->err_str[0] = 0;
|
state->err_str[0] = 0;
|
||||||
}
|
}
|
||||||
@@ -573,14 +543,14 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
// Terminator without subsequent Link Control
|
// Terminator without subsequent Link Control
|
||||||
state->dmrburstL = 28;
|
state->dmrburstL = 28;
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," TDU\n");
|
fprintf (stderr," TDU\n");
|
||||||
}
|
}
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
{
|
{
|
||||||
closeMbeOutFile (opts, state);
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
}
|
}
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||||
state->lasttg = 0;
|
state->lasttg = 0;
|
||||||
state->lastsrc = 0;
|
state->lastsrc = 0;
|
||||||
@@ -594,120 +564,59 @@ processFrame (dsd_opts * opts, dsd_state * state)
|
|||||||
{
|
{
|
||||||
state->dmrburstL = 29;
|
state->dmrburstL = 29;
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," TSBK");
|
fprintf (stderr," TSBK");
|
||||||
}
|
}
|
||||||
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
}
|
||||||
if (opts->resume > 0)
|
if (opts->resume > 0)
|
||||||
{
|
{
|
||||||
resumeScan (opts, state);
|
resumeScan (opts, state);
|
||||||
}
|
}
|
||||||
state->lasttg = 0;
|
state->lasttg = 0;
|
||||||
state->lastsrc = 0;
|
state->lastsrc = 0;
|
||||||
state->lastp25type = 3;
|
state->lastp25type = 3;
|
||||||
sprintf (state->fsubtype, " TSBK ");
|
sprintf (state->fsubtype, " TSBK ");
|
||||||
|
|
||||||
// Now processing NID
|
|
||||||
processTSBK(opts, state);
|
processTSBK(opts, state);
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (strcmp (duid, "30") == 0)
|
else if (strcmp (duid, "30") == 0)
|
||||||
{
|
{
|
||||||
|
state->dmrburstL = 29;
|
||||||
if (opts->errorbars == 1)
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
printFrameInfo (opts, state);
|
printFrameInfo (opts, state);
|
||||||
fprintf (stderr," PDU\n");
|
fprintf (stderr," MPDU\n"); //multi block format PDU
|
||||||
}
|
}
|
||||||
|
if (opts->mbe_out_dir[0] != 0)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state);
|
||||||
|
if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state);
|
||||||
|
}
|
||||||
if (opts->resume > 0)
|
if (opts->resume > 0)
|
||||||
{
|
{
|
||||||
resumeScan (opts, state);
|
resumeScan (opts, state);
|
||||||
}
|
}
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f == NULL)
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->lastp25type = 4;
|
state->lastp25type = 4;
|
||||||
sprintf (state->fsubtype, " PDU ");
|
sprintf (state->fsubtype, " MPDU ");
|
||||||
}
|
|
||||||
// try to guess based on previous frame if unknown type
|
|
||||||
else if (state->lastp25type == 1)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
fprintf (stderr,"(LDU2) ");
|
|
||||||
}
|
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f == NULL)
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//state->lastp25type = 0;
|
|
||||||
// Guess that the state is LDU2
|
|
||||||
state->lastp25type = 2;
|
|
||||||
sprintf (state->fsubtype, "(LDU2) ");
|
|
||||||
state->numtdulc = 0;
|
|
||||||
processLDU2 (opts, state);
|
|
||||||
}
|
|
||||||
else if (state->lastp25type == 2)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
fprintf (stderr,"(LDU1) ");
|
|
||||||
}
|
|
||||||
if (opts->mbe_out_dir[0] != 0)
|
|
||||||
{
|
|
||||||
if (opts->mbe_out_f == NULL)
|
|
||||||
{
|
|
||||||
openMbeOutFile (opts, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//state->lastp25type = 0;
|
|
||||||
// Guess that the state is LDU1
|
|
||||||
state->lastp25type = 1;
|
|
||||||
sprintf (state->fsubtype, "(LDU1) ");
|
|
||||||
state->numtdulc = 0;
|
|
||||||
processLDU1 (opts, state);
|
|
||||||
}
|
|
||||||
else if (state->lastp25type == 3)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
fprintf (stderr," (TSBK)\n");
|
|
||||||
}
|
|
||||||
//state->lastp25type = 0;
|
|
||||||
// Guess that the state is TSBK
|
|
||||||
state->lastp25type = 3;
|
|
||||||
sprintf (state->fsubtype, "(TSBK) ");
|
|
||||||
|
|
||||||
// Now processing NID
|
processMPDU(opts, state);
|
||||||
skipDibit (opts, state, 328-25);
|
|
||||||
}
|
|
||||||
else if (state->lastp25type == 4)
|
|
||||||
{
|
|
||||||
if (opts->errorbars == 1)
|
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
fprintf (stderr," (PDU)\n");
|
|
||||||
}
|
|
||||||
state->lastp25type = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
state->lastp25type = 0;
|
||||||
|
sprintf (state->fsubtype, " ");
|
||||||
|
if (opts->errorbars == 1)
|
||||||
{
|
{
|
||||||
state->lastp25type = 0;
|
printFrameInfo (opts, state);
|
||||||
sprintf (state->fsubtype, " ");
|
// fprintf (stderr," duid:%s *Unknown DUID*\n", duid);
|
||||||
if (opts->errorbars == 1)
|
fprintf (stderr," duid:%s \n", duid); //DUID ERR
|
||||||
{
|
|
||||||
printFrameInfo (opts, state);
|
|
||||||
fprintf (stderr," duid:%s *Unknown DUID*\n", duid); //prints on dPMR frame 3
|
|
||||||
// fprintf (stderr, "\n"); //prints on dPMR frame 3
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
766
src/dsd_gps.c
Normal file
@@ -0,0 +1,766 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dsd_gps.c
|
||||||
|
* GPS Handling Functions for Various Protocols
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2023-12 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input)
|
||||||
|
{
|
||||||
|
//NOTE: This is defined in ETSI TS 102 361-4 V1.12.1 (2023-07) p208
|
||||||
|
//6.6.11.3.2 USBD Polling Service Poll Response PDU for LIP (That's a mouthful)
|
||||||
|
|
||||||
|
int slot = state->currentslot;
|
||||||
|
|
||||||
|
//NOTE: This format is pretty much the same as DMR EMB GPS, but has a few extra elements,
|
||||||
|
//so I got lazy and just lifted most of the code from there, also assuming same lat/lon calcs
|
||||||
|
//since those have been tested to work in DMR EMB GPS and units are same in LIP
|
||||||
|
|
||||||
|
fprintf (stderr, "Location Information Protocol; ");
|
||||||
|
|
||||||
|
// uint8_t service_type = (uint8_t)ConvertBitIntoBytes(&input[0], 4); //checked before arrival here
|
||||||
|
uint8_t time_elapsed = (uint8_t)ConvertBitIntoBytes(&input[6], 2);
|
||||||
|
uint8_t lon_sign = input[8];
|
||||||
|
uint32_t lon = (uint32_t)ConvertBitIntoBytes(&input[9], 24); //8, 25
|
||||||
|
uint8_t lat_sign = input[33];
|
||||||
|
uint32_t lat = (uint32_t)ConvertBitIntoBytes(&input[34], 23); //33, 24
|
||||||
|
uint8_t pos_err = (uint8_t)ConvertBitIntoBytes(&input[57], 2);
|
||||||
|
uint8_t hor_vel = (uint8_t)ConvertBitIntoBytes(&input[59], 7);
|
||||||
|
uint8_t dir_tra = (uint8_t)ConvertBitIntoBytes(&input[66], 4);
|
||||||
|
uint8_t reason = (uint8_t)ConvertBitIntoBytes(&input[70], 3);
|
||||||
|
uint8_t add_hash = (uint8_t)ConvertBitIntoBytes(&input[73], 8); //MS Source Address Hash
|
||||||
|
|
||||||
|
//NOTE: May need to use double instead of float to avoid rounding errors
|
||||||
|
double latitude, longitude = 0.0f;
|
||||||
|
double lat_unit = (double)180/ pow (2.0, 24); //180 divided by 2^24 -- 6.3.30
|
||||||
|
double lon_unit = (double)360/ pow (2.0, 25); //360 divided by 2^25 -- 6.3.50
|
||||||
|
double lon_sf = 1.0f; //float value we can multiple longitude with
|
||||||
|
double lat_sf = 1.0f; //float value we can multiple latitude with
|
||||||
|
|
||||||
|
char latstr[3];
|
||||||
|
char lonstr[3];
|
||||||
|
sprintf (latstr, "%s", "N");
|
||||||
|
sprintf (lonstr, "%s", "E");
|
||||||
|
|
||||||
|
char deg_glyph[4];
|
||||||
|
sprintf (deg_glyph, "%s", "°");
|
||||||
|
|
||||||
|
//lat and lon calculations
|
||||||
|
if (lat_sign)
|
||||||
|
{
|
||||||
|
lat = 0x800001 - lat;
|
||||||
|
sprintf (latstr, "%s", "S");
|
||||||
|
lat_sf = -1.0f;
|
||||||
|
}
|
||||||
|
latitude = ((double)lat * lat_unit);
|
||||||
|
|
||||||
|
if (lon_sign)
|
||||||
|
{
|
||||||
|
lon = 0x1000001 - lon;
|
||||||
|
sprintf (lonstr, "%s", "W");
|
||||||
|
lon_sf = -1.0f;
|
||||||
|
}
|
||||||
|
longitude = ((double)lon * lon_unit);
|
||||||
|
|
||||||
|
//6.3.63 Position Error
|
||||||
|
//6.3.17 Horizontal velocity
|
||||||
|
/*
|
||||||
|
Horizontal velocity shall be encoded for speeds 0 km/h to 28 km/h in 1 km/h steps and
|
||||||
|
from 28 km/h onwards using equation: v = C × (1 + x)^(K-A) + B where:
|
||||||
|
*/
|
||||||
|
float v = 0.0f;
|
||||||
|
float C = 16.0f;
|
||||||
|
float x = 0.038f;
|
||||||
|
float A = 13.0f;
|
||||||
|
float K = (float)hor_vel;
|
||||||
|
float B = 0.0f;
|
||||||
|
|
||||||
|
if (hor_vel > 28)
|
||||||
|
v = C * (pow(1+x, K-A)) + B; //I think this pow function is set up correctly
|
||||||
|
else v = (float)hor_vel;
|
||||||
|
|
||||||
|
float dir = (((float)dir_tra + 11.25f)/22.5f); //page 68, Table 6.45
|
||||||
|
|
||||||
|
//truncated and rounded forms
|
||||||
|
int vt = (int)v;
|
||||||
|
int dt = (int)dir;
|
||||||
|
|
||||||
|
//sanity check
|
||||||
|
if (abs (latitude) < 90 && abs(longitude) < 180)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Src(Hash); %03d; Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf); Spd: %d km/h; Dir: %d%s",add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, lat_sf * latitude, lon_sf * longitude, vt, dt, deg_glyph);
|
||||||
|
|
||||||
|
//6.3.63 Position Error
|
||||||
|
uint16_t position_error = 2 * pow(10, pos_err); //2 * 10^pos_err
|
||||||
|
if (pos_err == 0x7 ) fprintf (stderr, "\n Position Error: Unknown or Invalid;");
|
||||||
|
else fprintf (stderr, "\n Position Error: Less than %dm;", position_error);
|
||||||
|
|
||||||
|
//Reason For Sending 6.6.11.3.3 Table 6.80
|
||||||
|
if (reason) fprintf (stderr, " Reserved: %d;", reason);
|
||||||
|
else fprintf (stderr, " Request Response; ");
|
||||||
|
|
||||||
|
//6.3.78 Time elapsed
|
||||||
|
if (time_elapsed == 0) fprintf (stderr, " TE: < 5s;");
|
||||||
|
if (time_elapsed == 1) fprintf (stderr, " TE: < 5m;");
|
||||||
|
if (time_elapsed == 2) fprintf (stderr, " TE: < 30m;");
|
||||||
|
if (time_elapsed == 3) fprintf (stderr, " TE: NA or UNK;"); //not applicable or unknown
|
||||||
|
|
||||||
|
//save to array for ncurses
|
||||||
|
if (pos_err != 0x7)
|
||||||
|
{
|
||||||
|
sprintf (state->dmr_embedded_gps[slot], "%03d; LIP: %.5lf%s%s %.5lf%s%s; Err: %dm; Spd: %d km/h; Dir: %d%s", add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, position_error, vt, dt, deg_glyph);
|
||||||
|
}
|
||||||
|
else sprintf (state->dmr_embedded_gps[slot], "%03d; LIP: %.5lf%s%s %.5lf%s%s Unknown Pos Err; Spd: %d km/h; Dir %d%s", add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, vt, dt, deg_glyph);
|
||||||
|
|
||||||
|
//save to LRRP report for mapping/logging
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
if (opts->lrrp_file_output == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
char * datestr = getDate();
|
||||||
|
char * timestr = getTime();
|
||||||
|
|
||||||
|
//open file by name that is supplied in the ncurses terminal, or cli
|
||||||
|
pFile = fopen (opts->lrrp_out_file, "a");
|
||||||
|
fprintf (pFile, "%s\t", datestr );
|
||||||
|
fprintf (pFile, "%s\t", timestr );
|
||||||
|
fprintf (pFile, "%08d\t", add_hash);
|
||||||
|
fprintf (pFile, "%.5lf\t", latitude);
|
||||||
|
fprintf (pFile, "%.5lf\t", longitude);
|
||||||
|
fprintf (pFile, "%d\t", vt); //speed in km/h
|
||||||
|
fprintf (pFile, "%d\t", dt); //direction of travel
|
||||||
|
fprintf (pFile, "\n");
|
||||||
|
fclose (pFile);
|
||||||
|
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
if (datestr != NULL)
|
||||||
|
{
|
||||||
|
free (datestr);
|
||||||
|
datestr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else fprintf (stderr, " Position Calculation Error;");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int type)
|
||||||
|
{
|
||||||
|
int slot = state->currentslot;
|
||||||
|
|
||||||
|
//NOTE: The Only difference between Short (type == 1) and Long Format (type == 2)
|
||||||
|
//is the utc_ss3 on short vs utc_ss6 and inclusion of COG value on long
|
||||||
|
|
||||||
|
//NOTE: MFID Specific Formats are not handled here, unknown, could be added if worked out
|
||||||
|
//they could share most of the same elements and use the large spare bits in block 2 for extra
|
||||||
|
// uint8_t mfid = (uint8_t)ConvertBitIntoBytes(&input[88], 8); //on type 3, last octet of first block carries MFID
|
||||||
|
|
||||||
|
// uint8_t nmea_c = input[0]; //encrypted -- checked before we get here
|
||||||
|
uint8_t nmea_ns = input[1]; //north/south (lat sign)
|
||||||
|
uint8_t nmea_ew = input[2]; //east/west (lon sign)
|
||||||
|
uint8_t nmea_q = input[3]; //Quality Indicator (no fix or fix valid)
|
||||||
|
uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[4], 7); //speed in knots (127 = greater than 126 knots)
|
||||||
|
|
||||||
|
//Latitude Bits
|
||||||
|
uint8_t nmea_ndeg = (uint8_t)ConvertBitIntoBytes(&input[11], 7); //Latitude Degrees
|
||||||
|
uint8_t nmea_nmin = (uint8_t)ConvertBitIntoBytes(&input[18], 6); //Latitude Minutes
|
||||||
|
uint16_t nmea_nminf = (uint16_t)ConvertBitIntoBytes(&input[24], 14); //Latitude Fractions of Minutes
|
||||||
|
|
||||||
|
//Longitude Bits
|
||||||
|
uint8_t nmea_edeg = (uint8_t)ConvertBitIntoBytes(&input[38], 8); //Longitude Degrees
|
||||||
|
uint8_t nmea_emin = (uint8_t)ConvertBitIntoBytes(&input[46], 6); //Longitude Minutes
|
||||||
|
uint16_t nmea_eminf = (uint16_t)ConvertBitIntoBytes(&input[52], 14); //Longitude Fractions of Minutes
|
||||||
|
|
||||||
|
//UTC Time and COG
|
||||||
|
uint8_t nmea_utc_hh = (uint8_t)ConvertBitIntoBytes(&input[66], 5);
|
||||||
|
uint8_t nmea_utc_mm = (uint8_t)ConvertBitIntoBytes(&input[71], 6);
|
||||||
|
//seconds and the addition of COG is the difference between short and long formats
|
||||||
|
uint8_t nmea_utc_ss3 = (uint8_t)ConvertBitIntoBytes(&input[77], 3); //seconds in 10s
|
||||||
|
uint8_t nmea_utc_ss6 = (uint8_t)ConvertBitIntoBytes(&input[77], 6); //seconds in 1s
|
||||||
|
uint16_t nmea_cog = (uint16_t)ConvertBitIntoBytes(&input[103], 9); //course over ground in degrees
|
||||||
|
|
||||||
|
//lat and lon conversion
|
||||||
|
char deg_glyph[4];
|
||||||
|
sprintf (deg_glyph, "%s", "°");
|
||||||
|
float latitude = 0.0f;
|
||||||
|
float longitude = 0.0f;
|
||||||
|
// float m_unit = 1.0f / 60.0f; //unit to convert min into decimal value - (1/60)*60 minutes = 1 degree
|
||||||
|
// float mm_unit = 1.0f / 10000.0f; //unit to convert minf into decimal value - (0000 - 9999) 0.0001×9999 = .9999 minutes, so its sub 1 minute decimal
|
||||||
|
|
||||||
|
//testing w/ Harris NMEA like values (ran tests over there with this code, this seems to work on those values)
|
||||||
|
float m_unit = 1.0f / 60.0f; //unit to convert min into decimal value - (1/60)*60 minutes = 1 degree
|
||||||
|
float mm_unit = 1.0f / 600000.0f; //unit to convert minf into decimal value - (0000 - 9999) 0.0001×9999 = .9999 minutes, so its sub 1 minute decimal
|
||||||
|
|
||||||
|
//speed conversion
|
||||||
|
float fmps, fmph, fkph = 0.0f; //conversion of knots to mps, kph, and mph values
|
||||||
|
fmps = (float)nmea_speed * 0.514444; UNUSED(fmps);
|
||||||
|
fmph = (float)nmea_speed * 1.15078f; UNUSED(fmph);
|
||||||
|
fkph = (float)nmea_speed * 1.852f;
|
||||||
|
|
||||||
|
//calculate decimal representation of latidude and longitude (need some samples to test)
|
||||||
|
latitude = ( (float)nmea_ndeg + ((float)nmea_nmin*m_unit) + ((float)nmea_nminf*mm_unit) );
|
||||||
|
longitude = ( (float)nmea_edeg + ((float)nmea_emin*m_unit) + ((float)nmea_eminf*mm_unit) );
|
||||||
|
|
||||||
|
if (!nmea_ns) latitude *= -1.0f; //0 is South, 1 is North
|
||||||
|
if (!nmea_ew) longitude *= -1.0f; //0 is West, 1 is East
|
||||||
|
|
||||||
|
fprintf (stderr, " GPS: %f%s, %f%s;", latitude, deg_glyph, longitude, deg_glyph);
|
||||||
|
|
||||||
|
//Speed in Knots
|
||||||
|
if (nmea_speed > 126)
|
||||||
|
fprintf (stderr, " SPD > 126 knots or %f kph;", fkph);
|
||||||
|
else
|
||||||
|
fprintf (stderr, " SPD: %d knots; %f kph;", nmea_speed, fkph);
|
||||||
|
|
||||||
|
//Print UTC Time and COG according to Format Type
|
||||||
|
if (type == 1)
|
||||||
|
fprintf (stderr, " FIX: %d; %02d:%02d:%02d UTC; Short Format;", nmea_q, nmea_utc_hh, nmea_utc_mm, nmea_utc_ss3);
|
||||||
|
if (type == 2)
|
||||||
|
fprintf (stderr, " FIX: %d; %02d:%02d:%02d UTC; COG: %d%s; Long Format;", nmea_q, nmea_utc_hh, nmea_utc_mm, nmea_utc_ss6, nmea_cog, deg_glyph);
|
||||||
|
|
||||||
|
//save to ncurses string
|
||||||
|
sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
|
||||||
|
|
||||||
|
//save to LRRP report for mapping/logging
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
if (opts->lrrp_file_output == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
char * datestr = getDate();
|
||||||
|
char * timestr = getTime();
|
||||||
|
|
||||||
|
int s = (int)fkph; //rounded interger format for the log report
|
||||||
|
int a = 0;
|
||||||
|
if (type == 2)
|
||||||
|
a = nmea_cog; //long format only
|
||||||
|
//open file by name that is supplied in the ncurses terminal, or cli
|
||||||
|
pFile = fopen (opts->lrrp_out_file, "a");
|
||||||
|
fprintf (pFile, "%s\t", datestr );
|
||||||
|
fprintf (pFile, "%s\t", timestr ); //could switch to UTC time if desired, but would require local user offset
|
||||||
|
fprintf (pFile, "%08d\t", src);
|
||||||
|
fprintf (pFile, "%.6lf\t", latitude);
|
||||||
|
fprintf (pFile, "%.6lf\t", longitude);
|
||||||
|
fprintf (pFile, "%d\t ", s);
|
||||||
|
fprintf (pFile, "%d\t ", a);
|
||||||
|
fprintf (pFile, "\n");
|
||||||
|
fclose (pFile);
|
||||||
|
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
if (datestr != NULL)
|
||||||
|
{
|
||||||
|
free (datestr);
|
||||||
|
datestr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//restructured the Harris GPS to flow more like the DMR UDT NMEA Format when possible
|
||||||
|
void nmea_harris (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot)
|
||||||
|
{
|
||||||
|
|
||||||
|
//GPS working, Time Working (old method), COG and Speed are logical on due west sample
|
||||||
|
//TODO: Fiddle/Test various ns and ew bits for proper signs in case its located elsewhere
|
||||||
|
//UPDATE: Found two contiguous bits that always work on all my accumulated samples
|
||||||
|
//the bits also place it with the 'lone' flagged bit observed prior
|
||||||
|
|
||||||
|
uint8_t nmea_ns = input[88]; //north/south (lat sign) //72 previously used bit
|
||||||
|
uint8_t nmea_ew = input[89]; //east/west (lon sign) //88 previously used bit
|
||||||
|
uint8_t nmea_q = input[147]; //Quality Indicator (last fix or current fix)
|
||||||
|
// uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[129], 7); //129,7
|
||||||
|
uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[129], 6); //this is one bit shorter than called for, but I believe it works
|
||||||
|
|
||||||
|
//Latitude Bits
|
||||||
|
uint8_t nmea_ndeg = (uint8_t)ConvertBitIntoBytes(&input[65], 7); //Latitude Degrees
|
||||||
|
uint8_t nmea_nmin = (uint8_t)ConvertBitIntoBytes(&input[58], 6); //Latitude Minutes
|
||||||
|
uint16_t nmea_nminf = (uint16_t)ConvertBitIntoBytes(&input[42], 14); //Latitude Fractions of Minutes
|
||||||
|
|
||||||
|
//Longitude Bits
|
||||||
|
uint8_t nmea_edeg = (uint8_t)ConvertBitIntoBytes(&input[96], 8); //Longitude Degrees
|
||||||
|
uint8_t nmea_emin = (uint8_t)ConvertBitIntoBytes(&input[90], 6); //Longitude Minutes
|
||||||
|
uint16_t nmea_eminf = (uint16_t)ConvertBitIntoBytes(&input[74], 14); //Longitude Fractions of Minutes
|
||||||
|
|
||||||
|
//NOTE: Removed UTC hh.mm.ss6 format found in DMR UDT, could not make it work
|
||||||
|
//with bits already established for time and it works extremely well as is
|
||||||
|
|
||||||
|
//Course Over Ground in Degrees (0-360)
|
||||||
|
uint16_t nmea_cog = (uint16_t)ConvertBitIntoBytes(&input[135], 9) % 360; //course over ground in degrees //working on due west sample without %360
|
||||||
|
|
||||||
|
//timestamp (16-bit value w/ appended 17th bit to MSB)
|
||||||
|
uint32_t rtime = (uint32_t)ConvertBitIntoBytes(&input[104], 16);
|
||||||
|
rtime |= input[144] << 16; //this bit is flagged on after 0xFFFF rollover in the late afternoon
|
||||||
|
uint32_t thour = rtime / 3600;
|
||||||
|
uint32_t tmin = (rtime % 3600) / 60;
|
||||||
|
uint32_t tsec = (rtime % 3600) % 60;
|
||||||
|
|
||||||
|
//lat and lon conversion
|
||||||
|
char deg_glyph[4];
|
||||||
|
sprintf (deg_glyph, "%s", "°");
|
||||||
|
float latitude = 0.0f;
|
||||||
|
float longitude = 0.0f;
|
||||||
|
float m_unit = 1.0f / 60.0f;
|
||||||
|
float mm_unit = 1.0f / 600000.0f;
|
||||||
|
|
||||||
|
//speed conversion
|
||||||
|
float fmps, fmph, fkph = 0.0f; //conversion of knots to mps, kph, and mph values
|
||||||
|
fmps = (float)nmea_speed * 0.514444; UNUSED(fmps);
|
||||||
|
fmph = (float)nmea_speed * 1.15078f; UNUSED(fmph);
|
||||||
|
fkph = (float)nmea_speed * 1.852f; UNUSED(fkph);
|
||||||
|
|
||||||
|
//calculate decimal representation of latidude and longitude (need some samples to test)
|
||||||
|
latitude = ( (float)nmea_ndeg + ((float)nmea_nmin*m_unit) + ((float)nmea_nminf*mm_unit) );
|
||||||
|
longitude = ( (float)nmea_edeg + ((float)nmea_emin*m_unit) + ((float)nmea_eminf*mm_unit) );
|
||||||
|
|
||||||
|
//This is opposite (seemingly) compared to the version specified by DMR UDT NMEA, could still be different bits involved (to be tested)
|
||||||
|
// if (nmea_ns) latitude *= -1.0f; //1 is South, 0 is North
|
||||||
|
// if (nmea_ew) longitude *= -1.0f; //1 is West, 0 is East
|
||||||
|
|
||||||
|
//DMR UDT NMEA original version (working now with new bit selection 88 and 89 like how DMR UDT NMEA format specifies)
|
||||||
|
if (!nmea_ns) latitude *= -1.0f; //0 is South, 1 is North
|
||||||
|
if (!nmea_ew) longitude *= -1.0f; //0 is West, 1 is East
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
fprintf (stderr, " VCH: %d - SRC: %08d;", slot, src);
|
||||||
|
fprintf (stderr, " GPS: %f%s, %f%s;", latitude, deg_glyph, longitude, deg_glyph);
|
||||||
|
|
||||||
|
//Speed in Knots (assuming in knots, and not in mps, or kph)
|
||||||
|
if (nmea_speed > 126)
|
||||||
|
fprintf (stderr, " SPD > 126 knots or %f MPH;", fmph); //using MPH here, its Harris, they're in the U.S.
|
||||||
|
else
|
||||||
|
fprintf (stderr, " SPD: %d knots; %f MPH;", nmea_speed, fmph);
|
||||||
|
|
||||||
|
//Course Over Ground (COG)
|
||||||
|
fprintf (stderr, " COG: %03d%s;", nmea_cog, deg_glyph); //%360
|
||||||
|
|
||||||
|
//debug speed
|
||||||
|
// fprintf (stderr, " RSPD: %02X;", nmea_speed);
|
||||||
|
|
||||||
|
//debug cog
|
||||||
|
// fprintf (stderr, " RCOG: %03X;", nmea_cog >> 0);
|
||||||
|
|
||||||
|
//UTC Time
|
||||||
|
fprintf (stderr, " T: %02d:%02d:%02d UTC;", thour, tmin, tsec);
|
||||||
|
|
||||||
|
//Fix Indicator
|
||||||
|
if (nmea_q) fprintf (stderr, " Current Fix;");
|
||||||
|
else fprintf (stderr, " Last Fix;");
|
||||||
|
|
||||||
|
//save to ncurses string
|
||||||
|
sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
|
||||||
|
|
||||||
|
//save to LRRP report for mapping/logging
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
if (opts->lrrp_file_output == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
char * datestr = getDate();
|
||||||
|
char * timestr = getTime();
|
||||||
|
|
||||||
|
//rounded interger formats for the log report
|
||||||
|
int s = (int)fkph;
|
||||||
|
int a = nmea_cog;
|
||||||
|
|
||||||
|
//open file by name that is supplied in the ncurses terminal, or cli
|
||||||
|
pFile = fopen (opts->lrrp_out_file, "a");
|
||||||
|
fprintf (pFile, "%s\t", datestr );
|
||||||
|
fprintf (pFile, "%s\t", timestr ); //could switch to UTC time on PDU if desired
|
||||||
|
fprintf (pFile, "%08d\t", src);
|
||||||
|
fprintf (pFile, "%.6lf\t", latitude);
|
||||||
|
fprintf (pFile, "%.6lf\t", longitude);
|
||||||
|
fprintf (pFile, "%d\t ", s);
|
||||||
|
fprintf (pFile, "%d\t ", a);
|
||||||
|
fprintf (pFile, "\n");
|
||||||
|
fclose (pFile);
|
||||||
|
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
if (datestr != NULL)
|
||||||
|
{
|
||||||
|
free (datestr);
|
||||||
|
datestr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//NOTE: There seems to be a few more octets left undecoded in this PDU
|
||||||
|
//not including the CRC obviously, unsure of their meaning, could relate
|
||||||
|
//to other GPS functions like precision, etc, unknown.
|
||||||
|
|
||||||
|
//NOTE2: All these values are just best effort based on observation and
|
||||||
|
//making map points match using logic and distance over time for speed and course
|
||||||
|
//without any documentation, any of these values may still be wrong
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//fallback version (if desired/required)
|
||||||
|
void harris_gps(dsd_opts * opts, dsd_state * state, int slot, uint8_t * input)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint8_t lat_sign, lat_deg, lat_min = 0;
|
||||||
|
uint8_t lon_sign, lon_deg, lon_min = 0;
|
||||||
|
uint16_t lat_mmin = 0;
|
||||||
|
uint16_t lon_mmin = 0;
|
||||||
|
|
||||||
|
//potentially in this PDU, but unverifiable without documentation or reasonable
|
||||||
|
//mathematical proof vs map points and direction of travel / distance over time (assuming the radio is facing that direction)
|
||||||
|
uint16_t rspeed = (uint16_t)ConvertBitIntoBytes(&input[136], 8); //MSB //136
|
||||||
|
rspeed = (rspeed << 8) + (uint16_t)ConvertBitIntoBytes(&input[128], 8); //LSB //128
|
||||||
|
|
||||||
|
//this works okay for example of driver driving due west at the speed limit of the road (fluke?)
|
||||||
|
uint16_t rangle = (uint16_t)ConvertBitIntoBytes(&input[120], 8); //120,8
|
||||||
|
float fspeed = (float)rspeed;
|
||||||
|
float fangle = (float)rangle;
|
||||||
|
|
||||||
|
fspeed /= 255.0f; //unit value of 1 bit
|
||||||
|
fspeed *= 1.56f; //this is based on an observation of a driver moving approx 210 meters in about 8 seconds and this makes it just under the speed limit on the road there
|
||||||
|
// fangle *= 360.0f/255.0f; //unit value of 1 bit
|
||||||
|
fangle *= 2.0f; //may exceed 360 degrees as is (use %)
|
||||||
|
|
||||||
|
int s, a = 0;
|
||||||
|
s = (int)fspeed * 3.6;
|
||||||
|
a = (int)fangle % 360;
|
||||||
|
|
||||||
|
//fix and quality indicators?
|
||||||
|
uint8_t fix = input[147]; UNUSED(fix);
|
||||||
|
uint8_t quality = (uint16_t)ConvertBitIntoBytes(&input[148], 3); UNUSED(quality);
|
||||||
|
|
||||||
|
//timestamp (16-bit value w/ appended 17th bit to MSB)
|
||||||
|
uint32_t rtime = (uint32_t)ConvertBitIntoBytes(&input[104], 16); //seconds since midnight since last GPS fix (whatever the radio has as internal time)
|
||||||
|
rtime |= input[144] << 16; //test appending this as bit 17 (observed this bit set after the afternoon 0xFFFF rollover)
|
||||||
|
uint32_t thour = rtime / 3600;
|
||||||
|
uint32_t tmin = (rtime % 3600) / 60;
|
||||||
|
uint32_t tsec = (rtime % 3600) % 60;
|
||||||
|
|
||||||
|
float lat_dec = 0.0f;
|
||||||
|
float lon_dec = 0.0f;
|
||||||
|
|
||||||
|
char deg_glyph[4];
|
||||||
|
sprintf (deg_glyph, "%s", "°");
|
||||||
|
|
||||||
|
//This appears to be similar to the NMEA GPGGA format (DDmm.mm) but
|
||||||
|
//octets are ordered in least significant to most significant value
|
||||||
|
lat_mmin = (uint16_t)ConvertBitIntoBytes(&input[40], 16); //? bits required, but grabbing two octets
|
||||||
|
lat_min = (uint8_t)ConvertBitIntoBytes(&input[58], 6); //6 bits required to get 60
|
||||||
|
lat_deg = (uint8_t)ConvertBitIntoBytes(&input[65], 7); //7 bits required to get 90
|
||||||
|
lat_sign = input[72]; //64
|
||||||
|
|
||||||
|
lon_mmin = (uint16_t)ConvertBitIntoBytes(&input[72], 16); //? bits required, but grabbing two octets
|
||||||
|
lon_min = (uint8_t)ConvertBitIntoBytes(&input[90], 6); //6 bits required to get 60
|
||||||
|
lon_deg = (uint8_t)ConvertBitIntoBytes(&input[96], 8); //8 bits required to get 180
|
||||||
|
lon_sign = input[88]; //88, unsure of a correct location, but on the sample with 0 minutes, this lonely bit was flagged on
|
||||||
|
|
||||||
|
int src = 0;
|
||||||
|
if (slot == 0) src = state->lastsrc;
|
||||||
|
if (slot == 1) src = state->lastsrcR;
|
||||||
|
|
||||||
|
//calculate decimal representation (was a pain to figure out the sub minute values)
|
||||||
|
lat_dec = ( (float)lat_deg + ((float)lat_min/60.0f) + ((float)lat_mmin/600000.0f) );
|
||||||
|
lon_dec = ( (float)lon_deg + ((float)lon_min/60.0f) + ((float)lon_mmin/600000.0f) );
|
||||||
|
|
||||||
|
if (lat_sign)
|
||||||
|
lat_dec *= -1.0f;
|
||||||
|
|
||||||
|
if (lon_sign)
|
||||||
|
lon_dec *= -1.0f;
|
||||||
|
|
||||||
|
//line break
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
fprintf (stderr, " GPS: %f%s, %f%s;", lat_dec, deg_glyph, lon_dec, deg_glyph);
|
||||||
|
|
||||||
|
//gps fix time
|
||||||
|
// fprintf (stderr, " RT: %05X;", rtime); //debug
|
||||||
|
fprintf (stderr, " LTS: %02d:%02d:%02d UTC;", thour, tmin, tsec); //last time synced to GPS
|
||||||
|
|
||||||
|
//speed and direction
|
||||||
|
// fprintf (stderr, " RSPD: %04X;", rspeed); //debug
|
||||||
|
// fprintf (stderr, " RANG: %02X;", rangle); //debug
|
||||||
|
// fprintf (stderr, " MPS: %06.02f;", fspeed);
|
||||||
|
// fprintf (stderr, " KPH: %06.02f;", fspeed * 3.6f);
|
||||||
|
// fprintf (stderr, " MPH: %06.02f;", fspeed * 2.2369f);
|
||||||
|
fprintf (stderr, " DIR: %03d%s;", a, deg_glyph);
|
||||||
|
|
||||||
|
// fprintf (stderr, " FIX: %d", fix);
|
||||||
|
// fprintf (stderr, " QLT: %02d", quality);
|
||||||
|
|
||||||
|
// fprintf (stderr, " SRC: %08d;", src);
|
||||||
|
|
||||||
|
//save to array for ncurses
|
||||||
|
sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", lat_dec, deg_glyph, lon_dec, deg_glyph);
|
||||||
|
|
||||||
|
//save to LRRP report for mapping/logging
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
if (opts->lrrp_file_output == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
char * datestr = getDate();
|
||||||
|
char * timestr = getTime();
|
||||||
|
|
||||||
|
//open file by name that is supplied in the ncurses terminal, or cli
|
||||||
|
pFile = fopen (opts->lrrp_out_file, "a");
|
||||||
|
fprintf (pFile, "%s\t", datestr );
|
||||||
|
fprintf (pFile, "%s\t", timestr );
|
||||||
|
fprintf (pFile, "%08d\t", src);
|
||||||
|
fprintf (pFile, "%.6lf\t", lat_dec);
|
||||||
|
fprintf (pFile, "%.6lf\t", lon_dec);
|
||||||
|
fprintf (pFile, "%d\t ", s);
|
||||||
|
fprintf (pFile, "%d\t ", a);
|
||||||
|
fprintf (pFile, "\n");
|
||||||
|
fclose (pFile);
|
||||||
|
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
if (datestr != NULL)
|
||||||
|
{
|
||||||
|
free (datestr);
|
||||||
|
datestr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//NOTE: Thanks to DSheirer (SDRTrunk) for helping me work out a few of the things in here
|
||||||
|
//not entirely convinced on some of these calcs (speed, angle, and TS) but sure these bits
|
||||||
|
//are actual speed/direction/timestamps judging from what the coordinates show on a map
|
||||||
|
}
|
||||||
|
|
||||||
|
//externalize embedded GPS - Confirmed working now on NE, NW, SE, and SW coordinates
|
||||||
|
void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
||||||
|
{
|
||||||
|
UNUSED(opts);
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " Embedded GPS:");
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
uint8_t pf = lc_bits[0];
|
||||||
|
uint8_t res_a = lc_bits[1];
|
||||||
|
uint8_t res_b = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 4);
|
||||||
|
uint8_t pos_err = (uint8_t)ConvertBitIntoBytes(&lc_bits[20], 3);
|
||||||
|
UNUSED2(res_a, res_b);
|
||||||
|
|
||||||
|
char deg_glyph[4];
|
||||||
|
sprintf (deg_glyph, "%s", "°");
|
||||||
|
|
||||||
|
uint32_t lon_sign = lc_bits[23];
|
||||||
|
uint32_t lon = (uint32_t)ConvertBitIntoBytes(&lc_bits[24], 24);
|
||||||
|
uint32_t lat_sign = lc_bits[48];
|
||||||
|
uint32_t lat = (uint32_t)ConvertBitIntoBytes(&lc_bits[49], 23);
|
||||||
|
double lon_sf = 1.0f; //float value we can multiple longitude with
|
||||||
|
double lat_sf = 1.0f; //float value we can multiple latitude with
|
||||||
|
|
||||||
|
double lat_unit = (double)180/ pow (2.0, 24); //180 divided by 2^24
|
||||||
|
double lon_unit = (double)360/ pow (2.0, 25); //360 divided by 2^25
|
||||||
|
|
||||||
|
char latstr[3];
|
||||||
|
char lonstr[3];
|
||||||
|
sprintf (latstr, "%s", "N");
|
||||||
|
sprintf (lonstr, "%s", "E");
|
||||||
|
|
||||||
|
//run calculations and print
|
||||||
|
//7.2.16 and 7.2.17 (two's compliment)
|
||||||
|
|
||||||
|
double latitude = 0;
|
||||||
|
double longitude = 0;
|
||||||
|
|
||||||
|
if (pf) fprintf (stderr, " Protected");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (lat_sign)
|
||||||
|
{
|
||||||
|
lat = 0x800001 - lat;
|
||||||
|
sprintf (latstr, "%s", "S");
|
||||||
|
lat_sf = -1.0f;
|
||||||
|
}
|
||||||
|
latitude = ((double)lat * lat_unit);
|
||||||
|
|
||||||
|
if (lon_sign)
|
||||||
|
{
|
||||||
|
lon = 0x1000001 - lon;
|
||||||
|
sprintf (lonstr, "%s", "W");
|
||||||
|
lon_sf = -1.0f;
|
||||||
|
}
|
||||||
|
longitude = ((double)lon * lon_unit);
|
||||||
|
|
||||||
|
//sanity check
|
||||||
|
if (abs (latitude) < 90 && abs(longitude) < 180)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf)", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, lat_sf * latitude, lon_sf * longitude);
|
||||||
|
|
||||||
|
//7.2.15 Position Error
|
||||||
|
uint16_t position_error = 2 * pow(10, pos_err); //2 * 10^pos_err
|
||||||
|
if (pos_err == 0x7 ) fprintf (stderr, "\n Position Error: Unknown or Invalid");
|
||||||
|
else fprintf (stderr, "\n Position Error: Less than %dm", position_error);
|
||||||
|
|
||||||
|
//save to array for ncurses
|
||||||
|
if (pos_err != 0x7)
|
||||||
|
{
|
||||||
|
sprintf (state->dmr_embedded_gps[slot], "GPS: %.5lf%s%s %.5lf%s%s Err: %dm", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, position_error);
|
||||||
|
}
|
||||||
|
else sprintf (state->dmr_embedded_gps[slot], "GPS: %.5lf%s%s %.5lf%s%s Unknown Pos Err", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr);
|
||||||
|
|
||||||
|
//save to LRRP report for mapping/logging
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
if (opts->lrrp_file_output == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
char * datestr = getDate();
|
||||||
|
char * timestr = getTime();
|
||||||
|
|
||||||
|
int src = 0;
|
||||||
|
if (slot == 0) src = state->lasttg;
|
||||||
|
if (slot == 1) src = state->lasttgR;
|
||||||
|
|
||||||
|
//open file by name that is supplied in the ncurses terminal, or cli
|
||||||
|
pFile = fopen (opts->lrrp_out_file, "a");
|
||||||
|
fprintf (pFile, "%s\t", datestr );
|
||||||
|
fprintf (pFile, "%s\t", timestr );
|
||||||
|
fprintf (pFile, "%08d\t", src);
|
||||||
|
fprintf (pFile, "%.5lf\t", latitude);
|
||||||
|
fprintf (pFile, "%.5lf\t", longitude);
|
||||||
|
fprintf (pFile, "0\t " ); //zero for velocity
|
||||||
|
fprintf (pFile, "0\t " ); //zero for azimuth
|
||||||
|
fprintf (pFile, "\n");
|
||||||
|
fclose (pFile);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
//This Function has been redone and variables fixed to mirror SDRTrunk
|
||||||
|
//my earlier assumption that this was the same format as DMR Embedded GPS was incorrect
|
||||||
|
void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
||||||
|
{
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
fprintf (stderr, " GPS:");
|
||||||
|
uint8_t slot = state->currentslot;
|
||||||
|
uint8_t pf = lc_bits[0];
|
||||||
|
uint8_t res_a = lc_bits[1];
|
||||||
|
uint8_t res_b = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 7);
|
||||||
|
uint8_t expired = lc_bits[23]; //this bit seems to indicate that the GPS coordinates are out of date or fresh
|
||||||
|
|
||||||
|
char deg_glyph[4];
|
||||||
|
sprintf (deg_glyph, "%s", "°");
|
||||||
|
|
||||||
|
uint32_t lon_sign = lc_bits[48];
|
||||||
|
uint32_t lon = (uint32_t)ConvertBitIntoBytes(&lc_bits[49], 23);
|
||||||
|
uint32_t lat_sign = lc_bits[24];
|
||||||
|
uint32_t lat = (uint32_t)ConvertBitIntoBytes(&lc_bits[25], 23);
|
||||||
|
//todo: acquire more samples for additional validation
|
||||||
|
double lat_unit = 90.0f / 0x7FFFFF;
|
||||||
|
double lon_unit = 180.0f / 0x7FFFFF;
|
||||||
|
double lon_sf = 1.0f; //float value we can multiple longitude with
|
||||||
|
double lat_sf = 1.0f; //float value we can multiple latitude with
|
||||||
|
|
||||||
|
char latstr[3];
|
||||||
|
char lonstr[3];
|
||||||
|
char valid[9];
|
||||||
|
sprintf (latstr, "%s", "N");
|
||||||
|
sprintf (lonstr, "%s", "E");
|
||||||
|
sprintf (valid, "%s", "Current");
|
||||||
|
|
||||||
|
double latitude = 0;
|
||||||
|
double longitude = 0;
|
||||||
|
|
||||||
|
if (pf) fprintf (stderr, " Protected");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (lat_sign)
|
||||||
|
{
|
||||||
|
sprintf (latstr, "%s", "S");
|
||||||
|
lat_sf = -1.0f;
|
||||||
|
}
|
||||||
|
latitude = ((double)lat * lat_unit);
|
||||||
|
|
||||||
|
if (lon_sign)
|
||||||
|
{
|
||||||
|
sprintf (lonstr, "%s", "W");
|
||||||
|
lon_sf = -1.0f;
|
||||||
|
}
|
||||||
|
longitude = ((double)lon * lon_unit);
|
||||||
|
|
||||||
|
//sanity check
|
||||||
|
if (abs (latitude) < 90 && abs(longitude) < 180)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf) ", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, latitude * lat_sf, longitude * lon_sf);
|
||||||
|
|
||||||
|
if (expired)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Expired; ");
|
||||||
|
sprintf (valid, "%s", "Expired");
|
||||||
|
}
|
||||||
|
else if (!expired)
|
||||||
|
fprintf (stderr, "Current; ");
|
||||||
|
|
||||||
|
if (res_a)
|
||||||
|
fprintf (stderr, "RES_A: %d; ", res_a);
|
||||||
|
|
||||||
|
if (res_b)
|
||||||
|
fprintf (stderr, "RES_B: %02X; ", res_b);
|
||||||
|
|
||||||
|
//save to array for ncurses
|
||||||
|
sprintf (state->dmr_embedded_gps[slot], "GPS: %lf%s%s %lf%s%s %s", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, valid);
|
||||||
|
|
||||||
|
//save to LRRP report for mapping/logging
|
||||||
|
FILE * pFile; //file pointer
|
||||||
|
if (opts->lrrp_file_output == 1)
|
||||||
|
{
|
||||||
|
int src = 0;
|
||||||
|
if (slot == 0) src = state->lastsrc;
|
||||||
|
if (slot == 1) src = state->lastsrcR;
|
||||||
|
|
||||||
|
char * datestr = getDate();
|
||||||
|
char * timestr = getTime();
|
||||||
|
|
||||||
|
//open file by name that is supplied in the ncurses terminal, or cli
|
||||||
|
pFile = fopen (opts->lrrp_out_file, "a");
|
||||||
|
fprintf (pFile, "%s\t", datestr );
|
||||||
|
fprintf (pFile, "%s\t", timestr );
|
||||||
|
fprintf (pFile, "%08d\t", src);
|
||||||
|
fprintf (pFile, "%.5lf\t", latitude);
|
||||||
|
fprintf (pFile, "%.5lf\t", longitude);
|
||||||
|
fprintf (pFile, "0\t " ); //zero for velocity
|
||||||
|
fprintf (pFile, "0\t " ); //zero for azimuth
|
||||||
|
fprintf (pFile, "\n");
|
||||||
|
fclose (pFile);
|
||||||
|
|
||||||
|
if (timestr != NULL)
|
||||||
|
{
|
||||||
|
free (timestr);
|
||||||
|
timestr = NULL;
|
||||||
|
}
|
||||||
|
if (datestr != NULL)
|
||||||
|
{
|
||||||
|
free (datestr);
|
||||||
|
datestr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
112
src/dsd_import.c
@@ -18,6 +18,7 @@ int csvGroupImport(dsd_opts * opts, dsd_state * state)
|
|||||||
int row_count = 0;
|
int row_count = 0;
|
||||||
int field_count = 0;
|
int field_count = 0;
|
||||||
long int group_number = 0; //local group number for array index value
|
long int group_number = 0; //local group number for array index value
|
||||||
|
UNUSED(group_number);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (fgets(buffer, BSIZE, fp)) {
|
while (fgets(buffer, BSIZE, fp)) {
|
||||||
field_count = 0;
|
field_count = 0;
|
||||||
@@ -145,3 +146,114 @@ int csvChanImport(dsd_opts * opts, dsd_state * state) //channel map import
|
|||||||
fclose(fp);
|
fclose(fp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Decimal Variant of Key Import
|
||||||
|
int csvKeyImportDec(dsd_opts * opts, dsd_state * state) //multi-key support
|
||||||
|
{
|
||||||
|
char filename[1024] = "filename.csv";
|
||||||
|
sprintf (filename, "%s", opts->key_in_file);
|
||||||
|
|
||||||
|
char buffer[BSIZE];
|
||||||
|
FILE * fp;
|
||||||
|
fp = fopen(filename, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
printf("Unable to open file '%s'\n", filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int row_count = 0;
|
||||||
|
int field_count = 0;
|
||||||
|
|
||||||
|
unsigned long long int keynumber = 0;
|
||||||
|
unsigned long long int keyvalue = 0;
|
||||||
|
|
||||||
|
uint16_t hash = 0;
|
||||||
|
uint8_t hash_bits[24];
|
||||||
|
memset (hash_bits, 0, sizeof(hash_bits));
|
||||||
|
|
||||||
|
while (fgets(buffer, BSIZE, fp)) {
|
||||||
|
field_count = 0;
|
||||||
|
row_count++;
|
||||||
|
if (row_count == 1)
|
||||||
|
continue; //don't want labels
|
||||||
|
char * field = strtok(buffer, ","); //seperate by comma
|
||||||
|
while (field) {
|
||||||
|
|
||||||
|
if (field_count == 0)
|
||||||
|
{
|
||||||
|
sscanf (field, "%lld", &keynumber);
|
||||||
|
if (keynumber > 0xFFFF) //if larger than 16-bits, get its hash instead
|
||||||
|
{
|
||||||
|
keynumber = keynumber & 0xFFFFFF; //truncate to 24-bits (max allowed)
|
||||||
|
for (int i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
hash_bits[i] = ((keynumber << i) & 0x800000) >> 23; //load into array for CRC16
|
||||||
|
}
|
||||||
|
hash = ComputeCrcCCITT16d (hash_bits, 24);
|
||||||
|
keynumber = hash & 0xFFFF; //make sure its no larger than 16-bits
|
||||||
|
fprintf (stderr, "Hashed ");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field_count == 1)
|
||||||
|
{
|
||||||
|
sscanf (field, "%lld", &keyvalue);
|
||||||
|
state->rkey_array[keynumber] = keyvalue & 0xFFFFFFFFFF; //doesn't exceed 40-bit value
|
||||||
|
}
|
||||||
|
|
||||||
|
field = strtok(NULL, ",");
|
||||||
|
field_count++;
|
||||||
|
}
|
||||||
|
fprintf (stderr, "Key [%03lld] [%05lld]", keynumber, state->rkey_array[keynumber]);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
hash = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Hex Variant of Key Import
|
||||||
|
int csvKeyImportHex(dsd_opts * opts, dsd_state * state) //key import for rc4 keys
|
||||||
|
{
|
||||||
|
char filename[1024] = "filename.csv";
|
||||||
|
sprintf (filename, "%s", opts->key_in_file);
|
||||||
|
char buffer[BSIZE];
|
||||||
|
FILE * fp;
|
||||||
|
fp = fopen(filename, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
printf("Unable to open file '%s'\n", filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int row_count = 0;
|
||||||
|
int field_count = 0;
|
||||||
|
unsigned long long int keynumber;
|
||||||
|
|
||||||
|
while (fgets(buffer, BSIZE, fp)) {
|
||||||
|
field_count = 0;
|
||||||
|
row_count++;
|
||||||
|
if (row_count == 1)
|
||||||
|
continue; //don't want labels
|
||||||
|
char * field = strtok(buffer, ","); //seperate by comma
|
||||||
|
while (field) {
|
||||||
|
|
||||||
|
if (field_count == 0)
|
||||||
|
{
|
||||||
|
sscanf (field, "%llX", &keynumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field_count == 1)
|
||||||
|
{
|
||||||
|
sscanf (field, "%llX", &state->rkey_array[keynumber]);
|
||||||
|
}
|
||||||
|
|
||||||
|
field = strtok(NULL, ",");
|
||||||
|
field_count++;
|
||||||
|
}
|
||||||
|
fprintf (stderr, "Key [%04llX] [%016llX]", keynumber, state->rkey_array[keynumber]);
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
7
src/dsd_ks.c
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dsd_ks.c
|
||||||
|
* Placeholder File 6 -- This file is for temporary code storage
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
2876
src/dsd_main.c
1276
src/dsd_mbe.c
177
src/dsd_mbe2.c
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* TODO: Fill me in'
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2023-07 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
//new and simplified/organized ambe and imbe handling
|
||||||
|
//moving all audio handling and decryption to seperate files for simplicity (eventually)
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
//the initial functions will ONLY return demodulated ambe or imbe frames, THAT'S IT!
|
||||||
|
//decryption and audio handling etc will be handled at a different area
|
||||||
|
|
||||||
|
//using soft_demod will also allow for not compiling mbelib and also using a DVstick in the future
|
||||||
|
|
||||||
|
//P25p1 IMBE 7200 or AMBE+2 EFR
|
||||||
|
void soft_demod_imbe7200 (dsd_state * state, char imbe_fr7200[8][23], char imbe_d[88])
|
||||||
|
{
|
||||||
|
state->errs = mbe_eccImbe7200x4400C0 (imbe_fr7200);
|
||||||
|
state->errs2 = state->errs;
|
||||||
|
mbe_demodulateImbe7200x4400Data (imbe_fr7200);
|
||||||
|
state->errs2 += mbe_eccImbe7200x4400Data (imbe_fr7200, imbe_d);
|
||||||
|
state->debug_audio_errors += state->errs2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//ProVoice IMBE 7100
|
||||||
|
void soft_demod_imbe7100 (dsd_state * state, char imbe_fr7100[7][24], char imbe_d[88])
|
||||||
|
{
|
||||||
|
state->errs = mbe_eccImbe7100x4400C0 (imbe_fr7100);
|
||||||
|
state->errs2 = state->errs;
|
||||||
|
mbe_demodulateImbe7100x4400Data (imbe_fr7100);
|
||||||
|
state->errs2 += mbe_eccImbe7100x4400Data (imbe_fr7100, imbe_d);
|
||||||
|
state->debug_audio_errors += state->errs2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//AMBE+2 EHR
|
||||||
|
void soft_demod_ambe2_ehr(dsd_state * state, char ambe2_ehr[4][24], char ambe_d[49])
|
||||||
|
{
|
||||||
|
state->errs = mbe_eccAmbe3600x2450C0 (ambe2_ehr);
|
||||||
|
state->errs2 = state->errs;
|
||||||
|
mbe_demodulateAmbe3600x2450Data (ambe2_ehr);
|
||||||
|
state->errs2 += mbe_eccAmbe3600x2450Data (ambe2_ehr, ambe_d);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//AMBE One Shot (DSTAR)
|
||||||
|
void soft_demod_ambe_dstar(dsd_opts * opts, dsd_state * state, char ambe_fr[4][24], char ambe_d[49])
|
||||||
|
{
|
||||||
|
mbe_processAmbe3600x2400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2,
|
||||||
|
state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||||
|
if (opts->floating_point == 1)
|
||||||
|
memcpy (state->f_l, state->audio_out_temp_buf, sizeof(state->f_l));
|
||||||
|
else processAudio(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
//AMBE+2 One Shot (X2-TDMA)
|
||||||
|
void soft_demod_ambe_x2(dsd_opts * opts, dsd_state * state, char ambe_fr[4][24], char ambe_d[49])
|
||||||
|
{
|
||||||
|
mbe_processAmbe3600x2450Framef (state->audio_out_temp_buf, &state->errs, &state->errs2,
|
||||||
|
state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||||
|
if (opts->floating_point == 1)
|
||||||
|
memcpy (state->f_l, state->audio_out_temp_buf, sizeof(state->f_l));
|
||||||
|
else processAudio(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void soft_mbe (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char ambe_d[49];
|
||||||
|
char imbe_d[88];
|
||||||
|
int slot = state->currentslot;
|
||||||
|
memset (ambe_d, 0, sizeof(ambe_d));
|
||||||
|
memset (imbe_d, 0, sizeof(imbe_d));
|
||||||
|
|
||||||
|
UNUSED(i);
|
||||||
|
|
||||||
|
//P25p1, YSF FR, NXDN EFR
|
||||||
|
if (state->synctype == 0 || state->synctype == 1)
|
||||||
|
{
|
||||||
|
soft_demod_imbe7200(state, imbe_fr, imbe_d);
|
||||||
|
//handle decryption here
|
||||||
|
//print IMBE frame
|
||||||
|
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);
|
||||||
|
//send to playback here
|
||||||
|
}
|
||||||
|
|
||||||
|
//ProVoice
|
||||||
|
else if (state->synctype == 14 || state->synctype == 15)
|
||||||
|
{
|
||||||
|
soft_demod_imbe7100(state, imbe7100_fr, imbe_d);
|
||||||
|
//nothing to do
|
||||||
|
|
||||||
|
//convert to 7200
|
||||||
|
mbe_convertImbe7100to7200(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);
|
||||||
|
//send to playback here
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//D-STAR AMBE
|
||||||
|
else if (state->synctype == 6 || state->synctype == 7)
|
||||||
|
{
|
||||||
|
soft_demod_ambe_dstar(opts, state, ambe_fr, ambe_d);
|
||||||
|
if(opts->payload == 1)
|
||||||
|
PrintAMBEData (opts, state, ambe_d);
|
||||||
|
|
||||||
|
if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1)
|
||||||
|
playSynthesizedVoiceMS(opts, state);
|
||||||
|
if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1)
|
||||||
|
playSynthesizedVoiceFM(opts, state);
|
||||||
|
if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2)
|
||||||
|
playSynthesizedVoiceSS(opts, state);
|
||||||
|
if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 2)
|
||||||
|
playSynthesizedVoiceFS(opts, state);
|
||||||
|
|
||||||
|
if (opts->wav_out_f != NULL)
|
||||||
|
writeSynthesizedVoice (opts, state);
|
||||||
|
|
||||||
|
if (opts->mbe_out_f != NULL)
|
||||||
|
saveAmbe2450Data (opts, state, ambe_d);
|
||||||
|
}
|
||||||
|
|
||||||
|
//X2-TDMA AMBE
|
||||||
|
else if (state->synctype >= 2 && state->synctype <= 5)
|
||||||
|
{
|
||||||
|
soft_demod_ambe_x2(opts, state, ambe_fr, ambe_d);
|
||||||
|
if(opts->payload == 1)
|
||||||
|
PrintAMBEData (opts, state, ambe_d);
|
||||||
|
|
||||||
|
if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 1)
|
||||||
|
playSynthesizedVoiceMS(opts, state);
|
||||||
|
if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 1)
|
||||||
|
playSynthesizedVoiceFM(opts, state);
|
||||||
|
if (opts->floating_point == 0 && opts->pulse_digi_out_channels == 2)
|
||||||
|
playSynthesizedVoiceSS(opts, state);
|
||||||
|
if (opts->floating_point == 1 && opts->pulse_digi_out_channels == 2)
|
||||||
|
playSynthesizedVoiceFS(opts, state);
|
||||||
|
|
||||||
|
if (opts->wav_out_f != NULL)
|
||||||
|
writeSynthesizedVoice (opts, state);
|
||||||
|
|
||||||
|
if (opts->mbe_out_f != NULL)
|
||||||
|
saveAmbe2450Data (opts, state, ambe_d);
|
||||||
|
}
|
||||||
|
|
||||||
|
//AMBE+2 EHR (NXDN, DMR, P25p2, YSF VD/1)
|
||||||
|
else
|
||||||
|
{
|
||||||
|
soft_demod_ambe2_ehr (state, ambe_fr, ambe_d);
|
||||||
|
//decrypt here
|
||||||
|
if(opts->payload == 1)
|
||||||
|
PrintAMBEData (opts, state, ambe_d);
|
||||||
|
//make left or right channel decision
|
||||||
|
if (slot == 0)
|
||||||
|
{
|
||||||
|
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
|
||||||
|
ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||||
|
}
|
||||||
|
if (slot == 1)
|
||||||
|
{
|
||||||
|
mbe_processAmbe2450Dataf (state->audio_out_temp_bufR, &state->errsR, &state->errs2R, state->err_strR,
|
||||||
|
ambe_d, state->cur_mp2, state->prev_mp2, state->prev_mp_enhanced2, opts->uvquality);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
3679
src/dsd_ncurses.c
166
src/dsd_reset.c
@@ -2,26 +2,59 @@
|
|||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
#include "p25p1_heuristics.h"
|
#include "p25p1_heuristics.h"
|
||||||
|
|
||||||
void
|
//fixed the memory leak, but now random segfaults occur -- double free or corruption (out) or (!prev)
|
||||||
resetState (dsd_state * state)
|
void resetState (dsd_state * state)
|
||||||
{
|
{
|
||||||
|
|
||||||
int i, j;
|
int i;
|
||||||
|
|
||||||
|
//Dibit Buffer -- Free Allocated Memory
|
||||||
|
// free (state->dibit_buf);
|
||||||
|
|
||||||
|
//Dibit Buffer -- Memset/Init/Allocate Memory
|
||||||
// state->dibit_buf = malloc (sizeof (int) * 1000000);
|
// state->dibit_buf = malloc (sizeof (int) * 1000000);
|
||||||
// state->dibit_buf_p = state->dibit_buf + 200;
|
|
||||||
// memset (state->dibit_buf, 0, sizeof (int) * 200);
|
state->dibit_buf_p = state->dibit_buf + 200;
|
||||||
state->repeat = 0; //not sure yet
|
memset (state->dibit_buf, 0, sizeof (int) * 200);
|
||||||
//state->audio_out_buf = malloc (sizeof (short) * 1000000);
|
state->repeat = 0; //repeat frame?
|
||||||
//memset (state->audio_out_buf, 0, 100 * sizeof (short));
|
|
||||||
//state->audio_out_buf_p = state->audio_out_buf + 100;
|
//Audio Buffer -- Free Allocated Memory
|
||||||
//state->audio_out_float_buf = malloc (sizeof (float) * 1000000);
|
free (state->audio_out_buf);
|
||||||
//memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
|
free (state->audio_out_float_buf);
|
||||||
//state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
|
free (state->audio_out_bufR);
|
||||||
//state->audio_out_idx = 0;
|
free (state->audio_out_float_bufR);
|
||||||
//state->audio_out_idx2 = 0;
|
|
||||||
//state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
//Audio Buffer -- Memset/Init/Allocate Memory per slot
|
||||||
//state->wav_out_bytes = 0;
|
|
||||||
|
//slot 1
|
||||||
|
state->audio_out_float_buf = malloc (sizeof (float) * 1000000);
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
//slot 2
|
||||||
|
state->audio_out_bufR = malloc (sizeof (short) * 1000000);
|
||||||
|
state->audio_out_float_bufR = malloc (sizeof (float) * 1000000);
|
||||||
|
|
||||||
|
memset (state->audio_out_bufR, 0, 100 * sizeof (short));
|
||||||
|
state->audio_out_buf_pR = state->audio_out_bufR + 100;
|
||||||
|
|
||||||
|
memset (state->audio_out_float_bufR, 0, 100 * sizeof (float));
|
||||||
|
state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100;
|
||||||
|
|
||||||
|
state->audio_out_idxR = 0;
|
||||||
|
state->audio_out_idx2R = 0;
|
||||||
|
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
|
||||||
|
|
||||||
|
//Sync
|
||||||
state->center = 0;
|
state->center = 0;
|
||||||
state->jitter = -1;
|
state->jitter = -1;
|
||||||
state->synctype = -1;
|
state->synctype = -1;
|
||||||
@@ -31,76 +64,73 @@ resetState (dsd_state * state)
|
|||||||
state->umid = 0;
|
state->umid = 0;
|
||||||
state->minref = -12000;
|
state->minref = -12000;
|
||||||
state->maxref = 12000;
|
state->maxref = 12000;
|
||||||
|
|
||||||
state->lastsample = 0;
|
state->lastsample = 0;
|
||||||
for (i = 0; i < 128; i++)
|
for (i = 0; i < 128; i++)
|
||||||
{
|
{
|
||||||
state->sbuf[i] = 0;
|
state->sbuf[i] = 0;
|
||||||
}
|
}
|
||||||
state->sidx = 0;
|
state->sidx = 0;
|
||||||
for (i = 0; i < 1024; i++)
|
for (i = 0; i < 1024; i++)
|
||||||
{
|
{
|
||||||
state->maxbuf[i] = 15000;
|
state->maxbuf[i] = 15000;
|
||||||
}
|
}
|
||||||
for (i = 0; i < 1024; i++)
|
for (i = 0; i < 1024; i++)
|
||||||
{
|
{
|
||||||
state->minbuf[i] = -15000;
|
state->minbuf[i] = -15000;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->midx = 0;
|
state->midx = 0;
|
||||||
state->err_str[0] = 0;
|
|
||||||
sprintf (state->fsubtype, " ");
|
|
||||||
sprintf (state->ftype, " ");
|
|
||||||
state->symbolcnt = 0;
|
state->symbolcnt = 0;
|
||||||
state->rf_mod = 0;
|
|
||||||
state->numflips = 0;
|
state->numflips = 0;
|
||||||
state->lastsynctype = -1;
|
state->lastsynctype = -1;
|
||||||
state->lastp25type = 0;
|
state->lastp25type = 0;
|
||||||
state->offset = 0;
|
state->offset = 0;
|
||||||
state->carrier = 0;
|
state->carrier = 0;
|
||||||
for (i = 0; i < 25; i++)
|
|
||||||
{
|
//Reset Voice Errors in C0 and C1 (or remaining Codewords in IMBE)
|
||||||
for (j = 0; j < 16; j++)
|
|
||||||
{
|
|
||||||
state->tg[i][j] = 48;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state->tgcount = 0;
|
|
||||||
state->lasttg = 0;
|
|
||||||
state->lastsrc = 0;
|
|
||||||
state->nac = 0;
|
|
||||||
state->errs = 0;
|
state->errs = 0;
|
||||||
state->errs2 = 0;
|
state->errs2 = 0;
|
||||||
state->mbe_file_type = -1;
|
state->errsR = 0;
|
||||||
|
state->errs2R = 0;
|
||||||
|
|
||||||
|
//Misc -- may not be needed
|
||||||
state->optind = 0;
|
state->optind = 0;
|
||||||
state->numtdulc = 0;
|
state->numtdulc = 0;
|
||||||
state->firstframe = 0;
|
state->firstframe = 0;
|
||||||
sprintf (state->slot0light, " slot0 ");
|
|
||||||
sprintf (state->slot1light, " slot1 ");
|
|
||||||
state->aout_gain = 25;
|
|
||||||
memset (state->aout_max_buf, 0, sizeof (float) * 200);
|
|
||||||
state->aout_max_buf_p = state->aout_max_buf;
|
|
||||||
state->aout_max_buf_idx = 0;
|
|
||||||
state->samplesPerSymbol = 10;
|
|
||||||
state->symbolCenter = 4;
|
|
||||||
sprintf (state->algid, "________");
|
|
||||||
sprintf (state->keyid, "________________");
|
|
||||||
state->currentslot = 0;
|
|
||||||
state->cur_mp = malloc (sizeof (mbe_parms));
|
|
||||||
state->prev_mp = malloc (sizeof (mbe_parms));
|
|
||||||
state->prev_mp_enhanced = malloc (sizeof (mbe_parms));
|
|
||||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
|
||||||
state->p25kid = 0;
|
|
||||||
|
|
||||||
state->debug_audio_errors = 0;
|
//unsure if these are still used or ever were used,
|
||||||
state->debug_header_errors = 0;
|
// memset (state->aout_max_buf, 0, sizeof (float) * 200);
|
||||||
state->debug_header_critical_errors = 0;
|
// state->aout_max_buf_p = state->aout_max_buf;
|
||||||
|
// state->aout_max_buf_idx = 0;
|
||||||
|
|
||||||
state->nxdn_last_ran = 0;
|
// //MBE Specific
|
||||||
// state->payload_algid = 0;
|
// //free the memory before allocating it again -- may not use this
|
||||||
// state->payload_algidR = 0;
|
// free (state->cur_mp);
|
||||||
state->dmr_encL = 0;
|
// free (state->prev_mp);
|
||||||
state->dmr_encR = 0;
|
// free (state->prev_mp_enhanced);
|
||||||
|
|
||||||
//each time you run this, it increses memory use by 4MB, massive memory leak
|
// //memory allocation and init on mbe -- may not use this
|
||||||
//need to revisit this sometime and look into only resetting only the necesary items to let P25 switch between signals (C4FM or Wide) without needing a restart
|
// state->cur_mp = malloc (sizeof (mbe_parms));
|
||||||
initialize_p25_heuristics(&state->p25_heuristics); //see if we want to re-init this or not, currently seems to cause memory leak when running over and over, mitigated with a reset flag
|
// 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);
|
||||||
|
|
||||||
|
//rest the heurestics, we want to do this on each tune, each RF frequency can deviate quite a bit in strength
|
||||||
|
initialize_p25_heuristics(&state->p25_heuristics);
|
||||||
|
initialize_p25_heuristics(&state->inv_p25_heuristics);
|
||||||
|
}
|
||||||
|
|
||||||
|
//simple function to reset the dibit buffer
|
||||||
|
void reset_dibit_buffer(dsd_state * state)
|
||||||
|
{
|
||||||
|
//Dibit Buffer -- Free Allocated Memory
|
||||||
|
// free (state->dibit_buf);
|
||||||
|
|
||||||
|
//Dibit Buffer -- Memset/Init/Allocate Memory
|
||||||
|
// state->dibit_buf = malloc (sizeof (int) * 1000000);
|
||||||
|
|
||||||
|
state->dibit_buf_p = state->dibit_buf + 200;
|
||||||
|
memset (state->dibit_buf, 0, sizeof (int) * 200);
|
||||||
}
|
}
|
||||||
|
|||||||
285
src/dsd_rigctl.c
@@ -30,12 +30,16 @@ void error(char *msg) {
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in address;
|
||||||
|
struct sockaddr_in addressA;
|
||||||
|
struct sockaddr_in addressM17;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Connect
|
// Connect
|
||||||
//
|
//
|
||||||
int Connect (char *hostname, int portno)
|
int Connect (char *hostname, int portno)
|
||||||
{
|
{
|
||||||
int sockfd, n;
|
int sockfd;
|
||||||
struct sockaddr_in serveraddr;
|
struct sockaddr_in serveraddr;
|
||||||
struct hostent *server;
|
struct hostent *server;
|
||||||
|
|
||||||
@@ -131,7 +135,7 @@ bool SetFreq(int sockfd, long int freq)
|
|||||||
Send(sockfd, buf);
|
Send(sockfd, buf);
|
||||||
Recv(sockfd, buf);
|
Recv(sockfd, buf);
|
||||||
|
|
||||||
if (strcmp(buf, "RPRT 1") == 0 )
|
if (strcmp(buf, "RPRT 1\n") == 0 ) //sdr++ has a linebreak here, is that in all versions of the protocol?
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -141,11 +145,19 @@ bool SetModulation(int sockfd, int bandwidth)
|
|||||||
{
|
{
|
||||||
char buf[BUFSIZE];
|
char buf[BUFSIZE];
|
||||||
//the bandwidth is now a user/system based configurable variable
|
//the bandwidth is now a user/system based configurable variable
|
||||||
sprintf (buf, "M FM %d\n", bandwidth);
|
sprintf (buf, "M NFM %d\n", bandwidth); //SDR++ has changed the token from FM to NFM, even if Ryzerth fixes it later, users may still have an older version
|
||||||
Send(sockfd, buf);
|
Send(sockfd, buf);
|
||||||
Recv(sockfd, buf);
|
Recv(sockfd, buf);
|
||||||
|
|
||||||
if (strcmp(buf, "RPRT 1") == 0 )
|
//if it fails the first time, send the other token instead
|
||||||
|
if (strcmp(buf, "RPRT 1\n") == 0 ) //sdr++ has a linebreak here, is that in all versions of the protocol?
|
||||||
|
{
|
||||||
|
sprintf (buf, "M FM %d\n", bandwidth); //anything not SDR++
|
||||||
|
Send(sockfd, buf);
|
||||||
|
Recv(sockfd, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(buf, "RPRT 1\n") == 0 )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -222,9 +234,10 @@ bool GetSignalLevelEx(int sockfd, double *dBFS, int n_samp)
|
|||||||
//shoe in UDP input connection here...still having issues that I don't know how to resolve
|
//shoe in UDP input connection here...still having issues that I don't know how to resolve
|
||||||
int UDPBind (char *hostname, int portno)
|
int UDPBind (char *hostname, int portno)
|
||||||
{
|
{
|
||||||
int sockfd, n;
|
UNUSED(hostname);
|
||||||
struct sockaddr_in serveraddr, client_addr;
|
|
||||||
struct hostent *server;
|
int sockfd;
|
||||||
|
struct sockaddr_in serveraddr;
|
||||||
|
|
||||||
/* socket: create the socket */
|
/* socket: create the socket */
|
||||||
//UDP socket
|
//UDP socket
|
||||||
@@ -247,11 +260,20 @@ int UDPBind (char *hostname, int portno)
|
|||||||
perror("ERROR on binding UDP Port");
|
perror("ERROR on binding UDP Port");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//set these for non blocking when no samples to read
|
||||||
|
struct timeval read_timeout;
|
||||||
|
read_timeout.tv_sec = 0;
|
||||||
|
read_timeout.tv_usec = 10;
|
||||||
|
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &read_timeout, sizeof read_timeout);
|
||||||
|
|
||||||
return sockfd;
|
return sockfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//going to leave this function available, even if completely switched over to rtl_dev_tune now, may be useful in the future
|
||||||
void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
|
void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
|
||||||
{
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
int handle;
|
int handle;
|
||||||
unsigned short udp_port = opts->rtl_udp_port;
|
unsigned short udp_port = opts->rtl_udp_port;
|
||||||
char data[5] = {0}; //data buffer size is 5 for UDP frequency tuning
|
char data[5] = {0}; //data buffer size is 5 for UDP frequency tuning
|
||||||
@@ -273,4 +295,253 @@ void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
|
|||||||
address.sin_addr.s_addr = inet_addr("127.0.0.1"); //make user configurable later
|
address.sin_addr.s_addr = inet_addr("127.0.0.1"); //make user configurable later
|
||||||
address.sin_port = htons(udp_port);
|
address.sin_port = htons(udp_port);
|
||||||
sendto(handle, data, 5, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in));
|
sendto(handle, data, 5, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in));
|
||||||
|
|
||||||
|
close (handle); //close socket after sending.
|
||||||
|
}
|
||||||
|
|
||||||
|
void udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data)
|
||||||
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
size_t err = 0;
|
||||||
|
|
||||||
|
//listen with:
|
||||||
|
|
||||||
|
//short 8k/2
|
||||||
|
//socat stdio udp-listen:23456 | play --buffer 640 -q -b 16 -r 8000 -c2 -t s16 -
|
||||||
|
|
||||||
|
//short 8k/1
|
||||||
|
//socat stdio udp-listen:23456 | play --buffer 320 -q -b 16 -r 8000 -c1 -t s16 -
|
||||||
|
|
||||||
|
//float 8k/2
|
||||||
|
//socat stdio udp-listen:23456 | play --buffer 1280 -q -e float -b 32 -r 8000 -c2 -t f32 -
|
||||||
|
|
||||||
|
//float 8k/1
|
||||||
|
//socat stdio udp-listen:23456 | play --buffer 640 -q -e float -b 32 -r 8000 -c1 -t f32 -
|
||||||
|
|
||||||
|
//send audio or data to socket
|
||||||
|
err = sendto(opts->udp_sockfd, data, nsam, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in));
|
||||||
|
if (err < 0) fprintf (stderr, "\n UDP SENDTO ERR %ld", err); //return value here is size_t number of characters sent, or -1 for failure
|
||||||
|
if (err < nsam) fprintf (stderr, "\n UDP Underflow %ld", err); //I'm not even sure if this is possible
|
||||||
|
}
|
||||||
|
|
||||||
|
int m17_socket_receiver(dsd_opts * opts, void * data)
|
||||||
|
{
|
||||||
|
size_t err = 0;
|
||||||
|
struct sockaddr_in cliaddr;
|
||||||
|
socklen_t len = sizeof(cliaddr);
|
||||||
|
|
||||||
|
//receive data from socket
|
||||||
|
err = recvfrom(opts->udp_sockfd, data, 1000, 0, (struct sockaddr * ) & address, &len); //was MSG_WAITALL, but that seems to be = 256
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Analog UDP port on +2 of normal open socket
|
||||||
|
void udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void * data)
|
||||||
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
size_t err = 0;
|
||||||
|
|
||||||
|
//listen with:
|
||||||
|
|
||||||
|
//short 48k/1
|
||||||
|
//socat stdio udp-listen:23456 | play --buffer 1920 -q -b 16 -r 48000 -c1 -t s16 -
|
||||||
|
|
||||||
|
//send audio or data to socket
|
||||||
|
err = sendto(opts->udp_sockfdA, data, nsam, 0, (const struct sockaddr * ) & addressA, sizeof(struct sockaddr_in));
|
||||||
|
if (err < 0) fprintf (stderr, "\n UDP SENDTO ERR %ld", err); //return value here is size_t number of characters sent, or -1 for failure
|
||||||
|
if (err < nsam) fprintf (stderr, "\n UDP Underflow %ld", err); //I'm not even sure if this is possible
|
||||||
|
}
|
||||||
|
|
||||||
|
int m17_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data)
|
||||||
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
unsigned long long int err = 0;
|
||||||
|
|
||||||
|
//See notes in m17.c on line ~3395 regarding usage
|
||||||
|
|
||||||
|
//send audio or data to socket
|
||||||
|
err = sendto(opts->m17_udp_sock, data, nsam, 0, (const struct sockaddr * ) & addressM17, sizeof(struct sockaddr_in));
|
||||||
|
//RETURN Value should be ACKN or NACK, or PING, or PONG
|
||||||
|
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
int udp_socket_connect(dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
|
long int err = 0;
|
||||||
|
err = opts->udp_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " UDP Socket Error %ld\n", err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't think this is needed, but doesn't seem to hurt to keep it here either
|
||||||
|
int broadcastEnable = 1;
|
||||||
|
err = setsockopt(opts->udp_sockfd, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " UDP Broadcast Set Error %ld\n", err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset((char * ) & address, 0, sizeof(address));
|
||||||
|
address.sin_family = AF_INET;
|
||||||
|
err = address.sin_addr.s_addr = inet_addr(opts->udp_hostname);
|
||||||
|
if (err < 0) //error in this context reports back 32-bit inet_addr reversed order byte pairs
|
||||||
|
{
|
||||||
|
fprintf (stderr, " UDP inet_addr Error %ld\n", err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
address.sin_port = htons(opts->udp_portno);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " UDP htons Error %ld\n", err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int udp_socket_connectA(dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
|
long int err = 0;
|
||||||
|
err = opts->udp_sockfdA = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " UDP Socket Error %ld\n", err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't think this is needed, but doesn't seem to hurt to keep it here either
|
||||||
|
int broadcastEnable = 1;
|
||||||
|
err = setsockopt(opts->udp_sockfdA, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " UDP Broadcast Set Error %ld\n", err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset((char * ) & addressA, 0, sizeof(addressA));
|
||||||
|
addressA.sin_family = AF_INET;
|
||||||
|
err = addressA.sin_addr.s_addr = inet_addr(opts->udp_hostname);
|
||||||
|
if (err < 0) //error in this context reports back 32-bit inet_addr reversed order byte pairs
|
||||||
|
{
|
||||||
|
fprintf (stderr, " UDP inet_addr Error %ld\n", err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
addressA.sin_port = htons(opts->udp_portno+2); //plus 2 to current port assignment for the analog port value
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " UDP htons Error %ld\n", err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int udp_socket_connectM17(dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
|
long int err = 0;
|
||||||
|
err = opts->m17_udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " UDP Socket Error %ld\n", err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't think this is needed, but doesn't seem to hurt to keep it here either
|
||||||
|
int broadcastEnable = 1;
|
||||||
|
err = setsockopt(opts->m17_udp_sock, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " UDP Broadcast Set Error %ld\n", err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset((char * ) & addressM17, 0, sizeof(addressM17));
|
||||||
|
addressM17.sin_family = AF_INET;
|
||||||
|
err = addressM17.sin_addr.s_addr = inet_addr(opts->m17_hostname);
|
||||||
|
if (err < 0) //error in this context reports back 32-bit inet_addr reversed order byte pairs
|
||||||
|
{
|
||||||
|
fprintf (stderr, " UDP inet_addr Error %ld\n", err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
addressM17.sin_port = htons(opts->m17_portno);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " UDP htons Error %ld\n", err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void return_to_cc (dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
//extra safeguards due to sync issues with NXDN
|
||||||
|
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||||
|
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
||||||
|
|
||||||
|
memset (state->active_channel, 0, sizeof(state->active_channel));
|
||||||
|
|
||||||
|
//reset dmr blocks
|
||||||
|
dmr_reset_blocks (opts, state);
|
||||||
|
|
||||||
|
//zero out additional items
|
||||||
|
state->lasttg = 0;
|
||||||
|
state->lasttgR = 0;
|
||||||
|
state->lastsrc = 0;
|
||||||
|
state->lastsrcR = 0;
|
||||||
|
state->payload_algid = 0;
|
||||||
|
state->payload_algidR = 0;
|
||||||
|
state->payload_keyid = 0;
|
||||||
|
state->payload_keyidR = 0;
|
||||||
|
state->payload_mi = 0;
|
||||||
|
state->payload_miR = 0;
|
||||||
|
state->payload_miP = 0;
|
||||||
|
state->payload_miN = 0;
|
||||||
|
opts->p25_is_tuned = 0;
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
|
||||||
|
|
||||||
|
//tune back to the control channel -- NOTE: Doesn't work correctly on EDACS Analog Voice
|
||||||
|
//RIGCTL
|
||||||
|
if (opts->p25_trunk == 1 && opts->use_rigctl == 1)
|
||||||
|
{
|
||||||
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
|
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
//rtl
|
||||||
|
#ifdef USE_RTLSDR
|
||||||
|
if (opts->p25_trunk == 1 && opts->audio_in_type == 3) rtl_dev_tune (opts, state->p25_cc_freq);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
state->last_cc_sync_time = time(NULL);
|
||||||
|
|
||||||
|
//if P25p2 VCH and going back to P25p1 CC, flip symbolrate
|
||||||
|
if (state->p25_cc_is_tdma == 0)
|
||||||
|
{
|
||||||
|
state->samplesPerSymbol = 10;
|
||||||
|
state->symbolCenter = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if P25p1 Data Revert on P25p2 TDMA CC, flip symbolrate
|
||||||
|
if (state->p25_cc_is_tdma == 1)
|
||||||
|
{
|
||||||
|
state->samplesPerSymbol = 8;
|
||||||
|
state->symbolCenter = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fprintf (stderr, "\n User Activated Return to CC; \n ");
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
void
|
void
|
||||||
openSerial (dsd_opts * opts, dsd_state * state)
|
openSerial (dsd_opts * opts, dsd_state * state)
|
||||||
{
|
{
|
||||||
|
UNUSED(state);
|
||||||
|
|
||||||
struct termios tty;
|
struct termios tty;
|
||||||
speed_t baud;
|
speed_t baud;
|
||||||
@@ -73,6 +74,7 @@ resumeScan (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
char cmd[16];
|
char cmd[16];
|
||||||
ssize_t result;
|
ssize_t result;
|
||||||
|
UNUSED(result);
|
||||||
|
|
||||||
if (opts->serial_fd > 0)
|
if (opts->serial_fd > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
401
src/dsd_symbol.c
@@ -20,7 +20,7 @@
|
|||||||
int
|
int
|
||||||
getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
||||||
{
|
{
|
||||||
short sample, sample2;
|
short sample;
|
||||||
int i, sum, symbol, count;
|
int i, sum, symbol, count;
|
||||||
ssize_t result;
|
ssize_t result;
|
||||||
|
|
||||||
@@ -82,35 +82,24 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read the new sample from the input
|
// Read the new sample from the input
|
||||||
if(opts->audio_in_type == 0) // && state->menuopen == 0 still not quite working
|
if(opts->audio_in_type == 0) //pulse audio
|
||||||
{
|
{
|
||||||
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
|
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
|
||||||
//look into how processAudio handles playback, not sure if latency issues, or garbage sample values crash pulse when written
|
|
||||||
// if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample < 32767 && sample > -32767)
|
|
||||||
if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample != 0)
|
|
||||||
{
|
|
||||||
state->pulse_raw_out_buffer = sample; //steal raw out buffer sample here?
|
|
||||||
pa_simple_write(opts->pulse_raw_dev_out, (void*)&state->pulse_raw_out_buffer, 2, NULL);
|
|
||||||
}
|
|
||||||
//playback is wrong, depends on samples_per_symbol, maybe make a buffer and store first?
|
|
||||||
//making buffer might also fix raw audio monitoring as well
|
|
||||||
if (opts->wav_out_file_raw[0] != 0) //if set for recording sample raw files
|
|
||||||
{
|
|
||||||
//writeRawSample (opts, state, sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (opts->audio_in_type == 5) //OSS
|
||||||
|
{
|
||||||
|
read (opts->audio_in_fd, &sample, 2);
|
||||||
|
}
|
||||||
|
|
||||||
//stdin only, wav files moving to new number
|
//stdin only, wav files moving to new number
|
||||||
else if (opts->audio_in_type == 1)
|
else if (opts->audio_in_type == 1) //won't work in windows, needs posix pipe (mintty)
|
||||||
{
|
{
|
||||||
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
||||||
if(result == 0)
|
if(result == 0)
|
||||||
{
|
{
|
||||||
//cleanupAndExit (opts, state);
|
|
||||||
sf_close(opts->audio_in_file);
|
sf_close(opts->audio_in_file);
|
||||||
opts->audio_in_type = 0; //set input type
|
cleanupAndExit (opts, state);
|
||||||
openPulseInput(opts); //open pulse inpput
|
|
||||||
sample = 0; //send zero sample
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//wav files, same but using seperate value so we can still manipulate ncurses menu
|
//wav files, same but using seperate value so we can still manipulate ncurses menu
|
||||||
@@ -122,59 +111,279 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
{
|
{
|
||||||
|
|
||||||
sf_close(opts->audio_in_file);
|
sf_close(opts->audio_in_file);
|
||||||
opts->audio_in_type = 0; //set input type
|
fprintf (stderr, "\nEnd of %s\n", opts->audio_in_dev);
|
||||||
openPulseInput(opts); //open pulse inpput
|
//open pulse input if we are pulse output AND using ncurses terminal
|
||||||
sample = 0; //send zero sample
|
if (opts->audio_out_type == 0 && opts->use_ncurses_terminal == 1)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 0; //set input type
|
||||||
|
openPulseInput(opts); //open pulse input
|
||||||
|
}
|
||||||
|
//else cleanup and exit
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cleanupAndExit(opts, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (opts->audio_in_type == 3)
|
else if (opts->audio_in_type == 3)
|
||||||
{
|
{
|
||||||
#ifdef USE_RTLSDR
|
#ifdef USE_RTLSDR
|
||||||
// TODO: need to read demodulated stream here
|
// Read demodulated stream here
|
||||||
// get_rtlsdr_sample(&sample);
|
if (get_rtlsdr_sample(&sample, opts, state) < 0)
|
||||||
get_rtlsdr_sample(&sample, opts, state);
|
cleanupAndExit(opts, state);
|
||||||
if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample < 32767 && sample > -32767)
|
//update root means square power level
|
||||||
{
|
opts->rtl_rms = rtl_return_rms();
|
||||||
state->pulse_raw_out_buffer = sample; //steal raw out buffer sample here?
|
sample *= opts->rtl_volume_multiplier;
|
||||||
pa_simple_write(opts->pulse_raw_dev_out, (void*)&state->pulse_raw_out_buffer, 2, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
//tcp socket input from SDR++
|
|
||||||
|
//tcp socket input from SDR++ -- now with 1 retry if connection is broken
|
||||||
else if (opts->audio_in_type == 8)
|
else if (opts->audio_in_type == 8)
|
||||||
{
|
{
|
||||||
|
#ifdef AERO_BUILD
|
||||||
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
if(result == 0) {
|
if(result == 0) {
|
||||||
sf_close(opts->tcp_file_in);
|
fprintf (stderr, "\nConnection to TCP Server Interrupted. Trying again in 3 seconds.\n");
|
||||||
opts->audio_in_type = 0; //set input type
|
sample = 0;
|
||||||
openPulseInput(opts); //open pulse inpput
|
sf_close(opts->tcp_file_in); //close current connection on this end
|
||||||
sample = 0; //zero sample on bad result, keep the ball rolling
|
sleep (3); //halt all processing and wait 3 seconds
|
||||||
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
|
|
||||||
|
//attempt to reconnect to socket
|
||||||
|
opts->tcp_sockfd = 0;
|
||||||
|
opts->tcp_sockfd = Connect(opts->tcp_hostname, opts->tcp_portno);
|
||||||
|
if (opts->tcp_sockfd != 0)
|
||||||
|
{
|
||||||
|
//reset audio input stream
|
||||||
|
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 Reconnect to TCP with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
|
}
|
||||||
|
else fprintf (stderr, "TCP Socket Reconnected Successfully.\n");
|
||||||
|
}
|
||||||
|
else fprintf (stderr, "TCP Socket Connection Error.\n");
|
||||||
|
|
||||||
|
//now retry reading sample
|
||||||
|
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
if (result == 0) {
|
||||||
|
sf_close(opts->tcp_file_in);
|
||||||
|
opts->audio_in_type = 0; //set input type
|
||||||
|
opts->tcp_sockfd = 0; //added this line so we will know if it connected when using ncurses terminal keyboard shortcut
|
||||||
|
//openPulseInput(opts); //open pulse inpput
|
||||||
|
sample = 0; //zero sample on bad result, keep the ball rolling
|
||||||
|
//open pulse input if we are pulse output AND using ncurses terminal
|
||||||
|
if (opts->audio_out_type == 0 && opts->use_ncurses_terminal == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
|
||||||
|
fprintf (stderr, "Opening Pulse Audio Input.\n");
|
||||||
|
opts->audio_in_type = 0; //set input type
|
||||||
|
openPulseInput(opts); //open pulse input
|
||||||
|
}
|
||||||
|
//else cleanup and exit
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
|
||||||
|
fprintf (stderr, "Closing DSD-FME.\n");
|
||||||
|
cleanupAndExit(opts, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
if(result == 0) {
|
||||||
|
TCP_RETRY:
|
||||||
|
if (exitflag == 1) cleanupAndExit(opts, state); //needed to break the loop on ctrl+c
|
||||||
|
fprintf (stderr, "\nConnection to TCP Server Interrupted. Trying again in 3 seconds.\n");
|
||||||
|
sample = 0;
|
||||||
|
sf_close(opts->tcp_file_in); //close current connection on this end
|
||||||
|
sleep (3); //halt all processing and wait 3 seconds
|
||||||
|
|
||||||
|
//attempt to reconnect to socket
|
||||||
|
opts->tcp_sockfd = 0;
|
||||||
|
opts->tcp_sockfd = Connect(opts->tcp_hostname, opts->tcp_portno);
|
||||||
|
if (opts->tcp_sockfd != 0)
|
||||||
|
{
|
||||||
|
//reset audio input stream
|
||||||
|
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 Reconnect to TCP with libsndfile: %s\n", sf_strerror(NULL));
|
||||||
|
}
|
||||||
|
else fprintf (stderr, "TCP Socket Reconnected Successfully.\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "TCP Socket Connection Error.\n");
|
||||||
|
if (opts->frame_m17 == 1) goto TCP_RETRY; //if using m17 encoder/decoder, just keep looping to keep alive
|
||||||
|
}
|
||||||
|
|
||||||
|
//now retry reading sample
|
||||||
|
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||||
|
if (result == 0) {
|
||||||
|
sf_close(opts->tcp_file_in);
|
||||||
|
opts->audio_in_type = 0; //set input type
|
||||||
|
opts->tcp_sockfd = 0; //added this line so we will know if it connected when using ncurses terminal keyboard shortcut
|
||||||
|
openPulseInput(opts); //open pulse inpput
|
||||||
|
sample = 0; //zero sample on bad result, keep the ball rolling
|
||||||
|
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
//UDP Socket input...not working correct. Reads samples, but no sync
|
|
||||||
else if (opts->audio_in_type == 6)
|
//BUG REPORT: 1. DMR Simplex doesn't work with raw wav files. 2. Using the monitor w/ wav file saving may produce undecodable wav files.
|
||||||
|
//reworked a bit to allow raw audio wav file saving without the monitoring poriton active
|
||||||
|
if (have_sync == 0)
|
||||||
{
|
{
|
||||||
//I think this doesn't get the entire dgram when we run sf_read_short on the udp dgram
|
|
||||||
result = sf_read_short(opts->udp_file_in, &sample, 1);
|
//do an extra checkfor carrier signal so that random raw audio spurts don't play during decoding
|
||||||
// if (sample != 0)
|
// if ( (state->carrier == 1) && ((time(NULL) - state->last_vc_sync_time) < 2)) /This probably doesn't work correctly since we update time check when playing raw audio
|
||||||
// fprintf (stderr, "Result = %d Sample = %d \n", result, sample);
|
// {
|
||||||
|
// memset (state->analog_out, 0, sizeof(state->analog_out));
|
||||||
|
// state->analog_sample_counter = 0;
|
||||||
|
// } //This is the root cause of issue listed above, will evaluate further at a later time for a more elegant solution, or determine if anything is negatively impacted by removing this
|
||||||
|
|
||||||
|
//sanity check to prevent an overflow
|
||||||
|
if (state->analog_sample_counter > 959)
|
||||||
|
state->analog_sample_counter = 959;
|
||||||
|
|
||||||
|
state->analog_out[state->analog_sample_counter++] = sample;
|
||||||
|
|
||||||
|
if (state->analog_sample_counter == 960)
|
||||||
|
{
|
||||||
|
//get an rms value if not using the rtl built in version
|
||||||
|
if (opts->audio_in_type != 3 && opts->monitor_input_audio == 1)
|
||||||
|
opts->rtl_rms = raw_rms(state->analog_out, 960, 1);
|
||||||
|
|
||||||
|
//raw wav file saving -- only write when not NXDN, dPMR, or M17 due to noise that can cause tons of false positives when no sync
|
||||||
|
if (opts->wav_out_raw != NULL && opts->frame_nxdn48 == 0 && opts->frame_nxdn96 == 0 && opts->frame_dpmr == 0 && opts->frame_m17 == 0)
|
||||||
|
{
|
||||||
|
sf_write_short(opts->wav_out_raw, state->analog_out, 960);
|
||||||
|
sf_write_sync (opts->wav_out_raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
//low pass filter
|
||||||
|
if (opts->use_lpf == 1)
|
||||||
|
lpf(state, state->analog_out, 960);
|
||||||
|
|
||||||
|
//high pass filter
|
||||||
|
if (opts->use_hpf == 1)
|
||||||
|
hpf (state, state->analog_out, 960);
|
||||||
|
|
||||||
|
//pass band filter
|
||||||
|
if (opts->use_pbf == 1)
|
||||||
|
pbf(state, state->analog_out, 960);
|
||||||
|
|
||||||
|
//manual gain control
|
||||||
|
if (opts->audio_gainA > 0.0f)
|
||||||
|
analog_gain (opts, state, state->analog_out, 960);
|
||||||
|
|
||||||
|
//automatic gain control
|
||||||
|
else
|
||||||
|
agsm(opts, state, state->analog_out, 960);
|
||||||
|
|
||||||
|
//Running RMS after filtering does remove the analog spike from the RMS value
|
||||||
|
//but noise floor noise will still produce higher values
|
||||||
|
// if (opts->audio_in_type != 3 && opts->monitor_input_audio == 1)
|
||||||
|
// opts->rtl_rms = raw_rms(state->analog_out, 960, 1);
|
||||||
|
|
||||||
|
//seems to be working now, but RMS values are lower on actual analog signal than on no signal but noise
|
||||||
|
if ( (opts->rtl_rms > opts->rtl_squelch_level) && opts->monitor_input_audio == 1 && state->carrier == 0 ) //added carrier check here in lieu of disabling it above
|
||||||
|
{
|
||||||
|
if (opts->audio_out_type == 0)
|
||||||
|
pa_simple_write(opts->pulse_raw_dev_out, state->analog_out, 960*2, NULL);
|
||||||
|
|
||||||
|
if (opts->audio_out_type == 8)
|
||||||
|
udp_socket_blasterA (opts, state, 960*2, state->analog_out);
|
||||||
|
|
||||||
|
//NOTE: Worked okay earlier in Cygwin, so should be fine -- can only operate at 48k1, else slow mode lag
|
||||||
|
|
||||||
|
//This one will only operate when OSS 48k1 (when both input and output are OSS audio)
|
||||||
|
if (opts->audio_out_type == 5 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1)
|
||||||
|
write (opts->audio_out_fd, state->analog_out, 960*2);
|
||||||
|
|
||||||
|
//STDOUT, but only when operating at 48k1 (no go just yet)
|
||||||
|
// if (opts->audio_out_type == 1 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1)
|
||||||
|
// write (opts->audio_out_fd, state->analog_out, 960*2);
|
||||||
|
|
||||||
|
//OSS 8k1 (no go just yet)
|
||||||
|
// if (opts->audio_out_type == 2 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1)
|
||||||
|
// write (opts->audio_out_fd, state->analog_out, 960*2);
|
||||||
|
|
||||||
|
//test updating the sync time, so we can hold here while trunking or scanning
|
||||||
|
state->last_cc_sync_time = time(NULL);
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
//raw wav file saving -- save the WAV file samples before we apply filtering to them
|
||||||
|
// if (opts->wav_out_raw != NULL && opts->frame_nxdn48 == 0 && opts->frame_nxdn96 == 0 && opts->frame_dpmr == 0 && opts->frame_m17 == 0)
|
||||||
|
// {
|
||||||
|
// sf_write_short(opts->wav_out_raw, state->analog_out, 960);
|
||||||
|
// sf_write_sync (opts->wav_out_raw);
|
||||||
|
// }
|
||||||
|
|
||||||
|
memset (state->analog_out, 0, sizeof(state->analog_out));
|
||||||
|
state->analog_sample_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (have_sync == 1)
|
||||||
|
{
|
||||||
|
//sanity check to prevent an overflow
|
||||||
|
if (state->analog_sample_counter > 959)
|
||||||
|
state->analog_sample_counter = 959;
|
||||||
|
|
||||||
|
state->analog_out[state->analog_sample_counter++] = sample;
|
||||||
|
|
||||||
|
if (state->analog_sample_counter == 960)
|
||||||
|
{
|
||||||
|
//raw wav file saving -- file size on this blimps pretty fast 1 min ~= 6 MB; 1 hour ~= 360 MB;
|
||||||
|
if (opts->wav_out_raw != NULL)
|
||||||
|
{
|
||||||
|
sf_write_short(opts->wav_out_raw, state->analog_out, 960);
|
||||||
|
sf_write_sync (opts->wav_out_raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
//zero out and reset counter
|
||||||
|
memset (state->analog_out, 0, sizeof(state->analog_out));
|
||||||
|
state->analog_sample_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (opts->use_cosine_filter)
|
if (opts->use_cosine_filter)
|
||||||
{
|
{
|
||||||
if ( (state->lastsynctype >= 10 && state->lastsynctype <= 13) || state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34)
|
if ( (state->lastsynctype >= 10 && state->lastsynctype <= 13) || state->lastsynctype == 32 || state->lastsynctype == 33
|
||||||
|
|| state->lastsynctype == 34 || state->lastsynctype == 30 || state->lastsynctype == 31)
|
||||||
{
|
{
|
||||||
sample = dmr_filter(sample);
|
sample = dmr_filter(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (state->lastsynctype == 8 || state->lastsynctype == 9 ||
|
else if (state->lastsynctype == 8 || state->lastsynctype == 9 || state->lastsynctype == 16 || state->lastsynctype == 17 ||
|
||||||
state->lastsynctype == 16 || state->lastsynctype == 17 ||
|
state->lastsynctype == 86 || state->lastsynctype == 87 || state->lastsynctype == 98 || state->lastsynctype == 99)
|
||||||
|
{
|
||||||
|
sample = m17_filter(sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (
|
||||||
state->lastsynctype == 20 || state->lastsynctype == 21 ||
|
state->lastsynctype == 20 || state->lastsynctype == 21 ||
|
||||||
state->lastsynctype == 22 || state->lastsynctype == 23 ||
|
state->lastsynctype == 22 || state->lastsynctype == 23 ||
|
||||||
state->lastsynctype == 24 || state->lastsynctype == 25 ||
|
state->lastsynctype == 24 || state->lastsynctype == 25 ||
|
||||||
@@ -196,7 +405,7 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
{
|
{
|
||||||
//sample = dmr_filter(sample); //work on filter later
|
//sample = dmr_filter(sample); //work on filter later
|
||||||
}
|
}
|
||||||
else // the 12.5KHz NXDN filter is the same as the DMR filter...hopefully
|
else
|
||||||
{
|
{
|
||||||
sample = dmr_filter(sample);
|
sample = dmr_filter(sample);
|
||||||
}
|
}
|
||||||
@@ -281,7 +490,8 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
|
|
||||||
if (state->samplesPerSymbol == 20) //nxdn 4800 baud 2400 symbol rate
|
if (state->samplesPerSymbol == 20) //nxdn 4800 baud 2400 symbol rate
|
||||||
{
|
{
|
||||||
if ((i >= 9) && (i <= 11))
|
// if ((i >= 9) && (i <= 11))
|
||||||
|
if ((i >= 7) && (i <= 13)) //7, 13 working good on multiple nxdn48, fewer random errors
|
||||||
{
|
{
|
||||||
sum += sample;
|
sum += sample;
|
||||||
count++;
|
count++;
|
||||||
@@ -301,11 +511,23 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
{
|
{
|
||||||
// 0: C4FM modulation
|
// 0: C4FM modulation
|
||||||
|
|
||||||
if ((i >= state->symbolCenter - 1) && (i <= state->symbolCenter + 2))
|
//EXPERIMENTAL: manipulate the left edge depending on sync type
|
||||||
{
|
//TODO: See if we can manipulate this a bit more based on AFC or similar type function
|
||||||
sum += sample;
|
int l_edge = 2;
|
||||||
count++;
|
int r_edge = 2;
|
||||||
}
|
|
||||||
|
if (state->synctype == 30 || state->synctype == 31) l_edge = 1; //YSF (need to test with new recordings at BW:12000)
|
||||||
|
else if ((state->lastsynctype >= 10 && state->lastsynctype <= 13) || state->lastsynctype == 32 || state->lastsynctype == 33) l_edge = 1;
|
||||||
|
else l_edge = 2; //P25 and NXDN96 perform better, DMR doesn't seem to care too much, but may favor 1 and not 2
|
||||||
|
|
||||||
|
if ((i >= state->symbolCenter - l_edge) && (i <= state->symbolCenter + r_edge))
|
||||||
|
{
|
||||||
|
sum += sample;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug: show what was selected for the left edge
|
||||||
|
// fprintf (stderr, " L:%d", l_edge);
|
||||||
|
|
||||||
#ifdef TRACE_DSD
|
#ifdef TRACE_DSD
|
||||||
if (i == state->symbolCenter - 1) {
|
if (i == state->symbolCenter - 1) {
|
||||||
@@ -316,7 +538,18 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else if (state->rf_mod == 1) //QPSK
|
||||||
|
{
|
||||||
|
//going one left, two on the right for QPSK, local system seems to favor that
|
||||||
|
//with same PPM setting used on C4FM, unsure if that works on other systems, just
|
||||||
|
//testing things now, not like QPSK works well here anyways
|
||||||
|
if ((i == state->symbolCenter - 1) || (i == state->symbolCenter + 2)) //1,2
|
||||||
|
{
|
||||||
|
sum += sample;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else //GFSK
|
||||||
{
|
{
|
||||||
// 1: QPSK modulation
|
// 1: QPSK modulation
|
||||||
// 2: GFSK modulation
|
// 2: GFSK modulation
|
||||||
@@ -325,12 +558,23 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
// comes one sample too late.
|
// comes one sample too late.
|
||||||
// This change makes a significant improvement in the BER, at least for
|
// This change makes a significant improvement in the BER, at least for
|
||||||
// this file.
|
// this file.
|
||||||
//if ((i == state->symbolCenter) || (i == state->symbolCenter + 1))
|
// if ((i == state->symbolCenter) || (i == state->symbolCenter + 1))
|
||||||
|
//GFSK should ideally be the same on the left and right, I would think
|
||||||
|
//I have not observed a system that would benefit from this alignment
|
||||||
if ((i == state->symbolCenter - 1) || (i == state->symbolCenter + 1))
|
if ((i == state->symbolCenter - 1) || (i == state->symbolCenter + 1))
|
||||||
{
|
{
|
||||||
sum += sample;
|
sum += sample;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MISC Notes: When using RTL input, NXDN96 at BW:16 NXDN48 at BW:12
|
||||||
|
//I am beginning to suspect that RTL input is halfing the bandwidth, which
|
||||||
|
//is my original consclusion back when I was working on it last, unsure
|
||||||
|
//or the best course on that, could just be a rtl_fm flaw
|
||||||
|
//P25 may also work better at 16 as well now, hard to tell the difference since both are good
|
||||||
|
//DMR may not be any different on 12 or 16
|
||||||
|
//most likely though, this all will just depend on signal stregth more than anything
|
||||||
|
//as to how much BW you should set
|
||||||
|
|
||||||
#ifdef TRACE_DSD
|
#ifdef TRACE_DSD
|
||||||
//if (i == state->symbolCenter) {
|
//if (i == state->symbolCenter) {
|
||||||
@@ -383,6 +627,12 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//test throttle on wav input files
|
||||||
|
if (opts->audio_in_type == 2)
|
||||||
|
{
|
||||||
|
if (state->use_throttle == 1) usleep(.003); //very environment specific, tuning to cygwin
|
||||||
|
}
|
||||||
|
|
||||||
//read op25/fme symbol bin files
|
//read op25/fme symbol bin files
|
||||||
if (opts->audio_in_type == 4)
|
if (opts->audio_in_type == 4)
|
||||||
{
|
{
|
||||||
@@ -396,17 +646,36 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
|||||||
state->symbolc = fgetc(opts->symbolfile);
|
state->symbolc = fgetc(opts->symbolfile);
|
||||||
|
|
||||||
//experimental throttle
|
//experimental throttle
|
||||||
useconds_t stime = state->symbol_throttle;
|
|
||||||
if (state->use_throttle == 1)
|
if (state->use_throttle == 1)
|
||||||
{
|
{
|
||||||
usleep(stime);
|
// useconds_t stime = state->symbol_throttle;
|
||||||
|
// usleep(stime);
|
||||||
|
usleep(.003); //very environment specific, tuning to cygwin
|
||||||
}
|
}
|
||||||
//fprintf(stderr, "%d", state->symbolc);
|
//fprintf(stderr, "%d", state->symbolc);
|
||||||
if( feof(opts->symbolfile) )
|
if( feof(opts->symbolfile) )
|
||||||
{
|
{
|
||||||
opts->audio_in_type = 0; //switch to pulse after playback, ncurses terminal can initiate replay if wanted
|
// opts->audio_in_type = 0; //switch to pulse after playback, ncurses terminal can initiate replay if wanted
|
||||||
fclose(opts->symbolfile);
|
fclose(opts->symbolfile);
|
||||||
openPulseInput(opts);
|
fprintf (stderr, "\nEnd of %s\n", opts->audio_in_dev);
|
||||||
|
//in debug mode, re-run .bin files over and over (look for memory leaks, etc)
|
||||||
|
if (state->debug_mode == 1)
|
||||||
|
{
|
||||||
|
opts->symbolfile = NULL;
|
||||||
|
opts->symbolfile = fopen(opts->audio_in_dev, "r");
|
||||||
|
opts->audio_in_type = 4; //symbol capture bin files
|
||||||
|
}
|
||||||
|
//open pulse input if we are pulse output AND using ncurses terminal
|
||||||
|
else if (opts->audio_out_type == 0 && opts->use_ncurses_terminal == 1)
|
||||||
|
{
|
||||||
|
opts->audio_in_type = 0; //set input type
|
||||||
|
openPulseInput(opts); //open pulse input
|
||||||
|
}
|
||||||
|
//else cleanup and exit
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cleanupAndExit(opts, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//assign symbol/dibit values based on modulation type
|
//assign symbol/dibit values based on modulation type
|
||||||
|
|||||||
71
src/dsd_time.c
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* dsd_time.c
|
||||||
|
* Time and Date Functions
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2024-04 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
//TODO: Make sure everything still works as intended, every free(timestr) has a NULL check first,
|
||||||
|
//make sure no other loose or random time functions embedded in other files or functions, etc
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
//get HHmmss timestamp no colon (file operations)
|
||||||
|
char * getTime()
|
||||||
|
{
|
||||||
|
char * curr = calloc(7, sizeof(char));
|
||||||
|
time_t t = time(NULL);
|
||||||
|
struct tm * ptm = localtime(& t);
|
||||||
|
sprintf(curr,"%02d%02d%02d", ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get HH:mm:ss timestamp with colon (Sync/Console Display)
|
||||||
|
char * getTimeC()
|
||||||
|
{
|
||||||
|
char * curr = calloc(9, sizeof(char));
|
||||||
|
time_t t = time(NULL);
|
||||||
|
struct tm * ptm = localtime(& t);
|
||||||
|
sprintf(curr, "%02d:%02d:%02d", ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get HH:mm:ss timestamp with colon (Ncurses Call History)
|
||||||
|
char * getTimeN(time_t t)
|
||||||
|
{
|
||||||
|
char * curr = calloc(9, sizeof(char));
|
||||||
|
struct tm * ptm = localtime(& t);
|
||||||
|
sprintf(curr, "%02d:%02d:%02d", ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get YYYYMMDD without hyphen (file operations)
|
||||||
|
char * getDate()
|
||||||
|
{
|
||||||
|
char * curr = calloc(25, sizeof(char));
|
||||||
|
time_t t = time(NULL);
|
||||||
|
struct tm * ptm = localtime(& t);
|
||||||
|
sprintf(curr,"%04d%02d%02d", ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday);
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get YYYY-MM-DD with hyphen (Sync/Console Display)
|
||||||
|
char * getDateH()
|
||||||
|
{
|
||||||
|
char * curr = calloc(27, sizeof(char));
|
||||||
|
time_t t = time(NULL);
|
||||||
|
struct tm * ptm = localtime(& t);
|
||||||
|
sprintf(curr, "%04d-%02d-%02d", ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday);
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get YYYY-MM-DD with hyphen (Ncurses Call History)
|
||||||
|
char * getDateN(time_t t)
|
||||||
|
{
|
||||||
|
char * curr = calloc(27, sizeof(char));
|
||||||
|
struct tm * ptm = localtime(& t);
|
||||||
|
sprintf(curr, "%04d-%02d-%02d", ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday);
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,138 +1,47 @@
|
|||||||
/*
|
/*-------------------------------------------------------------------------------
|
||||||
* Copyright (C) 2010 DSD Author
|
* dsd_upsample.c
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
* Simplified 8k to 48k Upsample Functions
|
||||||
|
* Goodbye terrible ringing sound
|
||||||
*
|
*
|
||||||
* 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
|
* LWVMOBILE
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
* 2024-03 DSD-FME Florida Man Edition
|
||||||
* 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 "dsd.h"
|
||||||
|
|
||||||
void
|
//produce 6 short samples (48k) for every 1 short sample (8k)
|
||||||
upsample (dsd_state * state, float invalue)
|
void upsampleS (short invalue, short prev, short outbuf[6])
|
||||||
|
{
|
||||||
|
UNUSED(prev);
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
outbuf[i] = invalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//legacy 6x simplified version for the float_buf_p
|
||||||
|
void upsample (dsd_state * state, float invalue)
|
||||||
{
|
{
|
||||||
|
|
||||||
int i, j, sum;
|
float *outbuf1;
|
||||||
float *outbuf1, c, d;
|
|
||||||
if (state->currentslot == 0 && state->dmr_stereo == 1)
|
if (state->currentslot == 0 && state->dmr_stereo == 1)
|
||||||
{
|
|
||||||
outbuf1 = state->audio_out_float_buf_p;
|
outbuf1 = state->audio_out_float_buf_p;
|
||||||
}
|
|
||||||
if (state->currentslot == 1 && state->dmr_stereo == 1)
|
else if (state->currentslot == 1 && state->dmr_stereo == 1)
|
||||||
{
|
|
||||||
outbuf1 = state->audio_out_float_buf_pR;
|
outbuf1 = state->audio_out_float_buf_pR;
|
||||||
}
|
|
||||||
if (state->dmr_stereo == 0)
|
else if (state->dmr_stereo == 0)
|
||||||
{
|
|
||||||
outbuf1 = state->audio_out_float_buf_p;
|
outbuf1 = state->audio_out_float_buf_p;
|
||||||
}
|
|
||||||
//outbuf1 = state->audio_out_float_buf_p;
|
|
||||||
outbuf1--;
|
|
||||||
c = *outbuf1;
|
|
||||||
d = invalue;
|
|
||||||
// basic triangle interpolation
|
|
||||||
outbuf1++;
|
|
||||||
*outbuf1 = ((invalue * (float) 0.166) + (c * (float) 0.834));
|
|
||||||
outbuf1++;
|
|
||||||
*outbuf1 = ((invalue * (float) 0.332) + (c * (float) 0.668));
|
|
||||||
outbuf1++;
|
|
||||||
*outbuf1 = ((invalue * (float) 0.5) + (c * (float) 0.5));
|
|
||||||
outbuf1++;
|
|
||||||
*outbuf1 = ((invalue * (float) 0.668) + (c * (float) 0.332));
|
|
||||||
outbuf1++;
|
|
||||||
*outbuf1 = ((invalue * (float) 0.834) + (c * (float) 0.166));
|
|
||||||
outbuf1++;
|
|
||||||
*outbuf1 = d;
|
|
||||||
outbuf1++;
|
|
||||||
|
|
||||||
//only if enabled, produces crackling/buzzing sounds when input type is not pulse audio (reason still unknown)
|
|
||||||
//also, could be improved to provide better audio in the future
|
|
||||||
if (state->audio_smoothing == 1)
|
|
||||||
{
|
|
||||||
if (state->currentslot == 1 && state->dmr_stereo == 1)
|
|
||||||
{
|
|
||||||
if (state->audio_out_idx2R > 24)
|
|
||||||
{
|
|
||||||
// smoothing
|
|
||||||
outbuf1 -= 16;
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
sum = 0;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
*outbuf1 = (sum / (float) 3);
|
|
||||||
outbuf1++;
|
|
||||||
}
|
|
||||||
outbuf1 -= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state->currentslot == 0 && state->dmr_stereo == 1)
|
|
||||||
{
|
|
||||||
if (state->audio_out_idx2 > 24)
|
|
||||||
{
|
|
||||||
// smoothing
|
|
||||||
outbuf1 -= 16;
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
sum = 0;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
*outbuf1 = (sum / (float) 3);
|
|
||||||
outbuf1++;
|
|
||||||
}
|
|
||||||
outbuf1 -= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state->dmr_stereo == 0)
|
|
||||||
{
|
|
||||||
if (state->audio_out_idx2 > 24)
|
|
||||||
{
|
|
||||||
// smoothing
|
|
||||||
outbuf1 -= 16;
|
|
||||||
for (j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 6; i++)
|
|
||||||
{
|
|
||||||
sum = 0;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 += 2;
|
|
||||||
sum += (int)*outbuf1;
|
|
||||||
outbuf1 -= 2;
|
|
||||||
*outbuf1 = (sum / (float) 3);
|
|
||||||
outbuf1++;
|
|
||||||
}
|
|
||||||
outbuf1 -= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
*outbuf1 = invalue;
|
||||||
|
outbuf1++;
|
||||||
|
*outbuf1 = invalue;
|
||||||
|
outbuf1++;
|
||||||
|
*outbuf1 = invalue;
|
||||||
|
outbuf1++;
|
||||||
|
*outbuf1 = invalue;
|
||||||
|
outbuf1++;
|
||||||
|
*outbuf1 = invalue;
|
||||||
|
outbuf1++;
|
||||||
|
*outbuf1 = invalue;
|
||||||
}
|
}
|
||||||
550
src/dstar.c
@@ -1,183 +1,397 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 DSD Author
|
|
||||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
||||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note: D-STAR support is fairly complete at this point.
|
|
||||||
* The ambe3600x2450 decoder is similar butnot compatible with D-STAR voice frames.
|
|
||||||
* The dstar interleave pattern is different as well.
|
|
||||||
* GMSK modulation optimizations will also required to get a usable bit error
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
#include "dstar_const.h"
|
#include "dstar_const.h"
|
||||||
#include "dstar_header.h"
|
#include "fcs.h"
|
||||||
|
|
||||||
|
//simplified DSTAR
|
||||||
|
void processDSTAR(dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
uint8_t sd[480];
|
||||||
|
memset (sd, 0, sizeof(sd));
|
||||||
|
int i, j, dibit;
|
||||||
|
char ambe_fr[4][24];
|
||||||
|
const int *w, *x;
|
||||||
|
memset(ambe_fr, 0, sizeof(ambe_fr));
|
||||||
|
w = dW;
|
||||||
|
x = dX;
|
||||||
|
|
||||||
void processDSTAR(dsd_opts * opts, dsd_state * state) {
|
//20 voice and 19 slow data frames (20th is frame sync)
|
||||||
// extracts AMBE frames from D-STAR voice frame
|
for (j = 0; j < 21; j++)
|
||||||
int i, j, dibit;
|
{
|
||||||
char ambe_fr[4][24];
|
|
||||||
unsigned char data[9];
|
|
||||||
unsigned int bits[4];
|
|
||||||
int framecount;
|
|
||||||
int sync_missed = 0;
|
|
||||||
unsigned char slowdata[4];
|
|
||||||
unsigned int bitbuffer = 0;
|
|
||||||
const int *w, *x;
|
|
||||||
|
|
||||||
//set carrier to 1 so ncurses will light up green
|
memset(ambe_fr, 0, sizeof(ambe_fr));
|
||||||
state->carrier = 1;
|
w = dW;
|
||||||
|
x = dX;
|
||||||
|
|
||||||
if (opts->errorbars == 1) {
|
for (i = 0; i < 72; i++)
|
||||||
fprintf (stderr, "e:");
|
{
|
||||||
}
|
dibit = getDibit(opts, state);
|
||||||
|
ambe_fr[*w][*x] = dibit & 1;
|
||||||
|
w++;
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DSTAR_DUMP
|
soft_mbe(opts, state, NULL, ambe_fr, NULL);
|
||||||
fprintf (stderr, "\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (state->synctype == 18) {
|
if (j != 20)
|
||||||
framecount = 0;
|
{
|
||||||
state->synctype = 6;
|
for (i = 0; i < 24; i++)
|
||||||
} else if (state->synctype == 19) {
|
{
|
||||||
framecount = 0;
|
//slow data
|
||||||
state->synctype = 7;
|
sd[(j*24)+i] = (uint8_t) getDibit(opts, state);
|
||||||
} else {
|
|
||||||
framecount = 1; //just saw a sync frame; there should be 20 not 21 till the next
|
|
||||||
}
|
|
||||||
//why is this running in a loop for?
|
|
||||||
while (sync_missed < 3) {
|
|
||||||
|
|
||||||
memset(ambe_fr, 0, 96);
|
}
|
||||||
// voice frame
|
}
|
||||||
w = dW;
|
|
||||||
x = dX;
|
|
||||||
|
|
||||||
for (i = 0; i < 72; i++) {
|
if (j == 20)
|
||||||
|
processDSTAR_SD(opts, state, sd);
|
||||||
|
|
||||||
dibit = getDibit(opts, state);
|
//since we are in a long loop, use this to improve response time in ncurses
|
||||||
|
if (opts->use_ncurses_terminal == 1)
|
||||||
|
ncursesPrinter(opts, state);
|
||||||
|
|
||||||
bitbuffer <<= 1;
|
}
|
||||||
if (dibit == 1) {
|
|
||||||
bitbuffer |= 0x01;
|
|
||||||
}
|
|
||||||
if ((bitbuffer & 0x00FFFFFF) == 0x00AAB468) {
|
|
||||||
// we're slipping bits
|
|
||||||
fprintf (stderr, "sync in voice after i=%d, restarting\n", i);
|
|
||||||
//ugh just start over
|
|
||||||
i = 0;
|
|
||||||
w = dW;
|
|
||||||
x = dX;
|
|
||||||
framecount = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ambe_fr[*w][*x] = (1 & dibit);
|
fprintf (stderr, "\n");
|
||||||
w++;
|
|
||||||
x++;
|
}
|
||||||
|
|
||||||
}
|
void processDSTAR_HD(dsd_opts * opts, dsd_state * state)
|
||||||
|
{
|
||||||
|
|
||||||
processMbeFrame(opts, state, NULL, ambe_fr, NULL);
|
int i;
|
||||||
|
int radioheaderbuffer[660];
|
||||||
// data frame - 24 bits
|
|
||||||
for (i = 73; i < 97; i++) {
|
for (i = 0; i < 660; i++)
|
||||||
dibit = getDibit(opts, state);
|
radioheaderbuffer[i] = getDibit(opts, state);
|
||||||
bitbuffer <<= 1;
|
|
||||||
if (dibit == 1) {
|
dstar_header_decode(state, radioheaderbuffer);
|
||||||
bitbuffer |= 0x01;
|
processDSTAR(opts, state);
|
||||||
}
|
|
||||||
if ((bitbuffer & 0x00FFFFFF) == 0x00AAB468) {
|
}
|
||||||
// looking if we're slipping bits
|
|
||||||
if (i != 96) {
|
//first 24-bits of the larger scramble table
|
||||||
fprintf (stderr, "sync after i=%d\n", i);
|
uint8_t sd_d[48] =
|
||||||
i = 96;
|
{0,0,0,0, //0
|
||||||
}
|
1,1,1,0, //E
|
||||||
}
|
1,1,1,1, //F
|
||||||
}
|
0,0,1,0, //2
|
||||||
|
1,1,0,0, //C
|
||||||
slowdata[0] = (bitbuffer >> 16) & 0x000000FF;
|
1,0,0,1};//9
|
||||||
slowdata[1] = (bitbuffer >> 8) & 0x000000FF;
|
|
||||||
slowdata[2] = (bitbuffer) & 0x000000FF;
|
//no so simplified Slow Data
|
||||||
slowdata[3] = 0;
|
void processDSTAR_SD(dsd_opts * opts, dsd_state * state, uint8_t * sd)
|
||||||
|
{
|
||||||
if ((bitbuffer & 0x00FFFFFF) == 0x00AAB468) {
|
|
||||||
//We got sync!
|
int i, j, len;
|
||||||
//fprintf (stderr, "Sync on framecount = %d\n", framecount);
|
uint8_t sd_bytes[60]; //raw slow data packed into bytes
|
||||||
sync_missed = 0;
|
uint8_t hd_bytes[60]; //storage for packed bytes sans the header indicator byte (0x55)
|
||||||
} else if ((bitbuffer & 0x00FFFFFF) == 0xAAAAAA) {
|
uint8_t payload[60]; //unchanged copy of sd_bytes for payload printing
|
||||||
//End of transmission
|
uint8_t rev[480]; //storage for reversing the bit order
|
||||||
fprintf (stderr, "End of transmission\n");
|
memset (sd_bytes, 0, sizeof(sd_bytes));
|
||||||
goto end;
|
memset (hd_bytes, 0, sizeof(hd_bytes));
|
||||||
} else if (framecount % 21 == 0) {
|
memset (payload, 0, sizeof(payload));
|
||||||
fprintf (stderr, "Missed sync on framecount = %d, value = %x/%x/%x\n",
|
|
||||||
framecount, slowdata[0], slowdata[1], slowdata[2]);
|
//apply the descrambler
|
||||||
sync_missed++;
|
for (i = 0; i < 480; i++)
|
||||||
} else if (framecount != 0 && (bitbuffer & 0x00FFFFFF) != 0x000000) {
|
sd[i] ^= sd_d[i%24];
|
||||||
slowdata[0] ^= 0x70;
|
|
||||||
slowdata[1] ^= 0x4f;
|
//reverse the bit order
|
||||||
slowdata[2] ^= 0x93;
|
for (i = 0; i < 480; i++)
|
||||||
//fprintf (stderr, "unscrambled- %02X %02X %02X %02X \n", slowdata[0], slowdata[1], slowdata[2], slowdata[3] );
|
rev[i] = sd[479-i];
|
||||||
|
for (i = 0; i < 480; i++)
|
||||||
} else if (framecount == 0) {
|
sd[i] = rev[i];
|
||||||
//fprintf (stderr, "never scrambled-%c\n",slowdata);
|
|
||||||
//fprintf (stderr, "never scrambled- %02X %02X %02X %02X \n", slowdata[0], slowdata[1], slowdata[2], slowdata[3] );
|
//load bytes by least significant byte to most significant byte
|
||||||
|
//as seen in "The Format of D-Star Slow Data" by Jonathan Naylor
|
||||||
}
|
for (i = 0; i < 60; i++)
|
||||||
for (short int i = 0; i < 4; i++)
|
sd_bytes[59-i] = (uint8_t)ConvertBitIntoBytes(&sd[i*8+0], 8);
|
||||||
{
|
|
||||||
if (slowdata[i] <= 0x7E && slowdata[i] >=0x20)
|
//make a copy to payload that won't be altered
|
||||||
{
|
memcpy (payload, sd_bytes, sizeof(payload));
|
||||||
//fprintf (stderr, "%c", slowdata[i]);
|
|
||||||
}
|
len = sd_bytes[0] & 0xF;
|
||||||
//else fprintf (stderr, ".");
|
len += 1;
|
||||||
}
|
|
||||||
//fprintf (stderr, "\n");
|
//load payload bytes without the header indicator byte
|
||||||
framecount++;
|
for (i = 0, j = 0; i < 50; i++)
|
||||||
|
{
|
||||||
//since we are in a while loop, run ncursesPrinter here.
|
j++;
|
||||||
if (opts->use_ncurses_terminal == 1)
|
hd_bytes[i] = sd_bytes[j];
|
||||||
{
|
if (j == len*1-1) j++;
|
||||||
ncursesPrinter(opts, state);
|
if (j == len*2-1) j++;
|
||||||
}
|
if (j == len*3-1) j++;
|
||||||
|
if (j == len*4-1) j++;
|
||||||
} //end while loop
|
if (j == len*5-1) j++;
|
||||||
|
if (j == len*6-1) j++;
|
||||||
end: if (opts->errorbars == 1) {
|
if (j == len*7-1) j++;
|
||||||
fprintf (stderr, "\n");
|
if (j == len*8-1) j++;
|
||||||
}
|
if (j == len*9-1) j++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void processDSTAR_HD(dsd_opts * opts, dsd_state * state) {
|
//works on header format -- may not work on others
|
||||||
|
uint16_t crc_ext = (hd_bytes[39] << 8) + hd_bytes[40];
|
||||||
int i, j;
|
uint16_t crc_cmp = calc_fcs(hd_bytes, 39);
|
||||||
int radioheaderbuffer[660];
|
|
||||||
|
char str1[9];
|
||||||
for (j = 0; j < 660; j++) {
|
char str2[9];
|
||||||
radioheaderbuffer[j] = getDibit(opts, state);
|
char str3[9];
|
||||||
}
|
char str4[13];
|
||||||
|
char strf[60];
|
||||||
// Note: These routines contain GPLed code. Remove if you object to that.
|
char strt[60];
|
||||||
// Due to this, they are in a separate source file.
|
memset (strf, 0x20, sizeof(strf));
|
||||||
dstar_header_decode(state, radioheaderbuffer);
|
memset (strt, 0x20, sizeof(strf));
|
||||||
|
memcpy (str1, hd_bytes+3, 8);
|
||||||
//We officially have sync now, so just pass on to the above routine:
|
memcpy (str2, hd_bytes+11, 8);
|
||||||
|
memcpy (str3, hd_bytes+19, 8);
|
||||||
processDSTAR(opts, state);
|
memcpy (str4, hd_bytes+27, 12);
|
||||||
|
str1[8] = '\0';
|
||||||
|
str2[8] = '\0';
|
||||||
|
str3[8] = '\0';
|
||||||
|
str4[12] = '\0';
|
||||||
|
|
||||||
|
//safety check, don't want to load nasty values into the strings
|
||||||
|
for (i = 1; i < 60; i++)
|
||||||
|
{
|
||||||
|
//substitue non-ascii characters for spaces
|
||||||
|
if (sd_bytes[i] < 0x20)
|
||||||
|
sd_bytes[i] = 0x20;
|
||||||
|
else if (sd_bytes[i] > 0x7E)
|
||||||
|
sd_bytes[i] = 0x20;
|
||||||
|
|
||||||
|
//substitue non-ascii characters for spaces
|
||||||
|
if (hd_bytes[i] < 0x20)
|
||||||
|
hd_bytes[i] = 0x20;
|
||||||
|
else if (hd_bytes[i] > 0x7E)
|
||||||
|
hd_bytes[i] = 0x20;
|
||||||
|
|
||||||
|
//replace terminating ffffffff (repeating 0x66 values) with NULL (0x00)
|
||||||
|
if (i < 59)
|
||||||
|
{
|
||||||
|
if (sd_bytes[i] == 0x66 && sd_bytes[i+1] == 0x66)
|
||||||
|
sd_bytes[i] = 0x00;
|
||||||
|
|
||||||
|
if (hd_bytes[i] == 0x66 && hd_bytes[i+1] == 0x66)
|
||||||
|
hd_bytes[i] = 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 59 && sd_bytes[i] == 0x66)
|
||||||
|
sd_bytes[i] = 0x00;
|
||||||
|
|
||||||
|
if (i == 59 && hd_bytes[i] == 0x66)
|
||||||
|
hd_bytes[i] = 0x00;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy (strf, sd_bytes+1, 58); //copy the entire thing as a string -- full
|
||||||
|
memcpy (strt, hd_bytes+1, 48); //copy the entire thing as a string -- truncated
|
||||||
|
strf[59] = '\0';
|
||||||
|
strt[59] = '\0';
|
||||||
|
|
||||||
|
char type[7]; char temp[8]; char tempa[8]; char chungus[80]; UNUSED(chungus);
|
||||||
|
memset (chungus, 0, sizeof(chungus));
|
||||||
|
memset (temp, 0, sizeof(temp));
|
||||||
|
memset (tempa, 0, sizeof(tempa));
|
||||||
|
memset (type, 0, sizeof(type));
|
||||||
|
memcpy (type, sd_bytes+1, 5);
|
||||||
|
type[6] = '\0';
|
||||||
|
|
||||||
|
if (sd_bytes[0] == 0x55) //header format
|
||||||
|
{
|
||||||
|
|
||||||
|
if (crc_cmp == crc_ext)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " RPT 2: %s", str1);
|
||||||
|
fprintf (stderr, " RPT 1: %s", str2);
|
||||||
|
fprintf (stderr, " DST: %s", str3);
|
||||||
|
fprintf (stderr, " SRC: %s", str4);
|
||||||
|
|
||||||
|
//check flags for info
|
||||||
|
if (sd_bytes[1] & 0x80) fprintf (stderr, " DATA");
|
||||||
|
if (sd_bytes[1] & 0x40) fprintf (stderr, " REPEATER");
|
||||||
|
if (sd_bytes[1] & 0x20) fprintf (stderr, " INTERRUPTED");
|
||||||
|
if (sd_bytes[1] & 0x10) fprintf (stderr, " CONTROL SIGNAL");
|
||||||
|
if (sd_bytes[1] & 0x08) fprintf (stderr, " URGENT");
|
||||||
|
|
||||||
|
memcpy (state->dstar_rpt2, str1, sizeof(str1));
|
||||||
|
memcpy (state->dstar_rpt1, str2, sizeof(str2));
|
||||||
|
memcpy (state->dstar_dst, str3, sizeof(str3));
|
||||||
|
memcpy (state->dstar_src, str4, sizeof(str4));
|
||||||
|
}
|
||||||
|
else fprintf (stderr, " SLOW DATA - HEADER FORMAT (CRC ERR)");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else //any other format (text, aprs, etc)
|
||||||
|
{
|
||||||
|
//fixed-form at 5 bytes, evaluate
|
||||||
|
if (sd_bytes[0] == 0x35)
|
||||||
|
{
|
||||||
|
|
||||||
|
//might as well just dump both of these outputs
|
||||||
|
if (strcmp(type, "$$CRC") == 0) //APRS
|
||||||
|
{
|
||||||
|
memset (strt, 0x20, sizeof(strt)); //space fill
|
||||||
|
fprintf (stderr, " DATA: ");
|
||||||
|
for (i = 1; i < 59; i++)
|
||||||
|
{
|
||||||
|
if (i%6==0) i++;
|
||||||
|
if ( (sd_bytes[i] > 0x19) && (sd_bytes[i] < 0x7F) )
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%c", sd_bytes[i]);
|
||||||
|
// strt[i] = sd_bytes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// strt[59] = '\0';
|
||||||
|
// memcpy (state->dstar_gps, strt, sizeof(strt));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(type, "$$CRC") == 0) //APRS
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n APRS - ");
|
||||||
|
sprintf (state->dstar_gps, "APRS - ");
|
||||||
|
uint8_t aprs[51];
|
||||||
|
int k = 0;
|
||||||
|
memset (aprs, 0, sizeof(aprs));
|
||||||
|
//reshuffle (yet again) into an easier format to manipulate for aprs
|
||||||
|
for (i = 1; i < 59; i++)
|
||||||
|
{
|
||||||
|
if (i % 6 == 0 ) i++;
|
||||||
|
aprs[k] = sd_bytes[i];
|
||||||
|
if ( (aprs[k] > 0x19) && (aprs[k] < 0x7F) )
|
||||||
|
aprs[i] = 0x20;
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
//start by looking for the ! exclamation point token
|
||||||
|
int start = -1;
|
||||||
|
for (i = 30; i < 40; i++)
|
||||||
|
{
|
||||||
|
if (aprs[i] == 0x21)
|
||||||
|
{
|
||||||
|
start = i+1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//if not found, then skip (bad decode)
|
||||||
|
if (start == -1)
|
||||||
|
goto SKIP;
|
||||||
|
|
||||||
|
//debug
|
||||||
|
// fprintf (stderr, "S: %d; ", start); //38, or 39
|
||||||
|
|
||||||
|
//LAT
|
||||||
|
memcpy (temp, aprs+start, 2);
|
||||||
|
start += 2;
|
||||||
|
fprintf (stderr, "Lat: %sd ", temp);
|
||||||
|
strcat (state->dstar_gps, "Lat: ");
|
||||||
|
strcat (state->dstar_gps, temp);
|
||||||
|
strcat (state->dstar_gps, "d ");
|
||||||
|
memcpy (temp, aprs+start, 2);
|
||||||
|
start += 3;
|
||||||
|
fprintf (stderr, "%sm ", temp);
|
||||||
|
strcat (state->dstar_gps, temp);
|
||||||
|
strcat (state->dstar_gps, "m ");
|
||||||
|
memcpy (temp, aprs+start, 2);
|
||||||
|
start+=1;
|
||||||
|
fprintf (stderr, "%ss ", temp);
|
||||||
|
strcat (state->dstar_gps, temp);
|
||||||
|
strcat (state->dstar_gps, "s ");
|
||||||
|
fprintf (stderr, "%c", aprs[start]);
|
||||||
|
if (sd_bytes[start] == 0x4E)
|
||||||
|
strcat (state->dstar_gps, "N ");
|
||||||
|
else if (sd_bytes[start] == 0x53)
|
||||||
|
strcat (state->dstar_gps, "S ");
|
||||||
|
fprintf (stderr, "; ");
|
||||||
|
|
||||||
|
//LON
|
||||||
|
start += 3;
|
||||||
|
memcpy (tempa, aprs+start, 3);
|
||||||
|
start += 3;
|
||||||
|
fprintf (stderr, "Lon: %sd ", tempa);
|
||||||
|
strcat (state->dstar_gps, "Lon: ");
|
||||||
|
strcat (state->dstar_gps, tempa);
|
||||||
|
strcat (state->dstar_gps, "d ");
|
||||||
|
memcpy (temp, aprs+start, 2);
|
||||||
|
start += 3; //3
|
||||||
|
fprintf (stderr, "%sm ", temp);
|
||||||
|
strcat (state->dstar_gps, temp);
|
||||||
|
strcat (state->dstar_gps, "m ");
|
||||||
|
memcpy (temp, aprs+start, 2);
|
||||||
|
start += 2;
|
||||||
|
fprintf (stderr, "%ss ", temp);
|
||||||
|
strcat (state->dstar_gps, temp);
|
||||||
|
strcat (state->dstar_gps, "s ");
|
||||||
|
fprintf (stderr, "%c", aprs[start]);
|
||||||
|
if (aprs[start] == 0x45)
|
||||||
|
strcat (state->dstar_gps, "E ");
|
||||||
|
else if (aprs[start] == 0x57)
|
||||||
|
strcat (state->dstar_gps, "W ");
|
||||||
|
fprintf (stderr, "; ");
|
||||||
|
|
||||||
|
//Got the feeling nobody will actually ever see this message, but who knows
|
||||||
|
// fprintf(stderr, "\n DEV NOTE: If you see this message, but incorrect lat/lon,\n please submit samples to https://github.com/lwvmobile/dsd-fme/issues/164 ");
|
||||||
|
|
||||||
|
SKIP: ; //do nothing
|
||||||
|
|
||||||
|
//this seems to work okay with a few samples, but wouldn't be surprised if it broke down
|
||||||
|
//randomly on different users, depending on location and what else in in $$CRC
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// fprintf (stderr, " %s: ", type);
|
||||||
|
memset (strt, 0x20, sizeof(strt)); //space fill
|
||||||
|
fprintf (stderr, " TEXT: ");
|
||||||
|
for (i = 1; i < 59; i++)
|
||||||
|
{
|
||||||
|
if (i%6==0) i++;
|
||||||
|
if ( (sd_bytes[i] > 0x19) && (sd_bytes[i] < 0x7F) )
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%c", sd_bytes[i]);
|
||||||
|
strt[i] = sd_bytes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strt[59] = '\0';
|
||||||
|
memcpy (state->dstar_txt, strt, sizeof(strt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//free-form text at 5 bytes, truncate every 6th position
|
||||||
|
else if (sd_bytes[0] == 0x40)
|
||||||
|
{
|
||||||
|
memset (strt, 0x20, sizeof(strt)); //space fill
|
||||||
|
fprintf (stderr, " TEXT: ");
|
||||||
|
for (i = 1; i < 59; i++)
|
||||||
|
{
|
||||||
|
if (i%6==0) i++;
|
||||||
|
if ( (sd_bytes[i] > 0x19) && (sd_bytes[i] < 0x7F) )
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%c", sd_bytes[i]);
|
||||||
|
strt[i] = sd_bytes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strt[59] = '\0';
|
||||||
|
memcpy (state->dstar_txt, strt, sizeof(strt));
|
||||||
|
}
|
||||||
|
//anything else
|
||||||
|
else //print entire thing
|
||||||
|
{
|
||||||
|
fprintf (stderr, " _UNK:");
|
||||||
|
fprintf (stderr, " %s", strf);
|
||||||
|
// memcpy (state->dstar_txt, strf, sizeof(strf)); //don't copy the unknown garbo strings
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->payload == 1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n SD: ");
|
||||||
|
for (i = 0; i < 60; i++)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "[%02X]", payload[i]);
|
||||||
|
if (i == 29) fprintf (stderr, "\n ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crc_cmp != crc_ext)
|
||||||
|
fprintf (stderr, "CRC - EXT: %04X CMP: %04X", crc_ext, crc_cmp);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* This code is taken largely from on1arf's GMSK code. Original copyright below:
|
* This code is taken largely from on1arf's GMSK code. Original copyright below:
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Copyright (C) 2011 by Kristoff Bonne, ON1ARF
|
* Copyright (C) 2011 by Kristoff Bonne, ON1ARF
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
@@ -13,75 +13,84 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fcs.h"
|
|
||||||
#include "descramble.h"
|
#include "descramble.h"
|
||||||
#include "dstar_header.h"
|
#include "dstar_header.h"
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
// #include "fcs.h" //error: multiple definition of `calc_fcs'; CMakeFiles/dsd-fme.dir/src/dstar.c.o:dstar.c:(.text+0x0): first defined here
|
||||||
|
|
||||||
void dstar_header_decode(dsd_state * state, int radioheaderbuffer[660]) {
|
void dstar_header_decode(dsd_state * state, int radioheaderbuffer[660])
|
||||||
|
{
|
||||||
int radioheaderbuffer2[660];
|
int radioheaderbuffer2[660];
|
||||||
unsigned char radioheader[41];
|
char radioheader[41];
|
||||||
int octetcount, bitcount, loop;
|
int octetcount, bitcount, loop;
|
||||||
unsigned char bit2octet[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
|
unsigned char bit2octet[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
|
||||||
unsigned int FCSinheader;
|
|
||||||
unsigned int FCScalculated;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
scramble(radioheaderbuffer, radioheaderbuffer2);
|
scramble(radioheaderbuffer, radioheaderbuffer2);
|
||||||
deinterleave(radioheaderbuffer2, radioheaderbuffer);
|
deinterleave(radioheaderbuffer2, radioheaderbuffer);
|
||||||
len = FECdecoder(radioheaderbuffer, radioheaderbuffer2);
|
FECdecoder(radioheaderbuffer, radioheaderbuffer2);
|
||||||
memset(radioheader, 0, 41);
|
memset(radioheader, 0, 41);
|
||||||
//memset(state->dstarradioheader, 0, 41);
|
|
||||||
// note we receive 330 bits, but we only use 328 of them (41 octets)
|
// note we receive 330 bits, but we only use 328 of them (41 octets)
|
||||||
// bits 329 and 330 are unused
|
// bits 329 and 330 are unused
|
||||||
octetcount = 0;
|
octetcount = 0;
|
||||||
bitcount = 0;
|
bitcount = 0;
|
||||||
for (loop = 0; loop < 328; loop++) {
|
for (loop = 0; loop < 328; loop++)
|
||||||
if (radioheaderbuffer2[loop]) {
|
{
|
||||||
|
if (radioheaderbuffer2[loop])
|
||||||
radioheader[octetcount] |= bit2octet[bitcount];
|
radioheader[octetcount] |= bit2octet[bitcount];
|
||||||
};
|
|
||||||
bitcount++;
|
bitcount++;
|
||||||
// increase octetcounter and reset bitcounter every 8 bits
|
// increase octetcounter and reset bitcounter every 8 bits
|
||||||
if (bitcount >= 8) {
|
if (bitcount >= 8)
|
||||||
|
{
|
||||||
octetcount++;
|
octetcount++;
|
||||||
bitcount = 0;
|
bitcount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//state->dstarradioheader = radioheader;
|
|
||||||
|
|
||||||
for (short i = 0; i < 41; i++)
|
char str1[9];
|
||||||
{
|
char str2[9];
|
||||||
state->dstarradioheader[i] = radioheader[i];
|
char str3[9];
|
||||||
}
|
char str4[13];
|
||||||
|
|
||||||
|
memcpy (str1, radioheader+3, 8);
|
||||||
|
memcpy (str2, radioheader+11, 8);
|
||||||
|
memcpy (str3, radioheader+19, 8);
|
||||||
|
memcpy (str4, radioheader+27, 12);
|
||||||
|
|
||||||
// print header
|
str1[8] = '\0';
|
||||||
fprintf (stderr, "\nDSTAR HEADER: ");
|
str2[8] = '\0';
|
||||||
//fprintf (stderr, "FLAG1: %02X - FLAG2: %02X - FLAG3: %02X\n", radioheader[0],
|
str3[8] = '\0';
|
||||||
// radioheader[1], radioheader[2]);
|
str4[12] = '\0';
|
||||||
fprintf (stderr, "RPT 2: %c%c%c%c%c%c%c%c ", radioheader[3], radioheader[4],
|
|
||||||
radioheader[5], radioheader[6], radioheader[7], radioheader[8],
|
|
||||||
radioheader[9], radioheader[10]);
|
|
||||||
fprintf (stderr, "RPT 1: %c%c%c%c%c%c%c%c ", radioheader[11], radioheader[12],
|
|
||||||
radioheader[13], radioheader[14], radioheader[15], radioheader[16],
|
|
||||||
radioheader[17], radioheader[18]);
|
|
||||||
fprintf (stderr, "YOUR: %c%c%c%c%c%c%c%c ", radioheader[19], radioheader[20],
|
|
||||||
radioheader[21], radioheader[22], radioheader[23], radioheader[24],
|
|
||||||
radioheader[25], radioheader[26]);
|
|
||||||
fprintf (stderr, "MY: %c%c%c%c%c%c%c%c/%c%c%c%c\n", radioheader[27],
|
|
||||||
radioheader[28], radioheader[29], radioheader[30], radioheader[31],
|
|
||||||
radioheader[32], radioheader[33], radioheader[34], radioheader[35],
|
|
||||||
radioheader[36], radioheader[37], radioheader[38]);
|
|
||||||
|
|
||||||
//FCSinheader = ((radioheader[39] << 8) | radioheader[40]) & 0xFFFF;
|
//TODO: Add fcs_calc to header as well
|
||||||
//FCScalculated = calc_fcs((unsigned char*) radioheader, 39);
|
// uint16_t crc_ext = (radioheader[39] << 8) + radioheader[40];
|
||||||
//fprintf (stderr, "Check sum = %04X ", FCSinheader);
|
// uint16_t crc_cmp = calc_fcs(radioheader, 39);
|
||||||
//if (FCSinheader == FCScalculated) {
|
|
||||||
// fprintf (stderr, "(OK)\n");
|
//debug
|
||||||
//} else {
|
// fprintf (stderr, "\n HD: ");
|
||||||
// fprintf (stderr, "(NOT OK- Calculated FCS = %04X)\n", FCScalculated);
|
// for (int i = 0; i < 40; i++)
|
||||||
//}; // end else - if
|
// fprintf (stderr, "%02X ", radioheader[i]);
|
||||||
|
// fprintf (stderr, "\n");
|
||||||
|
|
||||||
|
fprintf (stderr, " RPT 2: %s", str1);
|
||||||
|
fprintf (stderr, " RPT 1: %s", str2);
|
||||||
|
fprintf (stderr, " DST: %s", str3);
|
||||||
|
fprintf (stderr, " SRC: %s", str4);
|
||||||
|
|
||||||
|
//check flags for info
|
||||||
|
if (radioheader[0] & 0x80) fprintf (stderr, " DATA");
|
||||||
|
if (radioheader[0] & 0x40) fprintf (stderr, " REPEATER");
|
||||||
|
if (radioheader[0] & 0x20) fprintf (stderr, " INTERRUPTED");
|
||||||
|
if (radioheader[0] & 0x10) fprintf (stderr, " CONTROL SIGNAL");
|
||||||
|
if (radioheader[0] & 0x08) fprintf (stderr, " URGENT");
|
||||||
|
|
||||||
|
memcpy (state->dstar_rpt2, str1, sizeof(str1));
|
||||||
|
memcpy (state->dstar_rpt1, str2, sizeof(str2));
|
||||||
|
memcpy (state->dstar_dst, str3, sizeof(str3));
|
||||||
|
memcpy (state->dstar_src, str4, sizeof(str4));
|
||||||
|
|
||||||
|
//TODO: Call History for DSTAR
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -332,7 +332,8 @@ decode_bch()
|
|||||||
{
|
{
|
||||||
register int i, j, u, q, t2, count = 0, syn_error = 0;
|
register int i, j, u, q, t2, count = 0, syn_error = 0;
|
||||||
int elp[1026][1024], d[1026], l[1026], u_lu[1026], s[1025];
|
int elp[1026][1024], d[1026], l[1026], u_lu[1026], s[1025];
|
||||||
int root[200], loc[200], err[1024], reg[201];
|
int root[200], loc[200], reg[201];
|
||||||
|
(void)(root);
|
||||||
|
|
||||||
t2 = 2 * t;
|
t2 = 2 * t;
|
||||||
|
|
||||||
@@ -435,10 +436,10 @@ decode_bch()
|
|||||||
d[u + 1] = alpha_to[s[u + 1]];
|
d[u + 1] = alpha_to[s[u + 1]];
|
||||||
else
|
else
|
||||||
d[u + 1] = 0;
|
d[u + 1] = 0;
|
||||||
for (i = 1; i <= l[u + 1]; i++)
|
for (i = 1; i <= l[u + 1]; i++)
|
||||||
if ((s[u + 1 - i] != -1) && (elp[u + 1][i] != 0))
|
if ((s[u + 1 - i] != -1) && (elp[u + 1][i] != 0))
|
||||||
d[u + 1] ^= alpha_to[(s[u + 1 - i]
|
d[u + 1] ^= alpha_to[(s[u + 1 - i]
|
||||||
+ index_of[elp[u + 1][i]]) % n];
|
+ index_of[elp[u + 1][i]]) % n];
|
||||||
/* put d[u+1] into index form */
|
/* put d[u+1] into index form */
|
||||||
d[u + 1] = index_of[d[u + 1]];
|
d[u + 1] = index_of[d[u + 1]];
|
||||||
}
|
}
|
||||||
@@ -480,7 +481,8 @@ decode_bch()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long int messagepp = 0x0;
|
//bugfix on 32-bit cygwin, probably overflowed here
|
||||||
|
unsigned long long int messagepp = 0x0;
|
||||||
|
|
||||||
//very simplified version, just to encode, get frame and compare
|
//very simplified version, just to encode, get frame and compare
|
||||||
unsigned long long int edacs_bch (unsigned long long int message)
|
unsigned long long int edacs_bch (unsigned long long int message)
|
||||||
|
|||||||
2247
src/edacs-fme.c
295
src/ez.cpp
@@ -178,146 +178,167 @@ int ez_rs28_sacch (int payload[180], int parity[132])
|
|||||||
return (ec);
|
return (ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
//I-ISCH Lookup section, borrowed from OP25
|
//std::map <std::uint64_t, int> isch_map;
|
||||||
std::map <std::string, int> isch_map;
|
std::map <uint64_t, int> isch_map; //User Noted GCC potential compiler error fix on BB OP25
|
||||||
void map_isch()
|
void map_isch()
|
||||||
{
|
{
|
||||||
isch_map["184229d461"] = 0;
|
isch_map[0x184229d461ULL] = 0;
|
||||||
isch_map["18761451f6"] = 1;
|
isch_map[0x18761451f6ULL] = 1;
|
||||||
isch_map["181ae27e2f"] = 2;
|
isch_map[0x181ae27e2fULL] = 2;
|
||||||
isch_map["182edffbb8"] = 3;
|
isch_map[0x182edffbb8ULL] = 3;
|
||||||
isch_map["18df8a7510"] = 4;
|
isch_map[0x18df8a7510ULL] = 4;
|
||||||
isch_map["18ebb7f087"] = 5;
|
isch_map[0x18ebb7f087ULL] = 5;
|
||||||
isch_map["188741df5e"] = 6;
|
isch_map[0x188741df5eULL] = 6;
|
||||||
isch_map["18b37c5ac9"] = 7;
|
isch_map[0x18b37c5ac9ULL] = 7;
|
||||||
isch_map["1146a44f13"] = 8;
|
isch_map[0x1146a44f13ULL] = 8;
|
||||||
isch_map["117299ca84"] = 9;
|
isch_map[0x117299ca84ULL] = 9;
|
||||||
isch_map["111e6fe55d"] = 10;
|
isch_map[0x111e6fe55dULL] = 10;
|
||||||
isch_map["112a5260ca"] = 11;
|
isch_map[0x112a5260caULL] = 11;
|
||||||
isch_map["11db07ee62"] = 12;
|
isch_map[0x11db07ee62ULL] = 12;
|
||||||
isch_map["11ef3a6bf5"] = 13;
|
isch_map[0x11ef3a6bf5ULL] = 13;
|
||||||
isch_map["1183cc442c"] = 14;
|
isch_map[0x1183cc442cULL] = 14;
|
||||||
isch_map["11b7f1c1bb"] = 15;
|
isch_map[0x11b7f1c1bbULL] = 15;
|
||||||
isch_map["1a4a2e239e"] = 16;
|
isch_map[0x1a4a2e239eULL] = 16;
|
||||||
isch_map["1a7e13a609"] = 17;
|
isch_map[0x1a7e13a609ULL] = 17;
|
||||||
isch_map["1a12e589d0"] = 18;
|
isch_map[0x1a12e589d0ULL] = 18;
|
||||||
isch_map["1a26d80c47"] = 19;
|
isch_map[0x1a26d80c47ULL] = 19;
|
||||||
isch_map["1ad78d82ef"] = 20;
|
isch_map[0x1ad78d82efULL] = 20;
|
||||||
isch_map["1ae3b00778"] = 21;
|
isch_map[0x1ae3b00778ULL] = 21;
|
||||||
isch_map["1a8f4628a1"] = 22;
|
isch_map[0x1a8f4628a1ULL] = 22;
|
||||||
isch_map["1abb7bad36"] = 23;
|
isch_map[0x1abb7bad36ULL] = 23;
|
||||||
isch_map["134ea3b8ec"] = 24;
|
isch_map[0x134ea3b8ecULL] = 24;
|
||||||
isch_map["137a9e3d7b"] = 25;
|
isch_map[0x137a9e3d7bULL] = 25;
|
||||||
isch_map["13166812a2"] = 26;
|
isch_map[0x13166812a2ULL] = 26;
|
||||||
isch_map["1322559735"] = 27;
|
isch_map[0x1322559735ULL] = 27;
|
||||||
isch_map["13d300199d"] = 28;
|
isch_map[0x13d300199dULL] = 28;
|
||||||
isch_map["13e73d9c0a"] = 29;
|
isch_map[0x13e73d9c0aULL] = 29;
|
||||||
isch_map["138bcbb3d3"] = 30;
|
isch_map[0x138bcbb3d3ULL] = 30;
|
||||||
isch_map["13bff63644"] = 31;
|
isch_map[0x13bff63644ULL] = 31;
|
||||||
isch_map["1442f705ef"] = 32;
|
isch_map[0x1442f705efULL] = 32;
|
||||||
isch_map["1476ca8078"] = 33;
|
isch_map[0x1476ca8078ULL] = 33;
|
||||||
isch_map["141a3cafa1"] = 34;
|
isch_map[0x141a3cafa1ULL] = 34;
|
||||||
isch_map["142e012a36"] = 35;
|
isch_map[0x142e012a36ULL] = 35;
|
||||||
isch_map["14df54a49e"] = 36;
|
isch_map[0x14df54a49eULL] = 36;
|
||||||
isch_map["14eb692109"] = 37;
|
isch_map[0x14eb692109ULL] = 37;
|
||||||
isch_map["14879f0ed0"] = 38;
|
isch_map[0x14879f0ed0ULL] = 38;
|
||||||
isch_map["14b3a28b47"] = 39;
|
isch_map[0x14b3a28b47ULL] = 39;
|
||||||
isch_map["1d467a9e9d"] = 40;
|
isch_map[0x1d467a9e9dULL] = 40;
|
||||||
isch_map["1d72471b0a"] = 41;
|
isch_map[0x1d72471b0aULL] = 41;
|
||||||
isch_map["1d1eb134d3"] = 42;
|
isch_map[0x1d1eb134d3ULL] = 42;
|
||||||
isch_map["1d2a8cb144"] = 43;
|
isch_map[0x1d2a8cb144ULL] = 43;
|
||||||
isch_map["1ddbd93fec"] = 44;
|
isch_map[0x1ddbd93fecULL] = 44;
|
||||||
isch_map["1defe4ba7b"] = 45;
|
isch_map[0x1defe4ba7bULL] = 45;
|
||||||
isch_map["1d831295a2"] = 46;
|
isch_map[0x1d831295a2ULL] = 46;
|
||||||
isch_map["1db72f1035"] = 47;
|
isch_map[0x1db72f1035ULL] = 47;
|
||||||
isch_map["164af0f210"] = 48;
|
isch_map[0x164af0f210ULL] = 48;
|
||||||
isch_map["167ecd7787"] = 49;
|
isch_map[0x167ecd7787ULL] = 49;
|
||||||
isch_map["16123b585e"] = 50;
|
isch_map[0x16123b585eULL] = 50;
|
||||||
isch_map["162606ddc9"] = 51;
|
isch_map[0x162606ddc9ULL] = 51;
|
||||||
isch_map["16d7535361"] = 52;
|
isch_map[0x16d7535361ULL] = 52;
|
||||||
isch_map["16e36ed6f6"] = 53;
|
isch_map[0x16e36ed6f6ULL] = 53;
|
||||||
isch_map["168f98f92f"] = 54;
|
isch_map[0x168f98f92fULL] = 54;
|
||||||
isch_map["16bba57cb8"] = 55;
|
isch_map[0x16bba57cb8ULL] = 55;
|
||||||
isch_map["1f4e7d6962"] = 56;
|
isch_map[0x1f4e7d6962ULL] = 56;
|
||||||
isch_map["1f7a40ecf5"] = 57;
|
isch_map[0x1f7a40ecf5ULL] = 57;
|
||||||
isch_map["1f16b6c32c"] = 58;
|
isch_map[0x1f16b6c32cULL] = 58;
|
||||||
isch_map["1f228b46bb"] = 59;
|
isch_map[0x1f228b46bbULL] = 59;
|
||||||
isch_map["1fd3dec813"] = 60;
|
isch_map[0x1fd3dec813ULL] = 60;
|
||||||
isch_map["1fe7e34d84"] = 61;
|
isch_map[0x1fe7e34d84ULL] = 61;
|
||||||
isch_map["1f8b15625d"] = 62;
|
isch_map[0x1f8b15625dULL] = 62;
|
||||||
isch_map["1fbf28e7ca"] = 63;
|
isch_map[0x1fbf28e7caULL] = 63;
|
||||||
isch_map["84d62c339"] = 64;
|
isch_map[0x084d62c339ULL] = 64;
|
||||||
isch_map["8795f46ae"] = 65;
|
isch_map[0x08795f46aeULL] = 65;
|
||||||
isch_map["815a96977"] = 66;
|
isch_map[0x0815a96977ULL] = 66;
|
||||||
isch_map["82194ece0"] = 67;
|
isch_map[0x082194ece0ULL] = 67;
|
||||||
isch_map["8d0c16248"] = 68;
|
isch_map[0x08d0c16248ULL] = 68;
|
||||||
isch_map["8e4fce7df"] = 69;
|
isch_map[0x08e4fce7dfULL] = 69;
|
||||||
isch_map["8880ac806"] = 70;
|
isch_map[0x08880ac806ULL] = 70;
|
||||||
isch_map["8bc374d91"] = 71;
|
isch_map[0x08bc374d91ULL] = 71;
|
||||||
isch_map["149ef584b"] = 72;
|
isch_map[0x0149ef584bULL] = 72;
|
||||||
isch_map["17dd2dddc"] = 73;
|
isch_map[0x017dd2dddcULL] = 73;
|
||||||
isch_map["11124f205"] = 74;
|
isch_map[0x011124f205ULL] = 74;
|
||||||
isch_map["125197792"] = 75;
|
isch_map[0x0125197792ULL] = 75;
|
||||||
isch_map["1d44cf93a"] = 76;
|
isch_map[0x01d44cf93aULL] = 76;
|
||||||
isch_map["1e0717cad"] = 77;
|
isch_map[0x01e0717cadULL] = 77;
|
||||||
isch_map["18c875374"] = 78;
|
isch_map[0x018c875374ULL] = 78;
|
||||||
isch_map["1b8bad6e3"] = 79;
|
isch_map[0x01b8bad6e3ULL] = 79;
|
||||||
isch_map["a456534c6"] = 80;
|
isch_map[0x0a456534c6ULL] = 80;
|
||||||
isch_map["a7158b151"] = 81;
|
isch_map[0x0a7158b151ULL] = 81;
|
||||||
isch_map["a1dae9e88"] = 82;
|
isch_map[0x0a1dae9e88ULL] = 82;
|
||||||
isch_map["a29931b1f"] = 83;
|
isch_map[0x0a29931b1fULL] = 83;
|
||||||
isch_map["ad8c695b7"] = 84;
|
isch_map[0x0ad8c695b7ULL] = 84;
|
||||||
isch_map["aecfb1020"] = 85;
|
isch_map[0x0aecfb1020ULL] = 85;
|
||||||
isch_map["a800d3ff9"] = 86;
|
isch_map[0x0a800d3ff9ULL] = 86;
|
||||||
isch_map["ab430ba6e"] = 87;
|
isch_map[0x0ab430ba6eULL] = 87;
|
||||||
isch_map["341e8afb4"] = 88;
|
isch_map[0x0341e8afb4ULL] = 88;
|
||||||
isch_map["375d52a23"] = 89;
|
isch_map[0x0375d52a23ULL] = 89;
|
||||||
isch_map["3192305fa"] = 90;
|
isch_map[0x03192305faULL] = 90;
|
||||||
isch_map["32d1e806d"] = 91;
|
isch_map[0x032d1e806dULL] = 91;
|
||||||
isch_map["3dc4b0ec5"] = 92;
|
isch_map[0x03dc4b0ec5ULL] = 92;
|
||||||
isch_map["3e8768b52"] = 93;
|
isch_map[0x03e8768b52ULL] = 93;
|
||||||
isch_map["38480a48b"] = 94;
|
isch_map[0x038480a48bULL] = 94;
|
||||||
isch_map["3b0bd211c"] = 95;
|
isch_map[0x03b0bd211cULL] = 95;
|
||||||
isch_map["44dbc12b7"] = 96;
|
isch_map[0x044dbc12b7ULL] = 96;
|
||||||
isch_map["479819720"] = 97;
|
isch_map[0x0479819720ULL] = 97;
|
||||||
isch_map["41577b8f9"] = 98;
|
isch_map[0x041577b8f9ULL] = 98;
|
||||||
isch_map["4214a3d6e"] = 99;
|
isch_map[0x04214a3d6eULL] = 99;
|
||||||
isch_map["4d01fb3c6"] = 100;
|
isch_map[0x04d01fb3c6ULL] = 100;
|
||||||
isch_map["4e4223651"] = 101;
|
isch_map[0x04e4223651ULL] = 101;
|
||||||
isch_map["488d41988"] = 102;
|
isch_map[0x0488d41988ULL] = 102;
|
||||||
isch_map["4bce99c1f"] = 103;
|
isch_map[0x04bce99c1fULL] = 103;
|
||||||
isch_map["d493189c5"] = 104;
|
isch_map[0x0d493189c5ULL] = 104;
|
||||||
isch_map["d7d0c0c52"] = 105;
|
isch_map[0x0d7d0c0c52ULL] = 105;
|
||||||
isch_map["d11fa238b"] = 106;
|
isch_map[0x0d11fa238bULL] = 106;
|
||||||
isch_map["d25c7a61c"] = 107;
|
isch_map[0x0d25c7a61cULL] = 107;
|
||||||
isch_map["dd49228b4"] = 108;
|
isch_map[0x0dd49228b4ULL] = 108;
|
||||||
isch_map["de0afad23"] = 109;
|
isch_map[0x0de0afad23ULL] = 109;
|
||||||
isch_map["d8c5982fa"] = 110;
|
isch_map[0x0d8c5982faULL] = 110;
|
||||||
isch_map["db864076d"] = 111;
|
isch_map[0x0db864076dULL] = 111;
|
||||||
isch_map["645bbe548"] = 112;
|
isch_map[0x0645bbe548ULL] = 112;
|
||||||
isch_map["6718660df"] = 113;
|
isch_map[0x06718660dfULL] = 113;
|
||||||
isch_map["61d704f06"] = 114;
|
isch_map[0x061d704f06ULL] = 114;
|
||||||
isch_map["6294dca91"] = 115;
|
isch_map[0x06294dca91ULL] = 115;
|
||||||
isch_map["6d8184439"] = 116;
|
isch_map[0x06d8184439ULL] = 116;
|
||||||
isch_map["6ec25c1ae"] = 117;
|
isch_map[0x06ec25c1aeULL] = 117;
|
||||||
isch_map["680d3ee77"] = 118;
|
isch_map[0x0680d3ee77ULL] = 118;
|
||||||
isch_map["6b4ee6be0"] = 119;
|
isch_map[0x06b4ee6be0ULL] = 119;
|
||||||
isch_map["f41367e3a"] = 120;
|
isch_map[0x0f41367e3aULL] = 120;
|
||||||
isch_map["f750bfbad"] = 121;
|
isch_map[0x0f750bfbadULL] = 121;
|
||||||
isch_map["f19fdd474"] = 122;
|
isch_map[0x0f19fdd474ULL] = 122;
|
||||||
isch_map["f2dc051e3"] = 123;
|
isch_map[0x0f2dc051e3ULL] = 123;
|
||||||
isch_map["fdc95df4b"] = 124;
|
isch_map[0x0fdc95df4bULL] = 124;
|
||||||
isch_map["fe8a85adc"] = 125;
|
isch_map[0x0fe8a85adcULL] = 125;
|
||||||
isch_map["f845e7505"] = 126;
|
isch_map[0x0f845e7505ULL] = 126;
|
||||||
isch_map["fb063f092"] = 127;
|
isch_map[0x0fb063f092ULL] = 127;
|
||||||
|
isch_map[0x575d57f7ffULL] = -2; // S-ISCH
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long long int isch_lookup (unsigned long long int isch)
|
//I-ISCH Lookup, borrowed from OP25 (now w/ error correction)
|
||||||
|
int isch_lookup (uint64_t isch)
|
||||||
{
|
{
|
||||||
map_isch(); //initialize the lookup map
|
map_isch(); //initialize the lookup map
|
||||||
char s[64];
|
int decoded = -2;
|
||||||
unsigned long long int decoded = -2; //initialize lookup to an invalid number
|
|
||||||
sprintf(s, "%llx", isch);
|
// Must contain bit errors if no exact match, so
|
||||||
decoded = isch_map[s];
|
// look for codeword with smallest hamming distance
|
||||||
return(decoded);
|
if (isch_map.find(isch) == isch_map.end())
|
||||||
|
{
|
||||||
|
int popct, popmin = 40;
|
||||||
|
for (auto it = isch_map.begin(); it != isch_map.end(); ++it)
|
||||||
|
{
|
||||||
|
popct = __builtin_popcountll(isch ^ it->first);
|
||||||
|
// Encoding is (40, 9, 16) which can correct at most 7 bit-errors
|
||||||
|
if ((popct <= 7) && (popct < popmin))
|
||||||
|
{
|
||||||
|
decoded = it->second;
|
||||||
|
popmin = popct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
else // No errors, so simply return the matching value
|
||||||
|
decoded = isch_map[isch];
|
||||||
|
|
||||||
|
return decoded;
|
||||||
|
|
||||||
}
|
}
|
||||||
115
src/fec.c
@@ -760,6 +760,82 @@ bool Hamming_16_11_4_decode(unsigned char *rxBits, unsigned char *decodedBits, i
|
|||||||
|
|
||||||
// ========================================================================================
|
// ========================================================================================
|
||||||
|
|
||||||
|
void rand_test_20_8() //from dforsi as a pull request to DSDcc
|
||||||
|
{
|
||||||
|
unsigned char msg[8];
|
||||||
|
unsigned char codeword[20], xcodeword[20];
|
||||||
|
int idx1, idx2, idx3 = 0;
|
||||||
|
int dataIn, dataOut; UNUSED(dataIn); //don't know why its reporting dataIn as unused for
|
||||||
|
int passCount = 0, failCount = 0, parityFailCount = 0;
|
||||||
|
|
||||||
|
// Run multiple times, to randomly corrupt different bits
|
||||||
|
// Takes about 10 seconds on a fast PC
|
||||||
|
for (int repeat = 0; repeat < 100000/2; repeat++)
|
||||||
|
{
|
||||||
|
// Exhaustively test all 8-bit inputs
|
||||||
|
for (int dataIn = 0; dataIn < 256; dataIn++)
|
||||||
|
{
|
||||||
|
// Convert to array of bits
|
||||||
|
for (int j = 0; j < 8; j++) {
|
||||||
|
msg[j] = (dataIn >> j) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode
|
||||||
|
Golay_20_8_encode(msg, codeword);
|
||||||
|
|
||||||
|
// Save copy of uncorrupted codeword
|
||||||
|
memcpy(xcodeword, codeword, 20*sizeof(unsigned char));
|
||||||
|
|
||||||
|
//change the seed each time, using time(NULL) will only change the seed once every second, otherwise resets it to the same seed
|
||||||
|
srand(dataIn+dataOut);
|
||||||
|
|
||||||
|
// Randomly corrupt up to 3 bits
|
||||||
|
idx1 = rand() % 20;
|
||||||
|
idx2 = rand() % 20;
|
||||||
|
idx3 = rand() % 20;
|
||||||
|
codeword[idx1] ^= codeword[idx1];
|
||||||
|
codeword[idx2] ^= codeword[idx2];
|
||||||
|
codeword[idx3] ^= codeword[idx3];
|
||||||
|
|
||||||
|
//flip the parity bit
|
||||||
|
// codeword[19] ^= 1;
|
||||||
|
|
||||||
|
bool fail = false;
|
||||||
|
// Decode and correct errors
|
||||||
|
dataOut = 0;
|
||||||
|
if (Golay_20_8_decode(codeword))
|
||||||
|
{
|
||||||
|
// Check data is corrected
|
||||||
|
for (int j = 0; j < 8; j++) {
|
||||||
|
dataOut |= codeword[j] << j;
|
||||||
|
}
|
||||||
|
if (dataIn != dataOut) {
|
||||||
|
fail = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check also that parity has been corrected, as we previously had a bug with this
|
||||||
|
if (memcmp(codeword, xcodeword, 20)) {
|
||||||
|
parityFailCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fail = true;
|
||||||
|
}
|
||||||
|
if (fail)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\n Decode Failed: IN:%05X; OUT:%05X; IDX: %d, %d, %d; ", dataIn, dataOut, idx1, idx2, idx3);
|
||||||
|
failCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
passCount++;
|
||||||
|
// fprintf (stderr, "\n Decode Passed: IN:%05X; OUT:%05X; IDX: %d, %d, %d; ", dataIn, dataOut, idx1, idx2, idx3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf (stderr, "\n Passcount: %d; Failcount: %d; Parity Fail Count: %d \n", passCount, failCount, parityFailCount);
|
||||||
|
}
|
||||||
|
|
||||||
void Golay_20_8_init()
|
void Golay_20_8_init()
|
||||||
{
|
{
|
||||||
@@ -806,7 +882,7 @@ void Golay_20_8_init()
|
|||||||
syndromeIP = syndromeI ^ (1 << (11-ip));
|
syndromeIP = syndromeI ^ (1 << (11-ip));
|
||||||
Golay_20_8_m_corr[syndromeIP][0] = i1;
|
Golay_20_8_m_corr[syndromeIP][0] = i1;
|
||||||
Golay_20_8_m_corr[syndromeIP][1] = i2;
|
Golay_20_8_m_corr[syndromeIP][1] = i2;
|
||||||
Golay_20_8_m_corr[syndromeIP][2] = 12 + ip;
|
Golay_20_8_m_corr[syndromeIP][2] = 8 + ip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -824,14 +900,14 @@ void Golay_20_8_init()
|
|||||||
{
|
{
|
||||||
syndromeIP1 = syndromeI ^ (1 << (11-ip1));
|
syndromeIP1 = syndromeI ^ (1 << (11-ip1));
|
||||||
Golay_20_8_m_corr[syndromeIP1][0] = i1;
|
Golay_20_8_m_corr[syndromeIP1][0] = i1;
|
||||||
Golay_20_8_m_corr[syndromeIP1][1] = 12 + ip1;
|
Golay_20_8_m_corr[syndromeIP1][1] = 8 + ip1;
|
||||||
|
|
||||||
for (ip2 = ip1+1; ip2 < 12; ip2++) // 1 more bit flip in parity
|
for (ip2 = ip1+1; ip2 < 12; ip2++) // 1 more bit flip in parity
|
||||||
{
|
{
|
||||||
syndromeIP2 = syndromeIP1 ^ (1 << (11-ip2));
|
syndromeIP2 = syndromeIP1 ^ (1 << (11-ip2));
|
||||||
Golay_20_8_m_corr[syndromeIP2][0] = i1;
|
Golay_20_8_m_corr[syndromeIP2][0] = i1;
|
||||||
Golay_20_8_m_corr[syndromeIP2][1] = 12 + ip1;
|
Golay_20_8_m_corr[syndromeIP2][1] = 8 + ip1;
|
||||||
Golay_20_8_m_corr[syndromeIP2][2] = 12 + ip2;
|
Golay_20_8_m_corr[syndromeIP2][2] = 8 + ip2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -840,23 +916,25 @@ void Golay_20_8_init()
|
|||||||
for (ip1 = 0; ip1 < 12; ip1++) // 1 bit flip in parity
|
for (ip1 = 0; ip1 < 12; ip1++) // 1 bit flip in parity
|
||||||
{
|
{
|
||||||
syndromeIP1 = (1 << (11-ip1));
|
syndromeIP1 = (1 << (11-ip1));
|
||||||
Golay_20_8_m_corr[syndromeIP1][0] = 12 + ip1;
|
Golay_20_8_m_corr[syndromeIP1][0] = 8 + ip1;
|
||||||
|
|
||||||
for (ip2 = ip1+1; ip2 < 12; ip2++) // 1 more bit flip in parity
|
for (ip2 = ip1+1; ip2 < 12; ip2++) // 1 more bit flip in parity
|
||||||
{
|
{
|
||||||
syndromeIP2 = syndromeIP1 ^ (1 << (11-ip2));
|
syndromeIP2 = syndromeIP1 ^ (1 << (11-ip2));
|
||||||
Golay_20_8_m_corr[syndromeIP2][0] = 12 + ip1;
|
Golay_20_8_m_corr[syndromeIP2][0] = 8 + ip1;
|
||||||
Golay_20_8_m_corr[syndromeIP2][1] = 12 + ip2;
|
Golay_20_8_m_corr[syndromeIP2][1] = 8 + ip2;
|
||||||
|
|
||||||
for (ip3 = ip2+1; ip3 < 12; ip3++) // 1 more bit flip in parity
|
for (ip3 = ip2+1; ip3 < 12; ip3++) // 1 more bit flip in parity
|
||||||
{
|
{
|
||||||
syndromeIP3 = syndromeIP2 ^ (1 << (11-ip3));
|
syndromeIP3 = syndromeIP2 ^ (1 << (11-ip3));
|
||||||
Golay_20_8_m_corr[syndromeIP3][0] = 12 + ip1;
|
Golay_20_8_m_corr[syndromeIP3][0] = 8 + ip1;
|
||||||
Golay_20_8_m_corr[syndromeIP3][1] = 12 + ip2;
|
Golay_20_8_m_corr[syndromeIP3][1] = 8 + ip2;
|
||||||
Golay_20_8_m_corr[syndromeIP3][2] = 12 + ip3;
|
Golay_20_8_m_corr[syndromeIP3][2] = 8 + ip3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rand_test_20_8(); //test
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not very efficient but encode is used for unit testing only
|
// Not very efficient but encode is used for unit testing only
|
||||||
@@ -1230,12 +1308,12 @@ void Golay_24_12_init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not very efficient but encode is used for unit testing only
|
// Not very efficient but encode is used for unit testing only, I think this is wrong for M17
|
||||||
void Golay_24_12_encode(unsigned char *origBits, unsigned char *encodedBits)
|
void Golay_24_12_encode(unsigned char *origBits, unsigned char *encodedBits)
|
||||||
{
|
{
|
||||||
int i = 0, j = 0;
|
int i = 0, j = 0;
|
||||||
|
|
||||||
memset(encodedBits, 0, 24);
|
// memset(encodedBits, 0, 24);
|
||||||
|
|
||||||
for (i = 0; i < 12; i++)
|
for (i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
@@ -1248,7 +1326,7 @@ void Golay_24_12_encode(unsigned char *origBits, unsigned char *encodedBits)
|
|||||||
for (i = 0; i < 24; i++)
|
for (i = 0; i < 24; i++)
|
||||||
{
|
{
|
||||||
encodedBits[i] %= 2;
|
encodedBits[i] %= 2;
|
||||||
// fprintf (stderr, "%b", encodedBits[i]); //disable later on
|
// fprintf (stderr, "%b", encodedBits[i]); //disable later on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1397,10 +1475,11 @@ void QR_16_7_6_encode(unsigned char *origBits, unsigned char *encodedBits)
|
|||||||
|
|
||||||
bool QR_16_7_6_decode(unsigned char *rxBits)
|
bool QR_16_7_6_decode(unsigned char *rxBits)
|
||||||
{
|
{
|
||||||
|
//2 bit errors or less
|
||||||
unsigned int syndromeI = 0; // syndrome index
|
unsigned int syndromeI = 0; // syndrome index
|
||||||
int is = 0;
|
int is = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
int corrections = 0;
|
||||||
for (is = 0; is < 9; is++)
|
for (is = 0; is < 9; is++)
|
||||||
{
|
{
|
||||||
syndromeI += (((rxBits[0] * QR_16_7_6_m_H[16*is + 0])
|
syndromeI += (((rxBits[0] * QR_16_7_6_m_H[16*is + 0])
|
||||||
@@ -1434,6 +1513,7 @@ bool QR_16_7_6_decode(unsigned char *rxBits)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
rxBits[QR_16_7_6_m_corr[syndromeI][i]] ^= 1; // flip bit
|
rxBits[QR_16_7_6_m_corr[syndromeI][i]] ^= 1; // flip bit
|
||||||
|
corrections++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1441,8 +1521,15 @@ bool QR_16_7_6_decode(unsigned char *rxBits)
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//disabling again for now
|
||||||
|
// if (corrections > 1) //no more than 1-bit error, else consider failure?
|
||||||
|
// {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,26 +36,25 @@
|
|||||||
|
|
||||||
|
|
||||||
/* Global variables ---------------------------------------------------------*/
|
/* Global variables ---------------------------------------------------------*/
|
||||||
static const uint8_t CNXDNConvolution_BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
static const uint8_t CNXDNConvolution_BIT_MASK_TABLE[8] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||||
|
|
||||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | CNXDNConvolution_BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~CNXDNConvolution_BIT_MASK_TABLE[(i)&7])
|
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | CNXDNConvolution_BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~CNXDNConvolution_BIT_MASK_TABLE[(i)&7])
|
||||||
#define READ_BIT1(p,i) (p[(i)>>3] & CNXDNConvolution_BIT_MASK_TABLE[(i)&7])
|
#define READ_BIT1(p,i) (p[(i)>>3] & CNXDNConvolution_BIT_MASK_TABLE[(i)&7])
|
||||||
|
|
||||||
static const uint8_t CNXDNConvolution_BRANCH_TABLE1[] = {0U, 0U, 0U, 0U, 2U, 2U, 2U, 2U};
|
static const uint8_t CNXDNConvolution_BRANCH_TABLE1[8] = {0U, 0U, 0U, 0U, 2U, 2U, 2U, 2U};
|
||||||
static const uint8_t CNXDNConvolution_BRANCH_TABLE2[] = {0U, 2U, 2U, 0U, 0U, 2U, 2U, 0U};
|
static const uint8_t CNXDNConvolution_BRANCH_TABLE2[8] = {0U, 2U, 2U, 0U, 0U, 2U, 2U, 0U};
|
||||||
|
|
||||||
static const unsigned int CNXDNConvolution_NUM_OF_STATES_D2 = 8U;
|
static const unsigned int CNXDNConvolution_NUM_OF_STATES_D2 = 8U;
|
||||||
static const unsigned int CNXDNConvolution_NUM_OF_STATES = 16U;
|
|
||||||
static const uint32_t CNXDNConvolution_M = 4U;
|
static const uint32_t CNXDNConvolution_M = 4U;
|
||||||
static const unsigned int CNXDNConvolution_K = 5U;
|
static const unsigned int CNXDNConvolution_K = 5U;
|
||||||
|
|
||||||
static uint16_t m_metrics1[16U] = {0};
|
//NOTE:
|
||||||
static uint16_t m_metrics2[16U] = {0};
|
uint16_t m_metrics1[16];
|
||||||
static uint64_t m_decisions[300U] = {0};
|
uint16_t m_metrics2[16];
|
||||||
static uint16_t * m_oldMetrics = NULL;
|
uint64_t m_decisions[8*300];
|
||||||
static uint16_t * m_newMetrics = NULL;
|
uint16_t * m_oldMetrics = NULL;
|
||||||
static uint64_t * m_dp = NULL;
|
uint16_t * m_newMetrics = NULL;
|
||||||
|
uint64_t * m_dp = NULL;
|
||||||
|
|
||||||
/* Functions ----------------------------------------------------------------*/
|
/* Functions ----------------------------------------------------------------*/
|
||||||
|
|
||||||
@@ -158,13 +157,17 @@ void CNXDNConvolution_encode(const unsigned char* in, unsigned char* out, unsign
|
|||||||
|
|
||||||
void CNXDNConvolution_start(void)
|
void CNXDNConvolution_start(void)
|
||||||
{
|
{
|
||||||
memset(m_metrics1, 0x00U, CNXDNConvolution_NUM_OF_STATES * sizeof(uint16_t));
|
|
||||||
memset(m_metrics2, 0x00U, CNXDNConvolution_NUM_OF_STATES * sizeof(uint16_t));
|
|
||||||
|
|
||||||
m_oldMetrics = m_metrics1;
|
m_oldMetrics = m_metrics1;
|
||||||
m_newMetrics = m_metrics2;
|
m_newMetrics = m_metrics2;
|
||||||
m_dp = m_decisions;
|
m_dp = m_decisions;
|
||||||
|
|
||||||
for (int x = 0; x < 4; x++) CNXDNConvolution_decode(0U, 0U);
|
}
|
||||||
|
|
||||||
|
void CNXDNConvolution_init()
|
||||||
|
{
|
||||||
|
memset(m_metrics1, 0x0, 16*sizeof(uint16_t));
|
||||||
|
memset(m_metrics2, 0x0, 16*sizeof(uint16_t));
|
||||||
|
memset(m_decisions,0x0, 8*300*sizeof(uint64_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1952
src/nxdn_element.c
392
src/nxdn_frame.c
@@ -32,53 +32,72 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
// length is implicitly 192, with frame sync in first 10 dibits
|
// length is implicitly 192, with frame sync in first 10 dibits
|
||||||
uint8_t dbuf[182];
|
uint8_t dbuf[182];
|
||||||
uint8_t lich;
|
uint8_t lich;
|
||||||
int answer_len=0;
|
|
||||||
uint8_t answer[32];
|
uint8_t answer[32];
|
||||||
uint8_t sacch_answer[32];
|
uint8_t sacch_answer[32];
|
||||||
int lich_parity_received;
|
int lich_parity_received;
|
||||||
int lich_parity_computed;
|
int lich_parity_computed;
|
||||||
int voice=0;
|
int voice = 0;
|
||||||
int facch=0;
|
int facch = 0;
|
||||||
int facch2=0;
|
int facch2 = 0;
|
||||||
int sacch=0;
|
int udch = 0;
|
||||||
int cac=0;
|
int sacch = 0;
|
||||||
int sr_structure;
|
int cac = 0;
|
||||||
int sr_ran;
|
|
||||||
|
|
||||||
uint8_t lich_dibits[8] = {0};
|
//new, and even more confusing NXDN Type-D / "IDAS" acronyms
|
||||||
uint8_t sacch_bits[60] = {0};
|
int idas = 0;
|
||||||
uint8_t facch_bits_a[144] = {0};
|
int scch = 0;
|
||||||
uint8_t facch_bits_b[144] = {0};
|
int facch3 = 0;
|
||||||
uint8_t cac_bits[300] = {0};
|
int udch2 = 0;
|
||||||
uint8_t facch2_bits[348] = {0}; //facch2 or udch, same amount of bits
|
|
||||||
|
|
||||||
//nxdn bit buffer, for easy assignment handling
|
//new breakdown of lich codes
|
||||||
int nxdn_bit_buffer[364] = {0};
|
uint8_t lich_rf = 0; //RF Channel Type
|
||||||
int nxdn_dibit_buffer[182] = {0};
|
uint8_t lich_fc = 0; //Functional Channel Type
|
||||||
|
uint8_t lich_op = 0; //Options
|
||||||
|
uint8_t direction; //inbound or outbound direction
|
||||||
|
UNUSED2(lich_fc, lich_op);
|
||||||
|
|
||||||
|
uint8_t lich_dibits[8];
|
||||||
|
uint8_t sacch_bits[60];
|
||||||
|
uint8_t facch_bits_a[144];
|
||||||
|
uint8_t facch_bits_b[144];
|
||||||
|
uint8_t cac_bits[300];
|
||||||
|
uint8_t facch2_bits[348]; //facch2 or udch, same amount of bits
|
||||||
|
uint8_t facch3_bits[288]; //facch3 or udch2, same amount of bits
|
||||||
|
|
||||||
|
//nxdn bit buffer, for easy assignment handling
|
||||||
|
int nxdn_bit_buffer[364];
|
||||||
|
int nxdn_dibit_buffer[182];
|
||||||
|
|
||||||
|
//init all arrays
|
||||||
memset (dbuf, 0, sizeof(dbuf));
|
memset (dbuf, 0, sizeof(dbuf));
|
||||||
|
memset (answer, 0, sizeof(answer));
|
||||||
|
memset (sacch_answer, 0, sizeof(sacch_answer));
|
||||||
|
memset (lich_dibits, 0, sizeof(lich_dibits));
|
||||||
|
memset (sacch_bits, 0, sizeof(sacch_bits));
|
||||||
|
memset (facch_bits_b, 0, sizeof(facch_bits_b));
|
||||||
|
memset (facch_bits_a, 0, sizeof(facch_bits_a));
|
||||||
|
memset (cac_bits, 0, sizeof(cac_bits));
|
||||||
|
memset (facch2_bits, 0, sizeof(facch2_bits));
|
||||||
|
|
||||||
|
memset (nxdn_bit_buffer, 0, sizeof(nxdn_bit_buffer));
|
||||||
|
memset (nxdn_dibit_buffer, 0, sizeof(nxdn_dibit_buffer));
|
||||||
|
|
||||||
//collect lich bits first, if they are good, then we can collect the rest of them
|
//collect lich bits first, if they are good, then we can collect the rest of them
|
||||||
for (int i = 0; i < 8; i++)
|
for (int i = 0; i < 8; i++) lich_dibits[i] = dbuf[i] = getDibit(opts, state);
|
||||||
{
|
|
||||||
lich_dibits[i] = dbuf[i] = getDibit(opts, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
nxdn_descramble (lich_dibits, 8);
|
nxdn_descramble (lich_dibits, 8);
|
||||||
|
|
||||||
lich = 0;
|
lich = 0;
|
||||||
for (int i=0; i<8; i++)
|
for (int i=0; i<8; i++) lich |= (lich_dibits[i] >> 1) << (7-i);
|
||||||
{
|
|
||||||
lich |= (lich_dibits[i] >> 1) << (7-i);
|
|
||||||
}
|
|
||||||
|
|
||||||
lich_parity_received = lich & 1;
|
lich_parity_received = lich & 1;
|
||||||
lich_parity_computed = ((lich >> 7) + (lich >> 6) + (lich >> 5) + (lich >> 4)) & 1;
|
lich_parity_computed = ((lich >> 7) + (lich >> 6) + (lich >> 5) + (lich >> 4)) & 1;
|
||||||
lich = lich >> 1;
|
lich = lich >> 1;
|
||||||
if (lich_parity_received != lich_parity_computed)
|
if (lich_parity_received != lich_parity_computed)
|
||||||
{
|
{
|
||||||
//fprintf(stderr, "NXDN lich parity error, ignoring frame \n");
|
if (opts->payload == 1) fprintf(stderr, " Lich Parity Error %02X\n", lich);
|
||||||
return;
|
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||||
|
goto END;
|
||||||
}
|
}
|
||||||
|
|
||||||
voice = 0;
|
voice = 0;
|
||||||
@@ -86,6 +105,16 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
facch2 = 0;
|
facch2 = 0;
|
||||||
sacch = 0;
|
sacch = 0;
|
||||||
cac = 0;
|
cac = 0;
|
||||||
|
|
||||||
|
//test for inbound direction lich when trunking (false positive) and skip
|
||||||
|
//all inbound lich are even value (lsb is set to 0 for inbound direction)
|
||||||
|
if (lich % 2 == 0 && opts->p25_trunk == 1)
|
||||||
|
{
|
||||||
|
if (opts->payload == 1) fprintf(stderr, " Simplex/Inbound NXDN lich on trunking system - type 0x%02X\n", lich);
|
||||||
|
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
switch(lich) { //cases without breaks continue to flow downwards until they hit the break
|
switch(lich) { //cases without breaks continue to flow downwards until they hit the break
|
||||||
case 0x01: // CAC types
|
case 0x01: // CAC types
|
||||||
case 0x05:
|
case 0x05:
|
||||||
@@ -93,21 +122,20 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
break;
|
break;
|
||||||
case 0x28: //facch2 types
|
case 0x28: //facch2 types
|
||||||
case 0x29:
|
case 0x29:
|
||||||
case 0x2e:
|
|
||||||
case 0x2f:
|
|
||||||
case 0x48:
|
case 0x48:
|
||||||
case 0x49:
|
case 0x49:
|
||||||
|
facch2 = 1;
|
||||||
|
break;
|
||||||
|
case 0x2e: //udch types
|
||||||
|
case 0x2f:
|
||||||
case 0x4e:
|
case 0x4e:
|
||||||
case 0x4f:
|
case 0x4f:
|
||||||
case 0x69:
|
udch = 1;
|
||||||
case 0x6f:
|
|
||||||
facch2 = 1;
|
|
||||||
break;
|
break;
|
||||||
case 0x32: //facch in 1, vch in 2
|
case 0x32: //facch in 1, vch in 2
|
||||||
case 0x33:
|
case 0x33:
|
||||||
case 0x52:
|
case 0x52:
|
||||||
case 0x53:
|
case 0x53:
|
||||||
case 0x73:
|
|
||||||
voice = 2;
|
voice = 2;
|
||||||
facch = 1;
|
facch = 1;
|
||||||
sacch = 1;
|
sacch = 1;
|
||||||
@@ -115,8 +143,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
case 0x34: //vch in 1, facch in 2
|
case 0x34: //vch in 1, facch in 2
|
||||||
case 0x35:
|
case 0x35:
|
||||||
case 0x54:
|
case 0x54:
|
||||||
case 0x55:
|
case 0x55: //disabled for testing, IDAS system randomly triggers this one, probably due to poor signal
|
||||||
case 0x75:
|
|
||||||
voice = 1;
|
voice = 1;
|
||||||
facch = 2;
|
facch = 2;
|
||||||
sacch = 1;
|
sacch = 1;
|
||||||
@@ -125,7 +152,6 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
case 0x37:
|
case 0x37:
|
||||||
case 0x56:
|
case 0x56:
|
||||||
case 0x57:
|
case 0x57:
|
||||||
case 0x77:
|
|
||||||
voice = 3;
|
voice = 3;
|
||||||
facch = 0;
|
facch = 0;
|
||||||
sacch = 1;
|
sacch = 1;
|
||||||
@@ -138,8 +164,6 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
case 0x41:
|
case 0x41:
|
||||||
case 0x50:
|
case 0x50:
|
||||||
case 0x51:
|
case 0x51:
|
||||||
case 0x61:
|
|
||||||
case 0x71:
|
|
||||||
voice = 0;
|
voice = 0;
|
||||||
facch = 3;
|
facch = 3;
|
||||||
sacch = 1;
|
sacch = 1;
|
||||||
@@ -148,10 +172,63 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
case 0x39:
|
case 0x39:
|
||||||
sacch = 1;
|
sacch = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
//NXDN "Type-D" or "IDAS" Specific Lich Codes
|
||||||
|
case 0x76: //normal vch voice (in one and two)
|
||||||
|
case 0x77:
|
||||||
|
idas = 1;
|
||||||
|
scch = 1;
|
||||||
|
voice = 3;
|
||||||
|
break;
|
||||||
|
case 0x74: //vch in 1, facch1 in 2 (facch 2 steal)
|
||||||
|
case 0x75:
|
||||||
|
idas = 1;
|
||||||
|
scch = 1;
|
||||||
|
voice = 1;
|
||||||
|
facch = 2;
|
||||||
|
break;
|
||||||
|
case 0x72: //facch in 1, vch in 2 (facch 1 steal)
|
||||||
|
case 0x73:
|
||||||
|
idas = 1;
|
||||||
|
scch = 1;
|
||||||
|
voice = 2;
|
||||||
|
facch = 1;
|
||||||
|
break;
|
||||||
|
case 0x70: //facch steal in vch 1 and vch 2 (during voice only)
|
||||||
|
case 0x71:
|
||||||
|
idas = 1;
|
||||||
|
scch = 1;
|
||||||
|
facch = 3;
|
||||||
|
break;
|
||||||
|
case 0x6E: //udch2
|
||||||
|
case 0x6F:
|
||||||
|
idas = 1;
|
||||||
|
scch = 1;
|
||||||
|
udch2 = 1;
|
||||||
|
break;
|
||||||
|
case 0x68:
|
||||||
|
case 0x69: //facch3
|
||||||
|
idas = 1;
|
||||||
|
scch = 1;
|
||||||
|
facch3 = 1;
|
||||||
|
break;
|
||||||
|
case 0x62:
|
||||||
|
case 0x63: //facch1 in 1, null data and post field in 2
|
||||||
|
idas = 1;
|
||||||
|
scch = 1;
|
||||||
|
facch = 1;
|
||||||
|
break;
|
||||||
|
case 0x60:
|
||||||
|
case 0x61: //facch1 in both (non vch)
|
||||||
|
idas = 1;
|
||||||
|
scch = 1;
|
||||||
|
facch = 3;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (opts->payload == 1) fprintf(stderr, " false sync or unsupported NXDN lich type 0x%02X\n", lich);
|
if (opts->payload == 1) fprintf(stderr, " false sync or unsupported NXDN lich type 0x%02X\n", lich);
|
||||||
//reset the sacch field, we probably got a false sync and need to wipe or give a bad crc
|
//reset the sacch field, we probably got a false sync and need to wipe or give a bad crc
|
||||||
memset (state->nxdn_sacch_frame_segment, 0, sizeof(state->nxdn_sacch_frame_segment));
|
memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment));
|
||||||
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc));
|
||||||
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
state->lastsynctype = -1; //set to -1 so we don't jump back here too quickly
|
||||||
voice = 0;
|
voice = 0;
|
||||||
@@ -159,17 +236,29 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
break;
|
break;
|
||||||
} // end of switch(lich)
|
} // end of switch(lich)
|
||||||
|
|
||||||
|
//enable these after good lich parity and known lich value
|
||||||
|
state->carrier = 1;
|
||||||
|
state->last_cc_sync_time = time(NULL);
|
||||||
|
|
||||||
//printframesync after determining we have a good lich and it has something in it
|
//printframesync after determining we have a good lich and it has something in it
|
||||||
if (voice || facch || sacch || facch2 || cac)
|
if (idas)
|
||||||
|
{
|
||||||
|
if (opts->frame_nxdn48 == 1)
|
||||||
|
{
|
||||||
|
printFrameSync (opts, state, "IDAS D ", 0, "-");
|
||||||
|
}
|
||||||
|
if (opts->payload == 1)
|
||||||
|
fprintf (stderr, "L%02X - ", lich);
|
||||||
|
}
|
||||||
|
else if (voice || facch || sacch || facch2 || udch || cac)
|
||||||
{
|
{
|
||||||
// if (state->synctype == 28) fprintf (stderr, " +");
|
|
||||||
// else fprintf (stderr, " -");
|
|
||||||
if (opts->frame_nxdn48 == 1)
|
if (opts->frame_nxdn48 == 1)
|
||||||
{
|
{
|
||||||
printFrameSync (opts, state, "NXDN48 ", 0, "-");
|
printFrameSync (opts, state, "NXDN48 ", 0, "-");
|
||||||
}
|
}
|
||||||
else printFrameSync (opts, state, "NXDN96 ", 0, "-");
|
else printFrameSync (opts, state, "NXDN96 ", 0, "-");
|
||||||
if (opts->payload == 1) fprintf (stderr, "L%02X -", lich);
|
if (opts->payload == 1)
|
||||||
|
fprintf (stderr, "L%02X - ", lich);
|
||||||
}
|
}
|
||||||
|
|
||||||
//now that we have a good LICH, we can collect all of our dibits
|
//now that we have a good LICH, we can collect all of our dibits
|
||||||
@@ -188,7 +277,7 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
nxdn_bit_buffer[i*2+1] = dbuf[i] & 1;
|
nxdn_bit_buffer[i*2+1] = dbuf[i] & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//sacch
|
//sacch or scch bits
|
||||||
for (int i = 0; i < 60; i++)
|
for (int i = 0; i < 60; i++)
|
||||||
{
|
{
|
||||||
sacch_bits[i] = nxdn_bit_buffer[i+16];
|
sacch_bits[i] = nxdn_bit_buffer[i+16];
|
||||||
@@ -213,24 +302,128 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
facch2_bits[i] = nxdn_bit_buffer[i+16];
|
facch2_bits[i] = nxdn_bit_buffer[i+16];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//udch2 or facch3
|
||||||
|
for (int i = 0; i < 288; i++)
|
||||||
|
{
|
||||||
|
facch3_bits[i] = nxdn_bit_buffer[i+16+60];
|
||||||
|
}
|
||||||
|
|
||||||
//vch frames stay inside dbuf, easier to assign that to ambe_fr frames
|
//vch frames stay inside dbuf, easier to assign that to ambe_fr frames
|
||||||
//sacch needs extra handling depending on superframe or non-superframe variety
|
//sacch needs extra handling depending on superframe or non-superframe variety
|
||||||
|
|
||||||
if (voice)
|
//Add advanced decoding of LICH (RF, FC, OPT, and Direction
|
||||||
|
lich_rf = (lich >> 5) & 0x3;
|
||||||
|
lich_fc = (lich >> 3) & 0x3;
|
||||||
|
lich_op = (lich >> 1) & 0x3;
|
||||||
|
if (lich % 2 == 0) direction = 0;
|
||||||
|
else direction = 1;
|
||||||
|
|
||||||
|
// RF Channel Type
|
||||||
|
if (lich_rf == 0) fprintf (stderr, "RCCH ");
|
||||||
|
else if (lich_rf == 1) fprintf (stderr, "RTCH ");
|
||||||
|
else if (lich_rf == 2) fprintf (stderr, "RDCH ");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (lich < 0x60) fprintf (stderr, "RTCH_C ");
|
||||||
|
else fprintf (stderr, "RTCH2 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functional Channel Type -- things start to get really convoluted here
|
||||||
|
// These will echo when handled, either with the decoded message type, or relevant crc err
|
||||||
|
// if (lich_rf == 0) //CAC Type
|
||||||
|
// {
|
||||||
|
// //Technically, we should be checking direction as well, but the fc never has split meaning on CAC
|
||||||
|
// if (lich_fc == 0) fprintf (stderr, "CAC ");
|
||||||
|
// else if (lich_fc == 1) fprintf (stderr, "Long CAC ");
|
||||||
|
// else if (lich_fc == 3) fprintf (stderr, "Short CAC ");
|
||||||
|
// else fprintf (stderr, "Reserved ");
|
||||||
|
// }
|
||||||
|
// else //USC Type
|
||||||
|
// {
|
||||||
|
// if (lich_fc == 0) fprintf (stderr, "NSF SACCH ");
|
||||||
|
// else if (lich_fc == 1) fprintf (stderr, "UDCH ");
|
||||||
|
// else if (lich_fc == 2) fprintf (stderr, "SF SACCH ");
|
||||||
|
// else if (lich_fc == 3) fprintf (stderr, "SF SACCH/IDLE ");
|
||||||
|
// }
|
||||||
|
|
||||||
|
#ifdef LIMAZULUTWEAKS
|
||||||
|
|
||||||
|
//LimaZulu specific tweak, load keys from frequency value, if avalable -- test before VCALL
|
||||||
|
//needs to be loaded here, if superframe data pair, then we need to run the LFSR on it as well
|
||||||
|
|
||||||
|
if (voice) //can this run TOO frequently?
|
||||||
|
{
|
||||||
|
long int freq = 0;
|
||||||
|
uint8_t hash_bits[24];
|
||||||
|
memset (hash_bits, 0, sizeof(hash_bits));
|
||||||
|
uint16_t limazulu = 0;
|
||||||
|
|
||||||
|
//if not available, then poll rigctl if its available
|
||||||
|
if (opts->use_rigctl == 1)
|
||||||
|
freq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||||
|
|
||||||
|
//if using rtl input, we can ask for the current frequency tuned
|
||||||
|
else if (opts->audio_in_type == 3)
|
||||||
|
freq = (long int)opts->rtlsdr_center_freq;
|
||||||
|
|
||||||
|
// freq = 167831250; //hardset for testing
|
||||||
|
|
||||||
|
//since a frequency value will be larger than the 16-bit max, we need to hash it first
|
||||||
|
//the hash has to be run the same way as the import, so at a 24-bit depth, which hopefully
|
||||||
|
//will not lead to any duplicate key loads due to multiple CRC16 collisions on a larger value?
|
||||||
|
for (int i = 0; i < 24; i++)
|
||||||
|
hash_bits[i] = ((freq << i) & 0x800000) >> 23; //load into array for CRC16
|
||||||
|
|
||||||
|
if (freq) limazulu = ComputeCrcCCITT16d (hash_bits, 24);
|
||||||
|
limazulu = limazulu & 0xFFFF; //make sure no larger than 16-bits
|
||||||
|
|
||||||
|
fprintf (stderr, "%s", KYEL);
|
||||||
|
if (freq) fprintf (stderr, "\n Freq: %ld - Freq Hash: %d", freq, limazulu);
|
||||||
|
if (state->rkey_array[limazulu] != 0) fprintf (stderr, " - Key Loaded: %lld", state->rkey_array[limazulu]);
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
|
if (state->rkey_array[limazulu] != 0)
|
||||||
|
state->R = state->rkey_array[limazulu];
|
||||||
|
|
||||||
|
if (state->R != 0 && state->M == 1) state->nxdn_cipher_type = 0x1;
|
||||||
|
|
||||||
|
//add additional time to last_sync_time for LimaZulu to hold on current frequency
|
||||||
|
//a little longer without affecting normal scan time on trunk_hangtime variable
|
||||||
|
state->last_cc_sync_time = time(NULL) + 2; //ask him for an ideal wait timer
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //end LIMAZULUTWEAKS
|
||||||
|
|
||||||
|
if (opts->scanner_mode == 1)
|
||||||
|
state->last_cc_sync_time = time(NULL) + 2; //add a little extra hangtime between resuming scan
|
||||||
|
|
||||||
|
//Option/Steal Flags echoed in Voice, V+F, or Data
|
||||||
|
if (voice && !facch) //voice only, no facch steal
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KGRN);
|
fprintf (stderr, "%s", KGRN);
|
||||||
fprintf (stderr, " Voice ");
|
fprintf (stderr, " Voice ");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
}
|
}
|
||||||
else
|
else if (voice && facch) //voice with facch1 steal
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KGRN);
|
||||||
|
fprintf (stderr, " V%d+F%d ", 3 - facch, facch); //print which position on each
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
else //Covers FACCH1 in both, FACCH2, UDCH, UDCH2, CAC
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s", KCYN);
|
fprintf (stderr, "%s", KCYN);
|
||||||
fprintf (stderr, " Data ");
|
fprintf (stderr, " Data ");
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
|
||||||
//roll the voice scrambler LFSR here if key available to advance seed (usually just needed on NXDN96)
|
//roll the voice scrambler LFSR here if key available to advance seed (usually just needed on NXDN96)
|
||||||
if (state->nxdn_cipher_type == 0x1 && state->R != 0) //!voice, or voice == 0
|
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
|
||||||
{
|
{
|
||||||
|
if (state->payload_miN == 0)
|
||||||
|
{
|
||||||
|
state->payload_miN = state->R;
|
||||||
|
}
|
||||||
|
|
||||||
char ambe_temp[49] = {0};
|
char ambe_temp[49] = {0};
|
||||||
char ambe_d[49] = {0};
|
char ambe_d[49] = {0};
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
@@ -241,14 +434,101 @@ void nxdn_frame (dsd_opts * opts, dsd_state * state)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state->nxdn_sacch_non_superframe = (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) ? true : false;
|
if (voice && facch == 1) //facch steal 1 -- before voice
|
||||||
if (sacch)nxdn_deperm_sacch(opts, state, sacch_bits);
|
{
|
||||||
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
|
//force scrambler here, but with unspecified key (just use what's loaded)
|
||||||
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits);
|
if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||||
if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
|
//roll the voice scrambler LFSR here if key available to advance seed -- half rotation on a facch steal
|
||||||
if (facch & 2) nxdn_deperm_facch(opts, state, facch_bits_b);
|
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
|
||||||
if (voice) nxdn_voice (opts, state, voice, dbuf);
|
{
|
||||||
|
if (state->payload_miN == 0)
|
||||||
|
{
|
||||||
|
state->payload_miN = state->R;
|
||||||
|
}
|
||||||
|
|
||||||
|
char ambe_temp[49] = {0};
|
||||||
|
char ambe_d[49] = {0};
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
LFSRN(ambe_temp, ambe_d, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lich == 0x20 || lich == 0x21 || lich == 0x61 || lich == 0x40 || lich == 0x41) state->nxdn_sacch_non_superframe = TRUE;
|
||||||
|
else state->nxdn_sacch_non_superframe = FALSE;
|
||||||
|
|
||||||
|
//TODO Later: Add Direction and/or LICH to all decoding functions
|
||||||
|
if (scch) nxdn_deperm_scch (opts, state, sacch_bits, direction);
|
||||||
|
|
||||||
|
if (udch2) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 0);
|
||||||
|
if (facch3) nxdn_deperm_facch3_udch2(opts, state, facch3_bits, 1);
|
||||||
|
|
||||||
|
if (sacch) nxdn_deperm_sacch(opts, state, sacch_bits);
|
||||||
|
if (cac) nxdn_deperm_cac(opts, state, cac_bits);
|
||||||
|
|
||||||
|
//Seperated UDCH user data from facch2 data
|
||||||
|
if (udch) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 0);
|
||||||
|
if (facch2) nxdn_deperm_facch2_udch(opts, state, facch2_bits, 1);
|
||||||
|
|
||||||
|
//SHOULD be okay to run facch1's again on steal frames, will need testing
|
||||||
|
// if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
|
||||||
|
// if (facch & 2) nxdn_deperm_facch(opts, state, facch_bits_b);
|
||||||
|
|
||||||
|
//only run facch in second slot if its not equal to the first one
|
||||||
|
//ideally, this would work better AFTER decoding/FEC
|
||||||
|
if (facch & 1) nxdn_deperm_facch(opts, state, facch_bits_a);
|
||||||
|
if (facch & 2)
|
||||||
|
{
|
||||||
|
if (memcmp (facch_bits_a, facch_bits_b, 144) != 0)
|
||||||
|
nxdn_deperm_facch(opts, state, facch_bits_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (voice)
|
||||||
|
{
|
||||||
|
//restore MBE file open here
|
||||||
|
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL)) openMbeOutFile (opts, state);
|
||||||
|
//update last voice sync time
|
||||||
|
state->last_vc_sync_time = time(NULL);
|
||||||
|
//turn on scrambler if forced by user option
|
||||||
|
if (state->M == 1 && state->R != 0) state->nxdn_cipher_type = 0x1;
|
||||||
|
//process voice frame
|
||||||
|
nxdn_voice (opts, state, voice, dbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
//close MBE file if no voice and its open
|
||||||
|
if (!voice)
|
||||||
|
{
|
||||||
|
if (opts->mbe_out_f != NULL)
|
||||||
|
{
|
||||||
|
if (opts->frame_nxdn96 == 1) //nxdn96 has pure voice and data frames mixed together, so we will need to do a time check first
|
||||||
|
{
|
||||||
|
if ( (time(NULL) - state->last_vc_sync_time) > 1) //test for optimal time, 1 sec should be okay
|
||||||
|
{
|
||||||
|
closeMbeOutFile (opts, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//may need to reconsider this, due to double FACCH1 steals on some Type-C (ASSGN_DUP, etc) and Conventional Systems (random IDLE FACCH1 steal for no reason)
|
||||||
|
if (opts->frame_nxdn48 == 1) closeMbeOutFile (opts, state); //okay to close right away if nxdn48, no data/voice frames mixing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (voice && facch == 2) //facch steal 2 -- after voice 1
|
||||||
|
{
|
||||||
|
//roll the voice scrambler LFSR here if key available to advance seed -- half rotation on a facch steal
|
||||||
|
if (state->nxdn_cipher_type == 0x1 && state->R != 0)
|
||||||
|
{
|
||||||
|
char ambe_temp[49] = {0};
|
||||||
|
char ambe_d[49] = {0};
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
LFSRN(ambe_temp, ambe_d, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->payload == 1 && !voice) fprintf (stderr, "\n");
|
||||||
|
else if (opts->payload == 0) fprintf (stderr, "\n");
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
END: ; //do nothing
|
END: ; //do nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,11 +55,10 @@ const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
|||||||
|
|
||||||
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182])
|
void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182])
|
||||||
{
|
{
|
||||||
int i, j, dibit;
|
int i;
|
||||||
int start, stop;
|
int start, stop;
|
||||||
char ambe_fr[4][24];
|
char ambe_fr[4][24];
|
||||||
|
|
||||||
unsigned char *pr;
|
|
||||||
const int *w, *x, *y, *z;
|
const int *w, *x, *y, *z;
|
||||||
|
|
||||||
//these conditions will determine our starting and stopping value for voice
|
//these conditions will determine our starting and stopping value for voice
|
||||||
@@ -69,7 +68,7 @@ void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182
|
|||||||
if (voice == 2) start = 2;
|
if (voice == 2) start = 2;
|
||||||
if (voice == 2 || voice == 3) stop = 4;
|
if (voice == 2 || voice == 3) stop = 4;
|
||||||
|
|
||||||
for (start; start < stop; start++)
|
for (; start < stop; start++)
|
||||||
{
|
{
|
||||||
w = nW;
|
w = nW;
|
||||||
x = nX;
|
x = nX;
|
||||||
@@ -90,6 +89,13 @@ void nxdn_voice (dsd_opts * opts, dsd_state * state, int voice, uint8_t dbuf[182
|
|||||||
|
|
||||||
}
|
}
|
||||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||||
|
|
||||||
|
memcpy (state->f_l, state->audio_out_temp_buf, sizeof(state->f_l));
|
||||||
|
|
||||||
|
if (opts->floating_point == 0 )
|
||||||
|
playSynthesizedVoiceMS(opts, state);
|
||||||
|
if (opts->floating_point == 1)
|
||||||
|
playSynthesizedVoiceFM(opts, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->payload == 1)
|
if (opts->payload == 1)
|
||||||
|
|||||||
167
src/p25_12.c
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* p25_12.c
|
||||||
|
* P25p1 1/2 Rate Simple Trellis Decoder
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2023-10 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
uint8_t p25_interleave[98] = {
|
||||||
|
0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97,
|
||||||
|
2, 3, 10, 11, 18, 19, 26, 27, 34, 35, 42, 43, 50, 51, 58, 59, 66, 67, 74, 75, 82, 83, 90, 91,
|
||||||
|
4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93,
|
||||||
|
6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95};
|
||||||
|
|
||||||
|
//this is a convertion table for converting the dibit pairs into constellation points
|
||||||
|
uint8_t p25_constellation_map[16] = {
|
||||||
|
11, 12, 0, 7, 14, 9, 5, 2, 10, 13, 1, 6, 15, 8, 4, 3
|
||||||
|
};
|
||||||
|
|
||||||
|
//digitized dibit to OTA symbol conversion for reference
|
||||||
|
//0 = +1; 1 = +3;
|
||||||
|
//2 = -1; 3 = -3;
|
||||||
|
|
||||||
|
//finite state machine values
|
||||||
|
uint8_t p25_fsm[16] = {
|
||||||
|
0,15,12,3,
|
||||||
|
4,11,8,7,
|
||||||
|
13,2,1,14,
|
||||||
|
9,6,5,10 };
|
||||||
|
|
||||||
|
//this is a dibit-pair to trellis dibit transition matrix (SDRTrunk and Ossmann)
|
||||||
|
//when evaluating hamming distance, we want to use this xor the dibit-pair nib,
|
||||||
|
//and not the fsm xor the constellation point
|
||||||
|
uint8_t p25_dtm[16] = {
|
||||||
|
2,12,1,15,
|
||||||
|
14,0,13,3,
|
||||||
|
9,7,10,4,
|
||||||
|
5,11,6,8 };
|
||||||
|
|
||||||
|
int count_bits(uint8_t b, int slen)
|
||||||
|
{
|
||||||
|
int i = 0; int j = 0;
|
||||||
|
for (j = 0; j < slen; j++)
|
||||||
|
{
|
||||||
|
if ( (b & 1) == 1) i++;
|
||||||
|
b = b >> 1;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t find_min(uint8_t list[4], int len)
|
||||||
|
{
|
||||||
|
int min = list[0];
|
||||||
|
uint8_t index = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 1; i < len; i++)
|
||||||
|
{
|
||||||
|
if (list[i] < min)
|
||||||
|
{
|
||||||
|
min = list[i];
|
||||||
|
index = (uint8_t)i;
|
||||||
|
}
|
||||||
|
|
||||||
|
//NOTE: Disqualifying result on uniqueness can impact decoding
|
||||||
|
//let the CRC determine if the result is good or bad
|
||||||
|
//its not uncommon for two values of the same min
|
||||||
|
//distance to emerge, so its 50/50 each time
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int p25_12(uint8_t * input, uint8_t treturn[12])
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
int irr_err = 0;
|
||||||
|
|
||||||
|
uint8_t deinterleaved_dibits[98];
|
||||||
|
memset (deinterleaved_dibits, 0, sizeof(deinterleaved_dibits));
|
||||||
|
|
||||||
|
//deinterleave our input dibits
|
||||||
|
for (i = 0; i < 98; i++)
|
||||||
|
deinterleaved_dibits[p25_interleave[i]] = input[i];
|
||||||
|
|
||||||
|
//pack the input into nibbles (dibit pairs)
|
||||||
|
uint8_t nibs[49];
|
||||||
|
memset (nibs, 0, sizeof(nibs));
|
||||||
|
|
||||||
|
for (i = 0; i < 49; i++)
|
||||||
|
nibs[i] = (deinterleaved_dibits[i*2+0] << 2) | (deinterleaved_dibits[i*2+1] << 0);
|
||||||
|
|
||||||
|
//convert our dibit pairs into constellation point values
|
||||||
|
uint8_t point[49];
|
||||||
|
memset (point, 0xFF, sizeof(point));
|
||||||
|
|
||||||
|
for (i = 0; i < 49; i++)
|
||||||
|
point[i] = p25_constellation_map[nibs[i]];
|
||||||
|
|
||||||
|
//debug view points
|
||||||
|
// fprintf (stderr, "\n P =");
|
||||||
|
// for (i = 0; i < 49; i++)
|
||||||
|
// fprintf (stderr, " %02d", point[i]);
|
||||||
|
|
||||||
|
//convert constellation points into tdibit values using the FSM
|
||||||
|
uint8_t state = 0;
|
||||||
|
uint8_t tdibits[49]; //trellis dibits 1/2 rate
|
||||||
|
memset (tdibits, 0xF, sizeof(tdibits));
|
||||||
|
uint8_t hd[4]; //array of the four fsm words hamming distance
|
||||||
|
memset (hd, 0, sizeof(hd));
|
||||||
|
uint8_t min = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 49; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
if ( p25_fsm[(state*4)+j] == point[i] )
|
||||||
|
{
|
||||||
|
//return our tdibit value and state for the next point
|
||||||
|
tdibits[i] = state = (uint8_t)j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if tdibit value is greater than 3, then we have a decoding error
|
||||||
|
if (tdibits[i] == 0xF)
|
||||||
|
{
|
||||||
|
irr_err++; //increment number of errors
|
||||||
|
|
||||||
|
//debug position of error and state value
|
||||||
|
// fprintf (stderr, " %d:%d;", i, state);
|
||||||
|
|
||||||
|
//this method only seems to be reliable up to 1-2 bit errors...sometimes
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
hd[j] = count_bits ( ((nibs[i] ^ p25_dtm[(state*4)+j]) & 0xF ), 4 );
|
||||||
|
min = find_min (hd, 4);
|
||||||
|
tdibits[i] = state = min;
|
||||||
|
|
||||||
|
memset (hd, 0, sizeof(hd));
|
||||||
|
min = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug view tdibits/states
|
||||||
|
// fprintf (stderr, "\n T =");
|
||||||
|
// for (i = 0; i < 49; i++)
|
||||||
|
// fprintf (stderr, " %02d", tdibits[i]);
|
||||||
|
|
||||||
|
//pack tdibits into return payload bytes
|
||||||
|
for (i = 0; i < 12; i++)
|
||||||
|
treturn[i] = (tdibits[(i*4)+0] << 6) | (tdibits[(i*4)+1] << 4) | (tdibits[(i*4)+2] << 2) | tdibits[(i*4)+3];
|
||||||
|
|
||||||
|
//trellis point/state err tally
|
||||||
|
// if (irr_err != 0)
|
||||||
|
// fprintf (stderr, " P_ERR = %d", irr_err);
|
||||||
|
|
||||||
|
return (irr_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
168
src/p25_crc.c
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* p25_crc.c
|
||||||
|
* P25 Misc CRC checks
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2022-09 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "dsd.h"
|
||||||
|
|
||||||
|
//modified from the LEH ComputeCrcCCITT to accept variable len buffer bits
|
||||||
|
uint16_t ComputeCrcCCITT16b(const uint8_t buf[], unsigned int len)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint16_t CRC = 0x0000;
|
||||||
|
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 ComputeCrcCCITT() */
|
||||||
|
|
||||||
|
//modified from crc12_ok to run a quickie on 16 instead
|
||||||
|
static uint16_t crc16_ok(const uint8_t bits[], unsigned int len) {
|
||||||
|
uint16_t crc = 0;
|
||||||
|
// fprintf (stderr, "\n LEN = %d", len);
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
crc = crc << 1;
|
||||||
|
crc = crc | bits[i+len];
|
||||||
|
}
|
||||||
|
uint16_t check = ComputeCrcCCITT16b(bits, len);
|
||||||
|
|
||||||
|
if (crc == check)
|
||||||
|
{
|
||||||
|
//fprintf (stderr, " CRC = %04X %04X", crc, check);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//fprintf (stderr, " CRC = %04X %04X", crc, check);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//TSBK/LCCH CRC16 x-bit bridge to crc16b and crc16b_okay, wip, may need multi pdu format as well
|
||||||
|
int crc16_lb_bridge (int payload[190], int len)
|
||||||
|
{
|
||||||
|
int err = -2;
|
||||||
|
uint8_t buf[190] = {0};
|
||||||
|
|
||||||
|
for (int i = 0; i < len+16; i++) //add +16 here so we load the entire frame but only run crc on the len portion
|
||||||
|
{
|
||||||
|
buf[i] = payload[i];
|
||||||
|
}
|
||||||
|
err = crc16_ok(buf, len);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
//borrowing crc12 from OP25
|
||||||
|
static uint16_t crc12(const uint8_t bits[], unsigned int len) {
|
||||||
|
uint16_t crc=0;
|
||||||
|
static const unsigned int K = 12;
|
||||||
|
//g12(x) = x12 + x11 + x7 + x4 + x2 + x + 1
|
||||||
|
static const uint8_t poly[13] = {1,1,0,0,0,1,0,0,1,0,1,1,1}; // p25 p2 crc 12 poly
|
||||||
|
uint8_t buf[256];
|
||||||
|
if (len+K > sizeof(buf)) {
|
||||||
|
//fprintf (stderr, "crc12: buffer length %u exceeds maximum %u\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 ^ 0xfff;
|
||||||
|
}
|
||||||
|
|
||||||
|
//borrowing crc12_ok from OP25
|
||||||
|
static uint16_t crc12_ok(const uint8_t bits[], unsigned int len) {
|
||||||
|
uint16_t crc = 0;
|
||||||
|
for (int i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
crc = crc << 1;
|
||||||
|
crc = crc | bits[i+len];
|
||||||
|
}
|
||||||
|
uint16_t check = crc12(bits,len);
|
||||||
|
|
||||||
|
if (crc == check)
|
||||||
|
{
|
||||||
|
//fprintf (stderr, " CRC = %03X %03X", crc, check);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//fprintf (stderr, " CRC = %03X %03X", crc, check);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//xCCH CRC12 x-bit bridge to crc12b and crc12b_okay
|
||||||
|
int crc12_xb_bridge (int payload[190], int len)
|
||||||
|
{
|
||||||
|
int err = -5;
|
||||||
|
uint8_t buf[190] = {0};
|
||||||
|
|
||||||
|
//need to load up the 12 crc bits as well, else it will fail on the check, but not on the crc calc
|
||||||
|
for (int i = 0; i < len+12; i++)
|
||||||
|
{
|
||||||
|
buf[i] = payload[i];
|
||||||
|
}
|
||||||
|
err = crc12_ok(buf, len);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t lsd_parity[256] = {
|
||||||
|
0x00, 0x39, 0x72, 0x4B, 0xE4, 0xDD, 0x96, 0xAF, 0xF1, 0xC8, 0x83, 0xBA, 0x15, 0x2C, 0x67, 0x5E,
|
||||||
|
0xDB, 0xE2, 0xA9, 0x90, 0x3F, 0x06, 0x4D, 0x74, 0x2A, 0x13, 0x58, 0x61, 0xCE, 0xF7, 0xBC, 0x85,
|
||||||
|
0x8F, 0xB6, 0xFD, 0xC4, 0x6B, 0x52, 0x19, 0x20, 0x7E, 0x47, 0x0C, 0x35, 0x9A, 0xA3, 0xE8, 0xD1,
|
||||||
|
0x54, 0x6D, 0x26, 0x1F, 0xB0, 0x89, 0xC2, 0xFB, 0xA5, 0x9C, 0xD7, 0xEE, 0x41, 0x78, 0x33, 0x0A,
|
||||||
|
0x27, 0x1E, 0x55, 0x6C, 0xC3, 0xFA, 0xB1, 0x88, 0xD6, 0xEF, 0xA4, 0x9D, 0x32, 0x0B, 0x40, 0x79,
|
||||||
|
0xFC, 0xC5, 0x8E, 0xB7, 0x18, 0x21, 0x6A, 0x53, 0x0D, 0x34, 0x7F, 0x46, 0xE9, 0xD0, 0x9B, 0xA2,
|
||||||
|
0xA8, 0x91, 0xDA, 0xE3, 0x4C, 0x75, 0x3E, 0x07, 0x59, 0x60, 0x2B, 0x12, 0xBD, 0x84, 0xCF, 0xF6,
|
||||||
|
0x73, 0x4A, 0x01, 0x38, 0x97, 0xAE, 0xE5, 0xDC, 0x82, 0xBB, 0xF0, 0xC9, 0x66, 0x5F, 0x14, 0x2D,
|
||||||
|
0x4E, 0x77, 0x3C, 0x05, 0xAA, 0x93, 0xD8, 0xE1, 0xBF, 0x86, 0xCD, 0xF4, 0x5B, 0x62, 0x29, 0x10,
|
||||||
|
0x95, 0xAC, 0xE7, 0xDE, 0x71, 0x48, 0x03, 0x3A, 0x64, 0x5D, 0x16, 0x2F, 0x80, 0xB9, 0xF2, 0xCB,
|
||||||
|
0xC1, 0xF8, 0xB3, 0x8A, 0x25, 0x1C, 0x57, 0x6E, 0x30, 0x09, 0x42, 0x7B, 0xD4, 0xED, 0xA6, 0x9F,
|
||||||
|
0x1A, 0x23, 0x68, 0x51, 0xFE, 0xC7, 0x8C, 0xB5, 0xEB, 0xD2, 0x99, 0xA0, 0x0F, 0x36, 0x7D, 0x44,
|
||||||
|
0x69, 0x50, 0x1B, 0x22, 0x8D, 0xB4, 0xFF, 0xC6, 0x98, 0xA1, 0xEA, 0xD3, 0x7C, 0x45, 0x0E, 0x37,
|
||||||
|
0xB2, 0x8B, 0xC0, 0xF9, 0x56, 0x6F, 0x24, 0x1D, 0x43, 0x7A, 0x31, 0x08, 0xA7, 0x9E, 0xD5, 0xEC,
|
||||||
|
0xE6, 0xDF, 0x94, 0xAD, 0x02, 0x3B, 0x70, 0x49, 0x17, 0x2E, 0x65, 0x5C, 0xF3, 0xCA, 0x81, 0xB8,
|
||||||
|
0x3D, 0x04, 0x4F, 0x76, 0xD9, 0xE0, 0xAB, 0x92, 0xCC, 0xF5, 0xBE, 0x87, 0x28, 0x11, 0x5A, 0x63};
|
||||||
|
|
||||||
|
int p25p1_lsd_fec(uint8_t * input)
|
||||||
|
{
|
||||||
|
uint8_t lsd = (uint8_t)ConvertBitIntoBytes(&input[0], 8);
|
||||||
|
uint8_t par = (uint8_t)ConvertBitIntoBytes(&input[8], 8);
|
||||||
|
uint8_t chk = lsd_parity[lsd];
|
||||||
|
//NOTE: Simply checking the parity doesn't account for the parity bits being bad, but the
|
||||||
|
//low speed data bits being correct, but is better than nothing
|
||||||
|
if (chk == par) return 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//TODO: some FEC functions
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* p25_frequency.c
|
* p25_frequency.c
|
||||||
* P25 Channel to Frequency Calculator
|
* P25 Channel to Frequency Calculator
|
||||||
*
|
*
|
||||||
* NXDN Channel to Frequency, Courtesy of IcomIcR20 and EricCottrell (base values)
|
* NXDN Channel to Frequency Calculator
|
||||||
*
|
*
|
||||||
* LWVMOBILE
|
* LWVMOBILE
|
||||||
* 2022-11 DSD-FME Florida Man Edition
|
* 2022-11 DSD-FME Florida Man Edition
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
//P25
|
//P25
|
||||||
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel)
|
long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channel)
|
||||||
{
|
{
|
||||||
|
UNUSED(opts);
|
||||||
|
|
||||||
//RX and SU TX frequencies.
|
//RX and SU TX frequencies.
|
||||||
//SU RX = (Base Frequency) + (Channel Number) x (Channel Spacing).
|
//SU RX = (Base Frequency) + (Channel Number) x (Channel Spacing).
|
||||||
@@ -24,6 +25,10 @@ long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channe
|
|||||||
(Channel Spacing) x (0.125 kHz).
|
(Channel Spacing) x (0.125 kHz).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//return 0 if channel value is 0 or 0xFFFF
|
||||||
|
if (channel == 0) return 0;
|
||||||
|
if (channel == 0xFFFF) return 0;
|
||||||
|
|
||||||
//Note: Base Frequency is calculated as (Base Frequency) x (0.000005 MHz) from the IDEN_UP message.
|
//Note: Base Frequency is calculated as (Base Frequency) x (0.000005 MHz) from the IDEN_UP message.
|
||||||
|
|
||||||
long int freq = -1;
|
long int freq = -1;
|
||||||
@@ -59,16 +64,18 @@ long int process_channel_to_freq (dsd_opts * opts, dsd_state * state, int channe
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//NXDN Channel to Frequency, Courtesy of IcomIcR20 on RR Forums
|
|
||||||
long int nxdn_channel_to_frequency(dsd_opts * opts, dsd_state * state, uint16_t channel)
|
long int nxdn_channel_to_frequency(dsd_opts * opts, dsd_state * state, uint16_t channel)
|
||||||
{
|
{
|
||||||
|
UNUSED(opts);
|
||||||
|
|
||||||
long int freq;
|
long int freq;
|
||||||
long int base = 0;
|
long int base = 0;
|
||||||
|
long int step = 0;
|
||||||
|
|
||||||
//the base freq value is per EricCottrell in the Understanding NXDN Trunking Forum Thread
|
//reworked to include Direct Frequency Assignment if available
|
||||||
//will need to do some research or tests to confirm these values are accurate
|
|
||||||
|
|
||||||
//first, check channel map
|
//first, check channel map for imported value, DFA systems most likely won't need an import,
|
||||||
|
//unless it has 'system definable' attributes
|
||||||
if (state->trunk_chan_map[channel] != 0)
|
if (state->trunk_chan_map[channel] != 0)
|
||||||
{
|
{
|
||||||
freq = state->trunk_chan_map[channel];
|
freq = state->trunk_chan_map[channel];
|
||||||
@@ -76,33 +83,41 @@ long int nxdn_channel_to_frequency(dsd_opts * opts, dsd_state * state, uint16_t
|
|||||||
return (freq);
|
return (freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if not found, attempt to find it via calculation
|
//then, let's see if its DFA instead -- 6.5.36
|
||||||
else
|
else if (state->nxdn_rcn == 1)
|
||||||
{
|
{
|
||||||
if ((channel > 0) && (channel <= 400))
|
//determine the base frequency in Hz
|
||||||
{
|
if (state->nxdn_base_freq == 1) base = 100000000; //100 MHz
|
||||||
if (opts->frame_nxdn48 == 1) base = 450000000;
|
else if (state->nxdn_base_freq == 2) base = 330000000; //330 Mhz
|
||||||
else base = 451000000;
|
else if (state->nxdn_base_freq == 3) base = 400000000; //400 Mhz
|
||||||
|
else if (state->nxdn_base_freq == 4) base = 750000000; //750 Mhz
|
||||||
|
else base = 0; //just set to zero, will be system definable most likely and won't be able to calc
|
||||||
|
|
||||||
freq = base + (channel - 1) * 12500;
|
//determine the step value in Hz
|
||||||
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
if (state->nxdn_step == 2) step = 1250; //1.25 kHz
|
||||||
return (freq);
|
else if (state->nxdn_step == 3) step = 3125; //3.125 kHz
|
||||||
}
|
else step = 0; //just set to zero, will be system definable most likely and won't be able to calc
|
||||||
else if ((channel >= 401) && (channel <= 800))
|
|
||||||
{
|
|
||||||
if (opts->frame_nxdn48 == 1) base = 460000000;
|
|
||||||
else base = 461000000;
|
|
||||||
|
|
||||||
freq = base + (channel - 401) * 12500;
|
//if we have a valid base and step, then calc frequency
|
||||||
fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
//6.5.45. Outbound/Inbound Frequency Number (OFN/IFN)
|
||||||
|
if (base && step)
|
||||||
|
{
|
||||||
|
freq = base + (channel * step);
|
||||||
|
fprintf (stderr, "\n DFA Frequency [%.6lf] MHz", (double)freq/1000000);
|
||||||
return (freq);
|
return (freq);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf(stderr, "\n Non-standard frequency or channel not found in import file");
|
fprintf(stderr, "\n Custom DFA Settings -- Unknown Freq;");
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\n Channel not found in import file");
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
597
src/p25_lcw.c
@@ -1,160 +1,483 @@
|
|||||||
|
/*-------------------------------------------------------------------------------
|
||||||
|
* p25_lcw.c
|
||||||
|
* P25p1 Link Control Word Decoding
|
||||||
|
*
|
||||||
|
* LWVMOBILE
|
||||||
|
* 2023-05 DSD-FME Florida Man Edition
|
||||||
|
*-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#include "dsd.h"
|
#include "dsd.h"
|
||||||
|
|
||||||
void
|
//new p25_lcw function here -- TIA-102.AABF-D LCW Format Messages (if anybody wants to fill the rest out)
|
||||||
processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo)
|
void p25_lcw (dsd_opts * opts, dsd_state * state, uint8_t LCW_bits[], uint8_t irrecoverable_errors)
|
||||||
{
|
{
|
||||||
|
UNUSED(irrecoverable_errors);
|
||||||
|
|
||||||
char tgid[17], tmpstr[255];
|
uint8_t lc_format = (uint8_t)ConvertBitIntoBytes(&LCW_bits[0], 8); //format
|
||||||
long talkgroup, source;
|
uint8_t lc_opcode = (uint8_t)ConvertBitIntoBytes(&LCW_bits[2], 6); //opcode portion of format
|
||||||
int i, j;
|
uint8_t lc_mfid = (uint8_t)ConvertBitIntoBytes(&LCW_bits[8], 8); //mfid
|
||||||
|
uint8_t lc_svcopt = (uint8_t)ConvertBitIntoBytes(&LCW_bits[16], 8); //service options
|
||||||
|
uint8_t lc_pf = LCW_bits[0]; //protect flag
|
||||||
|
uint8_t lc_sf = LCW_bits[1]; //Implicit / Explicit MFID Format
|
||||||
|
UNUSED2(lc_opcode, lc_sf);
|
||||||
|
|
||||||
tgid[16] = 0;
|
if (lc_pf == 1) //check the protect flag -- if set, its an encrypted lcw
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s",KRED);
|
||||||
|
fprintf (stderr, " LCW Protected ");
|
||||||
|
fprintf (stderr, "%s",KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
if (opts->p25lc == 1)
|
if (lc_pf == 0) //not protected/encrypted lcw
|
||||||
{
|
{
|
||||||
fprintf (stderr, "lcformat: %s mfid: %s lcinfo: %s ", lcformat, mfid, lcinfo);
|
|
||||||
if (opts->p25tg == 0)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp (lcformat, "00000100") == 0)
|
if (opts->payload == 1) fprintf (stderr, " LCW");
|
||||||
|
|
||||||
|
//check to see if we need to run these as MFID 0 or 1 only (standard)
|
||||||
|
if (lc_mfid == 0 || lc_mfid == 1) //lc_mfid == 0
|
||||||
{
|
{
|
||||||
|
|
||||||
// first tg is the active channel
|
//check the service options on applicable formats
|
||||||
j = 0;
|
if (lc_format == 0x4A || lc_format == 0x46 || lc_format == 0x45
|
||||||
for (i = 40; i < 52; i++)
|
|| lc_format == 0x44 || lc_format == 0x03 || lc_format == 0x00)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (lc_svcopt & 0x80) fprintf (stderr, " Emergency");
|
||||||
|
if (lc_svcopt & 0x40) fprintf (stderr, " Encrypted");
|
||||||
|
|
||||||
|
if (opts->payload == 1) //hide behind payload due to len
|
||||||
{
|
{
|
||||||
if (state->tgcount < 24)
|
if (lc_svcopt & 0x20) fprintf (stderr, " Duplex");
|
||||||
{
|
if (lc_svcopt & 0x10) fprintf (stderr, " Packet");
|
||||||
state->tg[state->tgcount][j] = lcinfo[i];
|
else fprintf (stderr, " Circuit");
|
||||||
}
|
if (lc_svcopt & 0x8) fprintf (stderr, " R"); //reserved bit is on
|
||||||
tmpstr[j] = lcinfo[i];
|
fprintf (stderr, " Priority %d", lc_svcopt & 0x7); //call priority
|
||||||
j++;
|
|
||||||
}
|
}
|
||||||
tmpstr[12] = 48;
|
}
|
||||||
tmpstr[13] = 48;
|
|
||||||
tmpstr[14] = 48;
|
if (lc_format == 0x00)
|
||||||
tmpstr[15] = 48;
|
{
|
||||||
tmpstr[16] = 0;
|
fprintf (stderr, " Group Voice Channel User");
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
uint8_t res = (uint8_t)ConvertBitIntoBytes(&LCW_bits[24], 7);
|
||||||
state->lasttg = talkgroup;
|
uint8_t explicit = LCW_bits[24]; //explicit source id required == 1, full SUID on seperate LCW
|
||||||
if (state->tgcount < 24)
|
uint16_t group = (uint16_t)ConvertBitIntoBytes(&LCW_bits[32], 16);
|
||||||
|
uint32_t source = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||||||
|
fprintf (stderr, " - Group %d Source %d", group, source);
|
||||||
|
UNUSED2(res, explicit);
|
||||||
|
|
||||||
|
//don't set this when zero, annoying blink occurs in ncurses
|
||||||
|
if (group != 0) state->lasttg = group;
|
||||||
|
if (source != 0) state->lastsrc = source;
|
||||||
|
|
||||||
|
sprintf (state->call_string[0], " Group ");
|
||||||
|
if (lc_svcopt & 0x80) strcat (state->call_string[0], " Emergency ");
|
||||||
|
else if (lc_svcopt & 0x40) strcat (state->call_string[0], " Encrypted ");
|
||||||
|
else strcat (state->call_string[0], " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x03)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Unit to Unit Voice Channel User");
|
||||||
|
uint32_t target = (uint32_t)ConvertBitIntoBytes(&LCW_bits[24], 24);
|
||||||
|
uint32_t source = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||||||
|
fprintf (stderr, " - Target %d Source %d", target, source);
|
||||||
|
|
||||||
|
//don't set this when zero, annoying blink occurs in ncurses
|
||||||
|
if (target != 0) state->lasttg = target;
|
||||||
|
if (source != 0) state->lastsrc = source;
|
||||||
|
|
||||||
|
sprintf (state->call_string[0], " Private ");
|
||||||
|
if (lc_svcopt & 0x80) strcat (state->call_string[0], " Emergency ");
|
||||||
|
else if (lc_svcopt & 0x40) strcat (state->call_string[0], " Encrypted ");
|
||||||
|
else strcat (state->call_string[0], " ");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Allow Tuning from Call Grants either in LDU1 or TDULC? (TDMA to p1 fallback?)
|
||||||
|
//TODO: Allow TG Hold overrides here either in LDU1 or TDULC?
|
||||||
|
//NOTE: If we have an active TG hold, we really should't be here anyways
|
||||||
|
else if (lc_format == 0x42) //is this group only, or group and private?
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Group Voice Channel Update - ");
|
||||||
|
uint16_t channel1 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[8], 16);
|
||||||
|
uint16_t group1 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[24], 16);
|
||||||
|
uint16_t channel2 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[40], 16);
|
||||||
|
uint16_t group2 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[56], 16);
|
||||||
|
|
||||||
|
if (channel1 && group1)
|
||||||
{
|
{
|
||||||
state->tgcount = state->tgcount + 1;
|
fprintf (stderr, "Ch: %04X TG: %d; ", channel1, group1);
|
||||||
}
|
sprintf (state->active_channel[0], "Active Ch: %04X TG: %d; ", channel1, group1);
|
||||||
if (opts->p25tg == 1)
|
state->last_active_time = time(NULL);
|
||||||
{
|
|
||||||
fprintf (stderr, "tg: %li ", talkgroup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->p25tg == 1)
|
if (channel2 && group2 && group1 != group2)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "tg: %li ", talkgroup);
|
fprintf (stderr, "Ch: %04X TG: %d; ", channel2, group2);
|
||||||
|
sprintf (state->active_channel[1], "Active Ch: %04X TG: %d; ", channel2, group2);
|
||||||
// the remaining 3 appear to be other active tg's on the system
|
state->last_active_time = time(NULL);
|
||||||
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);
|
|
||||||
fprintf (stderr, "%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);
|
|
||||||
fprintf (stderr, "%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);
|
|
||||||
fprintf (stderr, "%li\n", talkgroup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x44)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Group Voice Channel Update – Explicit");
|
||||||
|
uint16_t group1 = (uint16_t)ConvertBitIntoBytes(&LCW_bits[24], 16);
|
||||||
|
uint16_t channelt = (uint16_t)ConvertBitIntoBytes(&LCW_bits[40], 16);
|
||||||
|
uint16_t channelr = (uint16_t)ConvertBitIntoBytes(&LCW_bits[56], 16);
|
||||||
|
fprintf (stderr, "Ch: %04X TG: %d; ", channelt, group1);
|
||||||
|
UNUSED(channelr);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x45)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Unit to Unit Answer Request");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x46)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Telephone Interconnect Voice Channel User");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x47)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Telephone Interconnect Answer Request");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x49)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Source ID Extension -");
|
||||||
|
uint32_t nid = (uint32_t)ConvertBitIntoBytes(&LCW_bits[16], 24);
|
||||||
|
uint32_t src = (uint32_t)ConvertBitIntoBytes(&LCW_bits[40], 24);
|
||||||
|
fprintf (stderr, " Full SUID: %08X-%08d", nid, src);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x4A)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Unit to Unit Voice Channel User – Extended");
|
||||||
|
uint32_t target = (uint32_t)ConvertBitIntoBytes(&LCW_bits[16], 24);
|
||||||
|
uint32_t src = (uint32_t)ConvertBitIntoBytes(&LCW_bits[40], 24);
|
||||||
|
fprintf (stderr, "TGT: %d; SRC: %d; ", target, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x50)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Group Affiliation Query");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x51)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Unit Registration Command");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x52) //wonder if anybody uses this if its deleted/obsolete
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Unit Authentication Command - OBSOLETE");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x53)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Status Query");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x54)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Status Update");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x55)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Status Update");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x56)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Call Alert");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
else if (lc_format == 0x5A)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Status Update – Source ID Extension Required");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x5C)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Extended Function Command – Source ID Extension Required");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x60)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " System Service Broadcast");
|
||||||
|
}
|
||||||
|
|
||||||
|
//this PDU does not have an associated MFID, often seen on kiwi and matches its TSBK counterpart
|
||||||
|
//its possible some of the other ones here don't as well, need to re-check all of them
|
||||||
|
else if (lc_format == 0x61)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Secondary Control Channel Broadcast");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x62)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Adjacent Site Status Broadcast");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x63)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " RFSS Status Broadcast");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x64)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Network Status Broadcast");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x65)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Protection Parameter Broadcast - OBSOLETE");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
else if (lc_format == 0x66)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Secondary Control Channel Broadcast – Explicit (LCSCBX)");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x67) //explicit
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Adjacent Site Status (LCASBX)");
|
||||||
|
uint8_t lra = (uint8_t)ConvertBitIntoBytes(&LCW_bits[8], 8);
|
||||||
|
uint16_t channelt = (uint16_t)ConvertBitIntoBytes(&LCW_bits[16], 16);
|
||||||
|
uint8_t rfssid = (uint8_t)ConvertBitIntoBytes(&LCW_bits[32], 8);
|
||||||
|
uint8_t siteid = (uint8_t)ConvertBitIntoBytes(&LCW_bits[40], 8);
|
||||||
|
uint16_t channelr = (uint16_t)ConvertBitIntoBytes(&LCW_bits[48], 16);
|
||||||
|
uint8_t cfva = (uint8_t)ConvertBitIntoBytes(&LCW_bits[64], 4);
|
||||||
|
fprintf (stderr, " - RFSS %d Site %d CH %04X", rfssid, siteid, channelt);
|
||||||
|
UNUSED2(lra, channelr);
|
||||||
|
|
||||||
|
//debug print only
|
||||||
|
// fprintf (stderr, "\n ");
|
||||||
|
// fprintf (stderr, " LRA [%02X] RFSS [%03d] SITE [%03d] CHAN-T [%04X] CHAN-R [%02X] CFVA [%X]\n ", lra, rfssid, siteid, channelt, channelr, cfva);
|
||||||
|
// if (cfva & 0x8) fprintf (stderr, " Conventional");
|
||||||
|
// if (cfva & 0x4) fprintf (stderr, " Failure Condition");
|
||||||
|
// if (cfva & 0x2) fprintf (stderr, " Up to Date (Correct)");
|
||||||
|
// else fprintf (stderr, " Last Known");
|
||||||
|
if (cfva & 0x1) fprintf (stderr, " - Connection Active");
|
||||||
|
// process_channel_to_freq (opts, state, channelt);
|
||||||
|
//end debug, way too much for a simple link control line
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x68)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " RFSS Status Broadcast – Explicit (LCRSBX)");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x69)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Network Status Broadcast – Explicit (LCNSBX)");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x6A)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Conventional Fallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_format == 0x6B)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Message Update – Source ID Extension Required");
|
||||||
|
}
|
||||||
|
|
||||||
|
//tune back to CC here - save about 1-2 seconds
|
||||||
|
else if (lc_format == 0x4F) //# Call Termination/Cancellation
|
||||||
|
{
|
||||||
|
uint32_t tgt = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24); //can be individual, or all units (0xFFFFFF)
|
||||||
|
fprintf (stderr, " Call Termination; TGT: %d;", tgt);
|
||||||
|
if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && opts->p25_is_tuned == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
//Will we need to check for a symbolrate change here, can a P25p2 TDMA-CC system
|
||||||
|
//revert back to a phase 1 traffic channel or carry a phase 1 traffic channel?
|
||||||
|
if (state->p25_cc_is_tdma == 1)
|
||||||
|
{
|
||||||
|
state->samplesPerSymbol = 8;
|
||||||
|
state->symbolCenter = 3;
|
||||||
|
opts->frame_p25p1 = 0; //turn it back off
|
||||||
|
}
|
||||||
|
|
||||||
|
//clear heuristics from current traffic channel
|
||||||
|
if (opts->frame_p25p1 == 1 && opts->use_heuristics == 1)
|
||||||
|
{
|
||||||
|
initialize_p25_heuristics(&state->p25_heuristics);
|
||||||
|
initialize_p25_heuristics(&state->inv_p25_heuristics);
|
||||||
|
}
|
||||||
|
|
||||||
|
//re-enable both slots (failsafe)
|
||||||
|
opts->slot1_on = 1;
|
||||||
|
opts->slot2_on = 1;
|
||||||
|
|
||||||
|
//rigctl
|
||||||
|
if (opts->use_rigctl == 1)
|
||||||
|
{
|
||||||
|
state->lasttg = 0;
|
||||||
|
state->lastsrc = 0;
|
||||||
|
state->payload_algid = 0;
|
||||||
|
state->payload_keyid = 0;
|
||||||
|
// state->payload_miP = 0;
|
||||||
|
//reset some strings
|
||||||
|
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->active_channel[0], "%s", "");
|
||||||
|
sprintf (state->active_channel[1], "%s", "");
|
||||||
|
opts->p25_is_tuned = 0;
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
|
||||||
|
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||||
|
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
|
||||||
|
}
|
||||||
|
//rtl
|
||||||
|
else if (opts->audio_in_type == 3)
|
||||||
|
{
|
||||||
|
#ifdef USE_RTLSDR
|
||||||
|
state->lasttg = 0;
|
||||||
|
state->lastsrc = 0;
|
||||||
|
state->payload_algid = 0;
|
||||||
|
state->payload_keyid = 0;
|
||||||
|
// state->payload_miP = 0;
|
||||||
|
//reset some strings
|
||||||
|
sprintf (state->call_string[0], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->call_string[1], "%s", " "); //21 spaces
|
||||||
|
sprintf (state->active_channel[0], "%s", "");
|
||||||
|
sprintf (state->active_channel[1], "%s", "");
|
||||||
|
opts->p25_is_tuned = 0;
|
||||||
|
state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0;
|
||||||
|
rtl_dev_tune (opts, state->p25_cc_freq);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else fprintf (stderr, " Unknown Format %02X MFID %02X SVC %02X", lc_format, lc_mfid, lc_svcopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (strcmp (lcformat, "00000000") == 0)
|
//TODO: Look through all LCW format messages and move here if they don't use the MFID field
|
||||||
|
//just going to add/fix a few values I've observed for now, one issue with doing so may
|
||||||
|
//be where there is a reserved field that is also used as an MFID, i.e., Call Termination 0xF vs Moto Talker EOT 0xF with
|
||||||
|
//the reserved field showing the 0x90 for moto in it, and Harris 0xA vs Unit to Unit Voice Call 0xA
|
||||||
|
|
||||||
|
//TODO: Add Identification of Special SU Address values
|
||||||
|
//0 - No Unit
|
||||||
|
//1-0xFFFFFB - Assignable Units
|
||||||
|
//0xFFFFFC - FNE 16777212
|
||||||
|
//0xFFFFFD - System Default (FNE Calling Functions, Registration, Mobility)
|
||||||
|
//0xFFFFFE - Registration Default (registration transactions from SU)
|
||||||
|
//0xFFFFFF - All Units
|
||||||
|
|
||||||
|
//This lc_format doesn't use the MFID field
|
||||||
|
else if (lc_format == 0x42)
|
||||||
|
fprintf (stderr, " Conventional Fallback Indication");
|
||||||
|
|
||||||
|
//This lc_format doesn't use the MFID field
|
||||||
|
else if (lc_format == 0x57)
|
||||||
|
fprintf (stderr, " Extended Function Command");
|
||||||
|
|
||||||
|
//This lc_format doesn't use the MFID field
|
||||||
|
else if (lc_format == 0x58)
|
||||||
{
|
{
|
||||||
j = 0;
|
uint8_t iden = (uint8_t)ConvertBitIntoBytes(&LCW_bits[8], 4);
|
||||||
if (strcmp (mfid, "10010000") == 0)
|
uint32_t base = (uint32_t)ConvertBitIntoBytes(&LCW_bits[40], 32);
|
||||||
{
|
fprintf (stderr, " Channel Identifier Update VU; Iden: %X; Base: %d;", iden, base*5);
|
||||||
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 (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
state->tgcount = state->tgcount + 1;
|
|
||||||
}
|
|
||||||
if (opts->p25tg == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "tg: %li ", talkgroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
j = 0;
|
//This lc_format doesn't use the MFID field
|
||||||
for (i = 32; i < 56; i++)
|
else if (lc_format == 0x59)
|
||||||
{
|
|
||||||
tmpstr[j] = lcinfo[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
tmpstr[24] = 0;
|
|
||||||
source = strtol (tmpstr, NULL, 2);
|
|
||||||
state->lastsrc = source;
|
|
||||||
if (opts->p25tg == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "src: %li emr: %c\n", source, lcinfo[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((opts->p25tg == 1) && (opts->p25lc == 1))
|
|
||||||
{
|
{
|
||||||
fprintf (stderr, "\n");
|
uint8_t iden = (uint8_t)ConvertBitIntoBytes(&LCW_bits[8], 4);
|
||||||
|
uint32_t base = (uint32_t)ConvertBitIntoBytes(&LCW_bits[40], 32);
|
||||||
|
fprintf (stderr, " Channel Identifier Update VU; Iden: %X; Base: %d;", iden, base*5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//This lc_format doesn't use the MFID field
|
||||||
|
else if (lc_format == 0x61)
|
||||||
|
fprintf (stderr, " Secondary Control Channel Broadcast");
|
||||||
|
|
||||||
|
//This lc_format doesn't use the MFID field
|
||||||
|
else if (lc_format == 0x62)
|
||||||
|
fprintf (stderr, " Adjacent Site Status Broadcast");
|
||||||
|
|
||||||
|
//This lc_format doesn't use the MFID field
|
||||||
|
else if (lc_format == 0x63)
|
||||||
|
fprintf (stderr, " RFSS Status Broadcast");
|
||||||
|
|
||||||
|
//MFID 90 Embedded GPS
|
||||||
|
else if (lc_mfid == 0x90 && lc_opcode == 0x6)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " MFID90 (Moto)");
|
||||||
|
apx_embedded_gps (opts, state, LCW_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_mfid == 0x90 && lc_opcode == 0x0)
|
||||||
|
{
|
||||||
|
//needed to fill this in, since tuning this on P1 will just leave the TG/SRC as zeroes
|
||||||
|
fprintf (stderr, " MFID90 (Moto) Group Regroup Channel User (LCGRGR)");
|
||||||
|
uint32_t sg = (uint32_t)ConvertBitIntoBytes(&LCW_bits[32], 16);
|
||||||
|
uint32_t src = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||||||
|
fprintf (stderr, " SG: %d; SRC: %d;", sg, src);
|
||||||
|
if (LCW_bits[16] == 1) fprintf (stderr, " Res;"); //res bit (octet 2)
|
||||||
|
if (LCW_bits[17] == 1) fprintf (stderr, " ENC;"); //P-bit (octet 2)
|
||||||
|
if (LCW_bits[31] == 1) fprintf (stderr, " EXT;"); //Full SUID next LC (external) (octet 3)
|
||||||
|
state->lasttg = sg;
|
||||||
|
state->lastsrc = src;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_mfid == 0x90 && lc_opcode == 0x1)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " MFID90 (Moto) Group Regroup Channel Update (LCGRGU)");
|
||||||
|
uint32_t sg = (uint32_t)ConvertBitIntoBytes(&LCW_bits[24], 16);
|
||||||
|
uint32_t ch = (uint32_t)ConvertBitIntoBytes(&LCW_bits[56], 16);
|
||||||
|
fprintf (stderr, " SG: %d; CH: %04X;", sg, ch);
|
||||||
|
if (LCW_bits[16] == 1) fprintf (stderr, " Res;"); //res bit (octet 2)
|
||||||
|
if (LCW_bits[17] == 1) fprintf (stderr, " ENC;"); //P-bit (octet 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lc_mfid == 0x90 && lc_opcode == 0x3)
|
||||||
|
fprintf (stderr, " MFID90 (Moto) Group Regroup Add");
|
||||||
|
|
||||||
|
else if (lc_mfid == 0x90 && lc_opcode == 0x4)
|
||||||
|
fprintf (stderr, " MFID90 (Moto) Group Regroup Delete");
|
||||||
|
|
||||||
|
else if (lc_mfid == 0x90 && lc_opcode == 0xF)
|
||||||
|
{
|
||||||
|
uint32_t src = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||||||
|
fprintf (stderr, " MFID90 (Moto) Talker EOT; SRC: %d;", src);
|
||||||
|
}
|
||||||
|
|
||||||
|
//observed format value on Harris SNDCP data channel (Phase 2 CC to Phase 1 MPDU channel)
|
||||||
|
else if (lc_mfid == 0xA4 && lc_format == 0x0A)
|
||||||
|
{
|
||||||
|
//Could also just be a Unit to Unit Voice Channel User using the reserved field as the MFID?
|
||||||
|
//if it were similar to Unit to Unit though, the TGT and SRC values seem to be reversed
|
||||||
|
//this appears to be a data channel indicator, has a matching target and the FNE address in it
|
||||||
|
uint32_t src = (uint32_t)ConvertBitIntoBytes(&LCW_bits[24], 24);
|
||||||
|
uint32_t tgt = (uint32_t)ConvertBitIntoBytes(&LCW_bits[48], 24);
|
||||||
|
fprintf (stderr, " MFIDA4 (Harris) Data Channel; SRC: %d; TGT: %d;", src, tgt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//not a duplicate, this one will print if not MFID 0 or 1
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Unknown Format %02X MFID %02X ", lc_format, lc_mfid);
|
||||||
|
if (lc_mfid == 0x90) fprintf (stderr, "(Moto)");
|
||||||
|
else if (lc_mfid == 0xA4) fprintf (stderr, "(Harris)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ending line break
|
||||||
|
fprintf (stderr, "\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,276 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------------
|
|
||||||
* p25p1_block.c
|
|
||||||
* P25 Block Bridge for TSBK Deinterleaving, CRC12 and CRC16 checks
|
|
||||||
*
|
|
||||||
* original copyrights for portions used below (OP25, Ossman, and LEH)
|
|
||||||
*
|
|
||||||
* LWVMOBILE
|
|
||||||
* 2022-09 DSD-FME Florida Man Edition
|
|
||||||
*-----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#include "dsd.h"
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
typedef std::vector<bool> bit_vector;
|
|
||||||
|
|
||||||
/* adapted from wireshark/plugins/p25/packet-p25cai.c */
|
|
||||||
/* Copyright 2008, Michael Ossmann <mike@ossmann.com> */
|
|
||||||
/* deinterleave and trellis1_2 decoding, count_bits, and find_min*/
|
|
||||||
/* buf is assumed to be a buffer of 12 bytes */
|
|
||||||
|
|
||||||
//typedef std::vector<bool> bit_vector;
|
|
||||||
|
|
||||||
//ugly copy and paste below
|
|
||||||
static int count_bits(unsigned int n) {
|
|
||||||
int i = 0;
|
|
||||||
for (i = 0; n != 0; i++)
|
|
||||||
n &= n - 1;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int find_min(uint8_t list[], int len) {
|
|
||||||
int min = list[0];
|
|
||||||
int index = 0;
|
|
||||||
int unique = 1;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 1; i < len; i++) {
|
|
||||||
if (list[i] < min) {
|
|
||||||
min = list[i];
|
|
||||||
index = i;
|
|
||||||
unique = 1;
|
|
||||||
} else if (list[i] == min) {
|
|
||||||
unique = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* return -1 if a minimum can't be found */
|
|
||||||
if (!unique)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int block_deinterleave(bit_vector& bv, unsigned int start, uint8_t* buf)
|
|
||||||
{
|
|
||||||
static const uint16_t deinterleave_tb[] = {
|
|
||||||
0, 1, 2, 3, 52, 53, 54, 55, 100,101,102,103, 148,149,150,151,
|
|
||||||
4, 5, 6, 7, 56, 57, 58, 59, 104,105,106,107, 152,153,154,155,
|
|
||||||
8, 9, 10, 11, 60, 61, 62, 63, 108,109,110,111, 156,157,158,159,
|
|
||||||
12, 13, 14, 15, 64, 65, 66, 67, 112,113,114,115, 160,161,162,163,
|
|
||||||
16, 17, 18, 19, 68, 69, 70, 71, 116,117,118,119, 164,165,166,167,
|
|
||||||
20, 21, 22, 23, 72, 73, 74, 75, 120,121,122,123, 168,169,170,171,
|
|
||||||
24, 25, 26, 27, 76, 77, 78, 79, 124,125,126,127, 172,173,174,175,
|
|
||||||
28, 29, 30, 31, 80, 81, 82, 83, 128,129,130,131, 176,177,178,179,
|
|
||||||
32, 33, 34, 35, 84, 85, 86, 87, 132,133,134,135, 180,181,182,183,
|
|
||||||
36, 37, 38, 39, 88, 89, 90, 91, 136,137,138,139, 184,185,186,187,
|
|
||||||
40, 41, 42, 43, 92, 93, 94, 95, 140,141,142,143, 188,189,190,191,
|
|
||||||
44, 45, 46, 47, 96, 97, 98, 99, 144,145,146,147, 192,193,194,195,
|
|
||||||
48, 49, 50, 51 };
|
|
||||||
|
|
||||||
uint8_t hd[4];
|
|
||||||
int b, d, j;
|
|
||||||
int state = 0;
|
|
||||||
uint8_t codeword;
|
|
||||||
|
|
||||||
static const uint8_t next_words[4][4] = {
|
|
||||||
{0x2, 0xC, 0x1, 0xF},
|
|
||||||
{0xE, 0x0, 0xD, 0x3},
|
|
||||||
{0x9, 0x7, 0xA, 0x4},
|
|
||||||
{0x5, 0xB, 0x6, 0x8}
|
|
||||||
};
|
|
||||||
|
|
||||||
memset(buf, 0, 12);
|
|
||||||
|
|
||||||
for (b=0; b < 98*2; b += 4) {
|
|
||||||
codeword = (bv[start+deinterleave_tb[b+0]] << 3) +
|
|
||||||
(bv[start+deinterleave_tb[b+1]] << 2) +
|
|
||||||
(bv[start+deinterleave_tb[b+2]] << 1) +
|
|
||||||
bv[start+deinterleave_tb[b+3]] ;
|
|
||||||
|
|
||||||
/* try each codeword in a row of the state transition table */
|
|
||||||
for (j = 0; j < 4; j++) {
|
|
||||||
/* find Hamming distance for candidate */
|
|
||||||
hd[j] = count_bits(codeword ^ next_words[state][j]);
|
|
||||||
}
|
|
||||||
/* find the dibit that matches the most codeword bits (minimum Hamming distance) */
|
|
||||||
state = find_min(hd, 4);
|
|
||||||
/* error if minimum can't be found */
|
|
||||||
if(state == -1)
|
|
||||||
return -1; // decode error, return failure
|
|
||||||
/* It also might be nice to report a condition where the minimum is
|
|
||||||
* non-zero, i.e. an error has been corrected. It probably shouldn't
|
|
||||||
* be a permanent failure, though.
|
|
||||||
*
|
|
||||||
* DISSECTOR_ASSERT(hd[state] == 0);
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* append dibit onto output buffer */
|
|
||||||
d = b >> 2; // dibit ctr
|
|
||||||
if (d < 48) {
|
|
||||||
buf[d >> 2] |= state << (6 - ((d%4) * 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bd_bridge (int payload[196], uint8_t decoded[12])
|
|
||||||
{
|
|
||||||
std::vector<bool> vc(196,0);
|
|
||||||
int ec = 69; //initialize error value
|
|
||||||
|
|
||||||
//gather payload from tsbk handler and pass it to the block_deinterleave function
|
|
||||||
//and then return its payload back to the tsbk handler
|
|
||||||
|
|
||||||
//initialize our decoded byte buffer with zeroes
|
|
||||||
for (int i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
decoded[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//convert/load payload into a vector vc to pass to block_deinterleave
|
|
||||||
for (int i = 0; i < 196; i++)
|
|
||||||
{
|
|
||||||
vc[i] = payload[i];
|
|
||||||
}
|
|
||||||
int block_count = 0; //
|
|
||||||
unsigned int start = block_count*196;
|
|
||||||
|
|
||||||
ec = block_deinterleave(vc, start, decoded);
|
|
||||||
|
|
||||||
return ec;
|
|
||||||
}
|
|
||||||
|
|
||||||
//modified from the LEH ComputeCrcCCITT to accept variable len buffer bits
|
|
||||||
uint16_t ComputeCrcCCITT16b(const uint8_t buf[], unsigned int 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 ComputeCrcCCITT() */
|
|
||||||
|
|
||||||
|
|
||||||
//modified from crc12_ok to run a quickie on 16 instead
|
|
||||||
static uint16_t crc16_ok(const uint8_t bits[], unsigned int len) {
|
|
||||||
uint16_t crc = 0;
|
|
||||||
// fprintf (stderr, "\n LEN = %d", len);
|
|
||||||
for (int i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
crc = crc << 1;
|
|
||||||
crc = crc | bits[i+len];
|
|
||||||
}
|
|
||||||
// uint16_t check = crc16b(bits,len);
|
|
||||||
uint16_t check = ComputeCrcCCITT16b(bits, len);
|
|
||||||
|
|
||||||
if (crc == check)
|
|
||||||
{
|
|
||||||
//fprintf (stderr, " CRC = %04X %04X", crc, check);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//fprintf (stderr, " CRC = %04X %04X", crc, check);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//TSBK/LCCH CRC16 x-bit bridge to crc16b and crc16b_okay, wip, may need multi pdu format as well
|
|
||||||
int crc16_lb_bridge (int payload[190], int len)
|
|
||||||
{
|
|
||||||
int err = -2;
|
|
||||||
uint16_t crc = -2;
|
|
||||||
uint8_t buf[190] = {0};
|
|
||||||
|
|
||||||
for (int i = 0; i < len+16; i++) //add +16 here so we load the entire frame but only run crc on the len portion
|
|
||||||
{
|
|
||||||
buf[i] = payload[i];
|
|
||||||
}
|
|
||||||
err = crc16_ok(buf, len);
|
|
||||||
return (err);
|
|
||||||
}
|
|
||||||
|
|
||||||
//borrowing crc12 from OP25
|
|
||||||
static uint16_t crc12(const uint8_t bits[], unsigned int len) {
|
|
||||||
uint16_t crc=0;
|
|
||||||
static const unsigned int K = 12;
|
|
||||||
//g12(x) = x12 + x11 + x7 + x4 + x2 + x + 1
|
|
||||||
static const uint8_t poly[K+1] = {1,1,0,0,0,1,0,0,1,0,1,1,1}; // p25 p2 crc 12 poly
|
|
||||||
uint8_t buf[256];
|
|
||||||
if (len+K > sizeof(buf)) {
|
|
||||||
//fprintf (stderr, "crc12: buffer length %u exceeds maximum %u\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 ^ 0xfff;
|
|
||||||
}
|
|
||||||
|
|
||||||
//borrowing crc12_ok from OP25
|
|
||||||
static uint16_t crc12_ok(const uint8_t bits[], unsigned int len) {
|
|
||||||
uint16_t crc = 0;
|
|
||||||
for (int i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
crc = crc << 1;
|
|
||||||
crc = crc | bits[i+len];
|
|
||||||
}
|
|
||||||
uint16_t check = crc12(bits,len);
|
|
||||||
|
|
||||||
if (crc == check)
|
|
||||||
{
|
|
||||||
//fprintf (stderr, " CRC = %03X %03X", crc, check);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//fprintf (stderr, " CRC = %03X %03X", crc, check);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//xCCH CRC12 x-bit bridge to crc12b and crc12b_okay
|
|
||||||
int crc12_xb_bridge (int payload[190], int len)
|
|
||||||
{
|
|
||||||
int err = -5;
|
|
||||||
uint16_t crc = -2;
|
|
||||||
uint8_t buf[190] = {0};
|
|
||||||
|
|
||||||
//need to load up the 12 crc bits as well, else it will fail on the check, but not on the crc calc
|
|
||||||
for (int i = 0; i < len+12; i++)
|
|
||||||
{
|
|
||||||
buf[i] = payload[i];
|
|
||||||
}
|
|
||||||
err = crc12_ok(buf, len);
|
|
||||||
return (err);
|
|
||||||
}
|
|
||||||
129
src/p25p1_hdu.c
@@ -26,6 +26,7 @@ read_dibit (dsd_opts* opts, dsd_state* state, char* output, int* status_count, i
|
|||||||
{
|
{
|
||||||
int dibit;
|
int dibit;
|
||||||
int status;
|
int status;
|
||||||
|
UNUSED(status);
|
||||||
|
|
||||||
if (*status_count == 35) {
|
if (*status_count == 35) {
|
||||||
|
|
||||||
@@ -65,7 +66,6 @@ read_dibit_update_analog_data (dsd_opts* opts, dsd_state* state, char* output, u
|
|||||||
AnalogSignal* analog_signal_array, int* analog_signal_index)
|
AnalogSignal* analog_signal_array, int* analog_signal_index)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
unsigned int debug_left, debug_right;
|
|
||||||
|
|
||||||
for (i=0; i<count; i+=2)
|
for (i=0; i<count; i+=2)
|
||||||
{
|
{
|
||||||
@@ -215,9 +215,9 @@ correct_golay_dibits_6(char* corrected_hex_data, int hex_count, AnalogSignal* an
|
|||||||
void
|
void
|
||||||
processHDU(dsd_opts* opts, dsd_state* state)
|
processHDU(dsd_opts* opts, dsd_state* state)
|
||||||
{
|
{
|
||||||
char mi[73], mfid[9], algid[9], kid[17], tgid[17], tmpstr[255];
|
uint8_t mi[73];
|
||||||
|
char mfid[9], algid[9], kid[17], tgid[17];
|
||||||
int i, j;
|
int i, j;
|
||||||
long talkgroup;
|
|
||||||
int algidhex, kidhex;
|
int algidhex, kidhex;
|
||||||
char hex[6];
|
char hex[6];
|
||||||
int status_count;
|
int status_count;
|
||||||
@@ -225,6 +225,7 @@ processHDU(dsd_opts* opts, dsd_state* state)
|
|||||||
unsigned long long int mihex1, mihex2, mihex3;
|
unsigned long long int mihex1, mihex2, mihex3;
|
||||||
char hex_data[20][6]; // Data in hex-words (6 bit words). A total of 20 hex words.
|
char hex_data[20][6]; // Data in hex-words (6 bit words). A total of 20 hex words.
|
||||||
char hex_parity[16][6]; // Parity of the data, again in hex-word format. A total of 16 parity hex words.
|
char hex_parity[16][6]; // Parity of the data, again in hex-word format. A total of 16 parity hex words.
|
||||||
|
UNUSED4(mfid, tgid, status, mihex3);
|
||||||
|
|
||||||
int irrecoverable_errors;
|
int irrecoverable_errors;
|
||||||
|
|
||||||
@@ -453,93 +454,57 @@ processHDU(dsd_opts* opts, dsd_state* state)
|
|||||||
status = getDibit (opts, state);
|
status = getDibit (opts, state);
|
||||||
//TODO: Do something useful with the status bits...
|
//TODO: Do something useful with the status bits...
|
||||||
|
|
||||||
//if (state->carrier == 1 && state->errs == 0) //only update when carrier is present, and no errors??
|
algidhex = strtol (algid, NULL, 2);
|
||||||
if (state->carrier == 1)
|
kidhex = strtol (kid, NULL, 2);
|
||||||
{
|
mihex1 = (unsigned long long int)ConvertBitIntoBytes(&mi[0], 32);
|
||||||
algidhex = strtol (algid, NULL, 2);
|
mihex2 = (unsigned long long int)ConvertBitIntoBytes(&mi[32], 32);
|
||||||
kidhex = strtol (kid, NULL, 2);
|
mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8);
|
||||||
|
|
||||||
state->payload_algid = algidhex;
|
//reset dropbytes - skip first 11 for LCW
|
||||||
state->payload_keyid = kidhex;
|
state->dropL = 267;
|
||||||
//state->payload_mfid = ConvertBitIntoBytes(&mfid[0], 7);
|
|
||||||
//state->payload_mfid = strtol (mfid, NULL, 2);
|
|
||||||
|
|
||||||
mihex1 = (unsigned long long int)ConvertBitIntoBytes(&mi[0], 32);
|
|
||||||
mihex2 = (unsigned long long int)ConvertBitIntoBytes(&mi[32], 32);
|
|
||||||
mihex3 = (unsigned long long int)ConvertBitIntoBytes(&mi[64], 8);
|
|
||||||
//only use 64 MSB, trailing 8 bits aren't used, so no mihex3
|
|
||||||
state->payload_miP = (mihex1 << 32) | (mihex2);
|
|
||||||
}
|
|
||||||
|
|
||||||
//set vc counter to 0
|
//set vc counter to 0
|
||||||
state->p25vc = 0;
|
state->p25vc = 0;
|
||||||
|
|
||||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
if (irrecoverable_errors == 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
fprintf (stderr, "%s", KYEL);
|
fprintf (stderr, "%s", KYEL);
|
||||||
fprintf (stderr, " HDU ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08llX%08llX%02llX MFID: 0x%02X", algidhex, kidhex, mihex1, mihex2, mihex3, state->payload_mfid);
|
fprintf (stderr, " HDU ALG ID: 0x%02X KEY ID: 0x%04X MI: 0x%08llX%08llX", algidhex, kidhex, mihex1, mihex2);
|
||||||
|
state->payload_algid = algidhex;
|
||||||
|
state->payload_keyid = kidhex;
|
||||||
|
if (state->R != 0 && state->payload_algid == 0xAA)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " Key: %010llX", state->R);
|
||||||
|
opts->unmute_encrypted_p25 = 1;
|
||||||
|
}
|
||||||
|
else if (state->payload_algid != 0 && state->payload_algid != 0x80)
|
||||||
|
{
|
||||||
|
//may want to mute this again, or may not want to
|
||||||
|
opts->unmute_encrypted_p25 = 0;
|
||||||
|
}
|
||||||
fprintf (stderr, "%s", KNRM);
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
//only use 64 MSB, trailing 8 bits aren't used, so no mihex3
|
||||||
|
state->payload_miP = (mihex1 << 32) | (mihex2);
|
||||||
|
|
||||||
|
if (state->payload_algid != 0x80 && state->payload_algid != 0x0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s", KRED);
|
||||||
|
fprintf (stderr, " ENC");
|
||||||
|
fprintf (stderr, "%s", KNRM);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf (stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->payload_algid != 0x80 && state->payload_algid != 0x0) //print on payload == 1
|
|
||||||
{
|
|
||||||
fprintf (stderr, "%s", KRED);
|
|
||||||
fprintf (stderr, " ENC \n");
|
|
||||||
fprintf (stderr, "%s", KNRM);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts->p25enc == 1 && opts->payload == 0)
|
|
||||||
{
|
|
||||||
algidhex = strtol (algid, NULL, 2);
|
|
||||||
kidhex = strtol (kid, NULL, 2);
|
|
||||||
}
|
|
||||||
if (opts->p25lc == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "mfid: %s tgid: %s ", mfid, tgid);
|
|
||||||
if (opts->p25tg == 0)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "\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] = '0';
|
|
||||||
tmpstr[13] = '0';
|
|
||||||
tmpstr[14] = '0';
|
|
||||||
tmpstr[15] = '0';
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (i = 0; i < 16; i++)
|
fprintf (stderr, "%s", KRED);
|
||||||
{
|
fprintf (stderr, " HDU FEC ERR \n");
|
||||||
if (state->tgcount < 24)
|
fprintf (stderr, "%s", KNRM);
|
||||||
{
|
}
|
||||||
state->tg[state->tgcount][j] = tgid[i];
|
|
||||||
}
|
//reset gain
|
||||||
tmpstr[j] = tgid[i];
|
if (opts->floating_point == 1)
|
||||||
j++;
|
state->aout_gain = opts->audio_gain;
|
||||||
}
|
|
||||||
}
|
|
||||||
tmpstr[16] = 0;
|
|
||||||
talkgroup = strtol (tmpstr, NULL, 2);
|
|
||||||
state->lasttg = talkgroup;
|
|
||||||
if (state->tgcount < 24)
|
|
||||||
{
|
|
||||||
state->tgcount = state->tgcount + 1;
|
|
||||||
}
|
|
||||||
if (opts->p25tg == 1)
|
|
||||||
{
|
|
||||||
fprintf (stderr, "tg: %li\n", talkgroup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -315,7 +315,6 @@ int estimate_symbol(int rf_mod, P25Heuristics* heuristics, int previous_dibit, i
|
|||||||
static void debug_print_symbol_heuristics(int previous_dibit, int dibit, SymbolHeuristics* sh)
|
static void debug_print_symbol_heuristics(int previous_dibit, int dibit, SymbolHeuristics* sh)
|
||||||
{
|
{
|
||||||
float mean, sd;
|
float mean, sd;
|
||||||
int k;
|
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
n = sh->count;
|
n = sh->count;
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ process_IMBE (dsd_opts* opts, dsd_state* state, int* status_count)
|
|||||||
int j, dibit, status;
|
int j, dibit, status;
|
||||||
char imbe_fr[8][23];
|
char imbe_fr[8][23];
|
||||||
const int *w, *x, *y, *z;
|
const int *w, *x, *y, *z;
|
||||||
|
UNUSED(status);
|
||||||
|
|
||||||
w = iW;
|
w = iW;
|
||||||
x = iX;
|
x = iX;
|
||||||
@@ -144,9 +145,22 @@ process_IMBE (dsd_opts* opts, dsd_state* state, int* status_count)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* //this is what is observed on the sigid wiki example (matches above pattern)
|
||||||
|
IMBE 000F920291AD6F06540980 err = [3] [A]
|
||||||
|
IMBE 000F920294816F06540980 err = [3] [A]
|
||||||
|
IMBE 000E1C0294816F06540980 err = [3] [B]
|
||||||
|
|
||||||
|
//this is what is observed on the 'hole' examples (not a match, TODO: Examine imbe_fr add this?)
|
||||||
|
IMBE FC00000000000000000300 err = [2] [B]
|
||||||
|
IMBE FC00000000000000000300 err = [2] [B]
|
||||||
|
IMBE FC00000000000000000300 err = [2] [B]
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
// Skip this particular value. If we let it pass it will be signaled as an erroneus IMBE
|
// Skip this particular value. If we let it pass it will be signaled as an erroneus IMBE
|
||||||
fprintf (stderr, "(Non-standard IMBE c0 detected, skipped)");
|
if (opts->payload == 1)
|
||||||
|
fprintf (stderr, "\n IMBE Non-standard c0 detected, skipped;");
|
||||||
} else {
|
} else {
|
||||||
processMbeFrame (opts, state, imbe_fr, NULL, NULL);
|
processMbeFrame (opts, state, imbe_fr, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|||||||