mirror of
https://github.com/lwvmobile/dsd-fme.git
synced 2026-11-13 09:17:20 -05:00
Compare commits
532 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b722e43a3a | ||
|
|
2ba9953d33 | ||
|
|
bb2199ee84 | ||
|
|
4efec4e4ef | ||
|
|
12cf9aa80f | ||
|
|
e5a42dbb11 | ||
|
|
67eaeadc03 | ||
|
|
acb5c16c30 | ||
|
|
d16e775e46 | ||
|
|
11d4c8c49c | ||
|
|
1477880720 | ||
|
|
9290098081 | ||
|
|
eeb6924369 | ||
|
|
c02d7c0ba0 | ||
|
|
324191d79f | ||
|
|
e613b85829 | ||
|
|
49f81da4df | ||
|
|
826c99d563 | ||
|
|
2441664b19 | ||
|
|
5c733c0660 | ||
|
|
73824084ad | ||
|
|
209d4e039e | ||
|
|
fa4e84bc77 | ||
|
|
4946897914 | ||
|
|
57e3f518dd | ||
|
|
08a548339c | ||
|
|
36c337ede8 | ||
|
|
f6bb325dac | ||
|
|
626869992e | ||
|
|
5422b337bb | ||
|
|
b6f270be05 | ||
|
|
3ea813ebbc | ||
|
|
53980c98ed | ||
|
|
536372d877 | ||
|
|
896f3a8533 | ||
|
|
68bbea64d5 | ||
|
|
711a4cb871 | ||
|
|
8fd1d8fe35 | ||
|
|
23c396cbe8 | ||
|
|
e91c21fadb | ||
|
|
15a8f80344 | ||
|
|
5130899a67 | ||
|
|
f5ac9ff0b2 | ||
|
|
14dd045312 | ||
|
|
fbb00f78fe | ||
|
|
a5d23d9e12 | ||
|
|
a2b3da5b5a | ||
|
|
a9417afd67 | ||
|
|
438f89bf4c | ||
|
|
2f2eecddfc | ||
|
|
2fceee5a2f | ||
|
|
62c70cbf92 | ||
|
|
b97014ff0b | ||
|
|
68edb9463c | ||
|
|
178f88a28f | ||
|
|
f7d103c6a2 | ||
|
|
10d10063f0 | ||
|
|
dba3b79266 | ||
|
|
0441b8fdbd | ||
|
|
ea399e50a8 | ||
|
|
66f710e877 | ||
|
|
0f227e40c3 | ||
|
|
136ed75342 | ||
|
|
61cefa6a87 | ||
|
|
cc7504c280 | ||
|
|
07139b3d7d | ||
|
|
638f212525 | ||
|
|
9f624e0b62 | ||
|
|
eefe1bac5e | ||
|
|
532c1cc3b6 | ||
|
|
b1c40ed6e7 | ||
|
|
94dbd1512d | ||
|
|
c73d45d553 | ||
|
|
d1efa05164 | ||
|
|
7af5ffe9fa | ||
|
|
8141ea2e78 | ||
|
|
ec4ace1514 | ||
|
|
eb5cbaad93 | ||
|
|
f5d0575978 | ||
|
|
a0d9e349c2 | ||
|
|
7ca65cf687 | ||
|
|
b4a0e2e9f3 | ||
|
|
73f0d5df44 | ||
|
|
b003d0a210 | ||
|
|
83f390ba88 | ||
|
|
c8aafb1be0 | ||
|
|
3086b7852e | ||
|
|
8f6796d6f6 | ||
|
|
e9b1d76a98 | ||
|
|
afba623b20 | ||
|
|
9b92855b4e | ||
|
|
4138f92917 | ||
|
|
0bb9dd760c | ||
|
|
8b4f91ed8b | ||
|
|
a8fd2c7834 | ||
|
|
8670b99d69 | ||
|
|
6bd37821a9 | ||
|
|
215594a2a8 | ||
|
|
3f31d4ffe0 | ||
|
|
4a83c1beae | ||
|
|
67a78d8be0 | ||
|
|
14ba90a33f | ||
|
|
66fd3fd7bf | ||
|
|
d1ffcda819 | ||
|
|
f81575335e | ||
|
|
163db6b872 | ||
|
|
e6927d8115 | ||
|
|
3a19bf5583 | ||
|
|
181a6a5695 | ||
|
|
0d2244fac0 | ||
|
|
bd7d43de79 | ||
|
|
c37400bf81 | ||
|
|
bab139e2e4 | ||
|
|
6714761d8e | ||
|
|
467992ceff | ||
|
|
cb84f7469e | ||
|
|
ad4013d7d2 | ||
|
|
5b0e40e758 | ||
|
|
05a9268aef | ||
|
|
f3d2dbb4dd | ||
|
|
370d6be28e | ||
|
|
3224f0af37 | ||
|
|
5a73bcac8b | ||
|
|
af76c8de0d | ||
|
|
a3ecbe7414 | ||
|
|
4e04056f4b | ||
|
|
d8cf47b225 | ||
|
|
57d4c7afbe | ||
|
|
cb5c160fbf | ||
|
|
5390a52313 | ||
|
|
1d78e000da | ||
|
|
90700e7d28 | ||
|
|
0e9c2fc3dc | ||
|
|
4d24e93905 | ||
|
|
1505103c18 | ||
|
|
861ca3cb54 | ||
|
|
c228207588 | ||
|
|
09f92579ee | ||
|
|
a277707a4d | ||
|
|
41a236d244 | ||
|
|
837bfac5f5 | ||
|
|
2f05bfacfa | ||
|
|
f61c59ccc0 | ||
|
|
a682d9e882 | ||
|
|
88b3145cfd | ||
|
|
7199848f16 | ||
|
|
b7bb59dbba | ||
|
|
eb9152fb4e | ||
|
|
2ec4250e5b | ||
|
|
092da7b6d0 | ||
|
|
df6d7a6609 | ||
|
|
7f34ff9340 | ||
|
|
12e4b4cd6f | ||
|
|
6189ec95fa | ||
|
|
0f6140a30a | ||
|
|
5cb14c356b | ||
|
|
44cdfd912d | ||
|
|
ec4c4637df | ||
|
|
e5a7f8c41a | ||
|
|
03fb413750 | ||
|
|
f22d61df62 | ||
|
|
d20de53f91 | ||
|
|
3ba7a96bbd | ||
|
|
3e36e5e78d | ||
|
|
83edd7ff2b | ||
|
|
416edcbd59 | ||
|
|
65ce60f6b5 | ||
|
|
e461335b2e | ||
|
|
b9223e5207 | ||
|
|
805fdd7583 | ||
|
|
2b847c2270 | ||
|
|
6043b14c13 | ||
|
|
11b353089b | ||
|
|
b39d5d8401 | ||
|
|
5d44425b10 | ||
|
|
f45f4fe413 | ||
|
|
10ed2eac5a | ||
|
|
222cd706bf | ||
|
|
18714523c7 | ||
|
|
6c89c6260b | ||
|
|
48006c28c0 | ||
|
|
634eba522f | ||
|
|
81588b3201 | ||
|
|
1ca0f8a7d5 | ||
|
|
7133ad9dae | ||
|
|
caf21d2f0c | ||
|
|
2b0a85f99f | ||
|
|
3a3f717236 | ||
|
|
b4c7c60427 | ||
|
|
0f2dfe4dca | ||
|
|
f0cc508bf9 | ||
|
|
5b46fb8ab3 | ||
|
|
1a87f81f08 | ||
|
|
af6a5b39de | ||
|
|
2c839f7fd2 | ||
|
|
af0c0a4418 | ||
|
|
c495c90ef9 | ||
|
|
f8be80318b | ||
|
|
dbea859331 | ||
|
|
2463fe22f7 | ||
|
|
dc0c78e14d | ||
|
|
a2ac82c470 | ||
|
|
c402dfff96 | ||
|
|
3f586d9c98 | ||
|
|
4ab74a6bf0 | ||
|
|
d89507ebde | ||
|
|
806fe955cb | ||
|
|
c82e028032 | ||
|
|
357f59b290 | ||
|
|
cbd3d98151 | ||
|
|
5a39fe65db | ||
|
|
37c61aa5cb | ||
|
|
74d08ffaa6 | ||
|
|
171f372a4a | ||
|
|
3924c70386 | ||
|
|
656d7b2add | ||
|
|
66ec027bd2 | ||
|
|
6ad98fbf69 | ||
|
|
9b4e790095 | ||
|
|
e69451b0d7 | ||
|
|
1d30d5ec32 | ||
|
|
710e76d0ab | ||
|
|
994e728bfb | ||
|
|
2fd250b357 | ||
|
|
ed68f41f0e | ||
|
|
16e84ae104 | ||
|
|
4f1ecda2ad | ||
|
|
685c8aebe4 | ||
|
|
6de3e6c9f3 | ||
|
|
a848f9c0fe | ||
|
|
7d6c3dc67c | ||
|
|
2263ddae63 | ||
|
|
f8b9d47c6d | ||
|
|
064cc21b2b | ||
|
|
56da871ce2 | ||
|
|
ddc2a52a33 | ||
|
|
ec9c220ad7 | ||
|
|
02a27111d3 | ||
|
|
4c3a3e0959 | ||
|
|
ea40e3e3b7 | ||
|
|
ab873d3821 | ||
|
|
3cd1eefad8 | ||
|
|
8b48328431 | ||
|
|
2c842f5115 | ||
|
|
4ad5a8f9dd | ||
|
|
33cbad7618 | ||
|
|
504ee431b5 | ||
|
|
53fca195ba | ||
|
|
12dbd90509 | ||
|
|
2339cee016 | ||
|
|
4c44a47c61 | ||
|
|
11d4b2b330 | ||
|
|
cb57478fcc | ||
|
|
672e5cbd44 | ||
|
|
c1eb511855 | ||
|
|
2ccc27c03b | ||
|
|
43c1c9823b | ||
|
|
a3530c58a0 | ||
|
|
48ddba2ab8 | ||
|
|
9b554dfd93 | ||
|
|
edf465d03c | ||
|
|
e7a477dc88 | ||
|
|
7c461844ad | ||
|
|
ab29dd8e6a | ||
|
|
800fc3101e | ||
|
|
13ec6855f8 | ||
|
|
6a649376fc | ||
|
|
1ee3dba499 | ||
|
|
94487fd1c0 | ||
|
|
4af78289b2 | ||
|
|
5ee669aa18 | ||
|
|
6afb0d718f | ||
|
|
ea1f0bdf2f | ||
|
|
184b8b6a39 | ||
|
|
0fc9380b00 | ||
|
|
9f330e7902 | ||
|
|
d756ab6574 | ||
|
|
beb96f73db | ||
|
|
f053e53e76 | ||
|
|
c994f00e5d | ||
|
|
5834914e91 | ||
|
|
2b71652b23 | ||
|
|
d44c050ca4 | ||
|
|
1e9ea14a51 | ||
|
|
f6fdbfa922 | ||
|
|
7a341fa1c9 | ||
|
|
786f306eac | ||
|
|
b61b77e773 | ||
|
|
bba8579192 | ||
|
|
aca0adc4c5 | ||
|
|
b996d95b1c | ||
|
|
71be900630 | ||
|
|
5530d2ac9c | ||
|
|
a64c4187e5 | ||
|
|
568a8521c0 | ||
|
|
ab186da2f3 | ||
|
|
11d72c39d3 | ||
|
|
8669f6c309 | ||
|
|
fdaeae1307 | ||
|
|
010ff8cb60 | ||
|
|
12c7379d2a | ||
|
|
ae462d74a5 | ||
|
|
aa2600cdc2 | ||
|
|
69034b7c2f | ||
|
|
49ea9f7f52 | ||
|
|
670e4e7bec | ||
|
|
5b4d624bb2 | ||
|
|
1a4440f1ab | ||
|
|
c917c61b85 | ||
|
|
a239cd94fe | ||
|
|
a6c83a7fbb | ||
|
|
c7e52ab4fe | ||
|
|
df14508778 | ||
|
|
178b76f092 | ||
|
|
36fbbfd4d7 | ||
|
|
d1903b1574 | ||
|
|
fea03a0242 | ||
|
|
2ed2dc36aa | ||
|
|
2223183805 | ||
|
|
25affc5b54 | ||
|
|
ad13d06acc | ||
|
|
291f9ed963 | ||
|
|
455a7a9329 | ||
|
|
7e9eadaea5 | ||
|
|
387f2253b1 | ||
|
|
af401a7e5b | ||
|
|
4404c1afed | ||
|
|
417a48cc71 | ||
|
|
9acff308b9 | ||
|
|
48c9ae4b28 | ||
|
|
c3c4956ebb | ||
|
|
9751eb165f | ||
|
|
409321eb43 | ||
|
|
6f2682d667 | ||
|
|
46196cf283 | ||
|
|
80526be3ed | ||
|
|
d26d45eb7e | ||
|
|
36f522922d | ||
|
|
d70cab2499 | ||
|
|
6583fbe8f7 | ||
|
|
b3383cd543 | ||
|
|
9596301aee | ||
|
|
fbbd4285f5 | ||
|
|
97bc736f86 | ||
|
|
460b8999c3 | ||
|
|
88d7a59afe | ||
|
|
6be364c6ce | ||
|
|
3d83c0b06c | ||
|
|
3b9185fc40 | ||
|
|
5897131f05 | ||
|
|
c7a83c8c51 | ||
|
|
0eda6f5cf1 | ||
|
|
718d049927 | ||
|
|
e0c2e740d2 | ||
|
|
aab0208605 | ||
|
|
d037d2e0ad | ||
|
|
dd8e48789f | ||
|
|
d37ccf2883 | ||
|
|
6fb16cbd95 | ||
|
|
7a3e1a67da | ||
|
|
0f5c019d44 | ||
|
|
9092259ce1 | ||
|
|
079ed7e8cc | ||
|
|
3bb8a5608f | ||
|
|
142fb0c60e | ||
|
|
d088d09328 | ||
|
|
baca1be1b3 | ||
|
|
47c43f9c9c | ||
|
|
b1dcbeb459 | ||
|
|
ceb98222b6 | ||
|
|
34338ce2b1 | ||
|
|
3ef8c846c6 | ||
|
|
325bff80d0 | ||
|
|
fcf680a138 | ||
|
|
05b9e29724 | ||
|
|
fcfe06e1b2 | ||
|
|
7e4a537035 | ||
|
|
62bf2d91ca | ||
|
|
003b956d2c | ||
|
|
c408a57198 | ||
|
|
9f0e0adee8 | ||
|
|
b23705b7b6 | ||
|
|
eaec87ac44 | ||
|
|
4f012390c0 | ||
|
|
d125d12132 | ||
|
|
617b5d8072 | ||
|
|
4b81636947 | ||
|
|
4c8f0f3c50 | ||
|
|
130afdc278 | ||
|
|
a4aab9a56d | ||
|
|
2b8ce4b248 | ||
|
|
be165c8c77 | ||
|
|
46fa8b5c60 | ||
|
|
628b9c30c2 | ||
|
|
13a453193a | ||
|
|
b58b2b578a | ||
|
|
b66cac75d2 | ||
|
|
6573521363 | ||
|
|
f5ed83289f | ||
|
|
2859ad3927 | ||
|
|
aaa8e5c3f1 | ||
|
|
f6673b6f59 | ||
|
|
f3dac806a8 | ||
|
|
92db25f56d | ||
|
|
4e60c61eb0 | ||
|
|
fd9f9126d7 | ||
|
|
7e6b3adf9d | ||
|
|
e0bddd6699 | ||
|
|
d98040895d | ||
|
|
5a8b6f1d4a | ||
|
|
4ca81a5dc2 | ||
|
|
7222e71c08 | ||
|
|
cb9e6c632c | ||
|
|
005d356859 | ||
|
|
7a94be9292 | ||
|
|
9ae874449f | ||
|
|
1e95db0977 | ||
|
|
93ccc668d7 | ||
|
|
defa6455d6 | ||
|
|
7c5fef56e9 | ||
|
|
6ed5e0bcab | ||
|
|
31157b72ab | ||
|
|
0cb68d7c53 | ||
|
|
b67a3bb6de | ||
|
|
d0086a5f96 | ||
|
|
2b84518caf | ||
|
|
60593dcce9 | ||
|
|
fad831d1aa | ||
|
|
3c11d72652 | ||
|
|
97a17df9d1 | ||
|
|
2523383b14 | ||
|
|
a50f3b714f | ||
|
|
df3f58b05e | ||
|
|
2debc3e5f1 | ||
|
|
f383d165fd | ||
|
|
5324d53c47 | ||
|
|
f0f067510f | ||
|
|
2bdcc9cc65 | ||
|
|
0b0754c1d5 | ||
|
|
502607a608 | ||
|
|
290a2204ba | ||
|
|
f175834e45 | ||
|
|
98f9e36291 | ||
|
|
5f8aedb864 | ||
|
|
b674f277d9 | ||
|
|
386cf93eb1 | ||
|
|
7ee04e58c5 | ||
|
|
9eaaa323fe | ||
|
|
6605925162 | ||
|
|
6fbda52998 | ||
|
|
8e60a7b6a8 | ||
|
|
e18cbf730e | ||
|
|
8db618f83d | ||
|
|
60807e09ab | ||
|
|
c55f8b71fd | ||
|
|
cc1fb3b33c | ||
|
|
cfcbf6a8da | ||
|
|
dd5c11acb3 | ||
|
|
fd998e2bf7 | ||
|
|
6a9aa076b8 | ||
|
|
45eb53f245 | ||
|
|
74fa117850 | ||
|
|
5dd236ec96 | ||
|
|
55b190ef49 | ||
|
|
df3c0dcab4 | ||
|
|
d077cd8316 | ||
|
|
05caba143b | ||
|
|
f2767707ef | ||
|
|
4e338dede9 | ||
|
|
f207eba8f9 | ||
|
|
3e9dc91e81 | ||
|
|
c39b0e6e43 | ||
|
|
a9995c695a | ||
|
|
f062fe0ef3 | ||
|
|
5f30a6aad6 | ||
|
|
e0c574c470 | ||
|
|
68c471d639 | ||
|
|
1c05c6da6e | ||
|
|
8f61ac63a3 | ||
|
|
840dbac8b0 | ||
|
|
a0c7d16905 | ||
|
|
9655ab0bd7 | ||
|
|
9e6b3b7978 | ||
|
|
d8181ec79e | ||
|
|
f8cd8d715f | ||
|
|
20c55d073b | ||
|
|
1e854adb84 | ||
|
|
6ab62d006a | ||
|
|
9d0fe07931 | ||
|
|
b45e563e54 | ||
|
|
ad0c849dc4 | ||
|
|
3229737e6c | ||
|
|
596451eba0 | ||
|
|
c23ff4fe0c | ||
|
|
134f2e8e21 | ||
|
|
01a2b6959a | ||
|
|
7299b78c3b | ||
|
|
9b35ef5c83 | ||
|
|
75972cb024 | ||
|
|
461031d3f1 | ||
|
|
772eb7f810 | ||
|
|
3f4ff87078 | ||
|
|
1553973718 | ||
|
|
95c3d8fd27 | ||
|
|
d7b36f32c0 | ||
|
|
9b9cb78ddf | ||
|
|
e40e89ae81 | ||
|
|
2f6214fe72 | ||
|
|
271a7e459a | ||
|
|
e3dde6f7f9 | ||
|
|
16110bd251 | ||
|
|
945f93d678 | ||
|
|
2630e98128 | ||
|
|
855fdd64e8 | ||
|
|
158ae7c520 | ||
|
|
7bd0a10c65 | ||
|
|
e0376bc009 | ||
|
|
25765ef4d0 | ||
|
|
9965f8a36d | ||
|
|
4763717b59 | ||
|
|
8c8dea4142 | ||
|
|
ac816f9207 | ||
|
|
040ea148b0 | ||
|
|
27960ae4be | ||
|
|
5fb79e9baa | ||
|
|
de3e5e7f60 | ||
|
|
e3bafe96e9 | ||
|
|
f978a8759e | ||
|
|
260ec82ea1 | ||
|
|
e40c32d8ad | ||
|
|
20271c6df5 | ||
|
|
fa73feded2 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,2 +1,7 @@
|
||||
*.o
|
||||
dsd
|
||||
*.[ao]
|
||||
*.so*
|
||||
*.dylib
|
||||
build
|
||||
*.sh
|
||||
*.csv
|
||||
examples
|
||||
|
||||
92
CHANGELOG
92
CHANGELOG
@@ -1,92 +0,0 @@
|
||||
1.6.0
|
||||
New features:
|
||||
Auto mutes P25 encrypted signals by default.
|
||||
Raised cosine filters.
|
||||
|
||||
Fixed bugs:
|
||||
Changed the crazy dibit buffer which filled for ever until it
|
||||
ran out of allocated memory then caused a segmentation error. The
|
||||
buffer is now allowed to fill to 90% before being returned to the
|
||||
initial pointer value where it over writes the old samples, it
|
||||
seems to work ok and no segmentation errors now.
|
||||
Input level is now calculated differently, before when it reported 50%
|
||||
the soundcard would be fully overloaded. With the new method aim for 30% inlvl.
|
||||
|
||||
1.4.1
|
||||
New features:
|
||||
Several new sync types for existing formats now recognized:
|
||||
Decodes voice from NXDN 4800 (6.25kHz) signals
|
||||
Decodes voice from NXDN 9600 (12.5kHz) repeater output
|
||||
Decodes voice from DMR/MotoTRBO simplex/repeater input
|
||||
Decodes voice from X2-TDMA simplex/repeater input
|
||||
|
||||
Fixed bugs:
|
||||
renamed "input:" to "inlvl:" to reduce confusion. This value
|
||||
indicates the audio input level, NOT the "decode success
|
||||
rate". Voice decode errors are indicated by the errorbars "=".
|
||||
|
||||
1.4
|
||||
New features:
|
||||
Decodes voice from NXDN 9600 (12.5 kHz) simplex/repeater input
|
||||
NXDN96 frames enabled by default
|
||||
Improved resistance to NXDN96 sync false positives
|
||||
|
||||
Fixed bugs:
|
||||
.wav file header updated after playing .imb/.amb data files
|
||||
.imb/.amb files now have correct tgid in filename
|
||||
|
||||
1.3.1 New features:
|
||||
Support for ProVoice EA sync
|
||||
CTRL-C is now caught so .wav files can be properly closed
|
||||
DSD now shows mbelib version as well as it's own version
|
||||
-R resume option now triggers on any TSDU so control channels can be left
|
||||
in conventional scanlists.
|
||||
Auto output gain now has 0.5 second hold time for faster error burst recovery
|
||||
(was 1.5 seconds)
|
||||
Audio output upsampling function simplified and improved
|
||||
|
||||
Fixed bugs:
|
||||
DSD_Author.pgp now has correct public key (was copy of mbelib_Author key)
|
||||
TGID and SRC are now cleared after TDULC or TDU.
|
||||
Voice error counter is now reset in noCarrier()
|
||||
TGID and SRC were not displaying for X2-TDMA frames
|
||||
Fixed buffer issue in resumeScan()
|
||||
Fixed error in .wav file headers preventing playback on some apps
|
||||
|
||||
1.3 New features:
|
||||
Decodes voice from ProVoice signals (requires -fp option)
|
||||
algid and kid are now shown in hex notation
|
||||
|
||||
Fixed bugs:
|
||||
auto output gain now has faster rise time
|
||||
MoTDMA is now correctly labeled X2-TDMA
|
||||
|
||||
1.2 New features:
|
||||
Decodes voice for DMR standard (including MOTOTRBO)
|
||||
Full metadata (src, talkgroup, lcinfo, mfid, lcformat, mi, algid, keyid) for X2-TDMA
|
||||
TDMA slot identification for X2-TDMA/DMR/MOTOTRBO
|
||||
Identifies non-voice frame types for X2-TDMA/DMR/MOTOTRBO
|
||||
Frame only (no voice yet) support for 9600 baud NXDN
|
||||
Auto leveling audio output gain (default) and -g option for fixed gain
|
||||
GFSK modulation optimizations and improved C4FM/GFSK/QPSK auto detect
|
||||
|
||||
Fixed bugs:
|
||||
nac was showing wrong ID
|
||||
P25 metadata (lcinfo, mfid, lfcormat, mi, keyid, algid, lsd) are now
|
||||
printed out in the correct bit order (MSB -> LSB), was reversed.
|
||||
fixed serveral bugs in dsd_upsample.c, with improved quality
|
||||
|
||||
1.1 New features:
|
||||
Scanner control options to allow scan resume during certain TDULC
|
||||
Improved upsampling function audio quality
|
||||
Greatly improved handling of X2-TDMA frames
|
||||
Much faster QPSK decision point tracking by default
|
||||
|
||||
Fixed bugs:
|
||||
playMbeFiles was not output to .wav file when -w was given
|
||||
Now correctly detects/handles Mot and P25 talkgroup formats
|
||||
PDU frames were not detected
|
||||
|
||||
1.0.3 Fixed buggy C4FM/QPSK auto detection and added -A option to adjust it.
|
||||
|
||||
1.0 Initial release
|
||||
68
CMakeLists.txt
Normal file
68
CMakeLists.txt
Normal file
@@ -0,0 +1,68 @@
|
||||
project(dsd-fme-lite)
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
|
||||
#Set curses to ncurses, and wide true for ascii
|
||||
set(CURSES_NEED_NCURSES TRUE)
|
||||
set(CURSES_NEED_WIDE TRUE)
|
||||
|
||||
include(git_revision)
|
||||
git_describe(GIT_TAG)
|
||||
|
||||
find_package(LibSndFile REQUIRED)
|
||||
find_package(MBE REQUIRED)
|
||||
find_package(ITPP REQUIRED)
|
||||
find_package(RTLSDR)
|
||||
find_package(Curses REQUIRED)
|
||||
find_package(PULSEAUDIO REQUIRED)
|
||||
|
||||
include_directories(SYSTEM ${LIBSNDFILE_INCLUDE_DIR} ${MBE_INCLUDE_DIR} ${ITPP_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIRS} ${CURSES_INCLUDE_DIR})
|
||||
set(LIBS ${MBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY} ${CURSES_LIBRARY})
|
||||
|
||||
if(RTLSDR_FOUND)
|
||||
find_package(Threads)
|
||||
include_directories(SYSTEM ${RTLSDR_INCLUDE_DIRS})
|
||||
list(APPEND LIBS ${RTLSDR_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
add_definitions(-DUSE_RTLSDR)
|
||||
endif(RTLSDR_FOUND)
|
||||
|
||||
FILE(GLOB SRCS src/*.c src/*.cpp)
|
||||
FILE(GLOB HEADERS include/*.h include/*.hpp)
|
||||
|
||||
if(NOT RTLSDR_FOUND)
|
||||
list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_fm.cpp)
|
||||
endif(NOT RTLSDR_FOUND)
|
||||
|
||||
configure_file("src/git_ver.c.in" "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c" @ONLY)
|
||||
list(APPEND SRCS "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c")
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/include")
|
||||
|
||||
ADD_EXECUTABLE(dsd-fme-lite ${SRCS} ${HEADERS})
|
||||
TARGET_LINK_LIBRARIES(dsd-fme-lite ${LIBS})
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS dsd-fme-lite DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
# man page
|
||||
find_program(HELP2MAN_FOUND help2man)
|
||||
if (HELP2MAN_FOUND)
|
||||
add_custom_command(TARGET dsd-fme-lite POST_BUILD
|
||||
COMMAND help2man
|
||||
ARGS -n "Digital Speech Decoder"
|
||||
--version-string=${GIT_TAG}
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/dsd-fme-lite.1
|
||||
--no-info
|
||||
$<TARGET_FILE:dsd-fme-lite>
|
||||
)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dsd-fme-lite.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
||||
endif()
|
||||
|
||||
# uninstall target
|
||||
configure_file(
|
||||
"cmake/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
add_custom_target(uninstall
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
||||
19
COPYRIGHT
19
COPYRIGHT
@@ -1,3 +1,18 @@
|
||||
dstar_header.c/h, descramble.h, and fcs.h are under the following license:
|
||||
|
||||
dstar_header.c/h and fcs.h: Copyright (C) 2010 by Kristoff Bonne, ON1ARF
|
||||
descramble.h: Copyright (C) 2011 by Jonathan Naylor, G4KLX
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
All other code is under the following license:
|
||||
|
||||
Copyright (C) 2010 DSD Author
|
||||
GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
|
||||
@@ -12,3 +27,7 @@ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
# Digital Speech Decoder - Florida Man Edition
|
||||
|
||||
This version of DSD is a flavor blend of [szechyjs](https://github.com/szechyjs/dsd "szechyjs") RTL branch and some of my own additions, along with portions of DMR and NXDN code from the [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve") branch. This code also borrows snippets, inspiration, and ideas from other open source works including [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25"), [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"). This project wouldn't be possible without a few good people providing me plenty of sample audio files to run over and over again. Special thanks to jurek1111, KrisMar, noamlivne, racingfan360, iScottyBotty, LimaZulu and hrh17 for the many hours of wav samples submitted by them.
|
||||
|
||||
115
Makefile
115
Makefile
@@ -1,115 +0,0 @@
|
||||
# Copyright (C) 2010 DSD Author
|
||||
# GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -O2 -Wall
|
||||
INCLUDES = -I. -I/usr/local/include -I/usr/include
|
||||
LIBS = -L/usr/local/lib -lm -lmbe
|
||||
INSTALL=install
|
||||
AR=ar
|
||||
RANLIB=ranlib
|
||||
DEST_BASE=/usr/local
|
||||
DEST_INC=${DEST_BASE}/include
|
||||
DEST_LIB=${DEST_BASE}/lib
|
||||
DEST_BIN=${DEST_BASE}/bin
|
||||
OBJS = dsd_main.o dsd_symbol.o dsd_dibit.o dsd_frame_sync.o dsd_file.o dsd_audio.o dsd_serial.o dsd_frame.o dsd_mbe.o dsd_upsample.o p25p1_hdu.o p25p1_ldu1.o p25p1_ldu2.o p25p1_tdulc.o p25_lcw.o x2tdma_voice.o x2tdma_data.o dstar.o nxdn_voice.o nxdn_data.o dmr_voice.o dmr_data.o provoice.o
|
||||
|
||||
all: dsd
|
||||
|
||||
build: all
|
||||
|
||||
dsd_main.o: dsd_main.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_main.c -o dsd_main.o
|
||||
|
||||
dsd_symbol.o: dsd_symbol.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_symbol.c -o dsd_symbol.o
|
||||
|
||||
dsd_dibit.o: dsd_dibit.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_dibit.c -o dsd_dibit.o
|
||||
|
||||
dsd_frame_sync.o: dsd_frame_sync.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_frame_sync.c -o dsd_frame_sync.o
|
||||
|
||||
dsd_file.o: dsd_file.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_file.c -o dsd_file.o
|
||||
|
||||
dsd_audio.o: dsd_audio.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_audio.c -o dsd_audio.o
|
||||
|
||||
dsd_serial.o: dsd_serial.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_serial.c -o dsd_serial.o
|
||||
|
||||
dsd_frame.o: dsd_frame.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_frame.c -o dsd_frame.o
|
||||
|
||||
dsd_mbe.o: dsd_mbe.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_mbe.c -o dsd_mbe.o
|
||||
|
||||
dsd_upsample.o: dsd_upsample.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dsd_upsample.c -o dsd_upsample.o
|
||||
|
||||
p25p1_hdu.o: p25p1_hdu.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c p25p1_hdu.c -o p25p1_hdu.o
|
||||
|
||||
p25p1_ldu1.o: p25p1_ldu1.c p25p1_const.h dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c p25p1_ldu1.c -o p25p1_ldu1.o
|
||||
|
||||
p25p1_ldu2.o: p25p1_ldu2.c p25p1_const.h dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c p25p1_ldu2.c -o p25p1_ldu2.o
|
||||
|
||||
p25p1_tdulc.o: p25p1_tdulc.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c p25p1_tdulc.c -o p25p1_tdulc.o
|
||||
|
||||
p25_lcw.o: p25_lcw.c dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c p25_lcw.c -o p25_lcw.o
|
||||
|
||||
x2tdma_voice.o: x2tdma_voice.c x2tdma_const.h dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c x2tdma_voice.c -o x2tdma_voice.o
|
||||
|
||||
x2tdma_data.o: x2tdma_data.c x2tdma_const.h dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c x2tdma_data.c -o x2tdma_data.o
|
||||
|
||||
dstar.o: dstar.c dstar_const.h dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dstar.c -o dstar.o
|
||||
|
||||
nxdn48_voice.o: nxdn48_voice.c nxdn_const.h dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c nxdn48_voice.c -o nxdn48_voice.o
|
||||
|
||||
nxdn_voice.o: nxdn_voice.c nxdn_const.h dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c nxdn_voice.c -o nxdn_voice.o
|
||||
|
||||
nxdn_data.o: nxdn_data.c nxdn_const.h dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c nxdn_data.c -o nxdn_data.o
|
||||
|
||||
dmr_voice.o: dmr_voice.c dmr_const.h dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dmr_voice.c -o dmr_voice.o
|
||||
|
||||
dmr_data.o: dmr_data.c dmr_const.h dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c dmr_data.c -o dmr_data.o
|
||||
|
||||
provoice.o: provoice.c provoice_const.h dsd.h config.h
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c provoice.c -o provoice.o
|
||||
|
||||
dsd: $(OBJS) dsd.h config.h
|
||||
$(CC) $(CFLAGS) -o dsd $(OBJS) $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f dsd
|
||||
|
||||
install: dsd
|
||||
$(INSTALL) dsd $(DEST_BIN)
|
||||
chgrp uucp $(DEST_BIN)/dsd
|
||||
chmod g+s $(DEST_BIN)/dsd
|
||||
521
README
521
README
@@ -1,521 +0,0 @@
|
||||
Digital Speech Decoder 1.6.0
|
||||
Copyright (C) 2010 DSD Author
|
||||
GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
DSD is able to decode several digital voice formats from discriminator
|
||||
tap audio and synthesize the decoded speech. Speech
|
||||
synthesis requires mbelib, which is a separate package. DSD 1.4.1
|
||||
requires mbelib 1.1 or later.
|
||||
|
||||
Supported formats in version 1.6.0:
|
||||
|
||||
P25 Phase 1 Widely deployed radio standard used in public safety
|
||||
and amateur radio.
|
||||
|
||||
Support includes decoding and synthesis of speech,
|
||||
display of all link control info, and the ability to save
|
||||
and replay .imb data files
|
||||
|
||||
ProVoice EDACS Digital voice format used by public safety and
|
||||
amateur radio.
|
||||
|
||||
Support includes decoding and synthesis of speech and
|
||||
the ability to save and replay .imb data files.
|
||||
Note: not enabled by default, use -fp to enable.
|
||||
|
||||
X2-TDMA Two slot TDMA system currently being deployed by several
|
||||
public safety organizations. Based on the DMR
|
||||
standard with extensions for P25 style signaling.
|
||||
|
||||
Support includes decoding and synthesis of speech,
|
||||
display of all link control info, and the ability to save
|
||||
and replay .amb data files
|
||||
|
||||
DMR/MOTOTRBO "Digital Mobile Radio" Eurpoean two slot TDMA standard.
|
||||
MOTOTRBO is a popular implementation of this standard.
|
||||
|
||||
Support includes decoding and synthesis of speech and
|
||||
the ability to save and replay .amb data files.
|
||||
|
||||
NXDN Digital radio standard used by NEXEDGE and IDAS brands.
|
||||
Supports both 9600 baud (12.5 kHz) and
|
||||
4800 baud (6.25 kHz) digital voice.
|
||||
|
||||
Support includes decoding and synthesis of speech and
|
||||
the ability to save and replay .amb data files.
|
||||
|
||||
Development (no speech) support only:
|
||||
|
||||
D-STAR Amateur radio digital voice standard
|
||||
|
||||
Development support only. DSD recognized frames and can
|
||||
extract the voice bits but speech is not yet decoded.
|
||||
D-STAR likely uses a version of AMBE not yet supported by
|
||||
mbelib. The voice bit interleave pattern also needs to be
|
||||
determined.
|
||||
Note: not enabled by default, use -fd to enable.
|
||||
|
||||
Unsupported formats in version 1.4 considered for future development:
|
||||
|
||||
P25 Phase 2 This is not yet a published standard. Full support is
|
||||
expected once the standard is published and there are
|
||||
systems operating to test against. Phase 2 will use
|
||||
a vocoder supported by mbelib.
|
||||
|
||||
OpenSKY It is possible that the four slot version uses a vocoder
|
||||
supported by mbelib. The two slot version does not.
|
||||
|
||||
Supported demodulation optimizations in version 1.4:
|
||||
|
||||
C4FM Continuous envelope 2 or 4 level FSK with relatively
|
||||
sharp transitions between symbols. Used by most P25
|
||||
systems.
|
||||
|
||||
Optimizations include calibrating decision points only
|
||||
during sync, 4/10 sample window per symbol, and symbol
|
||||
edge timing calibration.
|
||||
|
||||
GFSK Continuous envelope 2 or 4 level FSK with a narrower
|
||||
Gaussian/"raised cosine" filter that affects transitions
|
||||
between symbols. Used by DMR/MOTOTRBO, NXDN and many
|
||||
others. Noisy C4FM signals may be detected as GFSK
|
||||
|
||||
but this is ok, the optimization changes will help with
|
||||
noisy signals.
|
||||
|
||||
Optimizations are similar to C4FM except symbol transitions
|
||||
are only kept out of the middle 4 samples and only the
|
||||
middle two samples are used.
|
||||
|
||||
QPSK Quadrature Phase Shift Keying (and variants) used in
|
||||
some P25 systems and all known X2-TDMA systems. May be
|
||||
advertised under the marketing term "LSM"
|
||||
|
||||
Optimizations include continuous decision point
|
||||
calibration, using middle two samples, and using the
|
||||
symbol midpoint "spike" for symbol timing.
|
||||
|
||||
Installation
|
||||
|
||||
DSD should easily compile on any Linux or *BSD system with gcc.
|
||||
Just untar and run "make" or "make install". There are some debugging/
|
||||
development options in config.h that normal users will want to leave
|
||||
disabled as they can severely impact performance.
|
||||
|
||||
Operation
|
||||
|
||||
There are two main operating modes, "Live scanner" and "Play files"
|
||||
|
||||
Usage: dsd [options] Live scanner mode
|
||||
|
||||
Live Scanner mode takes 48KHz/16 bit mono audio samples from a
|
||||
sound card input and decodes speech in real time. Options are provided
|
||||
for controling information display and saving mbe data files.
|
||||
The synthesized speech can be output to a soundcard and/or a
|
||||
.wav file.
|
||||
|
||||
Usage: dsd [options] -r <files> Read/Play saved mbe data from file(s)
|
||||
|
||||
|
||||
Play files mode reads mbe data from files specified on the command
|
||||
line (including wildcards) and synthesizes speech from those files.
|
||||
The synthesized speech can be output to a soundcard and/or a
|
||||
.wav file. The -r command line options is used to activate Play files
|
||||
mode.
|
||||
|
||||
Display modes
|
||||
|
||||
There are two main display modes in Live scanner mode. "Errorbars"
|
||||
and "Datascope".
|
||||
|
||||
Errorbars mode output for P25 Phase 1 looks like this:
|
||||
|
||||
|
||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 0 tg: 32464 TDULC
|
||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 0 tg: 32464 TDULC
|
||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 0 tg: 32464 TDULC
|
||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 0 tg: 32464 TDULC
|
||||
Sync: -P25p1 mod: C4FM inlvl: 38% nac: 5C2 src: 0 tg: 32464 TDU
|
||||
Sync: -P25p1 mod: C4FM inlvl: 38% nac: 5C2 src: 0 tg: 32464 HDU
|
||||
Sync: -P25p1 mod: C4FM inlvl: 42% nac: 5C2 src: 0 tg: 32464 LDU1 e:
|
||||
Sync: (-P25p1) mod: C4FM inlvl: 39% nac: 5C2 src: 52610 tg: 32464 (LDU2) e:
|
||||
Sync: -P25p1 mod: C4FM inlvl: 38% nac: 5C2 src: 52610 tg: 32464 LDU1 e:
|
||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 52610 tg: 32464 LDU2 e:
|
||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 52610 tg: 32464 LDU1 e:
|
||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 52610 tg: 32464 LDU2 e:
|
||||
Sync: -P25p1 mod: C4FM inlvl: 39% nac: 5C2 src: 52610 tg: 32464 LDU1 e:
|
||||
|
||||
"Sync" indicates the frame type detected and whether the polarity is
|
||||
positive or negative. DSD automatically detects and handles either
|
||||
polarity except for DMR/MOTOTRBO/X2-TDMA which unfortunatley use both
|
||||
sync polarities.
|
||||
|
||||
Most combinations of transmitter, receiver and soundcard show netagive
|
||||
(-) polarity for X2-TDMA signals and (+) polarity for DMR/MOTOTRBO so
|
||||
those are the defaults.
|
||||
|
||||
********
|
||||
You may need to use the -x option to select non-inverted polarity if
|
||||
you are not getting usable X2-TDMA/MOTOTRBO/DMR speech. As they use both
|
||||
normal and inverted sync it is not possible to detect polariy
|
||||
automatically.
|
||||
********
|
||||
|
||||
"mod" indicates the current demodulation optimizations.
|
||||
|
||||
"inlvl" indicates the audio input level. QPSK signals tend to appear
|
||||
much "wider" than C4FM from a discriminator tap so it is important
|
||||
to set your input gain using a QPSK signal if you plan to montir them.
|
||||
It is not necessary nor desirable to get to 100%, in fact your sound
|
||||
card may max out below 100%. It is best to use the Datascope mode for
|
||||
setting input gain (see below). Typical values with good results are
|
||||
40% for C4FM and 66% for QPSK.
|
||||
|
||||
"nac" is the P25 Phase 1 Network Access Code. This is a 12 bit field
|
||||
in each P25 Phase 1 header. It should not be confused with the 16
|
||||
bit System ID used in non-P25 trunking control channels.
|
||||
|
||||
"src" is the radio id of the trasmitting subscriber unit.
|
||||
|
||||
"tg" is the talkgroup derived from link control information.
|
||||
|
||||
"HDU/LDU1/LDU2/TDU/TDULC" are P25 Phase 1 frame types, referred to as
|
||||
frame subtype within DSD.
|
||||
|
||||
"e:" is the beginning of the errorbars display. Each "=" indicates a
|
||||
detected error within the voice data. "R" and "M" indicat that a voice
|
||||
frame was repeated or muted due to excessive errors.
|
||||
|
||||
Values in parentheses () indicate an assumption (soft decision) was
|
||||
made based on the previous frame.
|
||||
|
||||
Errorbars mode output for X2-TDMA looks like this:
|
||||
|
||||
Sync: -X2-TDMA mod: QPSK inlvl: 59% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
|
||||
Sync: -X2-TDMA mod: QPSK inlvl: 47% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
|
||||
Sync: -X2-TDMA mod: QPSK inlvl: 43% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
|
||||
Sync: (-X2-TDMA) mod: QPSK inlvl: 28% src: 17211 tg: 197 [SLOT0] slot1 VOICE e:
|
||||
|
||||
DMR/MOTOTRBO display is similar except it does not yet show source
|
||||
and talkgroup information.
|
||||
|
||||
As of version 1.2 DSD shows which specific TDMA slots are active (with
|
||||
capital SLOT letters) and which slot is currently being monitored (with
|
||||
square brackets []. Noisy/degraded signals will affect the accuracy
|
||||
of this display.
|
||||
|
||||
The frame subtypes (Voice/LC etc) are shown based on the DMR standard
|
||||
types.
|
||||
|
||||
Datascope mode output looks like this:
|
||||
|
||||
|
||||
Demod mode: C4FM Nac: 8C3
|
||||
Frame Type: P25 Phase 1 Talkgroup: 16528
|
||||
Frame Subtype: LDU1 Source: 0
|
||||
TDMA activity: slot0 slot1 Voice errors:
|
||||
+----------------------------------------------------------------+
|
||||
| # ^ !| ^ # |
|
||||
| * | * |
|
||||
| * | * |
|
||||
| * | * * |
|
||||
| * * | * * |
|
||||
| * * | ** * |
|
||||
| * ** | ** * |
|
||||
| ** ** | ** * |
|
||||
| ** ** | ** * |
|
||||
| ** ** | ** * |
|
||||
+----------------------------------------------------------------+
|
||||
C4FM Example
|
||||
|
||||
Demod mode: C4FM Nac: 126
|
||||
Frame Type: P25 Phase 1 Talkgroup: 25283
|
||||
Frame Subtype: LDU2 Source: 0
|
||||
TDMA activity: slot0 slot1 Voice errors:
|
||||
+----------------------------------------------------------------+
|
||||
| # ^ ! ^ # |
|
||||
| * | |
|
||||
| * | |
|
||||
| ** | |
|
||||
| ** | * |
|
||||
| * ** | * * |
|
||||
| ** ** | * * |
|
||||
| *** ** | ** * |
|
||||
| *** ** | *** * |
|
||||
| *** **** | **** * * |
|
||||
+----------------------------------------------------------------+
|
||||
QPSK Example
|
||||
|
||||
|
||||
At the top is various information about the signal, similar to the
|
||||
information provided in Errorbars mode. The large box is similar to
|
||||
a spectrum analyzer viewing the channel bandwidth.
|
||||
|
||||
The horizontal axis is the input audio level, minimum on the left and
|
||||
maximum on the right. The vertical axis is the number of samples
|
||||
seend at each audio level.
|
||||
|
||||
The "*" symbols represent the number of audio
|
||||
samples that were at each level during the aggregation period.
|
||||
(default = 36 symbols) The -S options controls the aggregation period
|
||||
as well as the QPSK tracking symbol buffer, so changing that will affect
|
||||
QPSK performance as well as the Datascope display.
|
||||
|
||||
As you can see from the figures above, clean C4FM signals tend to have
|
||||
four very sharply defined audio levels. The datascope pattern also
|
||||
tends to be faily stable with minor shifts left and right as the
|
||||
receiver tries to frequency track any DC offset.
|
||||
|
||||
QPSK signals on the other hand tend to appear much broader (and artifact
|
||||
of how they are distored by FM PLL discriminators). They also tend
|
||||
to vary wildly in width and centering. This is especially true when
|
||||
monitoring simulcast systems. Muliple QPSK signals interfere much more
|
||||
dramatically with an FM discriminator than C4FM signals.
|
||||
|
||||
*******
|
||||
For this reason it is important to isolate your receiver to one
|
||||
transmitter tower, _especially_ for QPSK signals.
|
||||
*******
|
||||
|
||||
The "#" symbols indicate the detected min/max values that are used
|
||||
to calibrate the symbol decision points. These are indicated by
|
||||
"!" for the center decision point and "^" for the mid decision points.
|
||||
|
||||
Display Options
|
||||
|
||||
There are several options to control the type and quantity of
|
||||
information displayed in Errorbars mode:
|
||||
|
||||
-e Show Frame Info and errorbars (default)
|
||||
-pe Show P25 encryption sync bits
|
||||
-pl Show P25 link control bits
|
||||
-ps Show P25 status bits and low speed data
|
||||
-pt Show P25 talkgroup info
|
||||
-q Don't show Frame Info/errorbars
|
||||
-s Datascope (disables other display options)
|
||||
-t Show symbol timing during sync
|
||||
-v <num> Frame information Verbosity
|
||||
-z <num> Frame rate for datascope
|
||||
|
||||
Most of these options are self explanitory. Symbol timing is a noisy
|
||||
option that allows you to view the quality of the frame sync samples
|
||||
and accuracy of the symbol timing adjustments.
|
||||
|
||||
Symbol Timing display looks like this:
|
||||
|
||||
Symbol Timing:
|
||||
----------
|
||||
----------
|
||||
----------
|
||||
----------
|
||||
----------
|
||||
-+++++++++ 1
|
||||
+---------- 0
|
||||
----------
|
||||
++++++++++ 0
|
||||
++++++++++
|
||||
---------- 0
|
||||
----------
|
||||
++++++++++ 0
|
||||
++++++++++
|
||||
++++++++++
|
||||
++++++++++
|
||||
---------- 0
|
||||
++++++++++ 0
|
||||
---------- 0
|
||||
++++++++++ 0
|
||||
++++++++++
|
||||
++++++++++
|
||||
++++++++++
|
||||
++++++++++
|
||||
C4FM example
|
||||
|
||||
Symbol Timing:
|
||||
+---------
|
||||
----------
|
||||
----------
|
||||
----------
|
||||
-----X---- 5
|
||||
--+++O++++- 4
|
||||
----------
|
||||
----X----- 4
|
||||
++++O++--- 4
|
||||
--++O++++- 4
|
||||
----X----- 4
|
||||
----------
|
||||
++++O+++-- 4
|
||||
-+++O+++-- 4
|
||||
--++O+++-- 4
|
||||
--++O+++-- 4
|
||||
----------
|
||||
++++O++++- 4
|
||||
----------
|
||||
++++O+++-- 4
|
||||
-+++O++++- 4
|
||||
-+++O+++++ 4
|
||||
-+++O++--- 4
|
||||
--++O+++-- 4
|
||||
QPSK example
|
||||
|
||||
Symbol timing is only displayed for symbols during the frame sync
|
||||
period. Each horizontal line represents the 10 audio samples for each
|
||||
symbol. "-" indicates an audio sample below the center reference level
|
||||
and "+" represents a sample above center. "X" indicates a low spike
|
||||
below a reference threshold (reference minimum for C4FM and 80%
|
||||
of reference minimum for QPSK). "O" represents a high spike above
|
||||
the high reference threshold. The numbers to the right indicate which
|
||||
sample position the targeted transition occurred (+/- for C4FM or
|
||||
spike high/low for QPSK). The number of audio samples for the next
|
||||
symbol are adjusted to get this value closer to the target (0 for
|
||||
C4FM and 4 for QPSK). This shows how DSD maintains accurate symbol
|
||||
timing. Symbol timing adjustments are only made during sync, which
|
||||
is the only time reliable transitions can be observed.
|
||||
|
||||
In both examples above the symbol timing was off by one sample at
|
||||
the beginning of the frame sync period and was adjusted. Generally
|
||||
if you see any spike values "X/O" in C4FM mode, or lots of them in
|
||||
QPSK mode it indicates noise on the input signal.
|
||||
|
||||
Input/Output Options
|
||||
|
||||
-i <device> Audio input device (default is /dev/audio)
|
||||
-o <device> Audio output device (default is /dev/audio)
|
||||
-d <dir> Create mbe data files, use this directory
|
||||
-g <num> Audio output gain (default = 0 = auto)
|
||||
-n Do not send synthesized speech to audio output device
|
||||
-w <file> Output synthesized speech to a .wav file
|
||||
|
||||
The audio in device can be a sound card OR a .wav file if the file
|
||||
is in the exact format 48k/16bits/mono/pcm. Audio in should be an
|
||||
unfilterd discriminator tap signal.
|
||||
|
||||
The audio out device should be a sound card (use the -w options to
|
||||
output to a .wav file).
|
||||
|
||||
If the audio in device is the same as the audio out device, the
|
||||
synthesized speech has to be upsampled to the 48k sample rate required
|
||||
for input. A fast upsample function is provided but still leaves some
|
||||
artifacts.
|
||||
|
||||
********
|
||||
The best sound and minimum cpu usage is achieved with separate sound
|
||||
cards for input and output
|
||||
********
|
||||
|
||||
if you specify different input/output devices DSD will use 8k as the
|
||||
output sample rate and the lack of resampling results in much better
|
||||
audio as well as lowe cpu consumption.
|
||||
|
||||
If you are using onboard "AC97" sound device you may find that DSD uses
|
||||
much more cpu than expected, in some cases more than is available.
|
||||
This is because many AC97 sound devices are designed to rely on CPU
|
||||
processing power instead of hardware. You may also find that 8k sample
|
||||
rate output is upsampled in the driver using a very basic algorithim
|
||||
resulting in severe distortion. The solution is to use a real hardware
|
||||
sound device (pci card, usb device etc).
|
||||
|
||||
As of version 1.2 DSD now automatically levels the output audio. This
|
||||
greately improves readability and eliminates the painful effects of
|
||||
noise bursts. You can specify a fixed audio output gain with the -g
|
||||
option.
|
||||
|
||||
Scanner control options:
|
||||
|
||||
-B <num> Serial port baud rate (default=115200)
|
||||
-C <device> Serial port for scanner control (default=/dev/ttyUSB0)
|
||||
-R <num> Resume scan after <num> TDULC frames or any PDU or TSDU
|
||||
|
||||
|
||||
On some P25 systems Packet Data Units (PDU) are sent on the same
|
||||
frequencies used for voice traffic. If done constantly this can
|
||||
be a severe hinderance to scanning the system in conventional
|
||||
mode. The -R option enables sending a "resume scan" command to
|
||||
a scanner connected to a serial port. Use -B and -C to set the baud
|
||||
rate and serial port device if necessary.
|
||||
|
||||
Decoder options
|
||||
|
||||
-fa Auto-detect frame type (default)
|
||||
-f1 Decode only P25 Phase 1
|
||||
-fd Decode only D-STAR* (no audio)
|
||||
-fi Decode only NXDN48* (6.25 kHz) / IDAS*
|
||||
-fn Decode only NXDN96 (12.5 kHz)
|
||||
-fp Decode only ProVoice*
|
||||
-fr Decode only DMR/MOTOTRBO
|
||||
-fx Decode only X2-TDMA
|
||||
-ma Auto-select modulation optimizations (default)
|
||||
-mc Use only C4FM modulation optimizations
|
||||
-mg Use only GFSK modulation optimizations
|
||||
-mq Use only QPSK modulation optimizations
|
||||
-u <num> Unvoiced speech quality (default=3)
|
||||
-xx Expect non-inverted X2-TDMA signal
|
||||
-xr Expect inverted DMR/MOTOTRBO signal
|
||||
|
||||
* denotes frame types that cannot be auto-detected.
|
||||
|
||||
ProVoice and NXDN48 not auto-detected as use different symbol
|
||||
rates (9600 and 2400) than most formats (4800). D-STAR is not
|
||||
enabled by default as voice decode does not work and it has a
|
||||
short sync word that is prone to false triggering. It is included
|
||||
for development/testing only.
|
||||
|
||||
MBE speech synthesis is broken down into two main types of sounds,
|
||||
"Voiced" and "Unvoiced". Voiced speech bands are synthesized with
|
||||
a single sine wave centered in the frequency band with the appropriate
|
||||
phase and amplitude.
|
||||
|
||||
Unvoiced speech is supposed to be generated with a noise source, 256
|
||||
point DFT a number of band filters, followed by a 256 point inverse DFT.
|
||||
For computational simplicity mbelib uses a different method. For each
|
||||
unvoiced speech band, a number of sine waves are generated, each with a
|
||||
different random initial phase. The number of waves used per band is
|
||||
controlled by the -u option. A setting of 4 would approximate the
|
||||
performance of the 256 point DFT method as the maximum number of voice
|
||||
bands is 56, and very low frequencies are not synthesized. Values less
|
||||
than 3 have a noticable lack of unvoiced speech and/or artifacts. The
|
||||
defualt of 3 provides good speech quality with reasonable cpu use.
|
||||
Increasing the quality above the default rapidly consumes more CPU for
|
||||
increasingly diminishing returns.
|
||||
|
||||
|
||||
Advanced decoder options
|
||||
-A <num> QPSK modulation auto detection threshold (default=26)
|
||||
-S <num> Symbol buffer size for QPSK decision point tracking
|
||||
(default=36)
|
||||
-M <num> Min/Max buffer size for QPSK decision point tracking
|
||||
(default=15)
|
||||
|
||||
Encryption
|
||||
|
||||
********
|
||||
Decryption of speech is NOT supported, even if you lawfully posess the
|
||||
encryption keys. Decryption support will not be added in the future as
|
||||
the authors wish to steer as far away from the legal issues associated
|
||||
with encryption as possible.
|
||||
********
|
||||
|
||||
We realize that there are many legitemate and lawful uses of decryption
|
||||
software including system/interoperability testing and lawful monitoring.
|
||||
This software is distributed under a liberal BSD license so there is
|
||||
nothing to stop others from supplying patches, forking this project or
|
||||
incorporating it into a commercial product and adding decryption support.
|
||||
|
||||
There is support for displaying the encryption sync bits transmitted in
|
||||
the clear on P25 Phase 1 systems. These bits do not allow for the
|
||||
decryption of signals without the secret encryption keys. The
|
||||
encryption sync bits are useful for determining whether a signal is
|
||||
encrypted vs merely noisy or degraded. As the encryption sync bits
|
||||
typically include long strings of zeros when a transmission is not
|
||||
encrypted they can also be used to visually estimate bit error rates.
|
||||
43
README.md
Normal file
43
README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
## Notice
|
||||
|
||||
This branch is for modifications to make a Windows 32-bit binary to be precompiled, please DO NOT use this branch if you are a Linux User, or copy any contents out of here to modify your existing setup!
|
||||
|
||||
# Digital Speech Decoder - Florida Man Edition
|
||||
|
||||
DSD-FME is an evolution of the original DSD project from 'DSD Author' using the base code of [szechyjs](https://github.com/szechyjs/dsd "szechyjs"), some code and ideas from [LouisErigHerve](https://github.com/LouisErigHerve/dsd "LouisErigHerve"), [Boatbod OP25](https://github.com/boatbod/op25 "Boatbod OP25") and
|
||||
[Osmocom OP25](https://gitea.osmocom.org/op25/op25 "Osmocom OP25"), along with other snippets of code, information, and inspirations from other projects including [DSDcc](https://github.com/f4exb/dsdcc "DSDcc"), [SDTRunk](https://github.com/DSheirer/sdrtrunk "SDRTrunk"), [MMDVMHost](https://github.com/g4klx/MMDVMHost "MMDVMHost"), [LFSR](https://github.com/mattames/LFSR "LFSR"), [OK-DMRlib](https://github.com/OK-DMR/ok-dmrlib "OK-DMRlib"), and [EZPWD-Reed-Solomon](https://github.com/pjkundert/ezpwd-reed-solomon "EZPWD"), Eric Cottrell, SP5WWP and others. Finally, this is all brought together with original code to extend the fuctionality and add new features including NCurses Terminal and Menu system, Pulse Audio, TCP Direct Link Audio, RIGCTL, Trunking Features, LRRP/GPS Mapping, P25 Phase 2, EDACS, OP25 Capture Bin compatability, etc. DSD-FME is primarily focused with Linux Desktop users, 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.
|
||||
|
||||
Click image below for video of current DMR/P25 trunking capabilities.
|
||||
|
||||
[](https://www.youtube.com/watch?v=hGIijBbbUxg "DSD-FME Trunking Demo")
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Information
|
||||
|
||||
See the [examples](https://github.com/lwvmobile/dsd-fme/tree/main/examples "examples") folder for information on [cloning and installing](https://github.com/lwvmobile/dsd-fme/blob/main/examples/Install_Notes.md "cloning and installing"), [example usage](https://github.com/lwvmobile/dsd-fme/blob/main/examples/Example_Usage.md "example usage"), and [trunking examples](https://github.com/lwvmobile/dsd-fme/blob/main/examples/trunking.sh "trunking examples").
|
||||
|
||||
## Notice
|
||||
|
||||
The pulseaudio branch has been deleted in favor of the new main branch which also uses Pulse Audio. Users moving to the main branch should review the help file and example usage files for changes in syntax. Users can download the last pulse audio branch version from the release link if they prefer the older version. 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 with 'auto intstall scripts' made for windows users may contain outdated information for building the deleted pulseaudio branch, please adjust any information found in those accordingly.
|
||||
|
||||
## License
|
||||
Copyright (C) 2010 DSD Author
|
||||
GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
15
cmake/FindITPP.cmake
Normal file
15
cmake/FindITPP.cmake
Normal file
@@ -0,0 +1,15 @@
|
||||
# - Try to find ITPP
|
||||
# Once done this will define
|
||||
#
|
||||
# ITPP_FOUND - System has ITPP
|
||||
# ITPP_INCLUDE_DIR - The ITPP include directory
|
||||
# ITPP_LIBRARY - The library needed to use ITPP
|
||||
#
|
||||
|
||||
find_path(ITPP_INCLUDE_DIR itpp/itcomm.h)
|
||||
|
||||
set(ITPP_NAMES ${ITPP_NAMES} itpp libitpp libitpp.dll)
|
||||
FIND_LIBRARY(ITPP_LIBRARY NAMES ${ITPP_NAMES})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(ITPP DEFAULT_MSG ITPP_LIBRARY ITPP_INCLUDE_DIR)
|
||||
16
cmake/FindLibSndFile.cmake
Normal file
16
cmake/FindLibSndFile.cmake
Normal file
@@ -0,0 +1,16 @@
|
||||
# - Try to find Libsndfile
|
||||
# Once done this will define
|
||||
#
|
||||
# LIBSNDFILE_FOUND - System has LIBSNDFILE
|
||||
# LIBSNDFILE_INCLUDE_DIR - The SNDFILE include directory
|
||||
# LIBSNDFILE_LIBRARY - The library needed to use SNDFILE
|
||||
#
|
||||
|
||||
find_path(LIBSNDFILE_INCLUDE_DIR sndfile.h)
|
||||
|
||||
SET(LIBSNDFILE_NAMES ${LIBSNDFILE_NAMES} sndfile libsndfile)
|
||||
FIND_LIBRARY(LIBSNDFILE_LIBRARY NAMES ${LIBSNDFILE_NAMES})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LibSndFile DEFAULT_MSG LIBSNDFILE_LIBRARY
|
||||
LIBSNDFILE_INCLUDE_DIR)
|
||||
15
cmake/FindMBE.cmake
Normal file
15
cmake/FindMBE.cmake
Normal file
@@ -0,0 +1,15 @@
|
||||
# - Try to find MBE
|
||||
# Once done this will define
|
||||
#
|
||||
# MBE_FOUND - System has MBE
|
||||
# MBE_INCLUDE_DIR - The MBE include directory
|
||||
# MBE_LIBRARY - The library needed to use MBE
|
||||
#
|
||||
|
||||
find_path(MBE_INCLUDE_DIR mbelib.h)
|
||||
|
||||
set(MBE_NAMES ${MBE_NAMES} mbe libmbe)
|
||||
find_library(MBE_LIBRARY NAMES ${MBE_NAMES})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(MBE DEFAULT_MSG MBE_LIBRARY MBE_INCLUDE_DIR)
|
||||
68
cmake/FindPULSEAUDIO.cmake
Normal file
68
cmake/FindPULSEAUDIO.cmake
Normal file
@@ -0,0 +1,68 @@
|
||||
#.rst:
|
||||
# FindPulseAudio
|
||||
# --------------
|
||||
# Finds the PulseAudio library
|
||||
#
|
||||
# This will define the following variables::
|
||||
#
|
||||
# PULSEAUDIO_FOUND - system has the PulseAudio library
|
||||
# PULSEAUDIO_INCLUDE_DIRS - the PulseAudio include directory
|
||||
# PULSEAUDIO_LIBRARIES - the libraries needed to use PulseAudio
|
||||
# PULSEAUDIO_DEFINITIONS - the definitions needed to use PulseAudio
|
||||
#
|
||||
# and the following imported targets::
|
||||
#
|
||||
# PulseAudio::PulseAudio - The PulseAudio library
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_PULSEAUDIO libpulse>=11.0.0 QUIET)
|
||||
pkg_check_modules(PC_PULSEAUDIO_MAINLOOP libpulse-mainloop-glib>=11.0.0 QUIET)
|
||||
pkg_check_modules(PC_PULSEAUDIO_SIMPLE libpulse-simple>=11.0.0 QUIET)
|
||||
endif()
|
||||
|
||||
find_path(PULSEAUDIO_INCLUDE_DIR NAMES pulse/pulseaudio.h pulse/simple.h
|
||||
PATHS ${PC_PULSEAUDIO_INCLUDEDIR} ${PC_PULSEAUDIO_INCLUDE_DIRS})
|
||||
|
||||
find_library(PULSEAUDIO_LIBRARY NAMES pulse libpulse
|
||||
PATHS ${PC_PULSEAUDIO_LIBDIR} ${PC_PULSEAUDIO_LIBRARY_DIRS})
|
||||
|
||||
find_library(PULSEAUDIO_SIMPLE_LIBRARY NAMES pulse-simple libpulse-simple
|
||||
PATHS ${PC_PULSEAUDIO_LIBDIR} ${PC_PULSEAUDIO_LIBRARY_DIRS})
|
||||
|
||||
find_library(PULSEAUDIO_MAINLOOP_LIBRARY NAMES pulse-mainloop pulse-mainloop-glib libpulse-mainloop-glib
|
||||
PATHS ${PC_PULSEAUDIO_LIBDIR} ${PC_PULSEAUDIO_LIBRARY_DIRS})
|
||||
|
||||
if(PC_PULSEAUDIO_VERSION)
|
||||
set(PULSEAUDIO_VERSION_STRING ${PC_PULSEAUDIO_VERSION})
|
||||
elseif(PULSEAUDIO_INCLUDE_DIR AND EXISTS "${PULSEAUDIO_INCLUDE_DIR}/pulse/version.h")
|
||||
file(STRINGS "${PULSEAUDIO_INCLUDE_DIR}/pulse/version.h" pulseaudio_version_str REGEX "^#define[\t ]+pa_get_headers_version\\(\\)[\t ]+\\(\".*\"\\).*")
|
||||
string(REGEX REPLACE "^#define[\t ]+pa_get_headers_version\\(\\)[\t ]+\\(\"([^\"]+)\"\\).*" "\\1" PULSEAUDIO_VERSION_STRING "${pulseaudio_version_str}")
|
||||
unset(pulseaudio_version_str)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(PulseAudio
|
||||
REQUIRED_VARS PULSEAUDIO_LIBRARY PULSEAUDIO_MAINLOOP_LIBRARY PULSEAUDIO_SIMPLE_LIBRARY PULSEAUDIO_INCLUDE_DIR
|
||||
VERSION_VAR PULSEAUDIO_VERSION_STRING)
|
||||
|
||||
if(PULSEAUDIO_FOUND)
|
||||
set(PULSEAUDIO_INCLUDE_DIRS ${PULSEAUDIO_INCLUDE_DIR})
|
||||
set(PULSEAUDIO_LIBRARIES ${PULSEAUDIO_LIBRARY} ${PULSEAUDIO_MAINLOOP_LIBRARY} ${PULSEAUDIO_SIMPLE_LIBRARY})
|
||||
set(PULSEAUDIO_DEFINITIONS -DHAS_PULSEAUDIO=1)
|
||||
|
||||
if(NOT TARGET PulseAudio::PulseAudioMainloop)
|
||||
add_library(PulseAudio::PulseAudioMainloop UNKNOWN IMPORTED)
|
||||
set_target_properties(PulseAudio::PulseAudioMainloop PROPERTIES
|
||||
IMPORTED_LOCATION "${PULSEAUDIO_MAINLOOP_LIBRARY}")
|
||||
endif()
|
||||
if(NOT TARGET PulseAudio::PulseAudio)
|
||||
add_library(PulseAudio::PulseAudio UNKNOWN IMPORTED)
|
||||
set_target_properties(PulseAudio::PulseAudio PROPERTIES
|
||||
IMPORTED_LOCATION "${PULSEAUDIO_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${PULSEAUDIO_INCLUDE_DIR}"
|
||||
INTERFACE_COMPILE_DEFINITIONS HAVE_LIBPULSE=1
|
||||
INTERFACE_LINK_LIBRARIES PulseAudio::PulseAudioMainloop)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(PULSEAUDIO_INCLUDE_DIR PULSEAUDIO_LIBRARY PULSEAUDIO_MAINLOOP_LIBRARY PULSEAUDIO_SIMPLE_LIBRARY)
|
||||
29
cmake/FindRTLSDR.cmake
Normal file
29
cmake/FindRTLSDR.cmake
Normal file
@@ -0,0 +1,29 @@
|
||||
# - Try to find rtlsdr - the hardware driver for the realtek chip in the dvb receivers
|
||||
# Once done this will define
|
||||
# RTLSDR_FOUND - System has rtlsdr
|
||||
# RTLSDR_LIBRARIES - The rtlsdr libraries
|
||||
# RTLSDR_INCLUDE_DIRS - The rtlsdr include directories
|
||||
# RTLSDR_LIB_DIRS - The rtlsdr library directories
|
||||
|
||||
if (NOT RTLSDR_FOUND)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(RTLSDR_PKG librtlsdr)
|
||||
set(RTLSDR_DEFINITIONS ${PC_RTLSDR_CFLAGS_OTHER})
|
||||
|
||||
find_path(RTLSDR_INCLUDE_DIR
|
||||
NAMES rtl-sdr.h
|
||||
HINTS ${RTLSDR_PKG_INCLUDE_DIRS})
|
||||
|
||||
find_library(RTLSDR_LIBRARY
|
||||
NAMES rtlsdr
|
||||
HINTS ${RTLSDR_PKG_LIBRARY_DIRS})
|
||||
|
||||
set(RTLSDR_LIBRARIES ${RTLSDR_LIBRARY})
|
||||
set(RTLSDR_INCLUDE_DIRS ${RTLSDR_INCLUDE_DIR})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(RTLSDR DEFAULT_MSG RTLSDR_LIBRARY RTLSDR_INCLUDE_DIR)
|
||||
mark_as_advanced(RTLSDR_INCLUDE_DIR RTLSDR_LIBRARY)
|
||||
|
||||
endif (NOT RTLSDR_FOUND)
|
||||
23
cmake/cmake_uninstall.cmake.in
Normal file
23
cmake/cmake_uninstall.cmake.in
Normal file
@@ -0,0 +1,23 @@
|
||||
if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||
message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
|
||||
endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||
|
||||
file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
|
||||
string(REGEX REPLACE "\n" ";" files "${files}")
|
||||
cmake_policy(SET CMP0007 OLD)
|
||||
list(REVERSE files)
|
||||
foreach (file ${files})
|
||||
message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
|
||||
if (EXISTS "$ENV{DESTDIR}${file}")
|
||||
execute_process(
|
||||
COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}"
|
||||
OUTPUT_VARIABLE rm_out
|
||||
RESULT_VARIABLE rm_retval
|
||||
)
|
||||
if(NOT ${rm_retval} EQUAL 0)
|
||||
message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
|
||||
endif (NOT ${rm_retval} EQUAL 0)
|
||||
else (EXISTS "$ENV{DESTDIR}${file}")
|
||||
message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
|
||||
endif (EXISTS "$ENV{DESTDIR}${file}")
|
||||
endforeach(file)
|
||||
130
cmake/git_revision.cmake
Normal file
130
cmake/git_revision.cmake
Normal file
@@ -0,0 +1,130 @@
|
||||
# - Returns a version string from Git
|
||||
#
|
||||
# These functions force a re-configure on each git commit so that you can
|
||||
# trust the values of the variables in your build system.
|
||||
#
|
||||
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the refspec and sha hash of the current head revision
|
||||
#
|
||||
# git_describe(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe on the source tree, and adjusting
|
||||
# the output so that it tests false if an error occurs.
|
||||
#
|
||||
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe --exact-match on the source tree,
|
||||
# and adjusting the output so that it tests false if there was no exact
|
||||
# matching tag.
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if(__get_git_revision_description)
|
||||
return()
|
||||
endif()
|
||||
set(__get_git_revision_description YES)
|
||||
|
||||
# We must run the following at "include" time, not at function call time,
|
||||
# to find the path to this module rather than the path to a calling list file
|
||||
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
|
||||
function(get_git_head_revision _refspecvar _hashvar)
|
||||
set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
||||
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
|
||||
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
|
||||
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
|
||||
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
|
||||
# We have reached the root directory, we are not in git
|
||||
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
||||
endwhile()
|
||||
# check if this is a submodule
|
||||
if(NOT IS_DIRECTORY ${GIT_DIR})
|
||||
file(READ ${GIT_DIR} submodule)
|
||||
string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
|
||||
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
|
||||
endif()
|
||||
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||
if(NOT EXISTS "${GIT_DATA}")
|
||||
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${GIT_DIR}/HEAD")
|
||||
return()
|
||||
endif()
|
||||
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
|
||||
|
||||
configure_file("cmake/git_revision.cmake.in"
|
||||
"${GIT_DATA}/grabRef.cmake"
|
||||
@ONLY)
|
||||
include("${GIT_DATA}/grabRef.cmake")
|
||||
|
||||
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
|
||||
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_describe _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# TODO sanitize
|
||||
#if((${ARGN}" MATCHES "&&") OR
|
||||
# (ARGN MATCHES "||") OR
|
||||
# (ARGN MATCHES "\\;"))
|
||||
# message("Please report the following error to the project!")
|
||||
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
|
||||
#endif()
|
||||
|
||||
#message(STATUS "Arguments to execute_process: ${ARGN}")
|
||||
|
||||
execute_process(COMMAND
|
||||
"${GIT_EXECUTABLE}"
|
||||
describe
|
||||
${hash}
|
||||
${ARGN}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_SOURCE_DIR}"
|
||||
RESULT_VARIABLE
|
||||
res
|
||||
OUTPUT_VARIABLE
|
||||
out
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
|
||||
set(${_var} "${out}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_get_exact_tag _var)
|
||||
git_describe(out --exact-match ${ARGN})
|
||||
set(${_var} "${out}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
38
cmake/git_revision.cmake.in
Normal file
38
cmake/git_revision.cmake.in
Normal file
@@ -0,0 +1,38 @@
|
||||
#
|
||||
# Internal file for GetGitRevisionDescription.cmake
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
set(HEAD_HASH)
|
||||
|
||||
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
|
||||
|
||||
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
|
||||
if(HEAD_CONTENTS MATCHES "ref")
|
||||
# named branch
|
||||
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
set(HEAD_HASH "${HEAD_REF}")
|
||||
endif()
|
||||
else()
|
||||
# detached HEAD
|
||||
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
endif()
|
||||
|
||||
if(NOT HEAD_HASH)
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
endif()
|
||||
3
configure
vendored
3
configure
vendored
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "There is no configure script, just run make"
|
||||
240
dmr_data.c
240
dmr_data.c
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
processDMRdata (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int i, dibit;
|
||||
int *dibit_p;
|
||||
char sync[25];
|
||||
char syncdata[25];
|
||||
char cachdata[13];
|
||||
char cc[5];
|
||||
char bursttype[5];
|
||||
|
||||
#ifdef DMR_DUMP
|
||||
int k;
|
||||
char syncbits[49];
|
||||
char cachbits[25];
|
||||
#endif
|
||||
|
||||
cc[4] = 0;
|
||||
bursttype[4] = 0;
|
||||
|
||||
dibit_p = state->dibit_buf_p - 90;
|
||||
|
||||
// CACH
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
cachdata[i] = dibit;
|
||||
if (i == 2)
|
||||
{
|
||||
state->currentslot = (1 & (dibit >> 1)); // bit 1
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
state->slot0light[0] = '[';
|
||||
state->slot0light[6] = ']';
|
||||
state->slot1light[0] = ' ';
|
||||
state->slot1light[6] = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
state->slot1light[0] = '[';
|
||||
state->slot1light[6] = ']';
|
||||
state->slot0light[0] = ' ';
|
||||
state->slot0light[6] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
cachdata[12] = 0;
|
||||
|
||||
#ifdef DMR_DUMP
|
||||
k = 0;
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
dibit = cachdata[i];
|
||||
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
k++;
|
||||
cachbits[k] = (1 & dibit) + 48; // bit 0
|
||||
k++;
|
||||
}
|
||||
cachbits[24] = 0;
|
||||
printf ("%s ", cachbits);
|
||||
#endif
|
||||
|
||||
// current slot
|
||||
dibit_p += 49;
|
||||
|
||||
// slot type
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
cc[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
cc[1] = (1 & dibit) + 48; // bit 0
|
||||
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
cc[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
cc[3] = (1 & dibit) + 48; // bit 0
|
||||
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
bursttype[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
bursttype[1] = (1 & dibit) + 48; // bit 0
|
||||
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
bursttype[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
bursttype[3] = (1 & dibit) + 48; // bit 0
|
||||
|
||||
// parity bit
|
||||
dibit_p++;
|
||||
|
||||
if (strcmp (bursttype, "0000") == 0)
|
||||
{
|
||||
sprintf (state->fsubtype, " PI Header ");
|
||||
}
|
||||
else if (strcmp (bursttype, "0001") == 0)
|
||||
{
|
||||
sprintf (state->fsubtype, " VOICE Header ");
|
||||
}
|
||||
else if (strcmp (bursttype, "0010") == 0)
|
||||
{
|
||||
sprintf (state->fsubtype, " TLC ");
|
||||
}
|
||||
else if (strcmp (bursttype, "0011") == 0)
|
||||
{
|
||||
sprintf (state->fsubtype, " CSBK ");
|
||||
}
|
||||
else if (strcmp (bursttype, "0100") == 0)
|
||||
{
|
||||
sprintf (state->fsubtype, " MBC Header ");
|
||||
}
|
||||
else if (strcmp (bursttype, "0101") == 0)
|
||||
{
|
||||
sprintf (state->fsubtype, " MBC ");
|
||||
}
|
||||
else if (strcmp (bursttype, "0110") == 0)
|
||||
{
|
||||
sprintf (state->fsubtype, " DATA Header ");
|
||||
}
|
||||
else if (strcmp (bursttype, "0111") == 0)
|
||||
{
|
||||
sprintf (state->fsubtype, " RATE 1/2 DATA");
|
||||
}
|
||||
else if (strcmp (bursttype, "1000") == 0)
|
||||
{
|
||||
sprintf (state->fsubtype, " RATE 3/4 DATA");
|
||||
}
|
||||
else if (strcmp (bursttype, "1001") == 0)
|
||||
{
|
||||
sprintf (state->fsubtype, " Slot idle ");
|
||||
}
|
||||
else if (strcmp (bursttype, "1010") == 0)
|
||||
{
|
||||
sprintf (state->fsubtype, " Rate 1 DATA ");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (state->fsubtype, " ");
|
||||
}
|
||||
|
||||
// signaling data or sync
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
syncdata[i] = dibit;
|
||||
sync[i] = (dibit | 1) + 48;
|
||||
}
|
||||
sync[24] = 0;
|
||||
syncdata[24] = 0;
|
||||
|
||||
#ifdef DMR_DUMP
|
||||
k = 0;
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
dibit = syncdata[i];
|
||||
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
k++;
|
||||
syncbits[k] = (1 & dibit) + 48; // bit 0
|
||||
k++;
|
||||
}
|
||||
syncbits[48] = 0;
|
||||
printf ("%s ", syncbits);
|
||||
#endif
|
||||
|
||||
if ((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
|
||||
{
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
sprintf (state->slot0light, "[slot0]");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (state->slot1light, "[slot1]");
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("%s %s ", state->slot0light, state->slot1light);
|
||||
}
|
||||
|
||||
// current slot second half, cach, next slot 1st half
|
||||
skipDibit (opts, state, 120);
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
if (strcmp (state->fsubtype, " ") == 0)
|
||||
{
|
||||
printf (" Unknown burst type: %s\n", bursttype);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("%s\n", state->fsubtype);
|
||||
}
|
||||
}
|
||||
}
|
||||
382
dmr_voice.c
382
dmr_voice.c
@@ -1,382 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include "dmr_const.h"
|
||||
|
||||
void
|
||||
processDMRvoice (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
// extracts AMBE frames from DMR frame
|
||||
int i, j, dibit;
|
||||
int *dibit_p;
|
||||
char ambe_fr[4][24];
|
||||
char ambe_fr2[4][24];
|
||||
char ambe_fr3[4][24];
|
||||
const int *w, *x, *y, *z;
|
||||
char sync[25];
|
||||
char syncdata[25];
|
||||
char cachdata[13];
|
||||
int mutecurrentslot;
|
||||
int msMode;
|
||||
|
||||
#ifdef DMR_DUMP
|
||||
int k;
|
||||
char syncbits[49];
|
||||
char cachbits[25];
|
||||
#endif
|
||||
|
||||
mutecurrentslot = 0;
|
||||
msMode = 0;
|
||||
|
||||
dibit_p = state->dibit_buf_p - 144;
|
||||
for (j = 0; j < 6; j++)
|
||||
{
|
||||
// 2nd half of previous slot
|
||||
for (i = 0; i < 54; i++)
|
||||
{
|
||||
if (j > 0)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CACH
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
if (j > 0)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
}
|
||||
cachdata[i] = dibit;
|
||||
if (i == 2)
|
||||
{
|
||||
state->currentslot = (1 & (dibit >> 1)); // bit 1
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
state->slot0light[0] = '[';
|
||||
state->slot0light[6] = ']';
|
||||
state->slot1light[0] = ' ';
|
||||
state->slot1light[6] = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
state->slot1light[0] = '[';
|
||||
state->slot1light[6] = ']';
|
||||
state->slot0light[0] = ' ';
|
||||
state->slot0light[6] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
cachdata[12] = 0;
|
||||
|
||||
|
||||
#ifdef DMR_DUMP
|
||||
k = 0;
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
dibit = cachdata[i];
|
||||
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
k++;
|
||||
cachbits[k] = (1 & dibit) + 48; // bit 0
|
||||
k++;
|
||||
}
|
||||
cachbits[24] = 0;
|
||||
printf ("%s ", cachbits);
|
||||
#endif
|
||||
|
||||
// current slot frame 1
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
for (i = 0; i < 36; i++)
|
||||
{
|
||||
if (j > 0)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
}
|
||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
|
||||
// current slot frame 2 first half
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
for (i = 0; i < 18; i++)
|
||||
{
|
||||
if (j > 0)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
}
|
||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
|
||||
// signaling data or sync
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
if (j > 0)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
}
|
||||
syncdata[i] = dibit;
|
||||
sync[i] = (dibit | 1) + 48;
|
||||
}
|
||||
sync[24] = 0;
|
||||
syncdata[24] = 0;
|
||||
|
||||
if ((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
|
||||
{
|
||||
mutecurrentslot = 1;
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
sprintf (state->slot0light, "[slot0]");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (state->slot1light, "[slot1]");
|
||||
}
|
||||
}
|
||||
else if ((strcmp (sync, DMR_BS_VOICE_SYNC) == 0) || (strcmp (sync, DMR_MS_VOICE_SYNC) == 0))
|
||||
{
|
||||
mutecurrentslot = 0;
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
sprintf (state->slot0light, "[SLOT0]");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (state->slot1light, "[SLOT1]");
|
||||
}
|
||||
}
|
||||
if ((strcmp (sync, DMR_MS_VOICE_SYNC) == 0) || (strcmp (sync, DMR_MS_DATA_SYNC) == 0))
|
||||
{
|
||||
msMode = 1;
|
||||
}
|
||||
|
||||
if ((j == 0) && (opts->errorbars == 1))
|
||||
{
|
||||
printf ("%s %s VOICE e:", state->slot0light, state->slot1light);
|
||||
}
|
||||
|
||||
#ifdef DMR_DUMP
|
||||
k = 0;
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
dibit = syncdata[i];
|
||||
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
k++;
|
||||
syncbits[k] = (1 & dibit) + 48; // bit 0
|
||||
k++;
|
||||
}
|
||||
syncbits[48] = 0;
|
||||
printf ("%s ", syncbits);
|
||||
#endif
|
||||
|
||||
// current slot frame 2 second half
|
||||
for (i = 0; i < 18; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
|
||||
if (mutecurrentslot == 0)
|
||||
{
|
||||
if (state->firstframe == 1)
|
||||
{ // we don't know if anything received before the first sync after no carrier is valid
|
||||
state->firstframe = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// current slot frame 3
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
for (i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
if (mutecurrentslot == 0)
|
||||
{
|
||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||
}
|
||||
|
||||
// CACH
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
cachdata[i] = dibit;
|
||||
}
|
||||
cachdata[12] = 0;
|
||||
|
||||
#ifdef DMR_DUMP
|
||||
k = 0;
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
dibit = cachdata[i];
|
||||
cachbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
k++;
|
||||
cachbits[k] = (1 & dibit) + 48; // bit 0
|
||||
k++;
|
||||
}
|
||||
cachbits[24] = 0;
|
||||
printf ("%s ", cachbits);
|
||||
#endif
|
||||
|
||||
|
||||
// next slot
|
||||
skipDibit (opts, state, 54);
|
||||
|
||||
// signaling data or sync
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
syncdata[i] = dibit;
|
||||
sync[i] = (dibit | 1) + 48;
|
||||
}
|
||||
sync[24] = 0;
|
||||
syncdata[24] = 0;
|
||||
|
||||
if ((strcmp (sync, DMR_BS_DATA_SYNC) == 0) || (msMode == 1))
|
||||
{
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
sprintf (state->slot1light, " slot1 ");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (state->slot0light, " slot0 ");
|
||||
}
|
||||
}
|
||||
else if (strcmp (sync, DMR_BS_VOICE_SYNC) == 0)
|
||||
{
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
sprintf (state->slot1light, " SLOT1 ");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (state->slot0light, " SLOT0 ");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DMR_DUMP
|
||||
k = 0;
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
dibit = syncdata[i];
|
||||
syncbits[k] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
k++;
|
||||
syncbits[k] = (1 & dibit) + 48; // bit 0
|
||||
k++;
|
||||
}
|
||||
syncbits[48] = 0;
|
||||
printf ("%s ", syncbits);
|
||||
#endif
|
||||
|
||||
if (j == 5)
|
||||
{
|
||||
// 2nd half next slot
|
||||
skipDibit (opts, state, 54);
|
||||
|
||||
// CACH
|
||||
skipDibit (opts, state, 12);
|
||||
|
||||
// first half current slot
|
||||
skipDibit (opts, state, 54);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
}
|
||||
BIN
dsd-fme-qgis-map.qgz
Normal file
BIN
dsd-fme-qgis-map.qgz
Normal file
Binary file not shown.
248
dsd.h
248
dsd.h
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#define __USE_XOPEN
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#ifdef SOLARIS
|
||||
#include <sys/audioio.h>
|
||||
#endif
|
||||
#ifdef BSD
|
||||
#include <sys/soundcard.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#include <mbelib.h>
|
||||
|
||||
/*
|
||||
* global variables
|
||||
*/
|
||||
int exitflag;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int onesymbol;
|
||||
char mbe_in_file[1024];
|
||||
FILE *mbe_in_f;
|
||||
int errorbars;
|
||||
int datascope;
|
||||
int symboltiming;
|
||||
int verbose;
|
||||
int p25enc;
|
||||
int p25lc;
|
||||
int p25status;
|
||||
int p25tg;
|
||||
int scoperate;
|
||||
char audio_in_dev[1024];
|
||||
int audio_in_fd;
|
||||
char audio_out_dev[1024];
|
||||
int audio_out_fd;
|
||||
int split;
|
||||
int playoffset;
|
||||
char mbe_out_dir[1024];
|
||||
char mbe_out_file[1024];
|
||||
FILE *mbe_out_f;
|
||||
float audio_gain;
|
||||
int audio_out;
|
||||
char wav_out_file[1024];
|
||||
FILE *wav_out_f;
|
||||
int wav_out_fd;
|
||||
int serial_baud;
|
||||
char serial_dev[1024];
|
||||
int serial_fd;
|
||||
int resume;
|
||||
int frame_dstar;
|
||||
int frame_x2tdma;
|
||||
int frame_p25p1;
|
||||
int frame_nxdn48;
|
||||
int frame_nxdn96;
|
||||
int frame_dmr;
|
||||
int frame_provoice;
|
||||
int mod_c4fm;
|
||||
int mod_qpsk;
|
||||
int mod_gfsk;
|
||||
int uvquality;
|
||||
int inverted_x2tdma;
|
||||
int inverted_dmr;
|
||||
int mod_threshold;
|
||||
int ssize;
|
||||
int msize;
|
||||
int playfiles;
|
||||
int delay;
|
||||
} dsd_opts;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int *dibit_buf;
|
||||
int *dibit_buf_p;
|
||||
int repeat;
|
||||
short *audio_out_buf;
|
||||
short *audio_out_buf_p;
|
||||
float *audio_out_float_buf;
|
||||
float *audio_out_float_buf_p;
|
||||
float audio_out_temp_buf[160];
|
||||
float *audio_out_temp_buf_p;
|
||||
int audio_out_idx;
|
||||
int audio_out_idx2;
|
||||
int wav_out_bytes;
|
||||
int center;
|
||||
int jitter;
|
||||
int synctype;
|
||||
int min;
|
||||
int max;
|
||||
int lmid;
|
||||
int umid;
|
||||
int minref;
|
||||
int maxref;
|
||||
int lastsample;
|
||||
int sbuf[128];
|
||||
int sidx;
|
||||
int maxbuf[1024];
|
||||
int minbuf[1024];
|
||||
int midx;
|
||||
char err_str[64];
|
||||
char fsubtype[16];
|
||||
char ftype[16];
|
||||
int symbolcnt;
|
||||
int rf_mod;
|
||||
int numflips;
|
||||
int lastsynctype;
|
||||
int lastp25type;
|
||||
int offset;
|
||||
int carrier;
|
||||
char tg[25][16];
|
||||
int tgcount;
|
||||
int lasttg;
|
||||
int lastsrc;
|
||||
int nac;
|
||||
int errs;
|
||||
int errs2;
|
||||
int mbe_file_type;
|
||||
int optind;
|
||||
int numtdulc;
|
||||
int firstframe;
|
||||
char slot0light[8];
|
||||
char slot1light[8];
|
||||
float aout_gain;
|
||||
float aout_max_buf[200];
|
||||
float *aout_max_buf_p;
|
||||
int aout_max_buf_idx;
|
||||
int samplesPerSymbol;
|
||||
int symbolCenter;
|
||||
char algid[9];
|
||||
char keyid[17];
|
||||
int currentslot;
|
||||
mbe_parms *cur_mp;
|
||||
mbe_parms *prev_mp;
|
||||
mbe_parms *prev_mp_enhanced;
|
||||
int p25kid;
|
||||
} dsd_state;
|
||||
|
||||
/*
|
||||
* Frame sync patterns
|
||||
*/
|
||||
#define INV_P25P1_SYNC "333331331133111131311111"
|
||||
#define P25P1_SYNC "111113113311333313133333"
|
||||
|
||||
#define X2TDMA_BS_VOICE_SYNC "113131333331313331113311"
|
||||
#define X2TDMA_BS_DATA_SYNC "331313111113131113331133"
|
||||
#define X2TDMA_MS_DATA_SYNC "313113333111111133333313"
|
||||
#define X2TDMA_MS_VOICE_SYNC "131331111333333311111131"
|
||||
|
||||
#define DSTAR_SYNC "313131313133131113313111"
|
||||
#define INV_DSTAR_SYNC "131313131311313331131333"
|
||||
|
||||
#define NXDN_MS_DATA_SYNC "313133113131111333"
|
||||
#define INV_NXDN_MS_DATA_SYNC "131311331313333111"
|
||||
#define NXDN_MS_VOICE_SYNC "313133113131113133"
|
||||
#define INV_NXDN_MS_VOICE_SYNC "131311331313331311"
|
||||
#define INV_NXDN_BS_DATA_SYNC "131311331313333131"
|
||||
#define NXDN_BS_DATA_SYNC "313133113131111313"
|
||||
#define INV_NXDN_BS_VOICE_SYNC "131311331313331331"
|
||||
#define NXDN_BS_VOICE_SYNC "313133113131113113"
|
||||
|
||||
#define DMR_BS_DATA_SYNC "313333111331131131331131"
|
||||
#define DMR_BS_VOICE_SYNC "131111333113313313113313"
|
||||
#define DMR_MS_DATA_SYNC "311131133313133331131113"
|
||||
#define DMR_MS_VOICE_SYNC "133313311131311113313331"
|
||||
|
||||
#define INV_PROVOICE_SYNC "31313111333133133311331133113311"
|
||||
#define PROVOICE_SYNC "13131333111311311133113311331133"
|
||||
#define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311"
|
||||
#define PROVOICE_EA_SYNC "31131311331331111133131311311133"
|
||||
|
||||
/*
|
||||
* function prototypes
|
||||
*/
|
||||
void processDMRdata (dsd_opts * opts, dsd_state * state);
|
||||
void processDMRvoice (dsd_opts * opts, dsd_state * state);
|
||||
void processAudio (dsd_opts * opts, dsd_state * state);
|
||||
void writeSynthesizedVoice (dsd_opts * opts, dsd_state * state);
|
||||
void playSynthesizedVoice (dsd_opts * opts, dsd_state * state);
|
||||
void openAudioOutDevice (dsd_opts * opts, int speed);
|
||||
void openAudioInDevice (dsd_opts * opts);
|
||||
int getDibit (dsd_opts * opts, dsd_state * state);
|
||||
void skipDibit (dsd_opts * opts, dsd_state * state, int count);
|
||||
void saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
||||
void saveAmbe2250Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
||||
int readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d);
|
||||
int readAmbe2250Data (dsd_opts * opts, dsd_state * state, char *ambe_d);
|
||||
void openMbeInFile (dsd_opts * opts, dsd_state * state);
|
||||
void closeMbeOutFile (dsd_opts * opts, dsd_state * state);
|
||||
void openMbeOutFile (dsd_opts * opts, dsd_state * state);
|
||||
void openWavOutFile (dsd_opts * opts, dsd_state * state);
|
||||
void closeWavOutFile (dsd_opts * opts, dsd_state * state);
|
||||
void printFrameInfo (dsd_opts * opts, dsd_state * state);
|
||||
void processFrame (dsd_opts * opts, dsd_state * state);
|
||||
void printFrameSync (dsd_opts * opts, dsd_state * state, char *frametype, int offset, char *modulation);
|
||||
int getFrameSync (dsd_opts * opts, dsd_state * state);
|
||||
int comp (const void *a, const void *b);
|
||||
void noCarrier (dsd_opts * opts, dsd_state * state);
|
||||
void initOpts (dsd_opts * opts);
|
||||
void initState (dsd_state * state);
|
||||
void usage ();
|
||||
void liveScanner (dsd_opts * opts, dsd_state * state);
|
||||
void cleanupAndExit (dsd_opts * opts, dsd_state * state);
|
||||
void sigfun (int sig);
|
||||
int main (int argc, char **argv);
|
||||
void playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv);
|
||||
void processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24]);
|
||||
void openSerial (dsd_opts * opts, dsd_state * state);
|
||||
void resumeScan (dsd_opts * opts, dsd_state * state);
|
||||
int getSymbol (dsd_opts * opts, dsd_state * state, int have_sync);
|
||||
void upsample (dsd_state * state, float invalue);
|
||||
void processDSTAR (dsd_opts * opts, dsd_state * state);
|
||||
void processNXDNVoice (dsd_opts * opts, dsd_state * state);
|
||||
void processNXDNData (dsd_opts * opts, dsd_state * state);
|
||||
void processP25lcw (dsd_opts * opts, dsd_state * state, char *lcformat, char *mfid, char *lcinfo);
|
||||
void processHDU (dsd_opts * opts, dsd_state * state);
|
||||
void processLDU1 (dsd_opts * opts, dsd_state * state);
|
||||
void processLDU2 (dsd_opts * opts, dsd_state * state);
|
||||
void processTDULC (dsd_opts * opts, dsd_state * state);
|
||||
void processProVoice (dsd_opts * opts, dsd_state * state);
|
||||
void processX2TDMAdata (dsd_opts * opts, dsd_state * state);
|
||||
void processX2TDMAvoice (dsd_opts * opts, dsd_state * state);
|
||||
369
dsd_audio.c
369
dsd_audio.c
@@ -1,369 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
processAudio (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int i, n;
|
||||
float aout_abs, max, gainfactor, gaindelta, maxbuf;
|
||||
|
||||
if (opts->audio_gain == (float) 0)
|
||||
{
|
||||
// detect max level
|
||||
max = 0;
|
||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
aout_abs = fabsf (*state->audio_out_temp_buf_p);
|
||||
if (aout_abs > max)
|
||||
{
|
||||
max = aout_abs;
|
||||
}
|
||||
state->audio_out_temp_buf_p++;
|
||||
}
|
||||
*state->aout_max_buf_p = max;
|
||||
state->aout_max_buf_p++;
|
||||
state->aout_max_buf_idx++;
|
||||
if (state->aout_max_buf_idx > 24)
|
||||
{
|
||||
state->aout_max_buf_idx = 0;
|
||||
state->aout_max_buf_p = state->aout_max_buf;
|
||||
}
|
||||
|
||||
// lookup max history
|
||||
for (i = 0; i < 25; i++)
|
||||
{
|
||||
maxbuf = state->aout_max_buf[i];
|
||||
if (maxbuf > max)
|
||||
{
|
||||
max = maxbuf;
|
||||
}
|
||||
}
|
||||
|
||||
// determine optimal gain level
|
||||
if (max > (float) 0)
|
||||
{
|
||||
gainfactor = ((float) 30000 / max);
|
||||
}
|
||||
else
|
||||
{
|
||||
gainfactor = (float) 50;
|
||||
}
|
||||
if (gainfactor < state->aout_gain)
|
||||
{
|
||||
state->aout_gain = gainfactor;
|
||||
gaindelta = (float) 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gainfactor > (float) 50)
|
||||
{
|
||||
gainfactor = (float) 50;
|
||||
}
|
||||
gaindelta = gainfactor - state->aout_gain;
|
||||
if (gaindelta > ((float) 0.05 * state->aout_gain))
|
||||
{
|
||||
gaindelta = ((float) 0.05 * state->aout_gain);
|
||||
}
|
||||
}
|
||||
gaindelta /= (float) 160;
|
||||
}
|
||||
else
|
||||
{
|
||||
gaindelta = (float) 0;
|
||||
}
|
||||
|
||||
// adjust output gain
|
||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
*state->audio_out_temp_buf_p = (state->aout_gain + ((float) n * gaindelta)) * (*state->audio_out_temp_buf_p);
|
||||
state->audio_out_temp_buf_p++;
|
||||
}
|
||||
state->aout_gain += ((float) 160 * gaindelta);
|
||||
|
||||
// copy audio datat to output buffer and upsample if necessary
|
||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||
if (opts->split == 0)
|
||||
{
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
upsample (state, *state->audio_out_temp_buf_p);
|
||||
state->audio_out_temp_buf_p++;
|
||||
state->audio_out_float_buf_p += 6;
|
||||
state->audio_out_idx += 6;
|
||||
state->audio_out_idx2 += 6;
|
||||
}
|
||||
state->audio_out_float_buf_p -= (960 + opts->playoffset);
|
||||
// copy to output (short) buffer
|
||||
for (n = 0; n < 960; n++)
|
||||
{
|
||||
if (*state->audio_out_float_buf_p > (float) 32760)
|
||||
{
|
||||
*state->audio_out_float_buf_p = (float) 32760;
|
||||
}
|
||||
else if (*state->audio_out_float_buf_p < (float) -32760)
|
||||
{
|
||||
*state->audio_out_float_buf_p = (float) -32760;
|
||||
}
|
||||
*state->audio_out_buf_p = (short) *state->audio_out_float_buf_p;
|
||||
state->audio_out_buf_p++;
|
||||
state->audio_out_float_buf_p++;
|
||||
}
|
||||
state->audio_out_float_buf_p += opts->playoffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
if (*state->audio_out_temp_buf_p > (float) 32760)
|
||||
{
|
||||
*state->audio_out_temp_buf_p = (float) 32760;
|
||||
}
|
||||
else if (*state->audio_out_temp_buf_p < (float) -32760)
|
||||
{
|
||||
*state->audio_out_temp_buf_p = (float) -32760;
|
||||
}
|
||||
*state->audio_out_buf_p = (short) *state->audio_out_temp_buf_p;
|
||||
state->audio_out_buf_p++;
|
||||
state->audio_out_temp_buf_p++;
|
||||
state->audio_out_idx++;
|
||||
state->audio_out_idx2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
writeSynthesizedVoice (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int n;
|
||||
short aout_buf[160];
|
||||
short *aout_buf_p;
|
||||
ssize_t result;
|
||||
|
||||
aout_buf_p = aout_buf;
|
||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
if (*state->audio_out_temp_buf_p > (float) 32760)
|
||||
{
|
||||
*state->audio_out_temp_buf_p = (float) 32760;
|
||||
}
|
||||
else if (*state->audio_out_temp_buf_p < (float) -32760)
|
||||
{
|
||||
*state->audio_out_temp_buf_p = (float) -32760;
|
||||
}
|
||||
*aout_buf_p = (short) *state->audio_out_temp_buf_p;
|
||||
aout_buf_p++;
|
||||
state->audio_out_temp_buf_p++;
|
||||
}
|
||||
|
||||
result = write (opts->wav_out_fd, aout_buf, 320);
|
||||
fflush (opts->wav_out_f);
|
||||
state->wav_out_bytes += 320;
|
||||
}
|
||||
|
||||
void
|
||||
playSynthesizedVoice (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
ssize_t result;
|
||||
|
||||
if (state->audio_out_idx > opts->delay)
|
||||
{
|
||||
// output synthesized speech to sound card
|
||||
result = write (opts->audio_out_fd, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2));
|
||||
state->audio_out_idx = 0;
|
||||
}
|
||||
|
||||
if (state->audio_out_idx2 >= 800000)
|
||||
{
|
||||
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
|
||||
state->audio_out_buf_p = state->audio_out_buf + 100;
|
||||
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
|
||||
memset (state->audio_out_buf, 0, 100 * sizeof (short));
|
||||
state->audio_out_idx2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
openAudioOutDevice (dsd_opts * opts, int speed)
|
||||
{
|
||||
|
||||
#ifdef SOLARIS
|
||||
sample_info_t aset, aget;
|
||||
|
||||
opts->audio_out_fd = open (opts->audio_out_dev, O_WRONLY);
|
||||
if (opts->audio_out_fd == -1)
|
||||
{
|
||||
printf ("Error, couldn't open %s\n", opts->audio_out_dev);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
// get current
|
||||
ioctl (opts->audio_out_fd, AUDIO_GETINFO, &aset);
|
||||
|
||||
aset.record.sample_rate = speed;
|
||||
aset.play.sample_rate = speed;
|
||||
aset.record.channels = 1;
|
||||
aset.play.channels = 1;
|
||||
aset.record.precision = 16;
|
||||
aset.play.precision = 16;
|
||||
aset.record.encoding = AUDIO_ENCODING_LINEAR;
|
||||
aset.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
|
||||
if (ioctl (opts->audio_out_fd, AUDIO_SETINFO, &aset) == -1)
|
||||
{
|
||||
printf ("Error setting sample device parameters\n");
|
||||
exit (1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BSD
|
||||
|
||||
int fmt;
|
||||
|
||||
opts->audio_out_fd = open (opts->audio_out_dev, O_WRONLY);
|
||||
if (opts->audio_out_fd == -1)
|
||||
{
|
||||
printf ("Error, couldn't open %s\n", opts->audio_out_dev);
|
||||
opts->audio_out = 0;
|
||||
}
|
||||
|
||||
fmt = 0;
|
||||
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0)
|
||||
{
|
||||
printf ("ioctl reset error \n");
|
||||
}
|
||||
fmt = speed;
|
||||
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SPEED, &fmt) < 0)
|
||||
{
|
||||
printf ("ioctl speed error \n");
|
||||
}
|
||||
fmt = 0;
|
||||
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_STEREO, &fmt) < 0)
|
||||
{
|
||||
printf ("ioctl stereo error \n");
|
||||
}
|
||||
fmt = AFMT_S16_LE;
|
||||
if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SETFMT, &fmt) < 0)
|
||||
{
|
||||
printf ("ioctl setfmt error \n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
printf ("Audio Out Device: %s\n", opts->audio_out_dev);
|
||||
}
|
||||
|
||||
void
|
||||
openAudioInDevice (dsd_opts * opts)
|
||||
{
|
||||
|
||||
#ifdef SOLARIS
|
||||
sample_info_t aset, aget;
|
||||
int rgain;
|
||||
|
||||
rgain = 64;
|
||||
|
||||
if (opts->split == 1)
|
||||
{
|
||||
opts->audio_in_fd = open (opts->audio_in_dev, O_RDONLY);
|
||||
}
|
||||
else
|
||||
{
|
||||
opts->audio_in_fd = open (opts->audio_in_dev, O_RDWR);
|
||||
}
|
||||
if (opts->audio_in_fd == -1)
|
||||
{
|
||||
printf ("Error, couldn't open /dev/audio\n");
|
||||
}
|
||||
|
||||
// get current
|
||||
ioctl (opts->audio_in_fd, AUDIO_GETINFO, &aset);
|
||||
|
||||
aset.record.sample_rate = 48000;
|
||||
aset.play.sample_rate = 48000;
|
||||
aset.record.channels = 1;
|
||||
aset.play.channels = 1;
|
||||
aset.record.precision = 16;
|
||||
aset.play.precision = 16;
|
||||
aset.record.encoding = AUDIO_ENCODING_LINEAR;
|
||||
aset.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
aset.record.port = AUDIO_LINE_IN;
|
||||
aset.record.gain = rgain;
|
||||
|
||||
if (ioctl (opts->audio_in_fd, AUDIO_SETINFO, &aset) == -1)
|
||||
{
|
||||
printf ("Error setting sample device parameters\n");
|
||||
exit (1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BSD
|
||||
int fmt;
|
||||
|
||||
if (opts->split == 1)
|
||||
{
|
||||
opts->audio_in_fd = open (opts->audio_in_dev, O_RDONLY);
|
||||
}
|
||||
else
|
||||
{
|
||||
opts->audio_in_fd = open (opts->audio_in_dev, O_RDWR);
|
||||
}
|
||||
|
||||
if (opts->audio_in_fd == -1)
|
||||
{
|
||||
printf ("Error, couldn't open %s\n", opts->audio_in_dev);
|
||||
opts->audio_out = 0;
|
||||
}
|
||||
|
||||
fmt = 0;
|
||||
if (ioctl (opts->audio_in_fd, SNDCTL_DSP_RESET) < 0)
|
||||
{
|
||||
printf ("ioctl reset error \n");
|
||||
}
|
||||
fmt = 48000;
|
||||
if (ioctl (opts->audio_in_fd, SNDCTL_DSP_SPEED, &fmt) < 0)
|
||||
{
|
||||
printf ("ioctl speed error \n");
|
||||
}
|
||||
fmt = 0;
|
||||
if (ioctl (opts->audio_in_fd, SNDCTL_DSP_STEREO, &fmt) < 0)
|
||||
{
|
||||
printf ("ioctl stereo error \n");
|
||||
}
|
||||
fmt = AFMT_S16_LE;
|
||||
if (ioctl (opts->audio_in_fd, SNDCTL_DSP_SETFMT, &fmt) < 0)
|
||||
{
|
||||
printf ("ioctl setfmt error \n");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (opts->split == 1)
|
||||
{
|
||||
printf ("Audio In Device: %s\n", opts->audio_in_dev);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("Audio In/Out Device: %s\n", opts->audio_in_dev);
|
||||
}
|
||||
}
|
||||
295
dsd_dibit.c
295
dsd_dibit.c
@@ -1,295 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
int
|
||||
getDibit (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
// returns one dibit value
|
||||
int i, j, o, symbol;
|
||||
int sbuf2[128];
|
||||
int spectrum[64];
|
||||
char modulation[8];
|
||||
int lmin, lmax, lsum;
|
||||
|
||||
state->numflips = 0;
|
||||
|
||||
symbol = getSymbol (opts, state, 1);
|
||||
state->sbuf[state->sidx] = symbol;
|
||||
|
||||
for (i = 0; i < opts->ssize; i++)
|
||||
{
|
||||
sbuf2[i] = state->sbuf[i];
|
||||
}
|
||||
|
||||
qsort (sbuf2, opts->ssize, sizeof (int), comp);
|
||||
// continuous update of min/max in rf_mod=1 (QPSK) mode
|
||||
// in c4fm min/max must only be updated during sync
|
||||
if (state->rf_mod == 1)
|
||||
{
|
||||
lmin = (sbuf2[0] + sbuf2[1]) / 2;
|
||||
lmax = (sbuf2[(opts->ssize - 1)] + sbuf2[(opts->ssize - 2)]) / 2;
|
||||
state->minbuf[state->midx] = lmin;
|
||||
state->maxbuf[state->midx] = lmax;
|
||||
if (state->midx == (opts->msize - 1))
|
||||
{
|
||||
state->midx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->midx++;
|
||||
}
|
||||
lsum = 0;
|
||||
for (i = 0; i < opts->msize; i++)
|
||||
{
|
||||
lsum += state->minbuf[i];
|
||||
}
|
||||
state->min = lsum / opts->msize;
|
||||
lsum = 0;
|
||||
for (i = 0; i < opts->msize; i++)
|
||||
{
|
||||
lsum += state->maxbuf[i];
|
||||
}
|
||||
state->max = lsum / opts->msize;
|
||||
state->center = ((state->max) + (state->min)) / 2;
|
||||
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
|
||||
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
|
||||
state->maxref = ((state->max) * 0.80);
|
||||
state->minref = ((state->min) * 0.80);
|
||||
}
|
||||
else
|
||||
{
|
||||
state->maxref = state->max;
|
||||
state->minref = state->min;
|
||||
}
|
||||
|
||||
if (state->sidx == (opts->ssize - 1))
|
||||
{
|
||||
|
||||
state->sidx = 0;
|
||||
|
||||
if (opts->datascope == 1)
|
||||
{
|
||||
if (state->rf_mod == 0)
|
||||
{
|
||||
sprintf (modulation, "C4FM");
|
||||
}
|
||||
else if (state->rf_mod == 1)
|
||||
{
|
||||
sprintf (modulation, "QPSK");
|
||||
}
|
||||
else if (state->rf_mod == 2)
|
||||
{
|
||||
sprintf (modulation, "GFSK");
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
spectrum[i] = 0;
|
||||
}
|
||||
for (i = 0; i < opts->ssize; i++)
|
||||
{
|
||||
o = (sbuf2[i] + 32768) / 1024;
|
||||
spectrum[o]++;
|
||||
}
|
||||
if (state->symbolcnt > (4800 / opts->scoperate))
|
||||
{
|
||||
state->symbolcnt = 0;
|
||||
printf ("\n");
|
||||
printf ("Demod mode: %s Nac: %4X\n", modulation, state->nac);
|
||||
printf ("Frame Type: %s Talkgroup: %7i\n", state->ftype, state->lasttg);
|
||||
printf ("Frame Subtype: %s Source: %12i\n", state->fsubtype, state->lastsrc);
|
||||
printf ("TDMA activity: %s %s Voice errors: %s\n", state->slot0light, state->slot1light, state->err_str);
|
||||
printf ("+----------------------------------------------------------------+\n");
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
printf ("|");
|
||||
for (j = 0; j < 64; j++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
if ((j == ((state->min) + 32768) / 1024) || (j == ((state->max) + 32768) / 1024))
|
||||
{
|
||||
printf ("#");
|
||||
}
|
||||
else if ((j == ((state->lmid) + 32768) / 1024) || (j == ((state->umid) + 32768) / 1024))
|
||||
{
|
||||
printf ("^");
|
||||
}
|
||||
else if (j == (state->center + 32768) / 1024)
|
||||
{
|
||||
printf ("!");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (j == 32)
|
||||
{
|
||||
printf ("|");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf (" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spectrum[j] > 9 - i)
|
||||
{
|
||||
printf ("*");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (j == 32)
|
||||
{
|
||||
printf ("|");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf (" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
printf ("|\n");
|
||||
}
|
||||
printf ("+----------------------------------------------------------------+\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state->sidx++;
|
||||
}
|
||||
|
||||
if (state->dibit_buf_p > state->dibit_buf + 900000)
|
||||
{
|
||||
state->dibit_buf_p = state->dibit_buf + 200;
|
||||
}
|
||||
|
||||
// determine dibit state
|
||||
if ((state->synctype == 6) || (state->synctype == 14))
|
||||
{
|
||||
if (symbol > state->center)
|
||||
{
|
||||
*state->dibit_buf_p = 1;
|
||||
state->dibit_buf_p++;
|
||||
return (0);
|
||||
}
|
||||
else
|
||||
{
|
||||
*state->dibit_buf_p = 3;
|
||||
state->dibit_buf_p++;
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
else if ((state->synctype == 7) || (state->synctype == 15))
|
||||
{
|
||||
if (symbol > state->center)
|
||||
{
|
||||
*state->dibit_buf_p = 1;
|
||||
state->dibit_buf_p++;
|
||||
return (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
*state->dibit_buf_p = 3;
|
||||
state->dibit_buf_p++;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) || (state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13))
|
||||
{
|
||||
if (symbol > state->center)
|
||||
{
|
||||
if (symbol > state->umid)
|
||||
{
|
||||
*state->dibit_buf_p = 1; // store non-inverted values in dibit_buf
|
||||
state->dibit_buf_p++;
|
||||
return (3);
|
||||
}
|
||||
else
|
||||
{
|
||||
*state->dibit_buf_p = 0;
|
||||
state->dibit_buf_p++;
|
||||
return (2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (symbol < state->lmid)
|
||||
{
|
||||
*state->dibit_buf_p = 3;
|
||||
state->dibit_buf_p++;
|
||||
return (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
*state->dibit_buf_p = 2;
|
||||
state->dibit_buf_p++;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (symbol > state->center)
|
||||
{
|
||||
if (symbol > state->umid)
|
||||
{
|
||||
*state->dibit_buf_p = 1;
|
||||
state->dibit_buf_p++;
|
||||
return (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
*state->dibit_buf_p = 0;
|
||||
state->dibit_buf_p++;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (symbol < state->lmid)
|
||||
{
|
||||
*state->dibit_buf_p = 3;
|
||||
state->dibit_buf_p++;
|
||||
return (3);
|
||||
}
|
||||
else
|
||||
{
|
||||
*state->dibit_buf_p = 2;
|
||||
state->dibit_buf_p++;
|
||||
return (2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
skipDibit (dsd_opts * opts, dsd_state * state, int count)
|
||||
{
|
||||
|
||||
short sample;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (count); i++)
|
||||
{
|
||||
sample = getDibit (opts, state);
|
||||
}
|
||||
}
|
||||
406
dsd_file.c
406
dsd_file.c
@@ -1,406 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
||||
{
|
||||
int i, j, k;
|
||||
unsigned char b;
|
||||
unsigned char err;
|
||||
|
||||
err = (unsigned char) state->errs2;
|
||||
fputc (err, opts->mbe_out_f);
|
||||
|
||||
k = 0;
|
||||
for (i = 0; i < 11; i++)
|
||||
{
|
||||
b = 0;
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
b = b << 1;
|
||||
b = b + imbe_d[k];
|
||||
k++;
|
||||
}
|
||||
fputc (b, opts->mbe_out_f);
|
||||
}
|
||||
fflush (opts->mbe_out_f);
|
||||
}
|
||||
|
||||
void
|
||||
saveAmbe2250Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||
{
|
||||
int i, j, k;
|
||||
unsigned char b;
|
||||
unsigned char err;
|
||||
|
||||
err = (unsigned char) state->errs2;
|
||||
fputc (err, opts->mbe_out_f);
|
||||
|
||||
k = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
b = 0;
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
b = b << 1;
|
||||
b = b + ambe_d[k];
|
||||
k++;
|
||||
}
|
||||
fputc (b, opts->mbe_out_f);
|
||||
}
|
||||
b = ambe_d[48];
|
||||
fputc (b, opts->mbe_out_f);
|
||||
fflush (opts->mbe_out_f);
|
||||
}
|
||||
|
||||
int
|
||||
readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
||||
{
|
||||
|
||||
int i, j, k;
|
||||
unsigned char b;
|
||||
|
||||
state->errs2 = fgetc (opts->mbe_in_f);
|
||||
state->errs = state->errs2;
|
||||
|
||||
k = 0;
|
||||
for (i = 0; i < 11; i++)
|
||||
{
|
||||
b = fgetc (opts->mbe_in_f);
|
||||
if (feof (opts->mbe_in_f))
|
||||
{
|
||||
return (1);
|
||||
}
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
imbe_d[k] = (b & 128) >> 7;
|
||||
b = b << 1;
|
||||
b = b & 255;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
readAmbe2250Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||
{
|
||||
|
||||
int i, j, k;
|
||||
unsigned char b;
|
||||
|
||||
state->errs2 = fgetc (opts->mbe_in_f);
|
||||
state->errs = state->errs2;
|
||||
|
||||
k = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
b = fgetc (opts->mbe_in_f);
|
||||
if (feof (opts->mbe_in_f))
|
||||
{
|
||||
return (1);
|
||||
}
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
ambe_d[k] = (b & 128) >> 7;
|
||||
b = b << 1;
|
||||
b = b & 255;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
b = fgetc (opts->mbe_in_f);
|
||||
ambe_d[48] = (b & 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
openMbeInFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
char cookie[5];
|
||||
|
||||
opts->mbe_in_f = fopen (opts->mbe_in_file, "ro");
|
||||
if (opts->mbe_in_f == NULL)
|
||||
{
|
||||
printf ("Error: could not open %s\n", opts->mbe_in_file);
|
||||
}
|
||||
|
||||
// read cookie
|
||||
cookie[0] = fgetc (opts->mbe_in_f);
|
||||
cookie[1] = fgetc (opts->mbe_in_f);
|
||||
cookie[2] = fgetc (opts->mbe_in_f);
|
||||
cookie[3] = fgetc (opts->mbe_in_f);
|
||||
cookie[4] = 0;
|
||||
if (strstr (cookie, ".amb") != NULL)
|
||||
{
|
||||
state->mbe_file_type = 1;
|
||||
}
|
||||
else if (strstr (cookie, ".imb") != NULL)
|
||||
{
|
||||
state->mbe_file_type = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->mbe_file_type = -1;
|
||||
printf ("Error - unrecognized file type\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
char shell[255], newfilename[64], ext[5], datestr[32];
|
||||
char tgid[17];
|
||||
int sum, i, j;
|
||||
int talkgroup;
|
||||
struct tm timep;
|
||||
int result;
|
||||
|
||||
if (opts->mbe_out_f != NULL)
|
||||
{
|
||||
if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
|
||||
{
|
||||
sprintf (ext, ".imb");
|
||||
strptime (opts->mbe_out_file, "%s.imb", &timep);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (ext, ".amb");
|
||||
strptime (opts->mbe_out_file, "%s.amb", &timep);
|
||||
}
|
||||
|
||||
if (state->tgcount > 0)
|
||||
{
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
sum = 0;
|
||||
for (j = 0; j < state->tgcount; j++)
|
||||
{
|
||||
sum = sum + state->tg[j][i] - 48;
|
||||
}
|
||||
tgid[i] = (char) (((float) sum / (float) state->tgcount) + 48.5);
|
||||
}
|
||||
tgid[16] = 0;
|
||||
talkgroup = (int) strtol (tgid, NULL, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
talkgroup = 0;
|
||||
}
|
||||
|
||||
fflush (opts->mbe_out_f);
|
||||
fclose (opts->mbe_out_f);
|
||||
opts->mbe_out_f = NULL;
|
||||
strftime (datestr, 31, "%Y-%m-%d-%H%M%S", &timep);
|
||||
sprintf (newfilename, "nac%X-%s-tg%i%s", state->nac, datestr, talkgroup, ext);
|
||||
sprintf (shell, "mv %s %s", opts->mbe_out_file, newfilename);
|
||||
result = system (shell);
|
||||
|
||||
state->tgcount = 0;
|
||||
for (i = 0; i < 25; i++)
|
||||
{
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
state->tg[i][j] = 48;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
struct timeval tv;
|
||||
int i, j;
|
||||
char ext[5];
|
||||
|
||||
if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
|
||||
{
|
||||
sprintf (ext, ".imb");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (ext, ".amb");
|
||||
}
|
||||
|
||||
// reset talkgroup id buffer
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
for (j = 0; j < 25; j++)
|
||||
{
|
||||
state->tg[j][i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
state->tgcount = 0;
|
||||
|
||||
gettimeofday (&tv, NULL);
|
||||
sprintf (opts->mbe_out_file, "%i%s", (int) tv.tv_sec, ext);
|
||||
opts->mbe_out_f = fopen (opts->mbe_out_file, "w");
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
printf ("Error, couldn't open %s\n", opts->mbe_out_file);
|
||||
}
|
||||
|
||||
// write magic
|
||||
fprintf (opts->mbe_out_f, "%s", ext);
|
||||
|
||||
fflush (opts->mbe_out_f);
|
||||
}
|
||||
|
||||
void
|
||||
openWavOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
opts->wav_out_f = fopen (opts->wav_out_file, "w");
|
||||
if (opts->wav_out_f == NULL)
|
||||
{
|
||||
printf ("Error - could not open wav output file %s\n", opts->wav_out_file);
|
||||
return;
|
||||
}
|
||||
opts->wav_out_fd = fileno (opts->wav_out_f);
|
||||
state->wav_out_bytes = 0;
|
||||
|
||||
fprintf (opts->wav_out_f, "RIFF");
|
||||
// total length
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
fprintf (opts->wav_out_f, "WAVE");
|
||||
fprintf (opts->wav_out_f, "fmt ");
|
||||
|
||||
// length of format chunk
|
||||
fputc (16, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// always 0x1
|
||||
fputc (1, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// channels
|
||||
fputc (1, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// sample rate
|
||||
fputc (64, opts->wav_out_f);
|
||||
fputc (31, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// bytes per second
|
||||
fputc (176, opts->wav_out_f);
|
||||
fputc (62, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// block align
|
||||
fputc (2, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// bits/sample
|
||||
fputc (16, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// data chunk header
|
||||
fprintf (opts->wav_out_f, "data");
|
||||
|
||||
// length of data
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
fflush (opts->wav_out_f);
|
||||
}
|
||||
|
||||
void
|
||||
closeWavOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int length;
|
||||
|
||||
if (opts->wav_out_f != NULL)
|
||||
{
|
||||
rewind (opts->wav_out_f);
|
||||
length = state->wav_out_bytes;
|
||||
|
||||
fprintf (opts->wav_out_f, "RIFF");
|
||||
// total length
|
||||
fputc (((36 + length) & 0xff), opts->wav_out_f);
|
||||
fputc ((((36 + length) >> 8) & 0xff), opts->wav_out_f);
|
||||
fputc ((((36 + length) >> 16) & 0xff), opts->wav_out_f);
|
||||
fputc ((((36 + length) >> 24) & 0xff), opts->wav_out_f);
|
||||
|
||||
fprintf (opts->wav_out_f, "WAVE");
|
||||
fprintf (opts->wav_out_f, "fmt ");
|
||||
|
||||
// length of format chunk
|
||||
fputc (16, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// always 0x1
|
||||
fputc (1, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// channels
|
||||
fputc (1, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// sample rate
|
||||
fputc (64, opts->wav_out_f);
|
||||
fputc (31, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// bytes per second
|
||||
fputc (128, opts->wav_out_f);
|
||||
fputc (62, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// block align
|
||||
fputc (2, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// bits/sample
|
||||
fputc (16, opts->wav_out_f);
|
||||
fputc (0, opts->wav_out_f);
|
||||
|
||||
// data chunk header
|
||||
fprintf (opts->wav_out_f, "data");
|
||||
|
||||
// length of data
|
||||
fputc ((length & 0xff), opts->wav_out_f);
|
||||
fputc (((length >> 8) & 0xff), opts->wav_out_f);
|
||||
fputc (((length >> 16) & 0xff), opts->wav_out_f);
|
||||
fputc (((length >> 24) & 0xff), opts->wav_out_f);
|
||||
|
||||
fflush (opts->wav_out_f);
|
||||
}
|
||||
}
|
||||
448
dsd_frame.c
448
dsd_frame.c
@@ -1,448 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
printFrameInfo (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int level;
|
||||
|
||||
level = (int) state->max / 164;
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
printf ("inlvl: %2i%% ", level);
|
||||
}
|
||||
if (state->nac != 0)
|
||||
{
|
||||
printf ("nac: %4X ", state->nac);
|
||||
}
|
||||
|
||||
if (opts->verbose > 1)
|
||||
{
|
||||
printf ("src: %8i ", state->lastsrc);
|
||||
}
|
||||
printf ("tg: %5i ", state->lasttg);
|
||||
}
|
||||
|
||||
void
|
||||
processFrame (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int i, j, dibit;
|
||||
char duid[3];
|
||||
char nac[13];
|
||||
int level;
|
||||
|
||||
duid[2] = 0;
|
||||
j = 0;
|
||||
|
||||
if (state->rf_mod == 1)
|
||||
{
|
||||
state->maxref = (state->max * 0.80);
|
||||
state->minref = (state->min * 0.80);
|
||||
}
|
||||
else
|
||||
{
|
||||
state->maxref = state->max;
|
||||
state->minref = state->min;
|
||||
}
|
||||
|
||||
if ((state->synctype == 8) || (state->synctype == 9))
|
||||
{
|
||||
state->rf_mod = 2;
|
||||
state->nac = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lasttg = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
level = (int) state->max / 164;
|
||||
printf ("inlvl: %2i%% ", level);
|
||||
}
|
||||
}
|
||||
state->nac = 0;
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
sprintf (state->fsubtype, " VOICE ");
|
||||
processNXDNVoice (opts, state);
|
||||
return;
|
||||
}
|
||||
else if ((state->synctype == 16) || (state->synctype == 17))
|
||||
{
|
||||
state->rf_mod = 2;
|
||||
state->nac = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lasttg = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
level = (int) state->max / 164;
|
||||
printf ("inlvl: %2i%% ", level);
|
||||
}
|
||||
}
|
||||
state->nac = 0;
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
sprintf (state->fsubtype, " DATA ");
|
||||
processNXDNData (opts, state);
|
||||
return;
|
||||
}
|
||||
else if ((state->synctype == 6) || (state->synctype == 7))
|
||||
{
|
||||
state->nac = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lasttg = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
level = (int) state->max / 164;
|
||||
printf ("inlvl: %2i%% ", level);
|
||||
}
|
||||
}
|
||||
state->nac = 0;
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
sprintf (state->fsubtype, " VOICE ");
|
||||
processDSTAR (opts, state);
|
||||
return;
|
||||
}
|
||||
else if ((state->synctype >= 10) && (state->synctype <= 13))
|
||||
{
|
||||
state->nac = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lasttg = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
level = (int) state->max / 164;
|
||||
printf ("inlvl: %2i%% ", level);
|
||||
}
|
||||
}
|
||||
if ((state->synctype == 11) || (state->synctype == 12))
|
||||
{
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
sprintf (state->fsubtype, " VOICE ");
|
||||
processDMRvoice (opts, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
closeMbeOutFile (opts, state);
|
||||
state->err_str[0] = 0;
|
||||
processDMRdata (opts, state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if ((state->synctype >= 2) && (state->synctype <= 5))
|
||||
{
|
||||
state->nac = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
}
|
||||
if ((state->synctype == 3) || (state->synctype == 4))
|
||||
{
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
sprintf (state->fsubtype, " VOICE ");
|
||||
processX2TDMAvoice (opts, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
closeMbeOutFile (opts, state);
|
||||
state->err_str[0] = 0;
|
||||
processX2TDMAdata (opts, state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if ((state->synctype == 14) || (state->synctype == 15))
|
||||
{
|
||||
state->nac = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lasttg = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
level = (int) state->max / 164;
|
||||
printf ("inlvl: %2i%% ", level);
|
||||
}
|
||||
}
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
sprintf (state->fsubtype, " VOICE ");
|
||||
processProVoice (opts, state);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
j = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
nac[j] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
j++;
|
||||
nac[j] = (1 & dibit) + 48; // bit 0
|
||||
j++;
|
||||
}
|
||||
nac[12] = 0;
|
||||
state->nac = strtol (nac, NULL, 2);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
duid[i] = getDibit (opts, state) + 48;
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp (duid, "00") == 0)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf (" HDU\n");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
closeMbeOutFile (opts, state);
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
state->lastp25type = 2;
|
||||
sprintf (state->fsubtype, " HDU ");
|
||||
processHDU (opts, state);
|
||||
}
|
||||
else if (strcmp (duid, "11") == 0)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf (" LDU1 ");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
}
|
||||
state->lastp25type = 1;
|
||||
sprintf (state->fsubtype, " LDU1 ");
|
||||
state->numtdulc = 0;
|
||||
processLDU1 (opts, state);
|
||||
}
|
||||
else if (strcmp (duid, "22") == 0)
|
||||
{
|
||||
if (state->lastp25type != 1)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf (" Ignoring LDU2 not preceeded by LDU1\n");
|
||||
}
|
||||
state->lastp25type = 0;
|
||||
sprintf (state->fsubtype, " ");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf (" LDU2 ");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
}
|
||||
state->lastp25type = 2;
|
||||
sprintf (state->fsubtype, " LDU2 ");
|
||||
state->numtdulc = 0;
|
||||
processLDU2 (opts, state);
|
||||
}
|
||||
}
|
||||
else if (strcmp (duid, "33") == 0)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf (" TDULC\n");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
closeMbeOutFile (opts, state);
|
||||
}
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lastp25type = 0;
|
||||
state->err_str[0] = 0;
|
||||
sprintf (state->fsubtype, " TDULC ");
|
||||
state->numtdulc++;
|
||||
if ((opts->resume > 0) && (state->numtdulc > opts->resume))
|
||||
{
|
||||
resumeScan (opts, state);
|
||||
}
|
||||
processTDULC (opts, state);
|
||||
state->err_str[0] = 0;
|
||||
}
|
||||
else if (strcmp (duid, "03") == 0)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf (" TDU\n");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
closeMbeOutFile (opts, state);
|
||||
}
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lastp25type = 0;
|
||||
state->err_str[0] = 0;
|
||||
sprintf (state->fsubtype, " TDU ");
|
||||
skipDibit (opts, state, 40);
|
||||
}
|
||||
else if (strcmp (duid, "13") == 0)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf (" TSDU\n");
|
||||
}
|
||||
if (opts->resume > 0)
|
||||
{
|
||||
resumeScan (opts, state);
|
||||
}
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lastp25type = 3;
|
||||
sprintf (state->fsubtype, " TSDU ");
|
||||
skipDibit (opts, state, 328);
|
||||
}
|
||||
else if (strcmp (duid, "30") == 0)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf (" PDU\n");
|
||||
}
|
||||
if (opts->resume > 0)
|
||||
{
|
||||
resumeScan (opts, state);
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
}
|
||||
state->lastp25type = 4;
|
||||
sprintf (state->fsubtype, " PDU ");
|
||||
}
|
||||
// try to guess based on previous frame if unknown type
|
||||
else if (state->lastp25type == 1)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf ("(LDU2) ");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
}
|
||||
state->lastp25type = 0;
|
||||
sprintf (state->fsubtype, "(LDU2) ");
|
||||
state->numtdulc = 0;
|
||||
processLDU2 (opts, state);
|
||||
}
|
||||
else if (state->lastp25type == 2)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf ("(LDU1) ");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
}
|
||||
state->lastp25type = 0;
|
||||
sprintf (state->fsubtype, "(LDU1) ");
|
||||
state->numtdulc = 0;
|
||||
processLDU1 (opts, state);
|
||||
}
|
||||
else if (state->lastp25type == 3)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf (" (TSDU)\n");
|
||||
}
|
||||
state->lastp25type = 0;
|
||||
sprintf (state->fsubtype, "(TSDU) ");
|
||||
skipDibit (opts, state, 328);
|
||||
}
|
||||
else if (state->lastp25type == 4)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf (" (PDU)\n");
|
||||
}
|
||||
state->lastp25type = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->lastp25type = 0;
|
||||
sprintf (state->fsubtype, " ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
printf (" duid:%s *Unknown DUID*\n", duid);
|
||||
}
|
||||
}
|
||||
}
|
||||
797
dsd_frame_sync.c
797
dsd_frame_sync.c
@@ -1,797 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
printFrameSync (dsd_opts * opts, dsd_state * state, char *frametype, int offset, char *modulation)
|
||||
{
|
||||
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
printf ("Sync: %s ", frametype);
|
||||
}
|
||||
if (opts->verbose > 2)
|
||||
{
|
||||
printf ("o: %4i ", offset);
|
||||
}
|
||||
if (opts->verbose > 1)
|
||||
{
|
||||
printf ("mod: %s ", modulation);
|
||||
}
|
||||
if (opts->verbose > 2)
|
||||
{
|
||||
printf ("g: %f ", state->aout_gain);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
getFrameSync (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
/* detects frame sync and returns frame type
|
||||
* 0 = +P25p1
|
||||
* 1 = -P25p1
|
||||
* 2 = +X2-TDMA (non inverted signal data frame)
|
||||
* 3 = +X2-TDMA (inverted signal voice frame)
|
||||
* 4 = -X2-TDMA (non inverted signal voice frame)
|
||||
* 5 = -X2-TDMA (inverted signal data frame)
|
||||
* 6 = +D-STAR
|
||||
* 7 = -D-STAR
|
||||
* 8 = +NXDN (non inverted voice frame)
|
||||
* 9 = -NXDN (inverted voice frame)
|
||||
* 10 = +DMR (non inverted singlan data frame)
|
||||
* 11 = -DMR (inverted signal voice frame)
|
||||
* 12 = +DMR (non inverted signal voice frame)
|
||||
* 13 = -DMR (inverted signal data frame)
|
||||
* 14 = +ProVoice
|
||||
* 15 = -ProVoice
|
||||
* 16 = +NXDN (non inverted data frame)
|
||||
* 17 = -NXDN (inverted data frame)
|
||||
*/
|
||||
|
||||
int i, j, t, o, dibit, sync, symbol, synctest_pos, lastt;
|
||||
char synctest[25];
|
||||
char synctest18[19];
|
||||
char synctest32[33];
|
||||
char modulation[8];
|
||||
char *synctest_p;
|
||||
char synctest_buf[10240];
|
||||
int lmin, lmax, lidx;
|
||||
int lbuf[24], lbuf2[24];
|
||||
int lsum;
|
||||
char spectrum[64];
|
||||
|
||||
for (i = 18; i < 24; i++)
|
||||
{
|
||||
lbuf[i] = 0;
|
||||
lbuf2[i] = 0;
|
||||
}
|
||||
|
||||
// detect frame sync
|
||||
t = 0;
|
||||
synctest[24] = 0;
|
||||
synctest18[18] = 0;
|
||||
synctest32[32] = 0;
|
||||
synctest_pos = 0;
|
||||
synctest_p = synctest_buf + 10;
|
||||
sync = 0;
|
||||
lmin = 0;
|
||||
lmax = 0;
|
||||
lidx = 0;
|
||||
lastt = 0;
|
||||
state->numflips = 0;
|
||||
if ((opts->symboltiming == 1) && (state->carrier == 1))
|
||||
{
|
||||
printf ("\nSymbol Timing:\n");
|
||||
}
|
||||
while (sync == 0)
|
||||
{
|
||||
t++;
|
||||
symbol = getSymbol (opts, state, 0);
|
||||
lbuf[lidx] = symbol;
|
||||
state->sbuf[state->sidx] = symbol;
|
||||
if (lidx == 23)
|
||||
{
|
||||
lidx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
lidx++;
|
||||
}
|
||||
if (state->sidx == (opts->ssize - 1))
|
||||
{
|
||||
state->sidx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->sidx++;
|
||||
}
|
||||
|
||||
if (lastt == 23)
|
||||
{
|
||||
lastt = 0;
|
||||
if (state->numflips > opts->mod_threshold)
|
||||
{
|
||||
if (opts->mod_qpsk == 1)
|
||||
{
|
||||
state->rf_mod = 1;
|
||||
}
|
||||
}
|
||||
else if (state->numflips > 18)
|
||||
{
|
||||
if (opts->mod_gfsk == 1)
|
||||
{
|
||||
state->rf_mod = 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (opts->mod_c4fm == 1)
|
||||
{
|
||||
state->rf_mod = 0;
|
||||
}
|
||||
}
|
||||
state->numflips = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastt++;
|
||||
}
|
||||
|
||||
if (state->dibit_buf_p > state->dibit_buf + 900000)
|
||||
{
|
||||
state->dibit_buf_p = state->dibit_buf + 200;
|
||||
}
|
||||
|
||||
//determine dibit state
|
||||
if (symbol > 0)
|
||||
{
|
||||
*state->dibit_buf_p = 1;
|
||||
state->dibit_buf_p++;
|
||||
dibit = 49;
|
||||
}
|
||||
else
|
||||
{
|
||||
*state->dibit_buf_p = 3;
|
||||
state->dibit_buf_p++;
|
||||
dibit = 51;
|
||||
}
|
||||
|
||||
*synctest_p = dibit;
|
||||
if (t >= 18)
|
||||
{
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
lbuf2[i] = lbuf[i];
|
||||
}
|
||||
qsort (lbuf2, 24, sizeof (int), comp);
|
||||
lmin = (lbuf2[2] + lbuf2[3] + lbuf2[4]) / 3;
|
||||
lmax = (lbuf2[21] + lbuf2[20] + lbuf2[19]) / 3;
|
||||
|
||||
if (state->rf_mod == 1)
|
||||
{
|
||||
state->minbuf[state->midx] = lmin;
|
||||
state->maxbuf[state->midx] = lmax;
|
||||
if (state->midx == (opts->msize - 1))
|
||||
{
|
||||
state->midx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->midx++;
|
||||
}
|
||||
lsum = 0;
|
||||
for (i = 0; i < opts->msize; i++)
|
||||
{
|
||||
lsum += state->minbuf[i];
|
||||
}
|
||||
state->min = lsum / opts->msize;
|
||||
lsum = 0;
|
||||
for (i = 0; i < opts->msize; i++)
|
||||
{
|
||||
lsum += state->maxbuf[i];
|
||||
}
|
||||
state->max = lsum / opts->msize;
|
||||
state->center = ((state->max) + (state->min)) / 2;
|
||||
state->maxref = ((state->max) * 0.80);
|
||||
state->minref = ((state->min) * 0.80);
|
||||
}
|
||||
else
|
||||
{
|
||||
state->maxref = state->max;
|
||||
state->minref = state->min;
|
||||
}
|
||||
|
||||
if (state->rf_mod == 0)
|
||||
{
|
||||
sprintf (modulation, "C4FM");
|
||||
}
|
||||
else if (state->rf_mod == 1)
|
||||
{
|
||||
sprintf (modulation, "QPSK");
|
||||
}
|
||||
else if (state->rf_mod == 2)
|
||||
{
|
||||
sprintf (modulation, "GFSK");
|
||||
}
|
||||
|
||||
if (opts->datascope == 1)
|
||||
{
|
||||
if (lidx == 0)
|
||||
{
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
spectrum[i] = 0;
|
||||
}
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
o = (lbuf2[i] + 32768) / 1024;
|
||||
spectrum[o]++;
|
||||
}
|
||||
if (state->symbolcnt > (4800 / opts->scoperate))
|
||||
{
|
||||
state->symbolcnt = 0;
|
||||
printf ("\n");
|
||||
printf ("Demod mode: %s Nac: %4X\n", modulation, state->nac);
|
||||
printf ("Frame Type: %s Talkgroup: %7i\n", state->ftype, state->lasttg);
|
||||
printf ("Frame Subtype: %s Source: %12i\n", state->fsubtype, state->lastsrc);
|
||||
printf ("TDMA activity: %s %s Voice errors: %s\n", state->slot0light, state->slot1light, state->err_str);
|
||||
printf ("+----------------------------------------------------------------+\n");
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
printf ("|");
|
||||
for (j = 0; j < 64; j++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
if ((j == ((state->min) + 32768) / 1024) || (j == ((state->max) + 32768) / 1024))
|
||||
{
|
||||
printf ("#");
|
||||
}
|
||||
else if (j == (state->center + 32768) / 1024)
|
||||
{
|
||||
printf ("!");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (j == 32)
|
||||
{
|
||||
printf ("|");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf (" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spectrum[j] > 9 - i)
|
||||
{
|
||||
printf ("*");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (j == 32)
|
||||
{
|
||||
printf ("|");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf (" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
printf ("|\n");
|
||||
}
|
||||
printf ("+----------------------------------------------------------------+\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strncpy (synctest, (synctest_p - 23), 24);
|
||||
if (opts->frame_p25p1 == 1)
|
||||
{
|
||||
if (strcmp (synctest, P25P1_SYNC) == 0)
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, " P25 Phase 1 ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " +P25p1 ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 0;
|
||||
return (0);
|
||||
}
|
||||
if (strcmp (synctest, INV_P25P1_SYNC) == 0)
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, " P25 Phase 1 ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " -P25p1 ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 1;
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
if (opts->frame_x2tdma == 1)
|
||||
{
|
||||
if ((strcmp (synctest, X2TDMA_BS_DATA_SYNC) == 0) || (strcmp (synctest, X2TDMA_MS_DATA_SYNC) == 0))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + (lmax)) / 2;
|
||||
state->min = ((state->min) + (lmin)) / 2;
|
||||
if (opts->inverted_x2tdma == 0)
|
||||
{
|
||||
// data frame
|
||||
sprintf (state->ftype, " X2-TDMA ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " +X2-TDMA ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 2;
|
||||
return (2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// inverted voice frame
|
||||
sprintf (state->ftype, " X2-TDMA ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " -X2-TDMA ", synctest_pos + 1, modulation);
|
||||
}
|
||||
if (state->lastsynctype != 3)
|
||||
{
|
||||
state->firstframe = 1;
|
||||
}
|
||||
state->lastsynctype = 3;
|
||||
return (3);
|
||||
}
|
||||
}
|
||||
if ((strcmp (synctest, X2TDMA_BS_VOICE_SYNC) == 0) || (strcmp (synctest, X2TDMA_MS_VOICE_SYNC) == 0))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
if (opts->inverted_x2tdma == 0)
|
||||
{
|
||||
// voice frame
|
||||
sprintf (state->ftype, " X2-TDMA ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " +X2-TDMA ", synctest_pos + 1, modulation);
|
||||
}
|
||||
if (state->lastsynctype != 4)
|
||||
{
|
||||
state->firstframe = 1;
|
||||
}
|
||||
state->lastsynctype = 4;
|
||||
return (4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// inverted data frame
|
||||
sprintf (state->ftype, " X2-TDMA ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " -X2-TDMA ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 5;
|
||||
return (5);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opts->frame_dmr == 1)
|
||||
{
|
||||
if ((strcmp (synctest, DMR_MS_DATA_SYNC) == 0) || (strcmp (synctest, DMR_BS_DATA_SYNC) == 0))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + (lmax)) / 2;
|
||||
state->min = ((state->min) + (lmin)) / 2;
|
||||
if (opts->inverted_dmr == 0)
|
||||
{
|
||||
// data frame
|
||||
sprintf (state->ftype, " DMR ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " +DMR ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 10;
|
||||
return (10);
|
||||
}
|
||||
else
|
||||
{
|
||||
// inverted voice frame
|
||||
sprintf (state->ftype, " DMR ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " -DMR ", synctest_pos + 1, modulation);
|
||||
}
|
||||
if (state->lastsynctype != 11)
|
||||
{
|
||||
state->firstframe = 1;
|
||||
}
|
||||
state->lastsynctype = 11;
|
||||
return (11);
|
||||
}
|
||||
}
|
||||
if ((strcmp (synctest, DMR_MS_VOICE_SYNC) == 0) || (strcmp (synctest, DMR_BS_VOICE_SYNC) == 0))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
if (opts->inverted_dmr == 0)
|
||||
{
|
||||
// voice frame
|
||||
sprintf (state->ftype, " DMR ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " +DMR ", synctest_pos + 1, modulation);
|
||||
}
|
||||
if (state->lastsynctype != 12)
|
||||
{
|
||||
state->firstframe = 1;
|
||||
}
|
||||
state->lastsynctype = 12;
|
||||
return (12);
|
||||
}
|
||||
else
|
||||
{
|
||||
// inverted data frame
|
||||
sprintf (state->ftype, " DMR ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " -DMR ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 13;
|
||||
return (13);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opts->frame_provoice == 1)
|
||||
{
|
||||
strncpy (synctest32, (synctest_p - 31), 32);
|
||||
if ((strcmp (synctest32, PROVOICE_SYNC) == 0) || (strcmp (synctest32, PROVOICE_EA_SYNC) == 0))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, " ProVoice ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " -ProVoice ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 14;
|
||||
return (14);
|
||||
}
|
||||
else if ((strcmp (synctest32, INV_PROVOICE_SYNC) == 0) || (strcmp (synctest32, INV_PROVOICE_EA_SYNC) == 0))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, " ProVoice ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " -ProVoice ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 15;
|
||||
return (15);
|
||||
}
|
||||
|
||||
}
|
||||
if ((opts->frame_nxdn96 == 1) || (opts->frame_nxdn48 == 1))
|
||||
{
|
||||
strncpy (synctest18, (synctest_p - 17), 18);
|
||||
if ((strcmp (synctest18, NXDN_BS_VOICE_SYNC) == 0) || (strcmp (synctest18, NXDN_MS_VOICE_SYNC) == 0))
|
||||
{
|
||||
if ((state->lastsynctype == 8) || (state->lastsynctype == 16))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
if (state->samplesPerSymbol == 20)
|
||||
{
|
||||
sprintf (state->ftype, " NXDN48 ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " +NXDN48 ", synctest_pos + 1, modulation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (state->ftype, " NXDN96 ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " +NXDN96 ", synctest_pos + 1, modulation);
|
||||
}
|
||||
}
|
||||
state->lastsynctype = 8;
|
||||
return (8);
|
||||
}
|
||||
else
|
||||
{
|
||||
state->lastsynctype = 8;
|
||||
}
|
||||
}
|
||||
else if ((strcmp (synctest18, INV_NXDN_BS_VOICE_SYNC) == 0) || (strcmp (synctest18, INV_NXDN_MS_VOICE_SYNC) == 0))
|
||||
{
|
||||
if ((state->lastsynctype == 9) || (state->lastsynctype == 17))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
if (state->samplesPerSymbol == 20)
|
||||
{
|
||||
sprintf (state->ftype, " NXDN48 ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " -NXDN48 ", synctest_pos + 1, modulation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (state->ftype, " NXDN96 ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " -NXDN96 ", synctest_pos + 1, modulation);
|
||||
}
|
||||
}
|
||||
state->lastsynctype = 9;
|
||||
return (9);
|
||||
}
|
||||
else
|
||||
{
|
||||
state->lastsynctype = 9;
|
||||
}
|
||||
}
|
||||
else if ((strcmp (synctest18, NXDN_BS_DATA_SYNC) == 0) || (strcmp (synctest18, NXDN_MS_DATA_SYNC) == 0))
|
||||
{
|
||||
if ((state->lastsynctype == 8) || (state->lastsynctype == 16))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
if (state->samplesPerSymbol == 20)
|
||||
{
|
||||
sprintf (state->ftype, " NXDN48 ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " +NXDN48 ", synctest_pos + 1, modulation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (state->ftype, " NXDN96 ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " +NXDN96 ", synctest_pos + 1, modulation);
|
||||
}
|
||||
}
|
||||
state->lastsynctype = 16;
|
||||
return (16);
|
||||
}
|
||||
else
|
||||
{
|
||||
state->lastsynctype = 16;
|
||||
}
|
||||
}
|
||||
else if ((strcmp (synctest18, INV_NXDN_BS_DATA_SYNC) == 0) || (strcmp (synctest18, INV_NXDN_MS_DATA_SYNC) == 0))
|
||||
{
|
||||
if ((state->lastsynctype == 9) || (state->lastsynctype == 17))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, " NXDN ");
|
||||
if (state->samplesPerSymbol == 20)
|
||||
{
|
||||
sprintf (state->ftype, " NXDN48 ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " -NXDN48 ", synctest_pos + 1, modulation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (state->ftype, " NXDN96 ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " -NXDN96 ", synctest_pos + 1, modulation);
|
||||
}
|
||||
}
|
||||
state->lastsynctype = 17;
|
||||
return (17);
|
||||
}
|
||||
else
|
||||
{
|
||||
state->lastsynctype = 17;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opts->frame_dstar == 1)
|
||||
{
|
||||
if (strcmp (synctest, DSTAR_SYNC) == 0)
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, " D-STAR ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " +D-STAR ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 6;
|
||||
return (6);
|
||||
}
|
||||
if (strcmp (synctest, INV_DSTAR_SYNC) == 0)
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, " D-STAR ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, " -D-STAR ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = 7;
|
||||
return (7);
|
||||
}
|
||||
}
|
||||
|
||||
if ((t == 24) && (state->lastsynctype != -1))
|
||||
{
|
||||
if ((state->lastsynctype == 0) && ((state->lastp25type == 1) || (state->lastp25type == 2)))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + (lmax)) / 2;
|
||||
state->min = ((state->min) + (lmin)) / 2;
|
||||
sprintf (state->ftype, "(P25 Phase 1)");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "(+P25p1) ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = -1;
|
||||
return (0);
|
||||
}
|
||||
else if ((state->lastsynctype == 1) && ((state->lastp25type == 1) || (state->lastp25type == 2)))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, "(P25 Phase 1)");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "(-P25p1) ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = -1;
|
||||
return (1);
|
||||
}
|
||||
else if ((state->lastsynctype == 3) && ((strcmp (synctest, X2TDMA_BS_VOICE_SYNC) != 0) || (strcmp (synctest, X2TDMA_MS_VOICE_SYNC) != 0)))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, "(X2-TDMA) ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "(-X2-TDMA) ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = -1;
|
||||
return (3);
|
||||
}
|
||||
else if ((state->lastsynctype == 4) && ((strcmp (synctest, X2TDMA_BS_DATA_SYNC) != 0) || (strcmp (synctest, X2TDMA_MS_DATA_SYNC) != 0)))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, "(X2-TDMA) ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "(+X2-TDMA) ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = -1;
|
||||
return (4);
|
||||
}
|
||||
else if ((state->lastsynctype == 11) && ((strcmp (synctest, DMR_BS_VOICE_SYNC) != 0) || (strcmp (synctest, DMR_MS_VOICE_SYNC) != 0)))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, "(DMR) ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "(-DMR) ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = -1;
|
||||
return (11);
|
||||
}
|
||||
else if ((state->lastsynctype == 12) && ((strcmp (synctest, DMR_BS_DATA_SYNC) != 0) || (strcmp (synctest, DMR_MS_DATA_SYNC) != 0)))
|
||||
{
|
||||
state->carrier = 1;
|
||||
state->offset = synctest_pos;
|
||||
state->max = ((state->max) + lmax) / 2;
|
||||
state->min = ((state->min) + lmin) / 2;
|
||||
sprintf (state->ftype, "(DMR) ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameSync (opts, state, "(+DMR) ", synctest_pos + 1, modulation);
|
||||
}
|
||||
state->lastsynctype = -1;
|
||||
return (12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exitflag == 1)
|
||||
{
|
||||
cleanupAndExit (opts, state);
|
||||
}
|
||||
|
||||
if (synctest_pos < 10200)
|
||||
{
|
||||
synctest_pos++;
|
||||
synctest_p++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// buffer reset
|
||||
synctest_pos = 0;
|
||||
synctest_p = synctest_buf;
|
||||
noCarrier (opts, state);
|
||||
}
|
||||
|
||||
if (state->lastsynctype != 1)
|
||||
{
|
||||
if (synctest_pos >= 1800)
|
||||
{
|
||||
if ((opts->errorbars == 1) && (opts->verbose > 1) && (state->carrier == 1))
|
||||
{
|
||||
printf ("Sync: no sync\n");
|
||||
}
|
||||
noCarrier (opts, state);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
698
dsd_main.c
698
dsd_main.c
@@ -1,698 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#define _MAIN
|
||||
|
||||
#include "dsd.h"
|
||||
#include "p25p1_const.h"
|
||||
#include "x2tdma_const.h"
|
||||
#include "dstar_const.h"
|
||||
#include "nxdn_const.h"
|
||||
#include "dmr_const.h"
|
||||
#include "provoice_const.h"
|
||||
|
||||
int
|
||||
comp (const void *a, const void *b)
|
||||
{
|
||||
if (*((const int *) a) == *((const int *) b))
|
||||
return 0;
|
||||
else if (*((const int *) a) < *((const int *) b))
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
noCarrier (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
state->dibit_buf_p = state->dibit_buf + 200;
|
||||
memset (state->dibit_buf, 0, sizeof (int) * 200);
|
||||
if (opts->mbe_out_f != NULL)
|
||||
{
|
||||
closeMbeOutFile (opts, state);
|
||||
}
|
||||
state->jitter = -1;
|
||||
state->lastsynctype = -1;
|
||||
state->carrier = 0;
|
||||
state->max = 15000;
|
||||
state->min = -15000;
|
||||
state->center = 0;
|
||||
state->err_str[0] = 0;
|
||||
sprintf (state->fsubtype, " ");
|
||||
sprintf (state->ftype, " ");
|
||||
state->errs = 0;
|
||||
state->errs2 = 0;
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lastp25type = 0;
|
||||
state->repeat = 0;
|
||||
state->nac = 0;
|
||||
state->numtdulc = 0;
|
||||
sprintf (state->slot0light, " slot0 ");
|
||||
sprintf (state->slot1light, " slot1 ");
|
||||
state->firstframe = 0;
|
||||
if (opts->audio_gain == (float) 0)
|
||||
{
|
||||
state->aout_gain = 25;
|
||||
}
|
||||
memset (state->aout_max_buf, 0, sizeof (float) * 200);
|
||||
state->aout_max_buf_p = state->aout_max_buf;
|
||||
state->aout_max_buf_idx = 0;
|
||||
sprintf (state->algid, "________");
|
||||
sprintf (state->keyid, "________________");
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
}
|
||||
|
||||
void
|
||||
initOpts (dsd_opts * opts)
|
||||
{
|
||||
|
||||
opts->onesymbol = 10;
|
||||
opts->mbe_in_file[0] = 0;
|
||||
opts->mbe_in_f = NULL;
|
||||
opts->errorbars = 1;
|
||||
opts->datascope = 0;
|
||||
opts->symboltiming = 0;
|
||||
opts->verbose = 2;
|
||||
opts->p25enc = 0;
|
||||
opts->p25lc = 0;
|
||||
opts->p25status = 0;
|
||||
opts->p25tg = 0;
|
||||
opts->scoperate = 15;
|
||||
sprintf (opts->audio_in_dev, "/dev/audio");
|
||||
opts->audio_in_fd = -1;
|
||||
sprintf (opts->audio_out_dev, "/dev/audio");
|
||||
opts->audio_out_fd = -1;
|
||||
opts->split = 0;
|
||||
opts->playoffset = 0;
|
||||
opts->mbe_out_dir[0] = 0;
|
||||
opts->mbe_out_file[0] = 0;
|
||||
opts->mbe_out_f = NULL;
|
||||
opts->audio_gain = 0;
|
||||
opts->audio_out = 1;
|
||||
opts->wav_out_file[0] = 0;
|
||||
opts->wav_out_f = NULL;
|
||||
opts->wav_out_fd = -1;
|
||||
opts->serial_baud = 115200;
|
||||
sprintf (opts->serial_dev, "/dev/ttyUSB0");
|
||||
opts->resume = 0;
|
||||
opts->frame_dstar = 0;
|
||||
opts->frame_x2tdma = 1;
|
||||
opts->frame_p25p1 = 1;
|
||||
opts->frame_nxdn48 = 0;
|
||||
opts->frame_nxdn96 = 1;
|
||||
opts->frame_dmr = 1;
|
||||
opts->frame_provoice = 0;
|
||||
opts->mod_c4fm = 1;
|
||||
opts->mod_qpsk = 1;
|
||||
opts->mod_gfsk = 1;
|
||||
opts->uvquality = 3;
|
||||
opts->inverted_x2tdma = 1; // most transmitter + scanner + sound card combinations show inverted signals for this
|
||||
opts->inverted_dmr = 0; // most transmitter + scanner + sound card combinations show non-inverted signals for this
|
||||
opts->mod_threshold = 26;
|
||||
opts->ssize = 36;
|
||||
opts->msize = 15;
|
||||
opts->playfiles = 0;
|
||||
opts->delay = 0;
|
||||
}
|
||||
|
||||
void
|
||||
initState (dsd_state * state)
|
||||
{
|
||||
|
||||
int i, j;
|
||||
|
||||
state->dibit_buf = malloc (sizeof (int) * 1000000);
|
||||
state->dibit_buf_p = state->dibit_buf + 200;
|
||||
memset (state->dibit_buf, 0, sizeof (int) * 200);
|
||||
state->repeat = 0;
|
||||
state->audio_out_buf = malloc (sizeof (short) * 1000000);
|
||||
memset (state->audio_out_buf, 0, 100 * sizeof (short));
|
||||
state->audio_out_buf_p = state->audio_out_buf + 100;
|
||||
state->audio_out_float_buf = malloc (sizeof (float) * 1000000);
|
||||
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
|
||||
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
|
||||
state->audio_out_idx = 0;
|
||||
state->audio_out_idx2 = 0;
|
||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||
state->wav_out_bytes = 0;
|
||||
state->center = 0;
|
||||
state->jitter = -1;
|
||||
state->synctype = -1;
|
||||
state->min = -15000;
|
||||
state->max = 15000;
|
||||
state->lmid = 0;
|
||||
state->umid = 0;
|
||||
state->minref = -12000;
|
||||
state->maxref = 12000;
|
||||
state->lastsample = 0;
|
||||
for (i = 0; i < 128; i++)
|
||||
{
|
||||
state->sbuf[i] = 0;
|
||||
}
|
||||
state->sidx = 0;
|
||||
for (i = 0; i < 1024; i++)
|
||||
{
|
||||
state->maxbuf[i] = 15000;
|
||||
}
|
||||
for (i = 0; i < 1024; i++)
|
||||
{
|
||||
state->minbuf[i] = -15000;
|
||||
}
|
||||
state->midx = 0;
|
||||
state->err_str[0] = 0;
|
||||
sprintf (state->fsubtype, " ");
|
||||
sprintf (state->ftype, " ");
|
||||
state->symbolcnt = 0;
|
||||
state->rf_mod = 0;
|
||||
state->numflips = 0;
|
||||
state->lastsynctype = -1;
|
||||
state->lastp25type = 0;
|
||||
state->offset = 0;
|
||||
state->carrier = 0;
|
||||
for (i = 0; i < 25; i++)
|
||||
{
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
state->tg[i][j] = 48;
|
||||
}
|
||||
}
|
||||
state->tgcount = 0;
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
state->nac = 0;
|
||||
state->errs = 0;
|
||||
state->errs2 = 0;
|
||||
state->mbe_file_type = -1;
|
||||
state->optind = 0;
|
||||
state->numtdulc = 0;
|
||||
state->firstframe = 0;
|
||||
sprintf (state->slot0light, " slot0 ");
|
||||
sprintf (state->slot1light, " slot1 ");
|
||||
state->aout_gain = 25;
|
||||
memset (state->aout_max_buf, 0, sizeof (float) * 200);
|
||||
state->aout_max_buf_p = state->aout_max_buf;
|
||||
state->aout_max_buf_idx = 0;
|
||||
state->samplesPerSymbol = 10;
|
||||
state->symbolCenter = 4;
|
||||
sprintf (state->algid, "________");
|
||||
sprintf (state->keyid, "________________");
|
||||
state->currentslot = 0;
|
||||
state->cur_mp = malloc (sizeof (mbe_parms));
|
||||
state->prev_mp = malloc (sizeof (mbe_parms));
|
||||
state->prev_mp_enhanced = malloc (sizeof (mbe_parms));
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
state->p25kid = 0;
|
||||
}
|
||||
|
||||
void
|
||||
usage ()
|
||||
{
|
||||
printf ("\n");
|
||||
printf ("Usage:\n");
|
||||
printf (" dsd [options] Live scanner mode\n");
|
||||
printf (" dsd [options] -r <files> Read/Play saved mbe data from file(s)\n");
|
||||
printf (" dsd -h Show help\n");
|
||||
printf ("\n");
|
||||
printf ("Display Options:\n");
|
||||
printf (" -e Show Frame Info and errorbars (default)\n");
|
||||
printf (" -pe Show P25 encryption sync bits\n");
|
||||
printf (" -pl Show P25 link control bits\n");
|
||||
printf (" -ps Show P25 status bits and low speed data\n");
|
||||
printf (" -pt Show P25 talkgroup info\n");
|
||||
printf (" -q Don't show Frame Info/errorbars\n");
|
||||
printf (" -s Datascope (disables other display options)\n");
|
||||
printf (" -t Show symbol timing during sync\n");
|
||||
printf (" -v <num> Frame information Verbosity\n");
|
||||
printf (" -z <num> Frame rate for datascope\n");
|
||||
printf ("\n");
|
||||
printf ("Input/Output options:\n");
|
||||
printf (" -i <device> Audio input device (default is /dev/audio)\n");
|
||||
printf (" -o <device> Audio output device (default is /dev/audio)\n");
|
||||
printf (" -d <dir> Create mbe data files, use this directory\n");
|
||||
printf (" -g <num> Audio output gain (default = 0 = auto)\n");
|
||||
printf (" -n Do not send synthesized speech to audio output device\n");
|
||||
printf (" -w <file> Output synthesized speech to a .wav file\n");
|
||||
printf ("\n");
|
||||
printf ("Scanner control options:\n");
|
||||
printf (" -B <num> Serial port baud rate (default=115200)\n");
|
||||
printf (" -C <device> Serial port for scanner control (default=/dev/ttyUSB0)\n");
|
||||
printf (" -R <num> Resume scan after <num> TDULC frames or any PDU or TSDU\n");
|
||||
printf ("\n");
|
||||
printf ("Decoder options:\n");
|
||||
printf (" -fa Auto-detect frame type (default)\n");
|
||||
printf (" -f1 Decode only P25 Phase 1\n");
|
||||
printf (" -fd Decode only D-STAR* (no audio)\n");
|
||||
printf (" -fi Decode only NXDN48* (6.25 kHz) / IDAS*\n");
|
||||
printf (" -fn Decode only NXDN96 (12.5 kHz)\n");
|
||||
printf (" -fp Decode only ProVoice*\n");
|
||||
printf (" -fr Decode only DMR/MOTOTRBO\n");
|
||||
printf (" -fx Decode only X2-TDMA\n");
|
||||
printf (" -ma Auto-select modulation optimizations (default)\n");
|
||||
printf (" -mc Use only C4FM modulation optimizations\n");
|
||||
printf (" -mg Use only GFSK modulation optimizations\n");
|
||||
printf (" -mq Use only QPSK modulation optimizations\n");
|
||||
printf (" -u <num> Unvoiced speech quality (default=3)\n");
|
||||
printf (" -xx Expect non-inverted X2-TDMA signal\n");
|
||||
printf (" -xr Expect inverted DMR/MOTOTRBO signal\n");
|
||||
printf ("\n");
|
||||
printf (" * denotes frame types that cannot be auto-detected.\n");
|
||||
printf ("\n");
|
||||
printf ("Advanced decoder options:\n");
|
||||
printf (" -A <num> QPSK modulation auto detection threshold (default=26)\n");
|
||||
printf (" -S <num> Symbol buffer size for QPSK decision point tracking\n");
|
||||
printf (" (default=36)\n");
|
||||
printf (" -M <num> Min/Max buffer size for QPSK decision point tracking\n");
|
||||
printf (" (default=15)\n");
|
||||
exit (0);
|
||||
}
|
||||
|
||||
void
|
||||
liveScanner (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
noCarrier (opts, state);
|
||||
state->synctype = getFrameSync (opts, state);
|
||||
// recalibrate center/umid/lmid
|
||||
state->center = ((state->max) + (state->min)) / 2;
|
||||
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
|
||||
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
|
||||
while (state->synctype != -1)
|
||||
{
|
||||
processFrame (opts, state);
|
||||
state->synctype = getFrameSync (opts, state);
|
||||
// recalibrate center/umid/lmid
|
||||
state->center = ((state->max) + (state->min)) / 2;
|
||||
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
|
||||
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cleanupAndExit (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
noCarrier (opts, state);
|
||||
if (opts->wav_out_f != NULL)
|
||||
{
|
||||
closeWavOutFile (opts, state);
|
||||
}
|
||||
printf ("Exiting.\n");
|
||||
exit (0);
|
||||
}
|
||||
|
||||
void
|
||||
sigfun (int sig)
|
||||
{
|
||||
exitflag = 1;
|
||||
signal (SIGINT, SIG_DFL);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
|
||||
int c;
|
||||
extern char *optarg;
|
||||
extern int optind, opterr, optopt;
|
||||
dsd_opts opts;
|
||||
dsd_state state;
|
||||
char versionstr[25];
|
||||
mbe_printVersion (versionstr);
|
||||
|
||||
printf ("Digital Speech Decoder 1.6.0\n");
|
||||
printf ("mbelib version %s\n", versionstr);
|
||||
|
||||
initOpts (&opts);
|
||||
initState (&state);
|
||||
|
||||
exitflag = 0;
|
||||
signal (SIGINT, sigfun);
|
||||
|
||||
while ((c = getopt (argc, argv, "hep:qstv:z:i:o:d:g:nw:B:C:R:f:m:u:x:A:S:M:r")) != -1)
|
||||
{
|
||||
opterr = 0;
|
||||
switch (c)
|
||||
{
|
||||
case 'h':
|
||||
usage ();
|
||||
exit (0);
|
||||
case 'e':
|
||||
opts.errorbars = 1;
|
||||
opts.datascope = 0;
|
||||
break;
|
||||
case 'p':
|
||||
if (optarg[0] == 'e')
|
||||
{
|
||||
opts.p25enc = 1;
|
||||
}
|
||||
else if (optarg[0] == 'l')
|
||||
{
|
||||
opts.p25lc = 1;
|
||||
}
|
||||
else if (optarg[0] == 's')
|
||||
{
|
||||
opts.p25status = 1;
|
||||
}
|
||||
else if (optarg[0] == 't')
|
||||
{
|
||||
opts.p25tg = 1;
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
opts.errorbars = 0;
|
||||
opts.verbose = 0;
|
||||
break;
|
||||
case 's':
|
||||
opts.errorbars = 0;
|
||||
opts.p25enc = 0;
|
||||
opts.p25lc = 0;
|
||||
opts.p25status = 0;
|
||||
opts.p25tg = 0;
|
||||
opts.datascope = 1;
|
||||
opts.symboltiming = 0;
|
||||
break;
|
||||
case 't':
|
||||
opts.symboltiming = 1;
|
||||
opts.errorbars = 1;
|
||||
opts.datascope = 0;
|
||||
break;
|
||||
case 'v':
|
||||
sscanf (optarg, "%d", &opts.verbose);
|
||||
break;
|
||||
case 'z':
|
||||
sscanf (optarg, "%d", &opts.scoperate);
|
||||
opts.errorbars = 0;
|
||||
opts.p25enc = 0;
|
||||
opts.p25lc = 0;
|
||||
opts.p25status = 0;
|
||||
opts.p25tg = 0;
|
||||
opts.datascope = 1;
|
||||
opts.symboltiming = 0;
|
||||
printf ("Setting datascope frame rate to %i frame per second.\n", opts.scoperate);
|
||||
break;
|
||||
case 'i':
|
||||
sscanf (optarg, "%s", opts.audio_in_dev);
|
||||
break;
|
||||
case 'o':
|
||||
sscanf (optarg, "%s", opts.audio_out_dev);
|
||||
break;
|
||||
case 'd':
|
||||
sscanf (optarg, "%s", opts.mbe_out_dir);
|
||||
printf ("Writing mbe data files to directory %s\n", opts.mbe_out_dir);
|
||||
break;
|
||||
case 'g':
|
||||
sscanf (optarg, "%f", &opts.audio_gain);
|
||||
if (opts.audio_gain <= (float) 0)
|
||||
{
|
||||
opts.audio_gain = (float) 0;
|
||||
printf ("Enabling audio out auto-gain\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("Setting audio out gain to %f\n", opts.audio_gain);
|
||||
state.aout_gain = opts.audio_gain;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
opts.audio_out = 0;
|
||||
printf ("Disabling audio output to soundcard.\n");
|
||||
break;
|
||||
case 'w':
|
||||
sscanf (optarg, "%s", opts.wav_out_file);
|
||||
printf ("Writing audio to file %s\n", opts.wav_out_file);
|
||||
openWavOutFile (&opts, &state);
|
||||
break;
|
||||
case 'B':
|
||||
sscanf (optarg, "%d", &opts.serial_baud);
|
||||
break;
|
||||
case 'C':
|
||||
sscanf (optarg, "%s", opts.serial_dev);
|
||||
break;
|
||||
case 'R':
|
||||
sscanf (optarg, "%d", &opts.resume);
|
||||
printf ("Enabling scan resume after %i TDULC frames\n", opts.resume);
|
||||
break;
|
||||
case 'f':
|
||||
if (optarg[0] == 'a')
|
||||
{
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 1;
|
||||
opts.frame_p25p1 = 1;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 1;
|
||||
opts.frame_dmr = 1;
|
||||
opts.frame_provoice = 0;
|
||||
}
|
||||
else if (optarg[0] == 'd')
|
||||
{
|
||||
opts.frame_dstar = 1;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 0;
|
||||
opts.frame_provoice = 0;
|
||||
printf ("Decoding only D-STAR frames.\n");
|
||||
}
|
||||
else if (optarg[0] == 'x')
|
||||
{
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 1;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 0;
|
||||
opts.frame_provoice = 0;
|
||||
printf ("Decoding only X2-TDMA frames.\n");
|
||||
}
|
||||
else if (optarg[0] == 'p')
|
||||
{
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 0;
|
||||
opts.frame_provoice = 1;
|
||||
state.samplesPerSymbol = 5;
|
||||
state.symbolCenter = 2;
|
||||
opts.mod_c4fm = 0;
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 1;
|
||||
state.rf_mod = 2;
|
||||
printf ("Setting symbol rate to 9600 / second\n");
|
||||
printf ("Enabling only GFSK modulation optimizations.\n");
|
||||
printf ("Decoding only ProVoice frames.\n");
|
||||
}
|
||||
else if (optarg[0] == '1')
|
||||
{
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 1;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 0;
|
||||
opts.frame_provoice = 0;
|
||||
printf ("Decoding only P25 Phase 1 frames.\n");
|
||||
}
|
||||
else if (optarg[0] == 'i')
|
||||
{
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_nxdn48 = 1;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 0;
|
||||
opts.frame_provoice = 0;
|
||||
state.samplesPerSymbol = 20;
|
||||
state.symbolCenter = 10;
|
||||
opts.mod_c4fm = 0;
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 1;
|
||||
state.rf_mod = 2;
|
||||
printf ("Setting symbol rate to 2400 / second\n");
|
||||
printf ("Enabling only GFSK modulation optimizations.\n");
|
||||
printf ("Decoding only NXDN 4800 baud frames.\n");
|
||||
}
|
||||
else if (optarg[0] == 'n')
|
||||
{
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 1;
|
||||
opts.frame_dmr = 0;
|
||||
opts.frame_provoice = 0;
|
||||
opts.mod_c4fm = 0;
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 1;
|
||||
state.rf_mod = 2;
|
||||
printf ("Enabling only GFSK modulation optimizations.\n");
|
||||
printf ("Decoding only NXDN 9600 baud frames.\n");
|
||||
}
|
||||
else if (optarg[0] == 'r')
|
||||
{
|
||||
opts.frame_dstar = 0;
|
||||
opts.frame_x2tdma = 0;
|
||||
opts.frame_p25p1 = 0;
|
||||
opts.frame_nxdn48 = 0;
|
||||
opts.frame_nxdn96 = 0;
|
||||
opts.frame_dmr = 1;
|
||||
opts.frame_provoice = 0;
|
||||
printf ("Decoding only DMR/MOTOTRBO frames.\n");
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (optarg[0] == 'a')
|
||||
{
|
||||
opts.mod_c4fm = 1;
|
||||
opts.mod_qpsk = 1;
|
||||
opts.mod_gfsk = 1;
|
||||
state.rf_mod = 0;
|
||||
}
|
||||
else if (optarg[0] == 'c')
|
||||
{
|
||||
opts.mod_c4fm = 1;
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 0;
|
||||
state.rf_mod = 0;
|
||||
printf ("Enabling only C4FM modulation optimizations.\n");
|
||||
}
|
||||
else if (optarg[0] == 'g')
|
||||
{
|
||||
opts.mod_c4fm = 0;
|
||||
opts.mod_qpsk = 0;
|
||||
opts.mod_gfsk = 1;
|
||||
state.rf_mod = 2;
|
||||
printf ("Enabling only GFSK modulation optimizations.\n");
|
||||
}
|
||||
else if (optarg[0] == 'q')
|
||||
{
|
||||
opts.mod_c4fm = 0;
|
||||
opts.mod_qpsk = 1;
|
||||
opts.mod_gfsk = 0;
|
||||
state.rf_mod = 1;
|
||||
printf ("Enabling only QPSK modulation optimizations.\n");
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
sscanf (optarg, "%i", &opts.uvquality);
|
||||
if (opts.uvquality < 1)
|
||||
{
|
||||
opts.uvquality = 1;
|
||||
}
|
||||
else if (opts.uvquality > 64)
|
||||
{
|
||||
opts.uvquality = 64;
|
||||
}
|
||||
printf ("Setting unvoice speech quality to %i waves per band.\n", opts.uvquality);
|
||||
break;
|
||||
case 'x':
|
||||
if (optarg[0] == 'x')
|
||||
{
|
||||
opts.inverted_x2tdma = 0;
|
||||
printf ("Expecting non-inverted X2-TDMA signals.\n");
|
||||
}
|
||||
else if (optarg[0] == 'r')
|
||||
{
|
||||
opts.inverted_dmr = 1;
|
||||
printf ("Expecting inverted DMR/MOTOTRBO signals.\n");
|
||||
}
|
||||
break;
|
||||
case 'A':
|
||||
sscanf (optarg, "%i", &opts.mod_threshold);
|
||||
printf ("Setting C4FM/QPSK auto detection threshold to %i\n", opts.mod_threshold);
|
||||
break;
|
||||
case 'S':
|
||||
sscanf (optarg, "%i", &opts.ssize);
|
||||
if (opts.ssize > 128)
|
||||
{
|
||||
opts.ssize = 128;
|
||||
}
|
||||
else if (opts.ssize < 1)
|
||||
{
|
||||
opts.ssize = 1;
|
||||
}
|
||||
printf ("Setting QPSK symbol buffer to %i\n", opts.ssize);
|
||||
break;
|
||||
case 'M':
|
||||
sscanf (optarg, "%i", &opts.msize);
|
||||
if (opts.msize > 1024)
|
||||
{
|
||||
opts.msize = 1024;
|
||||
}
|
||||
else if (opts.msize < 1)
|
||||
{
|
||||
opts.msize = 1;
|
||||
}
|
||||
printf ("Setting QPSK Min/Max buffer to %i\n", opts.msize);
|
||||
break;
|
||||
case 'r':
|
||||
opts.playfiles = 1;
|
||||
opts.errorbars = 0;
|
||||
opts.datascope = 0;
|
||||
state.optind = optind;
|
||||
break;
|
||||
default:
|
||||
usage ();
|
||||
exit (0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (opts.resume > 0)
|
||||
{
|
||||
openSerial (&opts, &state);
|
||||
}
|
||||
|
||||
if (opts.playfiles == 1)
|
||||
{
|
||||
opts.split = 1;
|
||||
opts.playoffset = 0;
|
||||
opts.delay = 0;
|
||||
openAudioOutDevice (&opts, 8000);
|
||||
}
|
||||
else if (strcmp (opts.audio_in_dev, opts.audio_out_dev) != 0)
|
||||
{
|
||||
opts.split = 1;
|
||||
opts.playoffset = 0;
|
||||
opts.delay = 0;
|
||||
openAudioOutDevice (&opts, 8000);
|
||||
openAudioInDevice (&opts);
|
||||
}
|
||||
else
|
||||
{
|
||||
opts.split = 0;
|
||||
opts.playoffset = 25; // 38
|
||||
opts.delay = 0;
|
||||
openAudioInDevice (&opts);
|
||||
opts.audio_out_fd = opts.audio_in_fd;
|
||||
}
|
||||
|
||||
if (opts.playfiles == 1)
|
||||
{
|
||||
playMbeFiles (&opts, &state, argc, argv);
|
||||
}
|
||||
else
|
||||
{
|
||||
liveScanner (&opts, &state);
|
||||
}
|
||||
cleanupAndExit (&opts, &state);
|
||||
return (0);
|
||||
}
|
||||
126
dsd_mbe.c
126
dsd_mbe.c
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
|
||||
{
|
||||
|
||||
int i;
|
||||
char imbe_d[88];
|
||||
char ambe_d[49];
|
||||
|
||||
for (i = state->optind; i < argc; i++)
|
||||
{
|
||||
sprintf (opts->mbe_in_file, "%s", argv[i]);
|
||||
openMbeInFile (opts, state);
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
printf ("playing %s\n", opts->mbe_in_file);
|
||||
while (feof (opts->mbe_in_f) == 0)
|
||||
{
|
||||
if (state->mbe_file_type == 0)
|
||||
{
|
||||
readImbe4400Data (opts, state, imbe_d);
|
||||
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
processAudio (opts, state);
|
||||
if (opts->wav_out_fd != -1)
|
||||
{
|
||||
writeSynthesizedVoice (opts, state);
|
||||
}
|
||||
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
playSynthesizedVoice (opts, state);
|
||||
}
|
||||
}
|
||||
else if (state->mbe_file_type == 1)
|
||||
{
|
||||
readAmbe2250Data (opts, state, ambe_d);
|
||||
mbe_processAmbe2250Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
processAudio (opts, state);
|
||||
if (opts->wav_out_fd != -1)
|
||||
{
|
||||
writeSynthesizedVoice (opts, state);
|
||||
}
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
playSynthesizedVoice (opts, state);
|
||||
}
|
||||
}
|
||||
if (exitflag == 1)
|
||||
{
|
||||
cleanupAndExit (opts, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24])
|
||||
{
|
||||
|
||||
int i;
|
||||
char imbe_d[88];
|
||||
char ambe_d[49];
|
||||
|
||||
for (i = 0; i < 88; i++)
|
||||
{
|
||||
imbe_d[i] = 0;
|
||||
}
|
||||
|
||||
if ((state->synctype == 0) || (state->synctype == 1))
|
||||
{
|
||||
mbe_processImbe7200x4400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe_fr, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
if (opts->mbe_out_f != NULL)
|
||||
{
|
||||
saveImbe4400Data (opts, state, imbe_d);
|
||||
}
|
||||
}
|
||||
else if ((state->synctype == 14) || (state->synctype == 15))
|
||||
{
|
||||
mbe_processImbe7100x4400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe7100_fr, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
if (opts->mbe_out_f != NULL)
|
||||
{
|
||||
saveImbe4400Data (opts, state, imbe_d);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mbe_processAmbe3600x2250Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
if (opts->mbe_out_f != NULL)
|
||||
{
|
||||
saveAmbe2250Data (opts, state, ambe_d);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("%s", state->err_str);
|
||||
}
|
||||
|
||||
processAudio (opts, state);
|
||||
if (opts->wav_out_fd != -1)
|
||||
{
|
||||
writeSynthesizedVoice (opts, state);
|
||||
}
|
||||
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
playSynthesizedVoice (opts, state);
|
||||
}
|
||||
}
|
||||
238
dsd_symbol.c
238
dsd_symbol.c
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
int
|
||||
getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
||||
{
|
||||
|
||||
short sample;
|
||||
int i, sum, symbol, count;
|
||||
ssize_t result;
|
||||
|
||||
sum = 0;
|
||||
count = 0;
|
||||
for (i = 0; i < state->samplesPerSymbol; i++)
|
||||
{
|
||||
// timing control
|
||||
if ((i == 0) && (have_sync == 0))
|
||||
{
|
||||
if (state->samplesPerSymbol == 20)
|
||||
{
|
||||
if ((state->jitter >= 7) && (state->jitter <= 10))
|
||||
{
|
||||
i--;
|
||||
}
|
||||
else if ((state->jitter >= 11) && (state->jitter <= 14))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (state->rf_mod == 1)
|
||||
{
|
||||
if ((state->jitter >= 0) && (state->jitter < state->symbolCenter))
|
||||
{
|
||||
i++; // fall back
|
||||
}
|
||||
else if ((state->jitter > state->symbolCenter) && (state->jitter < 10))
|
||||
{
|
||||
i--; // catch up
|
||||
}
|
||||
}
|
||||
else if (state->rf_mod == 2)
|
||||
{
|
||||
if ((state->jitter >= state->symbolCenter - 1) && (state->jitter <= state->symbolCenter))
|
||||
{
|
||||
i--;
|
||||
}
|
||||
else if ((state->jitter >= state->symbolCenter + 1) && (state->jitter <= state->symbolCenter + 2))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (state->rf_mod == 0)
|
||||
{
|
||||
if ((state->jitter > 0) && (state->jitter <= state->symbolCenter))
|
||||
{
|
||||
i--; // catch up
|
||||
}
|
||||
else if ((state->jitter > state->symbolCenter) && (state->jitter < state->samplesPerSymbol))
|
||||
{
|
||||
i++; // fall back
|
||||
}
|
||||
}
|
||||
state->jitter = -1;
|
||||
}
|
||||
|
||||
result = read (opts->audio_in_fd, &sample, 2);
|
||||
{
|
||||
#define NZEROS 60
|
||||
#define GAIN 7.423339364e+00
|
||||
|
||||
static float xv[NZEROS+1];
|
||||
|
||||
static float xcoeffs[] =
|
||||
{ -0.0083649323, -0.0265444850, -0.0428141462, -0.0537571943,
|
||||
-0.0564141052, -0.0489161045, -0.0310068662, -0.0043393881,
|
||||
+0.0275375106, +0.0595423283, +0.0857543325, +0.1003565948,
|
||||
+0.0986944931, +0.0782804830, +0.0395670487, -0.0136691535,
|
||||
-0.0744390415, -0.1331834575, -0.1788967208, -0.2005995448,
|
||||
-0.1889627181, -0.1378439993, -0.0454976231, +0.0847488694,
|
||||
+0.2444859269, +0.4209222342, +0.5982295474, +0.7593684540,
|
||||
+0.8881539892, +0.9712773915, +0.9999999166, +0.9712773915,
|
||||
+0.8881539892, +0.7593684540, +0.5982295474, +0.4209222342,
|
||||
+0.2444859269, +0.0847488694, -0.0454976231, -0.1378439993,
|
||||
-0.1889627181, -0.2005995448, -0.1788967208, -0.1331834575,
|
||||
-0.0744390415, -0.0136691535, +0.0395670487, +0.0782804830,
|
||||
+0.0986944931, +0.1003565948, +0.0857543325, +0.0595423283,
|
||||
+0.0275375106, -0.0043393881, -0.0310068662, -0.0489161045,
|
||||
-0.0564141052, -0.0537571943, -0.0428141462, -0.0265444850,
|
||||
-0.0083649323,
|
||||
};
|
||||
|
||||
float sum; int i;
|
||||
|
||||
for (i = 0; i < NZEROS; i++)
|
||||
xv[i] = xv[i+1];
|
||||
|
||||
xv[NZEROS] = sample; // unfiltered sample in
|
||||
sum = 0.0;
|
||||
|
||||
for (i = 0; i <= NZEROS; i++)
|
||||
sum += (xcoeffs[i] * xv[i]);
|
||||
|
||||
sample = sum / GAIN; // filtered sample out
|
||||
}
|
||||
if ((sample > state->max) && (have_sync == 1) && (state->rf_mod == 0))
|
||||
{
|
||||
sample = state->max;
|
||||
}
|
||||
else if ((sample < state->min) && (have_sync == 1) && (state->rf_mod == 0))
|
||||
{
|
||||
sample = state->min;
|
||||
}
|
||||
|
||||
if (sample > state->center)
|
||||
{
|
||||
if (state->lastsample < state->center)
|
||||
{
|
||||
state->numflips += 1;
|
||||
}
|
||||
if (sample > (state->maxref * 1.25))
|
||||
{
|
||||
if (state->lastsample < (state->maxref * 1.25))
|
||||
{
|
||||
state->numflips += 1;
|
||||
}
|
||||
if ((state->jitter < 0) && (state->rf_mod == 1))
|
||||
{ // first spike out of place
|
||||
state->jitter = i;
|
||||
}
|
||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
||||
{
|
||||
printf ("O");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
||||
{
|
||||
printf ("+");
|
||||
}
|
||||
if ((state->jitter < 0) && (state->lastsample < state->center) && (state->rf_mod != 1))
|
||||
{ // first transition edge
|
||||
state->jitter = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // sample < 0
|
||||
if (state->lastsample > state->center)
|
||||
{
|
||||
state->numflips += 1;
|
||||
}
|
||||
if (sample < (state->minref * 1.25))
|
||||
{
|
||||
if (state->lastsample > (state->minref * 1.25))
|
||||
{
|
||||
state->numflips += 1;
|
||||
}
|
||||
if ((state->jitter < 0) && (state->rf_mod == 1))
|
||||
{ // first spike out of place
|
||||
state->jitter = i;
|
||||
}
|
||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
||||
{
|
||||
printf ("X");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
||||
{
|
||||
printf ("-");
|
||||
}
|
||||
if ((state->jitter < 0) && (state->lastsample > state->center) && (state->rf_mod != 1))
|
||||
{ // first transition edge
|
||||
state->jitter = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state->samplesPerSymbol == 20)
|
||||
{
|
||||
if ((i >= 9) && (i <= 11))
|
||||
{
|
||||
sum += sample;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (state->samplesPerSymbol == 5)
|
||||
{
|
||||
if (i == 2)
|
||||
{
|
||||
sum += sample;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (((i >= state->symbolCenter - 1) && (i <= state->symbolCenter + 2) && (state->rf_mod == 0)) || (((i == state->symbolCenter) || (i == state->symbolCenter + 1)) && (state->rf_mod != 0)))
|
||||
{
|
||||
sum += sample;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
state->lastsample = sample;
|
||||
}
|
||||
symbol = (sum / count);
|
||||
|
||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
||||
{
|
||||
if (state->jitter >= 0)
|
||||
{
|
||||
printf (" %i\n", state->jitter);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
state->symbolcnt++;
|
||||
return (symbol);
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
upsample (dsd_state * state, float invalue)
|
||||
{
|
||||
|
||||
int i, j, sum;
|
||||
float *outbuf1, c, d;
|
||||
|
||||
outbuf1 = state->audio_out_float_buf_p;
|
||||
outbuf1--;
|
||||
c = *outbuf1;
|
||||
d = invalue;
|
||||
// basic triangle interpolation
|
||||
outbuf1++;
|
||||
*outbuf1 = ((invalue * (float) 0.166) + (c * (float) 0.834));
|
||||
outbuf1++;
|
||||
*outbuf1 = ((invalue * (float) 0.332) + (c * (float) 0.668));
|
||||
outbuf1++;
|
||||
*outbuf1 = ((invalue * (float) 0.5) + (c * (float) 0.5));
|
||||
outbuf1++;
|
||||
*outbuf1 = ((invalue * (float) 0.668) + (c * (float) 0.332));
|
||||
outbuf1++;
|
||||
*outbuf1 = ((invalue * (float) 0.834) + (c * (float) 0.166));
|
||||
outbuf1++;
|
||||
*outbuf1 = d;
|
||||
outbuf1++;
|
||||
|
||||
if (state->audio_out_idx2 > 24)
|
||||
{
|
||||
// smoothing
|
||||
outbuf1 -= 16;
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
sum = 0;
|
||||
outbuf1 -= 2;
|
||||
sum += *outbuf1;
|
||||
outbuf1 += 2;
|
||||
sum += *outbuf1;
|
||||
outbuf1 += 2;
|
||||
sum += *outbuf1;
|
||||
outbuf1 -= 2;
|
||||
*outbuf1 = (sum / (float) 3);
|
||||
outbuf1++;
|
||||
}
|
||||
outbuf1 -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
79
dstar.c
79
dstar.c
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note: D-STAR support is very incomplete and does not work yet
|
||||
* It is unknown if the ambe3600x2250 decoder is even compatible with D-STAR
|
||||
* voice frames. If it is then the interleave pattern needs to be determined.
|
||||
* GMSK modulation optimizations will also required to get a usable bit error
|
||||
* rate. This was included mainly as an example of how other systmes might
|
||||
* be supported.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include "dstar_const.h"
|
||||
|
||||
void
|
||||
processDSTAR (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
// extracts AMBE frames from D-STAR voice frame
|
||||
int i, j, dibit;
|
||||
char ambe_fr[4][24];
|
||||
char dstar[300];
|
||||
const int *w, *x;
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("e:");
|
||||
}
|
||||
|
||||
#ifdef DSTAR_DUMP
|
||||
printf ("\n");
|
||||
#endif
|
||||
|
||||
for (j = 0; j < 210; j++) // 210 is very long but used for debugging
|
||||
{
|
||||
// voice frame
|
||||
w = dW;
|
||||
x = dX;
|
||||
for (i = 0; i < 72; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
dstar[i] = dibit + 48;
|
||||
ambe_fr[*w][*x] = (1 & dibit);
|
||||
w++;
|
||||
x++;
|
||||
}
|
||||
dstar[i] = 32;
|
||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||
|
||||
// data frame
|
||||
for (i = 73; i < 97; i++)
|
||||
{
|
||||
dstar[i] = getDibit (opts, state) + 48;
|
||||
}
|
||||
dstar[i] = 0;
|
||||
|
||||
#ifdef DSTAR_DUMP
|
||||
printf ("%s\n", dstar);
|
||||
#endif
|
||||
}
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("\n");
|
||||
}
|
||||
}
|
||||
311
dstar_const.h
311
dstar_const.h
@@ -1,311 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* dstar interleave experiments
|
||||
*/
|
||||
|
||||
#ifndef _MAIN
|
||||
|
||||
extern const int dW[72];
|
||||
extern const int dX[72];
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* 4x18
|
||||
*/
|
||||
/*
|
||||
const int dW[72] = {
|
||||
0,0,1,2,
|
||||
0,0,1,2,
|
||||
0,0,1,2,
|
||||
0,0,1,2,
|
||||
0,0,1,3,
|
||||
0,0,1,3,
|
||||
0,1,1,3,
|
||||
0,1,1,3,
|
||||
0,1,1,3,
|
||||
0,1,1,3,
|
||||
0,1,1,3,
|
||||
0,1,2,3,
|
||||
0,1,2,3,
|
||||
0,1,2,3,
|
||||
0,1,2,3,
|
||||
0,1,2,3,
|
||||
0,1,2,3,
|
||||
0,1,2,3
|
||||
};
|
||||
|
||||
const int dX[72] = {
|
||||
23,5,10,3,
|
||||
22,4,9,2,
|
||||
21,3,8,1,
|
||||
20,2,7,0,
|
||||
19,1,6,13,
|
||||
18,0,5,12,
|
||||
17,22,4,11,
|
||||
16,21,3,10,
|
||||
15,20,2,9,
|
||||
14,19,1,8,
|
||||
13,18,0,7,
|
||||
12,17,10,6,
|
||||
11,16,9,5,
|
||||
10,15,8,4,
|
||||
9,14,7,3,
|
||||
8,13,6,2,
|
||||
7,12,5,1,
|
||||
6,11,4,0
|
||||
};
|
||||
*/
|
||||
|
||||
/*
|
||||
* 3x24 alt
|
||||
*/
|
||||
const int dW[72] = {
|
||||
1, 0, 3,
|
||||
1, 0, 3,
|
||||
1, 0, 3,
|
||||
1, 0, 3,
|
||||
1, 0, 3,
|
||||
1, 0, 3,
|
||||
1, 0, 3,
|
||||
1, 0, 3,
|
||||
1, 0, 3,
|
||||
1, 0, 3,
|
||||
1, 0, 3,
|
||||
1, 0, 3,
|
||||
1, 0, 3,
|
||||
1, 0, 2,
|
||||
1, 0, 2,
|
||||
1, 0, 2,
|
||||
1, 0, 2,
|
||||
1, 0, 2,
|
||||
1, 0, 2,
|
||||
1, 0, 2,
|
||||
1, 0, 2,
|
||||
1, 0, 2,
|
||||
1, 0, 2,
|
||||
0, 3, 2,
|
||||
};
|
||||
|
||||
const int dX[72] = {
|
||||
22, 22, 12,
|
||||
21, 21, 11,
|
||||
20, 20, 10,
|
||||
19, 19, 9,
|
||||
18, 18, 8,
|
||||
17, 17, 7,
|
||||
16, 16, 6,
|
||||
15, 15, 5,
|
||||
14, 14, 4,
|
||||
13, 13, 3,
|
||||
12, 12, 2,
|
||||
11, 11, 1,
|
||||
10, 10, 0,
|
||||
9, 9, 10,
|
||||
8, 8, 9,
|
||||
7, 7, 8,
|
||||
6, 6, 7,
|
||||
5, 5, 6,
|
||||
4, 4, 5,
|
||||
3, 3, 4,
|
||||
2, 2, 3,
|
||||
1, 1, 2,
|
||||
0, 0, 1,
|
||||
23, 13, 0
|
||||
};
|
||||
|
||||
/*
|
||||
* 3x24
|
||||
*/
|
||||
/*
|
||||
const int dW[72] = {
|
||||
0,1,2,
|
||||
0,1,2,
|
||||
0,1,2,
|
||||
0,1,2,
|
||||
0,1,2,
|
||||
0,1,2,
|
||||
0,1,2,
|
||||
0,1,2,
|
||||
0,1,2,
|
||||
0,1,2,
|
||||
0,1,3,
|
||||
0,1,3,
|
||||
0,1,3,
|
||||
0,1,3,
|
||||
0,1,3,
|
||||
0,1,3,
|
||||
0,1,3,
|
||||
0,1,3,
|
||||
0,1,3,
|
||||
0,1,3,
|
||||
0,1,3,
|
||||
0,1,3,
|
||||
0,1,3,
|
||||
0,2,3,
|
||||
};
|
||||
|
||||
const int dX[72] = {
|
||||
23,22,9,
|
||||
22,21,8,
|
||||
21,20,7,
|
||||
20,19,6,
|
||||
19,18,5,
|
||||
18,17,4,
|
||||
17,16,3,
|
||||
16,15,2,
|
||||
15,14,1,
|
||||
14,13,0,
|
||||
13,12,13,
|
||||
12,11,12,
|
||||
11,10,11,
|
||||
10,9,10,
|
||||
9,8,9,
|
||||
8,7,8,
|
||||
7,6,7,
|
||||
6,5,6,
|
||||
5,4,5,
|
||||
4,3,4,
|
||||
3,2,3,
|
||||
2,1,2,
|
||||
1,0,1,
|
||||
0,10,0
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* 2x36
|
||||
*/
|
||||
/*
|
||||
const int dW[72] = {
|
||||
0,1,
|
||||
0,1,
|
||||
0,1,
|
||||
0,1,
|
||||
0,1,
|
||||
0,1,
|
||||
0,1,
|
||||
0,1,
|
||||
0,1,
|
||||
0,1,
|
||||
0,1,
|
||||
0,2,
|
||||
0,2,
|
||||
0,2,
|
||||
0,2,
|
||||
0,2,
|
||||
0,2,
|
||||
0,2,
|
||||
0,2,
|
||||
0,2,
|
||||
0,2,
|
||||
0,2,
|
||||
0,3,
|
||||
0,3,
|
||||
1,3,
|
||||
1,3,
|
||||
1,3,
|
||||
1,3,
|
||||
1,3,
|
||||
1,3,
|
||||
1,3,
|
||||
1,3,
|
||||
1,3,
|
||||
1,3,
|
||||
1,3,
|
||||
1,3
|
||||
};
|
||||
|
||||
const int dX[72] = {
|
||||
23,10,
|
||||
22,9,
|
||||
21,8,
|
||||
20,7,
|
||||
19,6,
|
||||
18,5,
|
||||
17,4,
|
||||
16,3,
|
||||
15,2,
|
||||
14,1,
|
||||
13,0,
|
||||
12,10,
|
||||
11,9,
|
||||
10,8,
|
||||
9,7,
|
||||
8,6,
|
||||
7,5,
|
||||
6,4,
|
||||
5,3,
|
||||
4,2,
|
||||
3,1,
|
||||
2,0,
|
||||
1,13,
|
||||
0,12,
|
||||
22,11,
|
||||
21,10,
|
||||
20,9,
|
||||
19,8,
|
||||
18,7,
|
||||
17,6,
|
||||
16,5,
|
||||
15,4,
|
||||
14,3,
|
||||
13,2,
|
||||
12,1,
|
||||
11,0
|
||||
};
|
||||
*/
|
||||
|
||||
/*
|
||||
* non-interleaved
|
||||
*/
|
||||
/*
|
||||
const int dW[72] = {
|
||||
0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,
|
||||
1,1,1,1,1,1,
|
||||
1,1,1,1,1,1,
|
||||
1,1,1,1,1,1,
|
||||
1,1,1,1,1,2,
|
||||
2,2,2,2,2,2,
|
||||
2,2,2,2,3,3,
|
||||
3,3,3,3,3,3,
|
||||
3,3,3,3,3,3
|
||||
};
|
||||
|
||||
const int dX[72] = {
|
||||
23,22,21,20,19,18,
|
||||
17,16,15,14,13,12,
|
||||
11,10,9,8,7,6,
|
||||
5,4,3,2,1,0,
|
||||
22,21,20,19,18,17,
|
||||
16,15,14,13,12,11,
|
||||
10,9,8,7,6,5,
|
||||
4,3,2,1,0,10,
|
||||
9,8,7,6,5,4,
|
||||
3,2,1,0,13,12,
|
||||
11,10,9,8,7,6,
|
||||
5,4,3,2,1,0
|
||||
};
|
||||
*/
|
||||
|
||||
#endif
|
||||
162
examples/Example_Usage.md
Normal file
162
examples/Example_Usage.md
Normal file
@@ -0,0 +1,162 @@
|
||||
## Notice
|
||||
|
||||
These examples will need to be modified to run on the 'lite' branch, or any precompiled Windows binaries released. (i.e. dsd-fme to dsd-fme-lite.exe, -i pulse -o pulse, etc)
|
||||
|
||||
### 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.
|
||||
|
||||
```
|
||||
-ft XDMA decoder class (P25 1 and 2, and DMR Stereo)
|
||||
-fs DMR Stereo, also may need to use -xr if inverted DMR
|
||||
-fa Legacy Auto (not recommended)
|
||||
-fi NXDN48
|
||||
-fn NXDN96
|
||||
-fp ProVoice
|
||||
-fm dPMR, also may need to use -xd if inverted dPMR.
|
||||
-fr DMR Mono, also may need to use -xr if inverted DMR.
|
||||
-f1 P25P1
|
||||
-f2 P25P2 (will need to specify wacn/sys/nac manually)
|
||||
-fx X2-TDMA
|
||||
```
|
||||
|
||||
## Trunking Examples
|
||||
Please see trunking.sh for sample start up scripts or scroll down to the bottom for more explanation.
|
||||
|
||||
## Example Usage - Default Decoder, Ncurses Terminal, Pulse Input/Output, and Log Console to file
|
||||
`dsd-fme -N 2> voice.log`
|
||||
|
||||
and in a second terminal tab, same folder, run
|
||||
|
||||
`tail -n 40 -f voice.log`
|
||||
|
||||
Then you can open up your pavucontrol "Pulse Audio Volume Control" or "Volume Control" application and route input into DSD-FME from any application and DSD-FME output to the left and right speakers respectively. (unlock the channel in the application stream and adjust left and right independently)
|
||||
|
||||
### Input ###
|
||||
|
||||
--TCP Direct Audio Link with SDR++
|
||||
|
||||
`dsd-fme -i tcp` Currently defaults to localhost:7355 (SDR++ defaults)
|
||||
|
||||
`dsd-fme -i tcp:192.168.7.5:7356` (remote host and custom port)
|
||||
|
||||
--48000/9600 Mono Wav File Input
|
||||
|
||||
`dsd-fme -i filename.wav` 48k/1 Audio (SDR++, GQRX wav recordings)
|
||||
|
||||
`dsd-fme -i filename.wav -s 96000` 96k/1 (DSDPlus Raw Signal Wav Files)
|
||||
|
||||
Wav File Input Note: Due to 96000 rate audio requiring me to double the symbol rate and center, be sure to use the -s 96000 at the very end of the startup command. Also, some NXDN48/96 may have difficulties decoding properly with wav file input.
|
||||
|
||||
## Example Usage - RTL2832 Dongle Input
|
||||
`dsd-fme -ft -i rtl:0:154.9875M:26:-2:8:0:6020 `
|
||||
|
||||
```
|
||||
RTL-SDR options:
|
||||
WARNING! Old CLI Switch Handling has been depreciated in favor of rtl:<parms>
|
||||
Usage: rtl:dev:freq:gain:ppm:bw:sq:udp
|
||||
dev <num> RTL-SDR Device Index Number
|
||||
freq <num> RTL-SDR Frequency (851800000 or 851.8M)
|
||||
gain <num> RTL-SDR Device Gain (0-49) (default = 26)(0 = Hardware AGC, not recommended)
|
||||
ppm <num> RTL-SDR PPM Error (default = 0)
|
||||
bw <num> RTL-SDR VFO Bandwidth kHz (default = 12)(6, 8, 12, 24)
|
||||
sq <num> RTL-SDR Squelch Level (0 - Open, 25 - Little, 50 - Higher)
|
||||
udp <num> RTL-SDR UDP Remote Port (default = 6020)
|
||||
Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:12:0:6021
|
||||
|
||||
```
|
||||
|
||||
## Example Usage - Extra Payload/PDU Logging
|
||||
|
||||
`dsd-fme -Z -N 2>> log.ans`
|
||||
|
||||
and in a second terminal tab, same folder, run
|
||||
|
||||
`tail -n 40 -f log.ans`
|
||||
|
||||
### EDACS/P25/NXDN/DMR Simple/Single VFO Trunking ###
|
||||
|
||||
EDACS Trunking (w/ channel map import)
|
||||
|
||||
--EDACS/PV Trunking using RIGCTL and TCP Direct Link Audio inside of SDR++ (Tested and Working on EDACS/EDACS-EA with Provoice only, no analog voice monitoring)
|
||||
|
||||
`dsd-fme -i tcp -fp -C channel_map.csv -G group.csv -T -U 4532 -N 2> log.ans`
|
||||
|
||||
--NXDN48 Trunking (standard band plan) with SDR++ (untested for frequency accuracy)
|
||||
|
||||
`dsd-fme -fi -i tcp -T -U 4532 -N 2> log.ans`
|
||||
|
||||
--NXDN48 Trunking (w/ channel map import) with SDR++ (put control channel frequency at channel 0 in channel_map.csv)
|
||||
|
||||
`dsd-fme -fi -i tcp -T -U 4532 -C channel_map.csv -N 2> log.ans`
|
||||
|
||||
--DMR Trunking (w/ channel map import) with SDR++ (put control channel frequency at channel 0 (TIII and Con+) in channel_map.csv)
|
||||
|
||||
`dsd-fme -fs -i tcp -T -U 4532 -C channel_map.csv -N 2> log.ans`
|
||||
|
||||
--P25 Trunking P1 and P2 (C4FM) with SDR++
|
||||
|
||||
`dsd-fme -i tcp -T -U 4532 -N 2> log.ans`
|
||||
|
||||
--P25 Trunking (CQPSK) with P1 Control Channel (Should switch symbol rate and center on Phase 2 audio channels)
|
||||
|
||||
`dsd-fme -i tcp -T -U 4532 -N -mq 2> log.ans`
|
||||
|
||||
--P25 Trunking Phase 2 TDMA Control Channel systems with CQPSK (non Phase 1 systems)
|
||||
|
||||
`dsd-fme -i tcp -T -U 4532 -N -f2 -m2 2> log.ans`
|
||||
|
||||
Trunking Note1: All samples above can also be run with the RTL input method and setting of RTL UDP remote port. In terms of performance, however, SDR++ will do a much better job on weaker/marginal signal compared to the RTL input. RTL input should only be used on strong signal.
|
||||
|
||||
`dsd-fme -fp -i rtl:0:851.8M:22:-2:24:0:6020 -T -C channel_map.csv -G group.csv -N 2> log.ans`
|
||||
|
||||
Trunking Note2: 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 Note3: DMR Trunking has been coded, and some testing and tweaks have been carried out. Cap+, Con+, and TIII systems seem to do well with trunking now. Placing the frequency for the control channel at channel map 0 in your channel_map.csv file is not required now if using RIGCTL or the RTL Input, both can poll the VFO for the current frequency if it believes its on a control channel, but setting a fake channel number (i.e. 999) first with the CC frequency will result in finding the CC faster on startup if desired. If you need to map out your channels for TIII, you can observe the console output and look for channel numbers. For conveniece I have included the DSDPlus channel numbering (as best as I can figure it, but they seem consistent) into the console print so it will make it easier for users from DSDPlus to map frequencies into the channel_map.csv file. Make sure your channel numbers are the Cd (channel decimal) values from the log, and not the C+ (dsdplus) values. Notice: TIII Site ID value needs work to determine proper DMRLA values for system area and sub area.
|
||||
|
||||
```
|
||||
Talkgroup Voice Channel Grant (TV_GRANT) - Logical
|
||||
Ch [036] Cd [0054] C+ [0110] - TS [1] - Target [01900500] - Source [01900505]
|
||||
```
|
||||
|
||||
Use channel 54 in your import file, which would correspond to dsdplus channels 109 and 110 (109 = TS0 / 110 = TS1).
|
||||
|
||||
For Connect Plus, enumerate your list from 1 to the last channel and add the frequency. For Capacity Plus, 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.
|
||||
|
||||
Currently uncoded/unknown DMR trunking systems include Hytera XPT.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## NCurses Keyboard Shortcuts ##
|
||||
|
||||
Some Keyboard Shortcuts have been implemented for testing to see how users like them. Just hope nobody pushes a key on accident and doesn't know which one it was or what it does. The current list of keyboard shortcuts include:
|
||||
|
||||
```
|
||||
esc or arrow keys - ncurses menu
|
||||
q - quit
|
||||
c - toggle compact mode
|
||||
h - toggle call history
|
||||
z - toggle console payloads
|
||||
a - toggle call alert beep
|
||||
4 - force dmr privacy key assertion over fid and svc bits
|
||||
i - toggle signal inversion on types that can't auto detect (dmr, dpmr)
|
||||
m - toggle c4fm/qpsk 10/4 (everything but phase 2 signal)
|
||||
M - toggle c4fm/qpsk 8/3 (phase 2 tdma control channel)
|
||||
R - start capturing symbol capture bin (date/time name file)
|
||||
r - stop capturing symbol capture bin
|
||||
spacebar - replay last symbol capture bin (captures must be stopped first)
|
||||
s - stop playing symbol capture bin or wav input file
|
||||
P - start per call decoded wav files
|
||||
p - stop per call decoded wav files
|
||||
t - toggle trunking (needs either rtl input, or rigctl connection)
|
||||
1 - Lockout Tuning/Playback of TG in Slot 1 or Conventional (Current Session Only)
|
||||
2 - Lockout Tuning/Playback of TG in Slot 2 (Current Session Only)
|
||||
0 - Toggle Audio Smoothing - May produce crackling if enabled on RTL/TCP or wav/bin files
|
||||
w - Toggle Trunking/Playback White List (Allow A Groups Only) / Black List (Block B or DE groups only) Mode
|
||||
g - Toggle Trunking Tuning to Group Calls (DMR T3, Con+, Cap+, P25)
|
||||
u - Toggle Trunking Tuning to Private Calls (DMR T3, P25)
|
||||
d - Toggle Trunking Tuning to Data Calls (DMR T3)
|
||||
|
||||
```
|
||||
131
examples/Install_Notes.md
Normal file
131
examples/Install_Notes.md
Normal file
@@ -0,0 +1,131 @@
|
||||
## Notice
|
||||
|
||||
These should not be followed for the 'lite' branch
|
||||
|
||||
## How to clone and build this branch
|
||||
|
||||
### Ubuntu 22.04/20.04/LM20/Debian Bullseye or Newer:
|
||||
|
||||
Using the included download-and-install.sh should make for a simple and painless clone, build, and install on newer Debian/Ubuntu/Mint/Pi systems. Simply acquire or copy the script, and run it. Update: Ubuntu 22.04 and RPi Bullseye 64-bit has been tested working with the installer script and functions appropriately.
|
||||
|
||||
If you need all dependencies build and installed first (only on Debian/Ubuntu/Mint/Pi), run:
|
||||
|
||||
```
|
||||
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/main/download-and-install.sh
|
||||
chmod +x download-and-install.sh
|
||||
./download-and-install.sh
|
||||
```
|
||||
|
||||
If you have dependencies already installed (i.e. need a fresh clean install on a system with DSD-FME already or using system other than Debian/Ubuntu, etc), please run this instead:
|
||||
|
||||
```
|
||||
wget https://raw.githubusercontent.com/lwvmobile/dsd-fme/main/download-and-install-nodeps.sh
|
||||
chmod +x download-and-install-nodeps.sh
|
||||
./download-and-install-nodeps.sh
|
||||
```
|
||||
|
||||
### Ubuntu 18.04/LM19/Buster Note:
|
||||
The above install.sh should now function on older system types. You can elect to manually follow the steps down below. Do NOT Manually build and install ITPP 4.3.1 on older systems, it is currently not wanting to build on Ubuntu 18.04 and Linux Mint 19. Install it from the repository instead.
|
||||
|
||||
## Manual Install
|
||||
|
||||
First, install dependency packages. This guide will assume you are using Debian/Ubuntu based distros. Check your package manager for equivalent packages if different.
|
||||
|
||||
```
|
||||
sudo apt update
|
||||
sudo apt install libpulse-dev pavucontrol libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev
|
||||
```
|
||||
## Headless
|
||||
|
||||
If running headless, swap out pavucontrol for pulsemixer, and also install pulseaudio as well. Attempting to install pavucontrol in a headless environment may attempt to install a minimal desktop environment. Note: Default behavior of pulseaudio in a headless environment may be to be muted, so check by opening pulsemixer and unmuting and routing audio appropriately.
|
||||
|
||||
```
|
||||
sudo apt install libpulse-dev libsndfile1-dev libfftw3-dev liblapack-dev socat libusb-1.0-0-dev libncurses5 libncurses5-dev rtl-sdr librtlsdr-dev libusb-1.0-0-dev cmake git wget make build-essential libitpp-dev libncursesw5-dev pulsemixer pulseaudio
|
||||
```
|
||||
|
||||
### Build and Install ITPP - ONLY IF NOT IN REPO!!
|
||||
|
||||
```
|
||||
wget -O itpp-latest.tar.bz2 http://sourceforge.net/projects/itpp/files/latest/download?source=files
|
||||
tar xjf itpp*
|
||||
#if you can't cd into this folder, double check folder name first
|
||||
cd itpp-4.3.1
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j `nproc`
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
cd ..
|
||||
cd ..
|
||||
```
|
||||
|
||||
MBELib is considered a requirement in this build. You must read this notice prior to continuing. [MBElib Patent Notice](https://github.com/lwvmobile/mbelib#readme "MBElib Patent Notice") This version of MBELib is 1.3.2 and prints to STDERR, using the stock 1.3.0 MBElib may cause problems with print alignments if paired with this version of DSD-FME, or cause future compatibility issues.
|
||||
|
||||
```
|
||||
git clone https://github.com/lwvmobile/mbelib
|
||||
cd mbelib
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j `nproc`
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
cd ..
|
||||
cd ..
|
||||
```
|
||||
|
||||
Note: Some environment/distro users (Cygwin, Arch, and others) may need to manually copy any libmbe files found in the build folder to the /usr/lib folder on root.
|
||||
`sudo cp libmbe* /usr/lib`
|
||||
|
||||
Finish by running these steps to clone and build DSD-FME.
|
||||
|
||||
```
|
||||
git clone https://github.com/lwvmobile/dsd-fme
|
||||
cd dsd-fme
|
||||
sudo cp tone8.wav /usr/share/
|
||||
sudo cp tone24.wav /usr/share/
|
||||
sudo cp tone48.wav /usr/share/
|
||||
sudo chmod 777 /usr/share/tone8.wav
|
||||
sudo chmod 777 /usr/share/tone24.wav
|
||||
sudo chmod 777 /usr/share/tone48.wav
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j `nproc`
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
|
||||
```
|
||||
Optional 'Virtual Sinks' for routing audio from SDR++ or GQRX, Media Players, etc. into DSD-FME
|
||||
|
||||
You may wish to direct sound into DSD-FME via Virtual Sinks. You may set up a Virtual Sink or two on your machine for routing audio in and out of applications to other applications using the following command, and opening up pavucontrol "PulseAudio Volume Control" in the menu (or `pulsemixer` in headless mode) to change line out of application to virtual sink, and line in of DSD-FME to monitor of virtual sink. This command will not persist past a reboot, so you will need to invoke them each time you reboot, or search for how to add this to your conf files for persistency if desired.
|
||||
|
||||
```
|
||||
pacmd load-module module-null-sink sink_name=virtual_sink sink_properties=device.description=Virtual_Sink
|
||||
pacmd load-module module-null-sink sink_name=virtual_sink2 sink_properties=device.description=Virtual_Sink2
|
||||
```
|
||||
|
||||
Already have this branch, and just want to pull the latest build? You can run the rebuild.sh file in the dsd-fme folder, or manually do the pull with the commands:
|
||||
|
||||
```
|
||||
##Open your clone folder##
|
||||
git pull
|
||||
##cd into your build folder##
|
||||
cd build
|
||||
cmake ..
|
||||
make -j `nproc`
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
```
|
||||
|
||||
If the call alert wav files aren't playing, then make sure to run the following in the dsd-fme folder to copy the wav files to the /usr/share/ folder and give them adequate permission to be accessed.
|
||||
|
||||
```
|
||||
sudo cp tone8.wav /usr/share/
|
||||
sudo cp tone24.wav /usr/share/
|
||||
sudo cp tone48.wav /usr/share/
|
||||
sudo chmod 777 /usr/share/tone8.wav
|
||||
sudo chmod 777 /usr/share/tone24.wav
|
||||
sudo chmod 777 /usr/share/tone48.wav
|
||||
```
|
||||
10
examples/capacity_plus_chan.csv
Normal file
10
examples/capacity_plus_chan.csv
Normal file
@@ -0,0 +1,10 @@
|
||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||
1,858606250
|
||||
2,858606250
|
||||
3,859606250
|
||||
4,859606250
|
||||
5,860606250
|
||||
6,860606250
|
||||
7,861606250
|
||||
8,861606250
|
||||
|
||||
|
19
examples/connect_plus_chan.csv
Normal file
19
examples/connect_plus_chan.csv
Normal file
@@ -0,0 +1,19 @@
|
||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||
999,862093750,default cc (come here first), optional use non-existent channel as first entry
|
||||
1,863093750
|
||||
2,862093750
|
||||
3,861093750
|
||||
4,860093750
|
||||
5,859093750
|
||||
6,863593750
|
||||
7,862593750
|
||||
8,861593750
|
||||
9,860593750
|
||||
10,859593750
|
||||
11,863393750
|
||||
12,862393750
|
||||
13,861393750
|
||||
14,860393750
|
||||
15,859393750
|
||||
|
||||
|
||||
|
8
examples/dmr_t3_chan.csv
Normal file
8
examples/dmr_t3_chan.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||
999,456318750,default cc (come here first), optional use non-existent channel as first entry
|
||||
36,455756250
|
||||
54,455981250
|
||||
81,456318750
|
||||
|
||||
|
||||
|
||||
|
6
examples/edacs_channel_map.csv
Normal file
6
examples/edacs_channel_map.csv
Normal file
@@ -0,0 +1,6 @@
|
||||
ChannelNumber(dec),frequency(Hz) (do not delete this line or won't import properly)
|
||||
1,851375000
|
||||
2,851800000
|
||||
3,855987500
|
||||
4,858487500
|
||||
5,852312500
|
||||
|
5
examples/group.csv
Normal file
5
examples/group.csv
Normal file
@@ -0,0 +1,5 @@
|
||||
DEC,Mode(A- Allow, B - Block, DE - Digital Enc),Name of Group,Tag (do not delete this line or won't import properly)
|
||||
100,B,Example Name,Tag
|
||||
1449,A,Fire Dispatch,Fire
|
||||
929,A,Fire Tac,Fire
|
||||
22033,DE,Law Dispatch,Law
|
||||
|
40
examples/trunking.sh
Normal file
40
examples/trunking.sh
Normal file
@@ -0,0 +1,40 @@
|
||||
#example trunking sh scripts (SDR++ and RTL Input) -T = trunking
|
||||
#make sure to put a # in front of all lines except the one you wish to use
|
||||
|
||||
#P25 P1 FDMA CC with C4FM
|
||||
#dsd-fme -i tcp -U 4532 -T -N 2> log.ans
|
||||
#dsd-fme -i rtl:0:850M:26:-2:8:0:6020 -T -N 2> log.ans
|
||||
|
||||
#P25 P1 FDMA CC with QPSK (Simulcast)
|
||||
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -mq -N 2> log.ans
|
||||
#dsd-fme -i rtl:0:855.625M:26:-2:12:0:6020 -T -mq -N 2> log.ans
|
||||
|
||||
#P25 P2 TDMA CC with QPSK (Simulcast)
|
||||
#dsd-fme -i tcp:192.168.7.8:7355 -U 4532 -T -f2 -m2 -N 2> log.ans
|
||||
#dsd-fme -i rtl:0:855.625M:26:-2:12:0:6020 -T -f2 -m2 -N 2> log.ans
|
||||
|
||||
#DMR TIII, Con+, Cap+
|
||||
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
|
||||
|
||||
#EDACS/EDACS-EA Digital Only
|
||||
#dsd-fme -fp -i tcp:192.168.7.5:7355 -U 4532 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||
#dsd-fme -fp -i rtl:0:850M:26:-2:24:0:6020 -T -C edacs_channel_map.csv -G group.csv -N 2> log.ans
|
||||
|
||||
#NXDN48 Trunking with Standard Band Plan (Channels 0-800)
|
||||
#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
|
||||
|
||||
#NXDN48 Trunking with Channel Map (Custom Channels > 800)
|
||||
#dsd-fme -fi -i tcp -T -U 4532 -C 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
|
||||
|
||||
#Notes: parameters after -i tcp can be omitted if using localhost as the default hostname to connect
|
||||
#and 7355 as the default port number. -U is the port number for RIGCTL and will mirror the tcp hostname
|
||||
#running under the assumption that the user will be connnecting the VFO to the same instance of SDR++
|
||||
#You can also specify an ip address to connect to a remote SDR++ instance, make sure that the settings
|
||||
#inside of SDR++ match, i.e., change in SDR++ from 'localhost' to an IP to bind to (machine's IP address)
|
||||
|
||||
#Note2: RTL input should only be used on strong signal, the SDR++ VFO will perform much better on marginal signal
|
||||
|
||||
#Having a -G group.csv is purely optional, but useful if you want to block groups, or give groups names
|
||||
412
include/Golay24.hpp
Normal file
412
include/Golay24.hpp
Normal file
@@ -0,0 +1,412 @@
|
||||
|
||||
#ifndef GOLAY24_HPP_7a68240afda9406facf81fcad3851111
|
||||
#define GOLAY24_HPP_7a68240afda9406facf81fcad3851111
|
||||
|
||||
/**
|
||||
* Based on the work of Mr Hank Wallace.
|
||||
*
|
||||
* Code adapted from http://www.aqdi.com/golay.htm
|
||||
*
|
||||
* Tried before with the implementation from ITPP library but couldn't make it produce the same outputs
|
||||
* expected from the P25 transmissions that I have tested. This implementation does work.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define POLY 0xAE3
|
||||
|
||||
class Golay24
|
||||
{
|
||||
private:
|
||||
unsigned int golay(unsigned int cw)
|
||||
/* This function calculates [23,12] Golay codewords.
|
||||
The format of the returned int is
|
||||
[checkbits(11),data(12)]. */
|
||||
{
|
||||
int i;
|
||||
unsigned int c;
|
||||
cw&=0xfffl;
|
||||
c=cw; /* save original codeword */
|
||||
for (i=1; i<=12; i++) /* examine each data bit */
|
||||
{
|
||||
if (cw & 1) /* test data bit */
|
||||
cw^=POLY; /* XOR polynomial */
|
||||
cw>>=1; /* shift intermediate result */
|
||||
}
|
||||
return((cw<<12)|c); /* assemble codeword */
|
||||
}
|
||||
|
||||
int parity(unsigned int cw)
|
||||
/* This function checks the overall parity of codeword cw.
|
||||
If parity is even, 0 is returned, else 1. */
|
||||
{
|
||||
unsigned char p;
|
||||
|
||||
/* XOR the bytes of the codeword */
|
||||
p=*(unsigned char*)&cw;
|
||||
p^=*((unsigned char*)&cw+1);
|
||||
p^=*((unsigned char*)&cw+2);
|
||||
|
||||
/* XOR the halves of the intermediate result */
|
||||
p=p ^ (p>>4);
|
||||
p=p ^ (p>>2);
|
||||
p=p ^ (p>>1);
|
||||
|
||||
/* return the parity result */
|
||||
return(p & 1);
|
||||
}
|
||||
|
||||
unsigned int syndrome(unsigned int cw)
|
||||
/* This function calculates and returns the syndrome
|
||||
of a [23,12] Golay codeword. */
|
||||
{
|
||||
int i;
|
||||
cw&=0x7fffffl;
|
||||
for (i=1; i<=12; i++) /* examine each data bit */
|
||||
{
|
||||
if (cw & 1) /* test data bit */
|
||||
cw^=POLY; /* XOR polynomial */
|
||||
cw>>=1; /* shift intermediate result */
|
||||
}
|
||||
return(cw<<12); /* value pairs with upper bits of cw */
|
||||
}
|
||||
|
||||
int weight(unsigned int cw)
|
||||
/* This function calculates the weight of
|
||||
23 bit codeword cw. */
|
||||
{
|
||||
int bits,k;
|
||||
|
||||
/* nibble weight table */
|
||||
const char wgt[16] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
|
||||
|
||||
bits=0; /* bit counter */
|
||||
k=0;
|
||||
/* do all bits, six nibbles max */
|
||||
while ((k<6) && (cw))
|
||||
{
|
||||
bits=bits+wgt[cw & 0xf];
|
||||
cw>>=4;
|
||||
k++;
|
||||
}
|
||||
|
||||
return(bits);
|
||||
}
|
||||
|
||||
unsigned int rotate_left(unsigned int cw, int n)
|
||||
/* This function rotates 23 bit codeword cw left by n bits. */
|
||||
{
|
||||
int i;
|
||||
|
||||
if (n != 0)
|
||||
{
|
||||
for (i=1; i<=n; i++)
|
||||
{
|
||||
if ((cw & 0x400000l) != 0)
|
||||
cw=(cw << 1) | 1;
|
||||
else
|
||||
cw<<=1;
|
||||
}
|
||||
}
|
||||
|
||||
return(cw & 0x7fffffl);
|
||||
}
|
||||
|
||||
unsigned int rotate_right(unsigned int cw, int n)
|
||||
/* This function rotates 23 bit codeword cw right by n bits. */
|
||||
{
|
||||
int i;
|
||||
|
||||
if (n != 0)
|
||||
{
|
||||
for (i=1; i<=n; i++)
|
||||
{
|
||||
if ((cw & 1) != 0)
|
||||
cw=(cw >> 1) | 0x400000l;
|
||||
else
|
||||
cw>>=1;
|
||||
}
|
||||
}
|
||||
|
||||
return(cw & 0x7fffffl);
|
||||
}
|
||||
|
||||
public:
|
||||
unsigned int correct(unsigned int cw, int *errs, unsigned int *errors_detected)
|
||||
/* This function corrects Golay [23,12] codeword cw, returning the
|
||||
corrected codeword. This function will produce the corrected codeword
|
||||
for three or fewer errors. It will produce some other valid Golay
|
||||
codeword for four or more errors, possibly not the intended
|
||||
one. *errs is set to the number of bit errors corrected. */
|
||||
{
|
||||
unsigned char
|
||||
w; /* current syndrome limit weight, 2 or 3 */
|
||||
unsigned int
|
||||
mask; /* mask for bit flipping */
|
||||
int
|
||||
i,j; /* index */
|
||||
unsigned int
|
||||
s, /* calculated syndrome */
|
||||
cwsaver; /* saves initial value of cw */
|
||||
|
||||
cwsaver=cw; /* save */
|
||||
*errs=0;
|
||||
*errors_detected = 0;
|
||||
|
||||
w=3; /* initial syndrome weight threshold */
|
||||
j=-1; /* -1 = no trial bit flipping on first pass */
|
||||
mask=1;
|
||||
while (j<23) /* flip each trial bit */
|
||||
{
|
||||
if (j != -1) /* toggle a trial bit */
|
||||
{
|
||||
if (j>0) /* restore last trial bit */
|
||||
{
|
||||
cw=cwsaver ^ mask;
|
||||
mask+=mask; /* point to next bit */
|
||||
}
|
||||
cw=cwsaver ^ mask; /* flip next trial bit */
|
||||
w=2; /* lower the threshold while bit diddling */
|
||||
}
|
||||
|
||||
s=syndrome(cw); /* look for errors */
|
||||
if (s) /* errors exist */
|
||||
{
|
||||
(*errors_detected)++;
|
||||
for (i=0; i<23; i++) /* check syndrome of each cyclic shift */
|
||||
{
|
||||
if ((*errs=weight(s)) <= w) /* syndrome matches error pattern */
|
||||
{
|
||||
cw=cw ^ s; /* remove errors */
|
||||
cw=rotate_right(cw,i); /* unrotate data */
|
||||
return(s=cw);
|
||||
}
|
||||
else
|
||||
{
|
||||
cw=rotate_left(cw,1); /* rotate to next pattern */
|
||||
s=syndrome(cw); /* calc new syndrome */
|
||||
}
|
||||
}
|
||||
j++; /* toggle next trial bit */
|
||||
}
|
||||
else
|
||||
{
|
||||
return(cw); /* return corrected codeword */
|
||||
}
|
||||
}
|
||||
|
||||
return(cwsaver); /* return original if no corrections */
|
||||
} /* correct */
|
||||
|
||||
unsigned int encode(unsigned int data)
|
||||
{
|
||||
unsigned int codeword =golay(data); /* make a test codeword */
|
||||
if (parity(codeword)) {
|
||||
codeword^=0x800000l;
|
||||
}
|
||||
|
||||
return codeword;
|
||||
}
|
||||
|
||||
unsigned int encode23(unsigned int data)
|
||||
{
|
||||
return golay(data); /* make a test codeword */
|
||||
}
|
||||
|
||||
int decode(int *errs, unsigned int *cw)
|
||||
/* This function decodes codeword *cw. Here error correction is attempted,
|
||||
with *errs set to the number of
|
||||
bits corrected, and returning 0 if no errors exist, or 1 if parity errors
|
||||
exist. */
|
||||
{
|
||||
unsigned int parity_bit;
|
||||
unsigned int detected_errors;
|
||||
parity_bit=*cw & 0x800000l; /* save parity bit */
|
||||
*cw&=~0x800000l; /* remove parity bit for correction */
|
||||
|
||||
*cw=correct(*cw, errs, &detected_errors); /* correct up to three bits */
|
||||
*cw|=parity_bit; /* restore parity bit */
|
||||
|
||||
/* check for 4 bit errors */
|
||||
if (parity(*cw)) /* odd parity is an error */
|
||||
return(1);
|
||||
|
||||
return(0); /* no errors */
|
||||
} /* decode */
|
||||
|
||||
int detect(int *errs, unsigned int cw)
|
||||
/* This function decodes codeword cw. Here error detection is performed on cw,
|
||||
returning 0 if no errors exist, 1 if an overall parity error exists, and
|
||||
2 if a codeword error exists. */
|
||||
{
|
||||
*errs=0;
|
||||
if (parity(cw)) /* odd parity is an error */
|
||||
{
|
||||
*errs=1;
|
||||
return(1);
|
||||
}
|
||||
if (syndrome(cw))
|
||||
{
|
||||
*errs=1;
|
||||
return(2);
|
||||
}
|
||||
else
|
||||
return(0); /* no errors */
|
||||
|
||||
} /* decode */
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience class that adapts Mr Wallace's implementation to the input/output format of DSD.
|
||||
* DSD works with data stored in char arrays where each element represents a bit (0 and 1 values).
|
||||
* The original implementation works with codewords that pack the 24 bits of information (12 data and
|
||||
* another 12 of parity) in an integer.
|
||||
*/
|
||||
class DSDGolay24 : public Golay24
|
||||
{
|
||||
public:
|
||||
unsigned int adapt_to_codeword(const char* word, unsigned int length, const char* parity)
|
||||
{
|
||||
unsigned int codeword = 0;
|
||||
|
||||
// Data needs to be packed with the 12 bits of parity as the most significant
|
||||
// bits and 12 bits of data as the less significant. All these discovered by trial and error.
|
||||
for (unsigned int i=0; i<12; i++) {
|
||||
assert(parity[11-i] == 0 || parity[11-i] == 1);
|
||||
codeword <<= 1;
|
||||
codeword |= parity[11-i];
|
||||
}
|
||||
for (unsigned int i=0; i<length; i++) {
|
||||
char bit = word[length-1-i];
|
||||
assert(bit == 0 || bit == 1);
|
||||
codeword <<= 1;
|
||||
codeword |= bit;
|
||||
}
|
||||
// We only have length bits of data. We fill up the less significant bits of codeword with zeros.
|
||||
if (length < 12) {
|
||||
codeword <<= (12 - length);
|
||||
}
|
||||
|
||||
return codeword;
|
||||
}
|
||||
|
||||
void adapt_to_word(unsigned int codeword, char* word, unsigned int length)
|
||||
{
|
||||
// put back in word the bits from codeword
|
||||
for (unsigned int i=0, mask = 1<<(12-length); i<length; i++, mask<<=1) {
|
||||
word[i] = (codeword & mask) != 0? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int adapt_from_word(char* word, unsigned int length)
|
||||
{
|
||||
unsigned int codeword = 0;
|
||||
|
||||
// encode the hex bits into a codeword
|
||||
for (unsigned int i=0; i<length; i++) {
|
||||
int bit = word[length-1-i];
|
||||
assert(bit == 0 || bit == 1);
|
||||
codeword <<= 1;
|
||||
codeword |= bit;
|
||||
}
|
||||
|
||||
// We only have length bits of data. We fill up the less significant bits of codeword with zeros.
|
||||
if (length < 12) {
|
||||
codeword <<= (12-length);
|
||||
}
|
||||
|
||||
return codeword;
|
||||
}
|
||||
|
||||
/**
|
||||
* Important method that takes an hex word, a 12 bit parity word and uses the Golay24 implementation
|
||||
* to error correct it.
|
||||
* \param hex The data to error correct, packed in an array of 6 chars, representing a bit each.
|
||||
* \param parity The 12 bits of parity packed in an array of chars.
|
||||
* \param fixed_errors Output argument, returns the number of errors detected and fixed.
|
||||
* \return 1 if we were unable to correct the hex word. Too many errors detected.
|
||||
* in this case the original data remains unchanged.
|
||||
* 0 if the data was successfully error corrected.
|
||||
*/
|
||||
int decode_6(char* hex, const char* parity, int* fixed_errors)
|
||||
{
|
||||
unsigned int codeword = adapt_to_codeword(hex, 6, parity);
|
||||
// codeword now contains:
|
||||
// bits 0- 5: zeros
|
||||
// bits 6-11: hex bits
|
||||
// bits 12-23: golay (24,12) parity bits
|
||||
|
||||
// bits are ordered from left to right, so bit 0 is the most significant and bit 23 is the less.
|
||||
|
||||
int uncorrectable_errors = Golay24::decode(fixed_errors, &codeword);
|
||||
|
||||
// codeword is now hopefully fixed
|
||||
|
||||
// If there are uncorrectable errors and the fixed proposal includes ones
|
||||
// in bits 0-5 then probably the fix is not good and we discard it.
|
||||
// Bits 0-5 should always be zero, if there is a problem it's on the other bits
|
||||
// from 6 to 24.
|
||||
if (uncorrectable_errors == 1 && (codeword & 0x3f) != 0) {
|
||||
// discard, don't touch hex
|
||||
} else {
|
||||
// put it back into our hex format
|
||||
adapt_to_word(codeword, hex, 6);
|
||||
uncorrectable_errors = 0;
|
||||
}
|
||||
|
||||
return uncorrectable_errors;
|
||||
}
|
||||
|
||||
int decode_12(char* dodeca, const char* parity, int* fixed_errors)
|
||||
{
|
||||
unsigned int codeword = adapt_to_codeword(dodeca, 12, parity);
|
||||
// codeword contains:
|
||||
// bits 0-11: dodeca bits
|
||||
// bits 12-23: golay (24,12) parity bits
|
||||
|
||||
// bits are ordered from left to right, so bit 0 is the most significant and bit 23 is the less.
|
||||
|
||||
int uncorrectable_errors = Golay24::decode(fixed_errors, &codeword);
|
||||
|
||||
// codeword is now hopefully fixed
|
||||
|
||||
// If there are uncorrectable errors and the fixed proposal includes ones
|
||||
// in bits 0-5 then probably the fix is not good and we discard it.
|
||||
// Bits 0-5 should always be zero, if there is a problem it's on the other bits
|
||||
// from 6 to 24.
|
||||
if (uncorrectable_errors == 1 && (codeword & 0x3f) != 0) {
|
||||
// discard, don't touch hex
|
||||
} else {
|
||||
// put it back into our hex format
|
||||
adapt_to_word(codeword, dodeca, 12);
|
||||
uncorrectable_errors = 0;
|
||||
}
|
||||
|
||||
return uncorrectable_errors;
|
||||
}
|
||||
|
||||
void encode_6(char* hex, char* out_parity)
|
||||
{
|
||||
unsigned int data = adapt_from_word(hex, 6);
|
||||
unsigned int codeword = Golay24::encode(data);
|
||||
|
||||
// Fill up the parity
|
||||
for (unsigned int i=0, mask = 1<<12; i<12; i++, mask<<=1) {
|
||||
out_parity[i] = (codeword & mask) != 0? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
void encode_12(char* dodeca, char* out_parity)
|
||||
{
|
||||
unsigned int data = adapt_from_word(dodeca, 12);
|
||||
unsigned int codeword = Golay24::encode(data);
|
||||
|
||||
// Fill up the parity
|
||||
for (unsigned int i=0, mask = 1<<12; i<12; i++, mask<<=1) {
|
||||
out_parity[i] = (codeword & mask) != 0? 1 : 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // GOLAY24_HPP_7a68240afda9406facf81fcad3851111
|
||||
413
include/Hamming.hpp
Normal file
413
include/Hamming.hpp
Normal file
@@ -0,0 +1,413 @@
|
||||
|
||||
#ifndef HAMMING_HPP_e7123c3795b94d14b5774d5d8f016a04
|
||||
#define HAMMING_HPP_e7123c3795b94d14b5774d5d8f016a04
|
||||
|
||||
#include <bitset>
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/**
|
||||
* \brief Holds data for the Hamming_10_6_3 class. See next.
|
||||
*/
|
||||
class Hamming_10_6_3_data
|
||||
{
|
||||
public:
|
||||
std::bitset<10> g0;
|
||||
std::bitset<10> g1;
|
||||
std::bitset<10> g2;
|
||||
std::bitset<10> g3;
|
||||
std::bitset<10> g4;
|
||||
std::bitset<10> g5;
|
||||
|
||||
std::bitset<6> gt0;
|
||||
std::bitset<6> gt1;
|
||||
std::bitset<6> gt2;
|
||||
std::bitset<6> gt3;
|
||||
|
||||
std::bitset<10> h0;
|
||||
std::bitset<10> h1;
|
||||
std::bitset<10> h2;
|
||||
std::bitset<10> h3;
|
||||
|
||||
int bad_bit_table[16];
|
||||
|
||||
Hamming_10_6_3_data()
|
||||
{
|
||||
// G matrix come from the APCO 25 reference documentation:
|
||||
g0 = std::bitset<10>(std::string("1000001110"));
|
||||
g1 = std::bitset<10>(std::string("0100001101"));
|
||||
g2 = std::bitset<10>(std::string("0010001011"));
|
||||
g3 = std::bitset<10>(std::string("0001000111"));
|
||||
g4 = std::bitset<10>(std::string("0000100011"));
|
||||
g5 = std::bitset<10>(std::string("0000011100"));
|
||||
|
||||
gt0 = std::bitset<6>(std::string("111001"));
|
||||
gt1 = std::bitset<6>(std::string("110101"));
|
||||
gt2 = std::bitset<6>(std::string("101110"));
|
||||
gt3 = std::bitset<6>(std::string("011110"));
|
||||
|
||||
// H matrix, calculated transposing G. See wikipedia.
|
||||
h0 = std::bitset<10>(std::string("1110011000"));
|
||||
h1 = std::bitset<10>(std::string("1101010100"));
|
||||
h2 = std::bitset<10>(std::string("1011100010"));
|
||||
h3 = std::bitset<10>(std::string("0111100001"));
|
||||
|
||||
// bad_bit_table indicates which is the bit to correct from the calculated syndromes.
|
||||
// It's calculated from the previous matrices: See the left-most column of the matrix whose rows are
|
||||
// h0,h1,h2 and h3. It's [1 1 1 0]. It means: when the syndrome value is the binary number 1110, the
|
||||
// bit that is wrong is the bit 9 (the left-most column is for the bit 9). Another example with
|
||||
// the right-most column: it's column is [0 0 0 1], so when the syndrome is 0001, the bit to fix is
|
||||
// bit 0 (one of the parity bits).
|
||||
bad_bit_table[ 1] = 0;
|
||||
bad_bit_table[ 2] = 1;
|
||||
bad_bit_table[ 4] = 2;
|
||||
bad_bit_table[ 8] = 3;
|
||||
bad_bit_table[12] = 4;
|
||||
bad_bit_table[ 3] = 5;
|
||||
bad_bit_table[ 7] = 6;
|
||||
bad_bit_table[11] = 7;
|
||||
bad_bit_table[13] = 8;
|
||||
bad_bit_table[14] = 9;
|
||||
|
||||
// If the syndrome is zero, then the data is correct, no errors detected.
|
||||
bad_bit_table[ 0] = -2;
|
||||
|
||||
// Incorrect values, if the syndrome gets one of this values, then we fail to correct the data.
|
||||
bad_bit_table[ 5] = -1;
|
||||
bad_bit_table[ 6] = -1;
|
||||
bad_bit_table[ 9] = -1;
|
||||
bad_bit_table[10] = -1;
|
||||
bad_bit_table[15] = -1;
|
||||
}
|
||||
};
|
||||
|
||||
class Hamming_15_11_3_data
|
||||
{
|
||||
public:
|
||||
std::bitset<15> g0;
|
||||
std::bitset<15> g1;
|
||||
std::bitset<15> g2;
|
||||
std::bitset<15> g3;
|
||||
std::bitset<15> g4;
|
||||
std::bitset<15> g5;
|
||||
std::bitset<15> g6;
|
||||
std::bitset<15> g7;
|
||||
std::bitset<15> g8;
|
||||
std::bitset<15> g9;
|
||||
std::bitset<15> g10;
|
||||
|
||||
|
||||
std::bitset<11> gt0;
|
||||
std::bitset<11> gt1;
|
||||
std::bitset<11> gt2;
|
||||
std::bitset<11> gt3;
|
||||
|
||||
std::bitset<15> h0;
|
||||
std::bitset<15> h1;
|
||||
std::bitset<15> h2;
|
||||
std::bitset<15> h3;
|
||||
|
||||
int bad_bit_table[16];
|
||||
|
||||
Hamming_15_11_3_data()
|
||||
{
|
||||
// G matrix come from the APCO 25 reference documentation:
|
||||
g0 = std::bitset<15>(std::string("100000000001111"));
|
||||
g1 = std::bitset<15>(std::string("010000000001110"));
|
||||
g2 = std::bitset<15>(std::string("001000000001101"));
|
||||
g3 = std::bitset<15>(std::string("000100000001100"));
|
||||
g4 = std::bitset<15>(std::string("000010000001011"));
|
||||
g5 = std::bitset<15>(std::string("000001000001010"));
|
||||
g6 = std::bitset<15>(std::string("000000100001001"));
|
||||
g7 = std::bitset<15>(std::string("000000010000111"));
|
||||
g8 = std::bitset<15>(std::string("000000001000110"));
|
||||
g9 = std::bitset<15>(std::string("000000000100101"));
|
||||
g10 = std::bitset<15>(std::string("000000000010011"));
|
||||
|
||||
gt0 = std::bitset<11>(std::string("11111110000"));
|
||||
gt1 = std::bitset<11>(std::string("11110001110"));
|
||||
gt2 = std::bitset<11>(std::string("11001101101"));
|
||||
gt3 = std::bitset<11>(std::string("10101011011"));
|
||||
|
||||
// H matrix, calculated transposing G. See wikipedia.
|
||||
h0 = std::bitset<15>(std::string("111111100001000"));
|
||||
h1 = std::bitset<15>(std::string("111100011100100"));
|
||||
h2 = std::bitset<15>(std::string("110011011010010"));
|
||||
h3 = std::bitset<15>(std::string("101010110110001"));
|
||||
|
||||
// bad_bit_table indicates which is the bit to correct from the calculated syndromes.
|
||||
// It's calculated from the previous matrices: See the left-most column of the matrix whose rows are
|
||||
// h0,h1,h2 and h3. It's [1 1 1 0]. It means: when the syndrome value is the binary number 1110, the
|
||||
// bit that is wrong is the bit 9 (the left-most column is for the bit 9). Another example with
|
||||
// the right-most column: it's column is [0 0 0 1], so when the syndrome is 0001, the bit to fix is
|
||||
// bit 0 (one of the parity bits).
|
||||
bad_bit_table[ 1] = 0;
|
||||
bad_bit_table[ 2] = 1;
|
||||
bad_bit_table[ 4] = 2;
|
||||
bad_bit_table[ 8] = 3;
|
||||
bad_bit_table[12] = 4;
|
||||
bad_bit_table[ 3] = 5;
|
||||
bad_bit_table[ 7] = 6;
|
||||
bad_bit_table[11] = 7;
|
||||
bad_bit_table[13] = 8;
|
||||
bad_bit_table[14] = 9;
|
||||
|
||||
// If the syndrome is zero, then the data is correct, no errors detected.
|
||||
bad_bit_table[ 0] = -2;
|
||||
|
||||
// Incorrect values, if the syndrome gets one of this values, then we fail to correct the data.
|
||||
bad_bit_table[ 5] = -1;
|
||||
bad_bit_table[ 6] = -1;
|
||||
bad_bit_table[ 9] = -1;
|
||||
bad_bit_table[10] = -1;
|
||||
bad_bit_table[15] = -1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An interface used by the two implementations defined here.
|
||||
*/
|
||||
class Hamming_Inteface
|
||||
{
|
||||
public:
|
||||
virtual ~Hamming_Inteface()
|
||||
{
|
||||
// Does nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Decodes using the Hamming (10,6,3) algorithm a sequence of 10 bits expressed as an integer.
|
||||
* \arg input The number to decode.
|
||||
* \arg output A pointer to int where the decoded result is stored.
|
||||
* \return Count of detected errors.
|
||||
*/
|
||||
virtual int decode(int input, int* output) = 0;
|
||||
|
||||
/**
|
||||
* \brief Decodes using the Hamming (10,6,3) algorithm a sequence of 10 bits expressed as a pointer
|
||||
* to six chars (data) and a pointer to four chars (parity). Altogether ten chars, each holding one bit.
|
||||
* \arg hex A pointer to six chars. If error are detected the values are modified acordingly.
|
||||
* \arg parity A pointer to four chars. Represent the parity of the data.
|
||||
* \return Count of detected errors.
|
||||
*/
|
||||
virtual int decode(char* hex, char* parity) = 0;
|
||||
|
||||
virtual int encode(int input) = 0;
|
||||
|
||||
virtual void encode(char* hex, char* out_parity) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Hamming (10,6,3) error correction implementation.
|
||||
*/
|
||||
class Hamming_10_6_3 : public Hamming_Inteface
|
||||
{
|
||||
private:
|
||||
static Hamming_10_6_3_data data;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \brief Decodes using the Hamming (10,6,3) algorithm a sequence of 10 bits expressed as a std::bitset.
|
||||
* \arg input The sequence to decode.
|
||||
* \return Count of detected errors.
|
||||
*/
|
||||
int decode(std::bitset<10>& input);
|
||||
|
||||
int decode(int input, int* output)
|
||||
{
|
||||
assert (input < 1024 && input >= 0);
|
||||
|
||||
std::bitset<10> bitset_input(input);
|
||||
int error_count = decode(bitset_input);
|
||||
if (error_count == 1) {
|
||||
// t has possibly been modified
|
||||
input = (int)bitset_input.to_ulong();
|
||||
} else {
|
||||
// both in case that there were no errors or there were irrecoverable errors we leave the data as
|
||||
// it is
|
||||
}
|
||||
|
||||
// discard the four parity bits at the end
|
||||
*output = input >> 4;
|
||||
|
||||
return error_count;
|
||||
}
|
||||
|
||||
int decode(char* hex, char* parity)
|
||||
{
|
||||
// Make a bitset from hex and parity
|
||||
std::bitset<10> value;
|
||||
// in the bitset 9 is the left-most and 0 is the right-most
|
||||
for (unsigned int i=0; i<6; i++) {
|
||||
value[9-i] = (hex[i] == 1)? true : false;
|
||||
}
|
||||
for (unsigned int i=0; i<4; i++) {
|
||||
value[3-i] = (parity[i] == 1)? true : false;
|
||||
}
|
||||
|
||||
int error_count = decode(value);
|
||||
|
||||
// Modify hex if needed
|
||||
if (error_count == 1) {
|
||||
for (unsigned int i=0; i<6; i++) {
|
||||
hex[i] = value[9-i];
|
||||
}
|
||||
} else {
|
||||
// No errors or irrecoverable errors, in both cases don't touch the input
|
||||
}
|
||||
|
||||
return error_count;
|
||||
}
|
||||
|
||||
int encode(std::bitset<6>& input);
|
||||
|
||||
int encode(int input)
|
||||
{
|
||||
assert (input < 64 && input >= 0);
|
||||
|
||||
std::bitset<6> bitset_input(input);
|
||||
|
||||
return encode(bitset_input);
|
||||
}
|
||||
|
||||
void encode(char* hex, char* out_parity)
|
||||
{
|
||||
// Make a bitset from hex
|
||||
std::bitset<6> value;
|
||||
// in the bitset 5 is the left-most and 0 is the right-most
|
||||
for (unsigned int i=0; i<6; i++) {
|
||||
value[5-i] = (hex[i] == 1)? true : false;
|
||||
}
|
||||
|
||||
int parity = encode(value);
|
||||
|
||||
// Put the calculated parity in the form of a char array
|
||||
for (int i=3; i>=0; i--) {
|
||||
out_parity[i] = parity & 1;
|
||||
parity >>= 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Stores data for the Hamming_10_6_3_TableImpl that comes next.
|
||||
*/
|
||||
class Hamming_10_6_3_TableImpl_data
|
||||
{
|
||||
public:
|
||||
int fixed_values[1024];
|
||||
int error_counts[1024];
|
||||
int encode_parities[64];
|
||||
|
||||
/**
|
||||
* \brief On initialization it builds a table to calculate every possible outcome (there are 1024) before
|
||||
* hand. Uses Hamming_10_6_3 to build the table.
|
||||
*/
|
||||
Hamming_10_6_3_TableImpl_data()
|
||||
{
|
||||
Hamming_10_6_3 hamming;
|
||||
|
||||
// Build the tables
|
||||
for (int i=0; i<1024; i++) {
|
||||
int fixed;
|
||||
int error_count = hamming.decode(i, &fixed);
|
||||
fixed_values[i] = fixed;
|
||||
error_counts[i] = error_count;
|
||||
}
|
||||
|
||||
for (int i=0; i<64; i++) {
|
||||
int parity = hamming.encode(i);
|
||||
encode_parities[i] = parity;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Hamming (10,6,3) error correction implementation.
|
||||
*/
|
||||
class Hamming_10_6_3_TableImpl : public Hamming_Inteface
|
||||
{
|
||||
private:
|
||||
static Hamming_10_6_3_TableImpl_data data;
|
||||
|
||||
public:
|
||||
int decode(int input, int* output);
|
||||
|
||||
static int convert_hex_to_int(char* hex)
|
||||
{
|
||||
// Make an int from hex
|
||||
int value = 0;
|
||||
// in the bitset 9 is the left-most and 0 is the right-most
|
||||
for (unsigned int i=0; i<6; i++) {
|
||||
assert(hex[i] == 0 || hex[i] == 1);
|
||||
value <<= 1;
|
||||
value |= hex[i];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int convert_hex_parity_to_int(char* hex, char* parity)
|
||||
{
|
||||
// Make an int from hex and parity
|
||||
int value = 0;
|
||||
// in the bitset 9 is the left-most and 0 is the right-most
|
||||
for (unsigned int i=0; i<6; i++) {
|
||||
assert(hex[i] == 0 || hex[i] == 1);
|
||||
value <<= 1;
|
||||
value |= hex[i];
|
||||
}
|
||||
for (unsigned int i=0; i<4; i++) {
|
||||
assert(parity[i] == 0 || parity[i] == 1);
|
||||
value <<= 1;
|
||||
value |= parity[i];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void convert_int_to_hex(int value, char* hex)
|
||||
{
|
||||
unsigned int v = value;
|
||||
for (unsigned int i=0; i<6; i++) {
|
||||
hex[5-i] = v & 1;
|
||||
v >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
int decode(char* hex, char* parity)
|
||||
{
|
||||
int value = convert_hex_parity_to_int(hex, parity);
|
||||
int fixed;
|
||||
int error_count = decode(value, &fixed);
|
||||
|
||||
// Modify hex if needed
|
||||
if (error_count == 1) {
|
||||
convert_int_to_hex(fixed, hex);
|
||||
} else {
|
||||
// No errors or irrecoverable errors, in both cases don't touch the input
|
||||
}
|
||||
|
||||
return error_count;
|
||||
}
|
||||
|
||||
int encode(int input);
|
||||
|
||||
void encode(char* hex, char* out_parity)
|
||||
{
|
||||
int value = convert_hex_to_int(hex);
|
||||
int parity = encode(value);
|
||||
|
||||
// Put the calculated parity in the form of a char array
|
||||
for (int i=3; i>=0; i--) {
|
||||
out_parity[i] = parity & 1;
|
||||
parity >>= 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // HAMMING_HPP_e7123c3795b94d14b5774d5d8f016a04
|
||||
670
include/ReedSolomon.hpp
Normal file
670
include/ReedSolomon.hpp
Normal file
@@ -0,0 +1,670 @@
|
||||
|
||||
#ifndef REEDSOLOMON_HPP_b1405fdab6374ba2a4e65e8d45ec3d80
|
||||
#define REEDSOLOMON_HPP_b1405fdab6374ba2a4e65e8d45ec3d80
|
||||
|
||||
/**
|
||||
* Code taken and adapted from www.eccpage.com/rs.c
|
||||
* Credit goes to Mr Simon Rockliff.
|
||||
*
|
||||
* Tried before with the implementation from ITPP library but couldn't make it produce the same outputs
|
||||
* expected from the P25 transmissions that I have tested. This implementation does work.
|
||||
*/
|
||||
|
||||
/* This program is an encoder/decoder for Reed-Solomon codes. Encoding is in
|
||||
systematic form, decoding via the Berlekamp iterative algorithm.
|
||||
In the present form , the constants mm, nn, tt, and kk=nn-2tt must be
|
||||
specified (the double letters are used simply to avoid clashes with
|
||||
other n,k,t used in other programs into which this was incorporated!)
|
||||
Also, the irreducible polynomial used to generate GF(2**mm) must also be
|
||||
entered -- these can be found in Lin and Costello, and also Clark and Cain.
|
||||
|
||||
The representation of the elements of GF(2**m) is either in index form,
|
||||
where the number is the power of the primitive element alpha, which is
|
||||
convenient for multiplication (add the powers modulo 2**m-1) or in
|
||||
polynomial form, where the bits represent the coefficients of the
|
||||
polynomial representation of the number, which is the most convenient form
|
||||
for addition. The two forms are swapped between via lookup tables.
|
||||
This leads to fairly messy looking expressions, but unfortunately, there
|
||||
is no easy alternative when working with Galois arithmetic.
|
||||
|
||||
The code is not written in the most elegant way, but to the best
|
||||
of my knowledge, (no absolute guarantees!), it works.
|
||||
However, when including it into a simulation program, you may want to do
|
||||
some conversion of global variables (used here because I am lazy!) to
|
||||
local variables where appropriate, and passing parameters (eg array
|
||||
addresses) to the functions may be a sensible move to reduce the number
|
||||
of global variables and thus decrease the chance of a bug being introduced.
|
||||
|
||||
This program does not handle erasures at present, but should not be hard
|
||||
to adapt to do this, as it is just an adjustment to the Berlekamp-Massey
|
||||
algorithm. It also does not attempt to decode past the BCH bound -- see
|
||||
Blahut "Theory and practice of error control codes" for how to do this.
|
||||
|
||||
Simon Rockliff, University of Adelaide 21/9/89
|
||||
|
||||
26/6/91 Slight modifications to remove a compiler dependent bug which hadn't
|
||||
previously surfaced. A few extra comments added for clarity.
|
||||
Appears to all work fine, ready for posting to net!
|
||||
|
||||
Notice
|
||||
--------
|
||||
This program may be freely modified and/or given to whoever wants it.
|
||||
A condition of such distribution is that the author's contribution be
|
||||
acknowledged by his name being left in the comments heading the program,
|
||||
however no responsibility is accepted for any financial or other loss which
|
||||
may result from some unforseen errors or malfunctioning of the program
|
||||
during use.
|
||||
Simon Rockliff, 26th June 1991
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
template <int TT>
|
||||
class ReedSolomon_63
|
||||
{
|
||||
private:
|
||||
static const int MM = 6; /* RS code over GF(2**mm) */
|
||||
static const int NN = 63; /* nn=2**mm -1 length of codeword */
|
||||
//int tt; /* number of errors that can be corrected */
|
||||
//int kk; /* kk = nn-2*tt */
|
||||
static const int KK = NN-2*TT;
|
||||
// distance = nn-kk+1 = 2*tt+1
|
||||
|
||||
int* alpha_to;
|
||||
int* index_of;
|
||||
int* gg;
|
||||
|
||||
void generate_gf(int* generator_polinomial)
|
||||
/* generate GF(2**mm) from the irreducible polynomial p(X) in pp[0]..pp[mm]
|
||||
lookup tables: index->polynomial form alpha_to[] contains j=alpha**i;
|
||||
polynomial form -> index form index_of[j=alpha**i] = i
|
||||
alpha=2 is the primitive element of GF(2**mm)
|
||||
*/
|
||||
{
|
||||
register int i, mask;
|
||||
|
||||
mask = 1;
|
||||
alpha_to[MM] = 0;
|
||||
for (i = 0; i < MM; i++) {
|
||||
alpha_to[i] = mask;
|
||||
index_of[alpha_to[i]] = i;
|
||||
if (generator_polinomial[i] != 0)
|
||||
alpha_to[MM] ^= mask;
|
||||
mask <<= 1;
|
||||
}
|
||||
index_of[alpha_to[MM]] = MM;
|
||||
mask >>= 1;
|
||||
for (i = MM + 1; i < NN; i++) {
|
||||
if (alpha_to[i - 1] >= mask)
|
||||
alpha_to[i] = alpha_to[MM] ^ ((alpha_to[i - 1] ^ mask) << 1);
|
||||
else
|
||||
alpha_to[i] = alpha_to[i - 1] << 1;
|
||||
index_of[alpha_to[i]] = i;
|
||||
}
|
||||
index_of[0] = -1;
|
||||
}
|
||||
|
||||
void gen_poly()
|
||||
/* Obtain the generator polynomial of the tt-error correcting, length
|
||||
nn=(2**mm -1) Reed Solomon code from the product of (X+alpha**i), i=1..2*tt
|
||||
*/
|
||||
{
|
||||
register int i, j;
|
||||
|
||||
gg[0] = 2; /* primitive element alpha = 2 for GF(2**mm) */
|
||||
gg[1] = 1; /* g(x) = (X+alpha) initially */
|
||||
for (i = 2; i <= NN - KK; i++) {
|
||||
gg[i] = 1;
|
||||
for (j = i - 1; j > 0; j--)
|
||||
if (gg[j] != 0)
|
||||
gg[j] = gg[j - 1] ^ alpha_to[(index_of[gg[j]] + i) % NN];
|
||||
else
|
||||
gg[j] = gg[j - 1];
|
||||
gg[0] = alpha_to[(index_of[gg[0]] + i) % NN]; /* gg[0] can never be zero */
|
||||
}
|
||||
/* convert gg[] to index form for quicker encoding */
|
||||
for (i = 0; i <= NN - KK; i++)
|
||||
gg[i] = index_of[gg[i]];
|
||||
}
|
||||
|
||||
public:
|
||||
ReedSolomon_63()
|
||||
{
|
||||
alpha_to = new int[NN + 1];
|
||||
index_of = new int[NN + 1];
|
||||
gg = new int[NN - KK + 1];
|
||||
|
||||
// Polynom used in P25 is alpha**6+alpha+1
|
||||
int generator_polinomial[] = { 1, 1, 0, 0, 0, 0, 1 }; /* specify irreducible polynomial coeffts */
|
||||
|
||||
generate_gf(generator_polinomial);
|
||||
|
||||
gen_poly();
|
||||
}
|
||||
|
||||
virtual ~ReedSolomon_63()
|
||||
{
|
||||
delete[] gg;
|
||||
delete[] index_of;
|
||||
delete[] alpha_to;
|
||||
}
|
||||
|
||||
void encode(const int* data, int* bb)
|
||||
/* take the string of symbols in data[i], i=0..(k-1) and encode systematically
|
||||
to produce 2*tt parity symbols in bb[0]..bb[2*tt-1]
|
||||
data[] is input and bb[] is output in polynomial form.
|
||||
Encoding is done by using a feedback shift register with appropriate
|
||||
connections specified by the elements of gg[], which was generated above.
|
||||
Codeword is c(X) = data(X)*X**(nn-kk)+ b(X) */
|
||||
{
|
||||
register int i, j;
|
||||
int feedback;
|
||||
|
||||
for (i = 0; i < NN - KK; i++)
|
||||
bb[i] = 0;
|
||||
for (i = KK - 1; i >= 0; i--) {
|
||||
feedback = index_of[data[i] ^ bb[NN - KK - 1]];
|
||||
if (feedback != -1) {
|
||||
for (j = NN - KK - 1; j > 0; j--)
|
||||
if (gg[j] != -1)
|
||||
bb[j] = bb[j - 1] ^ alpha_to[(gg[j] + feedback) % NN];
|
||||
else
|
||||
bb[j] = bb[j - 1];
|
||||
bb[0] = alpha_to[(gg[0] + feedback) % NN];
|
||||
} else {
|
||||
for (j = NN - KK - 1; j > 0; j--)
|
||||
bb[j] = bb[j - 1];
|
||||
bb[0] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int decode(const int* input, int* recd)
|
||||
/* assume we have received bits grouped into mm-bit symbols in recd[i],
|
||||
i=0..(nn-1), and recd[i] is polynomial form.
|
||||
We first compute the 2*tt syndromes by substituting alpha**i into rec(X) and
|
||||
evaluating, storing the syndromes in s[i], i=1..2tt (leave s[0] zero) .
|
||||
Then we use the Berlekamp iteration to find the error location polynomial
|
||||
elp[i]. If the degree of the elp is >tt, we cannot correct all the errors
|
||||
and hence just put out the information symbols uncorrected. If the degree of
|
||||
elp is <=tt, we substitute alpha**i , i=1..n into the elp to get the roots,
|
||||
hence the inverse roots, the error location numbers. If the number of errors
|
||||
located does not equal the degree of the elp, we have more than tt errors
|
||||
and cannot correct them. Otherwise, we then solve for the error value at
|
||||
the error location and correct the error. The procedure is that found in
|
||||
Lin and Costello. For the cases where the number of errors is known to be too
|
||||
large to correct, the information symbols as received are output (the
|
||||
advantage of systematic encoding is that hopefully some of the information
|
||||
symbols will be okay and that if we are in luck, the errors are in the
|
||||
parity part of the transmitted codeword). Of course, these insoluble cases
|
||||
can be returned as error flags to the calling routine if desired. */
|
||||
{
|
||||
register int i, j, u, q;
|
||||
int elp[NN - KK + 2][NN - KK], d[NN - KK + 2], l[NN - KK + 2], u_lu[NN - KK
|
||||
+ 2], s[NN - KK + 1];
|
||||
int count = 0, syn_error = 0, root[TT], loc[TT], z[TT + 1], err[NN], reg[TT
|
||||
+ 1];
|
||||
|
||||
int irrecoverable_error = 0;
|
||||
|
||||
for (int i = 0; i < NN; i++)
|
||||
recd[i] = index_of[input[i]]; /* put recd[i] into index form (ie as powers of alpha) */
|
||||
|
||||
/* first form the syndromes */
|
||||
for (i = 1; i <= NN - KK; i++) {
|
||||
s[i] = 0;
|
||||
for (j = 0; j < NN; j++)
|
||||
if (recd[j] != -1)
|
||||
s[i] ^= alpha_to[(recd[j] + i * j) % NN]; /* recd[j] in index form */
|
||||
/* convert syndrome from polynomial form to index form */
|
||||
if (s[i] != 0)
|
||||
syn_error = 1; /* set flag if non-zero syndrome => error */
|
||||
s[i] = index_of[s[i]];
|
||||
}
|
||||
|
||||
if (syn_error) /* if errors, try and correct */
|
||||
{
|
||||
/* compute the error location polynomial via the Berlekamp iterative algorithm,
|
||||
following the terminology of Lin and Costello : d[u] is the 'mu'th
|
||||
discrepancy, where u='mu'+1 and 'mu' (the Greek letter!) is the step number
|
||||
ranging from -1 to 2*tt (see L&C), l[u] is the
|
||||
degree of the elp at that step, and u_l[u] is the difference between the
|
||||
step number and the degree of the elp.
|
||||
*/
|
||||
/* initialise table entries */
|
||||
d[0] = 0; /* index form */
|
||||
d[1] = s[1]; /* index form */
|
||||
elp[0][0] = 0; /* index form */
|
||||
elp[1][0] = 1; /* polynomial form */
|
||||
for (i = 1; i < NN - KK; i++) {
|
||||
elp[0][i] = -1; /* index form */
|
||||
elp[1][i] = 0; /* polynomial form */
|
||||
}
|
||||
l[0] = 0;
|
||||
l[1] = 0;
|
||||
u_lu[0] = -1;
|
||||
u_lu[1] = 0;
|
||||
u = 0;
|
||||
|
||||
do {
|
||||
u++;
|
||||
if (d[u] == -1) {
|
||||
l[u + 1] = l[u];
|
||||
for (i = 0; i <= l[u]; i++) {
|
||||
elp[u + 1][i] = elp[u][i];
|
||||
elp[u][i] = index_of[elp[u][i]];
|
||||
}
|
||||
} else
|
||||
/* search for words with greatest u_lu[q] for which d[q]!=0 */
|
||||
{
|
||||
q = u - 1;
|
||||
while ((d[q] == -1) && (q > 0))
|
||||
q--;
|
||||
/* have found first non-zero d[q] */
|
||||
if (q > 0) {
|
||||
j = q;
|
||||
do {
|
||||
j--;
|
||||
if ((d[j] != -1) && (u_lu[q] < u_lu[j]))
|
||||
q = j;
|
||||
} while (j > 0);
|
||||
};
|
||||
|
||||
/* have now found q such that d[u]!=0 and u_lu[q] is maximum */
|
||||
/* store degree of new elp polynomial */
|
||||
if (l[u] > l[q] + u - q)
|
||||
l[u + 1] = l[u];
|
||||
else
|
||||
l[u + 1] = l[q] + u - q;
|
||||
|
||||
/* form new elp(x) */
|
||||
for (i = 0; i < NN - KK; i++)
|
||||
elp[u + 1][i] = 0;
|
||||
for (i = 0; i <= l[q]; i++)
|
||||
if (elp[q][i] != -1)
|
||||
elp[u + 1][i + u - q] = alpha_to[(d[u] + NN - d[q]
|
||||
+ elp[q][i]) % NN];
|
||||
for (i = 0; i <= l[u]; i++) {
|
||||
elp[u + 1][i] ^= elp[u][i];
|
||||
elp[u][i] = index_of[elp[u][i]]; /*convert old elp value to index*/
|
||||
}
|
||||
}
|
||||
u_lu[u + 1] = u - l[u + 1];
|
||||
|
||||
/* form (u+1)th discrepancy */
|
||||
if (u < NN - KK) /* no discrepancy computed on last iteration */
|
||||
{
|
||||
if (s[u + 1] != -1)
|
||||
d[u + 1] = alpha_to[s[u + 1]];
|
||||
else
|
||||
d[u + 1] = 0;
|
||||
for (i = 1; i <= l[u + 1]; i++)
|
||||
if ((s[u + 1 - i] != -1) && (elp[u + 1][i] != 0))
|
||||
d[u + 1] ^= alpha_to[(s[u + 1 - i]
|
||||
+ index_of[elp[u + 1][i]]) % NN];
|
||||
d[u + 1] = index_of[d[u + 1]]; /* put d[u+1] into index form */
|
||||
}
|
||||
} while ((u < NN - KK) && (l[u + 1] <= TT));
|
||||
|
||||
u++;
|
||||
if (l[u] <= TT) /* can correct error */
|
||||
{
|
||||
/* put elp into index form */
|
||||
for (i = 0; i <= l[u]; i++)
|
||||
elp[u][i] = index_of[elp[u][i]];
|
||||
|
||||
/* find roots of the error location polynomial */
|
||||
for (i = 1; i <= l[u]; i++)
|
||||
reg[i] = elp[u][i];
|
||||
count = 0;
|
||||
for (i = 1; i <= NN; i++) {
|
||||
q = 1;
|
||||
for (j = 1; j <= l[u]; j++)
|
||||
if (reg[j] != -1) {
|
||||
reg[j] = (reg[j] + j) % NN;
|
||||
q ^= alpha_to[reg[j]];
|
||||
};
|
||||
if (!q) /* store root and error location number indices */
|
||||
{
|
||||
root[count] = i;
|
||||
loc[count] = NN - i;
|
||||
count++;
|
||||
};
|
||||
};
|
||||
|
||||
if (count == l[u]) /* no. roots = degree of elp hence <= tt errors */
|
||||
{
|
||||
/* form polynomial z(x) */
|
||||
for (i = 1; i <= l[u]; i++) /* Z[0] = 1 always - do not need */
|
||||
{
|
||||
if ((s[i] != -1) && (elp[u][i] != -1))
|
||||
z[i] = alpha_to[s[i]] ^ alpha_to[elp[u][i]];
|
||||
else if ((s[i] != -1) && (elp[u][i] == -1))
|
||||
z[i] = alpha_to[s[i]];
|
||||
else if ((s[i] == -1) && (elp[u][i] != -1))
|
||||
z[i] = alpha_to[elp[u][i]];
|
||||
else
|
||||
z[i] = 0;
|
||||
for (j = 1; j < i; j++)
|
||||
if ((s[j] != -1) && (elp[u][i - j] != -1))
|
||||
z[i] ^= alpha_to[(elp[u][i - j] + s[j]) % NN];
|
||||
z[i] = index_of[z[i]]; /* put into index form */
|
||||
};
|
||||
|
||||
/* evaluate errors at locations given by error location numbers loc[i] */
|
||||
for (i = 0; i < NN; i++) {
|
||||
err[i] = 0;
|
||||
if (recd[i] != -1) /* convert recd[] to polynomial form */
|
||||
recd[i] = alpha_to[recd[i]];
|
||||
else
|
||||
recd[i] = 0;
|
||||
}
|
||||
for (i = 0; i < l[u]; i++) /* compute numerator of error term first */
|
||||
{
|
||||
err[loc[i]] = 1; /* accounts for z[0] */
|
||||
for (j = 1; j <= l[u]; j++)
|
||||
if (z[j] != -1)
|
||||
err[loc[i]] ^= alpha_to[(z[j] + j * root[i]) % NN];
|
||||
if (err[loc[i]] != 0) {
|
||||
err[loc[i]] = index_of[err[loc[i]]];
|
||||
q = 0; /* form denominator of error term */
|
||||
for (j = 0; j < l[u]; j++)
|
||||
if (j != i)
|
||||
q += index_of[1
|
||||
^ alpha_to[(loc[j] + root[i]) % NN]];
|
||||
q = q % NN;
|
||||
err[loc[i]] = alpha_to[(err[loc[i]] - q + NN) % NN];
|
||||
recd[loc[i]] ^= err[loc[i]]; /*recd[i] must be in polynomial form */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* no. roots != degree of elp => >tt errors and cannot solve */
|
||||
irrecoverable_error = 1;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* elp has degree >tt hence cannot solve */
|
||||
irrecoverable_error = 1;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* no non-zero syndromes => no errors: output received codeword */
|
||||
for (i = 0; i < NN; i++)
|
||||
if (recd[i] != -1) /* convert recd[] to polynomial form */
|
||||
recd[i] = alpha_to[recd[i]];
|
||||
else
|
||||
recd[i] = 0;
|
||||
}
|
||||
|
||||
if (irrecoverable_error) {
|
||||
for (i = 0; i < NN; i++) /* could return error flag if desired */
|
||||
if (recd[i] != -1) /* convert recd[] to polynomial form */
|
||||
recd[i] = alpha_to[recd[i]];
|
||||
else
|
||||
recd[i] = 0; /* just output received codeword as is */
|
||||
}
|
||||
|
||||
return irrecoverable_error;
|
||||
}
|
||||
|
||||
protected:
|
||||
int bin_to_hex(const char* input)
|
||||
{
|
||||
int output = ((input[0] != 0)? 32 : 0) |
|
||||
((input[1] != 0)? 16 : 0) |
|
||||
((input[2] != 0)? 8 : 0) |
|
||||
((input[3] != 0)? 4 : 0) |
|
||||
((input[4] != 0)? 2 : 0) |
|
||||
((input[5] != 0)? 1 : 0);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void hex_to_bin(int input, char* output)
|
||||
{
|
||||
output[0] = ((input & 32) != 0)? 1 : 0;
|
||||
output[1] = ((input & 16) != 0)? 1 : 0;
|
||||
output[2] = ((input & 8) != 0)? 1 : 0;
|
||||
output[3] = ((input & 4) != 0)? 1 : 0;
|
||||
output[4] = ((input & 2) != 0)? 1 : 0;
|
||||
output[5] = ((input & 1) != 0)? 1 : 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience class that does a Reed-Solomon (36,20,17) error correction adapting input and output to
|
||||
* the DSD data format: hex words packed as char arrays.
|
||||
*/
|
||||
class DSDReedSolomon_36_20_17 : public ReedSolomon_63<8>
|
||||
{
|
||||
public:
|
||||
// tt = (dd-1)/2
|
||||
// dd = 17 --> tt = 8
|
||||
DSDReedSolomon_36_20_17() : ReedSolomon_63<8>()
|
||||
{
|
||||
// Does nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a Reed-Solomon decode adapting the input and output to the expected DSD data format.
|
||||
* \param hex_data Data packed bits, originally a char[20][6], so containing 20 hex works, each char
|
||||
* is a bit. Bits are corrected in place.
|
||||
* \param hex_parity Parity packed bits, originally a char[16][6], 16 hex words.
|
||||
* \return 1 if irrecoverable errors have been detected, 0 otherwise.
|
||||
*/
|
||||
int decode(char* hex_data, const char* hex_parity)
|
||||
{
|
||||
int input[63];
|
||||
int output[63];
|
||||
|
||||
// First put the parity data, 16 hex words
|
||||
for(unsigned int i=0; i<16; i++) {
|
||||
input[i] = bin_to_hex(hex_parity + i*6);
|
||||
}
|
||||
|
||||
// Then the 20 hex words of data
|
||||
for(unsigned int i=16; i<16+20; i++) {
|
||||
input[i] = bin_to_hex(hex_data + (i-16)*6);
|
||||
}
|
||||
|
||||
// Fill up with zeros to complete the 47 expected hex words of data
|
||||
for(unsigned int i=16+20; i<63; i++) {
|
||||
input[i] = 0;
|
||||
}
|
||||
|
||||
// Now we can call decode on the base class
|
||||
int irrecoverable_errors = ReedSolomon_63<8>::decode(input, output);
|
||||
|
||||
// Convert it back to binary and put it into hex_data. If decode failed we should have
|
||||
// the input unchanged.
|
||||
for(unsigned int i=16; i<16+20; i++) {
|
||||
hex_to_bin(output[i], hex_data + (i-16)*6);
|
||||
}
|
||||
|
||||
return irrecoverable_errors;
|
||||
}
|
||||
|
||||
void encode(const char* hex_data, char* out_hex_parity)
|
||||
{
|
||||
int input[47];
|
||||
int output[63];
|
||||
|
||||
// Put the 20 hex words of data
|
||||
for(unsigned int i=0; i<20; i++) {
|
||||
input[i] = bin_to_hex(hex_data + i*6);
|
||||
}
|
||||
|
||||
// Fill up with zeros to complete the 47 expected hex words of data
|
||||
for(unsigned int i=20; i<47; i++) {
|
||||
input[i] = 0;
|
||||
}
|
||||
|
||||
// Now we can call encode on the base class
|
||||
ReedSolomon_63<8>::encode(input, output);
|
||||
|
||||
// Convert it back to binary form and put it into the parity
|
||||
for(unsigned int i=0; i<16; i++) {
|
||||
hex_to_bin(output[i], out_hex_parity + i*6);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience class that does a Reed-Solomon (24,12,13) error correction adapting input and output to
|
||||
* the DSD data format: hex words packed as char arrays.
|
||||
*/
|
||||
class DSDReedSolomon_24_12_13 : public ReedSolomon_63<6>
|
||||
{
|
||||
public:
|
||||
// tt = (dd-1)/2
|
||||
// dd = 13 --> tt = 6
|
||||
DSDReedSolomon_24_12_13() : ReedSolomon_63<6>()
|
||||
{
|
||||
// Does nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a Reed-Solomon decode adapting the input and output to the expected DSD data format.
|
||||
* \param hex_data Data packed bits, originally a char[12][6], so containing 12 hex works, each char
|
||||
* is a bit. Bits are corrected in place.
|
||||
* \param hex_parity Parity packed bits, originally a char[12][6], 12 hex words.
|
||||
* \return 1 if irrecoverable errors have been detected, 0 otherwise.
|
||||
*/
|
||||
int decode(char* hex_data, const char* hex_parity)
|
||||
{
|
||||
int input[63];
|
||||
int output[63];
|
||||
|
||||
// First put the parity data, 12 hex words
|
||||
for(unsigned int i=0; i<12; i++) {
|
||||
input[i] = bin_to_hex(hex_parity + i*6);
|
||||
}
|
||||
|
||||
// Then the 12 hex words of data
|
||||
for(unsigned int i=12; i<12+12; i++) {
|
||||
input[i] = bin_to_hex(hex_data + (i-12)*6);
|
||||
}
|
||||
|
||||
// Fill up with zeros to complete the 51 expected hex words of data
|
||||
for(unsigned int i=12+12; i<63; i++) {
|
||||
input[i] = 0;
|
||||
}
|
||||
|
||||
// Now we can call decode on the base class
|
||||
int irrecoverable_errors = ReedSolomon_63<6>::decode(input, output);
|
||||
|
||||
// Convert it back to binary and put it into hex_data. If decode failed we should have
|
||||
// the input unchanged.
|
||||
for(unsigned int i=12; i<12+12; i++) {
|
||||
hex_to_bin(output[i], hex_data + (i-12)*6);
|
||||
}
|
||||
|
||||
return irrecoverable_errors;
|
||||
}
|
||||
|
||||
void encode(const char* hex_data, char* out_hex_parity)
|
||||
{
|
||||
int input[51];
|
||||
int output[63];
|
||||
|
||||
// Put the 12 hex words of data
|
||||
for(unsigned int i=0; i<12; i++) {
|
||||
input[i] = bin_to_hex(hex_data + i*6);
|
||||
}
|
||||
|
||||
// Fill up with zeros to complete the 51 expected hex words of data
|
||||
for(unsigned int i=12; i<51; i++) {
|
||||
input[i] = 0;
|
||||
}
|
||||
|
||||
// Now we can call encode on the base class
|
||||
ReedSolomon_63<6>::encode(input, output);
|
||||
|
||||
// Convert it back to binary form and put it into the parity
|
||||
for(unsigned int i=0; i<12; i++) {
|
||||
hex_to_bin(output[i], out_hex_parity + i*6);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience class that does a Reed-Solomon (24,16,9) error correction adapting input and output to
|
||||
* the DSD data format: hex words packed as char arrays.
|
||||
*/
|
||||
class DSDReedSolomon_24_16_9 : public ReedSolomon_63<4>
|
||||
{
|
||||
public:
|
||||
// tt = (dd-1)/2
|
||||
// dd = 9 --> tt = 4
|
||||
DSDReedSolomon_24_16_9() : ReedSolomon_63<4>()
|
||||
{
|
||||
// Does nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a Reed-Solomon decode adapting the input and output to the expected DSD data format.
|
||||
* \param hex_data Data packed bits, originally a char[16][6], so containing 16 hex works, each char
|
||||
* is a bit. Bits are corrected in place.
|
||||
* \param hex_parity Parity packed bits, originally a char[8][6], 8 hex words.
|
||||
* \return 1 if irrecoverable errors have been detected, 0 otherwise.
|
||||
*/
|
||||
int decode(char* hex_data, const char* hex_parity)
|
||||
{
|
||||
int input[63];
|
||||
int output[63];
|
||||
|
||||
// First put the parity data, 8 hex words
|
||||
for(unsigned int i=0; i<8; i++) {
|
||||
input[i] = bin_to_hex(hex_parity + i*6);
|
||||
}
|
||||
|
||||
// Then the 16 hex words of data
|
||||
for(unsigned int i=8; i<8+16; i++) {
|
||||
input[i] = bin_to_hex(hex_data + (i-8)*6);
|
||||
}
|
||||
|
||||
// Fill up with zeros to complete the 55 expected hex words of data
|
||||
for(unsigned int i=8+16; i<63; i++) {
|
||||
input[i] = 0;
|
||||
}
|
||||
|
||||
// Now we can call decode on the base class
|
||||
int irrecoverable_errors = ReedSolomon_63<4>::decode(input, output);
|
||||
|
||||
// Convert it back to binary and put it into hex_data. If decode failed we should have
|
||||
// the input unchanged.
|
||||
for(unsigned int i=8; i<8+16; i++) {
|
||||
hex_to_bin(output[i], hex_data + (i-8)*6);
|
||||
}
|
||||
|
||||
return irrecoverable_errors;
|
||||
}
|
||||
|
||||
void encode(const char* hex_data, char* out_hex_parity)
|
||||
{
|
||||
int input[55];
|
||||
int output[63];
|
||||
|
||||
// Put the 16 hex words of data
|
||||
for(unsigned int i=0; i<16; i++) {
|
||||
input[i] = bin_to_hex(hex_data + i*6);
|
||||
}
|
||||
|
||||
// Fill up with zeros to complete the 55 expected hex words of data
|
||||
for(unsigned int i=16; i<55; i++) {
|
||||
input[i] = 0;
|
||||
}
|
||||
|
||||
// Now we can call encode on the base class
|
||||
ReedSolomon_63<4>::encode(input, output);
|
||||
|
||||
// Convert it back to binary form and put it into the parity
|
||||
for(unsigned int i=0; i<8; i++) {
|
||||
hex_to_bin(output[i], out_hex_parity + i*6);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // REEDSOLOMON_HPP_b1405fdab6374ba2a4e65e8d45ec3d80
|
||||
298
include/descramble.h
Normal file
298
include/descramble.h
Normal file
@@ -0,0 +1,298 @@
|
||||
/* descramble.h */
|
||||
|
||||
// Functions for processing the radio-header:
|
||||
// descramble
|
||||
// deinterleave
|
||||
// FECdecoder
|
||||
|
||||
// (C) 2011 Jonathan Naylor G4KLX
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
// This code was originally written by JOnathan Naylor, G4KLX, as part
|
||||
// of the "pcrepeatercontroller" project
|
||||
// More info:
|
||||
// http://groups.yahoo.com/group/pcrepeatercontroller
|
||||
|
||||
|
||||
|
||||
// Changes:
|
||||
// Convert C++ to C
|
||||
|
||||
// Version 20111106: initial release
|
||||
|
||||
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
// function traceBack
|
||||
int traceBack (int * out, int * m_pathMemory0, int * m_pathMemory1, int * m_pathMemory2, int * m_pathMemory3) {
|
||||
enum FEC_STATE { S0, S1, S2, S3 } state;
|
||||
int loop;
|
||||
int length=0;
|
||||
|
||||
state=S0;
|
||||
|
||||
for (loop=329; loop >= 0; loop--, length++) {
|
||||
|
||||
switch (state) {
|
||||
case S0: // if state S0
|
||||
if (m_pathMemory0[loop]) {
|
||||
state = S2; // lower path
|
||||
} else {
|
||||
state = S0; // upper path
|
||||
}; // end else - if
|
||||
out[loop]=0;
|
||||
break;
|
||||
|
||||
case S1: // if state S1
|
||||
if (m_pathMemory1[loop]) {
|
||||
state = S2; // lower path
|
||||
} else {
|
||||
state = S0; // upper path
|
||||
}; // end else - if
|
||||
out[loop]=1;
|
||||
break;
|
||||
|
||||
case S2: // if state S2
|
||||
if (m_pathMemory2[loop]) {
|
||||
state = S3; // lower path
|
||||
} else {
|
||||
state = S1; // upper path
|
||||
}; // end else - if
|
||||
out[loop]=0;
|
||||
break;
|
||||
|
||||
case S3: // if state S3
|
||||
if (m_pathMemory3[loop]) {
|
||||
state = S3; // lower path
|
||||
} else {
|
||||
state = S1; // upper path
|
||||
}; // end else - if
|
||||
out[loop]=1;
|
||||
break;
|
||||
|
||||
}; // end switch
|
||||
}; // end for
|
||||
|
||||
return(length);
|
||||
}; // end function
|
||||
|
||||
|
||||
|
||||
// function viterbiDecode
|
||||
|
||||
void viterbiDecode (int n, int *data, int *m_pathMemory0, int *m_pathMemory1, int *m_pathMemory2, int *m_pathMemory3, int *m_pathMetric) {
|
||||
int tempMetric[4];
|
||||
int metric[8];
|
||||
int loop;
|
||||
|
||||
int m1;
|
||||
int m2;
|
||||
|
||||
metric[0]=(data[1]^0)+(data[0]^0);
|
||||
metric[1]=(data[1]^1)+(data[0]^1);
|
||||
metric[2]=(data[1]^1)+(data[0]^0);
|
||||
metric[3]=(data[1]^0)+(data[0]^1);
|
||||
metric[4]=(data[1]^1)+(data[0]^1);
|
||||
metric[5]=(data[1]^0)+(data[0]^0);
|
||||
metric[6]=(data[1]^0)+(data[0]^1);
|
||||
metric[7]=(data[1]^1)+(data[0]^0);
|
||||
|
||||
// Pres. state = S0, Prev. state = S0 & S2
|
||||
m1=metric[0]+m_pathMetric[0];
|
||||
m2=metric[4]+m_pathMetric[2];
|
||||
if (m1<m2) {
|
||||
m_pathMemory0[n]=0;
|
||||
tempMetric[0]=m1;
|
||||
} else {
|
||||
m_pathMemory0[n]=1;
|
||||
tempMetric[0]=m2;
|
||||
}; // end else - if
|
||||
|
||||
// Pres. state = S1, Prev. state = S0 & S2
|
||||
m1=metric[1]+m_pathMetric[0];
|
||||
m2=metric[5]+m_pathMetric[2];
|
||||
if (m1<m2) {
|
||||
m_pathMemory1[n]=0;
|
||||
tempMetric[1]=m1;
|
||||
} else {
|
||||
m_pathMemory1[n]=1;
|
||||
tempMetric[1]=m2;
|
||||
}; // end else - if
|
||||
|
||||
// Pres. state = S2, Prev. state = S2 & S3
|
||||
m1=metric[2]+m_pathMetric[1];
|
||||
m2=metric[6]+m_pathMetric[3];
|
||||
if (m1<m2) {
|
||||
m_pathMemory2[n]=0;
|
||||
tempMetric[2]=m1;
|
||||
} else {
|
||||
m_pathMemory2[n]=1;
|
||||
tempMetric[2]=m2;
|
||||
}
|
||||
|
||||
// Pres. state = S3, Prev. state = S1 & S3
|
||||
m1=metric[3]+m_pathMetric[1];
|
||||
m2=metric[7]+m_pathMetric[3];
|
||||
if (m1 < m2) {
|
||||
m_pathMemory3[n]=0;
|
||||
tempMetric[3]=m1;
|
||||
} else {
|
||||
m_pathMemory3[n]=1;
|
||||
tempMetric[3]=m2;
|
||||
}; // end else - if
|
||||
|
||||
for (loop=0;loop<4;loop++) {
|
||||
m_pathMetric[loop]=tempMetric[loop];
|
||||
}; // end for
|
||||
|
||||
}; // end function ViterbiDecode
|
||||
|
||||
|
||||
// function FECdecoder
|
||||
// returns outlen
|
||||
int FECdecoder (int * in, int * out) {
|
||||
int outLen;
|
||||
|
||||
int m_pathMemory0[330];
|
||||
int m_pathMemory1[330];
|
||||
int m_pathMemory2[330];
|
||||
int m_pathMemory3[330];
|
||||
int m_pathMetric[4];
|
||||
|
||||
int loop,loop2;
|
||||
|
||||
int n=0;
|
||||
|
||||
memset(m_pathMemory0,0,330*sizeof(int));
|
||||
memset(m_pathMemory1,0,330*sizeof(int));
|
||||
memset(m_pathMemory2,0,330*sizeof(int));
|
||||
memset(m_pathMemory3,0,330*sizeof(int));
|
||||
|
||||
for (loop=0;loop<4;loop++) {
|
||||
m_pathMetric[loop]=0;
|
||||
}; // end for
|
||||
|
||||
|
||||
for (loop2=0;loop2<660;loop2+=2, n++) {
|
||||
int data[2];
|
||||
|
||||
if (in[loop2]) {
|
||||
data[1]=1;
|
||||
} else {
|
||||
data[1]=0;
|
||||
}; // end else - if
|
||||
|
||||
if (in[loop2+1]) {
|
||||
data[0]=1;
|
||||
} else {
|
||||
data[0]=0;
|
||||
}; // end else - if
|
||||
|
||||
viterbiDecode(n, data, m_pathMemory0, m_pathMemory1, m_pathMemory2, m_pathMemory3, m_pathMetric);
|
||||
}; // end for
|
||||
|
||||
outLen=traceBack(out, m_pathMemory0, m_pathMemory1, m_pathMemory2, m_pathMemory3);
|
||||
|
||||
// Swap endian-ness
|
||||
// code removed (done converting bits into octets), done in main program
|
||||
|
||||
//for (loop=0;loop<330;loop+=8) {
|
||||
// int temp;
|
||||
// temp=out[loop];out[loop]=out[loop+7];out[loop+7]=temp;
|
||||
// temp=out[loop+1];out[loop+1]=out[loop+6];out[loop+6]=temp;
|
||||
// temp=out[loop+2];out[loop+2]=out[loop+5];out[loop+5]=temp;
|
||||
// temp=out[loop+3];out[loop+3]=out[loop+4];out[loop+4]=temp;
|
||||
//}
|
||||
|
||||
return(outLen);
|
||||
|
||||
}; // end function FECdecoder
|
||||
|
||||
|
||||
// function deinterleave
|
||||
void deinterleave (int * in, int * out) {
|
||||
|
||||
int k=0;
|
||||
int loop=0;
|
||||
// function starts here
|
||||
|
||||
// init vars
|
||||
k=0;
|
||||
|
||||
for (loop=0;loop<660;loop++) {
|
||||
out[k]=in[loop];
|
||||
|
||||
k += 24;
|
||||
|
||||
if (k >= 672) {
|
||||
k -= 671;
|
||||
} else if (k >= 660) {
|
||||
k -= 647;
|
||||
}; // end elsif - if
|
||||
}; // end for
|
||||
|
||||
}; // end function deinterleave
|
||||
|
||||
|
||||
|
||||
/// function scramble
|
||||
|
||||
void scramble (int * in,int * out) {
|
||||
|
||||
static const int SCRAMBLER_TABLE_BITS[] = {
|
||||
0,0,0,0,1,1,1,0,1,1,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,
|
||||
0,0,1,0,0,1,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,
|
||||
1,1,0,1,0,1,0,0,1,1,1,0,0,1,1,1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,0,
|
||||
1,1,1,1,1,0,1,0,0,1,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,0,
|
||||
0,0,0,1,1,1,0,1,1,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,
|
||||
0,1,0,0,1,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,1,
|
||||
1,0,1,0,1,0,0,1,1,1,0,0,1,1,1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,0,1,
|
||||
1,1,1,1,0,1,0,0,1,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,
|
||||
0,0,1,1,1,0,1,1,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,
|
||||
1,0,0,1,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,1,1,
|
||||
0,1,0,1,0,0,1,1,1,0,0,1,1,1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,0,1,1,
|
||||
1,1,1,0,1,0,0,1,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,
|
||||
0,1,1,1,0,1,1,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,1,
|
||||
0,0,1,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,1,1,0,
|
||||
1,0,1,0,0,1,1,1,0,0,1,1,1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,0,1,1,1,
|
||||
1,1,0,1,0,0,1,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,
|
||||
1,1,1,0,1,1,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,
|
||||
0,1,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,1,1,0,1,
|
||||
0,1,0,0,1,1,1,0,0,1,1,1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,0,1,1,1,1,
|
||||
1,0,1,0,0,1,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,
|
||||
1,1,0,1,1,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,
|
||||
1,1,0,0,0,1,0,1,1,1,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,1,1,0,1,0,
|
||||
1,0,0,1,1,1,0,0,1,1,1,1,0,1,1,0};
|
||||
|
||||
const int SCRAMBLER_TABLE_BITS_LENGTH=720;
|
||||
|
||||
int loop=0;
|
||||
int m_count=0;
|
||||
|
||||
|
||||
for (loop=0; loop < 660; loop++) {
|
||||
out[loop] = in[loop] ^ SCRAMBLER_TABLE_BITS[m_count++];
|
||||
|
||||
if (m_count >= SCRAMBLER_TABLE_BITS_LENGTH) {
|
||||
m_count = 0U;
|
||||
}; // end if
|
||||
}; // end for
|
||||
|
||||
}; // end function scramble
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -15,19 +15,25 @@
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* NXDN AMBE interleave schedule
|
||||
*/
|
||||
|
||||
//#ifdef __cplusplus
|
||||
//extern "C" {
|
||||
//#endif
|
||||
|
||||
|
||||
#ifndef _MAIN
|
||||
extern const int nW[36];
|
||||
extern const int nX[36];
|
||||
extern const int nY[36];
|
||||
extern const int nZ[36];
|
||||
extern const int dPmrW[36];
|
||||
extern const int dPmrX[36];
|
||||
extern const int dPmrY[36];
|
||||
extern const int dPmrZ[36];
|
||||
|
||||
#else
|
||||
|
||||
const int nW[36] = { 0, 1, 0, 1, 0, 1,
|
||||
//#else
|
||||
/*
|
||||
* dPMR AMBE interleave schedule
|
||||
*/
|
||||
// bit 1
|
||||
const int dPmrW[36] = {
|
||||
0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 2,
|
||||
@@ -35,7 +41,8 @@ const int nW[36] = { 0, 1, 0, 1, 0, 1,
|
||||
0, 2, 0, 2, 0, 2
|
||||
};
|
||||
|
||||
const int nX[36] = { 23, 10, 22, 9, 21, 8,
|
||||
const int dPmrX[36] = {
|
||||
23, 10, 22, 9, 21, 8,
|
||||
20, 7, 19, 6, 18, 5,
|
||||
17, 4, 16, 3, 15, 2,
|
||||
14, 1, 13, 0, 12, 10,
|
||||
@@ -43,7 +50,9 @@ const int nX[36] = { 23, 10, 22, 9, 21, 8,
|
||||
8, 6, 7, 5, 6, 4
|
||||
};
|
||||
|
||||
const int nY[36] = { 0, 2, 0, 2, 0, 2,
|
||||
// bit 0
|
||||
const int dPmrY[36] = {
|
||||
0, 2, 0, 2, 0, 2,
|
||||
0, 2, 0, 3, 0, 3,
|
||||
1, 3, 1, 3, 1, 3,
|
||||
1, 3, 1, 3, 1, 3,
|
||||
@@ -51,7 +60,8 @@ const int nY[36] = { 0, 2, 0, 2, 0, 2,
|
||||
1, 3, 1, 3, 1, 3
|
||||
};
|
||||
|
||||
const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
||||
const int dPmrZ[36] = {
|
||||
5, 3, 4, 2, 3, 1,
|
||||
2, 0, 1, 13, 0, 12,
|
||||
22, 11, 21, 10, 20, 9,
|
||||
19, 8, 18, 7, 17, 6,
|
||||
@@ -60,3 +70,7 @@ const int nZ[36] = { 5, 3, 4, 2, 3, 1,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//#ifdef __cplusplus
|
||||
//}
|
||||
//#endif
|
||||
1046
include/dsd.h
Normal file
1046
include/dsd.h
Normal file
File diff suppressed because it is too large
Load Diff
129
include/dstar_const.h
Normal file
129
include/dstar_const.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* dstar interleave experiments
|
||||
*/
|
||||
|
||||
#ifndef _MAIN
|
||||
|
||||
extern const int dW[72];
|
||||
extern const int dX[72];
|
||||
|
||||
#else
|
||||
const int dW[72] = {
|
||||
|
||||
// 0-11
|
||||
0, 0,
|
||||
3, 2,
|
||||
1, 1,
|
||||
0, 0,
|
||||
1, 1,
|
||||
0, 0,
|
||||
|
||||
// 12-23
|
||||
3, 2,
|
||||
1, 1,
|
||||
3, 2,
|
||||
1, 1,
|
||||
0, 0,
|
||||
3, 2,
|
||||
|
||||
// 24-35
|
||||
0, 0,
|
||||
3, 2,
|
||||
1, 1,
|
||||
0, 0,
|
||||
1, 1,
|
||||
0, 0,
|
||||
|
||||
// 36-47
|
||||
3, 2,
|
||||
1, 1,
|
||||
3, 2,
|
||||
1, 1,
|
||||
0, 0,
|
||||
3, 2,
|
||||
|
||||
// 48-59
|
||||
0, 0,
|
||||
3, 2,
|
||||
1, 1,
|
||||
0, 0,
|
||||
1, 1,
|
||||
0, 0,
|
||||
|
||||
// 60-71
|
||||
3, 2,
|
||||
1, 1,
|
||||
3, 3,
|
||||
2, 1,
|
||||
0, 0,
|
||||
3, 3,
|
||||
};
|
||||
const int dX[72] = {
|
||||
|
||||
// 0-11
|
||||
10, 22,
|
||||
11, 9,
|
||||
10, 22,
|
||||
11, 23,
|
||||
8, 20,
|
||||
9, 21,
|
||||
|
||||
// 12-23
|
||||
10, 8,
|
||||
9, 21,
|
||||
8, 6,
|
||||
7, 19,
|
||||
8, 20,
|
||||
9, 7,
|
||||
|
||||
// 24-35
|
||||
6, 18,
|
||||
7, 5,
|
||||
6, 18,
|
||||
7, 19,
|
||||
4, 16,
|
||||
5, 17,
|
||||
|
||||
// 36-47
|
||||
6, 4,
|
||||
5, 17,
|
||||
4, 2,
|
||||
3, 15,
|
||||
4, 16,
|
||||
5, 3,
|
||||
|
||||
// 48-59
|
||||
2, 14,
|
||||
3, 1,
|
||||
2, 14,
|
||||
3, 15,
|
||||
0, 12,
|
||||
1, 13,
|
||||
|
||||
// 60-71
|
||||
2, 0,
|
||||
1, 13,
|
||||
0, 12,
|
||||
10, 11,
|
||||
0, 12,
|
||||
1, 13,
|
||||
};
|
||||
|
||||
#endif
|
||||
6
include/dstar_header.h
Normal file
6
include/dstar_header.h
Normal file
@@ -0,0 +1,6 @@
|
||||
/* This is the header file for dstar_header.c, which is under the GPL. */
|
||||
|
||||
#ifndef _DSTAR_HEADER_H
|
||||
#define _DSTAR_HEADER_H
|
||||
//void dstar_header_decode(int radioheaderbuffer[660]);
|
||||
#endif /* _DSTAR_HEADER_H */
|
||||
89
include/fcs.h
Normal file
89
include/fcs.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* fcs.h */
|
||||
|
||||
// Viterbi decoder using Traceback method.
|
||||
|
||||
// Original Source was written by Sho Tamaoki and Tom Wada
|
||||
// See http://www.lsi.ie.u-ryukyu.ac.jp/~sho/midterm/
|
||||
// Modified by Satoshi Yasuda 7m3tjz/ad6gz
|
||||
// Modified by Jonathan Nayor, G4KLX (C) 2009
|
||||
|
||||
// Converted from C++ to C by Kristoff Bonne, ON1ARF
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010 by Kristoff Bonne, ON1ARF
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static const unsigned short ccittTab[] = {
|
||||
0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,
|
||||
0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
|
||||
0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,
|
||||
0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876,
|
||||
0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,
|
||||
0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5,
|
||||
0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c,
|
||||
0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974,
|
||||
0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb,
|
||||
0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3,
|
||||
0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a,
|
||||
0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72,
|
||||
0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9,
|
||||
0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1,
|
||||
0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738,
|
||||
0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70,
|
||||
0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7,
|
||||
0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff,
|
||||
0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036,
|
||||
0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e,
|
||||
0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5,
|
||||
0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd,
|
||||
0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134,
|
||||
0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c,
|
||||
0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3,
|
||||
0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb,
|
||||
0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232,
|
||||
0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a,
|
||||
0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1,
|
||||
0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9,
|
||||
0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,
|
||||
0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78};
|
||||
|
||||
|
||||
uint16_t calc_fcs (unsigned char * dvstartframe, int size) {
|
||||
// this function calculated the CRC-values of a DSTAR digital
|
||||
// voice frame. It calculates this value on octets 0 up to 38 of the D-STAR
|
||||
// radio header (fields flag1, flag2, flag3, destination, departure, companion,
|
||||
// own1 and own2)
|
||||
uint16_t m_crc;
|
||||
int loop;
|
||||
unsigned short tmp;
|
||||
|
||||
m_crc=0xFFFF;
|
||||
|
||||
for (loop=0; loop < size; loop++) {
|
||||
tmp = (m_crc & 0x00ff) ^ dvstartframe[loop];
|
||||
|
||||
m_crc = (m_crc >> 8) ^ ccittTab[tmp];
|
||||
|
||||
}; // end for
|
||||
|
||||
// calculate and save crc-value in fields 54 and 55 of dvframe
|
||||
m_crc = ~m_crc;
|
||||
|
||||
tmp = m_crc;
|
||||
m_crc = (m_crc << 8) | (tmp >> 8 & 0xFF);
|
||||
|
||||
|
||||
// done
|
||||
return(m_crc);
|
||||
}; // end function
|
||||
1448
include/fec.h
Normal file
1448
include/fec.h
Normal file
File diff suppressed because it is too large
Load Diff
1
include/git_ver.h
Normal file
1
include/git_ver.h
Normal file
@@ -0,0 +1 @@
|
||||
extern const char GIT_TAG[];
|
||||
82
include/nxdn_const.h
Normal file
82
include/nxdn_const.h
Normal file
@@ -0,0 +1,82 @@
|
||||
// Portions Below from Osmocom OP25
|
||||
// NXDN Encoder (C) Copyright 2019 Max H. Parke KA1RBI
|
||||
// thx gr-ysf fr_vch_decoder_bb_impl.cc * Copyright 2015 Mathias Weyland *
|
||||
// OP25 is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3, or (at your option)
|
||||
// any later version.
|
||||
//
|
||||
// OP25 is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with OP25; see the file COPYING. If not, write to the Free
|
||||
// Software Foundation, Inc., 51 Franklin Street, Boston, MA
|
||||
// 02110-1301, USA.
|
||||
|
||||
static const uint8_t PERM_12_5[] = {
|
||||
0,12,24,36,48,
|
||||
1,13,25,37,49,
|
||||
2,14,26,38,50,
|
||||
3,15,27,39,51,
|
||||
4,16,28,40,52,
|
||||
5,17,29,41,53,
|
||||
6,18,30,42,54,
|
||||
7,19,31,43,55,
|
||||
8,20,32,44,56,
|
||||
9,21,33,45,57,
|
||||
10,22,34,46,58,
|
||||
11,23,35,47,59
|
||||
};
|
||||
|
||||
static const uint8_t PERM_16_9[] = {
|
||||
0, 16, 32, 48, 64, 80, 96, 112, 128,
|
||||
1, 17, 33, 49, 65, 81, 97, 113, 129,
|
||||
2, 18, 34, 50, 66, 82, 98, 114, 130,
|
||||
3, 19, 35, 51, 67, 83, 99, 115, 131,
|
||||
4, 20, 36, 52, 68, 84, 100, 116, 132,
|
||||
5, 21, 37, 53, 69, 85, 101, 117, 133,
|
||||
6, 22, 38, 54, 70, 86, 102, 118, 134,
|
||||
7, 23, 39, 55, 71, 87, 103, 119, 135,
|
||||
8, 24, 40, 56, 72, 88, 104, 120, 136,
|
||||
9, 25, 41, 57, 73, 89, 105, 121, 137,
|
||||
10, 26, 42, 58, 74, 90, 106, 122, 138,
|
||||
11, 27, 43, 59, 75, 91, 107, 123, 139,
|
||||
12, 28, 44, 60, 76, 92, 108, 124, 140,
|
||||
13, 29, 45, 61, 77, 93, 109, 125, 141,
|
||||
14, 30, 46, 62, 78, 94, 110, 126, 142,
|
||||
15, 31, 47, 63, 79, 95, 111, 127, 143
|
||||
};
|
||||
|
||||
static const uint16_t PERM_12_25[] = {
|
||||
0,12,24,36,48,60,72,84,96,108,120,132,144,156,168,180,192,204,216,228,240,252,264,276,288,
|
||||
1,13,25,37,49,61,73,85,97,109,121,133,145,157,169,181,193,205,217,229,241,253,265,277,289,
|
||||
2,14,26,38,50,62,74,86,98,110,122,134,146,158,170,182,194,206,218,230,242,254,266,278,290,
|
||||
3,15,27,39,51,63,75,87,99,111,123,135,147,159,171,183,195,207,219,231,243,255,267,279,291,
|
||||
4,16,28,40,52,64,76,88,100,112,124,136,148,160,172,184,196,208,220,232,244,256,268,280,292,
|
||||
5,17,29,41,53,65,77,89,101,113,125,137,149,161,173,185,197,209,221,233,245,257,269,281,293,
|
||||
6,18,30,42,54,66,78,90,102,114,126,138,150,162,174,186,198,210,222,234,246,258,270,282,294,
|
||||
7,19,31,43,55,67,79,91,103,115,127,139,151,163,175,187,199,211,223,235,247,259,271,283,295,
|
||||
8,20,32,44,56,68,80,92,104,116,128,140,152,164,176,188,200,212,224,236,248,260,272,284,296,
|
||||
9,21,33,45,57,69,81,93,105,117,129,141,153,165,177,189,201,213,225,237,249,261,273,285,297,
|
||||
10,22,34,46,58,70,82,94,106,118,130,142,154,166,178,190,202,214,226,238,250,262,274,286,298,
|
||||
11,23,35,47,59,71,83,95,107,119,131,143,155,167,179,191,203,215,227,239,251,263,275,287,299
|
||||
};
|
||||
|
||||
static const uint16_t PERM_12_29[] = {
|
||||
0,12,24,36,48,60,72,84,96,108,120,132,144,156,168,180,192,204,216,228,240,252,264,276,288,300,312,324,336,
|
||||
1,13,25,37,49,61,73,85,97,109,121,133,145,157,169,181,193,205,217,229,241,253,265,277,289,301,313,325,337,
|
||||
2,14,26,38,50,62,74,86,98,110,122,134,146,158,170,182,194,206,218,230,242,254,266,278,290,302,314,326,338,
|
||||
3,15,27,39,51,63,75,87,99,111,123,135,147,159,171,183,195,207,219,231,243,255,267,279,291,303,315,327,339,
|
||||
4,16,28,40,52,64,76,88,100,112,124,136,148,160,172,184,196,208,220,232,244,256,268,280,292,304,316,328,340,
|
||||
5,17,29,41,53,65,77,89,101,113,125,137,149,161,173,185,197,209,221,233,245,257,269,281,293,305,317,329,341,
|
||||
6,18,30,42,54,66,78,90,102,114,126,138,150,162,174,186,198,210,222,234,246,258,270,282,294,306,318,330,342,
|
||||
7,19,31,43,55,67,79,91,103,115,127,139,151,163,175,187,199,211,223,235,247,259,271,283,295,307,319,331,343,
|
||||
8,20,32,44,56,68,80,92,104,116,128,140,152,164,176,188,200,212,224,236,248,260,272,284,296,308,320,332,344,
|
||||
9,21,33,45,57,69,81,93,105,117,129,141,153,165,177,189,201,213,225,237,249,261,273,285,297,309,321,333,345,
|
||||
10,22,34,46,58,70,82,94,106,118,130,142,154,166,178,190,202,214,226,238,250,262,274,286,298,310,322,334,346,
|
||||
11,23,35,47,59,71,83,95,107,119,131,143,155,167,179,191,203,215,227,239,251,263,275,287,299,311,323,335,347
|
||||
};
|
||||
|
||||
60
include/p25p1_check_hdu.h
Normal file
60
include/p25p1_check_hdu.h
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
#ifndef P25P1_CHECK_HDU_H_f5f079faf2d64cf5831e1da1ab83b9ba
|
||||
#define P25P1_CHECK_HDU_H_f5f079faf2d64cf5831e1da1ab83b9ba
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Attempts to correct an hex (6-bit) word using the Golay(24,12,8) FEC.
|
||||
* \param hex The 6-bit word to error correct, given as a char array of 6 elements, one bit each.
|
||||
* \param parity A 12-bit word with the Golay parity data, given as a char array of 12 elements.
|
||||
* \param fixed_error Output. Filled with the number of errors fixed in the input.
|
||||
* \return 1 if there were uncorrectable errors in the input, 0 otherwise.
|
||||
*/
|
||||
int check_and_fix_golay_24_6(char* hex, char* parity, int* fixed_errors);
|
||||
|
||||
/**
|
||||
* Attempts to correct a 12-bit word using the Golay(24,12,8) FEC.
|
||||
* \param dodeca The 12-bit word to error correct, given as a char array of 12 elements, one bit each.
|
||||
* \param parity A 12-bit word with the Golay parity data, given as a char array of 12 elements.
|
||||
* \param fixed_error Output. Filled with the number of errors fixed in the input.
|
||||
* \return 1 if there were uncorrectable errors in the input, 0 otherwise.
|
||||
*/
|
||||
int check_and_fix_golay_24_12(char* dodeca, char* parity, int* fixed_errors);
|
||||
|
||||
/**
|
||||
* Encodes an hex word using the Golay(24,12,8) FEC.
|
||||
* \param hex A 6-bit word to encode.
|
||||
* \param out_parity The address to be filled with the calculated Golay parity.
|
||||
*/
|
||||
void encode_golay_24_6(char* hex, char* out_parity);
|
||||
|
||||
/**
|
||||
* Encodes an 12-bitword using the Golay(24,12,8) FEC.
|
||||
* \param dodeca A 12-bit word to encode.
|
||||
* \param out_parity The address to be filled with the calculated Golay parity.
|
||||
*/
|
||||
void encode_golay_24_12(char* dodeca, char* out_parity);
|
||||
|
||||
/**
|
||||
* Attempts to correct 20 hex words using the Reed-Solomon(36,20,17) FEC.
|
||||
* \param data The packed hex words, each of 6 chars, one after the other.
|
||||
* \param parity The corresponding 16 hex words with the parity information.
|
||||
* \return 1 if irrecoverable errors have been detected, 0 otherwise.
|
||||
*/
|
||||
int check_and_fix_redsolomon_36_20_17(char* data, char* parity);
|
||||
|
||||
/**
|
||||
* Calculates the Reed-Solomon parity of 20 words of 6 bits each.
|
||||
* \param hex_data The address with the words whose parity we want to calculate.
|
||||
* \param fixed_parity The address to be filled with the calculated parity. 16 words of 6 bits are produced.
|
||||
*/
|
||||
void encode_reedsolomon_36_20_17(char* hex_data, char* fixed_parity);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // P25P1_CHECK_HDU_H_f5f079faf2d64cf5831e1da1ab83b9ba
|
||||
58
include/p25p1_check_ldu.h
Normal file
58
include/p25p1_check_ldu.h
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
#ifndef P25P1_CHECK_LDU_H_c1734445a67e47caa25673d7a4ce7520
|
||||
#define P25P1_CHECK_LDU_H_c1734445a67e47caa25673d7a4ce7520
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Attempts to error correct a hex (6-bit) word using the Hamming(10,6,3) FEC.
|
||||
* \param hex The 6-bit word to error correct.
|
||||
* \param parity The parity to use in the error correction. 4 bits.
|
||||
* \return The estimated error count. It might be erroneous.
|
||||
*/
|
||||
int check_and_fix_hamming_10_6_3(char* hex, char* parity);
|
||||
|
||||
/**
|
||||
* Encodes an hex word with the Hamming(10,6,3) FEC.
|
||||
* \param hex The 6-bit word to error correct.
|
||||
* \param out_parity The address where to store the calculated parity. 4 bytes are used, one for each bit.
|
||||
*/
|
||||
void encode_hamming_10_6_3(char* hex, char* out_parity);
|
||||
|
||||
/**
|
||||
* Attempts to correct 12 hex words using the Reed-Solomon(24,12,13) FEC.
|
||||
* \param data The packed hex words, each of 6 chars, one after the other.
|
||||
* \param parity The corresponding 12 hex words with the parity information.
|
||||
* \return 1 if irrecoverable errors have been detected, 0 otherwise.
|
||||
*/
|
||||
int check_and_fix_reedsolomon_24_12_13(char* data, char* parity);
|
||||
|
||||
/**
|
||||
* Encodes 12 hex words with the Reed-Solomon(24,12,13) FEC.
|
||||
* \param hex_data The packed 12 hex words to encode.
|
||||
* \fixed_parity The address were to store the 12 hex words with the Reed-Solomon parity.
|
||||
*/
|
||||
void encode_reedsolomon_24_12_13(char* hex_data, char* fixed_parity);
|
||||
|
||||
/**
|
||||
* Attempts to correct 16 hex words using the Reed-Solomon(24,16,9) FEC.
|
||||
* \param data The packed 16 hex words, each of 6 chars, one after the other.
|
||||
* \param parity The corresponding 8 hex words with the parity information.
|
||||
* \return 1 if irrecoverable errors have been detected, 0 otherwise.
|
||||
*/
|
||||
int check_and_fix_reedsolomon_24_16_9(char* data, char* parity);
|
||||
|
||||
/**
|
||||
* Encodes 12 hex words with the Reed-Solomon(24,16,9) FEC.
|
||||
* \param hex_data The packed 16 hex words to encode.
|
||||
* \fixed_parity The address were to store the 8 hex words with the Reed-Solomon parity.
|
||||
*/
|
||||
void encode_reedsolomon_24_16_9(char* hex_data, char* fixed_parity);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // P25P1_CHECK_LDU_H_c1734445a67e47caa25673d7a4ce7520
|
||||
26
include/p25p1_check_nid.h
Normal file
26
include/p25p1_check_nid.h
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
#ifndef P25P1_CHECK_NID_H_3af071e917ea43fdb51326e2cbfbde0a
|
||||
#define P25P1_CHECK_NID_H_3af071e917ea43fdb51326e2cbfbde0a
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Checks a NID value, returns the fixed NAC, DUID and also an indication if it failed to decode the NID.
|
||||
* \param bch_code Input. An array to the 63 bytes, each containing one bit of the NID. This includes the
|
||||
* NAC (12 bits), DUID (4 bits) and 47 bits of BCH parity.
|
||||
* \param new_nac Output. An address where to store the calculated NAC value after error correction. Should
|
||||
* be large enough to accommodate for an integer.
|
||||
* \param new_duid Output. An address where to store the calculated DUID value after error correction. Should
|
||||
* be able to accommodate 3 chars.
|
||||
* \param parity Input. The parity value read.
|
||||
* \return 0 if there were errors processing the NID.
|
||||
*/
|
||||
int check_NID(char* bch_code, int* new_nac, char* new_duid, unsigned char parity);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // P25P1_CHECK_NID_H_3af071e917ea43fdb51326e2cbfbde0a
|
||||
73
include/p25p1_hdu.h
Normal file
73
include/p25p1_hdu.h
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
#ifndef P25P1_HDU_H_9f95c3a5072842e8aaf94444e1452d20
|
||||
#define P25P1_HDU_H_9f95c3a5072842e8aaf94444e1452d20
|
||||
|
||||
|
||||
#include "p25p1_heuristics.h"
|
||||
|
||||
/**
|
||||
* Reads a dibit (two bits) from the stream of data. Takes into account the periodic occurrence of status
|
||||
* bits that show up every 36 dibits. Status bits are discarded.
|
||||
* \param opts A pointer the the DSD options.
|
||||
* \param state A pointer the the DSD state structure.
|
||||
* \param output A pointer to be filled with the dibit read. Two bytes used for each dibit.
|
||||
* \param status_count Variable used to keep track of the status symbols in the dibit stream. There is one
|
||||
* status every 36 dibits. With this counter we can skip the status.
|
||||
* \param analog_signal A pointer with the actual analog value read from the input signal and that has been
|
||||
* interpreted as the dibit we are returning in output.
|
||||
* \param did_read_status Address were a boolean is returned. This is set to true when one status symbol was
|
||||
* read (and skipped). This indicates that the sequence on dibits has been broken and is used by the P25
|
||||
* heuristics.
|
||||
*/
|
||||
int read_dibit (dsd_opts* opts, dsd_state* state, char* output, int* status_count, int* analog_signal,
|
||||
int* did_read_status);
|
||||
|
||||
/**
|
||||
* Reads a number of dibits and stores its value together with the actual analog value that has been
|
||||
* intrepreted as the dibit into an AnalogSignal structure. This is used later in the P25 heuristics module
|
||||
* to improve the accuracy of the digitizer.
|
||||
* \param opts A pointer the the DSD options.
|
||||
* \param state A pointer the the DSD state structure.
|
||||
* \param output A pointer to be filled with the dibits read.
|
||||
* \param count The number of bits to read (remember: 1 dibit = 2 bits).
|
||||
* \param status_count Variable used to keep track of the status symbols in the dibit stream. There is one
|
||||
* status every 36 dibits. With this counter we can skip the status.
|
||||
* \param analog_signal_array The pointer to the start of a sequence of AnalogSignal structures. The
|
||||
* information is updated for each dibit read.
|
||||
* \param analog_signal_index The actual index into the AnalogSignal array. This is increased here wich each
|
||||
* dibit read.
|
||||
*/
|
||||
void read_dibit_update_analog_data (dsd_opts* opts, dsd_state* state, char* buffer, unsigned int count,
|
||||
int* status_count, AnalogSignal* analog_signal_array, int* analog_signal_index);
|
||||
|
||||
/**
|
||||
* Reads a word, made up of length bits or length/2 dibits. Also keeps track of the status dibits.
|
||||
*/
|
||||
void read_word (dsd_opts* opts, dsd_state* state, char* word, unsigned int length, int* status_count,
|
||||
AnalogSignal* analog_signal_array, int* analog_signal_index);
|
||||
|
||||
/**
|
||||
* Reads 4 bits from the stream of data. Those are the parity bits that follow each hex word.
|
||||
*/
|
||||
void read_hamm_parity (dsd_opts* opts, dsd_state* state, char* parity, int* status_count,
|
||||
AnalogSignal* analog_signal_array, int* analog_signal_index);
|
||||
|
||||
/**
|
||||
* Reads 12 bits from the stream of data. Those are the extended Golay parity bits that follow a word of data.
|
||||
*/
|
||||
void read_golay24_parity (dsd_opts* opts, dsd_state* state, char* parity, int* status_count, AnalogSignal* analog_signal_array, int* analog_signal_index);
|
||||
|
||||
/**
|
||||
* Reads bits from the data stream that should all be zeros. This is valuable information for the
|
||||
* heuristics module and we update it.
|
||||
* \param opts A pointer the the DSD options.
|
||||
* \param state A pointer the the DSD state structure.
|
||||
* \param analog_signal_array The pointer to the start of a sequence of AnalogSignal structures.
|
||||
* \param length Number of bits to read.
|
||||
* \param status_count Variable used to keep track of the status symbols in the dibit stream.
|
||||
* \param new_sequence Bool used to update the sequence_broken value of the first AnalogSignal element.
|
||||
*/
|
||||
void read_zeros(dsd_opts* opts, dsd_state* state, AnalogSignal* analog_signal_array, unsigned int length,
|
||||
int* status_count, int new_sequence);
|
||||
|
||||
#endif // P25P1_HDU_H_9f95c3a5072842e8aaf94444e1452d20
|
||||
91
include/p25p1_heuristics.h
Normal file
91
include/p25p1_heuristics.h
Normal file
@@ -0,0 +1,91 @@
|
||||
|
||||
#ifndef P25P1_HEURISTICS_H_030dd3530b7546abbb56f8dd1e66a2f6
|
||||
#define P25P1_HEURISTICS_H_030dd3530b7546abbb56f8dd1e66a2f6
|
||||
|
||||
#define HEURISTICS_SIZE 200
|
||||
typedef struct
|
||||
{
|
||||
int values[HEURISTICS_SIZE];
|
||||
float means[HEURISTICS_SIZE];
|
||||
int index;
|
||||
int count;
|
||||
float sum;
|
||||
float var_sum;
|
||||
} SymbolHeuristics;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int bit_count;
|
||||
unsigned int bit_error_count;
|
||||
SymbolHeuristics symbols[4][4];
|
||||
} P25Heuristics;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int value;
|
||||
int dibit;
|
||||
int corrected_dibit;
|
||||
int sequence_broken;
|
||||
} AnalogSignal;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initializes the heuristics state.
|
||||
* \param heuristics The P25Heuristics structure to initialize.
|
||||
*/
|
||||
void initialize_p25_heuristics(P25Heuristics* heuristics);
|
||||
|
||||
/**
|
||||
* Important method that estimates the most likely symbol for a given analog signal value and previous dibit.
|
||||
* This is called by the digitizer.
|
||||
* \param rf_mod Indicates the modulation used. The previous dibit is only used on C4FM.
|
||||
* \param heuristics Pointer to the P25Heuristics module with all the needed state information.
|
||||
* \param previous_dibit The previous dibit.
|
||||
* \param analog_value The signal's analog value we want to interpret as a dibit.
|
||||
* \param dibit Address were to store the estimated dibit.
|
||||
* \return A boolean set to true if we are able to estimate a dibit. The reason why we might not be able
|
||||
* to estimate it is because we don't have enough information to model the Gaussians (not enough data
|
||||
* has been passed to contribute_to_heuristics).
|
||||
*/
|
||||
int estimate_symbol(int rf_mod, P25Heuristics* heuristics, int previous_dibit, int analog_value, int* dibit);
|
||||
|
||||
/**
|
||||
* Log some useful information on the heuristics state.
|
||||
*/
|
||||
void debug_print_heuristics(P25Heuristics* heuristics);
|
||||
|
||||
/**
|
||||
* This method contributes valuable information from dibits whose value we are confident is correct. We take
|
||||
* the dibits and corresponding analog signal values to model the Gaussians for each dibit (and previous
|
||||
* dibit if enabled).
|
||||
* \param rf_mod Indicates the modulation used. The previous dibit is only used on C4FM.
|
||||
* \param heuristics Pointer to the P25Heuristics module with all the needed state information.
|
||||
* \param analog_signal_array Sequence of AnalogSignal which contain the cleared dibits and analog values.
|
||||
* \param count number of cleared dibits passed (= number of elements to use from analog_signal_array).
|
||||
*/
|
||||
void contribute_to_heuristics(int rf_mod, P25Heuristics* heuristics, AnalogSignal* analog_signal_array, int count);
|
||||
|
||||
/**
|
||||
* Updates the estimate for the BER (bit error rate). Mind this is method is not called for every single
|
||||
* bit in the data stream but only for those bits over which we have an estimate of its error rate,
|
||||
* specifically the bits that are protected by Reed-Solomon codes.
|
||||
* \param heuristics The heuristics state.
|
||||
* \param bits The number of bits we have read.
|
||||
* \param errors The number of errors we estimate in those bits.
|
||||
*/
|
||||
void update_error_stats(P25Heuristics* heuristics, int bits, int errors);
|
||||
|
||||
/**
|
||||
* Returns the estimate for the BER (bit error rate).
|
||||
* \return The estimated BER. This is just the percentage of errors over the processed bits.
|
||||
*/
|
||||
float get_P25_BER_estimate(P25Heuristics* heuristics);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // P25P1_HEURISTICS_H_030dd3530b7546abbb56f8dd1e66a2f6
|
||||
49
include/p25p1_ldu.h
Normal file
49
include/p25p1_ldu.h
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
#ifndef P25P1_LDU_H_a3c417fcb7804991b0e6d828066bd133
|
||||
#define P25P1_LDU_H_a3c417fcb7804991b0e6d828066bd133
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
#include "p25p1_hdu.h"
|
||||
#include "p25p1_const.h"
|
||||
#include "p25p1_heuristics.h"
|
||||
|
||||
/**
|
||||
* Separate imbe frames and deinterleave.
|
||||
* This important methods read the IMBE data from the stream and passes it to the vocoder to produce audio.
|
||||
* \param opts The DSD options.
|
||||
* \param state The DSD state.
|
||||
* \status_count An index that allows us to skip the status words interleaved every 36 dibit in the data
|
||||
* stream.
|
||||
*/
|
||||
void process_IMBE (dsd_opts* opts, dsd_state* state, int* status_count);
|
||||
|
||||
/**
|
||||
* Reads an hex word, its parity bits and attempts to error correct it using the Hamming FEC.
|
||||
* \param opts The DSD options.
|
||||
* \param state The DSD state.
|
||||
* \param hex Pointer where to store the read hex word. Six bytes, one per bit.
|
||||
* \param status_count An index that allows us to skip the status words interleaved every 36 dibit in the data
|
||||
* stream.
|
||||
* \param analog_singal_array Pointer to a sequence of AnalogSignal elements.
|
||||
* \param analog_signal_index The current index in the analog_singal_array. This value is increased on each
|
||||
* dibit read.
|
||||
*/
|
||||
void read_and_correct_hex_word (dsd_opts* opts, dsd_state* state, char* hex, int* status_count,
|
||||
AnalogSignal* analog_signal_array, int* analog_signal_index);
|
||||
|
||||
/**
|
||||
* Correct the information in analog_signal_array according with the content of data, which has been
|
||||
* error corrected and should be valid.
|
||||
* \param Dibits that have already been error corrected and we trust are correct.
|
||||
* \count Number of dibits.
|
||||
* \param analog_signal_array Pointer to a sequence of AnalogSignal elements, as many as the value of count.
|
||||
*/
|
||||
void correct_hamming_dibits(char* hex_data, int hex_count, AnalogSignal* analog_signal_array);
|
||||
|
||||
/**
|
||||
* Logs some debug info.
|
||||
*/
|
||||
void debug_ldu_header(dsd_state* state);
|
||||
|
||||
#endif // P25P1_LDU_H_a3c417fcb7804991b0e6d828066bd133
|
||||
6
include/pa_devs.h
Normal file
6
include/pa_devs.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef PA_DEVS_H
|
||||
#define PA_DEVS_H
|
||||
|
||||
void printPortAudioDevices();
|
||||
|
||||
#endif
|
||||
12
install.sh
Normal file
12
install.sh
Normal file
@@ -0,0 +1,12 @@
|
||||
sudo cp tone8.wav /usr/share/
|
||||
sudo cp tone24.wav /usr/share/
|
||||
sudo cp tone48.wav /usr/share/
|
||||
sudo chmod 777 /usr/share/tone8.wav
|
||||
sudo chmod 777 /usr/share/tone24.wav
|
||||
sudo chmod 777 /usr/share/tone48.wav
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j `nproc`
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
124
nxdn96.c
124
nxdn96.c
@@ -1,124 +0,0 @@
|
||||
#include "dsd.h"
|
||||
#include "nxdn96_const.h"
|
||||
|
||||
void
|
||||
processNXDN96 (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
int i, j, k, dibit;
|
||||
|
||||
char ambe_fr[4][24];
|
||||
const int *w, *x, *y, *z;
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("VOICE e:");
|
||||
}
|
||||
|
||||
#ifdef NXDN_DUMP
|
||||
printf ("\n");
|
||||
#endif
|
||||
|
||||
for (k = 0; k < 4; k++)
|
||||
{
|
||||
for (i = 0; i < 222; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
#ifdef NXDN_DUMP
|
||||
printf ("%c", dibit + 48);
|
||||
#endif
|
||||
}
|
||||
#ifdef NXDN_DUMP
|
||||
printf (" ");
|
||||
#endif
|
||||
|
||||
if (k < 3)
|
||||
{
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
w = nW;
|
||||
x = nX;
|
||||
y = nY;
|
||||
z = nZ;
|
||||
for (i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
#ifdef NXDN_DUMP
|
||||
printf ("%c", dibit + 48);
|
||||
#endif
|
||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||
#ifdef NXDN_DUMP
|
||||
printf (" ");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (j = 0; j < 3; j++) // we skip the last voice frame until frame sync can work with < 24 symbols
|
||||
{
|
||||
w = nW;
|
||||
x = nX;
|
||||
y = nY;
|
||||
z = nZ;
|
||||
for (i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
#ifdef NXDN_DUMP
|
||||
printf ("%c", dibit + 48);
|
||||
#endif
|
||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||
#ifdef NXDN_DUMP
|
||||
printf (" ");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (k < 3)
|
||||
{
|
||||
for (i = 0; i < 18; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
#ifdef NXDN_DUMP
|
||||
printf ("%c", dibit + 48);
|
||||
#endif
|
||||
}
|
||||
#ifdef NXDN_DUMP
|
||||
printf (" ");
|
||||
#endif
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < 30; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
#ifdef NXDN_DUMP
|
||||
printf ("%c", dibit + 48);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef NXDN_DUMP
|
||||
printf ("\n");
|
||||
#endif
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
}
|
||||
36
nxdn_data.c
36
nxdn_data.c
@@ -1,36 +0,0 @@
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
processNXDNData (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
int i, dibit;
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("DATA ");
|
||||
}
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
#ifdef NXDN_DUMP
|
||||
printf ("%c", dibit + 48);
|
||||
#endif
|
||||
}
|
||||
#ifdef NXDN_DUMP
|
||||
printf (" ");
|
||||
#endif
|
||||
|
||||
for (i = 0; i < 144; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
#ifdef NXDN_DUMP
|
||||
printf ("%c", dibit + 48);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("\n");
|
||||
}
|
||||
}
|
||||
59
nxdn_voice.c
59
nxdn_voice.c
@@ -1,59 +0,0 @@
|
||||
#include "dsd.h"
|
||||
#include "nxdn_const.h"
|
||||
|
||||
void
|
||||
processNXDNVoice (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
int i, j, dibit;
|
||||
char ambe_fr[4][24];
|
||||
const int *w, *x, *y, *z;
|
||||
const char *pr;
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("VOICE e:");
|
||||
}
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
#ifdef NXDN_DUMP
|
||||
printf ("%c", dibit + 48);
|
||||
#endif
|
||||
}
|
||||
#ifdef NXDN_DUMP
|
||||
printf (" ");
|
||||
#endif
|
||||
|
||||
pr = nxdnpr;
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
w = nW;
|
||||
x = nX;
|
||||
y = nY;
|
||||
z = nZ;
|
||||
for (i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
#ifdef NXDN_DUMP
|
||||
printf ("%c", dibit + 48);
|
||||
#endif
|
||||
ambe_fr[*w][*x] = *pr ^ (1 & (dibit >> 1)); // bit 1
|
||||
pr++;
|
||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||
#ifdef NXDN_DUMP
|
||||
printf (" ");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("\n");
|
||||
}
|
||||
}
|
||||
318
p25p1_hdu.c
318
p25p1_hdu.c
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
processHDU (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
char mi[73], mfid[9], algid[9], kid[17], tgid[17], tmpstr[255];
|
||||
int dibit, count, i, j;
|
||||
long talkgroup;
|
||||
int algidhex, kidhex;
|
||||
|
||||
mi[72] = 0;
|
||||
mfid[8] = 0;
|
||||
algid[8] = 0;
|
||||
kid[16] = 0;
|
||||
tgid[16] = 0;
|
||||
|
||||
skipDibit (opts, state, 25);
|
||||
count = 57;
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mi[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[5] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mi[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[7] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[9] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[11] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 7);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mi[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[13] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[15] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[16] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[17] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mi[18] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[19] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[20] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[21] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[22] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[23] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mi[24] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[25] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[26] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[27] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[28] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[29] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mi[30] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[31] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[32] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[33] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[34] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[35] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 7);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mi[36] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[37] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[38] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[39] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[40] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[41] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mi[42] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[43] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[44] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[45] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[46] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[47] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mi[48] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[49] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[50] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[51] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[52] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[53] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mi[54] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[55] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[56] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[57] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[58] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[59] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 7);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mi[60] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[61] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[62] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[63] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[64] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[65] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mi[66] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[67] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[68] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[69] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[70] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[71] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mfid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mfid[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mfid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mfid[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mfid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mfid[5] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mfid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mfid[7] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
algid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
algid[1] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 1);
|
||||
dibit = getDibit (opts, state);
|
||||
algid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
algid[3] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
algid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
algid[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
algid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
algid[7] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
kid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[1] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
kid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
kid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
kid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[7] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
kid[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[9] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
kid[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[11] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
kid[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[13] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
kid[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[15] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 1);
|
||||
dibit = getDibit (opts, state);
|
||||
tgid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
tgid[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
tgid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
tgid[3] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
tgid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
tgid[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
tgid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
tgid[7] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
tgid[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
tgid[9] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
tgid[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
tgid[11] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
tgid[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
tgid[13] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
tgid[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
tgid[15] = (1 & dibit) + 48; // bit 0
|
||||
|
||||
skipDibit (opts, state, 160);
|
||||
|
||||
state->p25kid = strtol(kid, NULL, 2);
|
||||
|
||||
if (opts->p25enc == 1)
|
||||
{
|
||||
algidhex = strtol (algid, NULL, 2);
|
||||
kidhex = strtol (kid, NULL, 2);
|
||||
printf ("mi: %s algid: $%x kid: $%x\n", mi, algidhex, kidhex);
|
||||
}
|
||||
if (opts->p25lc == 1)
|
||||
{
|
||||
printf ("mfid: %s tgid: %s ", mfid, tgid);
|
||||
if (opts->p25tg == 0)
|
||||
{
|
||||
printf ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
j = 0;
|
||||
if (strcmp (mfid, "10010000") == 0)
|
||||
{
|
||||
for (i = 4; i < 16; i++)
|
||||
{
|
||||
if (state->tgcount < 24)
|
||||
{
|
||||
state->tg[state->tgcount][j] = tgid[i];
|
||||
}
|
||||
tmpstr[j] = tgid[i];
|
||||
j++;
|
||||
}
|
||||
tmpstr[12] = 48;
|
||||
tmpstr[13] = 48;
|
||||
tmpstr[14] = 48;
|
||||
tmpstr[15] = 48;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
if (state->tgcount < 24)
|
||||
{
|
||||
state->tg[state->tgcount][j] = tgid[i];
|
||||
}
|
||||
tmpstr[j] = tgid[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
tmpstr[16] = 0;
|
||||
talkgroup = strtol (tmpstr, NULL, 2);
|
||||
state->lasttg = talkgroup;
|
||||
if (state->tgcount < 24)
|
||||
{
|
||||
state->tgcount = state->tgcount + 1;
|
||||
}
|
||||
if (opts->p25tg == 1)
|
||||
{
|
||||
printf ("tg: %li\n", talkgroup);
|
||||
}
|
||||
}
|
||||
298
p25p1_ldu1.c
298
p25p1_ldu1.c
@@ -1,298 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include "p25p1_const.h"
|
||||
|
||||
void
|
||||
processLDU1 (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
// extracts IMBE frames rom LDU frame
|
||||
int i, j, k, dibit, stats, count, scount;
|
||||
char imbe_fr[8][23];
|
||||
char lcformat[9], mfid[9], lcinfo[57], lsd1[9], lsd2[9], status[25];
|
||||
const int *w, *x, *y, *z;
|
||||
|
||||
lcformat[8] = 0;
|
||||
mfid[8] = 0;
|
||||
lcinfo[56] = 0;
|
||||
lsd1[8] = 0;
|
||||
lsd2[8] = 0;
|
||||
status[24] = 0;
|
||||
|
||||
skipDibit (opts, state, 3);
|
||||
status[0] = getDibit (opts, state) + 48;
|
||||
skipDibit (opts, state, 21);
|
||||
count = 57;
|
||||
scount = 1;
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("e:");
|
||||
}
|
||||
|
||||
// separate imbe frames and deinterleave
|
||||
stats = 21; // we skip the status dibits that occur every 36 symbols
|
||||
// the first IMBE frame starts 14 symbols before next status
|
||||
// so we start counter at 22
|
||||
for (i = 0; i < 9; i++)
|
||||
{ // 9 IMBE frames per LDU
|
||||
w = iW;
|
||||
x = iX;
|
||||
y = iY;
|
||||
z = iZ;
|
||||
for (j = 0; j < 72; j++)
|
||||
{
|
||||
if (stats == 35)
|
||||
{
|
||||
status[scount] = getDibit (opts, state) + 48;
|
||||
scount++;
|
||||
stats = 1;
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
stats++;
|
||||
}
|
||||
dibit = getDibit (opts, state);
|
||||
count++;
|
||||
imbe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
imbe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
if (state->p25kid == 0)
|
||||
{
|
||||
processMbeFrame (opts, state, imbe_fr, NULL, NULL);
|
||||
}
|
||||
|
||||
// skip over non imbe data sometimes between frames
|
||||
if ((i < 4) || (i == 8))
|
||||
{
|
||||
k = 0;
|
||||
}
|
||||
else if (i == 7)
|
||||
{
|
||||
//k=16;
|
||||
k = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
k = 20;
|
||||
}
|
||||
for (j = 0; j < k; j++)
|
||||
{
|
||||
if (stats == 35)
|
||||
{
|
||||
status[scount] = getDibit (opts, state) + 48;
|
||||
scount++;
|
||||
count++;
|
||||
stats = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
stats++;
|
||||
}
|
||||
skipDibit (opts, state, 1);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (i == 1)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
lcformat[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcformat[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcformat[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcformat[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcformat[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcformat[5] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
lcformat[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcformat[7] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mfid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mfid[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mfid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mfid[3] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
status[scount] = getDibit (opts, state) + 48;
|
||||
scount++;
|
||||
dibit = getDibit (opts, state);
|
||||
mfid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mfid[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mfid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mfid[7] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[1] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[7] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
stats = 10;
|
||||
}
|
||||
else if (i == 2)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[9] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[11] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[13] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[15] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[16] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[17] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[18] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[19] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[20] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[21] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[22] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[23] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[24] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[25] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[26] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[27] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[28] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[29] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[30] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[31] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
stats = 32;
|
||||
}
|
||||
else if (i == 3)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[32] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[33] = (1 & dibit) + 48; // bit 0
|
||||
status[scount] = getDibit (opts, state) + 48;
|
||||
scount++;
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[34] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[35] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[36] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[37] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[38] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[39] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[40] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[41] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[42] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[43] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[44] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[45] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[46] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[47] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[48] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[49] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[50] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[51] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[52] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[53] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[54] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[55] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
stats = 19;
|
||||
}
|
||||
else if (i == 7)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
lsd1[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd1[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lsd1[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd1[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lsd1[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd1[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lsd1[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd1[7] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 4);
|
||||
dibit = getDibit (opts, state);
|
||||
lsd2[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd2[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lsd2[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd2[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lsd2[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd2[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lsd2[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd2[7] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 4);
|
||||
stats = 33;
|
||||
}
|
||||
}
|
||||
// trailing status symbol
|
||||
status[scount] = getDibit (opts, state) + 48;
|
||||
scount++;
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
if (opts->p25status == 1)
|
||||
{
|
||||
printf ("status: %s lsd1: %s lsd2: %s\n", status, lsd1, lsd2);
|
||||
}
|
||||
|
||||
processP25lcw (opts, state, lcformat, mfid, lcinfo);
|
||||
}
|
||||
355
p25p1_ldu2.c
355
p25p1_ldu2.c
@@ -1,355 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include "p25p1_const.h"
|
||||
|
||||
void
|
||||
processLDU2 (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
// extracts IMBE frames rom LDU frame
|
||||
int i, j, k, dibit, stats, count, scount;
|
||||
char imbe_fr[8][23];
|
||||
char mi[73], algid[9], kid[17], lsd3[9], lsd4[9], status[25];
|
||||
const int *w, *x, *y, *z;
|
||||
int algidhex, kidhex;
|
||||
|
||||
status[24] = 0;
|
||||
mi[72] = 0;
|
||||
algid[8] = 0;
|
||||
kid[16] = 0;
|
||||
lsd3[8] = 0;
|
||||
lsd4[8] = 0;
|
||||
|
||||
skipDibit (opts, state, 3);
|
||||
status[0] = getDibit (opts, state) + 48;
|
||||
skipDibit (opts, state, 21);
|
||||
scount = 1;
|
||||
|
||||
count = 57;
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("e:");
|
||||
}
|
||||
|
||||
// separate imbe frames and deinterleave
|
||||
stats = 21;
|
||||
// we skip the status dibits that occur every 36 symbols
|
||||
// the first IMBE frame starts 14 symbols before next status
|
||||
// so we start counter at 22
|
||||
for (i = 0; i < 9; i++)
|
||||
{ // 9 IMBE frames per LDU
|
||||
w = iW;
|
||||
x = iX;
|
||||
y = iY;
|
||||
z = iZ;
|
||||
for (j = 0; j < 72; j++)
|
||||
{
|
||||
if (stats == 35)
|
||||
{
|
||||
status[scount] = getDibit (opts, state) + 48;
|
||||
scount++;
|
||||
stats = 1;
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
stats++;
|
||||
}
|
||||
dibit = getDibit (opts, state);
|
||||
count++;
|
||||
imbe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
imbe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
if (state->p25kid == 0)
|
||||
{
|
||||
processMbeFrame (opts, state, imbe_fr, NULL, NULL);
|
||||
}
|
||||
|
||||
// skip over non imbe data sometimes between frames
|
||||
if ((i < 5) || (i == 8))
|
||||
{
|
||||
k = 0;
|
||||
}
|
||||
else if (i == 7)
|
||||
{
|
||||
//k=16;
|
||||
k = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
k = 20;
|
||||
}
|
||||
|
||||
for (j = 0; j < k; j++)
|
||||
{
|
||||
if (stats == 35)
|
||||
{
|
||||
status[scount] = getDibit (opts, state) + 48;
|
||||
scount++;
|
||||
count++;
|
||||
stats = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
stats++;
|
||||
}
|
||||
skipDibit (opts, state, 1);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (i == 1)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
mi[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[5] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
mi[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[7] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[9] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[11] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
status[scount] = getDibit (opts, state) + 48;
|
||||
scount++;
|
||||
dibit = getDibit (opts, state);
|
||||
mi[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[13] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[15] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[16] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[17] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
mi[18] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[19] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[20] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[21] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[22] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[23] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
stats = 10;
|
||||
}
|
||||
else if (i == 2)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
mi[24] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[25] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[26] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[27] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[28] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[29] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
mi[30] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[31] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[32] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[33] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[34] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[35] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
mi[36] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[37] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[38] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[39] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[40] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[41] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
mi[42] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[43] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[44] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[45] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[46] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[47] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
stats = 32;
|
||||
}
|
||||
else if (i == 3)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
mi[48] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[49] = (1 & dibit) + 48; // bit 0
|
||||
status[scount] = getDibit (opts, state) + 48;
|
||||
scount++;
|
||||
dibit = getDibit (opts, state);
|
||||
mi[50] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[51] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[52] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[53] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
mi[54] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[55] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[56] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[57] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[58] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[59] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
mi[60] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[61] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[62] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[63] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[64] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[65] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
mi[66] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[67] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[68] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[69] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mi[70] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mi[71] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
stats = 19;
|
||||
}
|
||||
else if (i == 4)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
algid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
algid[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
algid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
algid[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
algid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
algid[5] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
algid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
algid[7] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
kid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
kid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[3] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
dibit = getDibit (opts, state);
|
||||
kid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
kid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[7] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
kid[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[9] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 1);
|
||||
status[scount] = getDibit (opts, state) + 48;
|
||||
scount++;
|
||||
skipDibit (opts, state, 1);
|
||||
dibit = getDibit (opts, state);
|
||||
kid[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[11] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
kid[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[13] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
kid[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
kid[15] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 2);
|
||||
stats = 6;
|
||||
}
|
||||
else if (i == 7)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
lsd3[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd3[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lsd3[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd3[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lsd3[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd3[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lsd3[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd3[7] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 4);
|
||||
dibit = getDibit (opts, state);
|
||||
lsd4[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd4[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lsd4[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd4[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lsd4[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd4[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lsd4[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lsd4[7] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 4);
|
||||
stats = 33;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//trailing status symbol
|
||||
status[scount] = getDibit (opts, state) + 48;
|
||||
scount++;
|
||||
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
if (opts->p25status == 1)
|
||||
{
|
||||
printf ("status: %s lsd3: %s lsd4: %s\n", status, lsd3, lsd4);
|
||||
}
|
||||
if (opts->p25enc == 1)
|
||||
{
|
||||
algidhex = strtol (algid, NULL, 2);
|
||||
kidhex = strtol (kid, NULL, 2);
|
||||
printf ("mi: %s algid: $%x kid: $%x\n", mi, algidhex, kidhex);
|
||||
}
|
||||
}
|
||||
158
p25p1_tdulc.c
158
p25p1_tdulc.c
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
processTDULC (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
char lcinfo[57], lcformat[9], mfid[9];
|
||||
int dibit, count;
|
||||
|
||||
lcformat[8] = 0;
|
||||
mfid[8] = 0;
|
||||
lcinfo[56] = 0;
|
||||
|
||||
skipDibit (opts, state, 25);
|
||||
count = 57;
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
lcformat[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcformat[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcformat[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcformat[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcformat[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcformat[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcformat[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcformat[7] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mfid[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mfid[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mfid[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mfid[3] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
mfid[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mfid[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
mfid[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
mfid[7] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 1);
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[0] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[1] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[2] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[3] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[4] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[5] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[6] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[7] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[8] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[9] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[10] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[11] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[12] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[13] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[14] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[15] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[16] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[17] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[18] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[19] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[20] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[21] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[22] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[23] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[24] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[25] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[26] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[27] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[28] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[29] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[30] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[31] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[32] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[33] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 1);
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[34] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[35] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[36] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[37] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[38] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[39] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[40] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[41] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[42] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[43] = (1 & dibit) + 48; // bit 0
|
||||
skipDibit (opts, state, 6);
|
||||
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[44] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[45] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[46] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[47] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[48] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[49] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[50] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[51] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[52] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[53] = (1 & dibit) + 48; // bit 0
|
||||
dibit = getDibit (opts, state);
|
||||
lcinfo[54] = (1 & (dibit >> 1)) + 48; // bit 1
|
||||
lcinfo[55] = (1 & dibit) + 48; // bit 0
|
||||
|
||||
skipDibit (opts, state, 91);
|
||||
|
||||
processP25lcw (opts, state, lcformat, mfid, lcinfo);
|
||||
}
|
||||
83
src/Hamming.cpp
Normal file
83
src/Hamming.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
#include "Hamming.hpp"
|
||||
|
||||
// definition outside class declaration
|
||||
Hamming_10_6_3_data Hamming_10_6_3::data;
|
||||
|
||||
// definition outside class declaration
|
||||
// Should go *after* Hamming_10_6_3_data since Hamming_10_6_3_TableImpl_data constructor uses
|
||||
// Hamming_10_6_3 which in its turn depends on Hamming_10_6_3_data
|
||||
Hamming_10_6_3_TableImpl_data Hamming_10_6_3_TableImpl::data;
|
||||
|
||||
|
||||
int Hamming_10_6_3::decode(std::bitset<10>& input)
|
||||
{
|
||||
int error_count;
|
||||
|
||||
// Compute syndromes
|
||||
int s0 = ((data.h0 & input).count() & 1) << 3;
|
||||
int s1 = ((data.h1 & input).count() & 1) << 2;
|
||||
int s2 = ((data.h2 & input).count() & 1) << 1;
|
||||
int s3 = ((data.h3 & input).count() & 1);
|
||||
int parity = s0 | s1 | s2 | s3;
|
||||
|
||||
if (parity == 0) {
|
||||
//std::cout << input << " is OK" << std::endl;
|
||||
error_count = 0;
|
||||
} else {
|
||||
// Error detected, attempt to fix it
|
||||
int bad_bit_index = data.bad_bit_table[parity];
|
||||
|
||||
if (bad_bit_index < 0) {
|
||||
//std::cout << "Irrecoverable error: " << std::bitset<6>(input.to_string()) << std::endl;
|
||||
error_count = 2;
|
||||
} else {
|
||||
// Error in a data bit, or more than one error
|
||||
// If there is one erroneous bit, the bad one is the one indicated by parity
|
||||
if (bad_bit_index < 4) {
|
||||
// Error detected in a parity bit
|
||||
//std::cout << "Error on parity bit " << bad_bit_index << std::endl;
|
||||
error_count = 1;
|
||||
} else {
|
||||
input.flip(bad_bit_index);
|
||||
//std::cout << "Error on data bit " << bad_bit_index << ", hopefully fixed: " << std::bitset<6>(input.to_string()) << std::endl;
|
||||
error_count = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
//std::cout << std::endl;
|
||||
|
||||
return error_count;
|
||||
}
|
||||
|
||||
int Hamming_10_6_3::encode(std::bitset<6>& input)
|
||||
{
|
||||
// Compute syndromes
|
||||
int s0 = ((data.gt0 & input).count() & 1) << 3;
|
||||
int s1 = ((data.gt1 & input).count() & 1) << 2;
|
||||
int s2 = ((data.gt2 & input).count() & 1) << 1;
|
||||
int s3 = ((data.gt3 & input).count() & 1);
|
||||
int parity = s0 | s1 | s2 | s3;
|
||||
|
||||
return parity;
|
||||
}
|
||||
|
||||
int Hamming_10_6_3_TableImpl::decode(int input, int* output)
|
||||
{
|
||||
assert (input < 1024 && input >= 0);
|
||||
|
||||
// Making use of a table...
|
||||
|
||||
*output = data.fixed_values[input];
|
||||
|
||||
return data.error_counts[input];
|
||||
}
|
||||
|
||||
int Hamming_10_6_3_TableImpl::encode(int input)
|
||||
{
|
||||
assert (input < 64 && input >= 0);
|
||||
|
||||
// Making use of a table...
|
||||
|
||||
return data.encode_parities[input];
|
||||
}
|
||||
560
src/bptc.c
Normal file
560
src/bptc.c
Normal file
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
============================================================================
|
||||
Name : bptc.c
|
||||
Author : MotoTRBO_Encrypted_DSD_Author
|
||||
Version : 1.0
|
||||
Date : 2017 - December - 11
|
||||
Copyright : None
|
||||
Description : BPTC library source
|
||||
============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/* Include ------------------------------------------------------------------*/
|
||||
#include "dsd.h"
|
||||
|
||||
|
||||
/* Define -------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/* Global variables ---------------------------------------------------------*/
|
||||
const uint8_t BPTCInterleavingIndex[196] =
|
||||
{
|
||||
0, 181, 166, 151,
|
||||
136, 121, 106, 91,
|
||||
76, 61, 46, 31,
|
||||
16, 1, 182, 167,
|
||||
152, 137, 122, 107,
|
||||
92, 77, 62, 47,
|
||||
32, 17, 2, 183,
|
||||
168, 153, 138, 123,
|
||||
108, 93, 78, 63,
|
||||
48, 33, 18, 3,
|
||||
184, 169, 154, 139,
|
||||
124, 109, 94, 79,
|
||||
64, 49, 34, 19,
|
||||
4, 185, 170, 155,
|
||||
140, 125, 110, 95,
|
||||
80, 65, 50, 35,
|
||||
20, 5, 186, 171,
|
||||
156, 141, 126, 111,
|
||||
96, 81, 66, 51,
|
||||
36, 21, 6, 187,
|
||||
172, 157, 142, 127,
|
||||
112, 97, 82, 67,
|
||||
52, 37, 22, 7,
|
||||
188, 173, 158, 143,
|
||||
128, 113, 98, 83,
|
||||
68, 53, 38, 23,
|
||||
8, 189, 174, 159,
|
||||
144, 129, 114, 99,
|
||||
84, 69, 54, 39,
|
||||
24, 9, 190, 175,
|
||||
160, 145, 130, 115,
|
||||
100, 85, 70, 55,
|
||||
40, 25, 10, 191,
|
||||
176, 161, 146, 131,
|
||||
116, 101, 86, 71,
|
||||
56, 41, 26, 11,
|
||||
192, 177, 162, 147,
|
||||
132, 117, 102, 87,
|
||||
72, 57, 42, 27,
|
||||
12, 193, 178, 163,
|
||||
148, 133, 118, 103,
|
||||
88, 73, 58, 43,
|
||||
28, 13, 194, 179,
|
||||
164, 149, 134, 119,
|
||||
104, 89, 74, 59,
|
||||
44, 29, 14, 195,
|
||||
180, 165, 150, 135,
|
||||
120, 105, 90, 75,
|
||||
60, 45, 30, 15
|
||||
};
|
||||
|
||||
|
||||
const uint8_t BPTCDeInterleavingIndex[196] =
|
||||
{
|
||||
0, 13, 26, 39,
|
||||
52, 65, 78, 91,
|
||||
104, 117, 130, 143,
|
||||
156, 169, 182, 195,
|
||||
12, 25, 38, 51,
|
||||
64, 77, 90, 103,
|
||||
116, 129, 142, 155,
|
||||
168, 181, 194, 11,
|
||||
24, 37, 50, 63,
|
||||
76, 89, 102, 115,
|
||||
128, 141, 154, 167,
|
||||
180, 193, 10, 23,
|
||||
36, 49, 62, 75,
|
||||
88, 101, 114, 127,
|
||||
140, 153, 166, 179,
|
||||
192, 9, 22, 35,
|
||||
48, 61, 74, 87,
|
||||
100, 113, 126, 139,
|
||||
152, 165, 178, 191,
|
||||
8, 21, 34, 47,
|
||||
60, 73, 86, 99,
|
||||
112, 125, 138, 151,
|
||||
164, 177, 190, 7,
|
||||
20, 33, 46, 59,
|
||||
72, 85, 98, 111,
|
||||
124, 137, 150, 163,
|
||||
176, 189, 6, 19,
|
||||
32, 45, 58, 71,
|
||||
84, 97, 110, 123,
|
||||
136, 149, 162, 175,
|
||||
188, 5, 18, 31,
|
||||
44, 57, 70, 83,
|
||||
96, 109, 122, 135,
|
||||
148, 161, 174, 187,
|
||||
4, 17, 30, 43,
|
||||
56, 69, 82, 95,
|
||||
108, 121, 134, 147,
|
||||
160, 173, 186, 3,
|
||||
16, 29, 42, 55,
|
||||
68, 81, 94, 107,
|
||||
120, 133, 146, 159,
|
||||
172, 185, 2, 15,
|
||||
28, 41, 54, 67,
|
||||
80, 93, 106, 119,
|
||||
132, 145, 158, 171,
|
||||
184, 1, 14, 27,
|
||||
40, 53, 66, 79,
|
||||
92, 105, 118, 131,
|
||||
144, 157, 170, 183
|
||||
};
|
||||
|
||||
|
||||
const uint8_t DeInterleaveReverseChannelBptc[32] =
|
||||
{
|
||||
0, 17, 2, 19, 4, 21, 6, 23,
|
||||
8, 25, 10, 27, 12, 29, 14, 31,
|
||||
16, 1, 18, 3, 20, 5, 22, 7,
|
||||
24, 9, 26, 11, 28, 13, 30, 15
|
||||
};
|
||||
|
||||
|
||||
/* Functions ----------------------------------------------------------------*/
|
||||
|
||||
|
||||
/*
|
||||
* @brief : This function deinterleave the DMR data by using BPTC (196,96)
|
||||
*
|
||||
* @param Input : Pointer of DMR input data interleaved (196 bytes)
|
||||
*
|
||||
* @param Output : Pointer where DMR deinterleaved data will be written (196 bytes)
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void BPTCDeInterleaveDMRData(uint8_t * Input, uint8_t * Output)
|
||||
{
|
||||
uint32_t i, DeInterleaveIndex;
|
||||
|
||||
for(i = 0; i < 196; i++)
|
||||
{
|
||||
DeInterleaveIndex = BPTCDeInterleavingIndex[i];
|
||||
Output[DeInterleaveIndex] = (Input[i] & 1);
|
||||
}
|
||||
} /* End BPTCDeInterleaveDMRData() */
|
||||
|
||||
|
||||
/*
|
||||
* @brief : This function extract the 96 bits of a deinteleaved 196 bits
|
||||
* buffer using BPTC (196,96)
|
||||
*
|
||||
* @param InputDeInteleavedData : Pointer of DMR input data deinterleaved (196 bytes)
|
||||
*
|
||||
* @param DMRDataExtracted : Pointer where the DMR data will be written (96 bytes)
|
||||
*
|
||||
* @return The total number of irrecoverable Hamming check errors
|
||||
*/
|
||||
uint32_t BPTC_196x96_Extract_Data(uint8_t InputDeInteleavedData[196], uint8_t DMRDataExtracted[96], uint8_t R[3])
|
||||
{
|
||||
uint32_t i, j, k;
|
||||
uint8_t DataMatrix[13][15];
|
||||
uint8_t LineUncorrected[15];
|
||||
uint8_t LineCorrected[11];
|
||||
uint8_t ColumnUncorrected[13];
|
||||
uint8_t ColumnCorrected[9];
|
||||
uint32_t HammingIrrecoverableErrorNb = 0;
|
||||
|
||||
/* First step : Reconstitute the BPTC 15x11 matrix
|
||||
* Note : Input data shall be deinterleaved */
|
||||
k = 1; /* Discard R(3) bit - See DMR standard chapter B1.1 BPTC (196,96) */
|
||||
for(i = 0; i < 13; i++)
|
||||
{
|
||||
for(j = 0; j < 15; j++)
|
||||
{
|
||||
/* Only the LSBit of the byte is stored */
|
||||
DataMatrix[i][j] = (InputDeInteleavedData[k] & 1);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
/* R(0) to R(2) may be used to transport some
|
||||
* Restricted Access System (RAS) information,
|
||||
* So save these three bytes before reset all of them
|
||||
* See patent US 2013/0288643 A1 */
|
||||
R[0] = DataMatrix[0][2]; /* Save R(0) */
|
||||
R[1] = DataMatrix[0][1]; /* Save R(1) */
|
||||
R[2] = DataMatrix[0][0]; /* Save R(2) */
|
||||
|
||||
/* Set to 0 R(0) to R(2) - See DMR standard chapter B1.1 BPTC (196,96) */
|
||||
DataMatrix[0][0] = 0; /* R(2) */
|
||||
DataMatrix[0][1] = 0; /* R(1) */
|
||||
DataMatrix[0][2] = 0; /* R(0) */
|
||||
|
||||
/* Init the Hamming (15,11,3) library
|
||||
* Not needed because it has already been done
|
||||
* in the "InitAllFecFunction()" function */
|
||||
//Hamming_15_11_init();
|
||||
|
||||
/* Init the Hamming (13,9,3) library
|
||||
* Not needed because it has already been done
|
||||
* in the "InitAllFecFunction()" function */
|
||||
//Hamming_13_9_init();
|
||||
|
||||
/* Init the Hamming error counter */
|
||||
HammingIrrecoverableErrorNb = 0;
|
||||
|
||||
/* Process the the Hamming (15,11,3) code
|
||||
* check on each line (first time).
|
||||
* Do not check the 4th last lines */
|
||||
for(i = 0; i < 9; i++)
|
||||
{
|
||||
/* Get a full line */
|
||||
for(j = 0; j < 15; j++)
|
||||
{
|
||||
LineUncorrected[j] = DataMatrix[i][j];
|
||||
}
|
||||
|
||||
/* Apply Hamming (15,11,3) code correction */
|
||||
if(Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
|
||||
{
|
||||
HammingIrrecoverableErrorNb++;
|
||||
}
|
||||
|
||||
/* Re-inject the line in the matrix (only the util data [11 bit],
|
||||
* not the Hamming part [4 bit]) */
|
||||
for(j = 0; j < 11; j++)
|
||||
{
|
||||
DataMatrix[i][j] = LineCorrected[j];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Process the the Hamming (15,11,3) code
|
||||
* check on each column/row (first time) */
|
||||
for(i = 0; i < 15; i++)
|
||||
{
|
||||
/* Get a full column */
|
||||
for(j = 0; j < 13; j++)
|
||||
{
|
||||
ColumnUncorrected[j] = DataMatrix[j][i];
|
||||
}
|
||||
|
||||
/* Apply Hamming (15,11,3) code correction */
|
||||
if(Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
|
||||
{
|
||||
HammingIrrecoverableErrorNb++;
|
||||
}
|
||||
|
||||
/* Re-inject the column in the matrix (only the util data [11 bit],
|
||||
* not the Hamming part [4 bit]) */
|
||||
for(j = 0; j < 9; j++)
|
||||
{
|
||||
DataMatrix[j][i] = ColumnCorrected[j];
|
||||
}
|
||||
}
|
||||
|
||||
/* The first Hamming code check has maybe corrected bit
|
||||
* witch can be use to correct other bit, so make the
|
||||
* same operation twice */
|
||||
|
||||
/* Reset the number of irrecoverable errors */
|
||||
HammingIrrecoverableErrorNb = 0;
|
||||
|
||||
|
||||
/* Process the the Hamming (15,11,3) code
|
||||
* check on each line (second time).
|
||||
* Do not check the 4th last lines */
|
||||
for(i = 0; i < 9; i++)
|
||||
{
|
||||
/* Get a full line */
|
||||
for(j = 0; j < 15; j++)
|
||||
{
|
||||
LineUncorrected[j] = DataMatrix[i][j];
|
||||
}
|
||||
|
||||
/* Apply Hamming (15,11,3) code correction */
|
||||
if(Hamming_15_11_decode(LineUncorrected, LineCorrected, 1) == false)
|
||||
{
|
||||
/* Row/Line contains irrecoverable errors */
|
||||
HammingIrrecoverableErrorNb++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Row/Line contains correct data */
|
||||
}
|
||||
|
||||
/* Re-inject the line in the matrix (only the util data [11 bit],
|
||||
* not the Hamming part [4 bit]) */
|
||||
for(j = 0; j < 11; j++)
|
||||
{
|
||||
DataMatrix[i][j] = LineCorrected[j];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Process the the Hamming (15,11,3) code
|
||||
* check on each column/row (second time) */
|
||||
for(i = 0; i < 15; i++)
|
||||
{
|
||||
/* Get a full column */
|
||||
for(j = 0; j < 13; j++)
|
||||
{
|
||||
ColumnUncorrected[j] = DataMatrix[j][i];
|
||||
}
|
||||
|
||||
/* Apply Hamming (15,11,3) code correction */
|
||||
if(Hamming_13_9_decode(ColumnUncorrected, ColumnCorrected, 1) == false)
|
||||
{
|
||||
/* Row/Line contains irrecoverable errors */
|
||||
HammingIrrecoverableErrorNb++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Row/Line contains correct data */
|
||||
}
|
||||
|
||||
/* Re-inject the column in the matrix (only the util data [11 bit],
|
||||
* not the Hamming part [4 bit]) */
|
||||
for(j = 0; j < 9; j++)
|
||||
{
|
||||
DataMatrix[j][i] = ColumnCorrected[j];
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract the DMR data (96 bit) from the matrix */
|
||||
/* First line : Do not take R(2), R(1) and R(0) */
|
||||
k = 0;
|
||||
|
||||
for(i = 3; i < 11; i++)
|
||||
{
|
||||
DMRDataExtracted[k] = DataMatrix[0][i];
|
||||
k++;
|
||||
}
|
||||
|
||||
/* Next lines */
|
||||
for(i = 1; i < 9; i++)
|
||||
{
|
||||
for(j = 0; j < 11; j++)
|
||||
{
|
||||
DMRDataExtracted[k] = DataMatrix[i][j];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the number of irrecoverable Hamming errors */
|
||||
return HammingIrrecoverableErrorNb;
|
||||
} /* End BPTC_196x96_Extract_Data() */
|
||||
|
||||
|
||||
/*
|
||||
* @brief : This function extract the 77 bits of a deinteleaved 128 bits
|
||||
* buffer using BPTC (128,77).
|
||||
*
|
||||
* @note : See DMR standard ETSI TS 102 361-1 chapter B.2.1 for details
|
||||
*
|
||||
* @param InputDeInteleavedData : Pointer of DMR input data deinterleaved (128 bytes
|
||||
act as a 16 x 8 byte data matrix)
|
||||
*
|
||||
* @param DMRDataExtracted : Pointer where the DMR data will be written (77 bytes)
|
||||
* DMRDataExtracted[0..71] = Util data (hamming corrected)
|
||||
* DMRDataExtracted[72..76] = 5 bit of CRC extracted (to be checked)
|
||||
*
|
||||
* @return 0 = All is OK
|
||||
* Other = Error - Irrecoverable Hamming parity check error number
|
||||
*/
|
||||
uint32_t BPTC_128x77_Extract_Data(uint8_t InputDataMatrix[8][16], uint8_t DMRDataExtracted[77])
|
||||
{
|
||||
uint32_t i, j, k;
|
||||
uint8_t DataMatrix[8][16];
|
||||
uint8_t LineUncorrected[16];
|
||||
uint8_t LineCorrected[11];
|
||||
uint32_t HammingIrrecoverableErrorNb = 0;
|
||||
uint32_t ParityCheckErrorNb = 0;
|
||||
uint32_t NbOfOne;
|
||||
|
||||
/* First step : Reconstitute the BPTC 16x8 matrix */
|
||||
for(i = 0; i < 8; i++)
|
||||
{
|
||||
for(j = 0; j < 16; j++)
|
||||
{
|
||||
/* Only the LSBit of the byte is stored */
|
||||
DataMatrix[i][j] = (InputDataMatrix[i][j] & 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Init the Hamming (16,11,4) library
|
||||
* Not needed because it has already been done
|
||||
* in the "InitAllFecFunction()" function */
|
||||
//Hamming_16_11_4_init();
|
||||
|
||||
/* Process the the Hamming (16,11,4) code
|
||||
* check on each line.
|
||||
* Do not check the last line (line nb 8) */
|
||||
for(i = 0; i < 7; i++)
|
||||
{
|
||||
/* Get a full line */
|
||||
for(j = 0; j < 16; j++)
|
||||
{
|
||||
LineUncorrected[j] = DataMatrix[i][j];
|
||||
}
|
||||
|
||||
/* Apply Hamming (16,11,4) code correction */
|
||||
if(Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
|
||||
{
|
||||
HammingIrrecoverableErrorNb++;
|
||||
}
|
||||
|
||||
/* Re-inject the line in the matrix (only the util data [11 bit],
|
||||
* not the Hamming part [4 bit]) */
|
||||
for(j = 0; j < 11; j++)
|
||||
{
|
||||
DataMatrix[i][j] = LineCorrected[j];
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract the DMR data (77 bit) from the matrix */
|
||||
k = 0;
|
||||
|
||||
/* 2 first lines */
|
||||
for(i = 0; i < 2; i++)
|
||||
{
|
||||
for(j = 0; j < 11; j++)
|
||||
{
|
||||
DMRDataExtracted[k] = DataMatrix[i][j];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 5 Next lines */
|
||||
for(i = 2; i < 7; i++)
|
||||
{
|
||||
for(j = 0; j < 10; j++)
|
||||
{
|
||||
DMRDataExtracted[k] = DataMatrix[i][j];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 5 bit of CRC */
|
||||
for(i = 2; i < 7; i++)
|
||||
{
|
||||
DMRDataExtracted[k] = DataMatrix[i][10];
|
||||
k++;
|
||||
}
|
||||
|
||||
/* Verify the data integrity by checking
|
||||
* all column parity bit */
|
||||
ParityCheckErrorNb = 0;
|
||||
for(i = 0; i < 16; i++)
|
||||
{
|
||||
NbOfOne = 0;
|
||||
|
||||
/* Get a full column */
|
||||
for(j = 0; j < 7; j++)
|
||||
{
|
||||
NbOfOne += DataMatrix[j][i];
|
||||
}
|
||||
|
||||
/* Check the parity bit (number of "1"
|
||||
* must be even) */
|
||||
if((NbOfOne % 2) != DataMatrix[7][i])
|
||||
{
|
||||
ParityCheckErrorNb++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the number of irrecoverable Hamming errors +
|
||||
* the number of parity check error */
|
||||
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
|
||||
} /* End BPTC_128x77_Extract_Data() */
|
||||
|
||||
|
||||
/*
|
||||
* @brief : This function extract the 32 bits of a deinteleaved 32 bits
|
||||
* buffer using Reverse Channel Single Burst BPTC.
|
||||
*
|
||||
* @note : See DMR standard ETSI TS 102 361-1 chapter B.2.2.2 for details
|
||||
*
|
||||
* @param InputInterleavedData : Pointer of DMR input data interleaved (32 bytes
|
||||
act as a 16 x 2 bits data matrix)
|
||||
*
|
||||
* @param DMRDataExtracted : Pointer where the DMR data will be written (77 bytes)
|
||||
* DMRDataExtracted[0..10] = Util data (Hamming corrected)
|
||||
* DMRDataExtracted[11..15] = Hamming data
|
||||
* DMRDataExtracted[16..31] = 16 bits of odd parity (to be checked)
|
||||
*
|
||||
* @return 0 = All is OK
|
||||
* Other = Error - Irrecoverable Hamming parity check error number
|
||||
*/
|
||||
uint32_t BPTC_16x2_Extract_Data(uint8_t InputInterleavedData[32], uint8_t DMRDataExtracted[32])
|
||||
{
|
||||
uint32_t i;
|
||||
uint8_t DataMatrix[32];
|
||||
uint8_t LineUncorrected[16];
|
||||
uint8_t LineCorrected[11];
|
||||
uint32_t HammingIrrecoverableErrorNb = 0;
|
||||
uint32_t ParityCheckErrorNb = 0;
|
||||
|
||||
for(i = 0; i < 32; i++)
|
||||
{
|
||||
DataMatrix[DeInterleaveReverseChannelBptc[i]] = InputInterleavedData[i] & 1;
|
||||
}
|
||||
|
||||
/* Copy the entire input buffer to the output buffer */
|
||||
for(i = 0; i < 32; i++)
|
||||
{
|
||||
DMRDataExtracted[i] = DataMatrix[i];
|
||||
}
|
||||
|
||||
/* Prepare the line to be corrected */
|
||||
for(i = 0; i < 16; i++)
|
||||
{
|
||||
LineUncorrected[i] = DataMatrix[i];
|
||||
}
|
||||
|
||||
/* Apply Hamming (16,11,4) code correction */
|
||||
if(Hamming_16_11_4_decode(LineUncorrected, LineCorrected, 1) == false)
|
||||
{
|
||||
HammingIrrecoverableErrorNb++;
|
||||
}
|
||||
|
||||
/* Copy the corrected content to the output buffer */
|
||||
for(i = 0; i < 11; i++)
|
||||
{
|
||||
DMRDataExtracted[i] = LineCorrected[i];
|
||||
}
|
||||
|
||||
/* Check Parity bits */
|
||||
for(i = 0; i < 16; i++)
|
||||
{
|
||||
/* Odd parity ==> If data = 1 then parity = 0
|
||||
* If data = 0 then parity = 1 */
|
||||
if(DMRDataExtracted[i] == DMRDataExtracted[i + 16]) ParityCheckErrorNb++;
|
||||
}
|
||||
|
||||
//fprintf(stderr, "Hamming ERR=%u ; Parity ERR=%u", HammingIrrecoverableErrorNb, ParityCheckErrorNb);
|
||||
|
||||
/* Return the number of irrecoverable Hamming errors +
|
||||
* the number of parity check error */
|
||||
return (HammingIrrecoverableErrorNb + ParityCheckErrorNb);
|
||||
|
||||
} /* End BPTC_16x2_Extract_Data() */
|
||||
|
||||
|
||||
|
||||
/* End of file */
|
||||
462
src/dmr_block.c
Normal file
462
src/dmr_block.c
Normal file
@@ -0,0 +1,462 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dmr_block.c
|
||||
* DMR Data Header and Data Block Assembly/Handling
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-12 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void dmr_dheader (dsd_opts * opts, dsd_state * state, uint8_t dheader[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||
{
|
||||
uint32_t i, j, k;
|
||||
uint8_t slot = state->currentslot;
|
||||
|
||||
//clear out unified pdu 'superframe' slot
|
||||
for (short int i = 0; i < 288; i++)
|
||||
{
|
||||
state->dmr_pdu_sf[slot][i] = 0;
|
||||
}
|
||||
|
||||
//TODO: ONLY RUN ON GOOD CRC?
|
||||
|
||||
//collect relevant data header info, figure out format, and grab appropriate info from it
|
||||
if (state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "%s ", KGRN);
|
||||
state->data_block_counter[slot] = 1; //reset block counter to 1
|
||||
state->data_header_format[slot] = dheader[0]; //collect the entire byte, we can check group or individual, response requested as well on this
|
||||
state->data_header_sap[slot] = (dheader[1] & 0xF0) >> 4;
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF) == 0x2)
|
||||
{
|
||||
state->data_conf_data[slot] = 0; //flag as unconfirmed data, no need for additional CRC/Serial checks
|
||||
state->data_header_padding[slot] = dheader[1] & 0xF; //number of padding octets on last block
|
||||
state->data_header_blocks[slot] = dheader[8] & 0x7F; //number of blocks to follow, most useful for when to poll the super array for data
|
||||
fprintf (stderr, "\n Unconfirmed Data Header: DPF %01X - SAP %02X - Block Count %02X - Padding Octets %02X",
|
||||
state->data_header_format[slot] & 0xF, state->data_header_sap[slot],
|
||||
state->data_header_blocks[slot], state->data_header_padding[slot] );
|
||||
}
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF) == 0x3)
|
||||
{
|
||||
state->data_conf_data[slot] = 1; //set flag to indicate the following blocks are confirmed data so we know to run additional CRC etc on it.
|
||||
state->data_header_padding[slot] = dheader[1] & 0xF; //number of padding octets on last block
|
||||
state->data_header_blocks[slot] = dheader[8] & 0x7F; //number of blocks to follow, most useful for when to poll the super array for data
|
||||
fprintf (stderr, "\n Confirmed Data Header: DPF %01X - SAP %02X - Block Count %02X - Padding Octets %02X",
|
||||
state->data_header_format[slot] & 0xF, state->data_header_sap[slot],
|
||||
state->data_header_blocks[slot], state->data_header_padding[slot] );
|
||||
}
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF) == 0xD) //DD_HEAD
|
||||
{
|
||||
state->data_conf_data[slot] = 0;
|
||||
state->data_header_padding[slot] = dheader[7] & 0xF;
|
||||
state->data_header_blocks[slot] = ( (dheader[0] & 0x30) | ((dheader[1] & 0xF) >> 0) ); //what a pain MSB | LSB
|
||||
fprintf (stderr, "\n Short Data - Defined: DPF %01X - SAP %02X - Appended Blocks %02X - Padding Bits %02X",
|
||||
state->data_header_format[slot] & 0xF, state->data_header_sap[slot],
|
||||
state->data_header_blocks[slot], state->data_header_padding[slot] );
|
||||
}
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF) == 0xE) //SP_HEAD or R_HEAD
|
||||
{
|
||||
state->data_conf_data[slot] = 0;
|
||||
state->data_header_padding[slot] = dheader[7] & 0xF;
|
||||
state->data_header_blocks[slot] = ( (dheader[0] & 0x30) | ((dheader[1] & 0xF) >> 0) );
|
||||
fprintf (stderr, "\n Short Data - Raw or Precoded: DPF %01X - SAP %02X - Appended Blocks %02X - Padding Bits %02X",
|
||||
state->data_header_format[slot] & 0xF, state->data_header_sap[slot],
|
||||
state->data_header_blocks[slot], state->data_header_padding[slot] );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//do we still need this? since we have ample PDU storage
|
||||
if (state->data_header_blocks[slot] > 7)
|
||||
{
|
||||
state->data_header_blocks[slot] = 7;
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
//end collecting data header info
|
||||
|
||||
fprintf (stderr, "%s ", KYEL);
|
||||
|
||||
if (state->data_header_sap[slot] == 0x4 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n IP Data - Source: ");
|
||||
fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] );
|
||||
|
||||
state->dmr_lrrp_source[slot] = (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7];
|
||||
|
||||
//Remus! Uncomment these line
|
||||
// if ( (state->data_header_format[slot] & 0xF ) != 0x1 && state->data_p_head[slot] == 0)
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "IP DATA SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]);
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[slot] == 0x0 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Unified Data Transport - Source: ");
|
||||
fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] );
|
||||
|
||||
state->dmr_lrrp_source[slot] = (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7];
|
||||
|
||||
//Remus! Uncomment these line
|
||||
// if ( (state->data_header_format[slot] & 0xF ) != 0x1 && state->data_p_head[slot] == 0)
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "UDT SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]);
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[slot] == 0x9 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Proprietary Packet Data - Source: ");
|
||||
fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] );
|
||||
|
||||
state->dmr_lrrp_source[slot] = (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7];
|
||||
|
||||
//Remus! Uncomment this line
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "P_HEAD SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]);
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[slot] == 0x2 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n TCP/IP Header Compression Data - Source: ");
|
||||
fprintf (stderr, "[%d]", dheader[3]);
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", dheader[2]);
|
||||
|
||||
state->dmr_lrrp_source[slot] = dheader[3];
|
||||
|
||||
//Remus! Uncomment these line
|
||||
// if ( (state->data_header_format[slot] & 0xF ) != 0x1 && state->data_p_head[slot] == 0)
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "IPC SRC [%d] DST [%d] ", dheader[3], dheader[2]);
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[slot] == 0x3 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n UDP/IP Header Compression Data - Source: ");
|
||||
fprintf (stderr, "[%d]", dheader[3]);
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", dheader[2]);
|
||||
|
||||
state->dmr_lrrp_source[slot] = dheader[3];
|
||||
|
||||
//Remus! Uncomment these line
|
||||
// if ( (state->data_header_format[slot] & 0xF ) != 0x1 && state->data_p_head[slot] == 0)
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "UDP SRC [%d] DST [%d] ", dheader[3], dheader[2]);
|
||||
|
||||
}
|
||||
|
||||
if (state->data_header_sap[slot] == 0x5 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n ARP - Address Resolution Protocol ");
|
||||
}
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF ) == 0xD && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Short Data - Defined - Source: ");
|
||||
fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] );
|
||||
|
||||
//Remus! Uncomment this line
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "SD-D SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]);
|
||||
|
||||
}
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF) == 0xE && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Short Data - Raw or Status/Precoded - Source: ");
|
||||
fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] );
|
||||
|
||||
//Remus! Uncomment this line
|
||||
// sprintf (state->dmr_lrrp_gps[slot], "SD-R/P SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]);
|
||||
|
||||
}
|
||||
|
||||
if ( (state->data_header_format[slot] & 0xF ) == 0x1 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Response Packet - Source: ");
|
||||
fprintf (stderr, "[%d]", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7] );
|
||||
fprintf (stderr, " Destination: ");
|
||||
fprintf (stderr, "[%d]", (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4] );
|
||||
|
||||
//Remus! Uncomment these lines
|
||||
// char rsp[90];
|
||||
// sprintf (rsp, " - RSP SRC [%d] DST [%d] ", (dheader[5] <<16 ) + (dheader[6] << 8) + dheader[7], (dheader[2] <<16 ) + (dheader[3] <<8 ) + dheader[4]);
|
||||
// strcat (state->dmr_lrrp_gps[slot], rsp);
|
||||
|
||||
}
|
||||
|
||||
//Removed Dup UDT Message
|
||||
|
||||
if ( (state->data_header_format[slot] & 0x40) == 0x40 && state->data_p_head[slot] == 0) //check response req bit
|
||||
{
|
||||
fprintf (stderr, "\n Response Requested - ");
|
||||
fprintf (stderr, " Source:");
|
||||
fprintf (stderr, " [%d]", (dheader[2] <<16 ) + (dheader[3] << 8) + dheader[4] );
|
||||
fprintf (stderr, " Destination:");
|
||||
fprintf (stderr, " [%d]", (dheader[5] <<16 ) + (dheader[6] <<8 ) + dheader[7] );
|
||||
|
||||
//Remus! Uncomment these lines
|
||||
// char rsp[90];
|
||||
// sprintf (rsp, "- RSP REQ ");
|
||||
// strcat (state->dmr_lrrp_gps[slot], rsp);
|
||||
|
||||
}
|
||||
|
||||
//if proprietary header was flagged by the last data header, then process data differently
|
||||
if (state->data_p_head[slot] == 1)
|
||||
{
|
||||
fprintf (stderr, "\n Proprietary Data Header: SAP %01X - Format %01X - MFID %02X",
|
||||
(dheader[0] & 0xF0) >> 4, dheader[0] & 0xF, dheader[1] );
|
||||
|
||||
//state->data_p_head[slot] = 0; //disabling this, we can flag it off after processing pdu instead
|
||||
state->data_header_sap[slot] = 0; //reset this to zero so we don't trip the condition below
|
||||
state->data_block_counter[slot]++; //increment the counter since this counts against the data blocks
|
||||
|
||||
//set overarching manufacturer in use when non-standard feature id set is up
|
||||
//if (dheader[1] != 0) state->dmr_mfid = dheader[1]; //diable, will only set in link control or csbk for now (random manufacturers getting set, bad decode? or random feature?)
|
||||
|
||||
//assign prop header pdu to 'superframe'
|
||||
uint8_t blocks = state->data_header_blocks[slot] - 1;
|
||||
for(i = 0; i < 12; i++)
|
||||
{
|
||||
state->dmr_pdu_sf[slot][i+(blocks*12)] = dheader[i];
|
||||
}
|
||||
}
|
||||
|
||||
//only set this flag after everything else is processed
|
||||
if (state->data_header_sap[slot] == 0x9 && state->data_p_head[slot] == 0)
|
||||
{
|
||||
fprintf (stderr, "\n Proprietary Packet Data Incoming ");
|
||||
state->data_p_head[slot] = 1; //flag that next data header will be a proprietary data header
|
||||
}
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
|
||||
//let user know the data header may have errors
|
||||
fprintf (stderr, "%s", KRED);
|
||||
if (CRCCorrect == 1) ; //CRCCorrect 1 is good, else is bad CRC; no print on good
|
||||
else if(IrrecoverableErrors == 0) fprintf(stderr, " (FEC OK)");
|
||||
else fprintf(stderr, " (FEC/CRC ERR)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
|
||||
//assemble the blocks as they come in, shuffle them into the unified dmr_pdu_sf
|
||||
void dmr_block_assembler (dsd_opts * opts, dsd_state * state, uint8_t block_bytes[], uint8_t block_len, uint8_t databurst, uint8_t type)
|
||||
{
|
||||
int i, j, k;
|
||||
uint8_t lb = 0; //mbc last block
|
||||
uint8_t pf = 0; //mbc protect flag
|
||||
uint8_t is_conf_data = 0; //internal flag to see if type 1 blocks contain confirmed data crcs and serials
|
||||
uint8_t slot = state->currentslot;
|
||||
int blocks;
|
||||
uint8_t blockcounter = state->data_block_counter[slot];
|
||||
|
||||
//crc specific variables
|
||||
uint8_t len = 24; //len of completed message needing crc32 number of blocks*block_len - 12?
|
||||
uint8_t crclen = 32; //len of the crc32 (obviously)
|
||||
uint8_t start = 12; //starting position (+12 offset loaded up data header)
|
||||
uint8_t n = 12; //number of bytes per block
|
||||
|
||||
//TODO: CRC check on Header, Blocks and full frames as appropriate
|
||||
uint32_t CRCCorrect = 0;
|
||||
uint32_t CRCComputed = 0;
|
||||
uint32_t CRCExtracted = 0;
|
||||
uint32_t IrrecoverableErrors = 0;
|
||||
|
||||
uint8_t dmr_pdu_sf_bits[18*8*8]; //8 blocks at 18 bytes at 8 bits //just a large value to have plenty storage space
|
||||
|
||||
//MBC Header and Block CRC
|
||||
uint8_t mbc_crc_good[2]; //header and blocks crc pass/fail local storage
|
||||
memset (mbc_crc_good, 0, sizeof (mbc_crc_good)); //init on 0 - bad crc
|
||||
|
||||
if (type == 1) blocks = state->data_header_blocks[slot] - 1;
|
||||
if (type == 2) blocks = state->data_block_counter[slot];
|
||||
|
||||
//sanity check, setting block_len and block values to sane numbers in case of missing header, else could overload array (crash) or print out
|
||||
if (blocks < 1) blocks = 1; //changed blocks from uint8_t to int
|
||||
if (blocks > 8) blocks = 8;
|
||||
if (block_len == 0) block_len = 18;
|
||||
if (block_len > 24) block_len = 24;
|
||||
|
||||
//TODO: add CRC/FEC validation to each data header and each confirmed data block as well - type 1
|
||||
if (type == 1)
|
||||
{
|
||||
//type 1 data block, shuffle method
|
||||
for (j = 0; j < blocks; j++) //6
|
||||
{
|
||||
for (i = 0; i < block_len; i++) //block_len
|
||||
{
|
||||
state->dmr_pdu_sf[slot][i+block_len*j] = state->dmr_pdu_sf[slot][i+block_len*(j+1)];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < block_len; i++)
|
||||
{
|
||||
state->dmr_pdu_sf[slot][i+(blocks*block_len)] = block_bytes[i];
|
||||
}
|
||||
//time to send the completed 'superframe' to the DMR PDU message handler
|
||||
if (state->data_block_counter[slot] == state->data_header_blocks[slot])
|
||||
{
|
||||
//TODO: CRC32 on completed messages
|
||||
dmr_pdu (opts, state, block_len, state->dmr_pdu_sf[slot]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//type 2 - MBC header and MBC continuation blocks
|
||||
if (type == 2)
|
||||
{
|
||||
//sanity check (marginal signal, bad decodes, etc)
|
||||
if (state->data_block_counter[slot] > 8) state->data_block_counter[slot] = 8;
|
||||
|
||||
//Type 2 data block, additive method
|
||||
for (i = 0; i < block_len; i++)
|
||||
{
|
||||
state->dmr_pdu_sf[slot][i+(blockcounter*block_len)] = block_bytes[i];
|
||||
}
|
||||
|
||||
memset (dmr_pdu_sf_bits, 0, sizeof(dmr_pdu_sf_bits));
|
||||
|
||||
lb = block_bytes[0] >> 7; //last block flag
|
||||
pf = (block_bytes[0] >> 6) & 1;
|
||||
|
||||
if (lb == 1) //last block arrived, time to send to cspdu decoder
|
||||
{
|
||||
for(i = 0, j = 0; i < 12*4; i++, j+=8) //
|
||||
{
|
||||
dmr_pdu_sf_bits[j + 0] = (state->dmr_pdu_sf[slot][i] >> 7) & 0x01;
|
||||
dmr_pdu_sf_bits[j + 1] = (state->dmr_pdu_sf[slot][i] >> 6) & 0x01;
|
||||
dmr_pdu_sf_bits[j + 2] = (state->dmr_pdu_sf[slot][i] >> 5) & 0x01;
|
||||
dmr_pdu_sf_bits[j + 3] = (state->dmr_pdu_sf[slot][i] >> 4) & 0x01;
|
||||
dmr_pdu_sf_bits[j + 4] = (state->dmr_pdu_sf[slot][i] >> 3) & 0x01;
|
||||
dmr_pdu_sf_bits[j + 5] = (state->dmr_pdu_sf[slot][i] >> 2) & 0x01;
|
||||
dmr_pdu_sf_bits[j + 6] = (state->dmr_pdu_sf[slot][i] >> 1) & 0x01;
|
||||
dmr_pdu_sf_bits[j + 7] = (state->dmr_pdu_sf[slot][i] >> 0) & 0x01;
|
||||
}
|
||||
|
||||
//CRC check on Header and full frames as appropriate (header crc already stored)
|
||||
//The 16 bit CRC in the header shall include the data carried by the header.
|
||||
//The 16 bit CRC in the last block shall be performed on
|
||||
//all MBC blocks (conmbined) except the header block. <-- misleading statement, just like half the dmr manual
|
||||
|
||||
mbc_crc_good[0] = state->data_block_crc_valid[slot][0];
|
||||
|
||||
CRCExtracted = 0;
|
||||
//extract crc from last block, apply to completed 'superframe' minus header
|
||||
for(i = 0; i < 16; i++)
|
||||
{
|
||||
CRCExtracted = CRCExtracted << 1;
|
||||
CRCExtracted = CRCExtracted | (uint32_t)(dmr_pdu_sf_bits[i + 96*(1+blocks) - 16] & 1);
|
||||
}
|
||||
|
||||
uint8_t mbc_block_bits[12*8*3];
|
||||
memset (mbc_block_bits, 0, sizeof(mbc_block_bits));
|
||||
//shift continuation blocks and last block into seperate array for crc check
|
||||
for (i = 0; i < 12*8*3; i++)
|
||||
{
|
||||
mbc_block_bits[i] = dmr_pdu_sf_bits[i+96]; //skip mbc header
|
||||
}
|
||||
|
||||
CRCComputed = ComputeCrcCCITT16d (mbc_block_bits, ((blocks+0)*96)-16 );
|
||||
|
||||
// fprintf (stderr, " \n CRC EXT %X CRC CMP %X", CRCExtracted, CRCComputed);
|
||||
|
||||
if (CRCComputed == CRCExtracted) mbc_crc_good[1] = 1;
|
||||
|
||||
CRCCorrect = 0;
|
||||
IrrecoverableErrors = 1;
|
||||
|
||||
//set good on good header and good blocks
|
||||
if (mbc_crc_good[0] == 1 && mbc_crc_good[1] == 1)
|
||||
{
|
||||
CRCCorrect = 1;
|
||||
IrrecoverableErrors = 0;
|
||||
}
|
||||
|
||||
//cspdu will only act on any fid/opcodes if good CRC to prevent falsing on control signalling
|
||||
dmr_cspdu (opts, state, dmr_pdu_sf_bits, state->dmr_pdu_sf[slot], CRCCorrect, IrrecoverableErrors);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Full Super Frame Type 1 - Debug Output
|
||||
if (opts->payload == 1 && type == 1 && state->data_block_counter[slot] == state->data_header_blocks[slot])
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, "\n Multi Block PDU Superframe - Slot [%d]\n ", slot+1);
|
||||
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", state->dmr_pdu_sf[slot][i]);
|
||||
if (i == 11 || i == 23 || i == 35 || i == 47 || i == 59 || i == 71 || i == 83 || i == 95)
|
||||
{
|
||||
fprintf (stderr, "\n ");
|
||||
}
|
||||
}
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
|
||||
//Full Super Frame MBC - Debug Output
|
||||
if (opts->payload == 1 && type == 2 && lb == 1)
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, "\n MBC Multi Block PDU Superframe - Slot [%d]\n ", slot+1);
|
||||
for (i = 0; i < ((blocks+1)*block_len); i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", state->dmr_pdu_sf[slot][i]);
|
||||
if (i == 11 || i == 23 || i == 35 || i == 47 || i == 59 || i == 71 || i == 83 || i == 95)
|
||||
{
|
||||
fprintf (stderr, "\n ");
|
||||
}
|
||||
}
|
||||
fprintf (stderr, "%s", KRED);
|
||||
if (mbc_crc_good[0] == 0) fprintf (stderr, "MBC Header CRC ERR ");
|
||||
if (mbc_crc_good[1] == 0) fprintf (stderr, "MBC Blocks CRC ERR ");
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
|
||||
state->data_block_counter[slot]++; //increment block counter
|
||||
|
||||
if (type == 2 && lb == 1)
|
||||
{
|
||||
state->data_block_counter[slot] = 0; //reset the block counter (MBCs)
|
||||
|
||||
//clear out unified pdu 'superframe' slot
|
||||
for (short int i = 0; i < 288; i++)
|
||||
{
|
||||
state->dmr_pdu_sf[slot][i] = 0;
|
||||
}
|
||||
|
||||
state->data_block_crc_valid[slot][0] = 0; //Zero Out MBC Header Block CRC Valid
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//failsafe to clear old data header, block info, cach, in case of tact/emb/slottype failures and we
|
||||
//can no longer verify accurate data block reporting
|
||||
void dmr_reset_blocks (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
memset (state->data_p_head, 0, sizeof(state->data_p_head));
|
||||
memset (state->data_conf_data, 0, sizeof(state->data_conf_data));
|
||||
memset (state->dmr_pdu_sf, 0, sizeof(state->dmr_pdu_sf));
|
||||
memset (state->data_block_counter, 0, sizeof(state->data_block_counter));
|
||||
memset (state->data_header_blocks, 0, sizeof(state->data_header_blocks));
|
||||
memset (state->data_block_crc_valid, 0, sizeof(state->data_block_crc_valid));
|
||||
memset (state->dmr_lrrp_source, 0, sizeof(state->dmr_lrrp_source));
|
||||
memset (state->dmr_cach_fragment, 1, sizeof (state->dmr_cach_fragment));
|
||||
}
|
||||
705
src/dmr_bs.c
Normal file
705
src/dmr_bs.c
Normal file
@@ -0,0 +1,705 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dmr_bs.c
|
||||
* DMR BS Voice Handling and Data Gathering Routines - "DMR STEREO"
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-12 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include "dmr_const.h"
|
||||
|
||||
//A subroutine for processing each TDMA frame individually to allow for
|
||||
//processing voice and/or data on both BS slots (channels) simultaneously
|
||||
void dmrBS (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
int i, j, k, l, dibit;
|
||||
int *dibit_p;
|
||||
|
||||
char ambe_fr[4][24];
|
||||
char ambe_fr2[4][24];
|
||||
char ambe_fr3[4][24];
|
||||
|
||||
//redundancy check (carrier signal loss event)
|
||||
char redundancyA[36];
|
||||
char redundancyB[36];
|
||||
|
||||
//memcpy of ambe_fr for late entry
|
||||
char m1[4][24];
|
||||
char m2[4][24];
|
||||
char m3[4][24];
|
||||
|
||||
const int *w, *x, *y, *z;
|
||||
char sync[25];
|
||||
char syncdata[48];
|
||||
char EmbeddedSignalling[16];
|
||||
|
||||
uint8_t emb_ok = 0;
|
||||
uint8_t tact_okay = 0;
|
||||
uint8_t cach_err = 0;
|
||||
|
||||
uint8_t internalslot;
|
||||
uint8_t vc1;
|
||||
uint8_t vc2;
|
||||
|
||||
//would be ideal to grab all dibits and break them into bits to pass to new data handler?
|
||||
uint8_t dummy_bits[196];
|
||||
memset (dummy_bits, 0, sizeof(dummy_bits));
|
||||
|
||||
//add time to mirror printFrameSync
|
||||
time_t now;
|
||||
char * getTime(void) //get pretty hh:mm:ss timestamp
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
|
||||
char * curr;
|
||||
char * stamp = asctime(localtime( & t));
|
||||
|
||||
curr = strtok(stamp, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
//Init slot lights
|
||||
sprintf (state->slot1light, " slot1 ");
|
||||
sprintf (state->slot2light, " slot2 ");
|
||||
|
||||
//Init the color code status
|
||||
state->color_code_ok = 0;
|
||||
|
||||
vc1 = 2;
|
||||
vc2 = 2;
|
||||
|
||||
short int loop = 1;
|
||||
short int skipcount = 0;
|
||||
|
||||
//cach
|
||||
char cachdata[25];
|
||||
int cachInterleave[24] =
|
||||
{0, 7, 8, 9, 1, 10,
|
||||
11, 12, 2, 13, 14,
|
||||
15, 3, 16, 4, 17, 18,
|
||||
19, 5, 20, 21, 22, 6, 23
|
||||
};
|
||||
|
||||
//cach tact bits
|
||||
uint8_t tact_bits[7];
|
||||
|
||||
//Run Loop while the getting is good
|
||||
while (loop == 1) {
|
||||
|
||||
internalslot = -1; //reset here so we know if this value is being set properly
|
||||
for(i = 0; i < 12; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
state->dmr_stereo_payload[i] = dibit;
|
||||
|
||||
cachdata[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
|
||||
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
||||
}
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
tact_bits[i] = cachdata[i];
|
||||
}
|
||||
|
||||
//disabled for dmr_cach testing
|
||||
tact_okay = 0;
|
||||
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
||||
if (tact_okay != 1)
|
||||
{
|
||||
if (opts->aggressive_framesync == 1)
|
||||
{
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
|
||||
internalslot = state->currentslot = tact_bits[1];
|
||||
|
||||
//Setup for first AMBE Frame
|
||||
//Interleave Schedule
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
|
||||
//First AMBE Frame, Full 36
|
||||
for(i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
state->dmr_stereo_payload[i+12] = dibit;
|
||||
redundancyA[i] = dibit;
|
||||
|
||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
//check for repetitive data if caught in a 'no carrier' loop? Just picking random values.
|
||||
//this will test for no carrier (input signal) and return us to no sync state if necessary
|
||||
if (redundancyA[16] == redundancyB[16] && redundancyA[27] == redundancyB[27] &&
|
||||
redundancyA[01] == redundancyB[01] && redundancyA[32] == redundancyB[32] &&
|
||||
redundancyA[03] == redundancyB[03] && redundancyA[33] == redundancyB[33] &&
|
||||
redundancyA[13] == redundancyB[13] && redundancyA[07] == redundancyB[07] )
|
||||
{
|
||||
goto END;
|
||||
}
|
||||
|
||||
//end redundancy test, set B to A
|
||||
memcpy (redundancyB, redundancyA, sizeof (redundancyA));
|
||||
|
||||
//Setup for Second AMBE Frame
|
||||
//Interleave Schedule
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
|
||||
//Second AMBE Frame, First Half 18 dibits just before Sync or EmbeddedSignalling
|
||||
for(i = 0; i < 18; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
state->dmr_stereo_payload[i+48] = dibit;
|
||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
//signaling data or sync
|
||||
for(i = 0; i < 24; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
state->dmr_stereo_payload[i+66] = dibit;
|
||||
sync[i] = (dibit | 1) + 48; //how does this work again?
|
||||
|
||||
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
||||
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||
|
||||
//embedded link control
|
||||
if(internalslot == 0 && vc1 > 1 && vc1 < 6) //grab on vc1 values 2-5 B C D and E
|
||||
{
|
||||
state->dmr_embedded_signalling[internalslot][vc1-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
||||
state->dmr_embedded_signalling[internalslot][vc1-1][i*2+1] = (1 & dibit); // bit 0
|
||||
}
|
||||
|
||||
if(internalslot == 1 && vc2 > 1 && vc2 < 6) //grab on vc2 values 2-5 B C D and E
|
||||
{
|
||||
state->dmr_embedded_signalling[internalslot][vc2-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
||||
state->dmr_embedded_signalling[internalslot][vc2-1][i*2+1] = (1 & dibit); // bit 0
|
||||
}
|
||||
|
||||
}
|
||||
sync[24] = 0;
|
||||
|
||||
for(i = 0; i < 8; i++) EmbeddedSignalling[i] = syncdata[i];
|
||||
for(i = 0; i < 8; i++) EmbeddedSignalling[i + 8] = syncdata[i + 40];
|
||||
|
||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||
for(i = 0; i < 18; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
state->dmr_stereo_payload[i+90] = dibit;
|
||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
//Setup for Third AMBE Frame
|
||||
//Interleave Schedule
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
|
||||
//Third AMBE Frame, Full 36
|
||||
for(i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
state->dmr_stereo_payload[i+108] = dibit;
|
||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
//reset vc counters to 1 if new voice sync frame on each slot
|
||||
if ( strcmp (sync, DMR_BS_VOICE_SYNC) == 0)
|
||||
{
|
||||
if (internalslot == 0) vc1 = 1;
|
||||
if (internalslot == 1) vc2 = 1;
|
||||
}
|
||||
|
||||
//check for sync pattern here after collected the rest of the payload, decide what to do with it
|
||||
if ( strcmp (sync, DMR_BS_DATA_SYNC) == 0 )
|
||||
{
|
||||
|
||||
fprintf (stderr,"%s ", getTime());
|
||||
if (internalslot == 0)
|
||||
{
|
||||
if (opts->inverted_dmr == 0)
|
||||
{
|
||||
fprintf (stderr,"Sync: +DMR ");
|
||||
}
|
||||
else fprintf (stderr,"Sync: -DMR ");
|
||||
|
||||
vc1 = 1;
|
||||
}
|
||||
if (internalslot == 1)
|
||||
{
|
||||
if (opts->inverted_dmr == 0)
|
||||
{
|
||||
fprintf (stderr,"Sync: +DMR ");
|
||||
}
|
||||
else fprintf (stderr,"Sync: -DMR ");
|
||||
|
||||
vc2 = 1;
|
||||
|
||||
}
|
||||
dmr_data_sync (opts, state);
|
||||
skipcount++;
|
||||
goto SKIP;
|
||||
}
|
||||
|
||||
//only play voice on no data sync
|
||||
if (strcmp (sync, DMR_BS_DATA_SYNC) != 0) //we already have a tact ecc check, so we won't get here without that, see if there is any other eccs we can run just to make sure
|
||||
{
|
||||
|
||||
//check the embedded signalling, if bad at this point, we probably aren't quite in sync
|
||||
if(QR_16_7_6_decode(EmbeddedSignalling)) emb_ok = 1;
|
||||
else emb_ok = 0;
|
||||
|
||||
//disable the goto END; if this causes more problems than fixing on late entry dual voices i.e., lots of forced resyncs
|
||||
if ( (strcmp (sync, DMR_BS_VOICE_SYNC) != 0) && emb_ok == 0) goto END; //fprintf (stderr, "EMB BAD? ");
|
||||
else if (emb_ok == 1)
|
||||
{
|
||||
uint8_t cc = ((EmbeddedSignalling[0] << 3) + (EmbeddedSignalling[1] << 2) + (EmbeddedSignalling[2] << 1) + EmbeddedSignalling[3]);
|
||||
uint8_t power = EmbeddedSignalling[4];
|
||||
uint8_t lcss = ((EmbeddedSignalling[5] << 1) + EmbeddedSignalling[6]);
|
||||
}
|
||||
|
||||
|
||||
skipcount = 0; //reset skip count if processing voice frames
|
||||
fprintf (stderr,"%s ", getTime());
|
||||
|
||||
//simplifying things
|
||||
char polarity[3];
|
||||
char light[18];
|
||||
uint8_t vc;
|
||||
if (internalslot == 0)
|
||||
{
|
||||
state->dmrburstL = 16;
|
||||
vc = vc1;
|
||||
sprintf (light, "%s", " [SLOT1] slot2 ");
|
||||
}
|
||||
else
|
||||
{
|
||||
state->dmrburstR = 16;
|
||||
vc = vc2;
|
||||
sprintf (light, "%s", " slot1 [SLOT2] ");
|
||||
}
|
||||
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
|
||||
else sprintf (polarity, "%s", "-");
|
||||
|
||||
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC%d ", polarity, light, state->dmr_color_code, vc);
|
||||
|
||||
if (internalslot == 0 && vc1 == 6)
|
||||
{
|
||||
//process embedded link control
|
||||
fprintf (stderr, "\n");
|
||||
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
||||
}
|
||||
|
||||
if (internalslot == 1 && vc2 == 6)
|
||||
{
|
||||
//process embedded link control
|
||||
fprintf (stderr, "\n");
|
||||
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
||||
}
|
||||
|
||||
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
|
||||
|
||||
//copy ambe_fr frames first, running process mbe will correct them,
|
||||
//but this also leads to issues extracting good le mi values when
|
||||
//we go to do correction on them there too
|
||||
memcpy (m1, ambe_fr, sizeof(m1));
|
||||
memcpy (m2, ambe_fr2, sizeof(m2));
|
||||
memcpy (m3, ambe_fr3, sizeof(m3));
|
||||
|
||||
// if ( opts->audio_in_type == 5 && internalslot == 0 && state->dmrburstR != 16 ) //(OSS shim)
|
||||
// {
|
||||
// processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||
// processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||
// processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||
// }
|
||||
// if ( opts->audio_in_type == 5 && internalslot == 1 && state->dmrburstL != 16 ) //(OSS shim)
|
||||
// {
|
||||
// processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||
// processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||
// processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||
// }
|
||||
|
||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||
|
||||
cach_err = dmr_cach (opts, state, cachdata);
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
// run alg refresh after vc6 ambe processing
|
||||
if (internalslot == 0 && vc1 == 6) dmr_alg_refresh (opts, state);
|
||||
if (internalslot == 1 && vc2 == 6) dmr_alg_refresh (opts, state);
|
||||
|
||||
dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
|
||||
|
||||
//increment the vc counters
|
||||
if (internalslot == 0) vc1++;
|
||||
if (internalslot == 1) vc2++;
|
||||
|
||||
//update cc amd vc sync time for trunking purposes (particularly Con+)
|
||||
if (opts->p25_is_tuned == 1)
|
||||
{
|
||||
state->last_vc_sync_time = time(NULL);
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
}
|
||||
|
||||
//reset err checks
|
||||
cach_err = 1;
|
||||
tact_okay = 0;
|
||||
emb_ok = 0;
|
||||
|
||||
//Extra safeguards to break loop
|
||||
// if ( (vc1 > 7 && vc2 > 7) ) goto END;
|
||||
if ( (vc1 > 14 || vc2 > 14) ) goto END;
|
||||
|
||||
}
|
||||
|
||||
SKIP:
|
||||
if (skipcount > 2) //after 2 consecutive data frames, drop back to getFrameSync and process with dmr_data_sync
|
||||
{
|
||||
//set tests to all good so we don't get a bogus/redundant voice error
|
||||
cach_err = 0;
|
||||
tact_okay = 1;
|
||||
emb_ok = 1;
|
||||
goto END;
|
||||
}
|
||||
|
||||
//since we are in a while loop, run ncursesPrinter here.
|
||||
if (opts->use_ncurses_terminal == 1)
|
||||
{
|
||||
ncursesPrinter(opts, state);
|
||||
}
|
||||
|
||||
} // while loop
|
||||
|
||||
END:
|
||||
state->dmr_stereo = 0;
|
||||
state->errs = 0;
|
||||
state->errs2 = 0;
|
||||
state->errs2R = 0;
|
||||
state->errs2 = 0;
|
||||
|
||||
//if we have a tact or emb err, then produce sync pattern/err message
|
||||
if (tact_okay != 1 || emb_ok != 1)
|
||||
{
|
||||
|
||||
fprintf (stderr,"%s ", getTime());
|
||||
fprintf (stderr,"Sync: DMR ");
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, "| VOICE CACH/EMB ERR");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
fprintf (stderr, "\n");
|
||||
//run LFSR if either slot had an active MI in it.
|
||||
if (state->payload_algid >= 0x21)
|
||||
{
|
||||
state->currentslot = 0;
|
||||
LFSR(state);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
if (state->payload_algidR >= 0x21)
|
||||
{
|
||||
state->currentslot = 1;
|
||||
LFSR(state);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
//failsafe to reset all data header and blocks when bad tact or emb
|
||||
dmr_reset_blocks (opts, state);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Process buffered half frame and 2nd half and then jump to full BS decoding
|
||||
void dmrBSBootstrap (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
int i, j, k, l, dibit;
|
||||
int *dibit_p;
|
||||
char ambe_fr[4][24];
|
||||
char ambe_fr2[4][24];
|
||||
char ambe_fr3[4][24];
|
||||
|
||||
//memcpy of ambe_fr for late entry
|
||||
char m1[4][24];
|
||||
char m2[4][24];
|
||||
char m3[4][24];
|
||||
|
||||
const int *w, *x, *y, *z;
|
||||
char sync[25];
|
||||
uint8_t tact_okay = 0;
|
||||
uint8_t cach_err = 0;
|
||||
uint8_t sync_okay = 1;
|
||||
|
||||
|
||||
char cachdata[25];
|
||||
int cachInterleave[24] =
|
||||
{0, 7, 8, 9, 1, 10,
|
||||
11, 12, 2, 13, 14,
|
||||
15, 3, 16, 4, 17, 18,
|
||||
19, 5, 20, 21, 22, 6, 23
|
||||
};
|
||||
//add time to mirror printFrameSync
|
||||
time_t now;
|
||||
char * getTime(void) //get pretty hh:mm:ss timestamp
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
|
||||
char * curr;
|
||||
char * stamp = asctime(localtime( & t));
|
||||
|
||||
curr = strtok(stamp, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
//payload buffer
|
||||
//CACH + First Half Payload + Sync = 12 + 54 + 24
|
||||
dibit_p = state->dmr_payload_p - 90;
|
||||
for (i = 0; i < 90; i++) //90
|
||||
{
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if(opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
state->dmr_stereo_payload[i] = dibit;
|
||||
}
|
||||
|
||||
for(i = 0; i < 12; i++)
|
||||
{
|
||||
dibit = state->dmr_stereo_payload[i];
|
||||
cachdata[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
|
||||
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
||||
}
|
||||
|
||||
//cach tact bits
|
||||
uint8_t tact_bits[7];
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
tact_bits[i] = cachdata[i];
|
||||
}
|
||||
|
||||
//decode and correct tact and compare
|
||||
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
||||
if (tact_okay != 1)
|
||||
{
|
||||
if (opts->aggressive_framesync == 1)
|
||||
{
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
|
||||
state->currentslot = tact_bits[1];
|
||||
|
||||
//Setup for first AMBE Frame
|
||||
|
||||
//Interleave Schedule
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
|
||||
//First AMBE Frame, Full 36
|
||||
for(i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = state->dmr_stereo_payload[i+12];
|
||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
//Setup for Second AMBE Frame
|
||||
|
||||
//Interleave Schedule
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
|
||||
//Second AMBE Frame, First Half 18 dibits just before Sync or EmbeddedSignalling
|
||||
for(i = 0; i < 18; i++)
|
||||
{
|
||||
dibit = state->dmr_stereo_payload[i+48];
|
||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
// signaling data or sync, just redo it
|
||||
for(i = 0; i < 24; i++)
|
||||
{
|
||||
dibit = state->dmr_stereo_payload[i+66];
|
||||
sync[i] = (dibit | 1) + 48;
|
||||
}
|
||||
sync[24] = 0;
|
||||
|
||||
if ( strcmp (sync, DMR_BS_VOICE_SYNC) != 0)
|
||||
{
|
||||
if (opts->aggressive_framesync == 1)
|
||||
{
|
||||
sync_okay = 0;
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
|
||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||
for(i = 0; i < 18; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
//Setup for Third AMBE Frame
|
||||
|
||||
//Interleave Schedule
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
|
||||
//Third AMBE Frame, Full 36
|
||||
for(i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
fprintf (stderr,"%s ", getTime());
|
||||
char polarity[3];
|
||||
char light[18];
|
||||
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
sprintf (light, "%s", " [SLOT1] slot2 ");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (light, "%s", " slot1 [SLOT2] ");
|
||||
}
|
||||
if (opts->inverted_dmr == 0) sprintf (polarity, "%s", "+");
|
||||
else sprintf (polarity, "%s", "-");
|
||||
|
||||
fprintf (stderr,"Sync: %sDMR %s| Color Code=%02d | VC1*", polarity, light, state->dmr_color_code);
|
||||
|
||||
dmr_alg_reset (opts, state);
|
||||
|
||||
//copy ambe_fr frames first, running process mbe will correct them,
|
||||
//but this also leads to issues extracting good le mi values when
|
||||
//we go to do correction on them there too
|
||||
memcpy (m1, ambe_fr, sizeof(m1));
|
||||
memcpy (m2, ambe_fr2, sizeof(m2));
|
||||
memcpy (m3, ambe_fr3, sizeof(m3));
|
||||
|
||||
if (opts->payload == 1) fprintf (stderr, "\n"); //extra line break necessary here
|
||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||
|
||||
//collect the mi fragment
|
||||
dmr_late_entry_mi_fragment (opts, state, 1, ambe_fr, ambe_fr2, ambe_fr3);
|
||||
|
||||
cach_err = dmr_cach (opts, state, cachdata);
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
//update voice sync time for trunking purposes (particularly Con+)
|
||||
if (opts->p25_is_tuned == 1) state->last_vc_sync_time = time(NULL);
|
||||
|
||||
dmrBS (opts, state); //bootstrap into full TDMA frame for BS mode
|
||||
END:
|
||||
//if we have a tact err, then produce sync pattern/err message
|
||||
if (tact_okay != 1 || sync_okay != 1)
|
||||
{
|
||||
fprintf (stderr,"%s ", getTime());
|
||||
fprintf (stderr,"Sync: DMR ");
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, "| VOICE CACH/SYNC ERR");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
//run LFSR if either slot had an active MI in it.
|
||||
if (state->payload_algid >= 0x21)
|
||||
{
|
||||
state->currentslot = 0;
|
||||
LFSR(state);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
if (state->payload_algidR >= 0x21)
|
||||
{
|
||||
state->currentslot = 1;
|
||||
LFSR(state);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
//failsafe to reset all data header and blocks when bad tact or emb
|
||||
dmr_reset_blocks (opts, state);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
960
src/dmr_csbk.c
Normal file
960
src/dmr_csbk.c
Normal file
@@ -0,0 +1,960 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dmr_csbk.c
|
||||
* DMR Control Signal Data PDU (CSBK, MBC, UDT) Handler and Related Functions
|
||||
*
|
||||
* Portions of Connect+ code reworked from Boatbod OP25
|
||||
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
|
||||
*
|
||||
* Portions of Capacity+ code reworked from Eric Cottrell
|
||||
* Source: https://github.com/LinuxSheeple-E/dsd/blob/Feature/DMRECC/dmr_csbk.c
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-12 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
//function for handling Control Signalling PDUs (CSBK, MBC, UDT) messages
|
||||
void dmr_cspdu (dsd_opts * opts, dsd_state * state, uint8_t cs_pdu_bits[], uint8_t cs_pdu[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||
{
|
||||
|
||||
int csbk_lb = 0;
|
||||
int csbk_pf = 0;
|
||||
int csbk_o = 0;
|
||||
int csbk_fid = 0;
|
||||
uint64_t csbk_data = 0;
|
||||
int csbk = 0;
|
||||
|
||||
long int ccfreq = 0;
|
||||
|
||||
if(IrrecoverableErrors == 0 && CRCCorrect == 1)
|
||||
{
|
||||
|
||||
csbk_lb = ( (cs_pdu[0] & 0x80) >> 7 );
|
||||
csbk_pf = ( (cs_pdu[0] & 0x40) >> 6 );
|
||||
csbk_o = cs_pdu[0] & 0x3F;
|
||||
csbk_fid = cs_pdu[1]; //feature set id
|
||||
csbk_pf = ( (cs_pdu[1] & 0x80) >> 7);
|
||||
csbk = ((cs_pdu[0] & 0x3F) << 8) | cs_pdu[1]; //opcode and fid combo set
|
||||
|
||||
//update time to prevent random 'Control Channel Signal Lost' hopping
|
||||
//in the middle of voice call on current Control Channel (con+ and t3)
|
||||
state->last_cc_sync_time = time(NULL);
|
||||
|
||||
if (csbk_pf == 1) //check the protect flag, don't run if set
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, "\n Protected Control Signalling Block(s)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (csbk_pf == 0) //okay to run
|
||||
{
|
||||
|
||||
//set overarching manufacturer in use when non-standard feature id set is up
|
||||
if (csbk_fid != 0) state->dmr_mfid = csbk_fid;
|
||||
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
|
||||
//7.1.1.1.1 Channel Grant CSBK/MBC PDU
|
||||
if (csbk_o > 47 && csbk_o < 55 )
|
||||
{
|
||||
|
||||
//maintain this to allow users to hardset the cc freq as map[0]; otherwise, set from rigctl or rtl freq at c_aloha_sys_parms
|
||||
// if (state->p25_cc_freq == 0 && state->trunk_chan_map[0] != 0) state->p25_cc_freq = state->trunk_chan_map[0];
|
||||
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
long int freq = 0;
|
||||
|
||||
//all of these messages share the same format (thankfully)
|
||||
if (csbk_o == 48) fprintf (stderr, " Private Voice Channel Grant (PV_GRANT)");
|
||||
if (csbk_o == 49) fprintf (stderr, " Talkgroup Voice Channel Grant (TV_GRANT)");
|
||||
if (csbk_o == 50) fprintf (stderr, " Private Broadcast Voice Channel Grant (BTV_GRANT)");
|
||||
if (csbk_o == 51) fprintf (stderr, " Private Data Channel Grant: Single Item (PD_GRANT)");
|
||||
if (csbk_o == 52) fprintf (stderr, " Talkgroup Data Channel Grant: Single Item (TD_GRANT)");
|
||||
if (csbk_o == 53) fprintf (stderr, " Duplex Private Voice Channel Grant (PV_GRANT_DX)");
|
||||
if (csbk_o == 54) fprintf (stderr, " Duplex Private Data Channel Grant (PD_GRANT_DX)");
|
||||
|
||||
//Logical Physical Channel Number
|
||||
uint16_t lpchannum = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 12);
|
||||
if (lpchannum == 0) fprintf (stderr, " - Invalid Channel"); //invalid channel, not sure why this would even be transmitted
|
||||
else if (lpchannum == 0xFFF) fprintf (stderr, " - Absolute"); //This is from an MBC, signalling an absolute and not a logical
|
||||
else fprintf (stderr, " - Logical");
|
||||
|
||||
//dsdplus channel values
|
||||
uint16_t pluschannum = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 13); //lcn bit included
|
||||
pluschannum += 1; //add a one for good measure
|
||||
|
||||
//LCN conveyed here is the tdma timeslot variety, and not the RF frequency variety
|
||||
uint8_t lcn = cs_pdu_bits[28];
|
||||
//the next three bits can have different meanings depending on which item above for context
|
||||
uint8_t st1 = cs_pdu_bits[29]; //late_entry, hi_rate, reserved(dx)
|
||||
uint8_t st2 = cs_pdu_bits[30]; //emergency
|
||||
uint8_t st3 = cs_pdu_bits[31]; //offset, call direction
|
||||
//target and source are always the same bits
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
//added my best guess as to how dsdplus arrives at a dmr channel value (seems pretty consistent) as C+
|
||||
fprintf (stderr, " Ch [%03X] Cd [%04d] C+ [%04d] - TS [%d] - Target [%08d] - Source [%08d]", lpchannum, lpchannum, pluschannum, lcn, target, source);
|
||||
|
||||
if (st2) fprintf (stderr, " Emergency");
|
||||
|
||||
if (lpchannum == 0xFFF) //This is from an MBC, signalling an absolute and not a logical
|
||||
{
|
||||
|
||||
//7.1.1.1.2 Channel Grant Absolute Parameters CG_AP appended MBC PDU
|
||||
uint8_t mbc_lb = cs_pdu_bits[96]; //
|
||||
uint8_t mbc_pf = cs_pdu_bits[97];
|
||||
uint8_t mbc_csbko = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[98], 6);
|
||||
uint8_t mbc_res = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[104], 4);
|
||||
uint8_t mbc_cc = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[108], 4);
|
||||
uint8_t mbc_cdeftype = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[112], 4); //see 7.2.19.7 = 0 for channel parms, 1 through FFFF reserved
|
||||
uint8_t mbc_res2 = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[116], 2);
|
||||
uint64_t mbc_cdefparms = (uint64_t)ConvertBitIntoBytes(&cs_pdu_bits[118], 58); //see 7.2.19.7.1
|
||||
|
||||
//this is how you read the 58 parm bits according to the appendix 7.2.19.7.1
|
||||
if (mbc_cdeftype == 0) //if 0, then absolute channel parms
|
||||
{
|
||||
uint16_t mbc_lpchannum = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[118], 12);
|
||||
uint16_t mbc_abs_tx_int = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[130], 10);
|
||||
uint16_t mbc_abs_tx_step = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[140], 13);
|
||||
uint16_t mbc_abs_rx_int = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[153], 10);
|
||||
uint16_t mbc_abs_rx_step = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[163], 13);
|
||||
|
||||
//tx_int (Mhz) + (tx_step * 125) = tx_freq
|
||||
//rx_int (Mhz) + (rx_step * 125) = rx_freq
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " ABS-CHAN [%03X][%04d] - RX INT [%d][%X] - RX STEP [%d][%X]", mbc_lpchannum, mbc_lpchannum, mbc_abs_rx_int, mbc_abs_rx_int, mbc_abs_rx_step, mbc_abs_rx_step );
|
||||
//DOULBE CHECK APPENDIX C - C.1.1.2 Fixed Channel Plan for accurate information (found it after figuring out calc, so might be worth double checking)
|
||||
//The Frequency we want to tune is the RX Frequency
|
||||
freq = (mbc_abs_rx_int * 1000000 ) + (mbc_abs_rx_step * 125);
|
||||
//tx_freq = (mbc_abs_tx_int * 1000000 ) + (mbc_abs_tx_step * 125);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//print frequency from absolute
|
||||
if (freq != 0 && lpchannum == 0xFFF) fprintf (stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
||||
|
||||
//run external channel map function on logical
|
||||
if (lpchannum != 0 && lpchannum != 0xFFF)
|
||||
{
|
||||
freq = state->trunk_chan_map[lpchannum];
|
||||
(stderr, "\n Frequency [%.6lf] MHz", (double)freq/1000000);
|
||||
}
|
||||
|
||||
//Skip tuning group calls if group calls are disabled
|
||||
if (opts->trunk_tune_group_calls == 0 && csbk_o == 49) goto SKIPCALL;
|
||||
|
||||
//Allow tuning of data calls if user wishes by flipping the csbk_o to a group voice call
|
||||
if (csbk_o == 51 || csbk_o == 52 || csbk_o == 54)
|
||||
{
|
||||
if (opts->trunk_tune_data_calls == 1) csbk_o = 49;
|
||||
}
|
||||
|
||||
//Skip tuning private calls if private calls are disabled
|
||||
if (opts->trunk_tune_private_calls == 0 && csbk_o != 49) goto SKIPCALL;
|
||||
|
||||
//if not a data channel grant (only tuning to voice channel grants)
|
||||
if (csbk_o == 48 || csbk_o == 49 || csbk_o == 50 || csbk_o == 53) //48, 49, 50 are voice grants, 51 and 52 are data grants, 53 Duplex Private Voice, 54 Duplex Private Data
|
||||
{
|
||||
//TIII tuner fix if voice assignment occurs to the control channel itself,
|
||||
//then it may not want to resume tuning due to no framesync loss after call ends
|
||||
if ( (time(NULL) - state->last_vc_sync_time > 2) )
|
||||
{
|
||||
opts->p25_is_tuned = 0;
|
||||
//zero out vc frequencies
|
||||
state->p25_vc_freq[0] = 0;
|
||||
state->p25_vc_freq[1] = 0;
|
||||
}
|
||||
|
||||
//shim in here for ncurses freq display when not trunking (playback, not live)
|
||||
if (opts->p25_trunk == 0 && freq != 0)
|
||||
{
|
||||
//just set to both for now, could go on tslot later
|
||||
state->p25_vc_freq[0] = freq;
|
||||
state->p25_vc_freq[1] = freq;
|
||||
}
|
||||
|
||||
//don't tune if currently a vc on the control channel
|
||||
if ( (time(NULL) - state->last_vc_sync_time > 2) )
|
||||
{
|
||||
char mode[8]; //allow, block, digital, enc, etc
|
||||
|
||||
//if we are using allow/whitelist mode, then write 'B' to mode for block
|
||||
//comparison below will look for an 'A' to write to mode if it is allowed
|
||||
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
||||
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == target)
|
||||
{
|
||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||
strcpy (mode, state->group_array[i].groupMode);
|
||||
}
|
||||
}
|
||||
|
||||
if (state->p25_cc_freq != 0 && opts->p25_trunk == 1 && (strcmp(mode, "B") != 0) && (strcmp(mode, "DE") != 0))
|
||||
{
|
||||
if (freq != 0) //if we have a valid frequency
|
||||
{
|
||||
//RIGCTL
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||
SetFreq(opts->rigctl_sockfd, freq);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||
}
|
||||
|
||||
//rtl_udp
|
||||
else if (opts->audio_in_type == 3)
|
||||
{
|
||||
rtl_udp_tune (opts, state, freq);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq;
|
||||
opts->p25_is_tuned = 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SKIPCALL: ; //do nothing
|
||||
|
||||
}
|
||||
|
||||
//Move
|
||||
if (csbk_o == 57)
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Move (C_MOVE) (TODO)");
|
||||
}
|
||||
|
||||
//Aloha
|
||||
if (csbk_o == 25)
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
uint8_t reserved = cs_pdu_bits[16];
|
||||
uint8_t tsccas = cs_pdu_bits[17];
|
||||
uint8_t sync = cs_pdu_bits[18];
|
||||
uint8_t version = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[19], 3); //Document Version Control
|
||||
uint8_t offset = cs_pdu_bits[22]; //0-TSCC uses aligned timing; 1-TSCC uses offset timing
|
||||
uint8_t active = cs_pdu_bits[23]; //Active_Connection
|
||||
uint8_t mask = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[24], 5);
|
||||
uint8_t sf = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[29], 2); //service function
|
||||
uint8_t nrandwait = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[31], 4);
|
||||
uint8_t regreq = cs_pdu_bits[35];
|
||||
uint8_t backoff = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[36], 4);
|
||||
|
||||
uint8_t model = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 2);
|
||||
uint16_t net = 0;
|
||||
uint16_t site = 0;
|
||||
|
||||
//DMR Location Area - DMRLA
|
||||
uint16_t sys_area = 0;
|
||||
uint16_t sub_area = 0;
|
||||
uint16_t sub_mask = 0x1;
|
||||
//tiny n 1-3; small 1-5; large 1-8; huge 1-10
|
||||
uint16_t n = 1; //The minimum value of DMRLA is normally ≥ 1, 0 is reserved
|
||||
|
||||
|
||||
char model_str[8];
|
||||
char par_str[8]; //category A, B, AB, or reserved
|
||||
|
||||
sprintf (model_str, "%s", " ");
|
||||
sprintf (par_str, "%s", "Res");
|
||||
|
||||
if (model == 0) //Tiny
|
||||
{
|
||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 9);
|
||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[51], 3);
|
||||
sprintf (model_str, "%s", "Tiny");
|
||||
n = 3;
|
||||
}
|
||||
else if (model == 1) //Small
|
||||
{
|
||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 7);
|
||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[49], 5);
|
||||
sprintf (model_str, "%s", "Small");
|
||||
n = 5;
|
||||
}
|
||||
else if (model == 2) //Large
|
||||
{
|
||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 4);
|
||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[46], 8);
|
||||
sprintf (model_str, "%s", "Large");
|
||||
n = 8;
|
||||
}
|
||||
else if (model == 3) //Huge
|
||||
{
|
||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 2);
|
||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[44], 10);
|
||||
sprintf (model_str, "%s", "Huge");
|
||||
n = 10;
|
||||
}
|
||||
|
||||
//honestly can't say that this is accurate, just a guess
|
||||
uint8_t is_capmax = 0; //capmax(mot) flag
|
||||
if (csbk_fid == 0x10)
|
||||
{
|
||||
n = 0;
|
||||
is_capmax = 1;
|
||||
opts->dmr_dmrla_is_set = 1;
|
||||
opts->dmr_dmrla_n = 0;
|
||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
sprintf (state->dmr_branding_sub, "%s", "CapMax ");
|
||||
}
|
||||
|
||||
if (opts->dmr_dmrla_is_set == 1) n = opts->dmr_dmrla_n;
|
||||
|
||||
if (n == 0) sub_mask = 0x0;
|
||||
if (n == 1) sub_mask = 0x1;
|
||||
if (n == 2) sub_mask = 0x3;
|
||||
if (n == 3) sub_mask = 0x7;
|
||||
if (n == 4) sub_mask = 0xF;
|
||||
if (n == 5) sub_mask = 0x1F;
|
||||
if (n == 6) sub_mask = 0x3F;
|
||||
if (n == 7) sub_mask = 0x7F;
|
||||
if (n == 8) sub_mask = 0xFF;
|
||||
if (n == 9) sub_mask = 0x1FF;
|
||||
if (n == 10) sub_mask = 0x3FF;
|
||||
|
||||
uint8_t par = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[54], 2);
|
||||
if (par == 1) sprintf (par_str, "%s", "A ");
|
||||
if (par == 2) sprintf (par_str, "%s", "B ");
|
||||
if (par == 3) sprintf (par_str, "%s", "AB");
|
||||
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
||||
|
||||
if (n != 0) fprintf (stderr, " C_ALOHA_SYS_PARMS - %s - Net ID: %d Site ID: %d.%d Cat: %s\n Reg Req: %d V: %d Mask: %02X MS: %d", model_str, net+1, (site>>n)+1, (site & sub_mask)+1, par_str, regreq, version, mask, target);
|
||||
else fprintf (stderr, " C_ALOHA_SYS_PARMS - %s - Net ID: %d Site ID: %d Cat: %s\n Reg Req: %d V: %d Mask: %02X MS: %d", model_str, net, site, par_str, regreq, version, mask, target);
|
||||
if (is_capmax) fprintf (stderr, " Capacity Max ");
|
||||
|
||||
//add string for ncurses terminal display
|
||||
if (n != 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d.%d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1 );
|
||||
else sprintf (state->dmr_site_parms, "TIII - %s %d-%d ", model_str, net, site);
|
||||
|
||||
//if using rigctl we can set an unknown or updated cc frequency
|
||||
//by polling rigctl for the current frequency
|
||||
if (opts->use_rigctl == 1 && opts->p25_is_tuned == 0) //&& state->p25_cc_freq == 0
|
||||
{
|
||||
ccfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
||||
}
|
||||
|
||||
//if using rtl input, we can ask for the current frequency tuned
|
||||
if (opts->audio_in_type == 3 && opts->p25_is_tuned == 0) //&& state->p25_cc_freq == 0
|
||||
{
|
||||
ccfreq = (long int)opts->rtlsdr_center_freq;
|
||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
||||
}
|
||||
|
||||
//nullify any previous branding sub (bugfix for bad assignments or system type switching)
|
||||
//sprintf(state->dmr_branding_sub, "%s", "");
|
||||
|
||||
//debug print
|
||||
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 16);
|
||||
//fprintf (stderr, "\n SYSCODE: %016b", syscode);
|
||||
//fprintf (stderr, "\n SYSCODE: %02b.%b.%b", model, net, site); //won't show leading zeroes, but may not be required
|
||||
//fprintf (stderr, "\n SYSCODE: %04X", syscode);
|
||||
}
|
||||
|
||||
//P_CLEAR
|
||||
if (csbk_o == 46)
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Clear (P_CLEAR) (TODO)");
|
||||
}
|
||||
|
||||
//(P_PROTECT) - Could be a useful way of determining who is on the current timeslot p_kind 2
|
||||
if (csbk_o == 47)
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Protect (P_PROTECT) -");
|
||||
uint16_t reserved = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 12);
|
||||
uint8_t p_kind = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[28], 3);
|
||||
uint8_t gi = cs_pdu_bits[31];
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
||||
|
||||
if (p_kind == 0) fprintf (stderr, " Disable Target PTT (DIS_PTT)");
|
||||
if (p_kind == 1) fprintf (stderr, " Enable Target PTT (EN_PTT)");
|
||||
if (p_kind == 2) fprintf (stderr, " Private Call (ILLEGALLY_PARKED)");
|
||||
if (p_kind == 3) fprintf (stderr, " Enable Target MS PTT (EN_PTT_ONE_MS)");
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Target [%08d] - Source [%08d]", target, source);
|
||||
|
||||
//Illegally Parked is another way to say a private call is outbound on this channel
|
||||
//so, we can use this as a form of 'link control'
|
||||
if (p_kind == 2)
|
||||
{
|
||||
//don't set this if operating out of dmr mono or using call alert beep
|
||||
if (opts->dmr_mono == 0 && opts->call_alert == 0)
|
||||
{
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
state->lasttg = target;
|
||||
state->lastsrc = source;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->lasttgR = target;
|
||||
state->lastsrcR = source;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (csbk_o == 40) //0x28 //check to see if these are fid 0 only, or also in other manufacturer fids
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Announcements (C_BCAST)"); //site it, vote for etc etc
|
||||
uint8_t a_type = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 5);
|
||||
if (a_type == 0) fprintf (stderr, " Announce/Withdraw TSCC (Ann_WD_TSCC)");
|
||||
if (a_type == 1) fprintf (stderr, " Specify Call Timer Parameters (CallTimer_Parms)");
|
||||
if (a_type == 2) fprintf (stderr, " Vote Now Advice (Vote_Now)");
|
||||
if (a_type == 3) fprintf (stderr, " Broadcast Local Time (Local_Time)");
|
||||
if (a_type == 4) fprintf (stderr, " Mass Registration (MassReg)");
|
||||
if (a_type == 5) fprintf (stderr, " Announce Logical Channel/Frequency Relationship (Chan_Freq)");
|
||||
if (a_type == 6) fprintf (stderr, " Adjacent Site Information (Adjacent_Site)");
|
||||
if (a_type == 7) fprintf (stderr, " General Site Parameters (Gen_Site_Params)");
|
||||
if (a_type > 7 && a_type < 0x1E) fprintf (stderr, " Reserved %02X", a_type);
|
||||
if (a_type == 0x1E || a_type == 0x1F) fprintf (stderr, " Manufacturer Specific %02X", a_type);
|
||||
|
||||
//sysidcode, probably would have been easier to make this its own external function
|
||||
uint8_t model = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 2);
|
||||
uint16_t net = 0;
|
||||
uint16_t site = 0;
|
||||
|
||||
//DMR Location Area - DMRLA
|
||||
uint16_t sys_area = 0;
|
||||
uint16_t sub_area = 0;
|
||||
uint16_t sub_mask = 0x1;
|
||||
//tiny n 1-3; small 1-5; large 1-8; huge 1-10
|
||||
uint16_t n = 1; //The minimum value of DMRLA is normally ≥ 1, 0 is reserved
|
||||
|
||||
//channel number from Broadcast Parms 2
|
||||
uint16_t lpchannum = 0;
|
||||
|
||||
char model_str[8];
|
||||
char par_str[8]; //category A, B, AB, or reserved
|
||||
|
||||
sprintf (model_str, "%s", " ");
|
||||
sprintf (par_str, "%s", "Res");
|
||||
|
||||
if (model == 0) //Tiny
|
||||
{
|
||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 9);
|
||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[51], 3);
|
||||
sprintf (model_str, "%s", "Tiny");
|
||||
n = 3;
|
||||
}
|
||||
else if (model == 1) //Small
|
||||
{
|
||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 7);
|
||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[49], 5);
|
||||
sprintf (model_str, "%s", "Small");
|
||||
n = 5;
|
||||
}
|
||||
else if (model == 2) //Large
|
||||
{
|
||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 4);
|
||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[46], 8);
|
||||
sprintf (model_str, "%s", "Large");
|
||||
n = 8;
|
||||
}
|
||||
else if (model == 3) //Huge
|
||||
{
|
||||
net = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[42], 2);
|
||||
site = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[44], 10);
|
||||
sprintf (model_str, "%s", "Huge");
|
||||
n = 10;
|
||||
}
|
||||
|
||||
//disabled until this can be sorted/fleshed out by type, going to use aloha only for now
|
||||
|
||||
//honestly can't say that this is accurate, just a guess
|
||||
// uint8_t is_capmax = 0; //capmax(mot) flag
|
||||
// if (csbk_fid == 0x10)
|
||||
// {
|
||||
// n = 0;
|
||||
// is_capmax = 1;
|
||||
// opts->dmr_dmrla_is_set = 1;
|
||||
// opts->dmr_dmrla_n = 0;
|
||||
// sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
// sprintf (state->dmr_branding_sub, "%s", "CapMax ");
|
||||
// }
|
||||
|
||||
if (opts->dmr_dmrla_is_set == 1) n = opts->dmr_dmrla_n;
|
||||
|
||||
if (n == 0) sub_mask = 0x0;
|
||||
if (n == 1) sub_mask = 0x1;
|
||||
if (n == 2) sub_mask = 0x3;
|
||||
if (n == 3) sub_mask = 0x7;
|
||||
if (n == 4) sub_mask = 0xF;
|
||||
if (n == 5) sub_mask = 0x1F;
|
||||
if (n == 6) sub_mask = 0x3F;
|
||||
if (n == 7) sub_mask = 0x7F;
|
||||
if (n == 8) sub_mask = 0xFF;
|
||||
if (n == 9) sub_mask = 0x1FF;
|
||||
if (n == 10) sub_mask = 0x3FF;
|
||||
|
||||
uint8_t par = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[54], 2);
|
||||
if (par == 1) sprintf (par_str, "%s", "A ");
|
||||
if (par == 2) sprintf (par_str, "%s", "B ");
|
||||
if (par == 3) sprintf (par_str, "%s", "AB");
|
||||
|
||||
uint32_t parms2 = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
||||
lpchannum = parms2 & 0xFFF; //7.2.19.7, for DSDPlus, this value is (lpchannum << 1) + 1;
|
||||
|
||||
//disabled until this can be sorted/fleshed out by type, going to use aloha only for now
|
||||
|
||||
// if (a_type == 2 || a_type == 7)
|
||||
// {
|
||||
// fprintf (stderr, "\n");
|
||||
// if (n != 0) fprintf (stderr, " C_BCAST_SYS_PARMS - %s - Net ID: %d Site ID: %d.%d Cat: %s Cd: %d C+: %d", model_str, net+1, (site>>n)+1, (site & sub_mask)+1, par_str, lpchannum, (lpchannum << 1)+1 );
|
||||
// else fprintf (stderr, " C_BCAST_SYS_PARMS - %s - Net ID: %d Site ID: %d Cat: %s Cd: %d C+: %d", model_str, net, site, par_str, lpchannum, (lpchannum << 1)+1 );
|
||||
// if (is_capmax) fprintf (stderr, " Capacity Max");
|
||||
|
||||
// //add string for ncurses terminal display, if not adjacent site info
|
||||
// if (a_type != 6 && n != 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d.%d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1);
|
||||
// if (a_type != 6 && n == 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d ", model_str, net, site);
|
||||
// }
|
||||
|
||||
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&cs_pdu_bits[40], 16);
|
||||
//fprintf (stderr, "\n SYSCODE: %016b", syscode);
|
||||
//fprintf (stderr, "\n SYSCODE: %04X", syscode);
|
||||
|
||||
}
|
||||
|
||||
if (csbk_o == 28)
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " C_AHOY (TODO) ");
|
||||
}
|
||||
|
||||
if (csbk_o == 42)
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " P_MAINT (TODO) ");
|
||||
}
|
||||
|
||||
if (csbk_o == 30)
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " C_ACKVIT (TODO) ");
|
||||
}
|
||||
|
||||
if (csbk_o == 31)
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " C_RAND (TODO) ");
|
||||
}
|
||||
|
||||
//tier 2 csbks
|
||||
if (csbk_o == 4)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Unit to Unit Voice Service Request (UU_V_Req) - ");
|
||||
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
||||
fprintf (stderr, "Target [%d] - Source [%d] ", target, source);
|
||||
}
|
||||
|
||||
if (csbk_o == 5)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Unit to Unit Voice Service Answer Response (UU_Ans_Req) - ");
|
||||
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
||||
fprintf (stderr, "Target [%d] - Source [%d] ", target, source);
|
||||
|
||||
}
|
||||
|
||||
if (csbk_o == 7)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Channel Timing CSBK (CT_CSBK) ");
|
||||
}
|
||||
|
||||
if (csbk_o == 38)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Negative Acknowledgement Response (NACK_Rsp) - ");
|
||||
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
||||
fprintf (stderr, "Target [%d] - Source [%d] ", target, source);
|
||||
|
||||
}
|
||||
|
||||
if (csbk_o == 56)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " BS Outbound Activation (BS_Dwn_Act) - ");
|
||||
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
||||
fprintf (stderr, "Target [%d] - Source [%d] ", target, source);
|
||||
}
|
||||
|
||||
if (csbk_o == 61)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Preamble CSBK - ");
|
||||
uint8_t content = cs_pdu_bits[16];
|
||||
uint8_t gi = cs_pdu_bits[17];
|
||||
uint8_t res = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[18], 6);
|
||||
uint8_t blocks = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[24], 8);
|
||||
uint32_t target = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
||||
uint32_t source = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[56], 24);
|
||||
|
||||
if (gi == 0) fprintf (stderr, "Individual ");
|
||||
else fprintf (stderr, "Group ");
|
||||
|
||||
if (content == 0) fprintf (stderr, "CSBK - ");
|
||||
else fprintf (stderr, "Data - ");
|
||||
|
||||
fprintf (stderr, "Target [%d] - Source [%d] ", target, source);
|
||||
}
|
||||
//end tier 2 csbks
|
||||
|
||||
}
|
||||
|
||||
//Capacity+ Section
|
||||
if (csbk_fid == 0x10)
|
||||
{
|
||||
//not quite sure how these tuning rules will go over
|
||||
//if they don't work so well, may just fall back to
|
||||
//a 'follow rest channel on no sync' only approach
|
||||
if (csbk_o == 0x3E)
|
||||
{
|
||||
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
uint8_t fl = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[16], 2);
|
||||
uint8_t slot = cs_pdu_bits[18];
|
||||
uint8_t rest_channel = cs_pdu[2] & 0xF; //0xF, or 0x1F?
|
||||
uint8_t ch[8]; //one bit per channel
|
||||
uint8_t tg = 0;
|
||||
uint32_t tghex = 0; //combined all tgs for debug
|
||||
int i, j, k;
|
||||
|
||||
//tg and channel info for trunking purposes
|
||||
uint8_t t_tg[9];
|
||||
memset (t_tg, 0, sizeof(t_tg));
|
||||
|
||||
k = 0;
|
||||
if (rest_channel != state->dmr_rest_channel)
|
||||
{
|
||||
state->dmr_rest_channel = rest_channel;
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
ch[i] = cs_pdu_bits[i+24];
|
||||
}
|
||||
|
||||
//assign to cc freq to follow during no sync
|
||||
if (state->trunk_chan_map[rest_channel] != 0)
|
||||
{
|
||||
state->p25_cc_freq = state->trunk_chan_map[rest_channel];
|
||||
//set to always tuned
|
||||
opts->p25_is_tuned = 1;
|
||||
}
|
||||
|
||||
fprintf (stderr, " Capacity Plus Channel Status - Rest Channel %d", rest_channel);
|
||||
|
||||
fprintf (stderr, "\n ");
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
fprintf (stderr, "Ch%d: ", i+1);
|
||||
if (ch[i] != 0)
|
||||
{
|
||||
tg = (uint8_t)ConvertBitIntoBytes(&cs_pdu_bits[k*8+32], 8);
|
||||
fprintf (stderr, " %03d ", tg);
|
||||
//add values to trunking tg/channel potentials
|
||||
t_tg[i] = tg;
|
||||
k++;
|
||||
|
||||
}
|
||||
else if (i+1 == rest_channel) fprintf (stderr, "Rest ");
|
||||
else fprintf (stderr, "Idle ");
|
||||
|
||||
if (i == 3) fprintf (stderr, "\n ");
|
||||
}
|
||||
tghex = (uint32_t)ConvertBitIntoBytes(&cs_pdu_bits[32], 24);
|
||||
//fprintf (stderr, "\n TG Hex = 0x%06X", tghex);
|
||||
state->dmr_mfid = 0x10;
|
||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
sprintf (state->dmr_branding_sub, "%s", "Cap+ ");
|
||||
|
||||
//nullify any previous TIII data (bugfix for bad assignments or system type switching)
|
||||
sprintf(state->dmr_site_parms, "%s", "");
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//Skip tuning group calls if group calls are disabled
|
||||
if (opts->trunk_tune_group_calls == 0) goto SKIPCAP;
|
||||
|
||||
//don't tune if currently a vc on the current channel
|
||||
if ( (time(NULL) - state->last_vc_sync_time > 2) )
|
||||
{
|
||||
for (j = 0; j < 8; j++) //go through the channels stored looking for active ones to tune to
|
||||
{
|
||||
char mode[8]; //allow, block, digital, enc, etc
|
||||
|
||||
//if we are using allow/whitelist mode, then write 'B' to mode for block
|
||||
//comparison below will look for an 'A' to write to mode if it is allowed
|
||||
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
||||
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == t_tg[j])
|
||||
{
|
||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||
strcpy (mode, state->group_array[i].groupMode);
|
||||
}
|
||||
}
|
||||
|
||||
//no more 0 reporting, that was some bad code that caused that issue
|
||||
//without priority, this will tune the first one it finds (if group isn't blocked)
|
||||
if (t_tg[j] != 0 && state->p25_cc_freq != 0 && opts->p25_trunk == 1 && (strcmp(mode, "B") != 0) && (strcmp(mode, "DE") != 0))
|
||||
{
|
||||
if (state->trunk_chan_map[j+1] != 0) //if we have a valid frequency
|
||||
{
|
||||
//RIGCTL
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||
SetFreq(opts->rigctl_sockfd, state->trunk_chan_map[j+1]);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+1];
|
||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||
j = 11; //break loop
|
||||
}
|
||||
|
||||
//rtl_udp
|
||||
else if (opts->audio_in_type == 3)
|
||||
{
|
||||
rtl_udp_tune (opts, state, state->trunk_chan_map[j+1]);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[j+1];
|
||||
opts->p25_is_tuned = 1;
|
||||
j = 11; //break loop
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
SKIPCAP: ; //do nothing
|
||||
}
|
||||
|
||||
//Connect+ Section
|
||||
if (csbk_fid == 0x06)
|
||||
{
|
||||
|
||||
if (csbk_o == 0x01)
|
||||
{
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
uint8_t nb1 = cs_pdu[2] & 0x3F;
|
||||
uint8_t nb2 = cs_pdu[3] & 0x3F;
|
||||
uint8_t nb3 = cs_pdu[4] & 0x3F;
|
||||
uint8_t nb4 = cs_pdu[5] & 0x3F;
|
||||
uint8_t nb5 = cs_pdu[6] & 0x3F;
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Connect Plus Neighbors\n");
|
||||
fprintf (stderr, " NB1(%02d), NB2(%02d), NB3(%02d), NB4(%02d), NB5(%02d)", nb1, nb2, nb3, nb4, nb5);
|
||||
state->dmr_mfid = 0x06;
|
||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
sprintf(state->dmr_branding_sub, "Con+ ");
|
||||
|
||||
}
|
||||
|
||||
if (csbk_o == 0x03)
|
||||
{
|
||||
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
uint32_t srcAddr = ( (cs_pdu[2] << 16) + (cs_pdu[3] << 8) + cs_pdu[4] );
|
||||
uint32_t grpAddr = ( (cs_pdu[5] << 16) + (cs_pdu[6] << 8) + cs_pdu[7] );
|
||||
uint8_t lcn = ( (cs_pdu[8] & 0xF0 ) >> 4 );
|
||||
uint8_t tslot = ( (cs_pdu[8] & 0x08 ) >> 3 );
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Connect Plus Voice Channel Grant\n");
|
||||
fprintf (stderr, " srcAddr(%8d), grpAddr(%8d), LCN(%d), TS(%d)",srcAddr, grpAddr, lcn, tslot);
|
||||
state->dmr_mfid = 0x06;
|
||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
sprintf(state->dmr_branding_sub, "Con+ ");
|
||||
|
||||
//Skip tuning group calls if group calls are disabled
|
||||
if (opts->trunk_tune_group_calls == 0) goto SKIPCON;
|
||||
|
||||
//if using rigctl we can set an unknown or updated cc frequency
|
||||
//by polling rigctl for the current frequency
|
||||
if (opts->use_rigctl == 1 && opts->p25_is_tuned == 0) //&& state->p25_cc_freq == 0
|
||||
{
|
||||
ccfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
||||
}
|
||||
|
||||
//if using rtl input, we can ask for the current frequency tuned
|
||||
if (opts->audio_in_type == 3 && opts->p25_is_tuned == 0) //&& state->p25_cc_freq == 0
|
||||
{
|
||||
ccfreq = (long int)opts->rtlsdr_center_freq;
|
||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
||||
}
|
||||
|
||||
//shim in here for ncurses freq display when not trunking (playback, not live)
|
||||
if (opts->p25_trunk == 0 && state->trunk_chan_map[lcn] != 0)
|
||||
{
|
||||
//just set to both for now, could go on tslot later
|
||||
state->p25_vc_freq[0] = state->trunk_chan_map[lcn];
|
||||
state->p25_vc_freq[1] = state->trunk_chan_map[lcn];
|
||||
}
|
||||
|
||||
char mode[8]; //allow, block, digital, enc, etc
|
||||
|
||||
//if we are using allow/whitelist mode, then write 'B' to mode for block
|
||||
//comparison below will look for an 'A' to write to mode if it is allowed
|
||||
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
||||
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == grpAddr)
|
||||
{
|
||||
fprintf (stderr, " [%s]", state->group_array[i].groupName);
|
||||
strcpy (mode, state->group_array[i].groupMode);
|
||||
}
|
||||
}
|
||||
|
||||
//don't tune if currently a vc on the control channel
|
||||
if ( (opts->trunk_tune_group_calls == 1) && (time(NULL) - state->last_vc_sync_time > 2) )
|
||||
{
|
||||
|
||||
if (state->p25_cc_freq != 0 && opts->p25_trunk == 1 && (strcmp(mode, "B") != 0) && (strcmp(mode, "DE") != 0) )
|
||||
{
|
||||
if (state->trunk_chan_map[lcn] != 0) //if we have a valid frequency
|
||||
{
|
||||
//RIGCTL
|
||||
if (opts->use_rigctl == 1)
|
||||
{
|
||||
if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||
SetFreq(opts->rigctl_sockfd, state->trunk_chan_map[lcn]);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[lcn];
|
||||
opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop
|
||||
state->is_con_plus = 1; //flag on
|
||||
state->last_vc_sync_time = time(NULL); //bugfix: set sync here so we don't immediately tune back to CC constantly.
|
||||
}
|
||||
|
||||
//rtl_udp
|
||||
else if (opts->audio_in_type == 3)
|
||||
{
|
||||
rtl_udp_tune (opts, state, state->trunk_chan_map[lcn]);
|
||||
state->p25_vc_freq[0] = state->p25_vc_freq[1] = state->trunk_chan_map[lcn];
|
||||
opts->p25_is_tuned = 1;
|
||||
state->is_con_plus = 1; //flag on
|
||||
state->last_vc_sync_time = time(NULL); //bugfix: set sync here so we don't immediately tune back to CC constantly.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SKIPCON: ; //do nothing
|
||||
|
||||
}
|
||||
|
||||
//upon further examination, opcodes 6 and C (12) do not appear to be a channel grants or tlcs,
|
||||
//it is present in Connect+, but its purpose isn't entirely clear
|
||||
//DSDPlus only regards it as a 'CSBK [LB=1 CSBKO=6 (?) FID=06 v1=115213 v2=10488576 v3=0]
|
||||
//unsure of what the v1 v2 v3 fields specify
|
||||
// if (csbk_o == 0x06 || csbk_o == 0x0C)
|
||||
if (csbk_o != 0x01 && csbk_o != 0x03)
|
||||
{
|
||||
|
||||
uint32_t v1 = ( (cs_pdu[2] << 16) + (cs_pdu[3] << 8) + cs_pdu[4] );
|
||||
uint32_t v2 = ( (cs_pdu[5] << 16) + (cs_pdu[6] << 8) + cs_pdu[7] );
|
||||
uint32_t v3 = ( (cs_pdu[7] << 16) + (cs_pdu[8] << 8) + cs_pdu[9] );
|
||||
|
||||
// debug print
|
||||
// initial line break
|
||||
// fprintf (stderr, "\n");
|
||||
// fprintf (stderr, "%s", KYEL);
|
||||
// fprintf (stderr, " Connect Plus CSBK 0x%X\n", csbk_o);
|
||||
// fprintf (stderr, " v1(%d), v2(%d), v3(%d)",v1, v2, v3);
|
||||
// state->dmr_mfid = 0x06;
|
||||
// sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
// sprintf(state->dmr_branding_sub, "Con+ ");
|
||||
}
|
||||
|
||||
//the validity of these last three csbk opcodes cannot be confirmed
|
||||
//I recall making these based on observation and speculation long ago,
|
||||
//but cannot verify the accuracy of them now, so they will remain disabled
|
||||
//they offer no particular interest to trunking/listening
|
||||
|
||||
// if (csbk_o == 0x11)
|
||||
// {
|
||||
// //initial line break
|
||||
// fprintf (stderr, "\n");
|
||||
// fprintf (stderr, "%s", KYEL);
|
||||
// fprintf (stderr, " Connect Plus Registration Request");
|
||||
// state->dmr_mfid = 0x06;
|
||||
// sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
// sprintf(state->dmr_branding_sub, "Con+ ");
|
||||
// }
|
||||
|
||||
// if (csbk_o == 0x12)
|
||||
// {
|
||||
// //initial line break
|
||||
// fprintf (stderr, "\n");
|
||||
// fprintf (stderr, "%s", KYEL);
|
||||
// fprintf (stderr, " Connect Plus Registration Response");
|
||||
// state->dmr_mfid = 0x06;
|
||||
// sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
// sprintf(state->dmr_branding_sub, "Con+ ");
|
||||
// }
|
||||
|
||||
// if (csbk_o == 0x18)
|
||||
// {
|
||||
// //initial line break
|
||||
// fprintf (stderr, "\n");
|
||||
// fprintf (stderr, "%s", KYEL);
|
||||
// fprintf (stderr, " Connect Plus Talkgroup Affiliation");
|
||||
// state->dmr_mfid = 0x06;
|
||||
// sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
// sprintf(state->dmr_branding_sub, "Con+ ");
|
||||
// }
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
END:
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
345
src/dmr_data.c
Normal file
345
src/dmr_data.c
Normal file
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
dmr_data_sync (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int i, dibit;
|
||||
int *dibit_p;
|
||||
char sync[25];
|
||||
char syncdata[48];
|
||||
char cachdata[25];
|
||||
|
||||
char bursttype[5];
|
||||
uint8_t burst;
|
||||
char info[196];
|
||||
unsigned char SlotType[20];
|
||||
unsigned int SlotTypeOk;
|
||||
uint8_t cach_err = 0;
|
||||
|
||||
int cachInterleave[24] =
|
||||
{0, 7, 8, 9, 1, 10,
|
||||
11, 12, 2, 13, 14,
|
||||
15, 3, 16, 4, 17, 18,
|
||||
19, 5, 20, 21, 22, 6, 23
|
||||
};
|
||||
|
||||
dibit_p = state->dmr_payload_p - 90;
|
||||
|
||||
//collect cach and de-interleave
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
if (state->dmr_stereo == 1)
|
||||
{
|
||||
dibit = (int)state->dmr_stereo_payload[i];
|
||||
}
|
||||
|
||||
cachdata[cachInterleave[(i*2)]] = (1 & (dibit >> 1)); // bit 1
|
||||
cachdata[cachInterleave[(i*2)+1]] = (1 & dibit); // bit 0
|
||||
|
||||
}
|
||||
|
||||
//seperate tact bits from cach
|
||||
uint8_t tact_bits[7];
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
tact_bits[i] = cachdata[i];
|
||||
}
|
||||
|
||||
//run hamming on tact bits
|
||||
int cach_okay = -1;
|
||||
if ( Hamming_7_4_decode (tact_bits) ) cach_okay = 1;
|
||||
else
|
||||
{
|
||||
cach_okay = -1;
|
||||
if (opts->aggressive_framesync == 1)
|
||||
{
|
||||
SlotTypeOk = 0;
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
|
||||
state->currentslot = tact_bits[1];
|
||||
|
||||
//in the future, maybe we will remove the hard set on this
|
||||
if (state->dmr_ms_mode == 1)
|
||||
{
|
||||
state->currentslot = 0;
|
||||
}
|
||||
|
||||
// Current slot - First half - Data Payload - 1st part
|
||||
for (i = 0; i < 49; i++)
|
||||
{
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
if (state->dmr_stereo == 1)
|
||||
{
|
||||
dibit = (int)state->dmr_stereo_payload[i+12];
|
||||
}
|
||||
info[2*i] = (1 & (dibit >> 1)); // bit 1
|
||||
info[(2*i)+1] = (1 & dibit); // bit 0
|
||||
}
|
||||
|
||||
// slot type
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
if (state->dmr_stereo == 1)
|
||||
{
|
||||
dibit = (int)state->dmr_stereo_payload[61];
|
||||
}
|
||||
|
||||
SlotType[0] = (1 & (dibit >> 1)); // bit 1
|
||||
SlotType[1] = (1 & dibit); // bit 0
|
||||
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
if (state->dmr_stereo == 1)
|
||||
{
|
||||
dibit = (int)state->dmr_stereo_payload[62];
|
||||
}
|
||||
|
||||
SlotType[2] = (1 & (dibit >> 1)); // bit 1
|
||||
SlotType[3] = (1 & dibit); // bit 0
|
||||
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
if (state->dmr_stereo == 1) //state
|
||||
{
|
||||
dibit = (int)state->dmr_stereo_payload[63];
|
||||
}
|
||||
|
||||
SlotType[4] = (1 & (dibit >> 1)); // bit 1
|
||||
SlotType[5] = (1 & dibit); // bit 0
|
||||
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
if (state->dmr_stereo == 1) //state
|
||||
{
|
||||
dibit = (int)state->dmr_stereo_payload[64];
|
||||
}
|
||||
|
||||
SlotType[6] = (1 & (dibit >> 1)); // bit 1
|
||||
SlotType[7] = (1 & dibit); // bit 0
|
||||
|
||||
// Parity bit
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
if (state->dmr_stereo == 1)
|
||||
{
|
||||
dibit = (int)state->dmr_stereo_payload[65];
|
||||
}
|
||||
SlotType[8] = (1 & (dibit >> 1)); // bit 1
|
||||
SlotType[9] = (1 & dibit); // bit 0
|
||||
|
||||
// signaling data or sync
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
if (state->dmr_stereo == 1)
|
||||
{
|
||||
dibit = (int)state->dmr_stereo_payload[i+66];
|
||||
}
|
||||
syncdata[2*i] = (1 & (dibit >> 1)); // bit 1
|
||||
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||
sync[i] = (dibit | 1) + 48;
|
||||
}
|
||||
sync[24] = 0;
|
||||
|
||||
if((strcmp (sync, DMR_BS_DATA_SYNC) == 0) )
|
||||
{
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
sprintf(state->slot1light, "[slot1]");
|
||||
sprintf(state->slot2light, " slot2 ");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(state->slot1light, " slot1 ");
|
||||
sprintf(state->slot2light, "[slot2]");
|
||||
}
|
||||
}
|
||||
|
||||
else if(strcmp (sync, DMR_DIRECT_MODE_TS1_DATA_SYNC) == 0)
|
||||
{
|
||||
state->currentslot = 0;
|
||||
sprintf(state->slot1light, "[sLoT1]");
|
||||
sprintf(state->slot2light, "[DMODE]");
|
||||
}
|
||||
|
||||
else if(strcmp (sync, DMR_DIRECT_MODE_TS2_DATA_SYNC) == 0)
|
||||
{
|
||||
state->currentslot = 1;
|
||||
sprintf(state->slot1light, "[DMODE]");
|
||||
sprintf(state->slot2light, "[sLoT2]");
|
||||
}
|
||||
|
||||
if (state->dmr_ms_mode == 0)
|
||||
{
|
||||
fprintf(stderr, "%s %s ", state->slot1light, state->slot2light);
|
||||
}
|
||||
|
||||
// Slot type - Second part - Parity bit
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
if (state->dmr_stereo == 0)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
}
|
||||
if (state->dmr_stereo == 1)
|
||||
{
|
||||
dibit = (int)state->dmr_stereo_payload[i+90];
|
||||
}
|
||||
SlotType[(i*2) + 10] = (1 & (dibit >> 1)); // bit 1
|
||||
SlotType[(i*2) + 11] = (1 & dibit); // bit 0
|
||||
}
|
||||
|
||||
/* Check and correct the SlotType (apply Golay(20,8) FEC check) */
|
||||
|
||||
// golay (20,8) hamming-weight of 6 reliably corrects at most 2 bit-errors
|
||||
if( Golay_20_8_decode(SlotType) ) SlotTypeOk = 1;
|
||||
else
|
||||
{
|
||||
SlotTypeOk = -1;
|
||||
if (opts->aggressive_framesync == 1)
|
||||
{
|
||||
SlotTypeOk = 0;
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
|
||||
state->color_code = (SlotType[0] << 3) + (SlotType[1] << 2) +(SlotType[2] << 1) + (SlotType[3] << 0);
|
||||
state->color_code_ok = SlotTypeOk;
|
||||
|
||||
//not sure why I still have two variables for this, need to look and see what state->color_code still ties into
|
||||
if (SlotTypeOk == 1) state->dmr_color_code = state->color_code;
|
||||
|
||||
/* Reconstitute the burst type */
|
||||
burst = (uint8_t)((SlotType[4] << 3) + (SlotType[5] << 2) + (SlotType[6] << 1) + SlotType[7]);
|
||||
if (state->currentslot == 0) state->dmrburstL = burst;
|
||||
if (state->currentslot == 1) state->dmrburstR = burst;
|
||||
|
||||
|
||||
// Current slot - Second Half - Data Payload - 2nd part
|
||||
for (i = 0; i < 49; i++)
|
||||
{
|
||||
if (state->dmr_stereo == 0)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
}
|
||||
|
||||
if (state->dmr_stereo == 1)
|
||||
{
|
||||
dibit = (int)state->dmr_stereo_payload[i+95];
|
||||
}
|
||||
|
||||
info[(2*i) + 98] = (1 & (dibit >> 1)); // bit 1
|
||||
info[(2*i) + 99] = (1 & dibit); // bit 0
|
||||
}
|
||||
|
||||
dmr_data_burst_handler(opts, state, (uint8_t *)info, burst);
|
||||
|
||||
//don't run cach on simplex or mono
|
||||
if (state->dmr_ms_mode == 0 || opts->dmr_mono == 0)
|
||||
{
|
||||
cach_err = dmr_cach (opts, state, cachdata);
|
||||
}
|
||||
|
||||
//ending line break
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
END:
|
||||
if (SlotTypeOk == 0 || cach_okay != 1)
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, "| CACH/Burst FEC ERR");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
fprintf (stderr, "\n");
|
||||
dmr_reset_blocks (opts, state); //failsafe to reset all data header and blocks when bad tact or slottype
|
||||
}
|
||||
|
||||
// Skip cach (24 bit = 12 dibit) and next slot 1st half (98 + 10 bit = 49 + 5 dibit)
|
||||
if (state->dmr_stereo == 0)
|
||||
{
|
||||
skipDibit (opts, state, 12 + 49 + 5);
|
||||
}
|
||||
|
||||
//if using con+ trunking and last voice observed more than x seconds ago, snap back to con+ cc
|
||||
//con+ voice channels can have extremely long idle periods without properly tearing down
|
||||
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1 && state->is_con_plus == 1)
|
||||
{
|
||||
//bug fixed that caused hopping from CC to VC; decreased to 2 seconds.
|
||||
if ( (time(NULL) - state->last_vc_sync_time > 2) )
|
||||
{
|
||||
if (opts->use_rigctl == 1) //rigctl tuning
|
||||
{
|
||||
if (opts->setmod_bw != 0) SetModulation(opts->rigctl_sockfd, opts->setmod_bw);
|
||||
SetFreq(opts->rigctl_sockfd, state->p25_cc_freq);
|
||||
}
|
||||
|
||||
else if (opts->audio_in_type == 3) //rtl_fm tuning
|
||||
{
|
||||
//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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
447
src/dmr_dburst.c
Normal file
447
src/dmr_dburst.c
Normal file
@@ -0,0 +1,447 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dmr_dburst.c
|
||||
* DMR Data Burst Handling and related BPTC/FEC/CRC Functions
|
||||
*
|
||||
* Portions of BPTC/FEC/CRC code from LouisErigHerve
|
||||
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-12 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
//TODO: CRC9 on confirmed data blocks; CRC32 on completed data sf;
|
||||
//TODO: Reverse Channel Signalling - RC single burst BPTC/7-bit CRC
|
||||
//TODO: Single Burst Embedded LC - Non-RC single burst LC - Look for Late Entry Alg/Key
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void dmr_data_burst_handler(dsd_opts * opts, dsd_state * state, uint8_t info[196], uint8_t databurst)
|
||||
{
|
||||
|
||||
uint32_t i, j, k;
|
||||
uint32_t CRCExtracted = 0;
|
||||
uint32_t CRCComputed = 0;
|
||||
uint32_t CRCCorrect = 0;
|
||||
uint32_t IrrecoverableErrors = 0;
|
||||
uint8_t slot = state->currentslot;
|
||||
uint8_t block = state->data_block_counter[slot];
|
||||
|
||||
//BPTC 196x96 Specific
|
||||
uint8_t BPTCDeInteleavedData[196];
|
||||
uint8_t BPTCDmrDataBit[96];
|
||||
uint8_t BPTCDmrDataByte[12];
|
||||
|
||||
//Embedded Signalling Specific
|
||||
uint8_t BptcDataMatrix[8][16];
|
||||
uint8_t LC_DataBit[77];
|
||||
uint8_t LC_DataBytes[10];
|
||||
int Burst = -1;
|
||||
|
||||
//Unified PDU (is this the best nomenclature) Bytes
|
||||
uint8_t DMR_PDU[25]; //using 25 bytes as the max for now
|
||||
uint8_t DMR_PDU_bits[196]; //bitwise version of unified payload, will probably be a lot easier to use this w/ manual
|
||||
memset (DMR_PDU, 0, sizeof (DMR_PDU));
|
||||
memset (DMR_PDU_bits, 0, sizeof (DMR_PDU_bits));
|
||||
|
||||
uint8_t R[3];
|
||||
uint8_t BPTCReservedBits = 0;
|
||||
|
||||
uint32_t crcmask = 0;
|
||||
uint8_t crclen = 0;
|
||||
|
||||
uint8_t is_bptc = 0;
|
||||
uint8_t is_trellis = 0;
|
||||
uint8_t is_emb = 0;
|
||||
uint8_t is_lc = 0;
|
||||
uint8_t is_full = 0;
|
||||
uint8_t pdu_len = 0;
|
||||
uint8_t pdu_start = 0; //starting value of pdu (0 normal, 2 for confirmed)
|
||||
|
||||
switch(databurst){
|
||||
case 0x00: //PI
|
||||
is_bptc = 1;
|
||||
crclen = 16;
|
||||
crcmask = 0x6969; //insert pi and 69 jokes here
|
||||
pdu_len = 12; //12 bytes
|
||||
sprintf(state->fsubtype, " PI ");
|
||||
break;
|
||||
case 0x01: //VLC
|
||||
is_bptc = 1;
|
||||
is_lc = 1;
|
||||
crclen = 24;
|
||||
crcmask = 0x969696;
|
||||
pdu_len = 12; //12 bytes
|
||||
sprintf(state->fsubtype, " VLC ");
|
||||
break;
|
||||
case 0x02: //TLC
|
||||
is_bptc = 1;
|
||||
is_lc = 1;
|
||||
crcmask = 0x999999;
|
||||
crclen = 24;
|
||||
pdu_len = 12; //12 bytes
|
||||
sprintf(state->fsubtype, " TLC ");
|
||||
break;
|
||||
case 0x03: //CSBK
|
||||
is_bptc = 1;
|
||||
crclen = 16;
|
||||
crcmask = 0xA5A5;
|
||||
pdu_len = 12; //12 bytes
|
||||
sprintf(state->fsubtype, " CSBK");
|
||||
break;
|
||||
case 0x04: //MBC Header
|
||||
is_bptc = 1;
|
||||
crclen = 16;
|
||||
crcmask = 0xAAAA;
|
||||
pdu_len = 12; //12 bytes
|
||||
sprintf(state->fsubtype, " MBCH");
|
||||
break;
|
||||
case 0x05: //MBC Continuation
|
||||
is_bptc = 1;
|
||||
//let block assembler carry out crc on the completed message
|
||||
pdu_len = 12; //12 bytes
|
||||
sprintf(state->fsubtype, " MBCC");
|
||||
break;
|
||||
case 0x06: //Data Header
|
||||
is_bptc = 1;
|
||||
crclen = 16;
|
||||
crcmask = 0xCCCC;
|
||||
pdu_len = 12; //12 bytes
|
||||
sprintf(state->fsubtype, " DATA");
|
||||
break;
|
||||
//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
|
||||
is_bptc = 1;
|
||||
crclen = 9; //confirmed data only
|
||||
crcmask = 0x0F0;
|
||||
pdu_len = 12; //12 bytes unconfirmed
|
||||
if (state->data_conf_data[slot] == 1)
|
||||
{
|
||||
pdu_len = 10;
|
||||
pdu_start = 2; //start at plus two when assembling
|
||||
}
|
||||
sprintf(state->fsubtype, " R12D ");
|
||||
break;
|
||||
case 0x08: //3/4 Rate Data
|
||||
is_trellis = 1;
|
||||
crclen = 9; //confirmed data only
|
||||
crcmask = 0x1FF;
|
||||
pdu_len = 18; //18 bytes unconfirmed
|
||||
if (state->data_conf_data[slot] == 1)
|
||||
{
|
||||
pdu_len = 16;
|
||||
pdu_start = 2; //start at plus two when assembling
|
||||
}
|
||||
sprintf(state->fsubtype, " R34D ");
|
||||
break;
|
||||
case 0x09: //Idle
|
||||
//pseudo-random data fill, no need to do anything with this
|
||||
sprintf(state->fsubtype, " IDLE ");
|
||||
break;
|
||||
case 0x0A: //1 Rate Data
|
||||
crclen = 9; //confirmed data only
|
||||
crcmask = 0x1FF;
|
||||
is_full = 1;
|
||||
pdu_len = 25; //196 bits - 24.5 bytes
|
||||
sprintf(state->fsubtype, " R_1D ");
|
||||
break;
|
||||
case 0x0B: //Unified Data
|
||||
is_bptc = 1;
|
||||
crclen = 16;
|
||||
crcmask = 0x3333;
|
||||
pdu_len = 12; //12 bytes
|
||||
sprintf(state->fsubtype, " UDAT ");
|
||||
break;
|
||||
//special types (not real data 'sync' bursts)
|
||||
case 0xEB: //Embedded Signalling
|
||||
is_emb = 1;
|
||||
pdu_len = 9;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (databurst != 0xEB)
|
||||
{
|
||||
if (state->dmr_ms_mode == 0) fprintf(stderr, "| Color Code=%02d ", state->dmr_color_code);
|
||||
fprintf(stderr, "|%s", state->fsubtype);
|
||||
}
|
||||
|
||||
//Most Data Sync Burst types will use the bptc 196x96
|
||||
if (is_bptc)
|
||||
{
|
||||
CRCExtracted = 0;
|
||||
CRCComputed = 0;
|
||||
IrrecoverableErrors = 0;
|
||||
|
||||
/* Deinterleave DMR data */
|
||||
BPTCDeInterleaveDMRData(info, BPTCDeInteleavedData);
|
||||
|
||||
/* Extract the BPTC 196,96 DMR data */
|
||||
IrrecoverableErrors = BPTC_196x96_Extract_Data(BPTCDeInteleavedData, BPTCDmrDataBit, R);
|
||||
|
||||
/* Fill the reserved bit (R(0)-R(2) of the BPTC(196,96) block) */
|
||||
BPTCReservedBits = (R[0] & 0x01) | ((R[1] << 1) & 0x02) | ((R[2] << 2) & 0x08);
|
||||
|
||||
/* Convert the 96 bits BPTC data into 12 bytes */
|
||||
k = 0;
|
||||
for(i = 0; i < 12; i++)
|
||||
{
|
||||
BPTCDmrDataByte[i] = 0;
|
||||
for(j = 0; j < 8; j++)
|
||||
{
|
||||
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] << 1;
|
||||
BPTCDmrDataByte[i] = BPTCDmrDataByte[i] | (BPTCDmrDataBit[k] & 0x01);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill the CRC extracted (before Reed-Solomon (12,9) FEC correction) */
|
||||
CRCExtracted = 0;
|
||||
for(i = 0; i < crclen; i++)
|
||||
{
|
||||
CRCExtracted = CRCExtracted << 1;
|
||||
CRCExtracted = CRCExtracted | (uint32_t)(BPTCDmrDataBit[i + 96 - crclen] & 1);
|
||||
}
|
||||
|
||||
/* Apply the CRC mask (see DMR standard B.3.12 Data Type CRC Mask) */
|
||||
CRCExtracted = CRCExtracted ^ crcmask;
|
||||
|
||||
/* Check/correct the BPTC data and compute the Reed-Solomon (12,9) CRC */
|
||||
if (is_lc) CRCCorrect = ComputeAndCorrectFullLinkControlCrc(BPTCDmrDataByte, &CRCComputed, crcmask);
|
||||
|
||||
//TODO: run CRC9 on confirmed 1/2 data blocks
|
||||
else if (state->data_conf_data[slot] == 1)
|
||||
{
|
||||
//run CRC on individual blocks according to type and validate each individual block
|
||||
}
|
||||
|
||||
//run CCITT on other data forms
|
||||
else //will still run on unconfirmed data (shouldn't matter other than a possible bogus warning message)
|
||||
{
|
||||
CRCComputed = ComputeCrcCCITT(BPTCDmrDataBit);
|
||||
if (CRCComputed == CRCExtracted) CRCCorrect = 1;
|
||||
else CRCCorrect = 0;
|
||||
}
|
||||
|
||||
if (databurst == 0x04 || databurst == 0x06) //MBC Header, Data Header
|
||||
{
|
||||
if (CRCCorrect)
|
||||
{
|
||||
state->data_block_crc_valid[slot][0] = 1;
|
||||
}
|
||||
else state->data_block_crc_valid[slot][0] = 0;
|
||||
}
|
||||
|
||||
/* Convert corrected x bytes into x*8 bits */
|
||||
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
|
||||
{
|
||||
BPTCDmrDataBit[j + 0] = (BPTCDmrDataByte[i+pdu_start] >> 7) & 0x01;
|
||||
BPTCDmrDataBit[j + 1] = (BPTCDmrDataByte[i+pdu_start] >> 6) & 0x01;
|
||||
BPTCDmrDataBit[j + 2] = (BPTCDmrDataByte[i+pdu_start] >> 5) & 0x01;
|
||||
BPTCDmrDataBit[j + 3] = (BPTCDmrDataByte[i+pdu_start] >> 4) & 0x01;
|
||||
BPTCDmrDataBit[j + 4] = (BPTCDmrDataByte[i+pdu_start] >> 3) & 0x01;
|
||||
BPTCDmrDataBit[j + 5] = (BPTCDmrDataByte[i+pdu_start] >> 2) & 0x01;
|
||||
BPTCDmrDataBit[j + 6] = (BPTCDmrDataByte[i+pdu_start] >> 1) & 0x01;
|
||||
BPTCDmrDataBit[j + 7] = (BPTCDmrDataByte[i+pdu_start] >> 0) & 0x01;
|
||||
}
|
||||
|
||||
//convert to unified DMR_PDU and DMR_PDU_bits
|
||||
for (i = 0; i < pdu_len; i++)
|
||||
{
|
||||
DMR_PDU[i] = BPTCDmrDataByte[i+pdu_start];
|
||||
}
|
||||
|
||||
for (i = 0; i < pdu_len * 8; i++)
|
||||
{
|
||||
DMR_PDU_bits[i] = BPTCDmrDataBit[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Embedded Signalling will use BPTC 128x77
|
||||
if (is_emb)
|
||||
{
|
||||
|
||||
CRCExtracted = 0;
|
||||
CRCComputed = 0;
|
||||
IrrecoverableErrors = 0;
|
||||
|
||||
/* First step : Reconstitute the BPTC 16x8 matrix */
|
||||
Burst = 1; /* Burst B to E contains embedded signaling data */
|
||||
k = 0;
|
||||
for(i = 0; i < 16; i++)
|
||||
{
|
||||
for(j = 0; j < 8; j++)
|
||||
{
|
||||
/* Only the LSBit of the byte is stored */
|
||||
BptcDataMatrix[j][i] = state->dmr_embedded_signalling[slot][Burst][k+8];
|
||||
k++;
|
||||
|
||||
/* Go on to the next burst once 32 bit
|
||||
* of the SNYC have been stored */
|
||||
if(k >= 32)
|
||||
{
|
||||
k = 0;
|
||||
Burst++;
|
||||
}
|
||||
} /* End for(j = 0; j < 8; j++) */
|
||||
} /* End for(i = 0; i < 16; i++) */
|
||||
|
||||
/* Extract the 72 LC bit (+ 5 CRC bit) of the matrix */
|
||||
IrrecoverableErrors = BPTC_128x77_Extract_Data(BptcDataMatrix, LC_DataBit);
|
||||
|
||||
/* Reconstitute the 5 bit CRC */
|
||||
CRCExtracted = (LC_DataBit[72] & 1) << 4;
|
||||
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 */
|
||||
CRCComputed = ComputeCrc5Bit(LC_DataBit);
|
||||
|
||||
if(CRCExtracted == CRCComputed) CRCCorrect = 1;
|
||||
else CRCCorrect = 0;
|
||||
|
||||
for (i = 0; i < 72; i++)
|
||||
{
|
||||
DMR_PDU_bits[i] = LC_DataBit[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&LC_DataBit[i*8], 8);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//the ugly one
|
||||
if (is_trellis)
|
||||
{
|
||||
CRCExtracted = 0;
|
||||
CRCComputed = 0;
|
||||
IrrecoverableErrors = 1;
|
||||
bool trellis_good = TRUE;
|
||||
|
||||
uint8_t tdibits[98];
|
||||
memset (tdibits, 0, sizeof(tdibits));
|
||||
|
||||
//reconstitute info bits into reverse ordered dibits for the trellis decoder
|
||||
for (i = 0; i < 98; i++)
|
||||
{
|
||||
tdibits[97-i] = (info[i*2] << 1) | info[i*2+1];
|
||||
}
|
||||
|
||||
unsigned char TrellisReturn[18];
|
||||
memset (TrellisReturn, 0, sizeof(TrellisReturn));
|
||||
|
||||
trellis_good = CDMRTrellisDecode(tdibits, TrellisReturn);
|
||||
if (trellis_good == TRUE) IrrecoverableErrors = 0;
|
||||
// else (fprintf (stderr, " Trellis Failure")); //debug print
|
||||
|
||||
for (i = 0; i < pdu_len; i++) //18
|
||||
{
|
||||
DMR_PDU[i] = (uint8_t)TrellisReturn[i+pdu_start];
|
||||
}
|
||||
|
||||
for(i = 0, j = 0; i < pdu_len; i++, j+=8)
|
||||
{
|
||||
DMR_PDU_bits[j + 0] = (TrellisReturn[i+pdu_start] >> 7) & 0x01;
|
||||
DMR_PDU_bits[j + 1] = (TrellisReturn[i+pdu_start] >> 6) & 0x01;
|
||||
DMR_PDU_bits[j + 2] = (TrellisReturn[i+pdu_start] >> 5) & 0x01;
|
||||
DMR_PDU_bits[j + 3] = (TrellisReturn[i+pdu_start] >> 4) & 0x01;
|
||||
DMR_PDU_bits[j + 4] = (TrellisReturn[i+pdu_start] >> 3) & 0x01;
|
||||
DMR_PDU_bits[j + 5] = (TrellisReturn[i+pdu_start] >> 2) & 0x01;
|
||||
DMR_PDU_bits[j + 6] = (TrellisReturn[i+pdu_start] >> 1) & 0x01;
|
||||
DMR_PDU_bits[j + 7] = (TrellisReturn[i+pdu_start] >> 0) & 0x01;
|
||||
}
|
||||
|
||||
//TODO: run CRC9 on confirmed 3/4 data blocks
|
||||
|
||||
}
|
||||
|
||||
if (is_full) //Rate 1 Data
|
||||
{
|
||||
CRCExtracted = 0;
|
||||
CRCComputed = 0;
|
||||
IrrecoverableErrors = 0;
|
||||
|
||||
//TODO: run CRC9 on confirmed data
|
||||
|
||||
for (i = 0; i < 24; i++)
|
||||
{
|
||||
DMR_PDU[i] = (uint8_t)ConvertBitIntoBytes(&info[i*8], 8);
|
||||
}
|
||||
//leftover 4 bits, manual doesn't say which 4 so assuming last 4
|
||||
//pushing last 4 to 25 and shifting it
|
||||
DMR_PDU[25] = (uint8_t)ConvertBitIntoBytes(&info[192], 4);
|
||||
DMR_PDU[25] = DMR_PDU[25] << 4;
|
||||
|
||||
//copy info to dmr_pdu_bits
|
||||
memcpy(DMR_PDU_bits, info, sizeof(DMR_PDU_bits));
|
||||
|
||||
}
|
||||
|
||||
|
||||
//time for some pi
|
||||
if (databurst == 0x00) dmr_pi (opts, state, DMR_PDU, CRCCorrect, IrrecoverableErrors);
|
||||
|
||||
//full link control
|
||||
if (databurst == 0x01) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 1); //VLC
|
||||
if (databurst == 0x02) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 2); //TLC
|
||||
if (databurst == 0xEB) dmr_flco (opts, state, DMR_PDU_bits, CRCCorrect, IrrecoverableErrors, 3); //EMB
|
||||
|
||||
//dmr data header and multi block types (header, 1/2, 3/4, 1, Unified) - type 1
|
||||
if (databurst == 0x06) dmr_dheader (opts, state, DMR_PDU, 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 == 0x0A) dmr_block_assembler (opts, state, DMR_PDU, pdu_len, databurst, 1); //Full Rate Data - Disabled until further testing can be done
|
||||
|
||||
//is unified data handled like normal data, or control signalling data?
|
||||
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);
|
||||
|
||||
//both MBC header and MBC continuation will go to the block_assembler - type 2, and then to dmr_cspdu
|
||||
if (databurst == 0x04)
|
||||
{
|
||||
state->data_block_counter[slot] = 0; //zero block counter before running header
|
||||
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);
|
||||
|
||||
//print the unified PDU format here, if not slot idle
|
||||
if (opts->payload == 1 && databurst != 0x09)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, " DMR PDU Payload ");
|
||||
for (i = 0; i < pdu_len; i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", DMR_PDU[i]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
814
src/dmr_flco.c
Normal file
814
src/dmr_flco.c
Normal file
@@ -0,0 +1,814 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dmr_flco.c
|
||||
* DMR Full Link Control, Short Link Control, TACT/CACH and related funtions
|
||||
*
|
||||
* Portions of link control/voice burst/vlc/tlc from LouisErigHerve
|
||||
* Source: https://github.com/LouisErigHerve/dsd/blob/master/src/dmr_sync.c
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-12 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
//combined flco handler (vlc, tlc, emb), minus the superfluous structs and strings
|
||||
void dmr_flco (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors, uint8_t type)
|
||||
{
|
||||
|
||||
//force slot to 0 if using dmr mono handling
|
||||
if (opts->dmr_mono == 1) state->currentslot = 0;
|
||||
|
||||
uint8_t pf = 0;
|
||||
uint8_t reserved = 0;
|
||||
uint8_t flco = 0;
|
||||
uint8_t fid = 0;
|
||||
uint8_t so = 0;
|
||||
uint32_t target = 0;
|
||||
uint32_t source = 0;
|
||||
int restchannel = -1;
|
||||
int is_cap_plus = 0;
|
||||
int is_alias = 0;
|
||||
int is_gps = 0;
|
||||
uint8_t slot = state->currentslot;
|
||||
|
||||
|
||||
pf = (uint8_t)(lc_bits[0]); //Protect Flag
|
||||
reserved = (uint8_t)(lc_bits[1]); //Reserved
|
||||
flco = (uint8_t)ConvertBitIntoBytes(&lc_bits[2], 6); //Full Link Control Opcode
|
||||
fid = (uint8_t)ConvertBitIntoBytes(&lc_bits[8], 8); //Feature set ID (FID)
|
||||
so = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 8); //Service Options
|
||||
target = (uint32_t)ConvertBitIntoBytes(&lc_bits[24], 24); //Target or Talk Group
|
||||
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[48], 24);
|
||||
|
||||
//look for Cap+ on VLC header, then set source and/or rest channel appropriately
|
||||
if (type == 1 && fid == 0x10 && flco == 0x04)
|
||||
{
|
||||
is_cap_plus = 1;
|
||||
restchannel = (int)ConvertBitIntoBytes(&lc_bits[48], 8);
|
||||
source = (uint32_t)ConvertBitIntoBytes(&lc_bits[56], 16);
|
||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
sprintf (state->dmr_branding_sub, "%s", "Cap+ ");
|
||||
}
|
||||
|
||||
|
||||
if (IrrecoverableErrors == 0)
|
||||
{
|
||||
//Embedded Talker Alias Header Only (format and len storage)
|
||||
if (type == 3 && flco == 0x04)
|
||||
{
|
||||
dmr_embedded_alias_header(opts, state, lc_bits);
|
||||
}
|
||||
|
||||
//Embedded Talker Alias Header (continuation) and Blocks
|
||||
if (type == 3 && flco > 0x3 && flco < 0x08)
|
||||
{
|
||||
is_alias = 1;
|
||||
dmr_embedded_alias_blocks(opts, state, lc_bits);
|
||||
}
|
||||
|
||||
//Embedded GPS
|
||||
if (type == 3 && flco == 0x08)
|
||||
{
|
||||
is_gps = 1;
|
||||
if (fid == 0) dmr_embedded_gps(opts, state, lc_bits); //issues with fid 0x10 and flco 0x08
|
||||
|
||||
//NOTE: fid 0x10 and flco 0x08 produces a lot of 'zero' lat or lon values, I don't believe this is embedded gps
|
||||
//this has been observed to happen often on CapMax systems, so I believe it could be some CapMax 'thing'
|
||||
//Unknown Link Control - FLCO=0x08 FID=0x10 SVC=0xC1 or FLCO=0x08 FID=0x10 SVC=0xC0 <-- have the enc bit set on svc, but these calls aren't enc?
|
||||
//is this an encrypted embedded link control?
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr, " Slot %d Unknown EMB LC - FLCO=0x%02X FID=0x%02X SVC=0x%02X ", slot+1, flco, fid, so);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//will want to continue to observe for different flco and fid combinations to find out their meaning
|
||||
//Standard Addressing/Cap+ Addressing (trying to avoid embedded alias and gps, etc)
|
||||
if(IrrecoverableErrors == 0 && is_alias == 0 && is_gps == 0)
|
||||
{
|
||||
//set overarching manufacturer in use when non-standard feature id set is up
|
||||
//may not want to set moto 0x10 here either, lots of radios use that set as well
|
||||
if (fid != 0) state->dmr_mfid = fid;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//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 (type == 2) //TLC, zero out target, source, so, and fid per channel, and other odd and ends
|
||||
{
|
||||
//I wonder which of these we truly want to zero out, possibly none of them
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
state->dmr_fid = 0;
|
||||
state->dmr_so = 0;
|
||||
// state->lasttg = 0;
|
||||
// state->lastsrc = 0;
|
||||
state->payload_algid = 0;
|
||||
state->payload_mi = 0;
|
||||
}
|
||||
if (state->currentslot == 1)
|
||||
{
|
||||
state->dmr_fidR = 0;
|
||||
state->dmr_soR = 0;
|
||||
// state->lasttgR = 0;
|
||||
// state->lastsrcR = 0;
|
||||
state->payload_algidR = 0;
|
||||
state->payload_miR = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (restchannel != state->dmr_rest_channel && restchannel != -1)
|
||||
{
|
||||
state->dmr_rest_channel = restchannel;
|
||||
//assign to cc freq
|
||||
if (state->trunk_chan_map[restchannel] != 0)
|
||||
{
|
||||
state->p25_cc_freq = state->trunk_chan_map[restchannel];
|
||||
}
|
||||
}
|
||||
|
||||
if (type == 1) fprintf (stderr, "%s \n", KGRN);
|
||||
if (type == 2) fprintf (stderr, "%s \n", KRED);
|
||||
if (type == 3) fprintf (stderr, "%s", KGRN);
|
||||
|
||||
fprintf (stderr, " SLOT %d ", state->currentslot+1);
|
||||
fprintf(stderr, "TGT=%u SRC=%u ", target, source);
|
||||
if (opts->payload == 1) fprintf(stderr, "FLCO=0x%02X FID=0x%02X SVC=0x%02X ", flco, fid, so);
|
||||
|
||||
if (flco == 3) //UU_V_Ch_Usr
|
||||
{
|
||||
sprintf (state->call_string[slot], " Private");
|
||||
fprintf (stderr, "Private ");
|
||||
}
|
||||
else //Grp_V_Ch_Usr
|
||||
{
|
||||
sprintf (state->call_string[slot], " Group");
|
||||
fprintf (stderr, "Group ");
|
||||
}
|
||||
|
||||
if(so & 0x80)
|
||||
{
|
||||
strcat (state->call_string[slot], " Emergency ");
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf(stderr, "Emergency ");
|
||||
}
|
||||
else strcat (state->call_string[slot], " ");
|
||||
|
||||
if(so & 0x40)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], " Encrypted");
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf(stderr, "Encrypted ");
|
||||
}
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// else strcat (state->call_string[slot], " ");
|
||||
|
||||
/* Check the "Service Option" bits */
|
||||
if(so & 0x30)
|
||||
{
|
||||
/* Experimentally determined with DSD+,
|
||||
* is equal to 0x2, this is a TXI call */
|
||||
if((so & 0x30) == 0x20)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], " TXI");
|
||||
fprintf(stderr, "TXI ");
|
||||
}
|
||||
else
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], " RES");
|
||||
fprintf(stderr, "Reserved=%d ", (so & 0x30) >> 4);
|
||||
}
|
||||
}
|
||||
if(so & 0x08)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-BC ");
|
||||
fprintf(stderr, "Broadcast ");
|
||||
}
|
||||
if(so & 0x04)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-OVCM ");
|
||||
fprintf(stderr, "OVCM ");
|
||||
}
|
||||
if(so & 0x03)
|
||||
{
|
||||
if((so & 0x03) == 0x01)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-P1");
|
||||
fprintf(stderr, "Priority 1 ");
|
||||
}
|
||||
else if((so & 0x03) == 0x02)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-P2");
|
||||
fprintf(stderr, "Priority 2 ");
|
||||
}
|
||||
else if((so & 0x03) == 0x03)
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], "-P3");
|
||||
fprintf(stderr, "Priority 3 ");
|
||||
}
|
||||
else /* We should never go here */
|
||||
{
|
||||
//REMUS! Uncomment Line Below if desired
|
||||
// strcat (state->call_string[slot], " ");
|
||||
fprintf(stderr, "No Priority ");
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Call ");
|
||||
|
||||
fprintf (stderr, "%s", KRED);
|
||||
if (CRCCorrect == 1) ; //CRCCorrect 1 is good, else is bad CRC; no print on good
|
||||
else if(IrrecoverableErrors == 0) fprintf(stderr, "(FEC/RAS)");
|
||||
else fprintf(stderr, "(FEC/CRC ERR)");
|
||||
|
||||
//check Cap+ rest channel info if available and good fec
|
||||
if (is_cap_plus == 1)
|
||||
{
|
||||
if (restchannel != -1)
|
||||
{
|
||||
fprintf (stderr, "%s ", KYEL);
|
||||
fprintf (stderr, "Cap+ R-Ch %d", restchannel);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
|
||||
//group labels
|
||||
for (int i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
//Remus! Change target to source if you prefer
|
||||
if (state->group_array[i].groupNumber == target)
|
||||
{
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, "[%s] ", state->group_array[i].groupName);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
}
|
||||
|
||||
if (state->K != 0 && fid == 0x10 && so & 0x40)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, "Key %lld ", state->K);
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
|
||||
if (state->K1 != 0 && fid == 0x68 && so & 0x40)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Key %010llX ", state->K1);
|
||||
if (state->K2 != 0) fprintf (stderr, "%016llX ", state->K2);
|
||||
if (state->K4 != 0) fprintf (stderr, "%016llX %016llX", state->K3, state->K4);
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//externalized dmr cach - tact and slco fragment handling
|
||||
uint8_t dmr_cach (dsd_opts * opts, dsd_state * state, uint8_t cach_bits[25])
|
||||
{
|
||||
int i, j;
|
||||
uint8_t err = 0;
|
||||
uint8_t tact_valid = 0;
|
||||
uint8_t cach_valid = 0;
|
||||
|
||||
bool h1, h2, h3, crc;
|
||||
|
||||
//dump payload - testing
|
||||
uint8_t slco_raw_bits[68]; //raw
|
||||
uint8_t slco_bits[68]; //de-interleaved
|
||||
|
||||
//tact pdu
|
||||
uint8_t tact_bits[7];
|
||||
uint8_t at = 0; //access type, set to 1 during continuous transmission mode
|
||||
uint8_t slot = 2; //tdma time slot
|
||||
uint8_t lcss = 0; //link control start stop (9.3.3) NOTE: There is no Single fragment LC defined for CACH signalling
|
||||
|
||||
//cach_bits are already de-interleaved upon initial collection (still needs secodary slco de-interleave)
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
tact_bits[i] = cach_bits[i];
|
||||
}
|
||||
|
||||
//run hamming 7_4 on the tact_bits (redundant, since we do it earlier, but need the lcss)
|
||||
if ( Hamming_7_4_decode (tact_bits) )
|
||||
{
|
||||
at = tact_bits[0]; //any useful tricks with this? csbk on/off etc?
|
||||
slot = tact_bits[1]; //
|
||||
lcss = (tact_bits[2] << 1) | tact_bits[3];
|
||||
tact_valid = 1; //set to 1 for valid, else remains 0.
|
||||
//fprintf (stderr, "AT=%d LCSS=%d - ", at, lcss); //debug print
|
||||
}
|
||||
else //probably skip/memset/zeroes with else statement?
|
||||
{
|
||||
//do something?
|
||||
err = 1;
|
||||
//return (err);
|
||||
}
|
||||
|
||||
//determine counter value based on lcss value
|
||||
if (lcss == 0) //run as single block/fragment NOTE: There is no Single fragment LC defined for CACH signalling (but is mentioned in the manual table)
|
||||
{
|
||||
//reset the full cach, even if we aren't going to use it, may be beneficial for next time
|
||||
state->dmr_cach_counter = 0; //first fragment, set to zero.
|
||||
memset (state->dmr_cach_fragment, 1, sizeof (state->dmr_cach_fragment));
|
||||
|
||||
//seperate
|
||||
for (i = 0; i < 17; i++)
|
||||
{
|
||||
slco_raw_bits[i] = cach_bits[i+7];
|
||||
}
|
||||
|
||||
//De-interleave
|
||||
int src = 0;
|
||||
for (i = 0; i < 17; i++)
|
||||
{
|
||||
src = (i * 4) % 67;
|
||||
slco_bits[i] = slco_raw_bits[src];
|
||||
}
|
||||
//slco_bits[i] = slco_raw_bits[i];
|
||||
|
||||
//hamming check here
|
||||
h1 = Hamming17123 (slco_bits + 0);
|
||||
|
||||
//run single - manual would suggest that though this exists, there is no support? or perhaps its manufacturer only thing?
|
||||
if (h1) ; // dmr_slco (opts, state, slco_bits); //random false positives and sets bad parms/mfid etc
|
||||
else
|
||||
{
|
||||
err = 1;
|
||||
//return (err);
|
||||
}
|
||||
return (err);
|
||||
}
|
||||
|
||||
if (lcss == 1) //first block, reset counters and memset
|
||||
{
|
||||
//reset the full cach and counter
|
||||
state->dmr_cach_counter = 0;
|
||||
memset (state->dmr_cach_fragment, 1, sizeof (state->dmr_cach_fragment));
|
||||
}
|
||||
if (lcss == 3) state->dmr_cach_counter++; //continuation, so increment counter by one.
|
||||
if (lcss == 2) //final segment - assemble, de-interleave, hamming, crc, and execute
|
||||
{
|
||||
state->dmr_cach_counter = 3;
|
||||
}
|
||||
|
||||
//sanity check
|
||||
if (state->dmr_cach_counter > 3) //marginal/shaky/bad signal or tuned away
|
||||
{
|
||||
//zero out complete fragment array
|
||||
lcss = 5; //toss away value
|
||||
state->dmr_cach_counter = 0;
|
||||
memset (state->dmr_cach_fragment, 1, sizeof (state->dmr_cach_fragment));
|
||||
err = 1;
|
||||
return (err);
|
||||
}
|
||||
|
||||
//add fragment to array
|
||||
for (i = 0; i < 17; i++)
|
||||
{
|
||||
state->dmr_cach_fragment[state->dmr_cach_counter][i] = cach_bits[i+7];
|
||||
}
|
||||
|
||||
if (lcss == 2) //last block arrived, compile, hamming, crc and send off to dmr_slco
|
||||
{
|
||||
//assemble
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
for (i = 0; i < 17; i++)
|
||||
{
|
||||
slco_raw_bits[i+(17*j)] = state->dmr_cach_fragment[j][i];
|
||||
}
|
||||
}
|
||||
|
||||
//De-interleave method, hamming, and crc from Boatbod OP25
|
||||
|
||||
//De-interleave
|
||||
int src = 0;
|
||||
for (i = 0; i < 67; i++)
|
||||
{
|
||||
src = (i * 4) % 67;
|
||||
slco_bits[i] = slco_raw_bits[src];
|
||||
}
|
||||
slco_bits[i] = slco_raw_bits[i];
|
||||
|
||||
//hamming checks here
|
||||
h1 = Hamming17123 (slco_bits + 0);
|
||||
h2 = Hamming17123 (slco_bits + 17);
|
||||
h3 = Hamming17123 (slco_bits + 34);
|
||||
|
||||
// remove hamming and leave 36 bits of Short LC
|
||||
for (i = 17; i < 29; i++) {
|
||||
slco_bits[i-5] = slco_bits[i];
|
||||
}
|
||||
for (i = 34; i < 46; i++) {
|
||||
slco_bits[i-10] = slco_bits[i];
|
||||
}
|
||||
|
||||
//zero out the hangover bits
|
||||
for (i = 36; i < 68; i++)
|
||||
{
|
||||
slco_bits[i] = 0;
|
||||
}
|
||||
|
||||
//run crc8
|
||||
crc = crc8_ok(slco_bits, 36);
|
||||
|
||||
//only run SLCO on good everything
|
||||
if (h1 && h2 && h3 && crc) dmr_slco (opts, state, slco_bits);
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " SLCO CRC ERR");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
}
|
||||
return (err); //return err value based on success or failure, even if we aren't checking it
|
||||
}
|
||||
|
||||
void dmr_slco (dsd_opts * opts, dsd_state * state, uint8_t slco_bits[])
|
||||
{
|
||||
long int ccfreq = 0;
|
||||
|
||||
int i;
|
||||
uint8_t slco_bytes[6]; //completed byte blocks for payload print
|
||||
for (i = 0; i < 5; i++) slco_bytes[i] = (uint8_t)ConvertBitIntoBytes(&slco_bits[i*8], 8);
|
||||
slco_bytes[5] = (uint8_t)ConvertBitIntoBytes(&slco_bits[32], 4);
|
||||
|
||||
|
||||
//just going to decode the Short LC with all potential parameters known
|
||||
uint8_t slco = (uint8_t)ConvertBitIntoBytes(&slco_bits[0], 4);
|
||||
uint8_t model = (uint8_t)ConvertBitIntoBytes(&slco_bits[4], 2);
|
||||
uint16_t netsite = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 12);
|
||||
uint8_t reg = slco_bits[18]; //registration required/not required or normalchanneltype/composite cch
|
||||
uint8_t csc = (uint16_t)ConvertBitIntoBytes(&slco_bits[19], 9); //common slot counter, 0-511
|
||||
uint16_t net = 0;
|
||||
uint16_t site = 0;
|
||||
char model_str[8];
|
||||
sprintf (model_str, "%s", "");
|
||||
//activity update stuff
|
||||
uint8_t ts1_act = (uint8_t)ConvertBitIntoBytes(&slco_bits[4], 4); //activity update ts1
|
||||
uint8_t ts2_act = (uint8_t)ConvertBitIntoBytes(&slco_bits[8], 4); //activity update ts2
|
||||
uint8_t ts1_hash = (uint8_t)ConvertBitIntoBytes(&slco_bits[12], 8); //ts1 address hash (crc8) //361-1 B.3.7
|
||||
uint8_t ts2_hash = (uint8_t)ConvertBitIntoBytes(&slco_bits[20], 8); //ts2 address hash (crc8) //361-1 B.3.7
|
||||
|
||||
//DMR Location Area - DMRLA - should probably be state variables so we can use this in both slc and csbk
|
||||
uint16_t sys_area = 0;
|
||||
uint16_t sub_area = 0;
|
||||
uint16_t sub_mask = 0x1;
|
||||
//tiny n 1-3; small 1-5; large 1-8; huge 1-10
|
||||
uint16_t n = 1; //The minimum value of DMRLA is normally ≥ 1, 0 is reserved
|
||||
|
||||
//TODO: Add logic to set n and sub_mask values for DMRLA
|
||||
//assigning n as largest possible value for model type
|
||||
|
||||
//Sys_Parms
|
||||
if (slco == 0x2 || slco == 0x3)
|
||||
{
|
||||
if (model == 0) //Tiny
|
||||
{
|
||||
net = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 9);
|
||||
site = (uint16_t)ConvertBitIntoBytes(&slco_bits[15], 3);
|
||||
sprintf (model_str, "%s", "Tiny");
|
||||
n = 3;
|
||||
}
|
||||
else if (model == 1) //Small
|
||||
{
|
||||
net = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 7);
|
||||
site = (uint16_t)ConvertBitIntoBytes(&slco_bits[13], 5);
|
||||
sprintf (model_str, "%s", "Small");
|
||||
n = 5;
|
||||
}
|
||||
else if (model == 2) //Large
|
||||
{
|
||||
net = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 4);
|
||||
site = (uint16_t)ConvertBitIntoBytes(&slco_bits[10], 8);
|
||||
sprintf (model_str, "%s", "Large");
|
||||
n = 8;
|
||||
}
|
||||
else if (model == 3) //Huge
|
||||
{
|
||||
net = (uint16_t)ConvertBitIntoBytes(&slco_bits[6], 2);
|
||||
site = (uint16_t)ConvertBitIntoBytes(&slco_bits[8], 10);
|
||||
sprintf (model_str, "%s", "Huge");
|
||||
n = 10;
|
||||
}
|
||||
|
||||
if (opts->dmr_dmrla_is_set == 1) n = opts->dmr_dmrla_n;
|
||||
|
||||
if (n == 0) sub_mask = 0x0;
|
||||
if (n == 1) sub_mask = 0x1;
|
||||
if (n == 2) sub_mask = 0x3;
|
||||
if (n == 3) sub_mask = 0x7;
|
||||
if (n == 4) sub_mask = 0xF;
|
||||
if (n == 5) sub_mask = 0x1F;
|
||||
if (n == 6) sub_mask = 0x3F;
|
||||
if (n == 7) sub_mask = 0x7F;
|
||||
if (n == 8) sub_mask = 0xFF;
|
||||
if (n == 9) sub_mask = 0x1FF;
|
||||
if (n == 10) sub_mask = 0x3FF;
|
||||
|
||||
}
|
||||
|
||||
//Con+
|
||||
uint8_t con_netid = (uint8_t)ConvertBitIntoBytes(&slco_bits[8], 8);
|
||||
uint8_t con_siteid = (uint8_t)ConvertBitIntoBytes(&slco_bits[16], 8);
|
||||
//Cap+
|
||||
uint8_t restchannel = (uint8_t)ConvertBitIntoBytes(&slco_bits[12], 8);
|
||||
|
||||
//initial line break
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
|
||||
if (slco == 0x2) //C_SYS_Parms
|
||||
{
|
||||
if (n != 0) fprintf (stderr, " SLC_C_SYS_PARMS - %s - Net ID: %d Site ID: %d.%d - Reg Req: %d - CSC: %d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1, reg, csc);
|
||||
else fprintf (stderr, " SLC_C_SYS_PARMS - %s - Net ID: %d Site ID: %d - Reg Req: %d - Capacity Max ", model_str, net, site, reg);
|
||||
|
||||
//add string for ncurses terminal display
|
||||
if (n != 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d.%d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1 );
|
||||
else sprintf (state->dmr_site_parms, "TIII - %s %d-%d ", model_str, net, site);
|
||||
|
||||
//if using rigctl we can set an unknown cc frequency by polling rigctl for the current frequency
|
||||
if (opts->use_rigctl == 1 && state->p25_cc_freq == 0) //if not set from channel map 0
|
||||
{
|
||||
ccfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
||||
}
|
||||
|
||||
//debug print
|
||||
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&slco_bits[4], 14);
|
||||
//fprintf (stderr, "\n SYSCODE: %014b", syscode);
|
||||
//fprintf (stderr, "\n SYSCODE: %02b.%b.%b", model, net, site); //won't show leading zeroes, but may not be required
|
||||
|
||||
}
|
||||
else if (slco == 0x3) //P_SYS_Parms
|
||||
{
|
||||
if (n != 0) fprintf (stderr, " SLC_P_SYS_PARMS - %s - Net ID: %d Site ID: %d.%d - Comp CC: %d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1, reg);
|
||||
else fprintf (stderr, " SLC_P_SYS_PARMS - %s - Net ID: %d Site ID: %d - Capacity Max", model_str, net, site);
|
||||
|
||||
//add string for ncurses terminal display
|
||||
if (n != 0) sprintf (state->dmr_site_parms, "TIII - %s %d-%d.%d ", model_str, net+1, (site>>n)+1, (site & sub_mask)+1 );
|
||||
else sprintf (state->dmr_site_parms, "TIII - %s %d-%d ", model_str, net, site);
|
||||
|
||||
//debug print
|
||||
uint16_t syscode = (uint16_t)ConvertBitIntoBytes(&slco_bits[4], 14);
|
||||
//fprintf (stderr, "\n SYSCODE: %014b", syscode);
|
||||
//fprintf (stderr, "\n SYSCODE: %02b.%b.%b", model, net, site); //won't show leading zeroes, but may not be required
|
||||
|
||||
}
|
||||
else if (slco == 0x0) //null
|
||||
fprintf (stderr, " SLCO NULL ");
|
||||
else if (slco == 0x1)
|
||||
fprintf (stderr, " SLCO Activity Update TS1: %X Hash: %02X TS2: %X Hash: %02X", ts1_act, ts1_hash, ts2_act, ts2_hash); //102 361-2 7.1.3.2
|
||||
else if (slco == 0x9)
|
||||
{
|
||||
state->dmr_mfid = 0x10;
|
||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
sprintf (state->dmr_branding_sub, "%s", "Con+ ");
|
||||
fprintf (stderr, " SLCO Connect Plus Voice Channel - Net ID: %d Site ID: %d", con_netid, con_siteid);
|
||||
sprintf (state->dmr_site_parms, "%d-%d ", con_netid, con_siteid);
|
||||
}
|
||||
|
||||
else if (slco == 0xA)
|
||||
{
|
||||
state->dmr_mfid = 0x10;
|
||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
sprintf (state->dmr_branding_sub, "%s", "Con+ ");
|
||||
fprintf (stderr, " SLCO Connect Plus Control Channel - Net ID: %d Site ID: %d", con_netid, con_siteid);
|
||||
sprintf (state->dmr_site_parms, "%d-%d ", con_netid, con_siteid);
|
||||
|
||||
//if using rigctl we can set an unknown cc frequency by polling rigctl for the current frequency
|
||||
if (opts->use_rigctl == 1 && state->p25_cc_freq == 0) //if not set from channel map 0
|
||||
{
|
||||
ccfreq = GetCurrentFreq (opts->rigctl_sockfd);
|
||||
if (ccfreq != 0) state->p25_cc_freq = ccfreq;
|
||||
}
|
||||
}
|
||||
|
||||
else if (slco == 0xF)
|
||||
{
|
||||
state->dmr_mfid = 0x10;
|
||||
sprintf (state->dmr_branding, "%s", "Motorola");
|
||||
sprintf (state->dmr_branding_sub, "%s", "Cap+ ");
|
||||
fprintf (stderr, " SLCO Capacity Plus Rest Channel %d", restchannel);
|
||||
state->dmr_rest_channel = restchannel;
|
||||
//assign to cc freq if available
|
||||
if (state->trunk_chan_map[restchannel] != 0)
|
||||
{
|
||||
state->p25_cc_freq = state->trunk_chan_map[restchannel];
|
||||
}
|
||||
|
||||
//nullify any previous TIII data (bugfix for bad assignments or system type switching)
|
||||
sprintf(state->dmr_site_parms, "%s", "");
|
||||
}
|
||||
|
||||
else fprintf (stderr, " SLCO Unknown - %d ", slco);
|
||||
|
||||
if (opts->payload == 1 && slco != 0) //if not SLCO NULL
|
||||
{
|
||||
fprintf (stderr, "\n SLCO Completed Block ");
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
fprintf (stderr, "[%02X]", slco_bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
|
||||
//externalize embedded alias to keep the flco function relatively clean
|
||||
void dmr_embedded_alias_header (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
||||
{
|
||||
|
||||
uint8_t slot = state->currentslot;
|
||||
uint8_t pf;
|
||||
uint8_t res;
|
||||
uint8_t format = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 2);
|
||||
uint8_t len;
|
||||
|
||||
//this len seems to pertain to number of blocks? not bit len.
|
||||
//len = (uint8_t)ConvertBitIntoBytes(&lc_bits[18], 5);
|
||||
|
||||
if (format == 0) len = 7;
|
||||
else if (format == 1 || format == 2) len = 8;
|
||||
else len = 16;
|
||||
|
||||
state->dmr_alias_format[slot] = format;
|
||||
state->dmr_alias_len[slot] = len;
|
||||
|
||||
//fprintf (stderr, "F: %d L: %d - ", format, len);
|
||||
|
||||
}
|
||||
|
||||
void dmr_embedded_alias_blocks (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Embedded Alias: ");
|
||||
uint8_t slot = state->currentslot;
|
||||
uint8_t block = (uint8_t)ConvertBitIntoBytes(&lc_bits[2], 6); //FLCO equals block number
|
||||
uint8_t format = state->dmr_alias_format[slot]; //0=7-bit; 1=ISO8; 2=UTF-8; 3=UTF16BE
|
||||
uint8_t len = state->dmr_alias_len[slot];
|
||||
uint8_t start; //starting position depends on context of block and format
|
||||
|
||||
//there is some issue with the next three lines of code that prevent proper assignments, not sure what.
|
||||
//if (block > 4) start = 16;
|
||||
//else if (block == 4 && format > 0) start = 23; //8-bit and 16-bit chars
|
||||
//else start = 24;
|
||||
|
||||
//forcing start to 16 make it work on 8-bit alias, len seems okay when set off of format
|
||||
start = 16;
|
||||
len = 8;
|
||||
|
||||
// fprintf (stderr, "block: %d start: %d len: %d ", block, start, len);
|
||||
|
||||
//all may not be used depending on format, len, start.
|
||||
uint16_t A0, A1, A2, A3, A4, A5, A6;
|
||||
|
||||
//sanity check
|
||||
if (block > 7) block = 4; //prevent oob array (although we should never get here)
|
||||
|
||||
if (len > 6) //if not greater than zero, then the header hasn't arrived yet
|
||||
{
|
||||
A0 = 0; A1 = 0; A2 = 0; A3 = 0; A4 = 0; A5 = 0; A6 = 0; //NULL ASCII Characters
|
||||
A0 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*0], len);
|
||||
A1 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*1], len);
|
||||
A2 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*2], len);
|
||||
A3 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*3], len);
|
||||
A4 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*4], len);
|
||||
A5 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*5], len);
|
||||
A6 = (uint16_t)ConvertBitIntoBytes(&lc_bits[start+len*6], len);
|
||||
|
||||
//just going to assign the usual ascii set here to prevent 'naughty' characters, sans diacriticals
|
||||
if (A0 > 0x19 && A0 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][0], "%c", A0);
|
||||
if (A1 > 0x19 && A1 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][1], "%c", A1);
|
||||
if (A2 > 0x19 && A2 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][2], "%c", A2);
|
||||
if (A3 > 0x19 && A3 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][3], "%c", A3);
|
||||
if (A4 > 0x19 && A4 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][4], "%c", A4);
|
||||
if (A5 > 0x19 && A5 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][5], "%c", A5);
|
||||
if (A6 > 0x19 && A6 < 0x7F) sprintf (state->dmr_alias_block_segment[slot][block-4][6], "%c", A6);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
for (int j = 0; j < 7; j++)
|
||||
{
|
||||
fprintf (stderr, "%s", state->dmr_alias_block_segment[slot][i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else fprintf (stderr, "Missing Header Block Format and Len Data");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
}
|
||||
|
||||
//externalize embedded GPS - Needs samples to test/fix function
|
||||
void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
||||
{
|
||||
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);
|
||||
|
||||
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 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 (still cannot verify as accurate)
|
||||
//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");
|
||||
}
|
||||
latitude = ((double)lat * lat_unit);
|
||||
|
||||
if (lon_sign)
|
||||
{
|
||||
lon = 0x1000001 - lon;
|
||||
sprintf (lonstr, "%s", "W");
|
||||
}
|
||||
longitude = ((double)lon * lon_unit);
|
||||
|
||||
//sanity check
|
||||
if (abs (latitude) < 90 && abs(longitude) < 180)
|
||||
{
|
||||
fprintf (stderr, " Lat: %.5lf %s Lon: %.5lf %s", latitude, latstr, longitude, lonstr);
|
||||
|
||||
//7.2.15 Position Error
|
||||
uint16_t position_error = 2 * pow(10, pos_err); //2 * 10^pos_err
|
||||
if (pos_err == 0x7 ) fprintf (stderr, " Position Error: Unknown or Invalid");
|
||||
else fprintf (stderr, " Position Error: Less than %dm", position_error);
|
||||
|
||||
//save to array for ncurses
|
||||
if (pos_err != 0x7)
|
||||
{
|
||||
sprintf (state->dmr_embedded_gps[slot], "E-GPS (%lf %s %lf %s) Err: %dm", latitude, latstr, longitude, lonstr, position_error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
139
src/dmr_le.c
Normal file
139
src/dmr_le.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dmr_le.c
|
||||
* DMR Late Entry MI Fragment Assembly, Procesing, and related Alg functions
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-12 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
//gather ambe_fr mi fragments for processing
|
||||
void dmr_late_entry_mi_fragment (dsd_opts * opts, dsd_state * state, uint8_t vc, char ambe_fr[4][24], char ambe_fr2[4][24], char ambe_fr3[4][24])
|
||||
{
|
||||
|
||||
uint8_t slot = state->currentslot;
|
||||
|
||||
//collect our fragments and place them into storage
|
||||
state->late_entry_mi_fragment[slot][vc][0] = (uint64_t)ConvertBitIntoBytes(&ambe_fr[3][0], 4);
|
||||
state->late_entry_mi_fragment[slot][vc][1] = (uint64_t)ConvertBitIntoBytes(&ambe_fr2[3][0], 4);
|
||||
state->late_entry_mi_fragment[slot][vc][2] = (uint64_t)ConvertBitIntoBytes(&ambe_fr3[3][0], 4);
|
||||
|
||||
if (vc == 6) dmr_late_entry_mi (opts, state);
|
||||
|
||||
}
|
||||
|
||||
void dmr_late_entry_mi (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
uint8_t slot = state->currentslot;
|
||||
int i, j;
|
||||
int g[3];
|
||||
unsigned char mi_go_bits[24];
|
||||
|
||||
uint64_t mi_test = 0;
|
||||
uint64_t go_test = 0;
|
||||
uint64_t mi_corrected = 0;
|
||||
uint64_t go_corrected = 0;
|
||||
|
||||
mi_test = (state->late_entry_mi_fragment[slot][1][0] << 32L) | (state->late_entry_mi_fragment[slot][2][0] << 28) | (state->late_entry_mi_fragment[slot][3][0] << 24) |
|
||||
(state->late_entry_mi_fragment[slot][1][1] << 20) | (state->late_entry_mi_fragment[slot][2][1] << 16) | (state->late_entry_mi_fragment[slot][3][1] << 12) |
|
||||
(state->late_entry_mi_fragment[slot][1][2] << 8) | (state->late_entry_mi_fragment[slot][2][2] << 4) | (state->late_entry_mi_fragment[slot][3][2] << 0);
|
||||
|
||||
go_test = (state->late_entry_mi_fragment[slot][4][0] << 32L) | (state->late_entry_mi_fragment[slot][5][0] << 28) | (state->late_entry_mi_fragment[slot][6][0] << 24) |
|
||||
(state->late_entry_mi_fragment[slot][4][1] << 20) | (state->late_entry_mi_fragment[slot][5][1] << 16) | (state->late_entry_mi_fragment[slot][6][1] << 12) |
|
||||
(state->late_entry_mi_fragment[slot][4][2] << 8) | (state->late_entry_mi_fragment[slot][5][2] << 4) | (state->late_entry_mi_fragment[slot][6][2] << 0);
|
||||
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
mi_go_bits[i] = (( mi_test << (i+j*12) ) & 0x800000000) >> 35;
|
||||
mi_go_bits[i+12] = (( go_test << (i+j*12) ) & 0x800000000) >> 35;
|
||||
}
|
||||
//execute golay decode and assign pass or fail to g
|
||||
if ( Golay_24_12_decode(mi_go_bits) ) g[j] = 1;
|
||||
else g[j] = 0;
|
||||
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
mi_corrected = mi_corrected << 1;
|
||||
mi_corrected |= mi_go_bits[i];
|
||||
go_corrected = go_corrected << 1;
|
||||
go_corrected |= mi_go_bits[i+12];
|
||||
}
|
||||
}
|
||||
|
||||
int mi_final = 0;
|
||||
mi_final = (mi_corrected >> 4) & 0xFFFFFFFF;
|
||||
|
||||
if (g[0] && g[1] && g[2])
|
||||
{
|
||||
if (slot == 0 && state->payload_algid != 0)
|
||||
{
|
||||
if (state->payload_mi != mi_final)
|
||||
{
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, " Slot 1 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_mi, mi_final);
|
||||
state->payload_mi = mi_final;
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
}
|
||||
if (slot == 1 && state->payload_algidR != 0)
|
||||
{
|
||||
if (state->payload_miR != mi_final)
|
||||
{
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr, " Slot 2 PI/LFSR and Late Entry MI Mismatch - %08X : %08X \n", state->payload_miR, mi_final);
|
||||
state->payload_miR = mi_final;
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void dmr_alg_refresh (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
state->dropL = 256;
|
||||
|
||||
if (state->K1 != 0)
|
||||
{
|
||||
state->DMRvcL = 0;
|
||||
}
|
||||
|
||||
if (state->payload_algid >= 0x21)
|
||||
{
|
||||
LFSR (state);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
}
|
||||
if (state->currentslot == 1)
|
||||
{
|
||||
state->dropR = 256;
|
||||
|
||||
if (state->K1 != 0)
|
||||
{
|
||||
state->DMRvcR = 0;
|
||||
}
|
||||
|
||||
if (state->payload_algidR >= 0x21)
|
||||
{
|
||||
LFSR (state);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void dmr_alg_reset (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
state->dropL = 256;
|
||||
state->dropR = 256;
|
||||
state->DMRvcL = 0;
|
||||
state->DMRvcR = 0;
|
||||
state->payload_miP = 0;
|
||||
}
|
||||
|
||||
608
src/dmr_ms.c
Normal file
608
src/dmr_ms.c
Normal file
@@ -0,0 +1,608 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dmr_ms.c
|
||||
* DMR MS/Simplex/Direct Mode Voice Handling and Data Gathering Routines
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-12 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include "dmr_const.h"
|
||||
|
||||
//A subroutine for processing MS voice
|
||||
void dmrMS (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int i, j, k, l, dibit;
|
||||
int *dibit_p;
|
||||
char ambe_fr[4][24];
|
||||
char ambe_fr2[4][24];
|
||||
char ambe_fr3[4][24];
|
||||
char ambe_fr4[4][24];
|
||||
|
||||
//memcpy of ambe_fr for late entry
|
||||
char m1[4][24];
|
||||
char m2[4][24];
|
||||
char m3[4][24];
|
||||
|
||||
const int *w, *x, *y, *z;
|
||||
char sync[25];
|
||||
char syncdata[48];
|
||||
char EmbeddedSignalling[16];
|
||||
|
||||
//cach
|
||||
char cachdata[25];
|
||||
int cachInterleave[24] =
|
||||
{0, 7, 8, 9, 1, 10,
|
||||
11, 12, 2, 13, 14,
|
||||
15, 3, 16, 4, 17, 18,
|
||||
19, 5, 20, 21, 22, 6, 23
|
||||
};
|
||||
|
||||
//cach tact bits
|
||||
uint8_t tact_bits[7];
|
||||
|
||||
uint8_t emb_ok = 0;
|
||||
uint8_t tact_okay = 0;
|
||||
uint8_t cach_err = 0;
|
||||
uint8_t EmbeddedSignallingOk = 0;
|
||||
|
||||
uint8_t internalslot;
|
||||
uint8_t vc;
|
||||
|
||||
//dummy bits to pass to dburst for link control
|
||||
uint8_t dummy_bits[196];
|
||||
memset (dummy_bits, 0, sizeof (dummy_bits));
|
||||
|
||||
//add time to mirror printFrameSync
|
||||
time_t now;
|
||||
char * getTime(void) //get pretty hh:mm:ss timestamp
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
|
||||
char * curr;
|
||||
char * stamp = asctime(localtime( & t));
|
||||
|
||||
curr = strtok(stamp, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
vc = 2;
|
||||
|
||||
short int skipcount = 0;
|
||||
|
||||
//Hardset variables for MS/Mono
|
||||
state->currentslot = 0; //0
|
||||
|
||||
//Note: Manual dibit inversion required here since I didn't seperate inverted return from normal return in framesync,
|
||||
//so getDibit doesn't know to invert it before it gets here
|
||||
|
||||
for (j = 0; j < 6; j++) {
|
||||
state->dmrburstL = 16;
|
||||
|
||||
for(i = 0; i < 12; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
||||
|
||||
cachdata[i] = dibit;
|
||||
state->dmr_stereo_payload[i] = dibit;
|
||||
}
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
tact_bits[i] = cachdata[i];
|
||||
}
|
||||
|
||||
tact_okay = 0;
|
||||
if ( Hamming_7_4_decode (tact_bits) ) tact_okay = 1;
|
||||
if (tact_okay != 1)
|
||||
{
|
||||
//do nothing since we aren't loop locked forever.
|
||||
}
|
||||
|
||||
//internalslot = tact_bits[1];
|
||||
internalslot = 0;
|
||||
|
||||
//Setup for first AMBE Frame
|
||||
//Interleave Schedule
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
|
||||
//First AMBE Frame, Full 36
|
||||
for(i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
||||
|
||||
state->dmr_stereo_payload[i+12] = dibit;
|
||||
|
||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
//Setup for Second AMBE Frame
|
||||
//Interleave Schedule
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
|
||||
//Second AMBE Frame, First Half 18 dibits just before Sync or EmbeddedSignalling
|
||||
for(i = 0; i < 18; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
||||
|
||||
state->dmr_stereo_payload[i+48] = dibit;
|
||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
// signaling data or sync
|
||||
for(i = 0; i < 24; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
||||
|
||||
state->dmr_stereo_payload[i+66] = dibit;
|
||||
|
||||
syncdata[(2*i)] = (1 & (dibit >> 1)); // bit 1
|
||||
syncdata[(2*i)+1] = (1 & dibit); // bit 0
|
||||
|
||||
sync[i] = (dibit | 1) + 48;
|
||||
|
||||
// load the superframe to do embedded signal processing
|
||||
if(vc > 1 && vc < 6) //grab on vc1 values 2-5 B C D and E
|
||||
{
|
||||
state->dmr_embedded_signalling[internalslot][vc-1][i*2] = (1 & (dibit >> 1)); // bit 1
|
||||
state->dmr_embedded_signalling[internalslot][vc-1][i*2+1] = (1 & dibit); // bit 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sync[24] = 0;
|
||||
EmbeddedSignallingOk = -1;
|
||||
if(QR_16_7_6_decode(EmbeddedSignalling))
|
||||
{
|
||||
EmbeddedSignallingOk = 1;
|
||||
}
|
||||
|
||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||
for(i = 0; i < 18; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
||||
|
||||
state->dmr_stereo_payload[i+90] = dibit;
|
||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
//Setup for Third AMBE Frame
|
||||
//Interleave Schedule
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
|
||||
//Third AMBE Frame, Full 36
|
||||
for(i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
||||
|
||||
state->dmr_stereo_payload[i+108] = dibit;
|
||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
state->dmr_ms_mode = 1;
|
||||
|
||||
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
|
||||
|
||||
//copy ambe_fr frames first, running process mbe will correct them,
|
||||
//but this also leads to issues extracting good le mi values when
|
||||
//we go to do correction on them there too
|
||||
memcpy (m1, ambe_fr, sizeof(m1));
|
||||
memcpy (m2, ambe_fr2, sizeof(m2));
|
||||
memcpy (m3, ambe_fr3, sizeof(m3));
|
||||
|
||||
if (state->directmode == 0)
|
||||
{
|
||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio on tdma direct
|
||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||
}
|
||||
|
||||
if (vc == 6)
|
||||
{
|
||||
dmr_data_burst_handler(opts, state, (uint8_t *)dummy_bits, 0xEB);
|
||||
fprintf (stderr, "\n");
|
||||
dmr_alg_refresh (opts, state);
|
||||
}
|
||||
|
||||
//collect the mi fragment
|
||||
dmr_late_entry_mi_fragment (opts, state, vc, m1, m2, m3);
|
||||
|
||||
//errors in ms/mono since we skip the other slot
|
||||
// cach_err = dmr_cach (opts, state, cachdata);
|
||||
|
||||
//update voice sync time for trunking purposes (particularly Con+)
|
||||
state->last_vc_sync_time = time(NULL);
|
||||
|
||||
vc++;
|
||||
//this is necessary because we need to skip and collect dibits, not just skip them
|
||||
if (vc > 6) goto END;
|
||||
|
||||
skipDibit (opts, state, 144); //skip to next tdma channel
|
||||
state->dmr_ms_rc = 0;
|
||||
|
||||
//since we are in a loop, run ncursesPrinter here
|
||||
if (opts->use_ncurses_terminal == 1)
|
||||
{
|
||||
ncursesPrinter(opts, state);
|
||||
}
|
||||
|
||||
} // end loop
|
||||
|
||||
END:
|
||||
//get first half payload dibits and store them in the payload for the next repitition
|
||||
skipDibit (opts, state, 144); //should we have two of these?
|
||||
|
||||
//CACH + First Half Payload = 12 + 54
|
||||
for (i = 0; i < 66; i++) //66
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
if (opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
state->dmr_stereo_payload[i] = dibit;
|
||||
|
||||
}
|
||||
|
||||
state->dmr_stereo = 0;
|
||||
state->dmr_ms_mode = 0;
|
||||
state->dmr_ms_rc = 0;
|
||||
state->directmode = 0; //flag off
|
||||
|
||||
|
||||
}
|
||||
|
||||
//collect buffered 1st half and get 2nd half voice payload and then jump to full MS Voice decoding.
|
||||
void dmrMSBootstrap (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
int i, j, k, l, dibit;
|
||||
int *dibit_p;
|
||||
|
||||
char ambe_fr[4][24];
|
||||
char ambe_fr2[4][24];
|
||||
char ambe_fr3[4][24];
|
||||
char ambe_fr4[4][24]; //shim for crappy tdma direct, need more samples to see if this is really a necessary shim or not
|
||||
|
||||
//memcpy of ambe_fr for late entry
|
||||
char m1[4][24];
|
||||
char m2[4][24];
|
||||
char m3[4][24];
|
||||
|
||||
const int *w, *x, *y, *z;
|
||||
char sync[25];
|
||||
char syncdata[48];
|
||||
|
||||
//cach
|
||||
char cachdata[25];
|
||||
int cachInterleave[24] =
|
||||
{0, 7, 8, 9, 1, 10,
|
||||
11, 12, 2, 13, 14,
|
||||
15, 3, 16, 4, 17, 18,
|
||||
19, 5, 20, 21, 22, 6, 23
|
||||
};
|
||||
|
||||
int mutecurrentslot;
|
||||
int msMode;
|
||||
char cc[4];
|
||||
char EmbeddedSignalling[16];
|
||||
|
||||
uint8_t emb_ok = 0;
|
||||
uint8_t tact_okay = 0;
|
||||
uint8_t cach_err = 0;
|
||||
uint8_t EmbeddedSignallingOk = 0;
|
||||
|
||||
//add time to mirror sync
|
||||
time_t now;
|
||||
char * getTime(void) //get pretty hh:mm:ss timestamp
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
|
||||
char * curr;
|
||||
char * stamp = asctime(localtime( & t));
|
||||
|
||||
curr = strtok(stamp, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
state->dmrburstL = 16;
|
||||
state->currentslot = 0; //force to slot 0
|
||||
|
||||
dibit_p = state->dmr_payload_p - 90;
|
||||
|
||||
//CACH + First Half Payload + Sync = 12 + 54 + 24
|
||||
for (i = 0; i < 90; i++) //90
|
||||
{
|
||||
state->dmr_stereo_payload[i] = *dibit_p;
|
||||
dibit_p++;
|
||||
}
|
||||
|
||||
for(i = 0; i < 12; i++)
|
||||
{
|
||||
dibit = state->dmr_stereo_payload[i];
|
||||
if(opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
cachdata[i] = dibit;
|
||||
}
|
||||
|
||||
//Setup for first AMBE Frame
|
||||
|
||||
//Interleave Schedule
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
|
||||
//First AMBE Frame, Full 36
|
||||
for(i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = state->dmr_stereo_payload[i+12];
|
||||
if(opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
state->dmr_stereo_payload[i+12] = dibit;
|
||||
ambe_fr[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
//Setup for Second AMBE Frame
|
||||
|
||||
//Interleave Schedule
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
|
||||
//Second AMBE Frame, First Half 18 dibits just before Sync or EmbeddedSignalling
|
||||
for(i = 0; i < 18; i++)
|
||||
{
|
||||
dibit = state->dmr_stereo_payload[i+48];
|
||||
if(opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
//Continue Second AMBE Frame, 18 after Sync or EmbeddedSignalling
|
||||
for(i = 0; i < 18; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
if(opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
ambe_fr2[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr2[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
//Setup for Third AMBE Frame
|
||||
|
||||
//Interleave Schedule
|
||||
w = rW;
|
||||
x = rX;
|
||||
y = rY;
|
||||
z = rZ;
|
||||
|
||||
//Third AMBE Frame, Full 36
|
||||
for(i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
if(opts->inverted_dmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
ambe_fr3[*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr3[*y][*z] = (1 & dibit); // bit 0
|
||||
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", getTime());
|
||||
if (opts->inverted_dmr == 0)
|
||||
{
|
||||
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
|
||||
}
|
||||
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
|
||||
|
||||
fprintf (stderr, "| VC* ");
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
//alg reset
|
||||
//dmr_alg_reset (opts, state);
|
||||
|
||||
memcpy (ambe_fr4, ambe_fr2, sizeof(ambe_fr2));
|
||||
|
||||
//copy ambe_fr frames first, running process mbe will correct them,
|
||||
//but this also leads to issues extracting good le mi values when
|
||||
//we go to do correction on them there too
|
||||
memcpy (m1, ambe_fr, sizeof(m1));
|
||||
memcpy (m2, ambe_fr2, sizeof(m2));
|
||||
memcpy (m3, ambe_fr3, sizeof(m3));
|
||||
|
||||
if (state->directmode == 0)
|
||||
{
|
||||
processMbeFrame (opts, state, NULL, ambe_fr, NULL);
|
||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
processMbeFrame (opts, state, NULL, ambe_fr4, NULL); //play duplicate of 2 here to smooth audio
|
||||
processMbeFrame (opts, state, NULL, ambe_fr2, NULL);
|
||||
processMbeFrame (opts, state, NULL, ambe_fr3, NULL);
|
||||
}
|
||||
|
||||
//collect the mi fragment
|
||||
dmr_late_entry_mi_fragment (opts, state, 1, ambe_fr, ambe_fr2, ambe_fr3);
|
||||
|
||||
//errors due to skipping other slot
|
||||
// cach_err = dmr_cach (opts, state, cachdata);
|
||||
|
||||
skipDibit (opts, state, 144); //skip to next TDMA slot
|
||||
dmrMS (opts, state); //bootstrap into full TDMA frame
|
||||
|
||||
}
|
||||
|
||||
//simplied to a simple data collector, and then passed on to dmr_data_sync for the usual processing
|
||||
void dmrMSData (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
int i, b, c;
|
||||
int dibit;
|
||||
int *dibit_p;
|
||||
|
||||
//add time to mirror sync
|
||||
time_t now;
|
||||
char * getTime(void) //get pretty hh:mm:ss timestamp
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
|
||||
char * curr;
|
||||
char * stamp = asctime(localtime( & t));
|
||||
|
||||
curr = strtok(stamp, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
|
||||
//CACH + First Half Payload + Sync = 12 + 54 + 24
|
||||
dibit_p = state->dmr_payload_p - 90;
|
||||
for (i = 0; i < 90; i++) //90
|
||||
{
|
||||
dibit = *dibit_p;
|
||||
dibit_p++;
|
||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
||||
state->dmr_stereo_payload[i] = dibit;
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < 54; i++)
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
if(opts->inverted_dmr == 1) dibit = (dibit ^ 2);
|
||||
state->dmr_stereo_payload[i+90] = dibit;
|
||||
|
||||
}
|
||||
|
||||
fprintf (stderr, "%s ", getTime());
|
||||
if (opts->inverted_dmr == 0)
|
||||
{
|
||||
fprintf (stderr,"Sync: +DMR MS/DM MODE/MONO ");
|
||||
}
|
||||
else fprintf (stderr,"Sync: -DMR MS/DM MODE/MONO ");
|
||||
|
||||
|
||||
sprintf(state->slot1light, "%s", "");
|
||||
sprintf(state->slot2light, "%s", "");
|
||||
|
||||
//process data
|
||||
state->dmr_stereo = 1;
|
||||
state->dmr_ms_mode = 1;
|
||||
|
||||
dmr_data_sync (opts, state);
|
||||
|
||||
state->dmr_stereo = 0;
|
||||
state->dmr_ms_mode = 0;
|
||||
state->directmode = 0; //flag off
|
||||
|
||||
//should just be loaded in the dmr_payload_buffer instead now
|
||||
for (i = 0; i < 144; i++) //66
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
state->dmr_stereo_payload[i] = dibit;
|
||||
}
|
||||
//CACH + First Half Payload = 12 + 54
|
||||
for (i = 0; i < 66; i++) //66
|
||||
{
|
||||
dibit = getDibit(opts, state);
|
||||
state->dmr_stereo_payload[i] = dibit;
|
||||
}
|
||||
|
||||
}
|
||||
414
src/dmr_pdu.c
Normal file
414
src/dmr_pdu.c
Normal file
@@ -0,0 +1,414 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dmr_bs.c
|
||||
* DMR Data (1/2, 3/4, 1) PDU Decoding
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-12 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
char * getTimeL(void) //get pretty hh:mm:ss timestamp
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
|
||||
char * curr;
|
||||
char * stamp = asctime(localtime( & t));
|
||||
|
||||
curr = strtok(stamp, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
char * getDateL(void) {
|
||||
char datename[120];
|
||||
char * curr2;
|
||||
struct tm * to;
|
||||
time_t t;
|
||||
t = time(NULL);
|
||||
to = localtime( & t);
|
||||
strftime(datename, sizeof(datename), "%Y/%m/%d", to);
|
||||
curr2 = strtok(datename, " ");
|
||||
return curr2;
|
||||
}
|
||||
|
||||
void dmr_pdu (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||
{
|
||||
|
||||
uint8_t message_len = 0;
|
||||
uint8_t slot = state->currentslot;
|
||||
uint8_t blocks = state->data_header_blocks[slot];
|
||||
uint8_t padding = state->data_header_padding[slot];
|
||||
uint8_t lrrp_conf = 0;
|
||||
|
||||
//consider disabling on prop_head data blocks if bad/false decodes
|
||||
//if (state->data_p_head[slot] == 0)
|
||||
lrrp_conf = dmr_lrrp (opts, state, block_len, DMR_PDU);
|
||||
|
||||
//maybe one day we will have more things to do here
|
||||
state->data_conf_data[slot] = 0; //flag off confirmed data after processing it
|
||||
state->data_p_head[slot] = 0; //flag off prop_head data after processing it
|
||||
|
||||
}
|
||||
|
||||
//The contents of this function are mostly my reversed engineered efforts by observing DSDPlus output and matching data bytes
|
||||
//combined with a few external sources such as OK-DMR for some token values and extra data values (rad and alt)
|
||||
//this is by no means an extensive LRRP list and is prone to error (unless somebody has the manual or something)
|
||||
uint8_t dmr_lrrp (dsd_opts * opts, dsd_state * state, uint8_t block_len, uint8_t DMR_PDU[])
|
||||
{
|
||||
int i, j;
|
||||
uint16_t message_len = 0;
|
||||
uint8_t slot = state->currentslot;
|
||||
uint8_t blocks = state->data_header_blocks[slot];
|
||||
uint8_t padding = state->data_header_padding[slot];
|
||||
uint8_t lrrp_confidence = 0; //variable to increment based on number of tokens found, the more, the higher the confidence level
|
||||
|
||||
//source/dest and ports
|
||||
uint32_t source = 0;
|
||||
uint32_t dest = 0;
|
||||
uint16_t port_s = 0;
|
||||
uint16_t port_d = 0;
|
||||
|
||||
//time
|
||||
uint16_t year = 0;
|
||||
uint16_t month = 0;
|
||||
uint16_t day = 0;
|
||||
uint16_t hour = 0;
|
||||
uint16_t minute = 0;
|
||||
uint16_t second = 0;
|
||||
|
||||
//lat-long-radius-alt
|
||||
uint32_t lat = 0;
|
||||
uint8_t lat_sign = 0;
|
||||
uint32_t lon = 0;
|
||||
uint16_t rad = 0;
|
||||
uint8_t alt = 0;
|
||||
double lat_unit = (double)180/(double)4294967295;
|
||||
double lon_unit = (double)360/(double)4294967295;
|
||||
|
||||
//speed/velocity
|
||||
uint16_t vel = 0;
|
||||
double velocity = 0;
|
||||
uint8_t vel_set = 0;
|
||||
|
||||
//track, direction, degrees
|
||||
uint8_t degrees = 0;
|
||||
uint8_t deg_set = 0;
|
||||
|
||||
//triggered information report
|
||||
uint8_t report = 0;
|
||||
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)
|
||||
for (i; i < ( (blocks*block_len) - (padding+4) ); i++)
|
||||
{
|
||||
|
||||
switch(DMR_PDU[i]){
|
||||
case 0x0C: //LRRP_TriggeredLocationReport
|
||||
if (source == 0)
|
||||
{
|
||||
source = (DMR_PDU[i+1] << 16 ) + (DMR_PDU[i+2] << 8) + DMR_PDU[i+3];
|
||||
dest = (DMR_PDU[i+5] << 16 ) + (DMR_PDU[i+6] << 8) + DMR_PDU[i+7];
|
||||
port_s = (DMR_PDU[i+8] << 8) + DMR_PDU[i+9];
|
||||
port_d = (DMR_PDU[i+10] << 8) + DMR_PDU[i+11];
|
||||
i += 11;
|
||||
lrrp_confidence++;
|
||||
}
|
||||
break;
|
||||
//may need more 'report' types to trigger various scenarios
|
||||
//disable all but 0x1F if issues or falsing happens often
|
||||
//case 0x1D: //ARRP_TriggeredInformationAnswer_NCDT
|
||||
//case 0x1E: //ARRP_TriggeredInformationReport = FALSE?
|
||||
//case 0x25: //ARRP_UnsolicitedInformationReport_NCDT = (0x25, True, "")
|
||||
//case 0x26: //ARRP_UnsolicitedInformationReport_NCDT
|
||||
//case 0x13: //LRRP_UnsolicitedLocationReport_NCDT
|
||||
//case 0x15: //LRRP_LocationProtocolReport_NCDT
|
||||
//case 0x21: //ARRP_TriggeredInformationStopRequest_NCDT
|
||||
case 0x1F: //ARRP_TriggeredInformationReport_NCDT
|
||||
if (report == 0)
|
||||
{
|
||||
report = DMR_PDU[i];
|
||||
i += 1;
|
||||
lrrp_confidence++;
|
||||
}
|
||||
break;
|
||||
//0x0D always seems to follow 0x1F or other 'report' types two/three bytes later
|
||||
case 0x0D: //message len indicator //LRRP_TriggeredLocationReport_NCDT
|
||||
if (report > 0 && message_len == 0)
|
||||
{
|
||||
message_len = DMR_PDU[i+1];
|
||||
i += 1;
|
||||
lrrp_confidence++;
|
||||
}
|
||||
else if (message_len == 0)
|
||||
{
|
||||
if (i > 3)
|
||||
{
|
||||
if (DMR_PDU[i-3] > 0) pot_report = DMR_PDU[i-3];
|
||||
if (pot_report < 0x27)
|
||||
{
|
||||
report = pot_report;
|
||||
message_len = DMR_PDU[i+1];
|
||||
i += 1;
|
||||
//no lrrp_confidence on speculative reporting, but will print it as such.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
//RESULT TOKEN or Request ID!
|
||||
case 0x23: //this value has been seen after the 0x0D message len, appears to be a 2-byte value, was tripping a false on time stamp
|
||||
i += 2;
|
||||
break;
|
||||
//answer and report tokens
|
||||
case 0x51: //circle-2d
|
||||
case 0x54: //circle-3d
|
||||
case 0x55: //circle-3d <- false positives on one sample, so either an earlier token occurred (that we don't have) or just random data?
|
||||
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 );
|
||||
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||
rad = (DMR_PDU[i+9] << 8) + DMR_PDU[i+10];
|
||||
i += 10; //double check
|
||||
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
||||
else lat = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x34: //Time Interval Periodic Trigger (timestamp)
|
||||
case 0x35: //Time Interval Periodic Trigger (timestamp)
|
||||
if (message_len > 0 && year == 0)
|
||||
{
|
||||
year = (DMR_PDU[i+1] << 6) + (DMR_PDU[i+2] >> 2);
|
||||
month = ((DMR_PDU[i+2] & 0x3) << 2) + ((DMR_PDU[i+3] & 0xC0) >> 6);
|
||||
day = ((DMR_PDU[i+3] & 0x30) >> 1) + ((DMR_PDU[i+3] & 0x0E) >> 1);
|
||||
hour = ((DMR_PDU[i+3] & 0x01) << 4) + ((DMR_PDU[i+4] & 0xF0) >> 4);
|
||||
minute = ((DMR_PDU[i+4] & 0x0F) << 2) + ((DMR_PDU[i+5] & 0xC0) >> 6);
|
||||
second = (DMR_PDU[i+5] & 0x3F);
|
||||
i += 5;
|
||||
//sanity check
|
||||
if (year > 2000 && year <= 2025) lrrp_confidence++;
|
||||
if (year > 2025 || year < 2000) year = 0; //needs future proofing
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x66: //point-2d
|
||||
if (message_len > 0 && lat == 0)
|
||||
{
|
||||
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 );
|
||||
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||
i += 8; //check
|
||||
lrrp_confidence++;
|
||||
}
|
||||
break;
|
||||
case 0x69: //point-3d
|
||||
case 0x6A: //point-3d
|
||||
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 );
|
||||
lon = ( ( (DMR_PDU[i+5] << 24 ) + (DMR_PDU[i+6] << 16) + (DMR_PDU[i+7] << 8) + DMR_PDU[i+8]) * 1 );
|
||||
alt = DMR_PDU[i+9];
|
||||
i += 9; //check
|
||||
if (lat > 0 && lon > 0) lrrp_confidence++; //would be better to set by absolute boundary (180 and 90?)
|
||||
else lat = 0;
|
||||
}
|
||||
break;
|
||||
case 0x36: //protocol-version
|
||||
i += 1;
|
||||
//lrrp_confidence++;
|
||||
break;
|
||||
//speed/velocity
|
||||
case 0x70: //speed-virt
|
||||
case 0x6C: //speed-hor
|
||||
if (message_len > 0 && vel_set == 0)
|
||||
{
|
||||
vel = (DMR_PDU[i+1] << 8) + DMR_PDU[i+2]; //don't think this is meant to be shifted, but doing it anyways for comparison
|
||||
velocity = ( ((double)( (DMR_PDU[i+1] ) + DMR_PDU[i+2] )) / ( (double)128));
|
||||
vel_set = 1;
|
||||
i += 2;
|
||||
lrrp_confidence++;
|
||||
}
|
||||
break;
|
||||
case 0x56: //direction-hor
|
||||
if (message_len > 0 && deg_set == 0)
|
||||
{
|
||||
degrees = DMR_PDU[i+1] * 2;
|
||||
deg_set = 1;
|
||||
i += 1;
|
||||
lrrp_confidence++;
|
||||
}
|
||||
break;
|
||||
//below all unknown
|
||||
case 0x37: //result
|
||||
case 0x38: //result
|
||||
case 0x39: //result 'operations error'? attributes 0x22?
|
||||
case 0x6B: //unknown-uint8
|
||||
case 0x65: //lev-conf
|
||||
default:
|
||||
//do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (report && message_len > 0)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, "\n LRRP Confidence: %d - Message Len: %d Octets", lrrp_confidence, message_len);
|
||||
if (lrrp_confidence >= 4) //find the sweet magical number
|
||||
{
|
||||
//now we can open our lrrp file and write to it as well
|
||||
FILE * pFile; //file pointer
|
||||
if (opts->lrrp_file_output == 1)
|
||||
{
|
||||
//open file by name that is supplied in the ncurses terminal, or cli
|
||||
pFile = fopen (opts->lrrp_out_file, "a");
|
||||
//write current date/time if not present in LRRP data
|
||||
if (!year) fprintf (pFile, "%s\t", getDateL() ); //current date, only add this IF no included timestamp in LRRP data?
|
||||
if (!year) fprintf (pFile, "%s\t", getTimeL() ); //current timestamp, only add this IF no included timestamp in LRRP data?
|
||||
if (year) fprintf (pFile, "%04d/%02d/%02d\t%02d:%02d:%02d\t", year, month, day, hour, minute, second); //add timestamp from decoded audio if available
|
||||
//write data header source (or destination?) if not available in lrrp data
|
||||
if (!source) fprintf (pFile, "%08lld\t", state->dmr_lrrp_source[state->currentslot]); //source address from data header
|
||||
if (source) fprintf (pFile, "%08d\t", source); //add source form decoded audio if available, else its from the header
|
||||
}
|
||||
|
||||
if (pot_report)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
||||
}
|
||||
if (report)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Report: 0x%02X", report);
|
||||
if (report == 0x1F) fprintf (stderr, " ARRP_TriggeredInformationReport_NCDT "); //customize later when more is learned
|
||||
if (report == 0x21) fprintf (stderr, " ARRP_TriggeredInformationStopRequest_NCDT ");
|
||||
if (report == 0x22) fprintf (stderr, " ARRP_TriggeredInformationStopAnswer ");
|
||||
if (report == 0x25) fprintf (stderr, " ARRP_UnsolicitedInformationReport_NCDT ");
|
||||
if (report == 0x26) fprintf (stderr, " ARRP_UnsolicitedInformationReport_NCDT ");
|
||||
if (report == 0x27) fprintf (stderr, " ARRP_InformationProtocolRequest_NCDT ");
|
||||
if (report == 0x13) fprintf (stderr, " LRRP_UnsolicitedLocationReport_NCDT ");
|
||||
if (report == 0x15) fprintf (stderr, " LRRP_UnsolicitedLocationReport_NCDT ");
|
||||
|
||||
}
|
||||
if (source)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Source: %08d - %04d", source, port_s);
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Destination: %08d - %04d", dest, port_d);
|
||||
|
||||
}
|
||||
if (year)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " LRRP - Time: ");
|
||||
fprintf (stderr, " %04d.%02d.%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
|
||||
|
||||
}
|
||||
if (lat)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
if (lat_sign) //print a '-' in front of the value
|
||||
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)
|
||||
{
|
||||
if (lat_sign) fprintf (pFile, "-");
|
||||
fprintf (pFile, "%.5lf\t", (double)lat * lat_unit);
|
||||
fprintf (pFile, "%.5lf\t", (double)lon * lon_unit);
|
||||
}
|
||||
|
||||
}
|
||||
if (rad)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " LRRP - Radius: %d", rad); //unsure of 'units' or calculation for radius (meters?)
|
||||
}
|
||||
if (alt)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " LRRP - Altitude: %d", alt); //unsure of 'units' or calculation for alt (meters?)
|
||||
}
|
||||
if (vel_set)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " LRRP - Velocity: %.4lf m/s %.4lf km/h %.4lf mph", velocity, (3.6 * velocity), (2.2369 * velocity));
|
||||
if (opts->lrrp_file_output == 1) fprintf (pFile, "%.3lf\t ", (velocity * 3.6) );
|
||||
}
|
||||
if (deg_set)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " LRRP - Track: %d Degrees", degrees);
|
||||
if (opts->lrrp_file_output == 1) fprintf (pFile, "%d\t",degrees);
|
||||
}
|
||||
|
||||
//close open file
|
||||
if (opts->lrrp_file_output == 1)
|
||||
{
|
||||
fprintf (pFile, "\n");
|
||||
fclose (pFile);
|
||||
}
|
||||
|
||||
//save to array for ncurses
|
||||
if (!source) source = state->dmr_lrrp_source[state->currentslot];
|
||||
char sign[8];
|
||||
char velstr[20];
|
||||
char degstr[20];
|
||||
char lrrpstr[100];
|
||||
sprintf (lrrpstr, "%s", "");
|
||||
sprintf (velstr, "%s", "");
|
||||
sprintf (degstr, "%s", "");
|
||||
if (lat_sign) sprintf (sign, "%s", "-");
|
||||
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 (deg_set) sprintf (degstr, " %d deg", degrees);
|
||||
sprintf (state->dmr_lrrp_gps[slot], "%s%s%s", lrrpstr, velstr, degstr);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if (pot_report)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, " Potential ARRP/LRRP Report (Debug): 0x%02X", report);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
return (lrrp_confidence);
|
||||
}
|
||||
136
src/dmr_pi.c
Normal file
136
src/dmr_pi.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dmr_pi.c
|
||||
* DMR Privacy Indicator and LFSR Function
|
||||
*
|
||||
* LFSR code courtesy of https://github.com/mattames/LFSR/
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-12 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void dmr_pi (dsd_opts * opts, dsd_state * state, uint8_t PI_BYTE[], uint32_t CRCCorrect, uint32_t IrrecoverableErrors)
|
||||
{
|
||||
|
||||
if((IrrecoverableErrors == 0))
|
||||
{
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
|
||||
if (0 == 0)
|
||||
{
|
||||
state->payload_algid = PI_BYTE[0];
|
||||
state->payload_keyid = PI_BYTE[2];
|
||||
}
|
||||
|
||||
state->payload_mi = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
||||
if (state->payload_algid < 0x26)
|
||||
{
|
||||
fprintf (stderr, "%s ", KYEL);
|
||||
fprintf (stderr, "\n Slot 1");
|
||||
fprintf (stderr, " DMR PI Header ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algid, state->payload_keyid, state->payload_mi);
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
|
||||
if (state->payload_algid >= 0x26)
|
||||
{
|
||||
if (0 == 0)
|
||||
{
|
||||
state->payload_algid = 0;
|
||||
state->payload_keyid = 0;
|
||||
state->payload_mi = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state->currentslot == 1)
|
||||
{
|
||||
if (0 == 0)
|
||||
{
|
||||
state->payload_algidR = PI_BYTE[0];
|
||||
state->payload_keyidR = PI_BYTE[2];
|
||||
}
|
||||
|
||||
state->payload_miR = ( ((PI_BYTE[3]) << 24) + ((PI_BYTE[4]) << 16) + ((PI_BYTE[5]) << 8) + (PI_BYTE[6]) );
|
||||
if (state->payload_algidR < 0x26)
|
||||
{
|
||||
fprintf (stderr, "%s ", KYEL);
|
||||
fprintf (stderr, "\n Slot 2");
|
||||
fprintf (stderr, " DMR PI Header ALG ID: 0x%02X KEY ID: 0x%02X MI: 0x%08X", state->payload_algidR, state->payload_keyidR, state->payload_miR);
|
||||
fprintf (stderr, "%s ", KNRM);
|
||||
}
|
||||
|
||||
if (state->payload_algidR >= 0x26)
|
||||
{
|
||||
if (0== 0)
|
||||
{
|
||||
state->payload_algidR = 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)
|
||||
{
|
||||
int lfsr = 0;
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
lfsr = state->payload_mi;
|
||||
}
|
||||
else lfsr = state->payload_miR;
|
||||
|
||||
uint8_t cnt = 0;
|
||||
|
||||
for(cnt=0;cnt<32;cnt++)
|
||||
{
|
||||
// Polynomial is C(x) = x^32 + x^4 + x^2 + 1
|
||||
int bit = ((lfsr >> 31) ^ (lfsr >> 3) ^ (lfsr >> 1)) & 0x1;
|
||||
lfsr = (lfsr << 1) | (bit);
|
||||
}
|
||||
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
if (1 == 1)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Slot 1");
|
||||
fprintf (stderr, " DMR PI Continuation ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algid, state->payload_keyid);
|
||||
fprintf(stderr, " Next MI: 0x%08X", lfsr);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
state->payload_mi = lfsr;
|
||||
}
|
||||
|
||||
if (state->currentslot == 1)
|
||||
{
|
||||
if (1 == 1)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " Slot 2");
|
||||
fprintf (stderr, " DMR PI Continuation ALG ID: 0x%02X KEY ID: 0x%02X", state->payload_algidR, state->payload_keyidR);
|
||||
fprintf(stderr, " Next MI: 0x%08X", lfsr);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
state->payload_miR = lfsr;
|
||||
}
|
||||
}
|
||||
332
src/dmr_utils.c
Normal file
332
src/dmr_utils.c
Normal file
@@ -0,0 +1,332 @@
|
||||
//DMR CRC/Utility Functions
|
||||
//Original File - dmr_sync.c
|
||||
//ConvertBitIntoBytes, ComputeCrcCCITT, ComputeCrc5Bit, ComputeAndCorrectFullLinkControlCrc, CRC32, CRC9
|
||||
//Original Source - https://github.com/LouisErigHerve/dsd
|
||||
|
||||
//Additional Functions
|
||||
//Hamming17123, crc8, crc8ok functions
|
||||
//Original Souce - https://github.com/boatbod/op25
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
//modified to accept variable payload size and len
|
||||
uint16_t ComputeCrcCCITT16d(const uint8_t buf[], uint8_t len)
|
||||
{
|
||||
uint32_t i;
|
||||
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */
|
||||
/* Polynomial x^16 + x^12 + x^5 + 1
|
||||
* Normal = 0x1021
|
||||
* Reciprocal = 0x0811
|
||||
* Reversed = 0x8408
|
||||
* Reversed reciprocal = 0x8810 */
|
||||
uint16_t Polynome = 0x1021;
|
||||
for(i = 0; i < len; i++)
|
||||
{
|
||||
if(((CRC >> 15) & 1) ^ (buf[i] & 1))
|
||||
{
|
||||
CRC = (CRC << 1) ^ Polynome;
|
||||
}
|
||||
else
|
||||
{
|
||||
CRC <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Invert the CRC */
|
||||
CRC ^= 0xFFFF;
|
||||
|
||||
/* Return the CRC */
|
||||
return CRC;
|
||||
} /* End ComputeCrcCCITTd() */
|
||||
|
||||
// A Hamming (17,12,3) Check for completed SLC message
|
||||
bool Hamming17123(uint8_t* d)
|
||||
{
|
||||
|
||||
// Calculate the checksum this column should have
|
||||
bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9];
|
||||
bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10];
|
||||
bool c2 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11];
|
||||
bool c3 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10];
|
||||
bool c4 = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11];
|
||||
|
||||
// Compare these with the actual bits
|
||||
unsigned char n = 0x00U;
|
||||
n |= (c0 != d[12]) ? 0x01U : 0x00U;
|
||||
n |= (c1 != d[13]) ? 0x02U : 0x00U;
|
||||
n |= (c2 != d[14]) ? 0x04U : 0x00U;
|
||||
n |= (c3 != d[15]) ? 0x08U : 0x00U;
|
||||
n |= (c4 != d[16]) ? 0x10U : 0x00U;
|
||||
|
||||
switch (n) {
|
||||
// Parity bit errors
|
||||
case 0x01U: d[12] = !d[12]; return true;
|
||||
case 0x02U: d[13] = !d[13]; return true;
|
||||
case 0x04U: d[14] = !d[14]; return true;
|
||||
case 0x08U: d[15] = !d[15]; return true;
|
||||
case 0x10U: d[16] = !d[16]; return true;
|
||||
|
||||
// Data bit errors
|
||||
case 0x1BU: d[0] = !d[0]; return true;
|
||||
case 0x1FU: d[1] = !d[1]; return true;
|
||||
case 0x17U: d[2] = !d[2]; return true;
|
||||
case 0x07U: d[3] = !d[3]; return true;
|
||||
case 0x0EU: d[4] = !d[4]; return true;
|
||||
case 0x1CU: d[5] = !d[5]; return true;
|
||||
case 0x11U: d[6] = !d[6]; return true;
|
||||
case 0x0BU: d[7] = !d[7]; return true;
|
||||
case 0x16U: d[8] = !d[8]; return true;
|
||||
case 0x05U: d[9] = !d[9]; return true;
|
||||
case 0x0AU: d[10] = !d[10]; return true;
|
||||
case 0x14U: d[11] = !d[11]; return true;
|
||||
|
||||
// No bit errors
|
||||
case 0x00U: return true;
|
||||
|
||||
// Unrecoverable errors
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t crc8(uint8_t bits[], unsigned int len)
|
||||
{
|
||||
uint8_t crc=0;
|
||||
unsigned int K = 8;
|
||||
uint8_t poly[9] = {1,0,0,0,0,0,1,1,1}; // crc8 poly
|
||||
uint8_t buf[256];
|
||||
if (len+K > sizeof(buf)) {
|
||||
//fprintf (stderr, "crc8: buffer length %u exceeds maximum %lu\n", len+K, sizeof(buf));
|
||||
return 0;
|
||||
}
|
||||
memset (buf, 0, sizeof(buf));
|
||||
for (unsigned int i=0; i<len; i++){
|
||||
buf[i] = bits[i];
|
||||
}
|
||||
for (unsigned int i=0; i<len; i++)
|
||||
if (buf[i])
|
||||
for (unsigned int j=0; j<K+1; j++)
|
||||
buf[i+j] ^= poly[j];
|
||||
for (unsigned int i=0; i<K; i++){
|
||||
crc = (crc << 1) + buf[len + i];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool crc8_ok(uint8_t bits[], unsigned int len)
|
||||
{
|
||||
uint16_t crc = 0;
|
||||
for (unsigned int i=0; i < 8; i++) {
|
||||
crc = (crc << 1) + bits[len+i];
|
||||
}
|
||||
return (crc == crc8(bits,len));
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief : This function compute the CRC-CCITT of the DMR data
|
||||
* by using the polynomial x^16 + x^12 + x^5 + 1
|
||||
*
|
||||
* @param Input : A buffer pointer of the DMR data (80 bytes)
|
||||
*
|
||||
* @return The 16 bit CRC
|
||||
*/
|
||||
|
||||
uint16_t ComputeCrcCCITT(uint8_t * DMRData)
|
||||
{
|
||||
uint32_t i;
|
||||
uint16_t CRC = 0x0000; /* Initialization value = 0x0000 */
|
||||
/* Polynomial x^16 + x^12 + x^5 + 1
|
||||
* Normal = 0x1021
|
||||
* Reciprocal = 0x0811
|
||||
* Reversed = 0x8408
|
||||
* Reversed reciprocal = 0x8810 */
|
||||
uint16_t Polynome = 0x1021;
|
||||
for(i = 0; i < 80; i++)
|
||||
{
|
||||
if(((CRC >> 15) & 1) ^ (DMRData[i] & 1))
|
||||
{
|
||||
CRC = (CRC << 1) ^ Polynome;
|
||||
}
|
||||
else
|
||||
{
|
||||
CRC <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Invert the CRC */
|
||||
CRC ^= 0xFFFF;
|
||||
|
||||
/* Return the CRC */
|
||||
return CRC;
|
||||
} /* End ComputeCrcCCITT() */
|
||||
|
||||
|
||||
/*
|
||||
* @brief : This function compute the CRC-24 bit of the full
|
||||
* link control by using the Reed-Solomon(12,9) FEC
|
||||
*
|
||||
* @param FullLinkControlDataBytes : A buffer pointer of the DMR data bytes (12 bytes)
|
||||
*
|
||||
* @param CRCComputed : A 32 bit pointer where the computed CRC 24-bit will be stored
|
||||
*
|
||||
* @param CRCMask : The 24 bit CRC mask to apply
|
||||
*
|
||||
* @return 0 = CRC error
|
||||
* 1 = CRC is correct
|
||||
*/
|
||||
|
||||
uint32_t ComputeAndCorrectFullLinkControlCrc(uint8_t * FullLinkControlDataBytes, uint32_t * CRCComputed, uint32_t CRCMask)
|
||||
{
|
||||
uint32_t i;
|
||||
rs_12_9_codeword_t VoiceLCHeaderStr;
|
||||
rs_12_9_poly_t syndrome;
|
||||
uint8_t errors_found = 0;
|
||||
rs_12_9_correct_errors_result_t result = RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND;
|
||||
uint32_t CrcIsCorrect = 0;
|
||||
|
||||
for(i = 0; i < 12; i++)
|
||||
{
|
||||
VoiceLCHeaderStr.data[i] = FullLinkControlDataBytes[i];
|
||||
|
||||
/* Apply CRC mask on each 3 last bytes
|
||||
* of the full link control */
|
||||
if(i == 9)
|
||||
{
|
||||
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask >> 16);
|
||||
}
|
||||
else if(i == 10)
|
||||
{
|
||||
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask >> 8);
|
||||
}
|
||||
else if(i == 11)
|
||||
{
|
||||
VoiceLCHeaderStr.data[i] ^= (uint8_t)(CRCMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
}
|
||||
|
||||
/* Check and correct the full link LC control with Reed Solomon (12,9) FEC */
|
||||
rs_12_9_calc_syndrome(&VoiceLCHeaderStr, &syndrome);
|
||||
if(rs_12_9_check_syndrome(&syndrome) != 0) result = rs_12_9_correct_errors(&VoiceLCHeaderStr, &syndrome, &errors_found);
|
||||
|
||||
/* Reconstitue the CRC */
|
||||
*CRCComputed = (uint32_t)((VoiceLCHeaderStr.data[9] << 16) & 0xFF0000);
|
||||
*CRCComputed |= (uint32_t)((VoiceLCHeaderStr.data[10] << 8) & 0x00FF00);
|
||||
*CRCComputed |= (uint32_t)((VoiceLCHeaderStr.data[11] << 0) & 0x0000FF);
|
||||
|
||||
if((result == RS_12_9_CORRECT_ERRORS_RESULT_NO_ERRORS_FOUND) ||
|
||||
(result == RS_12_9_CORRECT_ERRORS_RESULT_ERRORS_CORRECTED))
|
||||
{
|
||||
//fprintf(stderr, "CRC OK : 0x%06X\n", *CRCComputed);
|
||||
CrcIsCorrect = 1;
|
||||
|
||||
/* Reconstitue full link control data after FEC correction */
|
||||
for(i = 0; i < 12; i++)
|
||||
{
|
||||
FullLinkControlDataBytes[i] = VoiceLCHeaderStr.data[i];
|
||||
|
||||
/* Apply CRC mask on each 3 last bytes
|
||||
* of the full link control */
|
||||
if(i == 9)
|
||||
{
|
||||
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask >> 16);
|
||||
}
|
||||
else if(i == 10)
|
||||
{
|
||||
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask >> 8);
|
||||
}
|
||||
else if(i == 11)
|
||||
{
|
||||
FullLinkControlDataBytes[i] ^= (uint8_t)(CRCMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//fprintf(stderr, "CRC ERROR : 0x%06X\n", *CRCComputed);
|
||||
CrcIsCorrect = 0;
|
||||
}
|
||||
|
||||
/* Return the CRC status */
|
||||
return CrcIsCorrect;
|
||||
} /* End ComputeAndCorrectFullLinkControlCrc() */
|
||||
|
||||
|
||||
/*
|
||||
* @brief : This function compute the 5 bit CRC of the DMR voice burst data
|
||||
* See ETSI TS 102 361-1 chapter B.3.11
|
||||
*
|
||||
* @param Input : A buffer pointer of the DMR data (72 bytes)
|
||||
*
|
||||
* @return The 5 bit CRC
|
||||
*/
|
||||
|
||||
uint8_t ComputeCrc5Bit(uint8_t * DMRData)
|
||||
{
|
||||
uint32_t i, j, k;
|
||||
uint8_t Buffer[9];
|
||||
uint32_t Sum;
|
||||
uint8_t CRC = 0;
|
||||
|
||||
/* Convert the 72 bit into 9 bytes */
|
||||
k = 0;
|
||||
for(i = 0; i < 9; i++)
|
||||
{
|
||||
Buffer[i] = 0;
|
||||
for(j = 0; j < 8; j++)
|
||||
{
|
||||
Buffer[i] = Buffer[i] << 1;
|
||||
Buffer[i] = Buffer[i] | DMRData[k++];
|
||||
}
|
||||
}
|
||||
|
||||
/* Add all 9 bytes */
|
||||
Sum = 0;
|
||||
for(i = 0; i < 9; i++)
|
||||
{
|
||||
Sum += (uint32_t)Buffer[i];
|
||||
}
|
||||
|
||||
/* Sum MOD 31 = CRC */
|
||||
CRC = (uint8_t)(Sum % 31);
|
||||
|
||||
/* Return the CRC */
|
||||
return CRC;
|
||||
} /* End ComputeCrc5Bit() */
|
||||
|
||||
uint32_t ConvertBitIntoBytes(uint8_t * BufferIn, uint32_t BitLength)
|
||||
{
|
||||
uint32_t Output = 0;
|
||||
uint32_t i;
|
||||
|
||||
for(i = 0; i < BitLength; i++)
|
||||
{
|
||||
Output <<= 1;
|
||||
Output |= (uint32_t)(BufferIn[i] & 1);
|
||||
}
|
||||
|
||||
return Output;
|
||||
} /* End ConvertBitIntoBytes() */
|
||||
|
||||
/*
|
||||
* @brief : This function compute the CRC-9 of the DMR data
|
||||
* by using the polynomial x^9 + x^6 + x^4 + x^3 + 1
|
||||
*
|
||||
* @param Input : A buffer pointer of the DMR data (80 bytes)
|
||||
* Rate 1/2 coded confirmed (10 data octets): 80 bit sequence (80 bytes)
|
||||
* Rate 3/4 coded confirmed (16 data octets): 128 bit sequence (120 bytes)
|
||||
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
|
||||
*
|
||||
* @param NbData : The number of bit to compute
|
||||
* Rate 1/2 coded confirmed (10 data octets): 80 bit sequence (80 bytes)
|
||||
* Rate 3/4 coded confirmed (16 data octets): 128 bit sequence (120 bytes)
|
||||
* Rate 1 coded confirmed (22 data octets): 176 bit sequence (176 bytes)
|
||||
*
|
||||
* @return The 9 bit CRC
|
||||
*/
|
||||
278
src/dpmr_data.c
Normal file
278
src/dpmr_data.c
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
|
||||
/* Convert the Channel code (24 bit) into a valid
|
||||
* dPMR color code [0..63]
|
||||
* Return -1 on error */
|
||||
int32_t GetdPmrColorCode(uint8_t ChannelCodeBit[24])
|
||||
{
|
||||
uint32_t ChannelCodeByte;
|
||||
int32_t ColorCode;
|
||||
uint32_t i;
|
||||
|
||||
/* Reconstitute the 24 bit channel code into a 3 bytes integer */
|
||||
ChannelCodeByte = 0;
|
||||
for(i = 0; i < 24; i++)
|
||||
{
|
||||
ChannelCodeByte <<= 1;
|
||||
ChannelCodeByte |= (ChannelCodeBit[i] & 1);
|
||||
}
|
||||
|
||||
/* By analyzing all channel code on the
|
||||
* dPMR standard, I observe that if we decompose the
|
||||
* 24 bit into 12 dibit, all dibit's LSB are always
|
||||
* equal to "1", so apply a mask to correct
|
||||
* flipped LSB dibit.
|
||||
*
|
||||
* For more details see ETSI TS 102 658 chapter
|
||||
* 6.1.5.2.2 "Channel Code Determined by Frequency
|
||||
* and System Identity Code".
|
||||
*
|
||||
* Apply the mask 0x555555
|
||||
* 0x555555 = 0b010101010101010101010101 => All dibit's LSB
|
||||
* are equal to "1".
|
||||
*/
|
||||
ChannelCodeByte |= 0x555555;
|
||||
|
||||
|
||||
/* Switch to the right color code */
|
||||
switch(ChannelCodeByte & 0xFF0000)
|
||||
{
|
||||
case 0x570000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0x575F77: {ColorCode = 0; break;}
|
||||
case 0x577577: {ColorCode = 1; break;}
|
||||
case 0x57DD75: {ColorCode = 2; break;}
|
||||
case 0x57F775: {ColorCode = 3; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0x570000 */
|
||||
|
||||
case 0x550000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0x55577D: {ColorCode = 4; break;}
|
||||
case 0x557D7D: {ColorCode = 5; break;}
|
||||
case 0x55D57F: {ColorCode = 6; break;}
|
||||
case 0x55FF7F: {ColorCode = 7; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0x550000 */
|
||||
|
||||
case 0x5F0000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0x5F555F: {ColorCode = 8; break;}
|
||||
case 0x5F7F5F: {ColorCode = 9; break;}
|
||||
case 0x5FD75D: {ColorCode = 10; break;}
|
||||
case 0x5FFD5D: {ColorCode = 11; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0x5F0000 */
|
||||
|
||||
case 0x5D0000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0x5D5D55: {ColorCode = 12; break;}
|
||||
case 0x5D7755: {ColorCode = 13; break;}
|
||||
case 0x5DDF57: {ColorCode = 14; break;}
|
||||
case 0x5DF557: {ColorCode = 15; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0x5D0000 */
|
||||
|
||||
case 0x770000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0x775DD7: {ColorCode = 16; break;}
|
||||
case 0x7777D7: {ColorCode = 17; break;}
|
||||
case 0x77DFD5: {ColorCode = 18; break;}
|
||||
case 0x77F5D5: {ColorCode = 19; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0x770000 */
|
||||
|
||||
case 0x750000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0x7555DD: {ColorCode = 20; break;}
|
||||
case 0x757FDD: {ColorCode = 21; break;}
|
||||
case 0x75D7DF: {ColorCode = 22; break;}
|
||||
case 0x75FDDF: {ColorCode = 23; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0x750000 */
|
||||
|
||||
case 0x7F0000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0x7F57FF: {ColorCode = 24; break;}
|
||||
case 0x7F7DFF: {ColorCode = 25; break;}
|
||||
case 0x7FD5FD: {ColorCode = 26; break;}
|
||||
case 0x7FFFFD: {ColorCode = 27; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0x7F0000 */
|
||||
|
||||
case 0x7D0000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0x7D5FF5: {ColorCode = 28; break;}
|
||||
case 0x7D75F5: {ColorCode = 29; break;}
|
||||
case 0x7DDDF7: {ColorCode = 30; break;}
|
||||
case 0x7DF7F7: {ColorCode = 31; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0x7D0000 */
|
||||
|
||||
case 0xD70000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0xD755F7: {ColorCode = 32; break;}
|
||||
case 0xD77FF7: {ColorCode = 33; break;}
|
||||
case 0xD7D7F5: {ColorCode = 34; break;}
|
||||
case 0xD7FDF5: {ColorCode = 35; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0xD70000 */
|
||||
|
||||
case 0xD50000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0xD55DFD: {ColorCode = 36; break;}
|
||||
case 0xD577FD: {ColorCode = 37; break;}
|
||||
case 0xD5DFFF: {ColorCode = 38; break;}
|
||||
case 0xD5F5FF: {ColorCode = 39; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0xD50000 */
|
||||
|
||||
case 0xDF0000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0xDF5FDF: {ColorCode = 40; break;}
|
||||
case 0xDF75DF: {ColorCode = 41; break;}
|
||||
case 0xDFDDDD: {ColorCode = 42; break;}
|
||||
case 0xDFF7DD: {ColorCode = 43; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0xDF0000 */
|
||||
|
||||
case 0xDD0000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0xDD57D5: {ColorCode = 44; break;}
|
||||
case 0xDD7DD5: {ColorCode = 45; break;}
|
||||
case 0xDDD5D7: {ColorCode = 46; break;}
|
||||
case 0xDDFFD7: {ColorCode = 47; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0xDD0000 */
|
||||
|
||||
case 0xF70000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0xF75757: {ColorCode = 48; break;}
|
||||
case 0xF77D57: {ColorCode = 49; break;}
|
||||
case 0xF7D555: {ColorCode = 50; break;}
|
||||
case 0xF7FF55: {ColorCode = 51; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0xF70000 */
|
||||
|
||||
case 0xF50000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0xF55F5D: {ColorCode = 52; break;}
|
||||
case 0xF5755D: {ColorCode = 53; break;}
|
||||
case 0xF5DD5F: {ColorCode = 54; break;}
|
||||
case 0xF5F75F: {ColorCode = 55; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0xF50000 */
|
||||
|
||||
case 0xFF0000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0xFF5D7F: {ColorCode = 56; break;}
|
||||
case 0xFF777F: {ColorCode = 57; break;}
|
||||
case 0xFFDF7D: {ColorCode = 58; break;}
|
||||
case 0xFFF57D: {ColorCode = 59; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0xFF0000 */
|
||||
|
||||
case 0xFD0000:
|
||||
{
|
||||
switch(ChannelCodeByte)
|
||||
{
|
||||
case 0xFD5575: {ColorCode = 60; break;}
|
||||
case 0xFD7F75: {ColorCode = 61; break;}
|
||||
case 0xFDD777: {ColorCode = 62; break;}
|
||||
case 0xFDFD77: {ColorCode = 63; break;}
|
||||
default: {ColorCode = -1; break;}
|
||||
} /* End switch(ChannelCodeByte) */
|
||||
break;
|
||||
} /* End case 0xFD0000 */
|
||||
|
||||
default:
|
||||
{
|
||||
ColorCode = -1;
|
||||
break;
|
||||
}
|
||||
} /* End switch(ChannelCodeByte & 0xFF0000) */
|
||||
|
||||
/* Return the colour code */
|
||||
return ColorCode;
|
||||
}
|
||||
|
||||
|
||||
/* End of file */
|
||||
704
src/dpmr_voice.c
Normal file
704
src/dpmr_voice.c
Normal file
@@ -0,0 +1,704 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include "dpmr_const.h"
|
||||
|
||||
void processdPMRvoice (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
uint32_t i, j, k, dibit;
|
||||
uint8_t CCH[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
||||
uint8_t CCHDescrambled[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
||||
uint8_t CCHDeInterleaved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][72] = {0};
|
||||
uint8_t CCHDataHammingCorrected[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][48];
|
||||
uint8_t CCHDataCRC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||
|
||||
memset (CCHDataHammingCorrected, 1, sizeof(CCHDataHammingCorrected));
|
||||
memset (CCHDataCRC, 1, sizeof(CCHDataCRC));
|
||||
|
||||
uint8_t CCHDataCRCComputed[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||
memset (CCHDataCRCComputed, 0, sizeof(CCHDataCRCComputed));
|
||||
|
||||
uint8_t CC[NB_OF_DPMR_VOICE_FRAME_TO_DECODE / 2][24] = {0};
|
||||
char ambe_fr[NB_OF_DPMR_VOICE_FRAME_TO_DECODE * 4][4][24];
|
||||
memset (ambe_fr, 0, sizeof (ambe_fr));
|
||||
const int *w, *x, *y, *z;
|
||||
uint32_t ScramblerLFSR = 0;
|
||||
bool correctable = true;
|
||||
bool HammingCorrectable[NB_OF_DPMR_VOICE_FRAME_TO_DECODE][6] = {0};
|
||||
uint32_t CrcOk[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
|
||||
uint32_t HammingOk[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
|
||||
uint32_t Temp = 0;
|
||||
/* CCH (Control CHannel) data */
|
||||
uint32_t CCH_FrameNumber[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
|
||||
uint32_t CCH_CalledID = 0;
|
||||
uint32_t CCH_CallingID = 0;
|
||||
uint32_t CCH_CommunicationMode[NB_OF_DPMR_VOICE_FRAME_TO_DECODE] = {0};
|
||||
uint32_t CCH_Version[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||
uint32_t CCH_CommsFormat[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||
uint32_t CCH_EmergencyPriority[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||
uint32_t CCH_Reserved[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||
uint32_t CCH_SlowData[NB_OF_DPMR_VOICE_FRAME_TO_DECODE];
|
||||
uint32_t *errs;
|
||||
uint32_t *errs2;
|
||||
uint8_t AmbeBitDescrambled[49] = {0};
|
||||
uint32_t ScramblerKey = 0;
|
||||
uint32_t PartOfSuperFrame = 0;
|
||||
uint32_t VoiceFrameFlag = 1; /* We consider the current frame as a voice frame */
|
||||
uint32_t AttachedDataFlag = 0;
|
||||
uint8_t CalledID[8] = {0};
|
||||
uint8_t CallingID[8] = {0};
|
||||
|
||||
/* First CCH (Control CHannel) - 72 bit */
|
||||
k = 0;
|
||||
|
||||
for (i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
|
||||
if (opts->inverted_dpmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
|
||||
CCH[0][(i * 2)] = (1 & (dibit >> 1)); // bit 1
|
||||
CCH[0][(i * 2) + 1] = (1 & dibit); // bit 0
|
||||
|
||||
}
|
||||
|
||||
/* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */
|
||||
k = 0;
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
w = dPmrW;
|
||||
x = dPmrX;
|
||||
y = dPmrY;
|
||||
z = dPmrZ;
|
||||
k = 0;
|
||||
for (i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
|
||||
if (opts->inverted_dpmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
|
||||
ambe_fr[j][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr[j][*y][*z] = (1 & dibit); // bit 0
|
||||
state->dPMRVoiceFS2Frame.RawVoiceBit[j][k] = (1 & (dibit >> 1)); // bit 1
|
||||
state->dPMRVoiceFS2Frame.RawVoiceBit[j][k + 1] = (1 & dibit); // bit 0
|
||||
k = k + 2;
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
}
|
||||
|
||||
/* First CC (Channel Code) - 24 bit */
|
||||
k = 0;
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
|
||||
if (opts->inverted_dpmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
|
||||
CC[0][k++] = (1 & (dibit >> 1)); // bit 1
|
||||
CC[0][k++] = (1 & dibit); // bit 0
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* Get the color code */
|
||||
state->dPMRVoiceFS2Frame.ColorCode[0] = (unsigned int)GetdPmrColorCode(CC[0]);
|
||||
|
||||
/* Second CCH (Control CHannel) - 72 bit */
|
||||
k = 0;
|
||||
for (i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
|
||||
if (opts->inverted_dpmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
|
||||
CCH[1][k++] = (1 & (dibit >> 1)); // bit 1
|
||||
CCH[1][k++] = (1 & dibit); // bit 0
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* 4 TCH (Traffic CHannel) = 4 x 72 bit voice playload */
|
||||
|
||||
k = 0;
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
w = dPmrW;
|
||||
x = dPmrX;
|
||||
y = dPmrY;
|
||||
z = dPmrZ;
|
||||
k = 0;
|
||||
for (i = 0; i < 36; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
|
||||
if (opts->inverted_dpmr == 1)
|
||||
{
|
||||
dibit = (dibit ^ 2);
|
||||
}
|
||||
|
||||
ambe_fr[j + 4][*w][*x] = (1 & (dibit >> 1)); // bit 1
|
||||
ambe_fr[j + 4][*y][*z] = (1 & dibit); // bit 0
|
||||
state->dPMRVoiceFS2Frame.RawVoiceBit[j + 4][k] = (1 & (dibit >> 1)); // bit 1
|
||||
state->dPMRVoiceFS2Frame.RawVoiceBit[j + 4][k + 1] = (1 & dibit); // bit 0
|
||||
k = k + 2;
|
||||
w++;
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Decoding all CCH (Control CHannel) */
|
||||
for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++)
|
||||
{
|
||||
/* Init LFSR value - All bit to '1' */
|
||||
ScramblerLFSR = 0x1FF;
|
||||
|
||||
/* Descramble data - Input 72 bit => Output 72 bit */
|
||||
ScrambledPMRBit(&ScramblerLFSR, CCH[i], CCHDescrambled[i], 72);
|
||||
|
||||
/* Deinterleave data - Input 72 bit => Output 72 bit */
|
||||
DeInterleave6x12DPmrBit(CCHDescrambled[i], CCHDeInterleaved[i]);
|
||||
|
||||
correctable = true;
|
||||
for(j = 0; j < 6; j++)
|
||||
{
|
||||
/* Apply the Hamming(12,8) correction - Input 72 bit => Output 48 bit */
|
||||
HammingCorrectable[i][j] = Hamming_12_8_decode(&CCHDeInterleaved[i][j*12], &CCHDataHammingCorrected[i][j*8], 1);
|
||||
if(HammingCorrectable[i][j] == false) correctable = false;
|
||||
}
|
||||
|
||||
if(correctable) HammingOk[i] = 1;
|
||||
else HammingOk[i] = 0;
|
||||
|
||||
/* Reconstitute the 7 bit CRC */
|
||||
CCHDataCRC[i] = 0;
|
||||
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][41]) << 6;
|
||||
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][42]) << 5;
|
||||
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][43]) << 4;
|
||||
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][44]) << 3;
|
||||
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][45]) << 2;
|
||||
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][46]) << 1;
|
||||
CCHDataCRC[i] |= (CCHDataHammingCorrected[i][47]) << 0;
|
||||
|
||||
/* Compute the 7 bit CRC */
|
||||
CCHDataCRCComputed[i] = CRC7BitdPMR(CCHDataHammingCorrected[i], 41);
|
||||
|
||||
if(CCHDataCRC[i] == CCHDataCRCComputed[i]) CrcOk[i] = 1;
|
||||
else CrcOk[i] = 0;
|
||||
|
||||
/* Fill the frame number */
|
||||
CCH_FrameNumber[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][0], 2);
|
||||
|
||||
/* Fill the communication mode */
|
||||
CCH_CommunicationMode[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][14], 3);
|
||||
|
||||
/* Fill the version */
|
||||
CCH_Version[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][17], 2);
|
||||
|
||||
/* Fill the comm format */
|
||||
CCH_CommsFormat[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][19], 2);
|
||||
|
||||
/* Fill the emergency priority bit */
|
||||
CCH_EmergencyPriority[i] = (uint32_t)CCHDataHammingCorrected[i][21];
|
||||
|
||||
/* Fill the reserved bit */
|
||||
CCH_Reserved[i] = (uint32_t)CCHDataHammingCorrected[i][22];
|
||||
|
||||
/* Fill the slow data */
|
||||
CCH_SlowData[i] = ConvertBitIntoBytes(&CCHDataHammingCorrected[i][23], 18);
|
||||
|
||||
/* Copy the CCH data into the structure */
|
||||
memcpy(state->dPMRVoiceFS2Frame.CCHData[i], CCHDataHammingCorrected[i], 48);
|
||||
state->dPMRVoiceFS2Frame.CCHDataHammingOk[i] = HammingOk[i];
|
||||
state->dPMRVoiceFS2Frame.CCHDataCRC[i] = CCHDataCRC[i];
|
||||
state->dPMRVoiceFS2Frame.CCHDataCrcOk[i] = CrcOk[i];
|
||||
state->dPMRVoiceFS2Frame.FrameNumbering[i] = CCH_FrameNumber[i];
|
||||
state->dPMRVoiceFS2Frame.CommunicationMode[i] = CCH_CommunicationMode[i];
|
||||
state->dPMRVoiceFS2Frame.Version[i] = CCH_Version[i];
|
||||
state->dPMRVoiceFS2Frame.CommsFormat[i] = CCH_CommsFormat[i];
|
||||
state->dPMRVoiceFS2Frame.EmergencyPriority[i] = CCH_EmergencyPriority[i];
|
||||
state->dPMRVoiceFS2Frame.Reserved[i] = CCH_Reserved[i];
|
||||
state->dPMRVoiceFS2Frame.SlowData[i] = CCH_SlowData[i];
|
||||
} /* End for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++) */
|
||||
|
||||
/* Get the last TG */
|
||||
strcpy((char *)CalledID, (char *)state->dPMRVoiceFS2Frame.CalledID);
|
||||
CalledID[7] = '\0';
|
||||
|
||||
/* Get the last source ID */
|
||||
strcpy((char *)CallingID, (char *)state->dPMRVoiceFS2Frame.CallingID);
|
||||
CallingID[7] = '\0';
|
||||
|
||||
|
||||
/* To determine the part of the payload frame, we need to check the
|
||||
* Hamming code result, the CRC and the frame number */
|
||||
|
||||
// if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 0)) ||
|
||||
// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 1)))
|
||||
|
||||
if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 0)) ||
|
||||
((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 1)))
|
||||
{
|
||||
/* First part of the super frame */
|
||||
PartOfSuperFrame = 1;
|
||||
|
||||
/* The next part will normally be the second part */
|
||||
opts->dPMR_next_part_of_superframe = 2;
|
||||
|
||||
/* Fill the called ID (talk group - TG) */
|
||||
Temp = ConvertBitIntoBytes(&CCHDataHammingCorrected[0][2], 12);
|
||||
CCH_CalledID = ((Temp << 12) & 0x00FFF000); /* 12 MSBit */
|
||||
Temp = ConvertBitIntoBytes(&CCHDataHammingCorrected[1][2], 12);
|
||||
CCH_CalledID |= (Temp & 0x00000FFF); /* 12 LSBit */
|
||||
ConvertAirInterfaceID(CCH_CalledID, CalledID);
|
||||
CalledID[7] = '\0';
|
||||
|
||||
/* Determine if the called TG ID only is correct */
|
||||
//if(HammingOk[0] && CrcOk[0] && HammingOk[1] && CrcOk[1])
|
||||
if((CrcOk[0] || (HammingCorrectable[0][0] && HammingCorrectable[0][1])) &&
|
||||
(CrcOk[1] || (HammingCorrectable[1][0] && HammingCorrectable[1][1])))
|
||||
{
|
||||
/* Save CCH data parameters */
|
||||
state->dPMRVoiceFS2Frame.CalledIDOk = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->dPMRVoiceFS2Frame.CalledIDOk = 0;
|
||||
}
|
||||
|
||||
/* BUGFIX : Copy in all case the TG ID */
|
||||
strcpy((char *)state->dPMRVoiceFS2Frame.CalledID, (char *)CalledID);
|
||||
}
|
||||
|
||||
/* To determine the part of the payload frame, we need to check the
|
||||
* Hamming code result, the CRC and the frame number */
|
||||
|
||||
// else if( (HammingOk[0] && CrcOk[0] && (CCH_FrameNumber[0] == 2)) ||
|
||||
// (HammingOk[1] && CrcOk[1] && (CCH_FrameNumber[1] == 3)))
|
||||
|
||||
else if(((CrcOk[0] || HammingCorrectable[0][0]) && (CCH_FrameNumber[0] == 2)) ||
|
||||
((CrcOk[1] || HammingCorrectable[1][0]) && (CCH_FrameNumber[1] == 3)))
|
||||
{
|
||||
/* Second part of the super frame */
|
||||
PartOfSuperFrame = 2;
|
||||
|
||||
/* The next part will normally be the first part */
|
||||
opts->dPMR_next_part_of_superframe = 1;
|
||||
|
||||
/* Fill the calling ID (source) */
|
||||
Temp = ConvertBitIntoBytes(&CCHDataHammingCorrected[0][2], 12);
|
||||
CCH_CallingID = ((Temp << 12) & 0x00FFF000); /* 12 MSBit */
|
||||
Temp = ConvertBitIntoBytes(&CCHDataHammingCorrected[1][2], 12);
|
||||
CCH_CallingID |= (Temp & 0x00000FFF); /* 12 LSBit */
|
||||
ConvertAirInterfaceID(CCH_CallingID, CallingID);
|
||||
CallingID[7] = '\0';
|
||||
|
||||
/* Determine if the calling SRC ID is correct */
|
||||
|
||||
//if(HammingOk[0] && CrcOk[0] && HammingOk[1] && CrcOk[1])
|
||||
|
||||
if((CrcOk[0] || (HammingCorrectable[0][0] && HammingCorrectable[0][1])) &&
|
||||
(CrcOk[1] || (HammingCorrectable[1][0] && HammingCorrectable[1][1])))
|
||||
{
|
||||
/* Save CCH data parameters */
|
||||
state->dPMRVoiceFS2Frame.CallingIDOk = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->dPMRVoiceFS2Frame.CallingIDOk = 0;
|
||||
}
|
||||
|
||||
/* BUGFIX : Copy in all case the SRC ID */
|
||||
strcpy((char *)state->dPMRVoiceFS2Frame.CallingID, (char *)CallingID);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The dPMR source and destination ID are now invalid */
|
||||
state->dPMRVoiceFS2Frame.CalledIDOk = 0;
|
||||
state->dPMRVoiceFS2Frame.CallingIDOk = 0;
|
||||
|
||||
/* Unknown part of superframe => We suppose
|
||||
* it is the next part of the previous frame received */
|
||||
PartOfSuperFrame = opts->dPMR_next_part_of_superframe;
|
||||
|
||||
/* Set the next part of the superframe to receive */
|
||||
if(opts->dPMR_next_part_of_superframe == 1)
|
||||
{
|
||||
/* Set the next part as the second part */
|
||||
opts->dPMR_next_part_of_superframe = 2;
|
||||
}
|
||||
else if(opts->dPMR_next_part_of_superframe == 2)
|
||||
{
|
||||
/* Set the next part as the first part */
|
||||
opts->dPMR_next_part_of_superframe = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Unknown current part, set to 0 */
|
||||
opts->dPMR_next_part_of_superframe = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Display the destination ID (talk group - TG) */
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
if(state->dPMRVoiceFS2Frame.CalledIDOk)
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf(stderr, " TG=%s", CalledID);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//check other as well before assigning
|
||||
if(state->dPMRVoiceFS2Frame.CallingIDOk) sprintf (state->dpmr_target_id, "%s", CalledID);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf(stderr, " TG=(CRC ERR)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
/* Display the source ID */
|
||||
if(state->dPMRVoiceFS2Frame.CallingIDOk)
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf(stderr, " Src=%s", CallingID);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//check other as well before assigning
|
||||
if(state->dPMRVoiceFS2Frame.CalledIDOk) sprintf (state->dpmr_caller_id, "%s", CallingID);
|
||||
|
||||
//stashed channel code in here, more likely to be correct, but no guarantee
|
||||
if(state->dPMRVoiceFS2Frame.ColorCode[0] != (unsigned int)(-1))
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf(stderr, " Channel Code=%02d", (int)state->dPMRVoiceFS2Frame.ColorCode[0]);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
|
||||
//check other as well before assigning
|
||||
if(state->dPMRVoiceFS2Frame.CalledIDOk) state->dpmr_color_code = (int)state->dPMRVoiceFS2Frame.ColorCode[0];
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf(stderr, " Src=(CRC ERR) Channel Code =(CRC ERR)");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (state->dPMRVoiceFS2Frame.Version[0] == 3)
|
||||
{
|
||||
fprintf (stderr, "%s", KRED);
|
||||
fprintf (stderr, " Scrambler");
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
if (state->R != 0)
|
||||
{
|
||||
fprintf (stderr, "%s", KYEL);
|
||||
fprintf (stderr, " KEY VALUE: [%05lld] ", state->R );
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
}
|
||||
|
||||
//Play only voice frames by first looking to see if there is a TCH voice in CommunicationMode,
|
||||
short start = 0;
|
||||
short end = 4;
|
||||
|
||||
//not really even sure why I made this loop
|
||||
//I probably should actually read the manual again
|
||||
for (short o = 0; o < 2; o++)
|
||||
{
|
||||
|
||||
//reset scrambler key seed on frame 0
|
||||
if (state->dPMRVoiceFS2Frame.FrameNumbering[o] == 0) state->payload_miN = 0;
|
||||
|
||||
//depending on first or second TCH, set start and end variables appropriately
|
||||
if (o == 1)
|
||||
{
|
||||
start = 4;
|
||||
end = 8;
|
||||
|
||||
}
|
||||
|
||||
//this is used so we can simply pass the voice here to the
|
||||
//nxdn scrambler by telling mbe these are nxdn frames and to
|
||||
//apply the same descramble method to them if necessary
|
||||
int realsynctype = state->synctype;
|
||||
|
||||
// fprintf (stderr, "\nCommunication Mode: %d\n", state->dPMRVoiceFS2Frame.CommunicationMode[o]);
|
||||
// fprintf (stderr, "Encryption Mode: %d\n", state->dPMRVoiceFS2Frame.Version[o]); //shows as 3 (manufacturer specific, 5.16) on the dPMR scrambler values received
|
||||
// fprintf (stderr, "Superframe Number: %d", state->dPMRVoiceFS2Frame.FrameNumbering[o]);
|
||||
|
||||
if((state->dPMRVoiceFS2Frame.CommunicationMode[o] == 0) ||
|
||||
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 1) ||
|
||||
(state->dPMRVoiceFS2Frame.CommunicationMode[o] == 5))
|
||||
{
|
||||
|
||||
//check to see if we are using scrambler
|
||||
if (state->dPMRVoiceFS2Frame.Version[o] == 3) //!= 0
|
||||
{
|
||||
state->synctype = 28; //fake it as nxdn
|
||||
state->nxdn_cipher_type = 0x01; //turn on nxdn scrambler cipher
|
||||
state->dmr_encL = 1; //flag on enc bit here so we can mute if no key provided
|
||||
}
|
||||
|
||||
//flag back off if key is provided
|
||||
if (state->R != 0) state->dmr_encL = 0;
|
||||
|
||||
//There are 4 AMBE voice frames per TCH
|
||||
for(i = start; i < end; i++)
|
||||
{
|
||||
processMbeFrame (opts, state, NULL, ambe_fr[i], NULL);
|
||||
}
|
||||
|
||||
//set the correct sync type again and flag off the cipher
|
||||
state->synctype = realsynctype;
|
||||
state->nxdn_cipher_type = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
|
||||
//keep code below, could be handy cheat sheet for other info
|
||||
#ifdef dPMR_PRINT_DEBUG_INFO
|
||||
//Display the CCH content
|
||||
for(i = 0; i < NB_OF_DPMR_VOICE_FRAME_TO_DECODE; i++)
|
||||
{
|
||||
fprintf(stderr, "i = %u - ", i);
|
||||
fprintf(stderr, "Comm Mode = %01u - ", state->dPMRVoiceFS2Frame.CommunicationMode[i]);
|
||||
fprintf(stderr, "Version = %01u - ", state->dPMRVoiceFS2Frame.Version[i]);
|
||||
fprintf(stderr, "Comms Format = %01u - ", state->dPMRVoiceFS2Frame.CommsFormat[i]);
|
||||
fprintf(stderr, "Emergency = %01u - ", state->dPMRVoiceFS2Frame.EmergencyPriority[i]);
|
||||
fprintf(stderr, "Reserved = %01u - ", state->dPMRVoiceFS2Frame.Reserved[i]);
|
||||
fprintf(stderr, "Slow Data = 0x%05X - ", state->dPMRVoiceFS2Frame.SlowData[i]);
|
||||
if(HammingOk[i] && CrcOk[i]) fprintf(stderr, "Valid");
|
||||
else fprintf(stderr, "CRC ERROR");
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
#endif //dPMR_PRINT_DEBUG_INFO
|
||||
|
||||
} //End processdPMRvoice()
|
||||
|
||||
|
||||
/* Scrambler used for dPMR transmission (different of the
|
||||
* voice encryption scrambler), see ETSI TS 102 658 chapter
|
||||
* 7.3 for the polynomial description.
|
||||
* It is a X^9 + X^5 + 1 polynomial. */
|
||||
|
||||
void ScrambledPMRBit(uint32_t * LfsrValue, uint8_t * BufferIn, uint8_t * BufferOut, uint32_t NbOfBitToScramble)
|
||||
{
|
||||
uint8_t S[9] = {0};
|
||||
uint32_t i;
|
||||
uint8_t Temp;
|
||||
uint32_t LFSRValue;
|
||||
|
||||
LFSRValue = *LfsrValue;
|
||||
|
||||
/* Load the initial LFSR value */
|
||||
for(i = 0; i < 9; i++)
|
||||
{
|
||||
S[i] = LFSRValue & 1;
|
||||
LFSRValue >>= 1;
|
||||
}
|
||||
|
||||
/* There are 72 bit to descramble for voice and 288 bit for data */
|
||||
for(i = 0; i < NbOfBitToScramble; i++)
|
||||
{
|
||||
BufferOut[i] = (BufferIn[i] ^ S[0]) & 0x01;
|
||||
|
||||
/* Shift registers */
|
||||
Temp = S[4] ^ S[0];
|
||||
S[0] = S[1];
|
||||
S[1] = S[2];
|
||||
S[2] = S[3];
|
||||
S[3] = S[4];
|
||||
S[4] = S[5];
|
||||
S[5] = S[6];
|
||||
S[6] = S[7];
|
||||
S[7] = S[8];
|
||||
S[8] = Temp;
|
||||
}
|
||||
|
||||
/* Save the final LFSR value */
|
||||
LFSRValue = 0;
|
||||
for(i = 9; i > 0; i--)
|
||||
{
|
||||
LFSRValue <<= 1;
|
||||
LFSRValue |= S[i - 1] & 1;
|
||||
}
|
||||
|
||||
*LfsrValue = LFSRValue;
|
||||
} /* End ScrambleDPmrBit() */
|
||||
|
||||
|
||||
/* Scrambler used for dPMR scrambling / descrambling,
|
||||
* see ETSI TS 102 658 chapter 7.4 for the
|
||||
* polynomial description.
|
||||
* It is a X^9 + X^5 + 1 polynomial. */
|
||||
void DeInterleave6x12DPmrBit(uint8_t * BufferIn, uint8_t * BufferOut)
|
||||
{
|
||||
uint8_t Matrix[12][6] = {0};
|
||||
uint32_t i, j, k;
|
||||
|
||||
/* Step 1 : Filling the 12 x 6 bit matrix */
|
||||
k = 0;
|
||||
for(i = 0; i < 12; i++)
|
||||
{
|
||||
for(j = 0; j < 6; j++)
|
||||
{
|
||||
Matrix[i][j] = BufferIn[k++];
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 2 : Filling the output buffer with deinterleaved data */
|
||||
k = 0;
|
||||
for(j = 0; j < 6; j++)
|
||||
{
|
||||
for(i = 0; i < 12; i++)
|
||||
{
|
||||
BufferOut[k++] = Matrix[i][j];
|
||||
}
|
||||
}
|
||||
} /* End DeInterleave6x12DPmrBit() */
|
||||
|
||||
|
||||
/* CRC 7 bit computation with the following
|
||||
* polynomial : X^7 + X^3 + 1 */
|
||||
uint8_t CRC7BitdPMR(uint8_t * BufferIn, uint32_t BitLength)
|
||||
{
|
||||
uint8_t ShiftRegister = 0x00; /* All bit to '0' (7 LSBit only used) */
|
||||
uint8_t Polynome = 0x09; /* X^7 + X^3 + 1 */
|
||||
uint32_t i;
|
||||
|
||||
for(i = 0; i < BitLength; i++)
|
||||
{
|
||||
if(((ShiftRegister >> 6) & 1) ^ BufferIn[i])
|
||||
{
|
||||
ShiftRegister = ((ShiftRegister << 1) ^ Polynome) & 0x7F;
|
||||
}
|
||||
else
|
||||
{
|
||||
ShiftRegister = (ShiftRegister << 1) & 0x7F;
|
||||
}
|
||||
}
|
||||
|
||||
return ShiftRegister;
|
||||
} /* End CRC7BitdPMR() */
|
||||
|
||||
|
||||
/* CRC 8 bit computation with the following
|
||||
* polynomial : X^8 + X^2 + X + 1 */
|
||||
uint8_t CRC8BitdPMR(uint8_t * BufferIn, uint32_t BitLength)
|
||||
{
|
||||
uint8_t ShiftRegister = 0xFF; /* All bit to '1' */
|
||||
uint8_t Polynome = 0x07; /* X^7 + X^3 + 1 */
|
||||
uint32_t i;
|
||||
|
||||
for(i = 0; i < BitLength; i++)
|
||||
{
|
||||
if(((ShiftRegister >> 7) & 1) ^ BufferIn[i])
|
||||
{
|
||||
ShiftRegister = ((ShiftRegister << 1) ^ Polynome) & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
ShiftRegister = (ShiftRegister << 1) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
return ShiftRegister;
|
||||
} /* End CRC8BitdPMR() */
|
||||
|
||||
|
||||
/* Convert an air interface identifier (AI ID) into
|
||||
* a 7 ASCII digit string.
|
||||
*
|
||||
* See dPMR standard chapter A.1.2.1.1.6
|
||||
* "Mapping of dialled strings to the AI address space" */
|
||||
void ConvertAirInterfaceID(uint32_t AI_ID, uint8_t ID[8])
|
||||
{
|
||||
uint32_t AI_ID_Temp = AI_ID;
|
||||
uint32_t Digit;
|
||||
|
||||
/* 1st digit */
|
||||
Digit = AI_ID_Temp / 1464100;
|
||||
AI_ID_Temp = AI_ID_Temp % 1464100;
|
||||
if(Digit == 10) ID[0] = '*';
|
||||
else ID[0] = Digit + '0';
|
||||
|
||||
/* 2nd digit */
|
||||
Digit = AI_ID_Temp / 146410;
|
||||
AI_ID_Temp = AI_ID_Temp % 146410;
|
||||
if(Digit == 10) ID[1] = '*';
|
||||
else ID[1] = Digit + '0';
|
||||
|
||||
/* 3rd digit */
|
||||
Digit = AI_ID_Temp / 14641;
|
||||
AI_ID_Temp = AI_ID_Temp % 14641;
|
||||
if(Digit == 10) ID[2] = '*';
|
||||
else ID[2] = Digit + '0';
|
||||
|
||||
/* 4th digit */
|
||||
Digit = AI_ID_Temp / 1331;
|
||||
AI_ID_Temp = AI_ID_Temp % 1331;
|
||||
if(Digit == 10) ID[3] = '*';
|
||||
else ID[3] = Digit + '0';
|
||||
|
||||
/* 5th digit */
|
||||
Digit = AI_ID_Temp / 121;
|
||||
AI_ID_Temp = AI_ID_Temp % 121;
|
||||
if(Digit == 10) ID[4] = '*';
|
||||
else ID[4] = Digit + '0';
|
||||
|
||||
/* 6th digit */
|
||||
Digit = AI_ID_Temp / 11;
|
||||
AI_ID_Temp = AI_ID_Temp % 11;
|
||||
if(Digit == 10) ID[5] = '*';
|
||||
else ID[5] = Digit + '0';
|
||||
|
||||
/* 7th digit */
|
||||
Digit = AI_ID_Temp;
|
||||
if(Digit == 10) ID[6] = '*';
|
||||
else ID[6] = Digit + '0';
|
||||
|
||||
/* Add the "end of string" */
|
||||
ID[7] = '\0';
|
||||
|
||||
} /* End convertAirInterfaceID() */
|
||||
|
||||
/* End of file */
|
||||
672
src/dsd_audio.c
Normal file
672
src/dsd_audio.c
Normal file
@@ -0,0 +1,672 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
pa_sample_spec ss;
|
||||
pa_sample_spec tt;
|
||||
pa_sample_spec zz;
|
||||
pa_sample_spec cc;
|
||||
|
||||
void closePulseOutput (dsd_opts * opts)
|
||||
{
|
||||
pa_simple_free (opts->pulse_digi_dev_out);
|
||||
if (opts->dmr_stereo == 1)
|
||||
{
|
||||
pa_simple_free (opts->pulse_digi_dev_outR);
|
||||
}
|
||||
}
|
||||
|
||||
void closePulseInput (dsd_opts * opts)
|
||||
{
|
||||
pa_simple_free (opts->pulse_digi_dev_in);
|
||||
}
|
||||
|
||||
void openPulseOutput(dsd_opts * opts)
|
||||
{
|
||||
|
||||
ss.format = PA_SAMPLE_S16NE;
|
||||
ss.channels = opts->pulse_raw_out_channels; //doing tests with 2 channels at 22050 for 44100 audio default in pulse
|
||||
ss.rate = opts->pulse_raw_rate_out; //48000
|
||||
|
||||
tt.format = PA_SAMPLE_S16NE;
|
||||
tt.channels = opts->pulse_digi_out_channels; //doing tests with 2 channels at 22050 for 44100 audio default in pulse
|
||||
tt.rate = opts->pulse_digi_rate_out; //48000, switches to 8000 when using RTL dongle
|
||||
|
||||
if (opts->monitor_input_audio == 1)
|
||||
{
|
||||
opts->pulse_raw_dev_out = pa_simple_new(NULL, "DSD-FME3", PA_STREAM_PLAYBACK, NULL, "Raw Audio Out", &ss, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
//tt
|
||||
pa_channel_map* left = 0; //NULL and 0 are same in this context
|
||||
pa_channel_map* right = 0; //NULL and 0 are same in this context
|
||||
|
||||
if (opts->dmr_stereo == 0)
|
||||
{
|
||||
opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME", PA_STREAM_PLAYBACK, NULL, opts->output_name, &tt, left, NULL, NULL);
|
||||
|
||||
}
|
||||
|
||||
if (opts->dmr_stereo == 1)
|
||||
{
|
||||
opts->pulse_digi_dev_out = pa_simple_new(NULL, "DSD-FME1", PA_STREAM_PLAYBACK, NULL, "XDMA SLOT 1", &tt, left, NULL, NULL);
|
||||
opts->pulse_digi_dev_outR = pa_simple_new(NULL, "DSD-FME2", PA_STREAM_PLAYBACK, NULL, "XDMA SLOT 2", &tt, right, NULL, NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void openPulseInput(dsd_opts * opts)
|
||||
{
|
||||
|
||||
cc.format = PA_SAMPLE_S16NE;
|
||||
cc.channels = opts->pulse_digi_in_channels;
|
||||
cc.rate = opts->pulse_digi_rate_in; //48000
|
||||
|
||||
opts->pulse_digi_dev_in = pa_simple_new(NULL, "DSD-FME", PA_STREAM_RECORD, NULL, opts->output_name, &cc, NULL, NULL, NULL);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
processAudio (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int i, n;
|
||||
float aout_abs, max, gainfactor, gaindelta, maxbuf;
|
||||
|
||||
if (opts->audio_gain == (float) 0)
|
||||
{
|
||||
// detect max level
|
||||
max = 0;
|
||||
|
||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
aout_abs = fabsf (*state->audio_out_temp_buf_p);
|
||||
if (aout_abs > max)
|
||||
{
|
||||
max = aout_abs;
|
||||
}
|
||||
state->audio_out_temp_buf_p++;
|
||||
}
|
||||
*state->aout_max_buf_p = max;
|
||||
|
||||
state->aout_max_buf_p++;
|
||||
|
||||
state->aout_max_buf_idx++;
|
||||
|
||||
if (state->aout_max_buf_idx > 24)
|
||||
{
|
||||
state->aout_max_buf_idx = 0;
|
||||
state->aout_max_buf_p = state->aout_max_buf;
|
||||
}
|
||||
|
||||
// lookup max history
|
||||
for (i = 0; i < 25; i++)
|
||||
{
|
||||
maxbuf = state->aout_max_buf[i];
|
||||
if (maxbuf > max)
|
||||
{
|
||||
max = maxbuf;
|
||||
}
|
||||
}
|
||||
|
||||
// determine optimal gain level
|
||||
if (max > (float) 0)
|
||||
{
|
||||
gainfactor = ((float) 30000 / max);
|
||||
}
|
||||
else
|
||||
{
|
||||
gainfactor = (float) 50;
|
||||
}
|
||||
if (gainfactor < state->aout_gain)
|
||||
{
|
||||
state->aout_gain = gainfactor;
|
||||
gaindelta = (float) 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gainfactor > (float) 50)
|
||||
{
|
||||
gainfactor = (float) 50;
|
||||
}
|
||||
gaindelta = gainfactor - state->aout_gain;
|
||||
if (gaindelta > ((float) 0.05 * state->aout_gain))
|
||||
{
|
||||
gaindelta = ((float) 0.05 * state->aout_gain);
|
||||
}
|
||||
}
|
||||
gaindelta /= (float) 160;
|
||||
}
|
||||
else
|
||||
{
|
||||
gaindelta = (float) 0;
|
||||
}
|
||||
|
||||
if(opts->audio_gain >= 0)
|
||||
{
|
||||
// adjust output gain
|
||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
*state->audio_out_temp_buf_p = (state->aout_gain + ((float) n * gaindelta)) * (*state->audio_out_temp_buf_p);
|
||||
state->audio_out_temp_buf_p++;
|
||||
}
|
||||
state->aout_gain += ((float) 160 * gaindelta);
|
||||
}
|
||||
|
||||
// copy audio data to output buffer and upsample if necessary
|
||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||
//we only want to upsample when using sample rates greater than 8k for output,
|
||||
//hard set to 8k for RTL mono and MBE playback, otherwise crackling may occur.
|
||||
if (opts->pulse_digi_rate_out > 8000)
|
||||
{
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
upsample (state, *state->audio_out_temp_buf_p);
|
||||
state->audio_out_temp_buf_p++;
|
||||
state->audio_out_float_buf_p += 6;
|
||||
state->audio_out_idx += 6;
|
||||
state->audio_out_idx2 += 6;
|
||||
}
|
||||
state->audio_out_float_buf_p -= (960 + opts->playoffset);
|
||||
// copy to output (short) buffer
|
||||
for (n = 0; n < 960; n++)
|
||||
{
|
||||
if (*state->audio_out_float_buf_p > 32767.0F)
|
||||
{
|
||||
*state->audio_out_float_buf_p = 32767.0F;
|
||||
}
|
||||
else if (*state->audio_out_float_buf_p < -32768.0F)
|
||||
{
|
||||
*state->audio_out_float_buf_p = -32768.0F;
|
||||
}
|
||||
*state->audio_out_buf_p = (short) *state->audio_out_float_buf_p;
|
||||
state->audio_out_buf_p++;
|
||||
state->audio_out_float_buf_p++;
|
||||
}
|
||||
state->audio_out_float_buf_p += opts->playoffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
if (*state->audio_out_temp_buf_p > 32767.0F)
|
||||
{
|
||||
*state->audio_out_temp_buf_p = 32767.0F;
|
||||
}
|
||||
else if (*state->audio_out_temp_buf_p < -32768.0F)
|
||||
{
|
||||
*state->audio_out_temp_buf_p = -32768.0F;
|
||||
}
|
||||
*state->audio_out_buf_p = (short) *state->audio_out_temp_buf_p;
|
||||
state->audio_out_buf_p++;
|
||||
state->audio_out_temp_buf_p++;
|
||||
state->audio_out_idx++;
|
||||
state->audio_out_idx2++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
processAudioR (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int i, n;
|
||||
float aout_abs, max, gainfactor, gaindelta, maxbuf;
|
||||
if (opts->audio_gainR == (float) 0)
|
||||
{
|
||||
// detect max level
|
||||
max = 0;
|
||||
|
||||
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
aout_abs = fabsf (*state->audio_out_temp_buf_pR);
|
||||
if (aout_abs > max)
|
||||
{
|
||||
max = aout_abs;
|
||||
}
|
||||
state->audio_out_temp_buf_pR++;
|
||||
}
|
||||
*state->aout_max_buf_pR = max;
|
||||
|
||||
state->aout_max_buf_pR++;
|
||||
|
||||
state->aout_max_buf_idxR++;
|
||||
|
||||
if (state->aout_max_buf_idxR > 24)
|
||||
{
|
||||
state->aout_max_buf_idxR = 0;
|
||||
state->aout_max_buf_pR = state->aout_max_bufR;
|
||||
}
|
||||
|
||||
// lookup max history
|
||||
for (i = 0; i < 25; i++)
|
||||
{
|
||||
maxbuf = state->aout_max_bufR[i];
|
||||
if (maxbuf > max)
|
||||
{
|
||||
max = maxbuf;
|
||||
}
|
||||
}
|
||||
|
||||
// determine optimal gain level
|
||||
if (max > (float) 0)
|
||||
{
|
||||
gainfactor = ((float) 30000 / max);
|
||||
}
|
||||
else
|
||||
{
|
||||
gainfactor = (float) 50;
|
||||
}
|
||||
if (gainfactor < state->aout_gainR)
|
||||
{
|
||||
state->aout_gainR = gainfactor;
|
||||
gaindelta = (float) 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gainfactor > (float) 50)
|
||||
{
|
||||
gainfactor = (float) 50;
|
||||
}
|
||||
gaindelta = gainfactor - state->aout_gainR;
|
||||
if (gaindelta > ((float) 0.05 * state->aout_gainR))
|
||||
{
|
||||
gaindelta = ((float) 0.05 * state->aout_gainR);
|
||||
}
|
||||
}
|
||||
gaindelta /= (float) 160;
|
||||
}
|
||||
else
|
||||
{
|
||||
gaindelta = (float) 0;
|
||||
}
|
||||
|
||||
if(opts->audio_gainR >= 0)
|
||||
{
|
||||
// adjust output gain
|
||||
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
*state->audio_out_temp_buf_pR = (state->aout_gainR + ((float) n * gaindelta)) * (*state->audio_out_temp_buf_pR);
|
||||
state->audio_out_temp_buf_pR++;
|
||||
}
|
||||
state->aout_gainR += ((float) 160 * gaindelta);
|
||||
}
|
||||
|
||||
// copy audio data to output buffer and upsample if necessary
|
||||
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
|
||||
//we only want to upsample when using sample rates greater than 8k for output,
|
||||
//hard set to 8k for RTL mono and MBE playback, otherwise crackling may occur.
|
||||
if (opts->pulse_digi_rate_out > 8000)
|
||||
{
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
upsample (state, *state->audio_out_temp_buf_pR);
|
||||
state->audio_out_temp_buf_pR++;
|
||||
state->audio_out_float_buf_pR += 6;
|
||||
state->audio_out_idxR += 6;
|
||||
state->audio_out_idx2R += 6;
|
||||
}
|
||||
state->audio_out_float_buf_pR -= (960 + opts->playoffsetR);
|
||||
// copy to output (short) buffer
|
||||
for (n = 0; n < 960; n++)
|
||||
{
|
||||
if (*state->audio_out_float_buf_pR > 32767.0F)
|
||||
{
|
||||
*state->audio_out_float_buf_pR = 32767.0F;
|
||||
}
|
||||
else if (*state->audio_out_float_buf_pR < -32768.0F)
|
||||
{
|
||||
*state->audio_out_float_buf_pR = -32768.0F;
|
||||
}
|
||||
*state->audio_out_buf_pR = (short) *state->audio_out_float_buf_pR;
|
||||
state->audio_out_buf_pR++;
|
||||
state->audio_out_float_buf_pR++;
|
||||
}
|
||||
state->audio_out_float_buf_pR += opts->playoffsetR;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
if (*state->audio_out_temp_buf_pR > 32767.0F)
|
||||
{
|
||||
*state->audio_out_temp_buf_pR = 32767.0F;
|
||||
}
|
||||
else if (*state->audio_out_temp_buf_pR < -32768.0F)
|
||||
{
|
||||
*state->audio_out_temp_buf_pR = -32768.0F;
|
||||
}
|
||||
*state->audio_out_buf_pR = (short) *state->audio_out_temp_buf_pR;
|
||||
state->audio_out_buf_pR++;
|
||||
state->audio_out_temp_buf_pR++;
|
||||
state->audio_out_idxR++;
|
||||
state->audio_out_idx2R++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void writeSynthesizedVoice (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
int n;
|
||||
short aout_buf[160];
|
||||
short *aout_buf_p;
|
||||
|
||||
aout_buf_p = aout_buf;
|
||||
state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
if (*state->audio_out_temp_buf_p > (float) 32767)
|
||||
{
|
||||
*state->audio_out_temp_buf_p = (float) 32767;
|
||||
}
|
||||
else if (*state->audio_out_temp_buf_p < (float) -32768)
|
||||
{
|
||||
*state->audio_out_temp_buf_p = (float) -32768;
|
||||
}
|
||||
*aout_buf_p = (short) *state->audio_out_temp_buf_p;
|
||||
aout_buf_p++;
|
||||
state->audio_out_temp_buf_p++;
|
||||
}
|
||||
|
||||
sf_write_short(opts->wav_out_f, aout_buf, 160);
|
||||
|
||||
}
|
||||
|
||||
void writeSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
int n;
|
||||
short aout_buf[160];
|
||||
short *aout_buf_p;
|
||||
|
||||
aout_buf_p = aout_buf;
|
||||
state->audio_out_temp_buf_pR = state->audio_out_temp_bufR;
|
||||
|
||||
for (n = 0; n < 160; n++)
|
||||
{
|
||||
if (*state->audio_out_temp_buf_pR > (float) 32767)
|
||||
{
|
||||
*state->audio_out_temp_buf_pR = (float) 32767;
|
||||
}
|
||||
else if (*state->audio_out_temp_buf_pR < (float) -32768)
|
||||
{
|
||||
*state->audio_out_temp_buf_pR = (float) -32768;
|
||||
}
|
||||
*aout_buf_p = (short) *state->audio_out_temp_buf_pR;
|
||||
aout_buf_p++;
|
||||
state->audio_out_temp_buf_pR++;
|
||||
}
|
||||
|
||||
sf_write_short(opts->wav_out_fR, aout_buf, 160);
|
||||
|
||||
}
|
||||
|
||||
void writeRawSample (dsd_opts * opts, dsd_state * state, short sample)
|
||||
{
|
||||
int n;
|
||||
short aout_buf[160];
|
||||
short *aout_buf_p;
|
||||
|
||||
//sf_write_short(opts->wav_out_raw, aout_buf, 160);
|
||||
|
||||
//only write if actual audio, truncate silence
|
||||
if (sample != 0)
|
||||
{
|
||||
sf_write_short(opts->wav_out_raw, &sample, 2); //2 to match pulseaudio input sample read
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
playSynthesizedVoice (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
ssize_t result;
|
||||
|
||||
if (state->audio_out_idx > opts->delay)
|
||||
{
|
||||
if (opts->audio_out_type == 5) //OSS
|
||||
{
|
||||
//OSS
|
||||
result = write (opts->audio_out_fd, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2));
|
||||
state->audio_out_idx = 0;
|
||||
}
|
||||
else if (opts->audio_out_type == 0)
|
||||
{
|
||||
pa_simple_write(opts->pulse_digi_dev_out, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2), NULL);
|
||||
state->audio_out_idx = 0;
|
||||
}
|
||||
else state->audio_out_idx = 0; //failsafe for audio_out == 0
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (state->audio_out_idx2 >= 800000)
|
||||
{
|
||||
state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
|
||||
state->audio_out_buf_p = state->audio_out_buf + 100;
|
||||
memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
|
||||
memset (state->audio_out_buf, 0, 100 * sizeof (short));
|
||||
state->audio_out_idx2 = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
playSynthesizedVoiceR (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
ssize_t result;
|
||||
|
||||
if (state->audio_out_idxR > opts->delay)
|
||||
{
|
||||
// output synthesized speech to sound card
|
||||
if (opts->audio_out_type == 5) //OSS
|
||||
{
|
||||
//OSS
|
||||
result = write (opts->audio_out_fdR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2));
|
||||
state->audio_out_idxR = 0;
|
||||
}
|
||||
else if (opts->audio_out_type == 0)
|
||||
{
|
||||
pa_simple_write(opts->pulse_digi_dev_outR, (state->audio_out_buf_pR - state->audio_out_idxR), (state->audio_out_idxR * 2), NULL);
|
||||
state->audio_out_idxR = 0;
|
||||
}
|
||||
else state->audio_out_idxR = 0; //failsafe for audio_out == 0
|
||||
|
||||
}
|
||||
|
||||
if (state->audio_out_idx2R >= 800000)
|
||||
{
|
||||
state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100;
|
||||
state->audio_out_buf_pR = state->audio_out_bufR + 100;
|
||||
memset (state->audio_out_float_bufR, 0, 100 * sizeof (float));
|
||||
memset (state->audio_out_bufR, 0, 100 * sizeof (short));
|
||||
state->audio_out_idx2R = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
openAudioOutDevice (dsd_opts * opts, int speed)
|
||||
{
|
||||
//converted to handle any calls to use portaudio
|
||||
if(strncmp(opts->audio_out_dev, "pa:", 3) == 0)
|
||||
{
|
||||
opts->audio_out_type = 0;
|
||||
fprintf (stderr,"Error, Port Audio is not supported by FME!\n");
|
||||
fprintf (stderr,"Using Pulse Audio Output Stream Instead! \n");
|
||||
sprintf (opts->audio_out_dev, "pulse");
|
||||
}
|
||||
if(strncmp(opts->audio_in_dev, "pulse", 5) == 0)
|
||||
{
|
||||
opts->audio_in_type = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat stat_buf;
|
||||
// if(stat(opts->audio_out_dev, &stat_buf) != 0 && strncmp(opts->audio_out_dev, "pulse", 5 != 0)) //HERE
|
||||
// {
|
||||
// fprintf (stderr,"Error, couldn't open %s\n", opts->audio_out_dev);
|
||||
// exit(1);
|
||||
// }
|
||||
|
||||
// if( (!(S_ISCHR(stat_buf.st_mode) || S_ISBLK(stat_buf.st_mode))) && strncmp(opts->audio_out_dev, "pulse", 5 != 0))
|
||||
// {
|
||||
// // this is not a device
|
||||
// fprintf (stderr,"Error, %s is not a device. use -w filename for wav output.\n", opts->audio_out_dev);
|
||||
// exit(1);
|
||||
// }
|
||||
}
|
||||
fprintf (stderr,"Audio Out Device: %s\n", opts->audio_out_dev);
|
||||
}
|
||||
|
||||
void
|
||||
openAudioInDevice (dsd_opts * opts)
|
||||
{
|
||||
char * extension;
|
||||
const char ch = '.';
|
||||
extension = strrchr(opts->audio_in_dev, ch); //return extension if this is a .wav or .bin file
|
||||
|
||||
// get info of device/file
|
||||
if (strncmp(opts->audio_in_dev, "-", 1) == 0)
|
||||
{
|
||||
opts->audio_in_type = 1;
|
||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||
opts->audio_in_file_info->channels=1;
|
||||
opts->audio_in_file_info->seekable=0;
|
||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||
opts->audio_in_file = sf_open_fd(fileno(stdin), SFM_READ, opts->audio_in_file_info, 0);
|
||||
|
||||
if(opts->audio_in_file == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error, couldn't open stdin with libsndfile: %s\n", sf_strerror(NULL));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
else if (strncmp(opts->audio_in_dev, "tcp", 3) == 0)
|
||||
{
|
||||
opts->audio_in_type = 8;
|
||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||
opts->audio_in_file_info->channels=1;
|
||||
opts->audio_in_file_info->seekable=0;
|
||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||
opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||
if(opts->tcp_file_in == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error, couldn't open TCP with libsndfile: %s\n", sf_strerror(NULL));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
else if (strncmp(opts->audio_in_dev, "udp", 3) == 0)
|
||||
{
|
||||
opts->audio_in_type = 6;
|
||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||
opts->audio_in_file_info->channels=1;
|
||||
opts->audio_in_file_info->seekable=0;
|
||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||
opts->udp_file_in = sf_open_fd(opts->udp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
|
||||
|
||||
if(opts->udp_file_in == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error, couldn't open UDP with libsndfile: %s\n", sf_strerror(NULL));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0)
|
||||
{
|
||||
opts->audio_in_type = 3;
|
||||
}
|
||||
else if(strncmp(opts->audio_in_dev, "pulse", 5) == 0)
|
||||
{
|
||||
opts->audio_in_type = 0;
|
||||
}
|
||||
else if((strncmp(opts->audio_in_dev, "/dev/dsp", 8) == 0))
|
||||
{
|
||||
opts->audio_in_type = 5;
|
||||
}
|
||||
else if (strncmp(extension, ".bin", 3) == 0)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
if (stat(opts->audio_in_dev, &stat_buf) != 0)
|
||||
{
|
||||
fprintf (stderr,"Error, couldn't open bin file %s\n", opts->audio_in_dev);
|
||||
exit(1);
|
||||
}
|
||||
if (S_ISREG(stat_buf.st_mode))
|
||||
{
|
||||
opts->symbolfile = fopen(opts->audio_in_dev, "r");
|
||||
opts->audio_in_type = 4; //symbol capture bin files
|
||||
}
|
||||
else
|
||||
{
|
||||
opts->audio_in_type = 0;
|
||||
}
|
||||
}
|
||||
//open as wav file as last resort, wav files subseptible to sample rate issues if not 48000
|
||||
else
|
||||
{
|
||||
struct stat stat_buf;
|
||||
if (stat(opts->audio_in_dev, &stat_buf) != 0)
|
||||
{
|
||||
fprintf (stderr,"Error, couldn't open wav file %s\n", opts->audio_in_dev);
|
||||
exit(1);
|
||||
}
|
||||
if (S_ISREG(stat_buf.st_mode))
|
||||
{
|
||||
opts->audio_in_type = 2; //two now, seperating STDIN and wav files
|
||||
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
|
||||
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
|
||||
opts->audio_in_file_info->channels=1;
|
||||
opts->audio_in_file_info->channels = 1;
|
||||
opts->audio_in_file_info->seekable=0;
|
||||
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
|
||||
//
|
||||
opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info);
|
||||
|
||||
if(opts->audio_in_file == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error, couldn't open wav file %s\n", opts->audio_in_dev);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
//open pulse audio if no bin or wav
|
||||
else //seems this condition is never met
|
||||
{
|
||||
//opts->audio_in_type = 5; //not sure if this works or needs to openPulse here
|
||||
fprintf(stderr, "Error, couldn't open input file.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (opts->split == 1)
|
||||
{
|
||||
fprintf (stderr,"Audio In Device: %s\n", opts->audio_in_dev);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr,"Audio In/Out Device: %s\n", opts->audio_in_dev);
|
||||
}
|
||||
}
|
||||
492
src/dsd_dibit.c
Normal file
492
src/dsd_dibit.c
Normal file
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
#include "p25p1_heuristics.h"
|
||||
|
||||
static void
|
||||
print_datascope(dsd_opts* opts, dsd_state* state, int* sbuf2)
|
||||
{
|
||||
int i, j, o;
|
||||
char modulation[8];
|
||||
int spectrum[64];
|
||||
|
||||
if (state->rf_mod == 0)
|
||||
{
|
||||
sprintf (modulation, "C4FM");
|
||||
}
|
||||
else if (state->rf_mod == 1)
|
||||
{
|
||||
sprintf (modulation, "QPSK");
|
||||
}
|
||||
else if (state->rf_mod == 2)
|
||||
{
|
||||
sprintf (modulation, "GFSK");
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
spectrum[i] = 0;
|
||||
}
|
||||
for (i = 0; i < opts->ssize; i++)
|
||||
{
|
||||
o = (sbuf2[i] + 32768) / 1024;
|
||||
spectrum[o]++;
|
||||
}
|
||||
if (state->symbolcnt > (4800 / opts->scoperate))
|
||||
{
|
||||
state->symbolcnt = 0;
|
||||
fprintf (stderr, "\n");
|
||||
fprintf (stderr, "Demod mode: %s Nac: %4X\n", modulation, state->nac);
|
||||
fprintf (stderr, "Frame Type: %s Talkgroup: %7i\n", state->ftype, state->lasttg);
|
||||
fprintf (stderr, "Frame Subtype: %s Source: %12i\n", state->fsubtype, state->lastsrc);
|
||||
fprintf (stderr, "TDMA activity: %s %s Voice errors: %s\n", state->slot0light, state->slot1light, state->err_str);
|
||||
fprintf (stderr, "+----------------------------------------------------------------+\n");
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
printf ("|");
|
||||
for (j = 0; j < 64; j++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
if ((j == ((state->min) + 32768) / 1024) || (j == ((state->max) + 32768) / 1024))
|
||||
{
|
||||
fprintf (stderr, "#");
|
||||
}
|
||||
else if ((j == ((state->lmid) + 32768) / 1024) || (j == ((state->umid) + 32768) / 1024))
|
||||
{
|
||||
fprintf (stderr, "^");
|
||||
}
|
||||
else if (j == (state->center + 32768) / 1024)
|
||||
{
|
||||
fprintf (stderr, "!");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (j == 32)
|
||||
{
|
||||
fprintf (stderr, "|");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spectrum[j] > 9 - i)
|
||||
{
|
||||
fprintf (stderr, "*");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (j == 32)
|
||||
{
|
||||
fprintf (stderr, "|");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf (stderr, "|\n");
|
||||
}
|
||||
fprintf (stderr, "+----------------------------------------------------------------+\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
use_symbol (dsd_opts* opts, dsd_state* state, int symbol)
|
||||
{
|
||||
int i;
|
||||
int sbuf2[128];
|
||||
int lmin, lmax, lsum;
|
||||
|
||||
for (i = 0; i < opts->ssize; i++)
|
||||
{
|
||||
sbuf2[i] = state->sbuf[i];
|
||||
}
|
||||
|
||||
qsort (sbuf2, opts->ssize, sizeof (int), comp);
|
||||
|
||||
// continuous update of min/max in rf_mod=1 (QPSK) mode
|
||||
// in c4fm min/max must only be updated during sync
|
||||
if (state->rf_mod == 1)
|
||||
{
|
||||
lmin = (sbuf2[0] + sbuf2[1]) / 2;
|
||||
lmax = (sbuf2[(opts->ssize - 1)] + sbuf2[(opts->ssize - 2)]) / 2;
|
||||
state->minbuf[state->midx] = lmin;
|
||||
state->maxbuf[state->midx] = lmax;
|
||||
if (state->midx == (opts->msize - 1))
|
||||
{
|
||||
state->midx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->midx++;
|
||||
}
|
||||
lsum = 0;
|
||||
for (i = 0; i < opts->msize; i++)
|
||||
{
|
||||
lsum += state->minbuf[i];
|
||||
}
|
||||
state->min = lsum / opts->msize;
|
||||
lsum = 0;
|
||||
for (i = 0; i < opts->msize; i++)
|
||||
{
|
||||
lsum += state->maxbuf[i];
|
||||
}
|
||||
state->max = lsum / opts->msize;
|
||||
state->center = ((state->max) + (state->min)) / 2;
|
||||
state->umid = (((state->max) - state->center) * 5 / 8) + state->center;
|
||||
state->lmid = (((state->min) - state->center) * 5 / 8) + state->center;
|
||||
state->maxref = (int)((state->max) * 0.80F);
|
||||
state->minref = (int)((state->min) * 0.80F);
|
||||
}
|
||||
else
|
||||
{
|
||||
state->maxref = state->max;
|
||||
state->minref = state->min;
|
||||
}
|
||||
|
||||
// Increase sidx
|
||||
if (state->sidx >= (opts->ssize - 1))
|
||||
{
|
||||
state->sidx = 0;
|
||||
|
||||
if (opts->datascope == 1)
|
||||
{
|
||||
print_datascope(opts, state, sbuf2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state->sidx++;
|
||||
}
|
||||
|
||||
if (state->dibit_buf_p > state->dibit_buf + 900000)
|
||||
{
|
||||
state->dibit_buf_p = state->dibit_buf + 200;
|
||||
}
|
||||
|
||||
//dmr buffer
|
||||
if (state->dmr_payload_p > state->dmr_payload_buf + 900000)
|
||||
{
|
||||
state->dmr_payload_p = state->dmr_payload_buf + 200;
|
||||
}
|
||||
//dmr buffer end
|
||||
}
|
||||
|
||||
static int
|
||||
invert_dibit(int dibit)
|
||||
{
|
||||
switch (dibit)
|
||||
{
|
||||
case 0:
|
||||
return 2;
|
||||
case 1:
|
||||
return 3;
|
||||
case 2:
|
||||
return 0;
|
||||
case 3:
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Error, shouldn't be here
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
||||
//int digitize (dsd_opts* opts, dsd_state* state, int symbol)
|
||||
{
|
||||
// determine dibit state
|
||||
if ((state->synctype == 6) || (state->synctype == 14)|| (state->synctype == 18) || (state->synctype == 37))
|
||||
|
||||
{
|
||||
// 6 +D-STAR
|
||||
// 14 +ProVoice
|
||||
// 18 +D-STAR_HD
|
||||
// 37 +EDACS
|
||||
|
||||
if (symbol > state->center)
|
||||
{
|
||||
*state->dibit_buf_p = 1;
|
||||
state->dibit_buf_p++;
|
||||
return (0); // +1
|
||||
}
|
||||
else
|
||||
{
|
||||
*state->dibit_buf_p = 3;
|
||||
state->dibit_buf_p++;
|
||||
return (1); // +3
|
||||
}
|
||||
}
|
||||
else if ((state->synctype == 7) || (state->synctype == 15)|| (state->synctype == 19) || (state->synctype == 38))
|
||||
{
|
||||
// 7 -D-STAR
|
||||
// 15 -ProVoice
|
||||
// 19 -D-STAR_HD
|
||||
// 38 -EDACS
|
||||
|
||||
if (symbol > state->center)
|
||||
{
|
||||
*state->dibit_buf_p = 1;
|
||||
state->dibit_buf_p++;
|
||||
return (1); // +3
|
||||
}
|
||||
else
|
||||
{
|
||||
*state->dibit_buf_p = 3;
|
||||
state->dibit_buf_p++;
|
||||
return (0); // +1
|
||||
}
|
||||
}
|
||||
|
||||
else if ((state->synctype == 1) || (state->synctype == 3) || (state->synctype == 5) ||
|
||||
(state->synctype == 9) || (state->synctype == 11) || (state->synctype == 13) ||
|
||||
(state->synctype == 17) || (state->synctype == 29) || (state->synctype == 36) )
|
||||
|
||||
{
|
||||
// 1 -P25p1
|
||||
// 3 -X2-TDMA (inverted signal voice frame)
|
||||
// 5 -X2-TDMA (inverted signal data frame)
|
||||
// 9 -NXDN (inverted voice frame)
|
||||
// 11 -DMR (inverted signal voice frame)
|
||||
// 13 -DMR (inverted signal data frame)
|
||||
// 17 -NXDN (inverted data frame)
|
||||
// 29 -NXDN (inverted FSW)
|
||||
// 36 -P25p2
|
||||
|
||||
int valid;
|
||||
int dibit;
|
||||
|
||||
valid = 0;
|
||||
|
||||
//only run estimate_symbol when we aren't trunking, otherwise noise/gain levels on other rf channels may impact performance severely
|
||||
//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)
|
||||
{
|
||||
// Use the P25 heuristics if available
|
||||
//valid = estimate_symbol(state->rf_mod, &(state->inv_p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||
}
|
||||
|
||||
if (valid == 0)
|
||||
{
|
||||
// Revert to the original approach: choose the symbol according to the regions delimited
|
||||
// by center, umid and lmid
|
||||
if (symbol > state->center)
|
||||
{
|
||||
if (symbol > state->umid)
|
||||
{
|
||||
dibit = 3; // -3
|
||||
}
|
||||
else
|
||||
{
|
||||
dibit = 2; // -1
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (symbol < state->lmid)
|
||||
{
|
||||
dibit = 1; // +3
|
||||
}
|
||||
else
|
||||
{
|
||||
dibit = 0; // +1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state->last_dibit = dibit;
|
||||
|
||||
*state->dibit_buf_p = invert_dibit(dibit);
|
||||
state->dibit_buf_p++;
|
||||
|
||||
//dmr buffer
|
||||
*state->dmr_payload_p = invert_dibit(dibit);
|
||||
state->dmr_payload_p++;
|
||||
//dmr buffer end
|
||||
|
||||
return dibit;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 0 +P25p1
|
||||
// 2 +X2-TDMA (non inverted signal data frame)
|
||||
// 4 +X2-TDMA (non inverted signal voice frame)
|
||||
// 8 +NXDN (non inverted voice frame)
|
||||
// 10 +DMR (non inverted signal data frame)
|
||||
// 12 +DMR (non inverted signal voice frame)
|
||||
// 16 +NXDN (non inverted data frame)
|
||||
// 28 +NXND (FSW)
|
||||
// 35 +p25p2
|
||||
|
||||
int valid;
|
||||
int dibit;
|
||||
|
||||
valid = 0;
|
||||
|
||||
//only run estimate_symbol when we aren't trunking, otherwise noise/gain levels on other rf channels may impact performance severely
|
||||
//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_trunk == 0)
|
||||
{
|
||||
// Use the P25 heuristics if available
|
||||
//valid = estimate_symbol(state->rf_mod, &(state->p25_heuristics), state->last_dibit, symbol, &dibit);
|
||||
}
|
||||
|
||||
if (valid == 0)
|
||||
{
|
||||
// Revert to the original approach: choose the symbol according to the regions delimited
|
||||
// by center, umid and lmid
|
||||
if (symbol > state->center)
|
||||
{
|
||||
if (symbol > state->umid)
|
||||
{
|
||||
dibit = 1; // +3
|
||||
}
|
||||
else
|
||||
{
|
||||
dibit = 0; // +1
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (symbol < state->lmid)
|
||||
{
|
||||
dibit = 3; // -3
|
||||
}
|
||||
else
|
||||
{
|
||||
dibit = 2; // -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state->last_dibit = dibit;
|
||||
|
||||
*state->dibit_buf_p = dibit;
|
||||
state->dibit_buf_p++;
|
||||
|
||||
//dmr buffer
|
||||
//note to self, perceived bug with initial dibit buffer appears to be caused by
|
||||
//media player, when playing back from audacity, the initial few dmr frames are
|
||||
//decoded properly, need to investigate the root cause of what audacity is doing
|
||||
//vs other audio sources...perhaps just the audio level itself?
|
||||
*state->dmr_payload_p = dibit;
|
||||
state->dmr_payload_p++;
|
||||
//dmr buffer end
|
||||
|
||||
return dibit;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
get_dibit_and_analog_signal (dsd_opts* opts, dsd_state* state, int* out_analog_signal)
|
||||
{
|
||||
int symbol;
|
||||
int dibit;
|
||||
|
||||
#ifdef TRACE_DSD
|
||||
unsigned int l, r;
|
||||
#endif
|
||||
|
||||
state->numflips = 0;
|
||||
|
||||
#ifdef TRACE_DSD
|
||||
l = state->debug_sample_index;
|
||||
#endif
|
||||
|
||||
symbol = getSymbol (opts, state, 1);
|
||||
|
||||
#ifdef TRACE_DSD
|
||||
r = state->debug_sample_index;
|
||||
#endif
|
||||
|
||||
|
||||
state->sbuf[state->sidx] = symbol;
|
||||
|
||||
if (out_analog_signal != NULL)
|
||||
{
|
||||
*out_analog_signal = symbol;
|
||||
}
|
||||
|
||||
use_symbol (opts, state, symbol);
|
||||
|
||||
dibit = digitize (opts, state, symbol);
|
||||
|
||||
if (opts->audio_in_type == 4)
|
||||
{
|
||||
//assign dibit from last symbol/dibit read from capture bin
|
||||
dibit = state->symbolc;
|
||||
if (state->use_throttle == 1)
|
||||
{
|
||||
usleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
//symbol/dibit file capture/writing
|
||||
if (opts->symbol_out == 1)
|
||||
{
|
||||
//fprintf (stderr, "%d", dibit);
|
||||
fputc (dibit, opts->symbol_out_f);
|
||||
}
|
||||
|
||||
#ifdef TRACE_DSD
|
||||
{
|
||||
float left, right;
|
||||
if (state->debug_label_dibit_file == NULL) {
|
||||
state->debug_label_dibit_file = fopen ("pp_label_dibit.txt", "w");
|
||||
}
|
||||
left = l / 48000.0;
|
||||
right = r / 48000.0;
|
||||
fprintf(state->debug_label_dibit_file, "%f\t%f\t%i\n", left, right, dibit);
|
||||
}
|
||||
#endif
|
||||
|
||||
return dibit;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief This important method reads the last analog signal value (getSymbol call) and digitizes it.
|
||||
* Depending of the ongoing transmission it in converted into a bit (0/1) or a di-bit (00/01/10/11).
|
||||
*/
|
||||
int
|
||||
getDibit (dsd_opts* opts, dsd_state* state)
|
||||
{
|
||||
return get_dibit_and_analog_signal (opts, state, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
skipDibit (dsd_opts * opts, dsd_state * state, int count)
|
||||
{
|
||||
|
||||
short sample;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (count); i++)
|
||||
{
|
||||
sample = getDibit (opts, state);
|
||||
}
|
||||
}
|
||||
524
src/dsd_file.c
Normal file
524
src/dsd_file.c
Normal file
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
time_t nowF;
|
||||
char * getTimeF(void) //get pretty hh:mm:ss timestamp
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
|
||||
char * curr;
|
||||
char * stamp = asctime(localtime( & t));
|
||||
|
||||
curr = strtok(stamp, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
curr = strtok(NULL, " ");
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
char * getDateF(void) {
|
||||
char datename[120]; //bug in 32-bit Ubuntu when using date in filename, date is garbage text
|
||||
char * curr2;
|
||||
struct tm * to;
|
||||
time_t t;
|
||||
t = time(NULL);
|
||||
to = localtime( & t);
|
||||
strftime(datename, sizeof(datename), "%Y-%m-%d", to);
|
||||
curr2 = strtok(datename, " ");
|
||||
return curr2;
|
||||
}
|
||||
|
||||
void
|
||||
saveImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
||||
{
|
||||
int i, j, k;
|
||||
unsigned char b;
|
||||
unsigned char err;
|
||||
|
||||
err = (unsigned char) state->errs2;
|
||||
fputc (err, opts->mbe_out_f);
|
||||
|
||||
k = 0;
|
||||
if (opts->payload == 1) //make opt variable later on to toggle this
|
||||
{
|
||||
//fprintf(stderr, "\n IMBE ");
|
||||
}
|
||||
|
||||
for (i = 0; i < 11; i++)
|
||||
{
|
||||
b = 0;
|
||||
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
b = b << 1;
|
||||
b = b + imbe_d[k];
|
||||
k++;
|
||||
}
|
||||
if (opts->payload == 1) //make opt variable later on to toggle this
|
||||
{
|
||||
//fprintf (stderr, "%02X", b);
|
||||
}
|
||||
fputc (b, opts->mbe_out_f);
|
||||
}
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
//fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
||||
}
|
||||
fflush (opts->mbe_out_f);
|
||||
}
|
||||
|
||||
void
|
||||
saveAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||
{
|
||||
int i, j, k;
|
||||
unsigned char b;
|
||||
unsigned char err;
|
||||
|
||||
err = (unsigned char) state->errs2;
|
||||
fputc (err, opts->mbe_out_f);
|
||||
|
||||
k = 0;
|
||||
if (opts->payload == 1) //make opt variable later on to toggle this
|
||||
{
|
||||
//fprintf(stderr, "\n AMBE ");
|
||||
}
|
||||
//for (i = 0; i < 6; i++)
|
||||
for (i = 0; i < 6; i++) //using 7 seems to break older amb files where it was 6, need to test 7 on 7 some more
|
||||
{
|
||||
b = 0;
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
b = b << 1;
|
||||
b = b + ambe_d[k];
|
||||
k++;
|
||||
}
|
||||
if (opts->payload == 1 && i < 6) //make opt variable later on to toggle this
|
||||
{
|
||||
//fprintf (stderr, "%02X", b);
|
||||
}
|
||||
if (opts->payload == 1 && i == 6) //7th octet should only contain 1 bit? value will be either 0x00 or 0x80?
|
||||
{
|
||||
//fprintf (stderr, "%02X", b & 0x80); //7th octet should only contain 1 bit?
|
||||
}
|
||||
|
||||
fputc (b, opts->mbe_out_f);
|
||||
}
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
//fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
||||
}
|
||||
b = ambe_d[48];
|
||||
fputc (b, opts->mbe_out_f);
|
||||
fflush (opts->mbe_out_f);
|
||||
}
|
||||
|
||||
void
|
||||
PrintIMBEData (dsd_opts * opts, dsd_state * state, char *imbe_d) //for P25P1 and ProVoice
|
||||
{
|
||||
int i, j, k;
|
||||
unsigned char b;
|
||||
unsigned char err;
|
||||
|
||||
err = (unsigned char) state->errs2;
|
||||
k = 0;
|
||||
if (opts->payload == 1) //print IMBE info
|
||||
{
|
||||
fprintf(stderr, "\n IMBE ");
|
||||
}
|
||||
for (i = 0; i < 11; i++)
|
||||
{
|
||||
b = 0;
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
b = b << 1;
|
||||
b = b + imbe_d[k];
|
||||
k++;
|
||||
}
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "%02X", b);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
||||
}
|
||||
|
||||
//fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
PrintAMBEData (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||
{
|
||||
int i, j, k;
|
||||
unsigned char b;
|
||||
unsigned char err;
|
||||
|
||||
err = (unsigned char) state->errs2;
|
||||
k = 0;
|
||||
if (opts->dmr_stereo == 0 && opts->dmr_mono == 0)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
if (opts->payload == 1) //print AMBE info from DMR Stereo method
|
||||
{
|
||||
fprintf(stderr, " AMBE ");
|
||||
}
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
b = 0;
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
b = b << 1;
|
||||
b = b + ambe_d[k];
|
||||
k++;
|
||||
}
|
||||
if (opts->payload == 1 && i < 6)
|
||||
{
|
||||
fprintf (stderr, "%02X", b);
|
||||
}
|
||||
if (opts->payload == 1 && i == 6)
|
||||
{
|
||||
fprintf (stderr, "%02X", b & 0x80);
|
||||
}
|
||||
}
|
||||
if (state->currentslot == 0)
|
||||
{
|
||||
fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
||||
}
|
||||
else fprintf(stderr, " err = [%X] [%X] ", state->errsR, state->errs2R);
|
||||
b = ambe_d[48];
|
||||
if (opts->dmr_stereo == 1 || opts->dmr_mono == 1) //need to fix the printouts again
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
readImbe4400Data (dsd_opts * opts, dsd_state * state, char *imbe_d)
|
||||
{
|
||||
|
||||
int i, j, k;
|
||||
unsigned char b, x;
|
||||
|
||||
state->errs2 = fgetc (opts->mbe_in_f);
|
||||
state->errs = state->errs2;
|
||||
|
||||
|
||||
k = 0;
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
for (i = 0; i < 11; i++)
|
||||
{
|
||||
b = fgetc (opts->mbe_in_f);
|
||||
if (feof (opts->mbe_in_f))
|
||||
{
|
||||
return (1);
|
||||
}
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
imbe_d[k] = (b & 128) >> 7;
|
||||
|
||||
x = x << 1;
|
||||
x |= ((b & 0x80) >> 7);
|
||||
|
||||
b = b << 1;
|
||||
b = b & 255;
|
||||
k++;
|
||||
}
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf (stderr, "%02X", x);
|
||||
}
|
||||
|
||||
}
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2); //not sure that errs here are legit values
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
readAmbe2450Data (dsd_opts * opts, dsd_state * state, char *ambe_d)
|
||||
{
|
||||
|
||||
int i, j, k;
|
||||
unsigned char b, x;
|
||||
|
||||
state->errs2 = fgetc (opts->mbe_in_f);
|
||||
state->errs = state->errs2;
|
||||
|
||||
k = 0;
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < 6; i++) //breaks backwards compatablilty with 6 files
|
||||
{
|
||||
b = fgetc (opts->mbe_in_f);
|
||||
if (feof (opts->mbe_in_f))
|
||||
{
|
||||
return (1);
|
||||
}
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
ambe_d[k] = (b & 128) >> 7;
|
||||
|
||||
x = x << 1;
|
||||
x |= ((b & 0x80) >> 7);
|
||||
|
||||
b = b << 1;
|
||||
b = b & 255;
|
||||
k++;
|
||||
}
|
||||
if (opts->payload == 1 && i < 6)
|
||||
{
|
||||
fprintf (stderr, "%02X", x);
|
||||
}
|
||||
if (opts->payload == 1 && i == 6)
|
||||
{
|
||||
fprintf (stderr, "%02X", x & 0x80);
|
||||
}
|
||||
}
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
fprintf(stderr, " err = [%X] [%X] ", state->errs, state->errs2);
|
||||
}
|
||||
b = fgetc (opts->mbe_in_f);
|
||||
ambe_d[48] = (b & 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
openMbeInFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
char cookie[5];
|
||||
|
||||
opts->mbe_in_f = fopen (opts->mbe_in_file, "ro");
|
||||
if (opts->mbe_in_f == NULL)
|
||||
{
|
||||
fprintf (stderr,"Error: could not open %s\n", opts->mbe_in_file);
|
||||
}
|
||||
|
||||
// read cookie
|
||||
cookie[0] = fgetc (opts->mbe_in_f);
|
||||
cookie[1] = fgetc (opts->mbe_in_f);
|
||||
cookie[2] = fgetc (opts->mbe_in_f);
|
||||
cookie[3] = fgetc (opts->mbe_in_f);
|
||||
cookie[4] = 0;
|
||||
if (strstr (cookie, ".amb") != NULL)
|
||||
{
|
||||
state->mbe_file_type = 1;
|
||||
}
|
||||
else if (strstr (cookie, ".imb") != NULL)
|
||||
{
|
||||
state->mbe_file_type = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->mbe_file_type = -1;
|
||||
fprintf (stderr,"Error - unrecognized file type\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//much simpler version
|
||||
void closeMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
if (opts->mbe_out == 1)
|
||||
{
|
||||
if (opts->mbe_out_f != NULL)
|
||||
{
|
||||
fflush (opts->mbe_out_f);
|
||||
fclose (opts->mbe_out_f);
|
||||
opts->mbe_out_f = NULL;
|
||||
opts->mbe_out = 0;
|
||||
fprintf (stderr, "\nClosing MBE out file.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
openMbeOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int i, j;
|
||||
char ext[5];
|
||||
|
||||
if ((state->synctype == 0) || (state->synctype == 1) || (state->synctype == 14) || (state->synctype == 15))
|
||||
{
|
||||
sprintf (ext, ".imb");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (ext, ".amb");
|
||||
}
|
||||
|
||||
// reset talkgroup id buffer
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
for (j = 0; j < 25; j++)
|
||||
{
|
||||
state->tg[j][i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
state->tgcount = 0;
|
||||
|
||||
sprintf (opts->mbe_out_file, "%s %s%s", getDateF(), getTimeF(), ext);
|
||||
|
||||
sprintf (opts->mbe_out_path, "%s%s", opts->mbe_out_dir, opts->mbe_out_file);
|
||||
|
||||
opts->mbe_out_f = fopen (opts->mbe_out_path, "w");
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
fprintf (stderr,"Error, couldn't open %s\n", opts->mbe_out_path);
|
||||
}
|
||||
else opts->mbe_out = 1;
|
||||
|
||||
//
|
||||
fprintf (opts->mbe_out_f, "%s", ext);
|
||||
|
||||
fflush (opts->mbe_out_f);
|
||||
}
|
||||
|
||||
void
|
||||
openWavOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
SF_INFO info;
|
||||
info.samplerate = 8000; //8000
|
||||
info.channels = 1;
|
||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
||||
|
||||
if (opts->wav_out_f == NULL)
|
||||
{
|
||||
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void openWavOutFileL (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
SF_INFO info;
|
||||
info.samplerate = 8000;
|
||||
info.channels = 1;
|
||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||
opts->wav_out_f = sf_open (opts->wav_out_file, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
||||
|
||||
if (opts->wav_out_f == NULL)
|
||||
{
|
||||
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void openWavOutFileR (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
SF_INFO info;
|
||||
info.samplerate = 8000; //8000
|
||||
info.channels = 1;
|
||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||
opts->wav_out_fR = sf_open (opts->wav_out_fileR, SFM_RDWR, &info); //RDWR will append to file instead of overwrite file
|
||||
|
||||
if (opts->wav_out_f == NULL)
|
||||
{
|
||||
fprintf (stderr,"Error - could not open wav output file %s\n", opts->wav_out_fileR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void openWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
SF_INFO info;
|
||||
info.samplerate = 48000; //8000
|
||||
info.channels = 1;
|
||||
info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||
opts->wav_out_raw = sf_open (opts->wav_out_file_raw, SFM_RDWR, &info);
|
||||
if (opts->wav_out_raw == NULL)
|
||||
{
|
||||
fprintf (stderr,"Error - could not open raw wav output file %s\n", opts->wav_out_file_raw);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void closeWavOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
sf_close(opts->wav_out_f);
|
||||
}
|
||||
|
||||
void closeWavOutFileL (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
sf_close(opts->wav_out_f);
|
||||
}
|
||||
|
||||
void closeWavOutFileR (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
sf_close(opts->wav_out_fR);
|
||||
}
|
||||
|
||||
void closeWavOutFileRaw (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
sf_close(opts->wav_out_raw);
|
||||
}
|
||||
|
||||
void openSymbolOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
//Do something
|
||||
opts->symbol_out_f = fopen (opts->symbol_out_file, "w");
|
||||
}
|
||||
|
||||
void writeSymbolOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
//Do something
|
||||
|
||||
}
|
||||
|
||||
void closeSymbolOutFile (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
//Do something
|
||||
if (opts->symbol_out == 1)
|
||||
{
|
||||
if (opts->symbol_out_file != NULL)
|
||||
{
|
||||
fclose(opts->symbol_out_f);
|
||||
}
|
||||
|
||||
opts->symbol_out = 0; //set flag to 1
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
206
src/dsd_filters.c
Normal file
206
src/dsd_filters.c
Normal file
@@ -0,0 +1,206 @@
|
||||
// Original DMR filter
|
||||
#define NZEROS 60
|
||||
float ngain = 7.423339364f;
|
||||
static float xv[NZEROS+1];
|
||||
float xcoeffs[] =
|
||||
{ -0.0083649323f, -0.0265444850f, -0.0428141462f, -0.0537571943f,
|
||||
-0.0564141052f, -0.0489161045f, -0.0310068662f, -0.0043393881f,
|
||||
+0.0275375106f, +0.0595423283f, +0.0857543325f, +0.1003565948f,
|
||||
+0.0986944931f, +0.0782804830f, +0.0395670487f, -0.0136691535f,
|
||||
-0.0744390415f, -0.1331834575f, -0.1788967208f, -0.2005995448f,
|
||||
-0.1889627181f, -0.1378439993f, -0.0454976231f, +0.0847488694f,
|
||||
+0.2444859269f, +0.4209222342f, +0.5982295474f, +0.7593684540f,
|
||||
+0.8881539892f, +0.9712773915f, +0.9999999166f, +0.9712773915f,
|
||||
+0.8881539892f, +0.7593684540f, +0.5982295474f, +0.4209222342f,
|
||||
+0.2444859269f, +0.0847488694f, -0.0454976231f, -0.1378439993f,
|
||||
-0.1889627181f, -0.2005995448f, -0.1788967208f, -0.1331834575f,
|
||||
-0.0744390415f, -0.0136691535f, +0.0395670487f, +0.0782804830f,
|
||||
+0.0986944931f, +0.1003565948f, +0.0857543325f, +0.0595423283f,
|
||||
+0.0275375106f, -0.0043393881f, -0.0310068662f, -0.0489161045f,
|
||||
-0.0564141052f, -0.0537571943f, -0.0428141462f, -0.0265444850f,
|
||||
-0.0083649323f,
|
||||
};
|
||||
|
||||
//reverted to original NXDN filters, do better with LICH/Data handling
|
||||
// NXDN48 filter - Original Version
|
||||
#define NXZEROS 134
|
||||
float nxgain = 15.95930463f;
|
||||
static float nxv[NXZEROS+1];
|
||||
|
||||
float nxcoeffs[] =
|
||||
{ +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f,
|
||||
+0.022653298f, +0.016379869f, +0.008737200f, +0.000003302f,
|
||||
-0.009468531f, -0.019262057f, -0.028914291f, -0.037935027f,
|
||||
-0.045828927f, -0.052119261f, -0.056372283f, -0.058221106f,
|
||||
-0.057387924f, -0.053703443f, -0.047122444f, -0.037734535f,
|
||||
-0.025769308f, -0.011595336f, +0.004287292f, +0.021260954f,
|
||||
+0.038610717f, +0.055550276f, +0.071252765f, +0.084885375f,
|
||||
+0.095646450f, +0.102803611f, +0.105731303f, +0.103946126f,
|
||||
+0.097138329f, +0.085197939f, +0.068234131f, +0.046586711f,
|
||||
+0.020828821f, -0.008239664f, -0.039608255f, -0.072081234f,
|
||||
-0.104311776f, -0.134843790f, -0.162160200f, -0.184736015f,
|
||||
-0.201094346f, -0.209863285f, -0.209831516f, -0.200000470f,
|
||||
-0.179630919f, -0.148282051f, -0.105841323f, -0.052543664f,
|
||||
+0.011020985f, +0.083912428f, +0.164857408f, +0.252278939f,
|
||||
+0.344336996f, +0.438979335f, +0.534000832f, +0.627109358f,
|
||||
+0.715995947f, +0.798406824f, +0.872214756f, +0.935487176f,
|
||||
+0.986548646f, +1.024035395f, +1.046939951f, +1.054644241f,
|
||||
+1.046939951f, +1.024035395f, +0.986548646f, +0.935487176f,
|
||||
+0.872214756f, +0.798406824f, +0.715995947f, +0.627109358f,
|
||||
+0.534000832f, +0.438979335f, +0.344336996f, +0.252278939f,
|
||||
+0.164857408f, +0.083912428f, +0.011020985f, -0.052543664f,
|
||||
-0.105841323f, -0.148282051f, -0.179630919f, -0.200000470f,
|
||||
-0.209831516f, -0.209863285f, -0.201094346f, -0.184736015f,
|
||||
-0.162160200f, -0.134843790f, -0.104311776f, -0.072081234f,
|
||||
-0.039608255f, -0.008239664f, +0.020828821f, +0.046586711f,
|
||||
+0.068234131f, +0.085197939f, +0.097138329f, +0.103946126f,
|
||||
+0.105731303f, +0.102803611f, +0.095646450f, +0.084885375f,
|
||||
+0.071252765f, +0.055550276f, +0.038610717f, +0.021260954f,
|
||||
+0.004287292f, -0.011595336f, -0.025769308f, -0.037734535f,
|
||||
-0.047122444f, -0.053703443f, -0.057387924f, -0.058221106f,
|
||||
-0.056372283f, -0.052119261f, -0.045828927f, -0.037935027f,
|
||||
-0.028914291f, -0.019262057f, -0.009468531f, +0.000003302f,
|
||||
+0.008737200f, +0.016379869f, +0.022653298f, +0.027362877f,
|
||||
+0.030401148f, +0.031747267f, +0.031462429f,
|
||||
};
|
||||
|
||||
// DMR filter F4EXB
|
||||
#define DMRNZEROS 60
|
||||
|
||||
// DMR filter F4EXB
|
||||
const float dmrgain = 6.82973073748f;
|
||||
|
||||
// DMR filter F4EXB
|
||||
static float dxv[DMRNZEROS+1];
|
||||
|
||||
// DMR filter F4EXB - root raised cosine alpha=0.7 Ts = 6650 S/s Fc = 48kHz
|
||||
const float dmrcoeffs[] =
|
||||
{0.0301506278, 0.0269200615, 0.0159662432, -0.0013114705, -0.0216605133,
|
||||
-0.0404938748, -0.0528141756, -0.0543747957, -0.0428325003, -0.0186176083,
|
||||
0.0147202645, 0.0508418571, 0.0816392577, 0.0988113688, 0.0957187780,
|
||||
0.0691512084, 0.0206194642, -0.0431564563, -0.1107569268, -0.1675773224,
|
||||
-0.1981519842, -0.1889130786, -0.1308939560, -0.0218608492, 0.1325685970,
|
||||
0.3190962499, 0.5182530574, 0.7070497652, 0.8623526878, 0.9644213921,
|
||||
1.0000000000, 0.9644213921, 0.8623526878, 0.7070497652, 0.5182530574,
|
||||
0.3190962499, 0.1325685970, -0.0218608492, -0.1308939560, -0.1889130786,
|
||||
-0.1981519842, -0.1675773224, -0.1107569268, -0.0431564563, 0.0206194642,
|
||||
0.0691512084, 0.0957187780, 0.0988113688, 0.0816392577, 0.0508418571,
|
||||
0.0147202645, -0.0186176083, -0.0428325003, -0.0543747957, -0.0528141756,
|
||||
-0.0404938748, -0.0216605133, -0.0013114705, 0.0159662432, 0.0269200615, 0.0301506278};
|
||||
|
||||
// dPMR filter, version of F4EXB
|
||||
static float dpmrxv[NXZEROS+1];
|
||||
|
||||
// dPMR filter, version of F4EXB
|
||||
#define DPMRNZEROS 134
|
||||
|
||||
// dPMR filter, version of F4EXB
|
||||
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
|
||||
const float dpmrcoeffs[] =
|
||||
{-0.0000983004, 0.0058388841, 0.0119748846, 0.0179185547, 0.0232592816,
|
||||
0.0275919612, 0.0305433586, 0.0317982965, 0.0311240307,
|
||||
0.0283911865, 0.0235897433, 0.0168387650, 0.0083888763,
|
||||
-0.0013831396, -0.0119878087, -0.0228442151, -0.0333082708,
|
||||
-0.0427067804, -0.0503756642, -0.0557003599, -0.0581561791,
|
||||
-0.0573462646, -0.0530347941, -0.0451732069, -0.0339174991,
|
||||
-0.0196350217, -0.0028997157, 0.0155246961, 0.0347134030,
|
||||
0.0536202583, 0.0711271166, 0.0861006725, 0.0974542022,
|
||||
0.1042112035, 0.1055676660, 0.1009496091, 0.0900625944,
|
||||
0.0729301774, 0.0499186839, 0.0217462748, -0.0105250265,
|
||||
-0.0455148664, -0.0815673067, -0.1168095612, -0.1492246435,
|
||||
-0.1767350726, -0.1972941202, -0.2089805758, -0.2100926829,
|
||||
-0.1992367833, -0.1754063031, -0.1380470370, -0.0871052089,
|
||||
-0.0230554989, 0.0530929052, 0.1398131936, 0.2351006721,
|
||||
0.3365341927, 0.4413570929, 0.5465745033, 0.6490630781,
|
||||
0.7456885564, 0.8334261381, 0.9094784589, 0.9713859928,
|
||||
1.0171250045, 1.0451886943, 1.0546479089, 1.0451886943,
|
||||
1.0171250045, 0.9713859928, 0.9094784589, 0.8334261381,
|
||||
0.7456885564, 0.6490630781, 0.5465745033, 0.4413570929,
|
||||
0.3365341927, 0.2351006721, 0.1398131936, 0.0530929052,
|
||||
-0.0230554989, -0.0871052089, -0.1380470370, -0.1754063031,
|
||||
-0.1992367833, -0.2100926829, -0.2089805758, -0.1972941202,
|
||||
-0.1767350726, -0.1492246435, -0.1168095612, -0.0815673067,
|
||||
-0.0455148664, -0.0105250265, 0.0217462748, 0.0499186839,
|
||||
0.0729301774, 0.0900625944, 0.1009496091, 0.1055676660,
|
||||
0.1042112035, 0.0974542022, 0.0861006725, 0.0711271166,
|
||||
0.0536202583, 0.0347134030, 0.0155246961, -0.0028997157,
|
||||
-0.0196350217, -0.0339174991, -0.0451732069, -0.0530347941,
|
||||
-0.0573462646, -0.0581561791, -0.0557003599, -0.0503756642,
|
||||
-0.0427067804, -0.0333082708, -0.0228442151, -0.0119878087,
|
||||
-0.0013831396, 0.0083888763, 0.0168387650, 0.0235897433,
|
||||
0.0283911865, 0.0311240307, 0.0317982965, 0.0305433586,
|
||||
0.0275919612, 0.0232592816, 0.0179185547, 0.0119748846,
|
||||
0.0058388841, -0.0000983004};
|
||||
|
||||
|
||||
|
||||
short dsd_input_filter(short sample, int mode);
|
||||
|
||||
short
|
||||
dmr_filter(short sample)
|
||||
{
|
||||
return dsd_input_filter(sample, 3);
|
||||
}
|
||||
|
||||
short
|
||||
nxdn_filter(short sample)
|
||||
{
|
||||
return dsd_input_filter(sample, 2);
|
||||
}
|
||||
|
||||
short
|
||||
dpmr_filter(short sample)
|
||||
{
|
||||
return dsd_input_filter(sample, 4);
|
||||
}
|
||||
|
||||
|
||||
short
|
||||
dsd_input_filter(short sample, int mode)
|
||||
{
|
||||
float sum; int i;
|
||||
float gain;
|
||||
int zeros;
|
||||
float *v, *coeffs;
|
||||
switch(mode) {
|
||||
case 1:
|
||||
gain = ngain;
|
||||
v = xv;
|
||||
coeffs = xcoeffs;
|
||||
zeros = NZEROS;
|
||||
break;
|
||||
case 2:
|
||||
gain = nxgain;
|
||||
v = nxv;
|
||||
coeffs = (float *)nxcoeffs;
|
||||
zeros = NXZEROS;
|
||||
break;
|
||||
case 3:
|
||||
gain = dmrgain;
|
||||
v = dxv;
|
||||
coeffs = (float *)dmrcoeffs;
|
||||
zeros = DMRNZEROS;
|
||||
break;
|
||||
case 4:
|
||||
gain = dpmrgain;
|
||||
v = dpmrxv;
|
||||
coeffs = (float *)dpmrcoeffs;
|
||||
zeros = DPMRNZEROS;
|
||||
break;
|
||||
default:
|
||||
return sample;
|
||||
}
|
||||
|
||||
for (i = 0; i < zeros; i++)
|
||||
v[i] = v[i+1];
|
||||
|
||||
v[zeros] = (float)sample; // unfiltered sample in
|
||||
sum = 0.0f;
|
||||
|
||||
for (i = 0; i <= zeros; i++)
|
||||
sum += (coeffs[i] * v[i]);
|
||||
|
||||
return (short)(sum / gain); // filtered sample out
|
||||
}
|
||||
713
src/dsd_frame.c
Normal file
713
src/dsd_frame.c
Normal file
@@ -0,0 +1,713 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
#if !defined(NULL)
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
#include "p25p1_check_nid.h"
|
||||
|
||||
void
|
||||
printFrameInfo (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int level;
|
||||
|
||||
level = (int) state->max / 164;
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
//fprintf (stderr,"inlvl: %2i%% ", level);
|
||||
}
|
||||
if (state->nac != 0)
|
||||
{
|
||||
fprintf (stderr, "%s", KCYN);
|
||||
fprintf (stderr,"nac: [%4X] ", state->nac);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
if (opts->verbose > 1)
|
||||
{
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr,"src: [%8i] ", state->lastsrc);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
fprintf (stderr, "%s", KGRN);
|
||||
fprintf (stderr,"tg: [%5i] ", state->lasttg);
|
||||
fprintf (stderr, "%s", KNRM);
|
||||
}
|
||||
|
||||
void
|
||||
processFrame (dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
|
||||
int i, j, dibit;
|
||||
char duid[3];
|
||||
char nac[13];
|
||||
int level;
|
||||
|
||||
char status_0;
|
||||
char bch_code[63];
|
||||
int index_bch_code;
|
||||
unsigned char parity;
|
||||
char v;
|
||||
int new_nac;
|
||||
char new_duid[3];
|
||||
int check_result;
|
||||
|
||||
nac[12] = 0;
|
||||
duid[2] = 0;
|
||||
j = 0;
|
||||
|
||||
if (state->rf_mod == 1)
|
||||
{
|
||||
state->maxref = (int)(state->max * 0.80F);
|
||||
state->minref = (int)(state->min * 0.80F);
|
||||
}
|
||||
else
|
||||
{
|
||||
state->maxref = state->max;
|
||||
state->minref = state->min;
|
||||
}
|
||||
|
||||
//NXDN FSW
|
||||
if ((state->synctype == 28) || (state->synctype == 29))
|
||||
{
|
||||
nxdn_frame (opts, state);
|
||||
//no MBEout...use symbol capture bin instead!
|
||||
return;
|
||||
}
|
||||
else if ((state->synctype == 6) || (state->synctype == 7))
|
||||
{
|
||||
state->nac = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lasttg = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
level = (int) state->max / 164;
|
||||
//fprintf (stderr,"inlvl: %2i%% ", level);
|
||||
}
|
||||
}
|
||||
state->nac = 0;
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
sprintf (state->fsubtype, " VOICE ");
|
||||
processDSTAR (opts, state);
|
||||
return;
|
||||
}
|
||||
else if ((state->synctype == 18) || (state->synctype == 19))
|
||||
{
|
||||
state->nac = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lasttg = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
level = (int) state->max / 164;
|
||||
//fprintf (stderr,"inlvl: %2i%% ", level);
|
||||
}
|
||||
}
|
||||
state->nac = 0;
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
sprintf (state->fsubtype, " DATA ");
|
||||
processDSTAR_HD (opts, state);
|
||||
return;
|
||||
}
|
||||
//Start DMR Types
|
||||
else if ((state->synctype >= 10) && (state->synctype <= 13) || (state->synctype == 32) || (state->synctype == 33) || (state->synctype == 34) ) //32-34 DMR MS and RC
|
||||
{
|
||||
|
||||
//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?
|
||||
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 == 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?
|
||||
//or, its just a decode error somewhere
|
||||
// else if (state->dmr_mfid == 0x20) sprintf (state->dmr_branding, "%s", "JVC Kenwood");
|
||||
// else if (state->dmr_mfid == 0x04) sprintf (state->dmr_branding, "%s", "Flyde Micro");
|
||||
// else if (state->dmr_mfid == 0x05) sprintf (state->dmr_branding, "%s", "PROD-EL SPA");
|
||||
// else if (state->dmr_mfid == 0x06) sprintf (state->dmr_branding, "%s", "Motorola"); //trident/moto con+
|
||||
// else if (state->dmr_mfid == 0x07) sprintf (state->dmr_branding, "%s", "RADIODATA");
|
||||
// else if (state->dmr_mfid == 0x08) sprintf (state->dmr_branding, "%s", "Hytera");
|
||||
// else if (state->dmr_mfid == 0x09) sprintf (state->dmr_branding, "%s", "ASELSAN");
|
||||
// else if (state->dmr_mfid == 0x0A) sprintf (state->dmr_branding, "%s", "Kirisun");
|
||||
// else if (state->dmr_mfid == 0x0B) sprintf (state->dmr_branding, "%s", "DMR Association");
|
||||
// else if (state->dmr_mfid == 0x13) sprintf (state->dmr_branding, "%s", "EMC S.P.A.");
|
||||
// else if (state->dmr_mfid == 0x1C) sprintf (state->dmr_branding, "%s", "EMC S.P.A.");
|
||||
// else if (state->dmr_mfid == 0x33) sprintf (state->dmr_branding, "%s", "Radio Activity");
|
||||
// else if (state->dmr_mfid == 0x3C) sprintf (state->dmr_branding, "%s", "Radio Activity");
|
||||
// else if (state->dmr_mfid == 0x77) sprintf (state->dmr_branding, "%s", "Vertex Standard");
|
||||
|
||||
//disable so radio id doesn't blink in and out during ncurses and aggressive_framesync
|
||||
state->nac = 0;
|
||||
//state->lastsrc = 0;
|
||||
//state->lasttg = 0;
|
||||
//state->lastsrcR = 0;
|
||||
//state->lasttgR = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
level = (int) state->max / 164;
|
||||
//fprintf (stderr,"inlvl: %2i%% ", level);
|
||||
}
|
||||
}
|
||||
if ( (state->synctype == 11) || (state->synctype == 12) || (state->synctype == 32) ) //DMR Voice Modes
|
||||
{
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL) && opts->dmr_stereo == 0)
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
sprintf (state->fsubtype, " VOICE ");
|
||||
if (opts->dmr_stereo == 0 && state->synctype < 32) // -T option for DMR (TDMA) stereo
|
||||
{
|
||||
sprintf (state->slot1light, " slot1 ");
|
||||
sprintf (state->slot2light, " slot2 ");
|
||||
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
|
||||
}
|
||||
}
|
||||
else if ( (state->synctype == 33) || (state->synctype == 34) ) //MS Data and RC data
|
||||
{
|
||||
dmrMSData (opts, state);
|
||||
// if (opts->dmr_stereo == 0)
|
||||
// {
|
||||
// closeMbeOutFile (opts, state);
|
||||
// state->err_str[0] = 0;
|
||||
// fprintf (stderr, "%s", KRED);
|
||||
// fprintf (stderr, "Please use XDMA or DMR Stereo to decode MS/Simplex \n");
|
||||
// fprintf (stderr, "%s", KNRM);
|
||||
// }
|
||||
// if (opts->dmr_stereo == 1)
|
||||
// {
|
||||
// dmrMSData (opts, state);
|
||||
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (opts->dmr_stereo == 0)
|
||||
{
|
||||
closeMbeOutFile (opts, state);
|
||||
state->err_str[0] = 0;
|
||||
sprintf (state->slot1light, " slot1 ");
|
||||
sprintf (state->slot2light, " slot2 ");
|
||||
state->payload_miP = 0; //zero out when switching back to data sync
|
||||
dmr_data_sync (opts, state);
|
||||
}
|
||||
//switch dmr_stereo to 0 when handling BS data frame syncs with processDMRdata
|
||||
if (opts->dmr_stereo == 1)
|
||||
{
|
||||
state->dmr_stereo = 0; //set the state to zero for handling pure data frames
|
||||
sprintf (state->slot1light, " slot1 ");
|
||||
sprintf (state->slot2light, " slot2 ");
|
||||
state->payload_miP = 0; //zero out when switching back to data sync
|
||||
dmr_data_sync (opts, state);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if ((state->synctype >= 2) && (state->synctype <= 5))
|
||||
{
|
||||
state->nac = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
}
|
||||
if ((state->synctype == 3) || (state->synctype == 4))
|
||||
{
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
sprintf (state->fsubtype, " VOICE ");
|
||||
processX2TDMAvoice (opts, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
closeMbeOutFile (opts, state);
|
||||
state->err_str[0] = 0;
|
||||
processX2TDMAdata (opts, state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if ((state->synctype == 14) || (state->synctype == 15))
|
||||
{
|
||||
//state->nac = 0;
|
||||
//state->lastsrc = 0;
|
||||
//state->lasttg = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
level = (int) state->max / 164;
|
||||
//fprintf (stderr,"inlvl: %2i%% ", level);
|
||||
}
|
||||
}
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
sprintf (state->fsubtype, " VOICE ");
|
||||
processProVoice (opts, state);
|
||||
return;
|
||||
}
|
||||
//edacs
|
||||
else if ((state->synctype == 37) || (state->synctype == 38))
|
||||
{
|
||||
edacs (opts, state);
|
||||
return;
|
||||
}
|
||||
//ysf
|
||||
else if ((state->synctype == 30) || (state->synctype == 31))
|
||||
{
|
||||
//Do stuff
|
||||
//fprintf(stderr, "YSF Sync! \n");
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
//openMbeOutFile (opts, state);
|
||||
}
|
||||
//sprintf(state->fsubtype, " VOICE ");
|
||||
processYSF(opts, state);
|
||||
return;
|
||||
}
|
||||
//P25 P2
|
||||
else if ((state->synctype == 35) || (state->synctype == 36))
|
||||
{
|
||||
//Do stuff
|
||||
//fprintf(stderr, "YSF Sync! \n");
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
//openMbeOutFile (opts, state);
|
||||
}
|
||||
//sprintf(state->fsubtype, " VOICE ");
|
||||
processP2(opts, state);
|
||||
return;
|
||||
}
|
||||
//dPMR
|
||||
else if ((state->synctype == 20) || (state->synctype == 24))
|
||||
{
|
||||
/* dPMR Frame Sync 1 */
|
||||
fprintf(stderr, "dPMR Frame Sync 1 ");
|
||||
}
|
||||
else if ((state->synctype == 21) || (state->synctype == 25))
|
||||
{
|
||||
/* dPMR Frame Sync 2 */
|
||||
fprintf(stderr, "dPMR Frame Sync 2 ");
|
||||
|
||||
state->nac = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lasttg = 0;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
if (opts->verbose > 0)
|
||||
{
|
||||
level = (int) state->max / 164;
|
||||
}
|
||||
}
|
||||
state->nac = 0;
|
||||
|
||||
if ((opts->mbe_out_dir[0] != 0) && (opts->mbe_out_f == NULL))
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
sprintf(state->fsubtype, " VOICE ");
|
||||
processdPMRvoice (opts, state);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
else if ((state->synctype == 22) || (state->synctype == 26))
|
||||
{
|
||||
/* dPMR Frame Sync 3 */
|
||||
fprintf(stderr, "dPMR Frame Sync 3 ");
|
||||
}
|
||||
else if ((state->synctype == 23) || (state->synctype == 27))
|
||||
{
|
||||
/* dPMR Frame Sync 4 */
|
||||
fprintf(stderr, "dPMR Frame Sync 4 ");
|
||||
}
|
||||
//dPMR
|
||||
else //P25
|
||||
{
|
||||
// Read the NAC, 12 bits
|
||||
j = 0;
|
||||
index_bch_code = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
|
||||
v = 1 & (dibit >> 1); // bit 1
|
||||
nac[j] = v + '0';
|
||||
j++;
|
||||
bch_code[index_bch_code] = v;
|
||||
index_bch_code++;
|
||||
|
||||
v = 1 & dibit; // bit 0
|
||||
nac[j] = v + '0';
|
||||
j++;
|
||||
bch_code[index_bch_code] = v;
|
||||
index_bch_code++;
|
||||
}
|
||||
//this one setting bogus nac data
|
||||
// state->nac = strtol (nac, NULL, 2);
|
||||
|
||||
// Read the DUID, 4 bits
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
duid[i] = dibit + '0';
|
||||
|
||||
bch_code[index_bch_code] = 1 & (dibit >> 1); // bit 1
|
||||
index_bch_code++;
|
||||
bch_code[index_bch_code] = 1 & dibit; // bit 0
|
||||
index_bch_code++;
|
||||
}
|
||||
|
||||
// Read the BCH data for error correction of NAC and DUID
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
|
||||
bch_code[index_bch_code] = 1 & (dibit >> 1); // bit 1
|
||||
index_bch_code++;
|
||||
bch_code[index_bch_code] = 1 & dibit; // bit 0
|
||||
index_bch_code++;
|
||||
}
|
||||
// Intermission: read the status dibit
|
||||
status_0 = getDibit (opts, state) + '0';
|
||||
// ... continue reading the BCH error correction data
|
||||
for (i = 0; i < 20; i++)
|
||||
{
|
||||
dibit = getDibit (opts, state);
|
||||
|
||||
bch_code[index_bch_code] = 1 & (dibit >> 1); // bit 1
|
||||
index_bch_code++;
|
||||
bch_code[index_bch_code] = 1 & dibit; // bit 0
|
||||
index_bch_code++;
|
||||
}
|
||||
|
||||
// Read the parity bit
|
||||
dibit = getDibit (opts, state);
|
||||
bch_code[index_bch_code] = 1 & (dibit >> 1); // bit 1
|
||||
parity = (1 & dibit); // bit 0
|
||||
|
||||
// Check if the NID is correct
|
||||
check_result = check_NID (bch_code, &new_nac, new_duid, parity);
|
||||
if (check_result) {
|
||||
if (new_nac != state->nac) {
|
||||
// NAC fixed by error correction
|
||||
state->nac = new_nac;
|
||||
if (state->p2_hardset == 0 && new_nac != 0)
|
||||
{
|
||||
state->p2_cc = new_nac;
|
||||
}
|
||||
state->debug_header_errors++;
|
||||
}
|
||||
if (strcmp(new_duid, duid) != 0) {
|
||||
// DUID fixed by error correction
|
||||
//fprintf (stderr,"Fixing DUID %s -> %s\n", duid, new_duid);
|
||||
duid[0] = new_duid[0];
|
||||
duid[1] = new_duid[1];
|
||||
state->debug_header_errors++;
|
||||
}
|
||||
} else {
|
||||
// Check of NID failed and unable to recover its value
|
||||
//fprintf (stderr,"NID error\n");
|
||||
duid[0] = 'E';
|
||||
duid[1] = 'E';
|
||||
state->debug_header_critical_errors++;
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp (duid, "00") == 0)
|
||||
{
|
||||
// Header Data Unit
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," HDU\n");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
closeMbeOutFile (opts, state);
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
state->lastp25type = 2;
|
||||
state->dmrburstL = 25;
|
||||
state->currentslot = 0;
|
||||
sprintf (state->fsubtype, " HDU ");
|
||||
processHDU (opts, state);
|
||||
}
|
||||
else if (strcmp (duid, "11") == 0)
|
||||
{
|
||||
// Logical Link Data Unit 1
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," LDU1 ");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
}
|
||||
state->lastp25type = 1;
|
||||
state->dmrburstL = 26;
|
||||
state->currentslot = 0;
|
||||
sprintf (state->fsubtype, " LDU1 ");
|
||||
state->numtdulc = 0;
|
||||
if (state->payload_algid == 0x81)
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
processLDU1 (opts, state);
|
||||
}
|
||||
else if (strcmp (duid, "22") == 0)
|
||||
{
|
||||
// Logical Link Data Unit 2
|
||||
state->dmrburstL = 27;
|
||||
state->currentslot = 0;
|
||||
if (state->lastp25type != 1)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr,"\n Ignoring LDU2 not preceeded by LDU1\n");
|
||||
}
|
||||
state->lastp25type = 0;
|
||||
sprintf (state->fsubtype, " ");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," LDU2 ");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
}
|
||||
state->lastp25type = 2;
|
||||
sprintf (state->fsubtype, " LDU2 ");
|
||||
state->numtdulc = 0;
|
||||
processLDU2 (opts, state);
|
||||
}
|
||||
}
|
||||
else if (strcmp (duid, "33") == 0)
|
||||
{
|
||||
// Terminator with subsequent Link Control
|
||||
state->dmrburstL = 28;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," TDULC\n");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
closeMbeOutFile (opts, state);
|
||||
}
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lastp25type = 0;
|
||||
state->err_str[0] = 0;
|
||||
sprintf (state->fsubtype, " TDULC ");
|
||||
state->numtdulc++;
|
||||
if ((opts->resume > 0) && (state->numtdulc > opts->resume))
|
||||
{
|
||||
resumeScan (opts, state);
|
||||
}
|
||||
processTDULC (opts, state);
|
||||
state->err_str[0] = 0;
|
||||
}
|
||||
else if (strcmp (duid, "03") == 0)
|
||||
{
|
||||
// Terminator without subsequent Link Control
|
||||
state->dmrburstL = 28;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," TDU\n");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
closeMbeOutFile (opts, state);
|
||||
}
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lastp25type = 0;
|
||||
state->err_str[0] = 0;
|
||||
sprintf (state->fsubtype, " TDU ");
|
||||
|
||||
processTDU (opts, state);
|
||||
}
|
||||
else if (strcmp (duid, "13") == 0)
|
||||
{
|
||||
state->dmrburstL = 29;
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," TSBK");
|
||||
}
|
||||
if (opts->resume > 0)
|
||||
{
|
||||
resumeScan (opts, state);
|
||||
}
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
state->lastp25type = 3;
|
||||
sprintf (state->fsubtype, " TSBK ");
|
||||
|
||||
// Now processing NID
|
||||
processTSBK(opts, state);
|
||||
|
||||
}
|
||||
else if (strcmp (duid, "30") == 0)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," PDU\n");
|
||||
}
|
||||
if (opts->resume > 0)
|
||||
{
|
||||
resumeScan (opts, state);
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
}
|
||||
state->lastp25type = 4;
|
||||
sprintf (state->fsubtype, " PDU ");
|
||||
}
|
||||
// try to guess based on previous frame if unknown type
|
||||
else if (state->lastp25type == 1)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr,"(LDU2) ");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
}
|
||||
//state->lastp25type = 0;
|
||||
// Guess that the state is LDU2
|
||||
state->lastp25type = 2;
|
||||
sprintf (state->fsubtype, "(LDU2) ");
|
||||
state->numtdulc = 0;
|
||||
processLDU2 (opts, state);
|
||||
}
|
||||
else if (state->lastp25type == 2)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr,"(LDU1) ");
|
||||
}
|
||||
if (opts->mbe_out_dir[0] != 0)
|
||||
{
|
||||
if (opts->mbe_out_f == NULL)
|
||||
{
|
||||
openMbeOutFile (opts, state);
|
||||
}
|
||||
}
|
||||
//state->lastp25type = 0;
|
||||
// Guess that the state is LDU1
|
||||
state->lastp25type = 1;
|
||||
sprintf (state->fsubtype, "(LDU1) ");
|
||||
state->numtdulc = 0;
|
||||
processLDU1 (opts, state);
|
||||
}
|
||||
else if (state->lastp25type == 3)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," (TSBK)\n");
|
||||
}
|
||||
//state->lastp25type = 0;
|
||||
// Guess that the state is TSBK
|
||||
state->lastp25type = 3;
|
||||
sprintf (state->fsubtype, "(TSBK) ");
|
||||
|
||||
// Now processing NID
|
||||
skipDibit (opts, state, 328-25);
|
||||
}
|
||||
else if (state->lastp25type == 4)
|
||||
{
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," (PDU)\n");
|
||||
}
|
||||
state->lastp25type = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->lastp25type = 0;
|
||||
sprintf (state->fsubtype, " ");
|
||||
if (opts->errorbars == 1)
|
||||
{
|
||||
printFrameInfo (opts, state);
|
||||
fprintf (stderr," duid:%s *Unknown DUID*\n", duid); //prints on dPMR frame 3
|
||||
// fprintf (stderr, "\n"); //prints on dPMR frame 3
|
||||
}
|
||||
}
|
||||
}
|
||||
1376
src/dsd_frame_sync.c
Normal file
1376
src/dsd_frame_sync.c
Normal file
File diff suppressed because it is too large
Load Diff
147
src/dsd_import.c
Normal file
147
src/dsd_import.c
Normal file
@@ -0,0 +1,147 @@
|
||||
|
||||
|
||||
#include "dsd.h"
|
||||
#define BSIZE 999
|
||||
|
||||
int csvGroupImport(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
char filename[1024] = "filename.csv";
|
||||
sprintf (filename, "%s", opts->group_in_file);
|
||||
//filename[1023] = '\0'; //necessary?
|
||||
char buffer[BSIZE];
|
||||
FILE * fp;
|
||||
fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
printf("Unable to open group file '%s'\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
int row_count = 0;
|
||||
int field_count = 0;
|
||||
long int group_number = 0; //local group number for array index value
|
||||
int i = 0;
|
||||
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)
|
||||
{
|
||||
//group_number = atol(field);
|
||||
state->group_array[i].groupNumber = atol(field);
|
||||
fprintf (stderr, "%ld, ", state->group_array[i].groupNumber);
|
||||
}
|
||||
if (field_count == 1)
|
||||
{
|
||||
strcpy(state->group_array[i].groupMode, field);
|
||||
fprintf (stderr, "%s, ", state->group_array[i].groupMode);
|
||||
}
|
||||
if (field_count == 2)
|
||||
{
|
||||
strcpy(state->group_array[i].groupName, field);
|
||||
fprintf (stderr, "%s ", state->group_array[i].groupName);
|
||||
}
|
||||
|
||||
field = strtok(NULL, ",");
|
||||
field_count++;
|
||||
}
|
||||
fprintf (stderr, "\n");
|
||||
i++;
|
||||
state->group_tally++;
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//LCN import for EDACS, migrated to channel map (channel map does both)
|
||||
int csvLCNImport(dsd_opts * opts, dsd_state * state)
|
||||
{
|
||||
char filename[1024] = "filename.csv";
|
||||
sprintf (filename, "%s", opts->lcn_in_file);
|
||||
//filename[1023] = '\0'; //necessary?
|
||||
char buffer[BSIZE];
|
||||
FILE * fp;
|
||||
fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
printf("Unable to open lcn file '%s'\n", filename);
|
||||
//have this return -1 and handle it inside of main
|
||||
exit(1);
|
||||
}
|
||||
int row_count = 0;
|
||||
int field_count = 0;
|
||||
|
||||
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) {
|
||||
|
||||
state->trunk_lcn_freq[field_count] = atol (field);
|
||||
state->lcn_freq_count++; //keep tally of number of Frequencies imported
|
||||
fprintf (stderr, "LCN [%d] [%ld]", field_count+1, state->trunk_lcn_freq[field_count]);
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
|
||||
field = strtok(NULL, ",");
|
||||
field_count++;
|
||||
}
|
||||
fprintf (stderr, "LCN Count %d\n", state->lcn_freq_count);
|
||||
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int csvChanImport(dsd_opts * opts, dsd_state * state) //channel map import
|
||||
{
|
||||
char filename[1024] = "filename.csv";
|
||||
sprintf (filename, "%s", opts->chan_in_file);
|
||||
|
||||
char buffer[BSIZE];
|
||||
FILE * fp;
|
||||
fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
printf("Unable to open channel map file '%s'\n", filename);
|
||||
//have this return -1 and handle it inside of main
|
||||
exit(1);
|
||||
}
|
||||
int row_count = 0;
|
||||
int field_count = 0;
|
||||
|
||||
long int chan_number;
|
||||
|
||||
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, "%ld", &chan_number);
|
||||
}
|
||||
|
||||
if (field_count == 1)
|
||||
{
|
||||
sscanf (field, "%ld", &state->trunk_chan_map[chan_number]);
|
||||
//adding this should be compatible with EDACS, test and obsolete the LCN Import function if desired
|
||||
sscanf (field, "%ld", &state->trunk_lcn_freq[state->lcn_freq_count]);
|
||||
state->lcn_freq_count++; //keep tally of number of Frequencies imported
|
||||
}
|
||||
|
||||
field = strtok(NULL, ",");
|
||||
field_count++;
|
||||
}
|
||||
fprintf (stderr, "Channel [%05ld] [%09ld]", chan_number, state->trunk_chan_map[chan_number]);
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
2397
src/dsd_main.c
Normal file
2397
src/dsd_main.c
Normal file
File diff suppressed because it is too large
Load Diff
663
src/dsd_mbe.c
Normal file
663
src/dsd_mbe.c
Normal file
@@ -0,0 +1,663 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
void
|
||||
playMbeFiles (dsd_opts * opts, dsd_state * state, int argc, char **argv)
|
||||
{
|
||||
|
||||
int i;
|
||||
char imbe_d[88];
|
||||
char ambe_d[49];
|
||||
|
||||
for (i = state->optind; i < argc; i++)
|
||||
{
|
||||
sprintf (opts->mbe_in_file, "%s", argv[i]);
|
||||
openMbeInFile (opts, state);
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
fprintf (stderr, "playing %s\n", opts->mbe_in_file);
|
||||
while (feof (opts->mbe_in_f) == 0)
|
||||
{
|
||||
if (state->mbe_file_type == 0)
|
||||
{
|
||||
readImbe4400Data (opts, state, imbe_d);
|
||||
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
processAudio(opts, state);
|
||||
}
|
||||
if (opts->wav_out_f != NULL)
|
||||
{
|
||||
writeSynthesizedVoice (opts, state);
|
||||
}
|
||||
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
|
||||
playSynthesizedVoice (opts, state);
|
||||
|
||||
}
|
||||
}
|
||||
else if (state->mbe_file_type == 1)
|
||||
{
|
||||
readAmbe2450Data (opts, state, ambe_d);
|
||||
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
processAudio(opts, state);
|
||||
}
|
||||
if (opts->wav_out_f != NULL)
|
||||
{
|
||||
writeSynthesizedVoice (opts, state);
|
||||
}
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
playSynthesizedVoice (opts, state);
|
||||
}
|
||||
}
|
||||
if (exitflag == 1)
|
||||
{
|
||||
cleanupAndExit (opts, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
processMbeFrame (dsd_opts * opts, dsd_state * state, char imbe_fr[8][23], char ambe_fr[4][24], char imbe7100_fr[7][24])
|
||||
{
|
||||
|
||||
//
|
||||
int Pr[256] = {
|
||||
0x0000, 0x1F00, 0xE300, 0xFC00, 0x2503, 0x3A03, 0xC603, 0xD903,
|
||||
0x4A05, 0x5505, 0xA905, 0xB605, 0x6F06, 0x7006, 0x8C06, 0x9306,
|
||||
0x2618, 0x3918, 0xC518, 0xDA18, 0x031B, 0x1C1B, 0xE01B, 0xFF1B,
|
||||
0x6C1D, 0x731D, 0x8F1D, 0x901D, 0x491E, 0x561E, 0xAA1E, 0xB51E, //31
|
||||
0x4B28, 0x5428, 0xA828, 0xB728, 0x6E2B, 0x712B, 0x8D2B, 0x922B,
|
||||
0x012D, 0x1E2D, 0xE22D, 0xFD2D, 0x242E, 0x3B2E, 0xC72E, 0xD82E,
|
||||
0x6D30, 0x7230, 0x8E30, 0x9130, 0x4833, 0x5733, 0xAB33, 0xB433,
|
||||
0x2735, 0x3835, 0xC435, 0xDB35, 0x0236, 0x1D36, 0xE136, 0xFE36, //63
|
||||
0x2B49, 0x3449, 0xC849, 0xD749, 0x0E4A, 0x114A, 0xED4A, 0xF24A,
|
||||
0x614C, 0xAE4C, 0x824C, 0x9D4C, 0x444F, 0x5B4F, 0xA74F, 0xB84F,
|
||||
0x0D51, 0x1251, 0xEE51, 0xF151, 0x2852, 0x3752, 0xCB52, 0xD452,
|
||||
0x4754, 0x5854, 0xA454, 0xBB54, 0x6257, 0x7D57, 0x8157, 0x9E57, //95
|
||||
0x6061, 0x7F61, 0x8361, 0x9C61, 0x4562, 0x5A62, 0xA662, 0xB962,
|
||||
0x2A64, 0x3564, 0xC964, 0xD664, 0x0F67, 0x1067, 0xEC67, 0xF367,
|
||||
0x4679, 0x5979, 0xA579, 0xBA79, 0x637A, 0x7C7A, 0x807A, 0x9F7A,
|
||||
0x0C7C, 0x137C, 0xEF7C, 0xF07C, 0x297F, 0x367F, 0xCA7F, 0xD57F, //127
|
||||
0x4D89, 0x5289, 0xAE89, 0xB189, 0x688A, 0x778A, 0x8B8A, 0x948A,
|
||||
0x078C, 0x188C, 0xE48C, 0xFB8C, 0x228F, 0x3D8F, 0xC18F, 0xDE8F,
|
||||
0x6B91, 0x7491, 0x8891, 0x9791, 0x4E92, 0x5192, 0xAD92, 0xB292,
|
||||
0x2194, 0x3E94, 0xC294, 0xDD94, 0x0497, 0x1B97, 0xE797, 0xF897, //159
|
||||
0x06A1, 0x19A1, 0xE5A1, 0xFAA1, 0x23A2, 0x3CA2, 0xC0A2, 0xDFA2,
|
||||
0x4CA4, 0x53A4, 0xAFA4, 0xB0A4, 0x69A7, 0x76A7, 0x8AA7, 0x95A7,
|
||||
0x20B9, 0x3FB9, 0xC3B9, 0xDCB9, 0x05BA, 0x1ABA, 0xE6BA, 0xF9BA,
|
||||
0x6ABC, 0x75BC, 0x89BC, 0x96BC, 0x4FBF, 0x50BF, 0xACBF, 0xB3BF, //191
|
||||
0x66C0, 0x79C0, 0x85C0, 0x9AC0, 0x43C3, 0x5CC3, 0xA0C3, 0xBFC3,
|
||||
0x2CC5, 0x33C5, 0xCFC5, 0xD0C5, 0x09C6, 0x16C6, 0xEAC6, 0xF5C6,
|
||||
0x84D0, 0x85DF, 0x8AD3, 0x8BDC, 0xB6D5, 0xB7DA, 0xB8D6, 0xB9D9,
|
||||
0xD0DA, 0xD1D5, 0xDED9, 0xDFD6, 0xE2DF, 0xE3D0, 0xECDC, 0xEDD3, //223
|
||||
0x2DE8, 0x32E8, 0xCEE8, 0xD1E8, 0x08EB, 0x17EB, 0xEBEB, 0xF4EB,
|
||||
0x67ED, 0x78ED, 0x84ED, 0x9BED, 0x42EE, 0x5DEE, 0xA1EE, 0xBEEE,
|
||||
0x0BF0, 0x14F0, 0xE8F0, 0xF7F0, 0x2EF3, 0x31F3, 0xCDF3, 0xD2F3,
|
||||
0x41F5, 0x5EF5, 0xA2F5, 0xBDF5, 0x64F6, 0x7BF6, 0x87F6, 0x98F6 //255
|
||||
};
|
||||
//
|
||||
|
||||
int i;
|
||||
char imbe_d[88];
|
||||
char ambe_d[49];
|
||||
char ambe_d_str[50];
|
||||
unsigned long long int k;
|
||||
int x;
|
||||
|
||||
int preempt = 0; //TDMA dual voice slot preemption(when using OSS output)
|
||||
|
||||
|
||||
for (i = 0; i < 88; i++)
|
||||
{
|
||||
imbe_d[i] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 49; i++)
|
||||
{
|
||||
ambe_d[i] = 0;
|
||||
}
|
||||
|
||||
//set playback mode for this frame
|
||||
char mode[8];
|
||||
|
||||
//if we are using allow/whitelist mode, then write 'B' to mode for block
|
||||
//comparison below will look for an 'A' to write to mode if it is allowed
|
||||
if (opts->trunk_use_allow_list == 1) sprintf (mode, "%s", "B");
|
||||
|
||||
int groupNumber = 0;
|
||||
|
||||
if (state->currentslot == 0) groupNumber = state->lasttg;
|
||||
else groupNumber = state->lasttgR;
|
||||
|
||||
for (i = 0; i < state->group_tally; i++)
|
||||
{
|
||||
if (state->group_array[i].groupNumber == groupNumber)
|
||||
{
|
||||
strcpy (mode, state->group_array[i].groupMode);
|
||||
}
|
||||
}
|
||||
|
||||
//set flag to not play audio this time, but won't prevent writing to wav files
|
||||
if (strcmp(mode, "B") == 0) opts->audio_out = 0;
|
||||
|
||||
//end set playback mode for this frame
|
||||
|
||||
if ((state->synctype == 0) || (state->synctype == 1))
|
||||
{
|
||||
// 0 +P25p1
|
||||
// 1 -P25p1
|
||||
state->errs = mbe_eccImbe7200x4400C0 (imbe_fr);
|
||||
//state->errs2 = state->errs;
|
||||
mbe_demodulateImbe7200x4400Data (imbe_fr);
|
||||
state->errs2 = mbe_eccImbe7200x4400Data (imbe_fr, imbe_d);
|
||||
|
||||
|
||||
|
||||
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
|
||||
imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
|
||||
//mbe_processImbe7200x4400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe_fr, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
PrintIMBEData (opts, state, imbe_d);
|
||||
}
|
||||
|
||||
//increment vc counter by one.
|
||||
state->p25vc++;
|
||||
|
||||
if (opts->mbe_out_f != NULL && state->dmr_encL == 0) //only save if this bit not set
|
||||
{
|
||||
saveImbe4400Data (opts, state, imbe_d);
|
||||
}
|
||||
}
|
||||
else if ((state->synctype == 14) || (state->synctype == 15)) //pV Sync
|
||||
{
|
||||
|
||||
state->errs = mbe_eccImbe7100x4400C0 (imbe7100_fr);
|
||||
//state->errs2 = state->errs;
|
||||
mbe_demodulateImbe7100x4400Data (imbe7100_fr);
|
||||
state->errs2 = mbe_eccImbe7100x4400Data (imbe7100_fr, imbe_d);
|
||||
mbe_convertImbe7100to7200(imbe_d); //needs extra conversion step apparently
|
||||
|
||||
mbe_processImbe4400Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
|
||||
imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
|
||||
//below line the old way of doing this
|
||||
// mbe_processImbe7100x4400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, imbe7100_fr, imbe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
PrintIMBEData (opts, state, imbe_d);
|
||||
}
|
||||
if (opts->mbe_out_f != NULL)
|
||||
{
|
||||
saveImbe4400Data (opts, state, imbe_d);
|
||||
}
|
||||
}
|
||||
else if ((state->synctype == 6) || (state->synctype == 7))
|
||||
{
|
||||
mbe_processAmbe3600x2400Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
PrintAMBEData (opts, state, ambe_d);
|
||||
}
|
||||
if (opts->mbe_out_f != NULL)
|
||||
{
|
||||
saveAmbe2450Data (opts, state, ambe_d);
|
||||
}
|
||||
}
|
||||
else if ((state->synctype == 28) || (state->synctype == 29)) //was 8 and 9
|
||||
{
|
||||
|
||||
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
||||
//state->errs2 = state->errs;
|
||||
mbe_demodulateAmbe3600x2450Data (ambe_fr);
|
||||
state->errs2 = mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
||||
|
||||
if ( (state->nxdn_cipher_type == 0x01 && state->R > 0) ||
|
||||
(state->M == 1 && state->R > 0) )
|
||||
{
|
||||
|
||||
if (state->payload_miN == 0)
|
||||
{
|
||||
state->payload_miN = state->R;
|
||||
}
|
||||
|
||||
char ambe_temp[49];
|
||||
for (short int i = 0; i < 49; i++)
|
||||
{
|
||||
ambe_temp[i] = ambe_d[i];
|
||||
ambe_d[i] = 0;
|
||||
}
|
||||
LFSRN(ambe_temp, ambe_d, state);
|
||||
|
||||
}
|
||||
|
||||
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
|
||||
ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
PrintAMBEData (opts, state, ambe_d);
|
||||
}
|
||||
|
||||
if (opts->mbe_out_f != NULL && (state->dmr_encL == 0 || opts->dmr_mute_encL == 0) )
|
||||
{
|
||||
saveAmbe2450Data (opts, state, ambe_d);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//stereo slots and slot 0 (left slot)
|
||||
if (state->currentslot == 0) //&& opts->dmr_stereo == 1
|
||||
{
|
||||
|
||||
state->errs = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
||||
//state->errs2 = state->errs;
|
||||
mbe_demodulateAmbe3600x2450Data (ambe_fr);
|
||||
state->errs2 = mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
||||
|
||||
if ( (state->K > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x10) ||
|
||||
(state->K > 0 && state->M == 1) )
|
||||
{
|
||||
k = Pr[state->K];
|
||||
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
||||
for (short int j = 0; j < 48; j++) //49
|
||||
{
|
||||
x = ( ((k << j) & 0x800000000000) >> 47 );
|
||||
ambe_d[j] ^= x;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (state->K1 > 0 && state->dmr_so & 0x40 && state->payload_keyid == 0 && state->dmr_fid == 0x68) ||
|
||||
(state->K1 > 0 && state->M == 1) )
|
||||
{
|
||||
|
||||
int pos = 0;
|
||||
|
||||
unsigned long long int k1 = state->K1;
|
||||
unsigned long long int k2 = state->K2;
|
||||
unsigned long long int k3 = state->K3;
|
||||
unsigned long long int k4 = state->K4;
|
||||
|
||||
int T_Key[256] = {0};
|
||||
int pN[882] = {0};
|
||||
|
||||
int len = 0;
|
||||
|
||||
if (k2 == 0)
|
||||
{
|
||||
len = 39;
|
||||
k1 = k1 << 24;
|
||||
}
|
||||
if (k2 != 0)
|
||||
{
|
||||
len = 127;
|
||||
}
|
||||
if (k4 != 0)
|
||||
{
|
||||
len = 255;
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
|
||||
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
|
||||
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
|
||||
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
|
||||
}
|
||||
|
||||
for (i = 0; i < 882; i++)
|
||||
{
|
||||
pN[i] = T_Key[pos];
|
||||
pos++;
|
||||
if (pos > len)
|
||||
{
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//sanity check
|
||||
if (state->DMRvcL > 17) //18
|
||||
{
|
||||
state->DMRvcL = 17; //18
|
||||
}
|
||||
|
||||
pos = state->DMRvcL * 49;
|
||||
for(i = 0; i < 49; i++)
|
||||
{
|
||||
ambe_d[i] ^= pN[pos];
|
||||
pos++;
|
||||
}
|
||||
state->DMRvcL++;
|
||||
}
|
||||
|
||||
mbe_processAmbe2450Dataf (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str,
|
||||
ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
|
||||
|
||||
//old method for this step below
|
||||
//mbe_processAmbe3600x2450Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
if (opts->payload == 1) // && state->R == 0 this is why slot 1 didn't primt abme, probably had it set during testing
|
||||
{
|
||||
PrintAMBEData (opts, state, ambe_d);
|
||||
}
|
||||
}
|
||||
//stereo slots and slot 1 (right slot)
|
||||
if (state->currentslot == 1) //&& opts->dmr_stereo == 1
|
||||
{
|
||||
|
||||
state->errsR = mbe_eccAmbe3600x2450C0 (ambe_fr);
|
||||
//state->errs2R = state->errsR;
|
||||
mbe_demodulateAmbe3600x2450Data (ambe_fr);
|
||||
state->errs2R = mbe_eccAmbe3600x2450Data (ambe_fr, ambe_d);
|
||||
|
||||
if ( (state->K > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x10) ||
|
||||
(state->K > 0 && state->M == 1) )
|
||||
{
|
||||
k = Pr[state->K];
|
||||
k = ( ((k & 0xFF0F) << 32 ) + (k << 16) + k );
|
||||
for (short int j = 0; j < 48; j++)
|
||||
{
|
||||
x = ( ((k << j) & 0x800000000000) >> 47 );
|
||||
ambe_d[j] ^= x;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (state->K1 > 0 && state->dmr_soR & 0x40 && state->payload_keyidR == 0 && state->dmr_fidR == 0x68) ||
|
||||
(state->K1 > 0 && state->M == 1))
|
||||
{
|
||||
|
||||
int pos = 0;
|
||||
|
||||
unsigned long long int k1 = state->K1;
|
||||
unsigned long long int k2 = state->K2;
|
||||
unsigned long long int k3 = state->K3;
|
||||
unsigned long long int k4 = state->K4;
|
||||
|
||||
int T_Key[256] = {0};
|
||||
int pN[882] = {0};
|
||||
|
||||
int len = 0;
|
||||
|
||||
if (k2 == 0)
|
||||
{
|
||||
len = 39;
|
||||
k1 = k1 << 24;
|
||||
}
|
||||
if (k2 != 0)
|
||||
{
|
||||
len = 127;
|
||||
}
|
||||
if (k4 != 0)
|
||||
{
|
||||
len = 255;
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
T_Key[i] = ( ((k1 << i) & 0x8000000000000000) >> 63 );
|
||||
T_Key[i+64] = ( ((k2 << i) & 0x8000000000000000) >> 63 );
|
||||
T_Key[i+128] = ( ((k3 << i) & 0x8000000000000000) >> 63 );
|
||||
T_Key[i+192] = ( ((k4 << i) & 0x8000000000000000) >> 63 );
|
||||
}
|
||||
|
||||
for (i = 0; i < 882; i++)
|
||||
{
|
||||
pN[i] = T_Key[pos];
|
||||
pos++;
|
||||
if (pos > len)
|
||||
{
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//sanity check
|
||||
if (state->DMRvcR > 17) //18
|
||||
{
|
||||
state->DMRvcR = 17; //18
|
||||
}
|
||||
|
||||
pos = state->DMRvcR * 49;
|
||||
for(i = 0; i < 49; i++)
|
||||
{
|
||||
ambe_d[i] ^= pN[pos];
|
||||
pos++;
|
||||
}
|
||||
state->DMRvcR++;
|
||||
}
|
||||
|
||||
mbe_processAmbe2450Dataf (state->audio_out_temp_bufR, &state->errsR, &state->errs2R, state->err_strR,
|
||||
ambe_d, state->cur_mp2, state->prev_mp2, state->prev_mp_enhanced2, opts->uvquality);
|
||||
|
||||
//old method for this step below
|
||||
//mbe_processAmbe3600x2450Framef (state->audio_out_temp_bufR, &state->errsR, &state->errs2R, state->err_strR, ambe_fr, ambe_d, state->cur_mp2, state->prev_mp2, state->prev_mp_enhanced2, opts->uvquality);
|
||||
if (opts->payload == 1)
|
||||
{
|
||||
PrintAMBEData (opts, state, ambe_d);
|
||||
}
|
||||
}
|
||||
|
||||
//X2-TDMA? Not sure what still makes it this far to run under Framef
|
||||
// if (opts->dmr_stereo == 0)
|
||||
// {
|
||||
// mbe_processAmbe3600x2450Framef (state->audio_out_temp_buf, &state->errs, &state->errs2, state->err_str, ambe_fr, ambe_d, state->cur_mp, state->prev_mp, state->prev_mp_enhanced, opts->uvquality);
|
||||
// if (opts->payload == 1)
|
||||
// {
|
||||
// PrintAMBEData (opts, state, ambe_d);
|
||||
// }
|
||||
// //only save MBE files if not enc or unmuted, THIS does not seem to work for some reason
|
||||
// if (opts->mbe_out_f != NULL && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
|
||||
// {
|
||||
// saveAmbe2450Data (opts, state, ambe_d);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
//quick enc check to determine whether or not to play enc traffic
|
||||
int enc_bit = 0;
|
||||
//end enc check
|
||||
|
||||
if ( (opts->dmr_mono == 1 || opts->dmr_stereo == 1) && state->currentslot == 0) //all mono traffic routed through 'left'
|
||||
{
|
||||
enc_bit = (state->dmr_so >> 6) & 0x1;
|
||||
if (enc_bit == 1)
|
||||
{
|
||||
state->dmr_encL = 1;
|
||||
}
|
||||
|
||||
//checkdown for P25 1 and 2
|
||||
else if (state->payload_algid != 0 && state->payload_algid != 0x80)
|
||||
{
|
||||
state->dmr_encL = 1;
|
||||
}
|
||||
else state->dmr_encL = 0;
|
||||
|
||||
//second checkdown for P25-2 WACN, SYSID, and CC set
|
||||
if (state->synctype == 35 || state->synctype == 36)
|
||||
{
|
||||
if (state->p2_wacn == 0 || state->p2_sysid == 0 || state->p2_cc == 0)
|
||||
{
|
||||
state->dmr_encL = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method)
|
||||
if (opts->reverse_mute == 1)
|
||||
{
|
||||
if (state->dmr_encL == 0)
|
||||
{
|
||||
state->dmr_encL = 1;
|
||||
opts->unmute_encrypted_p25 = 0;
|
||||
opts->dmr_mute_encL = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->dmr_encL = 0;
|
||||
opts->unmute_encrypted_p25 = 1;
|
||||
opts->dmr_mute_encL = 0;
|
||||
}
|
||||
}
|
||||
//end reverse mute test
|
||||
|
||||
//OSS Specific Voice Preemption if dual voices on TDMA and one slot has preference over the other
|
||||
if (opts->slot_preference == 1 && opts->audio_out_type == 5 && opts->audio_out == 1 && state->dmrburstR == 16)
|
||||
{
|
||||
opts->audio_out = 0;
|
||||
preempt = 1;
|
||||
if (opts->payload == 0) fprintf (stderr, " *MUTED*");
|
||||
}
|
||||
|
||||
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
|
||||
{
|
||||
state->debug_audio_errors += state->errs2;
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
processAudio(opts, state);
|
||||
}
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
playSynthesizedVoice (opts, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->dmr_stereo == 1 && state->currentslot == 1)
|
||||
{
|
||||
enc_bit = (state->dmr_soR >> 6) & 0x1;
|
||||
if (enc_bit == 0x1)
|
||||
{
|
||||
state->dmr_encR = 1;
|
||||
}
|
||||
|
||||
//checkdown for P25 1 and 2
|
||||
else if (state->payload_algidR != 0 && state->payload_algidR != 0x80)
|
||||
{
|
||||
state->dmr_encR = 1;
|
||||
}
|
||||
else state->dmr_encR = 0;
|
||||
|
||||
//second checkdown for P25-2 WACN, SYSID, and CC set
|
||||
if (state->synctype == 35 || state->synctype == 36)
|
||||
{
|
||||
if (state->p2_wacn == 0 || state->p2_sysid == 0 || state->p2_cc == 0)
|
||||
{
|
||||
state->dmr_encR = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//reverse mute testing, only mute unencrypted traffic (slave piggyback dsd+ method)
|
||||
if (opts->reverse_mute == 1)
|
||||
{
|
||||
if (state->dmr_encR == 0)
|
||||
{
|
||||
state->dmr_encR = 1;
|
||||
opts->unmute_encrypted_p25 = 0;
|
||||
opts->dmr_mute_encR = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->dmr_encR = 0;
|
||||
opts->unmute_encrypted_p25 = 1;
|
||||
opts->dmr_mute_encR = 0;
|
||||
}
|
||||
}
|
||||
//end reverse mute test
|
||||
|
||||
//OSS Specific Voice Preemption if dual voices on TDMA and one slot has preference over the other
|
||||
if (opts->slot_preference == 0 && opts->audio_out_type == 5 && opts->audio_out == 1 && state->dmrburstL == 16)
|
||||
{
|
||||
opts->audio_out = 0;
|
||||
preempt = 1;
|
||||
if (opts->payload == 0) fprintf (stderr, " *MUTED*");
|
||||
}
|
||||
|
||||
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
|
||||
{
|
||||
state->debug_audio_errorsR += state->errs2R;
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
processAudioR(opts, state);
|
||||
}
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
playSynthesizedVoiceR (opts, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if using anything but DMR Stereo, borrowing state->dmr_encL to signal enc or clear for other types
|
||||
if (opts->dmr_mono == 0 && opts->dmr_stereo == 0 && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0) )
|
||||
{
|
||||
state->debug_audio_errors += state->errs2;
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
processAudio(opts, state);
|
||||
}
|
||||
if (opts->audio_out == 1)
|
||||
{
|
||||
playSynthesizedVoice (opts, state);
|
||||
}
|
||||
}
|
||||
|
||||
//if using anything but DMR Stereo, borrowing state->dmr_encL to signal enc or clear for other types
|
||||
if (opts->wav_out_f != NULL && opts->dmr_stereo == 0 && (opts->unmute_encrypted_p25 == 1 || state->dmr_encL == 0))
|
||||
{
|
||||
writeSynthesizedVoice (opts, state);
|
||||
}
|
||||
|
||||
//per call can only be used when ncurses terminal is active since we use its call history matrix for when to record
|
||||
if (opts->dmr_stereo_wav == 1 && opts->dmr_stereo == 1 && state->currentslot == 0) //opts->use_ncurses_terminal == 1 &&
|
||||
{
|
||||
if (state->dmr_encL == 0 || opts->dmr_mute_encL == 0)
|
||||
{
|
||||
//write wav to per call on left channel Slot 1
|
||||
writeSynthesizedVoice (opts, state);
|
||||
}
|
||||
}
|
||||
|
||||
//per call can only be used when ncurses terminal is active since we use its call history matrix for when to record
|
||||
if (opts->dmr_stereo_wav == 1 && opts->dmr_stereo == 1 && state->currentslot == 1) //opts->use_ncurses_terminal == 1 &&
|
||||
{
|
||||
if (state->dmr_encR == 0 || opts->dmr_mute_encR == 0)
|
||||
{
|
||||
//write wav to per call on right channel Slot 2
|
||||
writeSynthesizedVoiceR (opts, state);
|
||||
}
|
||||
}
|
||||
|
||||
if (preempt == 1)
|
||||
{
|
||||
opts->audio_out = 1;
|
||||
preempt = 0;
|
||||
}
|
||||
|
||||
//reset audio out flag for next repitition
|
||||
if (strcmp(mode, "B") == 0) opts->audio_out = 1;
|
||||
//restore flag for null output type
|
||||
if (opts->audio_out_type == 9) opts->audio_out = 0;
|
||||
}
|
||||
3266
src/dsd_ncurses.c
Normal file
3266
src/dsd_ncurses.c
Normal file
File diff suppressed because it is too large
Load Diff
106
src/dsd_reset.c
Normal file
106
src/dsd_reset.c
Normal file
@@ -0,0 +1,106 @@
|
||||
|
||||
#include "dsd.h"
|
||||
#include "p25p1_heuristics.h"
|
||||
|
||||
void
|
||||
resetState (dsd_state * state)
|
||||
{
|
||||
|
||||
int i, j;
|
||||
|
||||
// state->dibit_buf = malloc (sizeof (int) * 1000000);
|
||||
// state->dibit_buf_p = state->dibit_buf + 200;
|
||||
// memset (state->dibit_buf, 0, sizeof (int) * 200);
|
||||
state->repeat = 0; //not sure yet
|
||||
//state->audio_out_buf = malloc (sizeof (short) * 1000000);
|
||||
//memset (state->audio_out_buf, 0, 100 * sizeof (short));
|
||||
//state->audio_out_buf_p = state->audio_out_buf + 100;
|
||||
//state->audio_out_float_buf = malloc (sizeof (float) * 1000000);
|
||||
//memset (state->audio_out_float_buf, 0, 100 * sizeof (float));
|
||||
//state->audio_out_float_buf_p = state->audio_out_float_buf + 100;
|
||||
//state->audio_out_idx = 0;
|
||||
//state->audio_out_idx2 = 0;
|
||||
//state->audio_out_temp_buf_p = state->audio_out_temp_buf;
|
||||
//state->wav_out_bytes = 0;
|
||||
state->center = 0;
|
||||
state->jitter = -1;
|
||||
state->synctype = -1;
|
||||
state->min = -15000;
|
||||
state->max = 15000;
|
||||
state->lmid = 0;
|
||||
state->umid = 0;
|
||||
state->minref = -12000;
|
||||
state->maxref = 12000;
|
||||
state->lastsample = 0;
|
||||
for (i = 0; i < 128; i++)
|
||||
{
|
||||
state->sbuf[i] = 0;
|
||||
}
|
||||
state->sidx = 0;
|
||||
for (i = 0; i < 1024; i++)
|
||||
{
|
||||
state->maxbuf[i] = 15000;
|
||||
}
|
||||
for (i = 0; i < 1024; i++)
|
||||
{
|
||||
state->minbuf[i] = -15000;
|
||||
}
|
||||
state->midx = 0;
|
||||
state->err_str[0] = 0;
|
||||
sprintf (state->fsubtype, " ");
|
||||
sprintf (state->ftype, " ");
|
||||
state->symbolcnt = 0;
|
||||
state->rf_mod = 0;
|
||||
state->numflips = 0;
|
||||
state->lastsynctype = -1;
|
||||
state->lastp25type = 0;
|
||||
state->offset = 0;
|
||||
state->carrier = 0;
|
||||
for (i = 0; i < 25; i++)
|
||||
{
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
state->tg[i][j] = 48;
|
||||
}
|
||||
}
|
||||
state->tgcount = 0;
|
||||
state->lasttg = 0;
|
||||
state->lastsrc = 0;
|
||||
state->nac = 0;
|
||||
state->errs = 0;
|
||||
state->errs2 = 0;
|
||||
state->mbe_file_type = -1;
|
||||
state->optind = 0;
|
||||
state->numtdulc = 0;
|
||||
state->firstframe = 0;
|
||||
sprintf (state->slot0light, " slot0 ");
|
||||
sprintf (state->slot1light, " slot1 ");
|
||||
state->aout_gain = 25;
|
||||
memset (state->aout_max_buf, 0, sizeof (float) * 200);
|
||||
state->aout_max_buf_p = state->aout_max_buf;
|
||||
state->aout_max_buf_idx = 0;
|
||||
state->samplesPerSymbol = 10;
|
||||
state->symbolCenter = 4;
|
||||
sprintf (state->algid, "________");
|
||||
sprintf (state->keyid, "________________");
|
||||
state->currentslot = 0;
|
||||
state->cur_mp = malloc (sizeof (mbe_parms));
|
||||
state->prev_mp = malloc (sizeof (mbe_parms));
|
||||
state->prev_mp_enhanced = malloc (sizeof (mbe_parms));
|
||||
mbe_initMbeParms (state->cur_mp, state->prev_mp, state->prev_mp_enhanced);
|
||||
state->p25kid = 0;
|
||||
|
||||
state->debug_audio_errors = 0;
|
||||
state->debug_header_errors = 0;
|
||||
state->debug_header_critical_errors = 0;
|
||||
|
||||
state->nxdn_last_ran = 0;
|
||||
// state->payload_algid = 0;
|
||||
// state->payload_algidR = 0;
|
||||
state->dmr_encL = 0;
|
||||
state->dmr_encR = 0;
|
||||
|
||||
//each time you run this, it increses memory use by 4MB, massive memory leak
|
||||
//need to revisit this sometime and look into only resetting only the necesary items to let P25 switch between signals (C4FM or Wide) without needing a restart
|
||||
initialize_p25_heuristics(&state->p25_heuristics); //see if we want to re-init this or not, currently seems to cause memory leak when running over and over, mitigated with a reset flag
|
||||
}
|
||||
276
src/dsd_rigctl.c
Normal file
276
src/dsd_rigctl.c
Normal file
@@ -0,0 +1,276 @@
|
||||
/*-------------------------------------------------------------------------------
|
||||
* dsd_rigctl.c
|
||||
* Simple RIGCTL Client for DSD (remote control of GQRX, SDR++, etc)
|
||||
*
|
||||
* Portions from https://github.com/neural75/gqrx-scanner
|
||||
*
|
||||
* LWVMOBILE
|
||||
* 2022-10 DSD-FME Florida Man Edition
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "dsd.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
|
||||
//UDP Specific
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define BUFSIZE 1024
|
||||
#define FREQ_MAX 4096
|
||||
#define SAVED_FREQ_MAX 1000
|
||||
#define TAG_MAX 100
|
||||
|
||||
//
|
||||
// error - wrapper for perror
|
||||
//
|
||||
void error(char *msg) {
|
||||
perror(msg);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
//
|
||||
// Connect
|
||||
//
|
||||
int Connect (char *hostname, int portno)
|
||||
{
|
||||
int sockfd, n;
|
||||
struct sockaddr_in serveraddr;
|
||||
struct hostent *server;
|
||||
|
||||
|
||||
/* socket: create the socket */
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0)
|
||||
{
|
||||
fprintf(stderr,"ERROR opening socket\n");
|
||||
error("ERROR opening socket");
|
||||
}
|
||||
|
||||
|
||||
/* gethostbyname: get the server's DNS entry */
|
||||
server = gethostbyname(hostname);
|
||||
if (server == NULL) {
|
||||
fprintf(stderr,"ERROR, no such host as %s\n", hostname);
|
||||
//exit(0);
|
||||
return (0); //return 0, check on other end and configure pulse input
|
||||
}
|
||||
|
||||
/* build the server's Internet address */
|
||||
bzero((char *) &serveraddr, sizeof(serveraddr));
|
||||
serveraddr.sin_family = AF_INET;
|
||||
bcopy((char *)server->h_addr,
|
||||
(char *)&serveraddr.sin_addr.s_addr, server->h_length);
|
||||
serveraddr.sin_port = htons(portno);
|
||||
|
||||
/* connect: create a connection with the server */
|
||||
if (connect(sockfd, (const struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0)
|
||||
{
|
||||
fprintf(stderr,"ERROR opening socket\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
//
|
||||
// Send
|
||||
//
|
||||
bool Send(int sockfd, char *buf)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = write(sockfd, buf, strlen(buf));
|
||||
if (n < 0)
|
||||
error("ERROR writing to socket");
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Recv
|
||||
//
|
||||
bool Recv(int sockfd, char *buf)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = read(sockfd, buf, BUFSIZE);
|
||||
if (n < 0)
|
||||
error("ERROR reading from socket");
|
||||
buf[n]= '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// GQRX Protocol
|
||||
//
|
||||
long int GetCurrentFreq(int sockfd)
|
||||
{
|
||||
long int freq = 0;
|
||||
char buf[BUFSIZE];
|
||||
char * ptr;
|
||||
char * token;
|
||||
|
||||
Send(sockfd, "f\n");
|
||||
Recv(sockfd, buf);
|
||||
|
||||
if (strcmp(buf, "RPRT 1") == 0 )
|
||||
return freq;
|
||||
|
||||
token = strtok (buf, "\n");
|
||||
freq = strtol (token, &ptr, 10);
|
||||
// fprintf (stderr, "\nRIGCTL VFO Freq: [%ld]\n", freq);
|
||||
return freq;
|
||||
}
|
||||
|
||||
bool SetFreq(int sockfd, long int freq)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
|
||||
sprintf (buf, "F %ld\n", freq);
|
||||
Send(sockfd, buf);
|
||||
Recv(sockfd, buf);
|
||||
|
||||
if (strcmp(buf, "RPRT 1") == 0 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetModulation(int sockfd, int bandwidth)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
//the bandwidth is now a user/system based configurable variable
|
||||
sprintf (buf, "M FM %d\n", bandwidth);
|
||||
Send(sockfd, buf);
|
||||
Recv(sockfd, buf);
|
||||
|
||||
if (strcmp(buf, "RPRT 1") == 0 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetSignalLevel(int sockfd, double *dBFS)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
|
||||
Send(sockfd, "l\n");
|
||||
Recv(sockfd, buf);
|
||||
|
||||
if (strcmp(buf, "RPRT 1") == 0 )
|
||||
return false;
|
||||
|
||||
sscanf(buf, "%lf", dBFS);
|
||||
*dBFS = round((*dBFS) * 10)/10;
|
||||
|
||||
if (*dBFS == 0.0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetSquelchLevel(int sockfd, double *dBFS)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
|
||||
Send(sockfd, "l SQL\n");
|
||||
Recv(sockfd, buf);
|
||||
|
||||
if (strcmp(buf, "RPRT 1") == 0 )
|
||||
return false;
|
||||
|
||||
sscanf(buf, "%lf", dBFS);
|
||||
*dBFS = round((*dBFS) * 10)/10;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetSquelchLevel(int sockfd, double dBFS)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
|
||||
sprintf (buf, "L SQL %f\n", dBFS);
|
||||
Send(sockfd, buf);
|
||||
Recv(sockfd, buf);
|
||||
|
||||
if (strcmp(buf, "RPRT 1") == 0 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
//
|
||||
// GetSignalLevelEx
|
||||
// Get a bunch of sample with some delay and calculate the mean value
|
||||
//
|
||||
bool GetSignalLevelEx(int sockfd, double *dBFS, int n_samp)
|
||||
{
|
||||
double temp_level;
|
||||
*dBFS = 0;
|
||||
int errors = 0;
|
||||
for (int i = 0; i < n_samp; i++)
|
||||
{
|
||||
if ( GetSignalLevel(sockfd, &temp_level) )
|
||||
*dBFS = *dBFS + temp_level;
|
||||
else
|
||||
errors++;
|
||||
usleep(1000);
|
||||
}
|
||||
*dBFS = *dBFS / (n_samp - errors);
|
||||
return true;
|
||||
}
|
||||
|
||||
//shoe in UDP input connection here...still having issues that I don't know how to resolve
|
||||
int UDPBind (char *hostname, int portno)
|
||||
{
|
||||
int sockfd, n;
|
||||
struct sockaddr_in serveraddr, client_addr;
|
||||
struct hostent *server;
|
||||
|
||||
/* socket: create the socket */
|
||||
//UDP socket
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
if (sockfd < 0)
|
||||
{
|
||||
fprintf(stderr,"ERROR opening UDP socket\n");
|
||||
error("ERROR opening UDP socket");
|
||||
}
|
||||
|
||||
/* build the server's Internet address */
|
||||
bzero((char *) &serveraddr, sizeof(serveraddr));
|
||||
serveraddr.sin_family = AF_INET;
|
||||
serveraddr.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY
|
||||
serveraddr.sin_port = htons(portno);
|
||||
|
||||
//Bind socket to listening
|
||||
if (bind(sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) {
|
||||
perror("ERROR on binding UDP Port");
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency)
|
||||
{
|
||||
int handle;
|
||||
unsigned short udp_port = opts->rtl_udp_port;
|
||||
char data[5] = {0}; //data buffer size is 5 for UDP frequency tuning
|
||||
struct sockaddr_in address;
|
||||
|
||||
uint32_t new_freq = frequency;
|
||||
opts->rtlsdr_center_freq = new_freq; //for ncurses terminal display after rtl is started up
|
||||
|
||||
handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
data[0] = 0;
|
||||
data[1] = new_freq & 0xFF;
|
||||
data[2] = (new_freq >> 8) & 0xFF;
|
||||
data[3] = (new_freq >> 16) & 0xFF;
|
||||
data[4] = (new_freq >> 24) & 0xFF;
|
||||
|
||||
memset((char * ) & address, 0, sizeof(address));
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = inet_addr("127.0.0.1"); //make user configurable later
|
||||
address.sin_port = htons(udp_port);
|
||||
sendto(handle, data, 5, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in));
|
||||
}
|
||||
@@ -8,11 +8,11 @@ openSerial (dsd_opts * opts, dsd_state * state)
|
||||
struct termios tty;
|
||||
speed_t baud;
|
||||
|
||||
printf ("Opening serial port %s and setting baud to %i\n", opts->serial_dev, opts->serial_baud);
|
||||
fprintf (stderr, "Opening serial port %s and setting baud to %i\n", opts->serial_dev, opts->serial_baud);
|
||||
opts->serial_fd = open (opts->serial_dev, O_WRONLY);
|
||||
if (opts->serial_fd == -1)
|
||||
{
|
||||
printf ("Error, couldn't open %s\n", opts->serial_dev);
|
||||
fprintf (stderr, "Error, couldn't open %s\n", opts->serial_dev);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
462
src/dsd_symbol.c
Normal file
462
src/dsd_symbol.c
Normal file
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* Copyright (C) 2010 DSD Author
|
||||
* GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0)
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "dsd.h"
|
||||
|
||||
int
|
||||
getSymbol (dsd_opts * opts, dsd_state * state, int have_sync)
|
||||
{
|
||||
short sample, sample2;
|
||||
int i, sum, symbol, count;
|
||||
ssize_t result;
|
||||
|
||||
sum = 0;
|
||||
count = 0;
|
||||
sample = 0; //init sample with a value of 0...see if this was causing issues with raw audio monitoring
|
||||
|
||||
for (i = 0; i < state->samplesPerSymbol; i++) //right HERE
|
||||
{
|
||||
|
||||
// timing control
|
||||
if ((i == 0) && (have_sync == 0))
|
||||
{
|
||||
if (state->samplesPerSymbol == 20)
|
||||
{
|
||||
if ((state->jitter >= 7) && (state->jitter <= 10))
|
||||
{
|
||||
i--;
|
||||
}
|
||||
else if ((state->jitter >= 11) && (state->jitter <= 14))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (state->rf_mod == 1)
|
||||
{
|
||||
if ((state->jitter >= 0) && (state->jitter < state->symbolCenter))
|
||||
{
|
||||
i++; // fall back
|
||||
}
|
||||
else if ((state->jitter > state->symbolCenter) && (state->jitter < 10))
|
||||
{
|
||||
i--; // catch up
|
||||
}
|
||||
}
|
||||
else if (state->rf_mod == 2)
|
||||
{
|
||||
if ((state->jitter >= state->symbolCenter - 1) && (state->jitter <= state->symbolCenter))
|
||||
{
|
||||
i--;
|
||||
}
|
||||
else if ((state->jitter >= state->symbolCenter + 1) && (state->jitter <= state->symbolCenter + 2))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (state->rf_mod == 0)
|
||||
{
|
||||
if ((state->jitter > 0) && (state->jitter <= state->symbolCenter))
|
||||
{
|
||||
i--; // catch up
|
||||
}
|
||||
else if ((state->jitter > state->symbolCenter) && (state->jitter < state->samplesPerSymbol))
|
||||
{
|
||||
i++; // fall back
|
||||
}
|
||||
}
|
||||
state->jitter = -1;
|
||||
}
|
||||
|
||||
// Read the new sample from the input
|
||||
if(opts->audio_in_type == 0) //pulse audio
|
||||
{
|
||||
pa_simple_read(opts->pulse_digi_dev_in, &sample, 2, NULL );
|
||||
}
|
||||
|
||||
else if (opts->audio_in_type == 5) //OSS
|
||||
{
|
||||
read (opts->audio_in_fd, &sample, 2);
|
||||
}
|
||||
|
||||
//stdin only, wav files moving to new number
|
||||
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);
|
||||
if(result == 0)
|
||||
{
|
||||
sf_close(opts->audio_in_file);
|
||||
cleanupAndExit (opts, state);
|
||||
}
|
||||
}
|
||||
//wav files, same but using seperate value so we can still manipulate ncurses menu
|
||||
//since we can not worry about getch/stdin conflict
|
||||
else if (opts->audio_in_type == 2)
|
||||
{
|
||||
result = sf_read_short(opts->audio_in_file, &sample, 1);
|
||||
if(result == 0)
|
||||
{
|
||||
|
||||
sf_close(opts->audio_in_file);
|
||||
fprintf (stderr, "\n\nEnd of .wav file.\n");
|
||||
//open pulse input if we are pulse output AND using ncurses terminal
|
||||
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)
|
||||
{
|
||||
#ifdef USE_RTLSDR
|
||||
// TODO: need to read demodulated stream here
|
||||
// get_rtlsdr_sample(&sample);
|
||||
get_rtlsdr_sample(&sample, opts, state);
|
||||
if (opts->monitor_input_audio == 1 && state->lastsynctype == -1 && sample < 32767 && sample > -32767)
|
||||
{
|
||||
state->pulse_raw_out_buffer = sample; //steal raw out buffer sample here?
|
||||
pa_simple_write(opts->pulse_raw_dev_out, (void*)&state->pulse_raw_out_buffer, 2, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
//tcp socket input from SDR++
|
||||
else if (opts->audio_in_type == 8)
|
||||
{
|
||||
result = sf_read_short(opts->tcp_file_in, &sample, 1);
|
||||
if(result == 0) {
|
||||
|
||||
fprintf (stderr, "Connection to TCP Server Disconnected.\n");
|
||||
//open pulse input if we are pulse output AND using ncurses terminal
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//UDP Socket input...not working correct. Reads samples, but no sync
|
||||
// else if (opts->audio_in_type == 6)
|
||||
// {
|
||||
//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);
|
||||
// if (sample != 0)
|
||||
// fprintf (stderr, "Result = %d Sample = %d \n", result, sample);
|
||||
// }
|
||||
|
||||
if (opts->use_cosine_filter)
|
||||
{
|
||||
if ( (state->lastsynctype >= 10 && state->lastsynctype <= 13) || state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34)
|
||||
{
|
||||
sample = dmr_filter(sample);
|
||||
}
|
||||
|
||||
else if (state->lastsynctype == 8 || state->lastsynctype == 9 ||
|
||||
state->lastsynctype == 16 || state->lastsynctype == 17 ||
|
||||
state->lastsynctype == 20 || state->lastsynctype == 21 ||
|
||||
state->lastsynctype == 22 || state->lastsynctype == 23 ||
|
||||
state->lastsynctype == 24 || state->lastsynctype == 25 ||
|
||||
state->lastsynctype == 26 || state->lastsynctype == 27 ||
|
||||
state->lastsynctype == 28 || state->lastsynctype == 29 ) //||
|
||||
//state->lastsynctype == 35 || state->lastsynctype == 36) //phase 2 C4FM disc tap input
|
||||
{
|
||||
//if(state->samplesPerSymbol == 20)
|
||||
if(opts->frame_nxdn48 == 1)
|
||||
{
|
||||
sample = nxdn_filter(sample);
|
||||
}
|
||||
//else if (state->lastsynctype >= 20 && state->lastsynctype <=27) //this the right range?
|
||||
else if (opts->frame_dpmr == 1)
|
||||
{
|
||||
sample = dpmr_filter(sample);
|
||||
}
|
||||
else if (state->samplesPerSymbol == 8) //phase 2 cqpsk
|
||||
{
|
||||
//sample = dmr_filter(sample); //work on filter later
|
||||
}
|
||||
else // the 12.5KHz NXDN filter is the same as the DMR filter...hopefully
|
||||
{
|
||||
sample = dmr_filter(sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((sample > state->max) && (have_sync == 1) && (state->rf_mod == 0))
|
||||
{
|
||||
sample = state->max;
|
||||
}
|
||||
else if ((sample < state->min) && (have_sync == 1) && (state->rf_mod == 0))
|
||||
{
|
||||
sample = state->min;
|
||||
}
|
||||
|
||||
if (sample > state->center)
|
||||
{
|
||||
if (state->lastsample < state->center)
|
||||
{
|
||||
state->numflips += 1;
|
||||
}
|
||||
if (sample > (state->maxref * 1.25))
|
||||
{
|
||||
if (state->lastsample < (state->maxref * 1.25))
|
||||
{
|
||||
state->numflips += 1;
|
||||
}
|
||||
if ((state->jitter < 0) && (state->rf_mod == 1))
|
||||
{ // first spike out of place
|
||||
state->jitter = i;
|
||||
}
|
||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
||||
{
|
||||
fprintf (stderr, "O");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
||||
{
|
||||
fprintf (stderr, "+");
|
||||
}
|
||||
if ((state->jitter < 0) && (state->lastsample < state->center) && (state->rf_mod != 1))
|
||||
{ // first transition edge
|
||||
state->jitter = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // sample < 0
|
||||
if (state->lastsample > state->center)
|
||||
{
|
||||
state->numflips += 1;
|
||||
}
|
||||
if (sample < (state->minref * 1.25))
|
||||
{
|
||||
if (state->lastsample > (state->minref * 1.25))
|
||||
{
|
||||
state->numflips += 1;
|
||||
}
|
||||
if ((state->jitter < 0) && (state->rf_mod == 1))
|
||||
{ // first spike out of place
|
||||
state->jitter = i;
|
||||
}
|
||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
||||
{
|
||||
fprintf (stderr, "X");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
||||
{
|
||||
fprintf (stderr, "-");
|
||||
}
|
||||
if ((state->jitter < 0) && (state->lastsample > state->center) && (state->rf_mod != 1))
|
||||
{ // first transition edge
|
||||
state->jitter = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state->samplesPerSymbol == 20) //nxdn 4800 baud 2400 symbol rate
|
||||
{
|
||||
if ((i >= 9) && (i <= 11))
|
||||
{
|
||||
sum += sample;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (state->samplesPerSymbol == 5) //provoice or gfsk
|
||||
{
|
||||
if (i == 2)
|
||||
{
|
||||
sum += sample;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state->rf_mod == 0)
|
||||
{
|
||||
// 0: C4FM modulation
|
||||
|
||||
if ((i >= state->symbolCenter - 1) && (i <= state->symbolCenter + 2))
|
||||
{
|
||||
sum += sample;
|
||||
count++;
|
||||
}
|
||||
|
||||
#ifdef TRACE_DSD
|
||||
if (i == state->symbolCenter - 1) {
|
||||
state->debug_sample_left_edge = state->debug_sample_index - 1;
|
||||
}
|
||||
if (i == state->symbolCenter + 2) {
|
||||
state->debug_sample_right_edge = state->debug_sample_index - 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1: QPSK modulation
|
||||
// 2: GFSK modulation
|
||||
// Note: this has been changed to use an additional symbol to the left
|
||||
// On the p25_raw_unencrypted.flac it is evident that the timing
|
||||
// comes one sample too late.
|
||||
// This change makes a significant improvement in the BER, at least for
|
||||
// this file.
|
||||
//if ((i == state->symbolCenter) || (i == state->symbolCenter + 1))
|
||||
if ((i == state->symbolCenter - 1) || (i == state->symbolCenter + 1))
|
||||
{
|
||||
sum += sample;
|
||||
count++;
|
||||
}
|
||||
|
||||
#ifdef TRACE_DSD
|
||||
//if (i == state->symbolCenter) {
|
||||
if (i == state->symbolCenter - 1) {
|
||||
state->debug_sample_left_edge = state->debug_sample_index - 1;
|
||||
}
|
||||
if (i == state->symbolCenter + 1) {
|
||||
state->debug_sample_right_edge = state->debug_sample_index - 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
state->lastsample = sample;
|
||||
|
||||
}
|
||||
|
||||
symbol = (sum / count);
|
||||
|
||||
if ((opts->symboltiming == 1) && (have_sync == 0) && (state->lastsynctype != -1))
|
||||
{
|
||||
if (state->jitter >= 0)
|
||||
{
|
||||
fprintf (stderr, " %i\n", state->jitter);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TRACE_DSD
|
||||
if (state->samplesPerSymbol == 10) {
|
||||
float left, right;
|
||||
if (state->debug_label_file == NULL) {
|
||||
state->debug_label_file = fopen ("pp_label.txt", "w");
|
||||
}
|
||||
left = state->debug_sample_left_edge / SAMPLE_RATE_IN;
|
||||
right = state->debug_sample_right_edge / SAMPLE_RATE_IN;
|
||||
if (state->debug_prefix != '\0') {
|
||||
if (state->debug_prefix == 'I') {
|
||||
fprintf(state->debug_label_file, "%f\t%f\t%c%c %i\n", left, right, state->debug_prefix, state->debug_prefix_2, symbol);
|
||||
} else {
|
||||
fprintf(state->debug_label_file, "%f\t%f\t%c %i\n", left, right, state->debug_prefix, symbol);
|
||||
}
|
||||
} else {
|
||||
fprintf(state->debug_label_file, "%f\t%f\t%i\n", left, right, symbol);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//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
|
||||
if (opts->audio_in_type == 4)
|
||||
{
|
||||
//use fopen and read in a symbol, check op25 for clues
|
||||
if(opts->symbolfile == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error Opening File %s\n", opts->audio_in_dev); //double check this
|
||||
return(-1);
|
||||
}
|
||||
|
||||
state->symbolc = fgetc(opts->symbolfile);
|
||||
|
||||
//experimental throttle
|
||||
useconds_t stime = state->symbol_throttle;
|
||||
if (state->use_throttle == 1)
|
||||
{
|
||||
// usleep(stime);
|
||||
usleep(.003); //very environment specific, tuning to cygwin
|
||||
}
|
||||
//fprintf(stderr, "%d", state->symbolc);
|
||||
if( feof(opts->symbolfile) )
|
||||
{
|
||||
// opts->audio_in_type = 0; //switch to pulse after playback, ncurses terminal can initiate replay if wanted
|
||||
fclose(opts->symbolfile);
|
||||
fprintf (stderr, "\n\nEnd of .bin file\n");
|
||||
//open pulse input if we are pulse output AND using ncurses terminal
|
||||
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
|
||||
if (state->rf_mod == 2) //GFSK
|
||||
{
|
||||
symbol = state->symbolc;
|
||||
if (state->symbolc == 0 )
|
||||
{
|
||||
symbol = -3; //-1
|
||||
}
|
||||
if (state->symbolc == 1 )
|
||||
{
|
||||
symbol = -1; //-3
|
||||
}
|
||||
}
|
||||
else //everything else
|
||||
{
|
||||
if (state->symbolc == 0)
|
||||
{
|
||||
symbol = 1; //-1
|
||||
}
|
||||
if (state->symbolc == 1)
|
||||
{
|
||||
symbol = 3; //-3
|
||||
}
|
||||
if (state->symbolc == 2)
|
||||
{
|
||||
symbol = -1; //1
|
||||
}
|
||||
if (state->symbolc == 3)
|
||||
{
|
||||
symbol = -3; //3
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
state->symbolcnt++;
|
||||
return (symbol);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user