mirror of
https://github.com/g4klx/MMDVM.git
synced 2026-10-15 16:24:11 +02:00
Compare commits
1015
Commits
48kHz
...
PlatformIO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f293b102b9 | ||
|
|
e6fbcd9c1a | ||
|
|
fb5ce0101d | ||
|
|
07d54f74cc | ||
|
|
05d1e7e9dc | ||
|
|
d9932fe5c0 | ||
|
|
81b0ce126d | ||
|
|
9e7f1040f8 | ||
|
|
c3246dbcb7 | ||
|
|
edd60caad3 | ||
|
|
b8a0f7b1cf | ||
|
|
28e564738e | ||
|
|
029e6df4b9 | ||
|
|
508018c89c | ||
|
|
bf23a94038 | ||
|
|
b2755864ef | ||
|
|
7276ca3929 | ||
|
|
d14b9dede3 | ||
|
|
de87d77ad6 | ||
|
|
2626ad5580 | ||
|
|
031b078e76 | ||
|
|
5d987969a9 | ||
|
|
f898390956 | ||
|
|
f8d1cc7aed | ||
|
|
cf9fa93cb9 | ||
|
|
69adaf2ebb | ||
|
|
7f4ae790ba | ||
|
|
416fc8fc8b | ||
|
|
5b79356ddb | ||
|
|
26fc87aca6 | ||
|
|
54745130c2 | ||
|
|
f65d52bf2f | ||
|
|
5800b33f21 | ||
|
|
105dba2c27 | ||
|
|
777cc9f2ea | ||
|
|
e879b4615a | ||
|
|
8d69fc59be | ||
|
|
2f4e9cc269 | ||
|
|
ee07f76bd5 | ||
|
|
d063a1da1e | ||
|
|
6a4dad4c44 | ||
|
|
ed6bd66fab | ||
|
|
1a07a6f0d1 | ||
|
|
70bf47f93e | ||
|
|
a5eaafc7e0 | ||
|
|
2200945857 | ||
|
|
63a8e61a45 | ||
|
|
7275f65e44 | ||
|
|
2aabdb61ab | ||
|
|
96c0e5b141 | ||
|
|
9c883cfafd | ||
|
|
8a1ab88c3a | ||
|
|
f4e1c0b401 | ||
|
|
43b53fe2fc | ||
|
|
6517229f87 | ||
|
|
d0557ab723 | ||
|
|
b5d106dcb6 | ||
|
|
34cbff41c9 | ||
|
|
752e13821f | ||
|
|
86b45c15eb | ||
|
|
4fa0e052f8 | ||
|
|
0736a5cbe8 | ||
|
|
6ffb68450c | ||
|
|
1ad20fd4a0 | ||
|
|
34f6dfc5bb | ||
|
|
1e065ba17f | ||
|
|
1df7d7a7fc | ||
|
|
7e919b12f7 | ||
|
|
bada4d9285 | ||
|
|
9ae4a5376d | ||
|
|
8038ce9c9c | ||
|
|
842dcdb5c5 | ||
|
|
f7ead5cc0e | ||
|
|
b155597693 | ||
|
|
7148cf9f7d | ||
|
|
9440b43114 | ||
|
|
c06a83660f | ||
|
|
657e71238d | ||
|
|
510d433906 | ||
|
|
ff39a9031b | ||
|
|
4f3aa8b7e1 | ||
|
|
a35b8f929a | ||
|
|
6d0335b357 | ||
|
|
54f4dcabf0 | ||
|
|
19e2ccf150 | ||
|
|
0c4f49fa68 | ||
|
|
98ebefe84b | ||
|
|
029e7eef4a | ||
|
|
ca4fff2ba6 | ||
|
|
23ba73cabd | ||
|
|
519af80ac4 | ||
|
|
c5b542f438 | ||
|
|
3503e0cc2a | ||
|
|
955578789d | ||
|
|
061e10e395 | ||
|
|
fa9040fdf0 | ||
|
|
8240225371 | ||
|
|
f2c7c19444 | ||
|
|
2e5f174cdd | ||
|
|
d144ce6f1c | ||
|
|
31744a237a | ||
|
|
46c0958704 | ||
|
|
99d2a0d822 | ||
|
|
81518bf74c | ||
|
|
dd5a465a39 | ||
|
|
a345586ee9 | ||
|
|
55c89a4413 | ||
|
|
60f87072ba | ||
|
|
f2d2033733 | ||
|
|
0334b5a791 | ||
|
|
a62fd814ae | ||
|
|
02000c8049 | ||
|
|
2509ab5ba1 | ||
|
|
2de9e4d533 | ||
|
|
fda1e3dcf4 | ||
|
|
cb3ed795d0 | ||
|
|
632ddda68b | ||
|
|
b6093406ad | ||
|
|
8d9b49bdb0 | ||
|
|
625ec93af8 | ||
|
|
b2111553c6 | ||
|
|
252d54a484 | ||
|
|
1ad9799123 | ||
|
|
6d67b9f104 | ||
|
|
fb85505433 | ||
|
|
823b91c97d | ||
|
|
2a001f491d | ||
|
|
b5ba384495 | ||
|
|
d8f138066e | ||
|
|
6964251122 | ||
|
|
0fca178bd7 | ||
|
|
a60ecb3e57 | ||
|
|
a2978d9e91 | ||
|
|
e304f5cce2 | ||
|
|
99a7df7c13 | ||
|
|
6dfe1a9271 | ||
|
|
9ff59570c7 | ||
|
|
d5e9b5a315 | ||
|
|
1ac0628c1c | ||
|
|
dc41fd83d2 | ||
|
|
20013e3adc | ||
|
|
5960fb97f3 | ||
|
|
8f8dc92a14 | ||
|
|
ccdbe126e0 | ||
|
|
d29a0bc3a6 | ||
|
|
4ff057069b | ||
|
|
f8d8f85841 | ||
|
|
ad77fd60fd | ||
|
|
aab8f5469f | ||
|
|
f1d6a8b36e | ||
|
|
843997ccad | ||
|
|
d5f2d1c382 | ||
|
|
c99f50575d | ||
|
|
29c8cf9414 | ||
|
|
2c6e2c2a11 | ||
|
|
8d10555dc9 | ||
|
|
14d5098018 | ||
|
|
52a351d0f1 | ||
|
|
43f84ae968 | ||
|
|
a73376671c | ||
|
|
759efd1da4 | ||
|
|
ea0c1953cc | ||
|
|
a29cb23a75 | ||
|
|
bb66c72ef3 | ||
|
|
2da28ede64 | ||
|
|
a7ac55795d | ||
|
|
835ef46aef | ||
|
|
3de10fe98b | ||
|
|
7ad70d04a4 | ||
|
|
e210062365 | ||
|
|
f9fcdc5292 | ||
|
|
0ea732780d | ||
|
|
33d922b8c2 | ||
|
|
114c2bcb15 | ||
|
|
6dbda2795a | ||
|
|
6a80d0ec09 | ||
|
|
0e8f20e1cc | ||
|
|
af11be16dc | ||
|
|
e332dbba8e | ||
|
|
746d915bf9 | ||
|
|
c896cf823e | ||
|
|
c2959466fd | ||
|
|
121c76f3d8 | ||
|
|
22cb16f831 | ||
|
|
57e18e337f | ||
|
|
e1d1ac4a4d | ||
|
|
64fe59382f | ||
|
|
a2e2853241 | ||
|
|
598d32287d | ||
|
|
53d8cfa360 | ||
|
|
1309ad9326 | ||
|
|
411d439d78 | ||
|
|
ec9428d63e | ||
|
|
2a4b28c35e | ||
|
|
4f060c0047 | ||
|
|
1ed3a4c9d5 | ||
|
|
f414288c9b | ||
|
|
72cd2ce03e | ||
|
|
9137af8e3f | ||
|
|
cdfe3d0ec9 | ||
|
|
4dfab06a50 | ||
|
|
47649171fc | ||
|
|
0bd79fa440 | ||
|
|
26d055a67b | ||
|
|
5db1adb5ea | ||
|
|
f86b5435bb | ||
|
|
31f0e24588 | ||
|
|
04d0b15545 | ||
|
|
f14222e77d | ||
|
|
7fabddd7f1 | ||
|
|
5039dfbe68 | ||
|
|
f9f81b9b8e | ||
|
|
3cc5d67696 | ||
|
|
3f960f9fc5 | ||
|
|
489853f200 | ||
|
|
2cf2ce4dfa | ||
|
|
96e1a765f9 | ||
|
|
04982c25c0 | ||
|
|
a3ffa9ed36 | ||
|
|
e7a22482db | ||
|
|
5f29808b80 | ||
|
|
063b21f999 | ||
|
|
89b8641955 | ||
|
|
6397c720e8 | ||
|
|
50c1e5aa3f | ||
|
|
970d02773f | ||
|
|
625749a546 | ||
|
|
d75042dc4c | ||
|
|
296ac88842 | ||
|
|
48e863d1d0 | ||
|
|
7b915f9cac | ||
|
|
e330c9f52a | ||
|
|
9470b1d6d3 | ||
|
|
388620b182 | ||
|
|
72e45177f3 | ||
|
|
90b82621f2 | ||
|
|
ff375abb0e | ||
|
|
cfcb7ed852 | ||
|
|
b245be464f | ||
|
|
139003d4c7 | ||
|
|
c47eacd010 | ||
|
|
6b01f12602 | ||
|
|
7169836621 | ||
|
|
c84d227242 | ||
|
|
4f93c6022c | ||
|
|
81f6b6be53 | ||
|
|
fa9f89700f | ||
|
|
9023b6ae13 | ||
|
|
df23e15c91 | ||
|
|
51e5e7b851 | ||
|
|
8e52bdcc5c | ||
|
|
1332c6544b | ||
|
|
703ed2628d | ||
|
|
f3943a7888 | ||
|
|
737454281a | ||
|
|
1aef42bb21 | ||
|
|
9550299e4d | ||
|
|
056dd908b3 | ||
|
|
62f239ccc9 | ||
|
|
f8879e1f25 | ||
|
|
994226e4b1 | ||
|
|
2cc6c1f68f | ||
|
|
8e8cf35b19 | ||
|
|
66de35b6d8 | ||
|
|
43e00bbe1d | ||
|
|
f31b682625 | ||
|
|
7dc420c8ff | ||
|
|
df0a9ebe72 | ||
|
|
66feb9d50b | ||
|
|
0b14b0b73f | ||
|
|
50706c6f09 | ||
|
|
e957f0154e | ||
|
|
b10c72fd78 | ||
|
|
496afcb40e | ||
|
|
9467b10e5c | ||
|
|
13c5f7b0b4 | ||
|
|
435544f7a1 | ||
|
|
b64712f657 | ||
|
|
d23c0900c1 | ||
|
|
a0a3080da2 | ||
|
|
0fad84b46d | ||
|
|
1b40044a23 | ||
|
|
a671263398 | ||
|
|
ca1cc7096a | ||
|
|
a5aabd8fac | ||
|
|
6d0e57ea88 | ||
|
|
1d04c1e903 | ||
|
|
042c26b3bb | ||
|
|
b4a125a759 | ||
|
|
8ecf80ff8f | ||
|
|
6786b9b6b1 | ||
|
|
86ffc69b30 | ||
|
|
5d4df6ffd9 | ||
|
|
eac2aa4ac1 | ||
|
|
8cbd52aa72 | ||
|
|
99b853a976 | ||
|
|
15bc1a4027 | ||
|
|
75e32e1b71 | ||
|
|
7ebc4044f9 | ||
|
|
1836ba928e | ||
|
|
c54c13c146 | ||
|
|
17a3743cd8 | ||
|
|
4a5a260c28 | ||
|
|
4bcc1e88c4 | ||
|
|
4cba5931f4 | ||
|
|
89458a2c93 | ||
|
|
c91a42f53d | ||
|
|
470a070d96 | ||
|
|
36537c981c | ||
|
|
c85978a4e3 | ||
|
|
d0062a55f8 | ||
|
|
66ea3ad0d1 | ||
|
|
3ad443dc2a | ||
|
|
da16ee277c | ||
|
|
85acdc7121 | ||
|
|
a2518e99f2 | ||
|
|
0f51ddd0bd | ||
|
|
aaa72e769f | ||
|
|
0e8fdb381a | ||
|
|
0b31c40c2b | ||
|
|
2b3df4e901 | ||
|
|
807f01ba72 | ||
|
|
03f18451f7 | ||
|
|
16c3d418eb | ||
|
|
55df5fe04c | ||
|
|
b0ba3b5dd8 | ||
|
|
05d21c0a14 | ||
|
|
26a703c68c | ||
|
|
6c3dd265ab | ||
|
|
c04e207821 | ||
|
|
67e0bf02e2 | ||
|
|
3bb7cde322 | ||
|
|
fa29a27695 | ||
|
|
7302dc8bd7 | ||
|
|
bb7383a301 | ||
|
|
61a69d8536 | ||
|
|
5a11fcea5b | ||
|
|
05775f0bfa | ||
|
|
973a1391d7 | ||
|
|
5d45649096 | ||
|
|
e91c4417cb | ||
|
|
ff3e6feeb6 | ||
|
|
0e2a83bc7b | ||
|
|
c84dbd02e5 | ||
|
|
c0aad5effa | ||
|
|
e0234dfc00 | ||
|
|
e30eb3adaf | ||
|
|
7c9385beab | ||
|
|
4c6601d778 | ||
|
|
e3a951b33b | ||
|
|
97192602d8 | ||
|
|
604957b3e2 | ||
|
|
bb731e7890 | ||
|
|
c81d13395b | ||
|
|
9b0fed975c | ||
|
|
18bc0a75b8 | ||
|
|
f1a43ee520 | ||
|
|
b73bca237d | ||
|
|
e7ff0d1898 | ||
|
|
880df2536d | ||
|
|
09b6929100 | ||
|
|
9afcf3acc9 | ||
|
|
5ca9313db8 | ||
|
|
feff1ee2c7 | ||
|
|
77e0deee76 | ||
|
|
d7cd7096d0 | ||
|
|
5d1b66dde3 | ||
|
|
96364dc189 | ||
|
|
a816818e3f | ||
|
|
61ae1fc83a | ||
|
|
eb9c0dfd93 | ||
|
|
79a09e61fa | ||
|
|
9978a75651 | ||
|
|
bd16d13531 | ||
|
|
f00d729264 | ||
|
|
a4ddc2a060 | ||
|
|
a938c8b708 | ||
|
|
aa7a484c7a | ||
|
|
915be8c279 | ||
|
|
87feadd451 | ||
|
|
419ab89ee9 | ||
|
|
fcdb582893 | ||
|
|
5e85565208 | ||
|
|
2d876b8d8e | ||
|
|
4feca3c5b7 | ||
|
|
f8e119c26b | ||
|
|
edddbedd4b | ||
|
|
baa4f7aa4c | ||
|
|
de3c257f79 | ||
|
|
b027e1d1bf | ||
|
|
0a5c496a3a | ||
|
|
5b32fecc0a | ||
|
|
92a4db9886 | ||
|
|
cc785e3225 | ||
|
|
be0690e587 | ||
|
|
69e39e36b0 | ||
|
|
b2f33fd4d2 | ||
|
|
200199c5d4 | ||
|
|
1465d6f91d | ||
|
|
373aed13c8 | ||
|
|
b23620ede3 | ||
|
|
da87afc8d4 | ||
|
|
626a280d27 | ||
|
|
4b267c2e1e | ||
|
|
cf18f95a92 | ||
|
|
dac0f26e5d | ||
|
|
13993891ec | ||
|
|
849c2a0f50 | ||
|
|
172ac615a8 | ||
|
|
30031d8e95 | ||
|
|
b2de5defa4 | ||
|
|
177c7548ae | ||
|
|
b9bfc36e6f | ||
|
|
980c529107 | ||
|
|
5156ca13b7 | ||
|
|
d08a3690b0 | ||
|
|
e8ed77241c | ||
|
|
82dcc44a2d | ||
|
|
93a8e63233 | ||
|
|
4ffaa62855 | ||
|
|
f9530ee82a | ||
|
|
d66c6d7f9c | ||
|
|
458a3d2d24 | ||
|
|
6f4c19c006 | ||
|
|
a31cf3d6f8 | ||
|
|
3fb0631953 | ||
|
|
028d0cf609 | ||
|
|
6824d5662a | ||
|
|
1bc8726046 | ||
|
|
05c8c130b6 | ||
|
|
5051a13711 | ||
|
|
103116e410 | ||
|
|
d973238596 | ||
|
|
4d664c4aa4 | ||
|
|
bd4096b6be | ||
|
|
f92d93772c | ||
|
|
b963b2b848 | ||
|
|
9dadb51f11 | ||
|
|
23cf22d87d | ||
|
|
d2d50e21e1 | ||
|
|
8c38fdb36d | ||
|
|
4fe1c11250 | ||
|
|
394ab8dfd1 | ||
|
|
5295e0bdc3 | ||
|
|
c638dbe55a | ||
|
|
6ac6deba10 | ||
|
|
d2ce76fe89 | ||
|
|
0b27ec5443 | ||
|
|
868d33ebd2 | ||
|
|
9fab1e0823 | ||
|
|
242bc7b322 | ||
|
|
e5c25f55fe | ||
|
|
33f88344c3 | ||
|
|
103d937152 | ||
|
|
e721dedf1e | ||
|
|
176e067ab9 | ||
|
|
2057ef5206 | ||
|
|
5c2659deaa | ||
|
|
4f9d5a8c70 | ||
|
|
8ecd8b67f4 | ||
|
|
22c40e1d96 | ||
|
|
a720ceeb54 | ||
|
|
f8d082e2dd | ||
|
|
507a4bf64c | ||
|
|
7a75db2aeb | ||
|
|
ce0aebce48 | ||
|
|
906de3ad11 | ||
|
|
bf5ad25609 | ||
|
|
f19009b132 | ||
|
|
526a53cd8c | ||
|
|
57730d7f81 | ||
|
|
f936bc6fe8 | ||
|
|
3f1f1d1586 | ||
|
|
7940025095 | ||
|
|
63847ebe62 | ||
|
|
03f5056236 | ||
|
|
23ff29c8f1 | ||
|
|
1b0229ca6c | ||
|
|
ae6cbcd9e7 | ||
|
|
1c599138ad | ||
|
|
9170c398be | ||
|
|
04fd9838b5 | ||
|
|
8d340cbfba | ||
|
|
87c3b57ae4 | ||
|
|
7befef2f4a | ||
|
|
b2dcbb2df8 | ||
|
|
0a51b4c34e | ||
|
|
3b653dca6a | ||
|
|
046a1de51a | ||
|
|
ae58f8eb64 | ||
|
|
e4c351d5d0 | ||
|
|
0b0a59497f | ||
|
|
939b019592 | ||
|
|
3d54519dbf | ||
|
|
94f566aacc | ||
|
|
a765e863c0 | ||
|
|
9e552ba1ab | ||
|
|
b03fead62d | ||
|
|
2825c25607 | ||
|
|
5ed8eeda6f | ||
|
|
1aef1a39f2 | ||
|
|
a310bf0547 | ||
|
|
116b51bd8d | ||
|
|
c05e7bf873 | ||
|
|
c14ae56e2d | ||
|
|
32a71bfbf6 | ||
|
|
7435074c0f | ||
|
|
ec202d7fb9 | ||
|
|
6575cba982 | ||
|
|
a17bd9cc1d | ||
|
|
f7e5c2436c | ||
|
|
85816d5bec | ||
|
|
ebdba2bdb9 | ||
|
|
6d5b864cba | ||
|
|
c8a450b00d | ||
|
|
61ccd79c5f | ||
|
|
c55002534e | ||
|
|
8ca756ec22 | ||
|
|
d1c106b3b8 | ||
|
|
564260b2f1 | ||
|
|
4a85122483 | ||
|
|
bc889f3d26 | ||
|
|
95e76f387c | ||
|
|
4a5f985d02 | ||
|
|
bbe56f5082 | ||
|
|
4b0c3d88f3 | ||
|
|
c103ac91b3 | ||
|
|
ebd87584da | ||
|
|
a9a985182f | ||
|
|
db6fde90e0 | ||
|
|
74228c506e | ||
|
|
bcd43006ac | ||
|
|
cf616caef9 | ||
|
|
7ab6e51c2c | ||
|
|
7a549e96ba | ||
|
|
3e4d709225 | ||
|
|
031563e727 | ||
|
|
3f80248477 | ||
|
|
c84d81d91c | ||
|
|
ef65268b45 | ||
|
|
9d725b9479 | ||
|
|
b4647d8d1b | ||
|
|
f28a22802d | ||
|
|
8a60fc1ecb | ||
|
|
dfad6d8409 | ||
|
|
b94b8afb77 | ||
|
|
0f37bfa20d | ||
|
|
d6d03158d0 | ||
|
|
7c863671c9 | ||
|
|
2a6db2ab52 | ||
|
|
c8a345a301 | ||
|
|
37efcb3d4f | ||
|
|
ff7a9fdd4e | ||
|
|
9ccbb842bf | ||
|
|
73d3bd5778 | ||
|
|
f785a5260a | ||
|
|
03716daca0 | ||
|
|
9987140790 | ||
|
|
6b00f7d27a | ||
|
|
98fffb9b77 | ||
|
|
000da1889f | ||
|
|
0a634b1223 | ||
|
|
5456f403a1 | ||
|
|
461182bf60 | ||
|
|
580ba0b536 | ||
|
|
5e3c4c91bb | ||
|
|
3875da80e7 | ||
|
|
6af252f315 | ||
|
|
e46ef4f9ce | ||
|
|
3bae422a8e | ||
|
|
d7526891c5 | ||
|
|
6563ee2fb2 | ||
|
|
c0952d73d1 | ||
|
|
4d780389bf | ||
|
|
5c9abf2eeb | ||
|
|
11997ca017 | ||
|
|
ed36d13c8f | ||
|
|
119eea161e | ||
|
|
d67809eda6 | ||
|
|
428d36da90 | ||
|
|
328e88856a | ||
|
|
99966e20ad | ||
|
|
1fef7772cb | ||
|
|
f8ab4fe9bd | ||
|
|
c2e785cdc4 | ||
|
|
5f19cfcb49 | ||
|
|
5405395e1a | ||
|
|
3c95d1f604 | ||
|
|
da8023eabf | ||
|
|
0d55ba317d | ||
|
|
404dc3d99d | ||
|
|
4821d0e959 | ||
|
|
db3649827f | ||
|
|
5bec0d0780 | ||
|
|
110062bf65 | ||
|
|
f3c621307a | ||
|
|
03177a05e7 | ||
|
|
e93b08380d | ||
|
|
012b366143 | ||
|
|
fd8bc07b61 | ||
|
|
68106058d6 | ||
|
|
d62c945887 | ||
|
|
0aea9aef38 | ||
|
|
88409dccd2 | ||
|
|
f705656178 | ||
|
|
5c61ee6be8 | ||
|
|
a4af04fae2 | ||
|
|
6b28efc011 | ||
|
|
8c6289802f | ||
|
|
78d79d4124 | ||
|
|
76d2058075 | ||
|
|
dcd8142c51 | ||
|
|
88c530d9e6 | ||
|
|
61d5dd6eb3 | ||
|
|
9eb6bc1a60 | ||
|
|
2098146af8 | ||
|
|
ef32988827 | ||
|
|
ce05dec2e6 | ||
|
|
895bed1f70 | ||
|
|
dbea23f171 | ||
|
|
ead9747f95 | ||
|
|
5fe665a6d3 | ||
|
|
3515b16222 | ||
|
|
a5c162a75e | ||
|
|
b5a56487b1 | ||
|
|
d151c100db | ||
|
|
56ced0d939 | ||
|
|
b6cb23d876 | ||
|
|
20a0a875f3 | ||
|
|
d6c1bea80a | ||
|
|
6d4ec5d8a1 | ||
|
|
474dce0141 | ||
|
|
5adbe6d0d5 | ||
|
|
28adbef81b | ||
|
|
c86d53d9d4 | ||
|
|
eb33bdde5a | ||
|
|
0a89469ffc | ||
|
|
bdd4f395ad | ||
|
|
4ae2999e95 | ||
|
|
077bb3b366 | ||
|
|
b0ca829cf6 | ||
|
|
c11ff6bf20 | ||
|
|
e1b99f6ee4 | ||
|
|
4c792ff668 | ||
|
|
260dca9664 | ||
|
|
91a15a5782 | ||
|
|
03f15d8689 | ||
|
|
86bf20b06b | ||
|
|
41ffe6d2eb | ||
|
|
41946ff0aa | ||
|
|
0ca06e2219 | ||
|
|
59ed3f07c8 | ||
|
|
e20630a425 | ||
|
|
ec8a2bc9ff | ||
|
|
b21c1f21a4 | ||
|
|
01b3dcc689 | ||
|
|
23f27cf496 | ||
|
|
9410e724b8 | ||
|
|
6f7b95a15c | ||
|
|
d5c2fe9280 | ||
|
|
450005d611 | ||
|
|
97a72f5b7f | ||
|
|
ce6d03ae13 | ||
|
|
23f7b6a750 | ||
|
|
36a4404c5c | ||
|
|
66241f2f1b | ||
|
|
e5aab0f165 | ||
|
|
14a4dc2bf9 | ||
|
|
42408d5a9a | ||
|
|
55cb3945df | ||
|
|
a4a95ba5d0 | ||
|
|
8a1ac05df2 | ||
|
|
78ae7f3b33 | ||
|
|
c783d6d101 | ||
|
|
3ea95ce53d | ||
|
|
16d978cea2 | ||
|
|
e43c992249 | ||
|
|
e78fc5b701 | ||
|
|
eebfc56013 | ||
|
|
1d4bd4e409 | ||
|
|
9f28d2f69a | ||
|
|
9c01a6328a | ||
|
|
46528127dc | ||
|
|
b34d3c3dbd | ||
|
|
2bc99deec2 | ||
|
|
a6b6219c6d | ||
|
|
caaf090b10 | ||
|
|
01b7641bd1 | ||
|
|
b42ddbbf42 | ||
|
|
f32b8dc4e8 | ||
|
|
9e6a4ca6df | ||
|
|
91e5d588b0 | ||
|
|
555c308749 | ||
|
|
7b88e883f9 | ||
|
|
e7b52023c6 | ||
|
|
e7a40343b1 | ||
|
|
dfc262bbcd | ||
|
|
615c705044 | ||
|
|
c59c32b350 | ||
|
|
d69df42de4 | ||
|
|
99dafe6327 | ||
|
|
5da3c9352d | ||
|
|
fdc15b355f | ||
|
|
4f49f65a1c | ||
|
|
d99e21f0f9 | ||
|
|
9b5bbee265 | ||
|
|
b5ca5e3839 | ||
|
|
b988750e78 | ||
|
|
78a51d416d | ||
|
|
3a539e1243 | ||
|
|
db133c819e | ||
|
|
02d5218baa | ||
|
|
394d0cbc6b | ||
|
|
b4456aed28 | ||
|
|
9e56268156 | ||
|
|
94e65ba6cc | ||
|
|
2ec02e53e3 | ||
|
|
c6e9227aee | ||
|
|
e1d1faea82 | ||
|
|
4871ad1210 | ||
|
|
7b03336469 | ||
|
|
b846e5a7f3 | ||
|
|
ee1d512132 | ||
|
|
628665175c | ||
|
|
a921df7335 | ||
|
|
6bbc716e4b | ||
|
|
cf448158cb | ||
|
|
6a6688ea8d | ||
|
|
c86ba51523 | ||
|
|
a8eae44209 | ||
|
|
efa407a579 | ||
|
|
b54dc34215 | ||
|
|
167296200d | ||
|
|
ca6601ca6d | ||
|
|
8d7b4ef84d | ||
|
|
2279162db5 | ||
|
|
9cfbdebc7b | ||
|
|
7583446380 | ||
|
|
94b4fe3fd6 | ||
|
|
d86b10fc1d | ||
|
|
7996a45125 | ||
|
|
655e7f8218 | ||
|
|
c2a3f83a9f | ||
|
|
0505e76422 | ||
|
|
265828b127 | ||
|
|
4134a656b0 | ||
|
|
cd0bd6e5c4 | ||
|
|
418ce895cb | ||
|
|
8a9510b48a | ||
|
|
b22ada3fa2 | ||
|
|
b4ac4838fd | ||
|
|
89000729d2 | ||
|
|
abea72b4d5 | ||
|
|
76c3e5786c | ||
|
|
793407fe16 | ||
|
|
990bf2a16a | ||
|
|
ea37f67b91 | ||
|
|
a805652f48 | ||
|
|
eb4cd221b2 | ||
|
|
a244b86f36 | ||
|
|
56a12a61c2 | ||
|
|
595f38b796 | ||
|
|
d6b7796c25 | ||
|
|
70e1fd4264 | ||
|
|
00bc8be152 | ||
|
|
cce2cc3cfc | ||
|
|
1f5a4f1ad7 | ||
|
|
a60059bc90 | ||
|
|
6c6b6c19fc | ||
|
|
5b1075f0f7 | ||
|
|
17007dd1e4 | ||
|
|
8f317fd332 | ||
|
|
1cee9e19c0 | ||
|
|
5b313b7cec | ||
|
|
8b52d372eb | ||
|
|
f4637585f6 | ||
|
|
3e03fcff8e | ||
|
|
74682e7ac0 | ||
|
|
20cf355e7e | ||
|
|
04ca97e5f8 | ||
|
|
669acb99e8 | ||
|
|
ba81fa7396 | ||
|
|
8540964097 | ||
|
|
9d1ebc0f8f | ||
|
|
786e57d043 | ||
|
|
902b079de2 | ||
|
|
54f54ab518 | ||
|
|
f8d767913e | ||
|
|
266db8bedb | ||
|
|
6c4496e034 | ||
|
|
8db83708ca | ||
|
|
7f45a0da83 | ||
|
|
ce03ad4ab7 | ||
|
|
ae9e35ccad | ||
|
|
9e5234539e | ||
|
|
dea7c714c0 | ||
|
|
4690186812 | ||
|
|
399b6e1d85 | ||
|
|
c2ed9d4ae7 | ||
|
|
1dc23a4aa6 | ||
|
|
61b05dd5af | ||
|
|
00a7f15011 | ||
|
|
8a5d5bc286 | ||
|
|
c80c6a7797 | ||
|
|
96c214c359 | ||
|
|
6d5f098e41 | ||
|
|
e53aec348e | ||
|
|
9722c85585 | ||
|
|
1a9c0d3ee7 | ||
|
|
7b68017411 | ||
|
|
a2cf35629b | ||
|
|
2506690e98 | ||
|
|
7e15d1ac70 | ||
|
|
0fb75f974e | ||
|
|
99ac6146d9 | ||
|
|
6161d298f7 | ||
|
|
7f181133c7 | ||
|
|
1d6ca99df9 | ||
|
|
baed701a6c | ||
|
|
8b31a53e4e | ||
|
|
f6f1198873 | ||
|
|
4b17bcd991 | ||
|
|
852785b616 | ||
|
|
1034670ac4 | ||
|
|
ff8bf96abb | ||
|
|
edc53fc347 | ||
|
|
3760404af9 | ||
|
|
68dfdf0c21 | ||
|
|
5bd01645fa | ||
|
|
f31cceac18 | ||
|
|
1b48c4de1c | ||
|
|
a6ed1e9667 | ||
|
|
c7eb072d2f | ||
|
|
caa73c6c4a | ||
|
|
9386fb2e11 | ||
|
|
9c9b759db5 | ||
|
|
cfb6e31e2f | ||
|
|
4f6f519dc7 | ||
|
|
fe8e8329ad | ||
|
|
38bcf1f4c4 | ||
|
|
6a1c06576b | ||
|
|
27b3773f00 | ||
|
|
871c328914 | ||
|
|
fa251afdcb | ||
|
|
27618658a9 | ||
|
|
bb42bdc422 | ||
|
|
f15fcc17ec | ||
|
|
59a9b3018f | ||
|
|
07b7801821 | ||
|
|
3d430cbe44 | ||
|
|
fd27ffa464 | ||
|
|
42517d691e | ||
|
|
53f51820fa | ||
|
|
61a86b7f06 | ||
|
|
696964a3a4 | ||
|
|
23228a0027 | ||
|
|
b4bef6e7c3 | ||
|
|
d2548f9e84 | ||
|
|
4a01e9b3a1 | ||
|
|
8a245e7313 | ||
|
|
c132a58d2e | ||
|
|
9cf91ddc7f | ||
|
|
016ed333ec | ||
|
|
453fb33b76 | ||
|
|
9c1410eaae | ||
|
|
5d1a54eee5 | ||
|
|
4f516a827f | ||
|
|
acb2004843 | ||
|
|
0f43670066 | ||
|
|
ab3cdbc277 | ||
|
|
9efe38d825 | ||
|
|
132cde0864 | ||
|
|
bc969ff5e5 | ||
|
|
ee3f2a0e6d | ||
|
|
421031323d | ||
|
|
6118653053 | ||
|
|
1a5bc9cf8c | ||
|
|
06ef230ad5 | ||
|
|
9d4779e4c3 | ||
|
|
d5531177f2 | ||
|
|
f2c50083d8 | ||
|
|
838d813ab9 | ||
|
|
dffc6f0a5a | ||
|
|
7a042c10ad | ||
|
|
0c44b8ca25 | ||
|
|
6f29fe62b1 | ||
|
|
937c0b8b97 | ||
|
|
941d77bd51 | ||
|
|
6a0fa19c07 | ||
|
|
a14ebca9e0 | ||
|
|
61b4093aec | ||
|
|
555197b0a2 | ||
|
|
bb6a554919 | ||
|
|
c0941c4856 | ||
|
|
e93b264d9c | ||
|
|
fcdbb8cda0 | ||
|
|
a4ff8920e5 | ||
|
|
db1c01f4dc | ||
|
|
ab50521f74 | ||
|
|
5843ca0e3d | ||
|
|
6b0868b8f2 | ||
|
|
a49871cd8b | ||
|
|
78f5d34373 | ||
|
|
9bcaba0419 | ||
|
|
a25b634366 | ||
|
|
4039f576ae | ||
|
|
03aa405b41 | ||
|
|
546243eec6 | ||
|
|
a033ee66fd | ||
|
|
70dfb0964e | ||
|
|
ab09848afd | ||
|
|
ed7b57d618 | ||
|
|
e8010137f7 | ||
|
|
c2d5c31db1 | ||
|
|
5f2375e99e | ||
|
|
fd56870d22 | ||
|
|
5bc21fa192 | ||
|
|
33293258f3 | ||
|
|
aee9cac546 | ||
|
|
c9b553bc2e | ||
|
|
12dc9183da | ||
|
|
8c38341c17 | ||
|
|
418b63b9ca | ||
|
|
e697a91710 | ||
|
|
e04057a5b5 | ||
|
|
e17d771d41 | ||
|
|
38516074dd | ||
|
|
3814d4243c | ||
|
|
bcbfb66131 | ||
|
|
9560c77358 | ||
|
|
785cb5584b | ||
|
|
917b303a0f | ||
|
|
80d659d7d0 | ||
|
|
90111dc43d | ||
|
|
9110f5404f | ||
|
|
df1210eaea | ||
|
|
a9b761d1dc | ||
|
|
8c970b26db | ||
|
|
d89b68ff76 | ||
|
|
7fafd762d2 | ||
|
|
a6affb765e | ||
|
|
2f9eb3523d | ||
|
|
3b5e1afb54 | ||
|
|
8b019d49fd | ||
|
|
d92a00ae6e | ||
|
|
b83d13fe3a | ||
|
|
bc6c650973 | ||
|
|
1bb474dfef | ||
|
|
18ec4bafb6 | ||
|
|
b2c490e0bd | ||
|
|
4221ac45b1 | ||
|
|
ec5100bf6f | ||
|
|
ac4249bbb1 | ||
|
|
f3a3132a6c | ||
|
|
4bc0f36b49 | ||
|
|
9d1fbcfc73 | ||
|
|
609c2ead53 | ||
|
|
37daa04a6c | ||
|
|
20a4cf0378 | ||
|
|
693f517aec | ||
|
|
4549027e95 | ||
|
|
b3a1961279 | ||
|
|
afae6e552c | ||
|
|
5ee6a0d6db | ||
|
|
49e0d13967 | ||
|
|
53067d36d0 | ||
|
|
a5568efd51 | ||
|
|
d4d1f8282b | ||
|
|
79b0b141a2 | ||
|
|
850978a954 | ||
|
|
f97e53f1eb | ||
|
|
dfd2a4db38 | ||
|
|
eab91cca3a | ||
|
|
1e7c06de45 | ||
|
|
b15933bf8a | ||
|
|
df5ab3de60 | ||
|
|
9a7b07027a | ||
|
|
8b13a3c8a6 | ||
|
|
1ce71bcd62 | ||
|
|
ed13addd3d | ||
|
|
3e937a71d9 | ||
|
|
a59da0ebd2 | ||
|
|
33f4d35e2b | ||
|
|
048888f45b | ||
|
|
57fde3afcc | ||
|
|
8777c2b29c | ||
|
|
8330ed7dcf | ||
|
|
9f47e67630 | ||
|
|
d0f320a862 | ||
|
|
a05eaee855 | ||
|
|
4b5f496a0c | ||
|
|
fca53dc67e | ||
|
|
52fbd4f6ce | ||
|
|
d22c86e4d8 | ||
|
|
cc1b6d1367 | ||
|
|
2ff955631f | ||
|
|
f66e32fbb4 | ||
|
|
33527dd3e9 | ||
|
|
442dbe0b6a | ||
|
|
fc200c4cd7 | ||
|
|
7266f5c53b | ||
|
|
957d0d8d34 | ||
|
|
c69c573000 | ||
|
|
cb30a8d357 | ||
|
|
38f6d7d9d8 | ||
|
|
461e7c942e | ||
|
|
773cda818d | ||
|
|
625a6c1a79 | ||
|
|
414f3eb193 | ||
|
|
bef0d9cfd6 | ||
|
|
1a270642cd | ||
|
|
75eccb751e | ||
|
|
2c76d67098 | ||
|
|
0585d817cb | ||
|
|
5d0d2d0bd9 |
@@ -0,0 +1,3 @@
|
|||||||
|
.pio
|
||||||
|
.vscode
|
||||||
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
In order to build MMDVM, you need to tell the Arduino GUI to link with the
|
|
||||||
CMSIS DSP library, which is doesn't do by default.
|
|
||||||
|
|
||||||
My development is on Windows and so I can vouch for the following instructions.
|
|
||||||
|
|
||||||
For Arduino 1.6.3 with SAM 1.6.4
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
1. Go to the where the platform.txt file is located. On my Windows machine it's
|
|
||||||
in:
|
|
||||||
|
|
||||||
C:\Users\Jonathan\AppData\Roaming\Arduino15\packages\arduino\hardware\sam\1.6.4
|
|
||||||
|
|
||||||
On Mac OS X it's located in:
|
|
||||||
|
|
||||||
/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/sam
|
|
||||||
Or
|
|
||||||
~/Library/Arduino15/packages/arduino/hardware/sam/1.6.6
|
|
||||||
|
|
||||||
2. You'll need to open the file in a text editor and find the line:
|
|
||||||
|
|
||||||
## Combine gc-sections, archives, and objects
|
|
||||||
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} -mcpu={build.mcu} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -mthumb -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--warn-unresolved-symbols -Wl,--start-group "{build.path}/syscalls_sam3.c.o" {object_files} "{build.variant.path}/{build.variant_system_lib}" "{build.path}/{archive_file}" -Wl,--end-group -lm -gcc
|
|
||||||
|
|
||||||
In my version it's line 73.
|
|
||||||
|
|
||||||
3. Modify it to read as follows:
|
|
||||||
|
|
||||||
## Combine gc-sections, archives, and objects
|
|
||||||
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} -mcpu={build.mcu} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -mthumb -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--warn-unresolved-symbols -Wl,--start-group "{build.path}/syscalls_sam3.c.o" {object_files} "{build.variant.path}/{build.variant_system_lib}" "{build.system.path}/CMSIS/CMSIS/Lib/ARM/arm_cortexM3l_math.lib" "{build.path}/{archive_file}" -Wl,--end-group -lm -gcc
|
|
||||||
|
|
||||||
The change is near the end, and is the addition of:
|
|
||||||
|
|
||||||
"{build.system.path}/CMSIS/CMSIS/Lib/ARM/arm_cortexM3l_math.lib"
|
|
||||||
|
|
||||||
Which is the CMSIS AMR3 DSP library for little-endian operation.
|
|
||||||
|
|
||||||
4. Save the file and start up the Arduino GUI and build MMDVM.
|
|
||||||
|
|
||||||
I would like to get instructions for doing the same on a Linux platform. As a
|
|
||||||
starter find the relevent platform.txt and try adding:
|
|
||||||
|
|
||||||
"{build.system.path}/CMSIS/CMSIS/Lib/ARM/libarm_cortexM3l_math.a"
|
|
||||||
|
|
||||||
or maybe:
|
|
||||||
|
|
||||||
"{build.variant.path}/libarm_cortexM3l_math.a"
|
|
||||||
|
|
||||||
Likely on Linux
|
|
||||||
|
|
||||||
"{build.system.path}/CMSIS/CMSIS/Lib/GCC/libarm_cortexM3l_math.a"
|
|
||||||
|
|
||||||
As with Arduino 1.6.7 with SAM 1.6.6, see below.
|
|
||||||
|
|
||||||
|
|
||||||
For Arduino 1.6.7 with SAM 1.6.6
|
|
||||||
--------------------------------
|
|
||||||
(checked OK on Arduino 1.6.11 and SAM 1.6.9)
|
|
||||||
|
|
||||||
1. Go to the where the platform.txt file is located. On my Windows machine it's
|
|
||||||
in:
|
|
||||||
|
|
||||||
C:\Users\Jonathan\AppData\Local\Arduino15\packages\arduino\hardware\sam\1.6.6
|
|
||||||
|
|
||||||
On Mac OS X it's located in:
|
|
||||||
|
|
||||||
/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/sam
|
|
||||||
|
|
||||||
On Linux, it's located in my home directory, downloaded and extracted from Arduino website, but must be installed.
|
|
||||||
|
|
||||||
/home/m1geo/arduino-1.6.7/hardware/arduino/sam
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
/home/m1geo/.arduino15/packages/arduino/hardware/sam/1.6.9 (if you let the board/library manager install SAM)
|
|
||||||
|
|
||||||
|
|
||||||
I (M1GEO) found it was necessary to download SAM-1.6.6 outside of the Arduino IDE, and manually extract the files.
|
|
||||||
The Board Manager didn't seem to install the SAM files correctly. Here's how I did it:
|
|
||||||
|
|
||||||
a) wget http://downloads.arduino.cc/cores/sam-1.6.6.tar.bz2 -O /tmp/sam-1.6.6.tar.bz2 (download and save in /tmp)
|
|
||||||
b) cd arduino-1.6.7/hardware/arduino/ (Arduino root, here, in my home directory)
|
|
||||||
c) tar xvfj /tmp/sam-1.6.6.tar.bz2
|
|
||||||
|
|
||||||
2. You'll need to open the file (platform.txt) in a text editor and find the line:
|
|
||||||
|
|
||||||
## Combine gc-sections, archives, and objects
|
|
||||||
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" -mcpu={build.mcu} -mthumb {compiler.c.elf.flags} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--start-group "{build.path}/core/syscalls_sam3.c.o" {object_files} "{build.variant.path}/{build.variant_system_lib}" "{build.path}/{archive_file}" -Wl,--end-group -lm -gcc
|
|
||||||
|
|
||||||
In my version it's line 73.
|
|
||||||
|
|
||||||
3. Modify it to read as follows:
|
|
||||||
|
|
||||||
## Combine gc-sections, archives, and objects
|
|
||||||
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" -mcpu={build.mcu} -mthumb {compiler.c.elf.flags} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--start-group "{build.path}/core/syscalls_sam3.c.o" {object_files} "{build.variant.path}/{build.variant_system_lib}" "{build.system.path}/CMSIS/CMSIS/Lib/ARM/arm_cortexM3l_math.lib" "{build.path}/{archive_file}" -Wl,--end-group -lm -gcc
|
|
||||||
|
|
||||||
The change is near the end, and is the addition of:
|
|
||||||
|
|
||||||
"{build.system.path}/CMSIS/CMSIS/Lib/ARM/arm_cortexM3l_math.lib"
|
|
||||||
|
|
||||||
On Linux, the path was found to differ slightly (GCC instead of ARM):
|
|
||||||
|
|
||||||
"{build.system.path}/CMSIS/CMSIS/Lib/GCC/libarm_cortexM3l_math.a"
|
|
||||||
|
|
||||||
Which is the CMSIS AMR3 DSP library for little-endian operation.
|
|
||||||
|
|
||||||
4. Save the file and start up the Arduino GUI and build MMDVM.
|
|
||||||
|
|
||||||
For Arduino 1.6.9 with SAM 1.6.8
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
1. Locate platform.txt. On Ubuntu 14.04 LTS x86_64 OS it is in:
|
|
||||||
|
|
||||||
/home/$user/.arduino15/packages/arduino/hardware/sam/1.6.8/
|
|
||||||
|
|
||||||
2. Open the file in a text editor and change the line:
|
|
||||||
|
|
||||||
## Combine gc-sections, archives, and objects
|
|
||||||
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" -mcpu={build.mcu} -mthumb {compiler.c.elf.flags} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--start-group {compiler.combine.flags} {object_files} "{build.variant.path}/{build.variant_system_lib}" "{build.path}/{archive_file}" -Wl,--end-group -lm -gcc
|
|
||||||
|
|
||||||
to
|
|
||||||
|
|
||||||
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" -mcpu={build.mcu} -mthumb {compiler.c.elf.flags} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--start-group {compiler.combine.flags} {object_files} "{build.variant.path}/{build.variant_system_lib}" "{build.system.path}/CMSIS/CMSIS/Lib/GCC/libarm_cortexM3l_math.a" "{build.path}/{archive_file}" -Wl,--end-group -lm -gcc
|
|
||||||
|
|
||||||
3. Save the file, open the Arduino IDE and build MMDVM
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015,2016 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(CONFIG_H)
|
|
||||||
#define CONFIG_H
|
|
||||||
|
|
||||||
// Allow for the use of high quality external clock oscillators
|
|
||||||
// The number is the frequency of the oscillator in Hertz.
|
|
||||||
//
|
|
||||||
// The frequency of the TCXO must be an integer multiple of 48000.
|
|
||||||
// Frequencies such as 12.0 Mhz (48000 * 250) and 14.4 Mhz (48000 * 300) are suitable.
|
|
||||||
// Frequencies such as 10.0 Mhz (48000 * 208.333) or 20 Mhz (48000 * 416.666) are not suitable.
|
|
||||||
//
|
|
||||||
// For 12 MHz
|
|
||||||
// #define EXTERNAL_OSC 12000000
|
|
||||||
// For 12.288 MHz
|
|
||||||
// #define EXTERNAL_OSC 12288000
|
|
||||||
// For 14.4 MHz
|
|
||||||
// #define EXTERNAL_OSC 14400000
|
|
||||||
// For 19.2 MHz
|
|
||||||
// #define EXTERNAL_OSC 19200000
|
|
||||||
|
|
||||||
// Allow the use of the COS line to lockout the modem
|
|
||||||
// #define USE_COS_AS_LOCKOUT
|
|
||||||
|
|
||||||
// Use pins to output the current mode
|
|
||||||
// #define ARDUINO_MODE_PINS
|
|
||||||
|
|
||||||
// For the original Arduino Due pin layout
|
|
||||||
// #define ARDUINO_DUE_PAPA
|
|
||||||
|
|
||||||
// For the ZUM V1.0 and V1.0.1 boards pin layout
|
|
||||||
#define ARDUINO_DUE_ZUM_V10
|
|
||||||
|
|
||||||
// For the SP8NTH board
|
|
||||||
// #define ARDUINO_DUE_NTH
|
|
||||||
|
|
||||||
// To use wider C4FSK filters for DMR, System Fusion and P25 on transmit
|
|
||||||
// #define WIDE_C4FSK_FILTERS_TX
|
|
||||||
// To use wider C4FSK filters for DMR, System Fusion and P25 on receive
|
|
||||||
// #define WIDE_C4FSK_FILTERS_RX
|
|
||||||
|
|
||||||
// Pass RSSI information to the host
|
|
||||||
// #define SEND_RSSI_DATA
|
|
||||||
|
|
||||||
// Use the modem as a serial repeater for Nextion displays
|
|
||||||
// #define SERIAL_REPEATER
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
-170
@@ -1,170 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
|
||||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
|
||||||
*
|
|
||||||
* 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// #define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
#include "DMRSlotType.h"
|
|
||||||
|
|
||||||
#if defined(WIDE_C4FSK_FILTERS_TX)
|
|
||||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
|
||||||
static q15_t DMR_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
|
||||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
|
||||||
const uint16_t DMR_C4FSK_FILTER_LEN = 22U;
|
|
||||||
#else
|
|
||||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
|
||||||
static q15_t DMR_C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
|
||||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
|
||||||
-553, -847, -731, -340, 104, 401, 0};
|
|
||||||
const uint16_t DMR_C4FSK_FILTER_LEN = 42U;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const q15_t DMR_LEVELA[] = { 640, 640 , 640, 640, 640};
|
|
||||||
const q15_t DMR_LEVELB[] = { 213, 213, 213, 213, 213};
|
|
||||||
const q15_t DMR_LEVELC[] = {-213, -213, -213, -213, -213};
|
|
||||||
const q15_t DMR_LEVELD[] = {-640, -640, -640, -640, -640};
|
|
||||||
|
|
||||||
|
|
||||||
CDMRDMOTX::CDMRDMOTX() :
|
|
||||||
m_fifo(),
|
|
||||||
m_modFilter(),
|
|
||||||
m_modState(),
|
|
||||||
m_poBuffer(),
|
|
||||||
m_poLen(0U),
|
|
||||||
m_poPtr(0U),
|
|
||||||
m_txDelay(240U), // 200ms
|
|
||||||
m_count(0U)
|
|
||||||
{
|
|
||||||
::memset(m_modState, 0x00U, 70U * sizeof(q15_t));
|
|
||||||
|
|
||||||
m_modFilter.numTaps = DMR_C4FSK_FILTER_LEN;
|
|
||||||
m_modFilter.pState = m_modState;
|
|
||||||
m_modFilter.pCoeffs = DMR_C4FSK_FILTER;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDMRDMOTX::process()
|
|
||||||
{
|
|
||||||
if (m_poLen == 0U && m_fifo.getData() > 0U) {
|
|
||||||
if (!m_tx) {
|
|
||||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
|
||||||
m_poBuffer[m_poLen++] = 0x00U;
|
|
||||||
} else {
|
|
||||||
for (unsigned int i = 0U; i < 72U; i++)
|
|
||||||
m_poBuffer[m_poLen++] = 0x00U;
|
|
||||||
|
|
||||||
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
|
|
||||||
m_poBuffer[i] = m_fifo.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_poPtr = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_poLen > 0U) {
|
|
||||||
uint16_t space = io.getSpace();
|
|
||||||
|
|
||||||
while (space > (4U * DMR_RADIO_SYMBOL_LENGTH)) {
|
|
||||||
uint8_t c = m_poBuffer[m_poPtr++];
|
|
||||||
|
|
||||||
writeByte(c);
|
|
||||||
|
|
||||||
space -= 4U * DMR_RADIO_SYMBOL_LENGTH;
|
|
||||||
|
|
||||||
if (m_poPtr >= m_poLen) {
|
|
||||||
m_poPtr = 0U;
|
|
||||||
m_poLen = 0U;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t CDMRDMOTX::writeData(const uint8_t* data, uint8_t length)
|
|
||||||
{
|
|
||||||
if (length != (DMR_FRAME_LENGTH_BYTES + 1U))
|
|
||||||
return 4U;
|
|
||||||
|
|
||||||
uint16_t space = m_fifo.getSpace();
|
|
||||||
if (space < DMR_FRAME_LENGTH_BYTES)
|
|
||||||
return 5U;
|
|
||||||
|
|
||||||
for (uint8_t i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
|
|
||||||
m_fifo.put(data[i + 1U]);
|
|
||||||
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDMRDMOTX::writeByte(uint8_t c)
|
|
||||||
{
|
|
||||||
q15_t inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
|
||||||
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
|
||||||
|
|
||||||
const uint8_t MASK = 0xC0U;
|
|
||||||
|
|
||||||
q15_t* p = inBuffer;
|
|
||||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += DMR_RADIO_SYMBOL_LENGTH) {
|
|
||||||
switch (c & MASK) {
|
|
||||||
case 0xC0U:
|
|
||||||
::memcpy(p, DMR_LEVELA, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
|
||||||
break;
|
|
||||||
case 0x80U:
|
|
||||||
::memcpy(p, DMR_LEVELB, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
|
||||||
break;
|
|
||||||
case 0x00U:
|
|
||||||
::memcpy(p, DMR_LEVELC, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
::memcpy(p, DMR_LEVELD, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t blockSize = DMR_RADIO_SYMBOL_LENGTH * 4U;
|
|
||||||
|
|
||||||
// Handle the case of the oscillator not being accurate enough
|
|
||||||
if (m_sampleCount > 0U) {
|
|
||||||
m_count += DMR_RADIO_SYMBOL_LENGTH * 4U;
|
|
||||||
|
|
||||||
if (m_count >= m_sampleCount) {
|
|
||||||
if (m_sampleInsert) {
|
|
||||||
inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U] = inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U - 1U];
|
|
||||||
blockSize++;
|
|
||||||
} else {
|
|
||||||
blockSize--;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_count -= m_sampleCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
|
|
||||||
|
|
||||||
io.write(STATE_DMR, outBuffer, blockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t CDMRDMOTX::getSpace() const
|
|
||||||
{
|
|
||||||
return m_fifo.getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDMRDMOTX::setTXDelay(uint8_t delay)
|
|
||||||
{
|
|
||||||
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009-2015 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(DSTARDEFINES_H)
|
|
||||||
#define DSTARDEFINES_H
|
|
||||||
|
|
||||||
const unsigned int DSTAR_RADIO_BIT_LENGTH = 5U; // At 24 kHz sample rate
|
|
||||||
|
|
||||||
const unsigned int DSTAR_HEADER_LENGTH_BYTES = 41U;
|
|
||||||
const unsigned int DSTAR_HEADER_LENGTH_BITS = DSTAR_HEADER_LENGTH_BYTES * 8U;
|
|
||||||
|
|
||||||
const unsigned int DSTAR_FEC_SECTION_LENGTH_BYTES = 83U;
|
|
||||||
const unsigned int DSTAR_FEC_SECTION_LENGTH_BITS = 660U;
|
|
||||||
|
|
||||||
const unsigned int DSTAR_DATA_LENGTH_BYTES = 12U;
|
|
||||||
const unsigned int DSTAR_DATA_LENGTH_BITS = DSTAR_DATA_LENGTH_BYTES * 8U;
|
|
||||||
|
|
||||||
const uint8_t DSTAR_EOT_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
||||||
const unsigned int DSTAR_EOT_LENGTH_BYTES = 6U;
|
|
||||||
const unsigned int DSTAR_EOT_LENGTH_BITS = DSTAR_EOT_LENGTH_BYTES * 8U;
|
|
||||||
|
|
||||||
const uint8_t DSTAR_DATA_SYNC_LENGTH_BYTES = 3U;
|
|
||||||
const uint8_t DSTAR_DATA_SYNC_LENGTH_BITS = DSTAR_DATA_SYNC_LENGTH_BYTES * 8U;
|
|
||||||
|
|
||||||
const uint8_t DSTAR_DATA_SYNC_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8, 0x55, 0x2D, 0x16};
|
|
||||||
|
|
||||||
const uint8_t DSTAR_SLOW_DATA_TYPE_TEXT = 0x40U;
|
|
||||||
const uint8_t DSTAR_SLOW_DATA_TYPE_HEADER = 0x50U;
|
|
||||||
|
|
||||||
const uint8_t DSTAR_SCRAMBLER_BYTES[] = {0x70U, 0x4FU, 0x93U};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015,2016 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(GLOBALS_H)
|
|
||||||
#define GLOBALS_H
|
|
||||||
|
|
||||||
#if defined(STM32F4XX) || defined(STM32F4)
|
|
||||||
#include "stm32f4xx.h"
|
|
||||||
#else
|
|
||||||
#include <Arduino.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__SAM3X8E__)
|
|
||||||
#define ARM_MATH_CM3
|
|
||||||
#elif defined(STM32F4XX) || defined(STM32F4) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
|
|
||||||
#define ARM_MATH_CM4
|
|
||||||
#else
|
|
||||||
#error "Unknown processor type"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <arm_math.h>
|
|
||||||
|
|
||||||
enum MMDVM_STATE {
|
|
||||||
STATE_IDLE = 0,
|
|
||||||
STATE_DSTAR = 1,
|
|
||||||
STATE_DMR = 2,
|
|
||||||
STATE_YSF = 3,
|
|
||||||
STATE_P25 = 4,
|
|
||||||
|
|
||||||
// Dummy states start at 90
|
|
||||||
STATE_CWID = 97,
|
|
||||||
STATE_DMRCAL = 98,
|
|
||||||
STATE_DSTARCAL = 99
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "SerialPort.h"
|
|
||||||
#include "DMRIdleRX.h"
|
|
||||||
#include "DMRDMORX.h"
|
|
||||||
#include "DMRDMOTX.h"
|
|
||||||
#include "DStarRX.h"
|
|
||||||
#include "DStarTX.h"
|
|
||||||
#include "DMRRX.h"
|
|
||||||
#include "DMRTX.h"
|
|
||||||
#include "YSFRX.h"
|
|
||||||
#include "YSFTX.h"
|
|
||||||
#include "P25RX.h"
|
|
||||||
#include "P25TX.h"
|
|
||||||
#include "CalDStarRX.h"
|
|
||||||
#include "CalDStarTX.h"
|
|
||||||
#include "CalDMR.h"
|
|
||||||
#include "CWIdTX.h"
|
|
||||||
#include "Debug.h"
|
|
||||||
#include "IO.h"
|
|
||||||
|
|
||||||
const uint8_t MARK_SLOT1 = 0x08U;
|
|
||||||
const uint8_t MARK_SLOT2 = 0x04U;
|
|
||||||
const uint8_t MARK_NONE = 0x00U;
|
|
||||||
|
|
||||||
const uint16_t RX_BLOCK_SIZE = 2U;
|
|
||||||
|
|
||||||
const uint16_t TX_RINGBUFFER_SIZE = 500U;
|
|
||||||
const uint16_t RX_RINGBUFFER_SIZE = 600U;
|
|
||||||
|
|
||||||
extern MMDVM_STATE m_modemState;
|
|
||||||
|
|
||||||
extern bool m_dstarEnable;
|
|
||||||
extern bool m_dmrEnable;
|
|
||||||
extern bool m_ysfEnable;
|
|
||||||
extern bool m_p25Enable;
|
|
||||||
|
|
||||||
extern bool m_duplex;
|
|
||||||
|
|
||||||
extern bool m_tx;
|
|
||||||
extern bool m_dcd;
|
|
||||||
|
|
||||||
extern uint32_t m_sampleCount;
|
|
||||||
extern bool m_sampleInsert;
|
|
||||||
|
|
||||||
extern CSerialPort serial;
|
|
||||||
extern CIO io;
|
|
||||||
|
|
||||||
extern CDStarRX dstarRX;
|
|
||||||
extern CDStarTX dstarTX;
|
|
||||||
|
|
||||||
extern CDMRIdleRX dmrIdleRX;
|
|
||||||
extern CDMRRX dmrRX;
|
|
||||||
extern CDMRTX dmrTX;
|
|
||||||
|
|
||||||
extern CDMRDMORX dmrDMORX;
|
|
||||||
extern CDMRDMOTX dmrDMOTX;
|
|
||||||
|
|
||||||
extern CYSFRX ysfRX;
|
|
||||||
extern CYSFTX ysfTX;
|
|
||||||
|
|
||||||
extern CP25RX p25RX;
|
|
||||||
extern CP25TX p25TX;
|
|
||||||
|
|
||||||
extern CCalDStarRX calDStarRX;
|
|
||||||
extern CCalDStarTX calDStarTX;
|
|
||||||
extern CCalDMR calDMR;
|
|
||||||
|
|
||||||
extern CCWIdTX cwIdTX;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,384 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
|
||||||
* Copyright (C) 2015 by Jim Mclaughlin KI6ZUM
|
|
||||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
|
||||||
*
|
|
||||||
* 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// #define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
#include "IO.h"
|
|
||||||
|
|
||||||
#if defined(WIDE_C4FSK_FILTERS_RX)
|
|
||||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
|
||||||
static q15_t C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
|
||||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
|
||||||
const uint16_t C4FSK_FILTER_LEN = 22U;
|
|
||||||
#else
|
|
||||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
|
||||||
static q15_t C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
|
||||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
|
||||||
-553, -847, -731, -340, 104, 401, 0};
|
|
||||||
const uint16_t C4FSK_FILTER_LEN = 42U;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Generated using gaussfir(0.5, 4, 5) in MATLAB
|
|
||||||
static q15_t GMSK_FILTER[] = {8, 104, 760, 3158, 7421, 9866, 7421, 3158, 760, 104, 8, 0};
|
|
||||||
const uint16_t GMSK_FILTER_LEN = 12U;
|
|
||||||
|
|
||||||
const uint16_t DC_OFFSET = 2048U;
|
|
||||||
|
|
||||||
CIO::CIO() :
|
|
||||||
m_started(false),
|
|
||||||
m_rxBuffer(RX_RINGBUFFER_SIZE),
|
|
||||||
m_txBuffer(TX_RINGBUFFER_SIZE),
|
|
||||||
m_rssiBuffer(RX_RINGBUFFER_SIZE),
|
|
||||||
m_C4FSKFilter(),
|
|
||||||
m_GMSKFilter(),
|
|
||||||
m_C4FSKState(),
|
|
||||||
m_GMSKState(),
|
|
||||||
m_pttInvert(false),
|
|
||||||
m_rxLevel(128 * 128),
|
|
||||||
m_cwIdTXLevel(128 * 128),
|
|
||||||
m_dstarTXLevel(128 * 128),
|
|
||||||
m_dmrTXLevel(128 * 128),
|
|
||||||
m_ysfTXLevel(128 * 128),
|
|
||||||
m_p25TXLevel(128 * 128),
|
|
||||||
m_ledCount(0U),
|
|
||||||
m_ledValue(true),
|
|
||||||
m_detect(false),
|
|
||||||
m_adcOverflow(0U),
|
|
||||||
m_dacOverflow(0U),
|
|
||||||
m_count(0U),
|
|
||||||
m_watchdog(0U),
|
|
||||||
m_lockout(false)
|
|
||||||
{
|
|
||||||
::memset(m_C4FSKState, 0x00U, 70U * sizeof(q15_t));
|
|
||||||
::memset(m_GMSKState, 0x00U, 40U * sizeof(q15_t));
|
|
||||||
|
|
||||||
m_C4FSKFilter.numTaps = C4FSK_FILTER_LEN;
|
|
||||||
m_C4FSKFilter.pState = m_C4FSKState;
|
|
||||||
m_C4FSKFilter.pCoeffs = C4FSK_FILTER;
|
|
||||||
|
|
||||||
m_GMSKFilter.numTaps = GMSK_FILTER_LEN;
|
|
||||||
m_GMSKFilter.pState = m_GMSKState;
|
|
||||||
m_GMSKFilter.pCoeffs = GMSK_FILTER;
|
|
||||||
|
|
||||||
initInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::start()
|
|
||||||
{
|
|
||||||
if (m_started)
|
|
||||||
return;
|
|
||||||
|
|
||||||
startInt();
|
|
||||||
|
|
||||||
m_count = 0U;
|
|
||||||
m_started = true;
|
|
||||||
|
|
||||||
setMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::process()
|
|
||||||
{
|
|
||||||
m_ledCount++;
|
|
||||||
if (m_started) {
|
|
||||||
// Two seconds timeout
|
|
||||||
if (m_watchdog >= 48000U) {
|
|
||||||
if (m_modemState == STATE_DSTAR || m_modemState == STATE_DMR || m_modemState == STATE_YSF) {
|
|
||||||
if (m_modemState == STATE_DMR && m_tx)
|
|
||||||
dmrTX.setStart(false);
|
|
||||||
m_modemState = STATE_IDLE;
|
|
||||||
setMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_watchdog = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_ledCount >= 24000U) {
|
|
||||||
m_ledCount = 0U;
|
|
||||||
m_ledValue = !m_ledValue;
|
|
||||||
setLEDInt(m_ledValue);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (m_ledCount >= 240000U) {
|
|
||||||
m_ledCount = 0U;
|
|
||||||
m_ledValue = !m_ledValue;
|
|
||||||
setLEDInt(m_ledValue);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(USE_COS_AS_LOCKOUT)
|
|
||||||
m_lockout = getCOSInt();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Switch off the transmitter if needed
|
|
||||||
if (m_txBuffer.getData() == 0U && m_tx) {
|
|
||||||
m_tx = false;
|
|
||||||
setPTTInt(m_pttInvert ? true : false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_rxBuffer.getData() >= RX_BLOCK_SIZE) {
|
|
||||||
q15_t samples[RX_BLOCK_SIZE + 1U];
|
|
||||||
uint8_t control[RX_BLOCK_SIZE + 1U];
|
|
||||||
uint16_t rssi[RX_BLOCK_SIZE + 1U];
|
|
||||||
|
|
||||||
uint8_t blockSize = RX_BLOCK_SIZE;
|
|
||||||
|
|
||||||
for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) {
|
|
||||||
uint16_t sample;
|
|
||||||
m_rxBuffer.get(sample, control[i]);
|
|
||||||
m_rssiBuffer.get(rssi[i]);
|
|
||||||
|
|
||||||
// Detect ADC overflow
|
|
||||||
if (m_detect && (sample == 0U || sample == 4095U))
|
|
||||||
m_adcOverflow++;
|
|
||||||
|
|
||||||
q15_t res1 = q15_t(sample) - DC_OFFSET;
|
|
||||||
q31_t res2 = res1 * m_rxLevel;
|
|
||||||
samples[i] = q15_t(__SSAT((res2 >> 15), 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the case of the oscillator not being accurate enough
|
|
||||||
if (m_sampleCount > 0U) {
|
|
||||||
m_count += RX_BLOCK_SIZE;
|
|
||||||
|
|
||||||
if (m_count >= m_sampleCount) {
|
|
||||||
if (m_sampleInsert) {
|
|
||||||
blockSize++;
|
|
||||||
samples[RX_BLOCK_SIZE] = 0;
|
|
||||||
for (int8_t i = RX_BLOCK_SIZE - 1; i >= 0; i--)
|
|
||||||
control[i + 1] = control[i];
|
|
||||||
} else {
|
|
||||||
blockSize--;
|
|
||||||
for (uint8_t i = 0U; i < (RX_BLOCK_SIZE - 1U); i++)
|
|
||||||
control[i] = control[i + 1U];
|
|
||||||
}
|
|
||||||
|
|
||||||
m_count -= m_sampleCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_lockout)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_modemState == STATE_IDLE) {
|
|
||||||
if (m_dstarEnable) {
|
|
||||||
q15_t GMSKVals[RX_BLOCK_SIZE + 1U];
|
|
||||||
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize);
|
|
||||||
|
|
||||||
dstarRX.samples(GMSKVals, blockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_dmrEnable || m_ysfEnable || m_p25Enable) {
|
|
||||||
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
|
|
||||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
|
|
||||||
|
|
||||||
if (m_dmrEnable) {
|
|
||||||
if (m_duplex)
|
|
||||||
dmrIdleRX.samples(C4FSKVals, blockSize);
|
|
||||||
else
|
|
||||||
dmrDMORX.samples(C4FSKVals, rssi, blockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_ysfEnable)
|
|
||||||
ysfRX.samples(C4FSKVals, blockSize);
|
|
||||||
|
|
||||||
if (m_p25Enable)
|
|
||||||
p25RX.samples(C4FSKVals, blockSize);
|
|
||||||
}
|
|
||||||
} else if (m_modemState == STATE_DSTAR) {
|
|
||||||
if (m_dstarEnable) {
|
|
||||||
q15_t GMSKVals[RX_BLOCK_SIZE + 1U];
|
|
||||||
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize);
|
|
||||||
|
|
||||||
dstarRX.samples(GMSKVals, blockSize);
|
|
||||||
}
|
|
||||||
} else if (m_modemState == STATE_DMR) {
|
|
||||||
if (m_dmrEnable) {
|
|
||||||
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
|
|
||||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
|
|
||||||
|
|
||||||
if (m_duplex) {
|
|
||||||
// If the transmitter isn't on, use the DMR idle RX to detect the wakeup CSBKs
|
|
||||||
if (m_tx)
|
|
||||||
dmrRX.samples(C4FSKVals, rssi, control, blockSize);
|
|
||||||
else
|
|
||||||
dmrIdleRX.samples(C4FSKVals, blockSize);
|
|
||||||
} else {
|
|
||||||
dmrDMORX.samples(C4FSKVals, rssi, blockSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (m_modemState == STATE_YSF) {
|
|
||||||
if (m_ysfEnable) {
|
|
||||||
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
|
|
||||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
|
|
||||||
|
|
||||||
ysfRX.samples(C4FSKVals, blockSize);
|
|
||||||
}
|
|
||||||
} else if (m_modemState == STATE_P25) {
|
|
||||||
if (m_p25Enable) {
|
|
||||||
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
|
|
||||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
|
|
||||||
|
|
||||||
p25RX.samples(C4FSKVals, blockSize);
|
|
||||||
}
|
|
||||||
} else if (m_modemState == STATE_DSTARCAL) {
|
|
||||||
q15_t GMSKVals[RX_BLOCK_SIZE + 1U];
|
|
||||||
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize);
|
|
||||||
|
|
||||||
calDStarRX.samples(GMSKVals, blockSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t* control)
|
|
||||||
{
|
|
||||||
if (!m_started)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_lockout)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Switch the transmitter on if needed
|
|
||||||
if (!m_tx) {
|
|
||||||
m_tx = true;
|
|
||||||
setPTTInt(m_pttInvert ? false : true);
|
|
||||||
}
|
|
||||||
|
|
||||||
q15_t txLevel = 0;
|
|
||||||
switch (mode) {
|
|
||||||
case STATE_DSTAR:
|
|
||||||
txLevel = m_dstarTXLevel;
|
|
||||||
break;
|
|
||||||
case STATE_DMR:
|
|
||||||
txLevel = m_dmrTXLevel;
|
|
||||||
break;
|
|
||||||
case STATE_YSF:
|
|
||||||
txLevel = m_ysfTXLevel;
|
|
||||||
break;
|
|
||||||
case STATE_P25:
|
|
||||||
txLevel = m_p25TXLevel;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
txLevel = m_cwIdTXLevel;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint16_t i = 0U; i < length; i++) {
|
|
||||||
q31_t res1 = samples[i] * txLevel;
|
|
||||||
q15_t res2 = q15_t(__SSAT((res1 >> 15), 16));
|
|
||||||
uint16_t res3 = uint16_t(res2 + DC_OFFSET);
|
|
||||||
|
|
||||||
// Detect DAC overflow
|
|
||||||
if (res3 > 4095U)
|
|
||||||
m_dacOverflow++;
|
|
||||||
|
|
||||||
if (control == NULL)
|
|
||||||
m_txBuffer.put(res3, MARK_NONE);
|
|
||||||
else
|
|
||||||
m_txBuffer.put(res3, control[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t CIO::getSpace() const
|
|
||||||
{
|
|
||||||
return m_txBuffer.getSpace();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setDecode(bool dcd)
|
|
||||||
{
|
|
||||||
if (dcd != m_dcd)
|
|
||||||
setCOSInt(dcd ? true : false);
|
|
||||||
|
|
||||||
m_dcd = dcd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setADCDetection(bool detect)
|
|
||||||
{
|
|
||||||
m_detect = detect;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setMode()
|
|
||||||
{
|
|
||||||
#if defined(ARDUINO_MODE_PINS)
|
|
||||||
setDStarInt(m_modemState == STATE_DSTAR);
|
|
||||||
setDMRInt(m_modemState == STATE_DMR);
|
|
||||||
setYSFInt(m_modemState == STATE_YSF);
|
|
||||||
setP25Int(m_modemState == STATE_P25);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel)
|
|
||||||
{
|
|
||||||
m_pttInvert = pttInvert;
|
|
||||||
|
|
||||||
m_rxLevel = q15_t(rxLevel * 128);
|
|
||||||
m_cwIdTXLevel = q15_t(cwIdTXLevel * 128);
|
|
||||||
m_dstarTXLevel = q15_t(dstarTXLevel * 128);
|
|
||||||
m_dmrTXLevel = q15_t(dmrTXLevel * 128);
|
|
||||||
m_ysfTXLevel = q15_t(ysfTXLevel * 128);
|
|
||||||
m_p25TXLevel = q15_t(p25TXLevel * 128);
|
|
||||||
|
|
||||||
if (rxInvert)
|
|
||||||
m_rxLevel = -m_rxLevel;
|
|
||||||
|
|
||||||
if (txInvert) {
|
|
||||||
m_dstarTXLevel = -m_dstarTXLevel;
|
|
||||||
m_dmrTXLevel = -m_dmrTXLevel;
|
|
||||||
m_ysfTXLevel = -m_ysfTXLevel;
|
|
||||||
m_p25TXLevel = -m_p25TXLevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::getOverflow(bool& adcOverflow, bool& dacOverflow)
|
|
||||||
{
|
|
||||||
adcOverflow = m_adcOverflow > 0U;
|
|
||||||
dacOverflow = m_dacOverflow > 0U;
|
|
||||||
|
|
||||||
#if defined(WANT_DEBUG)
|
|
||||||
if (m_adcOverflow > 0U || m_dacOverflow > 0U)
|
|
||||||
DEBUG3("IO: adc/dac", m_adcOverflow, m_dacOverflow);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_adcOverflow = 0U;
|
|
||||||
m_dacOverflow = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CIO::hasTXOverflow()
|
|
||||||
{
|
|
||||||
return m_txBuffer.hasOverflowed();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CIO::hasRXOverflow()
|
|
||||||
{
|
|
||||||
return m_rxBuffer.hasOverflowed();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::resetWatchdog()
|
|
||||||
{
|
|
||||||
m_watchdog = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CIO::hasLockout() const
|
|
||||||
{
|
|
||||||
return m_lockout;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015,2016 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(IO_H)
|
|
||||||
#define IO_H
|
|
||||||
|
|
||||||
#include "Globals.h"
|
|
||||||
|
|
||||||
#include "SampleRB.h"
|
|
||||||
#include "RSSIRB.h"
|
|
||||||
|
|
||||||
class CIO {
|
|
||||||
public:
|
|
||||||
CIO();
|
|
||||||
|
|
||||||
void start();
|
|
||||||
|
|
||||||
void process();
|
|
||||||
|
|
||||||
void write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t* control = NULL);
|
|
||||||
|
|
||||||
uint16_t getSpace() const;
|
|
||||||
|
|
||||||
void setDecode(bool dcd);
|
|
||||||
void setADCDetection(bool detect);
|
|
||||||
void setMode();
|
|
||||||
|
|
||||||
void interrupt(uint8_t source);
|
|
||||||
|
|
||||||
void setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel);
|
|
||||||
|
|
||||||
void getOverflow(bool& adcOverflow, bool& dacOverflow);
|
|
||||||
|
|
||||||
bool hasTXOverflow();
|
|
||||||
bool hasRXOverflow();
|
|
||||||
|
|
||||||
bool hasLockout() const;
|
|
||||||
|
|
||||||
void resetWatchdog();
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_started;
|
|
||||||
|
|
||||||
CSampleRB m_rxBuffer;
|
|
||||||
CSampleRB m_txBuffer;
|
|
||||||
CRSSIRB m_rssiBuffer;
|
|
||||||
|
|
||||||
arm_fir_instance_q15 m_C4FSKFilter;
|
|
||||||
arm_fir_instance_q15 m_GMSKFilter;
|
|
||||||
q15_t m_C4FSKState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
|
||||||
q15_t m_GMSKState[40U]; // NoTaps + BlockSize - 1, 12 + 20 - 1 plus some spare
|
|
||||||
|
|
||||||
bool m_pttInvert;
|
|
||||||
q15_t m_rxLevel;
|
|
||||||
q15_t m_cwIdTXLevel;
|
|
||||||
q15_t m_dstarTXLevel;
|
|
||||||
q15_t m_dmrTXLevel;
|
|
||||||
q15_t m_ysfTXLevel;
|
|
||||||
q15_t m_p25TXLevel;
|
|
||||||
|
|
||||||
uint32_t m_ledCount;
|
|
||||||
bool m_ledValue;
|
|
||||||
|
|
||||||
bool m_detect;
|
|
||||||
|
|
||||||
uint16_t m_adcOverflow;
|
|
||||||
uint16_t m_dacOverflow;
|
|
||||||
|
|
||||||
uint32_t m_count;
|
|
||||||
|
|
||||||
volatile uint32_t m_watchdog;
|
|
||||||
|
|
||||||
bool m_lockout;
|
|
||||||
|
|
||||||
// Hardware specific routines
|
|
||||||
void initInt();
|
|
||||||
void startInt();
|
|
||||||
|
|
||||||
bool getCOSInt();
|
|
||||||
|
|
||||||
void setLEDInt(bool on);
|
|
||||||
void setPTTInt(bool on);
|
|
||||||
void setCOSInt(bool on);
|
|
||||||
|
|
||||||
void setDStarInt(bool on);
|
|
||||||
void setDMRInt(bool on);
|
|
||||||
void setYSFInt(bool on);
|
|
||||||
void setP25Int(bool on);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
|
||||||
* Copyright (C) 2015 by Jim Mclaughlin KI6ZUM
|
|
||||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
|
||||||
*
|
|
||||||
* 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
#include "IO.h"
|
|
||||||
|
|
||||||
#if defined(__SAM3X8E__)
|
|
||||||
|
|
||||||
// An Arduino Due
|
|
||||||
#if defined(ARDUINO_DUE_PAPA)
|
|
||||||
#define PIN_COS 7
|
|
||||||
#define PIN_PTT 8
|
|
||||||
#define PIN_COSLED 11
|
|
||||||
#define PIN_DSTAR 16
|
|
||||||
#define PIN_DMR 17
|
|
||||||
#define PIN_YSF 18
|
|
||||||
#define PIN_P25 19
|
|
||||||
#define ADC_CHER_Chan (1<<7) // ADC on Due pin A0 - Due AD7 - (1 << 7)
|
|
||||||
#define ADC_ISR_EOC_Chan ADC_ISR_EOC7
|
|
||||||
#define ADC_CDR_Chan 7
|
|
||||||
#define DACC_MR_USER_SEL_Chan DACC_MR_USER_SEL_CHANNEL0 // DAC on Due DAC0
|
|
||||||
#define DACC_CHER_Chan DACC_CHER_CH0
|
|
||||||
#elif defined(ARDUINO_DUE_ZUM_V10)
|
|
||||||
#define PIN_COS 52
|
|
||||||
#define PIN_PTT 23
|
|
||||||
#define PIN_COSLED 22
|
|
||||||
#define PIN_DSTAR 9
|
|
||||||
#define PIN_DMR 8
|
|
||||||
#define PIN_YSF 7
|
|
||||||
#define PIN_P25 6
|
|
||||||
#define ADC_CHER_Chan (1<<13) // ADC on Due pin A11 - Due AD13 - (1 << 13)
|
|
||||||
#define ADC_ISR_EOC_Chan ADC_ISR_EOC13
|
|
||||||
#define ADC_CDR_Chan 13
|
|
||||||
#define DACC_MR_USER_SEL_Chan DACC_MR_USER_SEL_CHANNEL1 // DAC on Due DAC1
|
|
||||||
#define DACC_CHER_Chan DACC_CHER_CH1
|
|
||||||
#elif defined(ARDUINO_DUE_NTH)
|
|
||||||
#define PIN_COS A7
|
|
||||||
#define PIN_PTT A8
|
|
||||||
#define PIN_COSLED A11
|
|
||||||
#define PIN_DSTAR 9
|
|
||||||
#define PIN_DMR 8
|
|
||||||
#define PIN_YSF 7
|
|
||||||
#define PIN_P25 6
|
|
||||||
#define ADC_CHER_Chan (1<<7) // ADC on Due pin A0 - Due AD7 - (1 << 7)
|
|
||||||
#define ADC_ISR_EOC_Chan ADC_ISR_EOC7
|
|
||||||
#define ADC_CDR_Chan 7
|
|
||||||
#define DACC_MR_USER_SEL_Chan DACC_MR_USER_SEL_CHANNEL0 // DAC on Due DAC0
|
|
||||||
#define DACC_CHER_Chan DACC_CHER_CH0
|
|
||||||
#else
|
|
||||||
#error "Either ARDUINO_DUE_PAPA, ARDUINO_DUE_ZUM_V10, or ARDUINO_DUE_NTH need to be defined"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const uint16_t DC_OFFSET = 2048U;
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
void ADC_Handler()
|
|
||||||
{
|
|
||||||
io.interrupt(0U);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::initInt()
|
|
||||||
{
|
|
||||||
// Set up the TX, COS and LED pins
|
|
||||||
pinMode(PIN_PTT, OUTPUT);
|
|
||||||
pinMode(PIN_COSLED, OUTPUT);
|
|
||||||
pinMode(PIN_LED, OUTPUT);
|
|
||||||
pinMode(PIN_COS, INPUT);
|
|
||||||
|
|
||||||
#if defined(ARDUINO_MODE_PINS)
|
|
||||||
// Set up the mode output pins
|
|
||||||
pinMode(PIN_DSTAR, OUTPUT);
|
|
||||||
pinMode(PIN_DMR, OUTPUT);
|
|
||||||
pinMode(PIN_YSF, OUTPUT);
|
|
||||||
pinMode(PIN_P25, OUTPUT);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::startInt()
|
|
||||||
{
|
|
||||||
if (ADC->ADC_ISR & ADC_ISR_EOC_Chan) // Ensure there was an End-of-Conversion and we read the ISR reg
|
|
||||||
io.interrupt(0U);
|
|
||||||
|
|
||||||
// Set up the ADC
|
|
||||||
NVIC_EnableIRQ(ADC_IRQn); // Enable ADC interrupt vector
|
|
||||||
ADC->ADC_IDR = 0xFFFFFFFF; // Disable interrupts
|
|
||||||
ADC->ADC_IER = ADC_CHER_Chan; // Enable End-Of-Conv interrupt
|
|
||||||
ADC->ADC_CHDR = 0xFFFF; // Disable all channels
|
|
||||||
ADC->ADC_CHER = ADC_CHER_Chan; // Enable just one channel
|
|
||||||
ADC->ADC_CGR = 0x15555555; // All gains set to x1
|
|
||||||
ADC->ADC_COR = 0x00000000; // All offsets off
|
|
||||||
ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFF0) | (1 << 1) | ADC_MR_TRGEN; // 1 = trig source TIO from TC0
|
|
||||||
|
|
||||||
#if defined(EXTERNAL_OSC)
|
|
||||||
// Set up the external clock input on PA4 = AI5
|
|
||||||
REG_PIOA_ODR = 0x10; // Set pin as input
|
|
||||||
REG_PIOA_PDR = 0x10; // Disable PIO A bit 4
|
|
||||||
REG_PIOA_ABSR &= ~0x10; // Select A peripheral = TCLK1 Input
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Set up the timer
|
|
||||||
pmc_enable_periph_clk(TC_INTERFACE_ID + 0*3+0) ; // Clock the TC0 channel 0
|
|
||||||
TcChannel* t = &(TC0->TC_CHANNEL)[0]; // Pointer to TC0 registers for its channel 0
|
|
||||||
t->TC_CCR = TC_CCR_CLKDIS; // Disable internal clocking while setup regs
|
|
||||||
t->TC_IDR = 0xFFFFFFFF; // Disable interrupts
|
|
||||||
t->TC_SR; // Read int status reg to clear pending
|
|
||||||
#if defined(EXTERNAL_OSC)
|
|
||||||
t->TC_CMR = TC_CMR_TCCLKS_XC1 | // Use XC1 = TCLK1 external clock
|
|
||||||
#else
|
|
||||||
t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 | // Use TCLK1 (prescale by 2, = 42MHz)
|
|
||||||
#endif
|
|
||||||
TC_CMR_WAVE | // Waveform mode
|
|
||||||
TC_CMR_WAVSEL_UP_RC | // Count-up PWM using RC as threshold
|
|
||||||
TC_CMR_EEVT_XC0 | // Set external events from XC0 (this setup TIOB as output)
|
|
||||||
TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
|
|
||||||
TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR;
|
|
||||||
#if defined(EXTERNAL_OSC)
|
|
||||||
t->TC_RC = EXTERNAL_OSC / 24000; // Counter resets on RC, so sets period in terms of the external clock
|
|
||||||
t->TC_RA = EXTERNAL_OSC / 48000; // Roughly square wave
|
|
||||||
#else
|
|
||||||
t->TC_RC = 1750; // Counter resets on RC, so sets period in terms of 42MHz internal clock
|
|
||||||
t->TC_RA = 880; // Roughly square wave
|
|
||||||
#endif
|
|
||||||
t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET; // Set clear and set from RA and RC compares
|
|
||||||
t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; // re-enable local clocking and switch to hardware trigger source.
|
|
||||||
|
|
||||||
// Set up the DAC
|
|
||||||
pmc_enable_periph_clk(DACC_INTERFACE_ID); // Start clocking DAC
|
|
||||||
DACC->DACC_CR = DACC_CR_SWRST; // Reset DAC
|
|
||||||
DACC->DACC_MR =
|
|
||||||
DACC_MR_TRGEN_EN | DACC_MR_TRGSEL(1) | // Trigger 1 = TIO output of TC0
|
|
||||||
DACC_MR_USER_SEL_Chan | // Select channel
|
|
||||||
(24 << DACC_MR_STARTUP_Pos); // 24 = 1536 cycles which I think is in range 23..45us since DAC clock = 42MHz
|
|
||||||
DACC->DACC_IDR = 0xFFFFFFFF; // No interrupts
|
|
||||||
DACC->DACC_CHER = DACC_CHER_Chan; // Enable channel
|
|
||||||
|
|
||||||
digitalWrite(PIN_PTT, m_pttInvert ? HIGH : LOW);
|
|
||||||
digitalWrite(PIN_COSLED, LOW);
|
|
||||||
digitalWrite(PIN_LED, HIGH);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::interrupt(uint8_t source)
|
|
||||||
{
|
|
||||||
if ((ADC->ADC_ISR & ADC_ISR_EOC_Chan) == ADC_ISR_EOC_Chan) { // Ensure there was an End-of-Conversion and we read the ISR reg
|
|
||||||
uint8_t control = MARK_NONE;
|
|
||||||
uint16_t sample = DC_OFFSET;
|
|
||||||
|
|
||||||
m_txBuffer.get(sample, control);
|
|
||||||
DACC->DACC_CDR = sample;
|
|
||||||
|
|
||||||
sample = ADC->ADC_CDR[ADC_CDR_Chan];
|
|
||||||
m_rxBuffer.put(sample, control);
|
|
||||||
|
|
||||||
m_rssiBuffer.put(0U);
|
|
||||||
|
|
||||||
m_watchdog++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CIO::getCOSInt()
|
|
||||||
{
|
|
||||||
return digitalRead(PIN_COS) == HIGH;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setLEDInt(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_LED, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setPTTInt(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_PTT, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setCOSInt(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_COSLED, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setDStarInt(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_DSTAR, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setDMRInt(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_DMR, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setYSFInt(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_YSF, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setP25Int(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_P25, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,497 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
|
|
||||||
* Copyright (C) 2016 by Andy Uribe CA6JAU
|
|
||||||
*
|
|
||||||
* 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
#include "IO.h"
|
|
||||||
|
|
||||||
#if defined(STM32F4XX) || defined(STM32F4)
|
|
||||||
|
|
||||||
#if defined(STM32F4_DISCOVERY)
|
|
||||||
/*
|
|
||||||
Pin definitions for STM32F4 Discovery Board:
|
|
||||||
|
|
||||||
PTT PB13 output
|
|
||||||
COSLED PA7 output
|
|
||||||
LED PD15 output
|
|
||||||
COS PA5 input
|
|
||||||
|
|
||||||
DSTAR PD12 output
|
|
||||||
DMR PD13 output
|
|
||||||
YSF PD14 output
|
|
||||||
P25 PD11 output
|
|
||||||
|
|
||||||
RX PA0 analog input
|
|
||||||
RSSI PA1 analog input
|
|
||||||
TX PA4 analog output
|
|
||||||
|
|
||||||
EXT_CLK PA15 input
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define PIN_COS GPIO_Pin_5
|
|
||||||
#define PORT_COS GPIOA
|
|
||||||
#define RCC_Per_COS RCC_AHB1Periph_GPIOA
|
|
||||||
|
|
||||||
#define PIN_PTT GPIO_Pin_13
|
|
||||||
#define PORT_PTT GPIOB
|
|
||||||
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
|
|
||||||
|
|
||||||
#define PIN_COSLED GPIO_Pin_7
|
|
||||||
#define PORT_COSLED GPIOA
|
|
||||||
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOA
|
|
||||||
|
|
||||||
#define PIN_LED GPIO_Pin_15
|
|
||||||
#define PORT_LED GPIOD
|
|
||||||
#define RCC_Per_LED RCC_AHB1Periph_GPIOD
|
|
||||||
|
|
||||||
#define PIN_P25 GPIO_Pin_11
|
|
||||||
#define PORT_P25 GPIOD
|
|
||||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOD
|
|
||||||
|
|
||||||
#define PIN_DSTAR GPIO_Pin_12
|
|
||||||
#define PORT_DSTAR GPIOD
|
|
||||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOD
|
|
||||||
|
|
||||||
#define PIN_DMR GPIO_Pin_13
|
|
||||||
#define PORT_DMR GPIOD
|
|
||||||
#define RCC_Per_DMR RCC_AHB1Periph_GPIOD
|
|
||||||
|
|
||||||
#define PIN_YSF GPIO_Pin_14
|
|
||||||
#define PORT_YSF GPIOD
|
|
||||||
#define RCC_Per_YSF RCC_AHB1Periph_GPIOD
|
|
||||||
|
|
||||||
#define PIN_RX GPIO_Pin_0
|
|
||||||
#define PIN_RX_CH ADC_Channel_0
|
|
||||||
|
|
||||||
#define PIN_RSSI GPIO_Pin_1
|
|
||||||
#define PIN_RSSI_CH ADC_Channel_1
|
|
||||||
|
|
||||||
#elif defined(STM32F4_PI)
|
|
||||||
/*
|
|
||||||
Pin definitions for STM32F4 Pi Board:
|
|
||||||
|
|
||||||
PTT PB13 output
|
|
||||||
COSLED PB14 output
|
|
||||||
LED PB15 output
|
|
||||||
COS PC0 input
|
|
||||||
|
|
||||||
DSTAR PC7 output
|
|
||||||
DMR PC8 output
|
|
||||||
YSF PA8 output
|
|
||||||
P25 PC9 output
|
|
||||||
|
|
||||||
RX PA0 analog input
|
|
||||||
RSSI PA7 analog input
|
|
||||||
TX PA4 analog output
|
|
||||||
|
|
||||||
EXT_CLK PA15 input
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define PIN_COS GPIO_Pin_0
|
|
||||||
#define PORT_COS GPIOC
|
|
||||||
#define RCC_Per_COS RCC_AHB1Periph_GPIOC
|
|
||||||
|
|
||||||
#define PIN_PTT GPIO_Pin_13
|
|
||||||
#define PORT_PTT GPIOB
|
|
||||||
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
|
|
||||||
|
|
||||||
#define PIN_COSLED GPIO_Pin_14
|
|
||||||
#define PORT_COSLED GPIOB
|
|
||||||
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOB
|
|
||||||
|
|
||||||
#define PIN_LED GPIO_Pin_15
|
|
||||||
#define PORT_LED GPIOB
|
|
||||||
#define RCC_Per_LED RCC_AHB1Periph_GPIOB
|
|
||||||
|
|
||||||
#define PIN_P25 GPIO_Pin_9
|
|
||||||
#define PORT_P25 GPIOC
|
|
||||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOC
|
|
||||||
|
|
||||||
#define PIN_DSTAR GPIO_Pin_7
|
|
||||||
#define PORT_DSTAR GPIOC
|
|
||||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOC
|
|
||||||
|
|
||||||
#define PIN_DMR GPIO_Pin_8
|
|
||||||
#define PORT_DMR GPIOC
|
|
||||||
#define RCC_Per_DMR RCC_AHB1Periph_GPIOC
|
|
||||||
|
|
||||||
#define PIN_YSF GPIO_Pin_8
|
|
||||||
#define PORT_YSF GPIOA
|
|
||||||
#define RCC_Per_YSF RCC_AHB1Periph_GPIOA
|
|
||||||
|
|
||||||
#define PIN_RX GPIO_Pin_0
|
|
||||||
#define PIN_RX_CH ADC_Channel_0
|
|
||||||
|
|
||||||
#define PIN_RSSI GPIO_Pin_7
|
|
||||||
#define PIN_RSSI_CH ADC_Channel_7
|
|
||||||
|
|
||||||
#elif defined(STM32F4_NUCLEO)
|
|
||||||
/*
|
|
||||||
Pin definitions for STM32F4 Nucleo boards:
|
|
||||||
|
|
||||||
PTT PB13 output
|
|
||||||
COSLED PB14 output
|
|
||||||
LED PA5 output
|
|
||||||
COS PB15 input
|
|
||||||
|
|
||||||
DSTAR PB10 output
|
|
||||||
DMR PB4 output
|
|
||||||
YSF PB5 output
|
|
||||||
P25 PB3 output
|
|
||||||
|
|
||||||
RX PA0 analog input
|
|
||||||
RSSI PA1 analog input
|
|
||||||
TX PA4 analog output
|
|
||||||
|
|
||||||
EXT_CLK PA15 input
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define PIN_COS GPIO_Pin_15
|
|
||||||
#define PORT_COS GPIOB
|
|
||||||
#define RCC_Per_COS RCC_AHB1Periph_GPIOB
|
|
||||||
|
|
||||||
#define PIN_PTT GPIO_Pin_13
|
|
||||||
#define PORT_PTT GPIOB
|
|
||||||
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
|
|
||||||
|
|
||||||
#define PIN_COSLED GPIO_Pin_14
|
|
||||||
#define PORT_COSLED GPIOB
|
|
||||||
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOB
|
|
||||||
|
|
||||||
#define PIN_LED GPIO_Pin_5
|
|
||||||
#define PORT_LED GPIOA
|
|
||||||
#define RCC_Per_LED RCC_AHB1Periph_GPIOA
|
|
||||||
|
|
||||||
#define PIN_P25 GPIO_Pin_3
|
|
||||||
#define PORT_P25 GPIOB
|
|
||||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOB
|
|
||||||
|
|
||||||
#define PIN_DSTAR GPIO_Pin_10
|
|
||||||
#define PORT_DSTAR GPIOB
|
|
||||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOB
|
|
||||||
|
|
||||||
#define PIN_DMR GPIO_Pin_4
|
|
||||||
#define PORT_DMR GPIOB
|
|
||||||
#define RCC_Per_DMR RCC_AHB1Periph_GPIOB
|
|
||||||
|
|
||||||
#define PIN_YSF GPIO_Pin_5
|
|
||||||
#define PORT_YSF GPIOB
|
|
||||||
#define RCC_Per_YSF RCC_AHB1Periph_GPIOB
|
|
||||||
|
|
||||||
#define PIN_RX GPIO_Pin_0
|
|
||||||
#define PIN_RX_CH ADC_Channel_0
|
|
||||||
|
|
||||||
#define PIN_RSSI GPIO_Pin_1
|
|
||||||
#define PIN_RSSI_CH ADC_Channel_1
|
|
||||||
|
|
||||||
#else
|
|
||||||
#error "Either STM32F4_DISCOVERY, STM32F4_PI or STM32F4_NUCLEO need to be defined"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const uint16_t DC_OFFSET = 2048U;
|
|
||||||
|
|
||||||
// Sampling frequency
|
|
||||||
#define SAMP_FREQ 24000
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
void TIM2_IRQHandler() {
|
|
||||||
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
|
|
||||||
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
|
|
||||||
io.interrupt(0U);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::initInt()
|
|
||||||
{
|
|
||||||
GPIO_InitTypeDef GPIO_InitStruct;
|
|
||||||
GPIO_StructInit(&GPIO_InitStruct);
|
|
||||||
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
|
|
||||||
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
|
|
||||||
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;
|
|
||||||
|
|
||||||
// PTT pin
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_Per_PTT, ENABLE);
|
|
||||||
GPIO_InitStruct.GPIO_Pin = PIN_PTT;
|
|
||||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
|
||||||
GPIO_Init(PORT_PTT, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
// COSLED pin
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_Per_COSLED, ENABLE);
|
|
||||||
GPIO_InitStruct.GPIO_Pin = PIN_COSLED;
|
|
||||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
|
||||||
GPIO_Init(PORT_COSLED, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
// LED pin
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_Per_LED, ENABLE);
|
|
||||||
GPIO_InitStruct.GPIO_Pin = PIN_LED;
|
|
||||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
|
||||||
GPIO_Init(PORT_LED, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
// Init the input pins PIN_COS
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_Per_COS, ENABLE);
|
|
||||||
GPIO_InitStruct.GPIO_Pin = PIN_COS;
|
|
||||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
|
|
||||||
GPIO_Init(PORT_COS, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
#if defined(ARDUINO_MODE_PINS)
|
|
||||||
// DSTAR pin
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_Per_DSTAR, ENABLE);
|
|
||||||
GPIO_InitStruct.GPIO_Pin = PIN_DSTAR;
|
|
||||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
|
||||||
GPIO_Init(PORT_DSTAR, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
// DMR pin
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_Per_DMR, ENABLE);
|
|
||||||
GPIO_InitStruct.GPIO_Pin = PIN_DMR;
|
|
||||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
|
||||||
GPIO_Init(PORT_DMR, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
// YSF pin
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_Per_YSF, ENABLE);
|
|
||||||
GPIO_InitStruct.GPIO_Pin = PIN_YSF;
|
|
||||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
|
||||||
GPIO_Init(PORT_YSF, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
// P25 pin
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_Per_P25, ENABLE);
|
|
||||||
GPIO_InitStruct.GPIO_Pin = PIN_P25;
|
|
||||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
|
||||||
GPIO_Init(PORT_P25, &GPIO_InitStruct);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::startInt()
|
|
||||||
{
|
|
||||||
if ((ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != RESET))
|
|
||||||
io.interrupt(0U);
|
|
||||||
|
|
||||||
// Init the ADC
|
|
||||||
GPIO_InitTypeDef GPIO_InitStruct;
|
|
||||||
ADC_InitTypeDef ADC_InitStructure;
|
|
||||||
ADC_CommonInitTypeDef ADC_CommonInitStructure;
|
|
||||||
|
|
||||||
GPIO_StructInit(&GPIO_InitStruct);
|
|
||||||
ADC_CommonStructInit(&ADC_CommonInitStructure);
|
|
||||||
ADC_StructInit(&ADC_InitStructure);
|
|
||||||
|
|
||||||
// Enable ADC clock
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
|
|
||||||
#if defined(SEND_RSSI_DATA)
|
|
||||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE);
|
|
||||||
#else
|
|
||||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(SEND_RSSI_DATA)
|
|
||||||
GPIO_InitStruct.GPIO_Pin = PIN_RX | PIN_RSSI;
|
|
||||||
#else
|
|
||||||
GPIO_InitStruct.GPIO_Pin = PIN_RX;
|
|
||||||
#endif
|
|
||||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
|
|
||||||
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL ;
|
|
||||||
GPIO_Init(GPIOA, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
// Init ADCs in dual mode (RSSI), div clock by two
|
|
||||||
#if defined(SEND_RSSI_DATA)
|
|
||||||
ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;
|
|
||||||
#else
|
|
||||||
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
|
|
||||||
#endif
|
|
||||||
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
|
|
||||||
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
|
|
||||||
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
|
|
||||||
ADC_CommonInit(&ADC_CommonInitStructure);
|
|
||||||
|
|
||||||
// Init ADC1 and ADC2: 12bit, single-conversion
|
|
||||||
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
|
|
||||||
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
|
|
||||||
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
|
|
||||||
ADC_InitStructure.ADC_ExternalTrigConvEdge = 0;
|
|
||||||
ADC_InitStructure.ADC_ExternalTrigConv = 0;
|
|
||||||
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
|
|
||||||
ADC_InitStructure.ADC_NbrOfConversion = 1;
|
|
||||||
|
|
||||||
ADC_Init(ADC1, &ADC_InitStructure);
|
|
||||||
|
|
||||||
ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);
|
|
||||||
ADC_RegularChannelConfig(ADC1, PIN_RX_CH, 1, ADC_SampleTime_3Cycles);
|
|
||||||
|
|
||||||
// Enable ADC1
|
|
||||||
ADC_Cmd(ADC1, ENABLE);
|
|
||||||
|
|
||||||
#if defined(SEND_RSSI_DATA)
|
|
||||||
ADC_Init(ADC2, &ADC_InitStructure);
|
|
||||||
|
|
||||||
ADC_EOCOnEachRegularChannelCmd(ADC2, ENABLE);
|
|
||||||
ADC_RegularChannelConfig(ADC2, PIN_RSSI_CH, 1, ADC_SampleTime_3Cycles);
|
|
||||||
|
|
||||||
// Enable ADC2
|
|
||||||
ADC_Cmd(ADC2, ENABLE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Init the DAC
|
|
||||||
DAC_InitTypeDef DAC_InitStructure;
|
|
||||||
|
|
||||||
GPIO_StructInit(&GPIO_InitStruct);
|
|
||||||
DAC_StructInit(&DAC_InitStructure);
|
|
||||||
|
|
||||||
// GPIOA clock enable
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
|
|
||||||
|
|
||||||
// DAC Periph clock enable
|
|
||||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
|
|
||||||
|
|
||||||
// GPIO CONFIGURATION of DAC Pins (PA4)
|
|
||||||
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
|
|
||||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
|
|
||||||
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
|
||||||
GPIO_Init(GPIOA, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
|
|
||||||
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
|
|
||||||
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
|
|
||||||
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
|
|
||||||
DAC_Cmd(DAC_Channel_1, ENABLE);
|
|
||||||
|
|
||||||
// Init the timer
|
|
||||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
|
|
||||||
|
|
||||||
#if defined(EXTERNAL_OSC)
|
|
||||||
// Configure GPIO PA15 as external TIM2 clock source
|
|
||||||
GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_TIM2);
|
|
||||||
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
|
|
||||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
|
|
||||||
GPIO_Init(GPIOA, &GPIO_InitStruct);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TIM_TimeBaseInitTypeDef timerInitStructure;
|
|
||||||
TIM_TimeBaseStructInit (&timerInitStructure);
|
|
||||||
|
|
||||||
// TIM2 output frequency
|
|
||||||
#if defined(EXTERNAL_OSC)
|
|
||||||
timerInitStructure.TIM_Prescaler = (uint16_t) ((EXTERNAL_OSC/(2*SAMP_FREQ)) - 1);
|
|
||||||
#else
|
|
||||||
timerInitStructure.TIM_Prescaler = (uint16_t) ((SystemCoreClock/(4*SAMP_FREQ)) - 1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
|
|
||||||
timerInitStructure.TIM_Period = 1;
|
|
||||||
timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
|
|
||||||
timerInitStructure.TIM_RepetitionCounter = 0;
|
|
||||||
TIM_TimeBaseInit(TIM2, &timerInitStructure);
|
|
||||||
|
|
||||||
#if defined(EXTERNAL_OSC)
|
|
||||||
// Enable external clock (PA15)
|
|
||||||
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);
|
|
||||||
#else
|
|
||||||
// Enable internal clock
|
|
||||||
TIM_InternalClockConfig(TIM2);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Enable TIM2
|
|
||||||
TIM_Cmd(TIM2, ENABLE);
|
|
||||||
// Enable TIM2 interrupt
|
|
||||||
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
|
|
||||||
|
|
||||||
NVIC_InitTypeDef nvicStructure;
|
|
||||||
nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
|
|
||||||
nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
|
|
||||||
nvicStructure.NVIC_IRQChannelSubPriority = 1;
|
|
||||||
nvicStructure.NVIC_IRQChannelCmd = ENABLE;
|
|
||||||
NVIC_Init(&nvicStructure);
|
|
||||||
|
|
||||||
GPIO_ResetBits(PORT_COSLED, PIN_COSLED);
|
|
||||||
GPIO_SetBits(PORT_LED, PIN_LED);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::interrupt(uint8_t source)
|
|
||||||
{
|
|
||||||
uint8_t control = MARK_NONE;
|
|
||||||
uint16_t sample = DC_OFFSET;
|
|
||||||
uint16_t rawRSSI = 0U;
|
|
||||||
|
|
||||||
m_txBuffer.get(sample, control);
|
|
||||||
|
|
||||||
// Send the value to the DAC
|
|
||||||
DAC_SetChannel1Data(DAC_Align_12b_R, sample);
|
|
||||||
|
|
||||||
// Read value from ADC1 and ADC2
|
|
||||||
if ((ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)) {
|
|
||||||
// shouldn't be still in reset at this point so null the sample value?
|
|
||||||
sample = 0U;
|
|
||||||
} else {
|
|
||||||
sample = ADC_GetConversionValue(ADC1);
|
|
||||||
#if defined(SEND_RSSI_DATA)
|
|
||||||
rawRSSI = ADC_GetConversionValue(ADC2);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// trigger next ADC1
|
|
||||||
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
|
|
||||||
ADC_SoftwareStartConv(ADC1);
|
|
||||||
|
|
||||||
m_rxBuffer.put(sample, control);
|
|
||||||
m_rssiBuffer.put(rawRSSI);
|
|
||||||
|
|
||||||
m_watchdog++;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CIO::getCOSInt()
|
|
||||||
{
|
|
||||||
return GPIO_ReadOutputDataBit(PORT_COS, PIN_COS) == Bit_SET;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setLEDInt(bool on)
|
|
||||||
{
|
|
||||||
GPIO_WriteBit(PORT_LED, PIN_LED, on ? Bit_SET : Bit_RESET);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setPTTInt(bool on)
|
|
||||||
{
|
|
||||||
GPIO_WriteBit(PORT_PTT, PIN_PTT, on ? Bit_SET : Bit_RESET);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setCOSInt(bool on)
|
|
||||||
{
|
|
||||||
GPIO_WriteBit(PORT_COSLED, PIN_COSLED, on ? Bit_SET : Bit_RESET);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setDStarInt(bool on)
|
|
||||||
{
|
|
||||||
GPIO_WriteBit(PORT_DSTAR, PIN_DSTAR, on ? Bit_SET : Bit_RESET);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setDMRInt(bool on)
|
|
||||||
{
|
|
||||||
GPIO_WriteBit(PORT_DMR, PIN_DMR, on ? Bit_SET : Bit_RESET);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setYSFInt(bool on)
|
|
||||||
{
|
|
||||||
GPIO_WriteBit(PORT_YSF, PIN_YSF, on ? Bit_SET : Bit_RESET);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setP25Int(bool on)
|
|
||||||
{
|
|
||||||
GPIO_WriteBit(PORT_P25, PIN_P25, on ? Bit_SET : Bit_RESET);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
-227
@@ -1,227 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
#include "IO.h"
|
|
||||||
|
|
||||||
#if defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
|
|
||||||
|
|
||||||
#if defined(EXTERNAL_OSC)
|
|
||||||
#define PIN_LED 3
|
|
||||||
#else
|
|
||||||
#define PIN_LED 13
|
|
||||||
#endif
|
|
||||||
#define PIN_COS 4
|
|
||||||
#define PIN_PTT 5
|
|
||||||
#define PIN_COSLED 6
|
|
||||||
#define PIN_DSTAR 9
|
|
||||||
#define PIN_DMR 10
|
|
||||||
#define PIN_YSF 11
|
|
||||||
#define PIN_P25 12
|
|
||||||
#define PIN_ADC 5 // A0, Pin 14
|
|
||||||
#define PIN_RSSI 8 // A2, Pin 16
|
|
||||||
|
|
||||||
#define PDB_CHnC1_TOS 0x0100
|
|
||||||
#define PDB_CHnC1_EN 0x0001
|
|
||||||
|
|
||||||
const uint16_t DC_OFFSET = 2048U;
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
void adc0_isr()
|
|
||||||
{
|
|
||||||
io.interrupt(0U);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(SEND_RSSI_DATA)
|
|
||||||
void adc1_isr()
|
|
||||||
{
|
|
||||||
io.interrupt(1U);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::initInt()
|
|
||||||
{
|
|
||||||
// Set up the TX, COS and LED pins
|
|
||||||
pinMode(PIN_PTT, OUTPUT);
|
|
||||||
pinMode(PIN_COSLED, OUTPUT);
|
|
||||||
pinMode(PIN_LED, OUTPUT);
|
|
||||||
pinMode(PIN_COS, INPUT);
|
|
||||||
|
|
||||||
#if defined(ARDUINO_MODE_PINS)
|
|
||||||
// Set up the mode output pins
|
|
||||||
pinMode(PIN_DSTAR, OUTPUT);
|
|
||||||
pinMode(PIN_DMR, OUTPUT);
|
|
||||||
pinMode(PIN_YSF, OUTPUT);
|
|
||||||
pinMode(PIN_P25, OUTPUT);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::startInt()
|
|
||||||
{
|
|
||||||
// Initialise the DAC
|
|
||||||
SIM_SCGC2 |= SIM_SCGC2_DAC0;
|
|
||||||
DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACRFS; // 1.2V VDDA is DACREF_2
|
|
||||||
|
|
||||||
// Initialise ADC0
|
|
||||||
SIM_SCGC6 |= SIM_SCGC6_ADC0;
|
|
||||||
ADC0_CFG1 = ADC_CFG1_ADIV(1) | ADC_CFG1_ADICLK(1) | // Single-ended 12 bits, long sample time
|
|
||||||
ADC_CFG1_MODE(1) | ADC_CFG1_ADLSMP;
|
|
||||||
ADC0_CFG2 = ADC_CFG2_MUXSEL | ADC_CFG2_ADLSTS(2); // Select channels ADxxxb
|
|
||||||
ADC0_SC2 = ADC_SC2_REFSEL(1) | ADC_SC2_ADTRG; // Voltage ref internal, hardware trigger
|
|
||||||
ADC0_SC3 = ADC_SC3_AVGE | ADC_SC3_AVGS(0); // Enable averaging, 4 samples
|
|
||||||
|
|
||||||
ADC0_SC3 = ADC_SC3_CAL;
|
|
||||||
while (ADC0_SC3 & ADC_SC3_CAL) // Wait for calibration
|
|
||||||
;
|
|
||||||
|
|
||||||
uint16_t sum0 = ADC0_CLPS + ADC0_CLP4 + ADC0_CLP3 + // Plus side gain
|
|
||||||
ADC0_CLP2 + ADC0_CLP1 + ADC0_CLP0;
|
|
||||||
sum0 = (sum0 / 2U) | 0x8000U;
|
|
||||||
ADC0_PG = sum0;
|
|
||||||
|
|
||||||
ADC0_SC1A = ADC_SC1_AIEN | PIN_ADC; // Enable ADC interrupt, use A0
|
|
||||||
NVIC_ENABLE_IRQ(IRQ_ADC0);
|
|
||||||
|
|
||||||
#if defined(SEND_RSSI_DATA)
|
|
||||||
// Initialise ADC1
|
|
||||||
SIM_SCGC3 |= SIM_SCGC3_ADC1;
|
|
||||||
ADC1_CFG1 = ADC_CFG1_ADIV(1) | ADC_CFG1_ADICLK(1) | // Single-ended 12 bits, long sample time
|
|
||||||
ADC_CFG1_MODE(1) | ADC_CFG1_ADLSMP;
|
|
||||||
ADC1_CFG2 = ADC_CFG2_MUXSEL | ADC_CFG2_ADLSTS(2); // Select channels ADxxxb
|
|
||||||
ADC1_SC2 = ADC_SC2_REFSEL(1); // Voltage ref internal, software trigger
|
|
||||||
ADC1_SC3 = ADC_SC3_AVGE | ADC_SC3_AVGS(0); // Enable averaging, 4 samples
|
|
||||||
|
|
||||||
ADC1_SC3 = ADC_SC3_CAL;
|
|
||||||
while (ADC1_SC3 & ADC_SC3_CAL) // Wait for calibration
|
|
||||||
;
|
|
||||||
|
|
||||||
uint16_t sum1 = ADC1_CLPS + ADC1_CLP4 + ADC1_CLP3 + // Plus side gain
|
|
||||||
ADC1_CLP2 + ADC1_CLP1 + ADC1_CLP0;
|
|
||||||
sum1 = (sum1 / 2U) | 0x8000U;
|
|
||||||
ADC1_PG = sum1;
|
|
||||||
|
|
||||||
NVIC_ENABLE_IRQ(IRQ_ADC1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(EXTERNAL_OSC)
|
|
||||||
// Set ADC0 to trigger from the LPTMR at 24 kHz
|
|
||||||
SIM_SOPT7 = SIM_SOPT7_ADC0ALTTRGEN | // Enable ADC0 alternate trigger
|
|
||||||
SIM_SOPT7_ADC0PRETRGSEL | // Enable ADC0 pre-trigger
|
|
||||||
SIM_SOPT7_ADC0TRGSEL(14); // Trigger ADC0 by LPTMR0
|
|
||||||
|
|
||||||
CORE_PIN13_CONFIG = PORT_PCR_MUX(3);
|
|
||||||
|
|
||||||
SIM_SCGC5 |= SIM_SCGC5_LPTIMER; // Enable Low Power Timer Access
|
|
||||||
LPTMR0_CSR = 0; // Disable
|
|
||||||
LPTMR0_PSR = LPTMR_PSR_PBYP; // Bypass prescaler/filter
|
|
||||||
LPTMR0_CMR = (EXTERNAL_OSC / 24000) - 1; // Frequency divided by CMR + 1
|
|
||||||
LPTMR0_CSR = LPTMR_CSR_TPS(2) | // Pin: 0=CMP0, 1=xtal, 2=pin13
|
|
||||||
LPTMR_CSR_TMS; // Mode Select, 0=timer, 1=counter
|
|
||||||
LPTMR0_CSR |= LPTMR_CSR_TEN; // Enable
|
|
||||||
#else
|
|
||||||
// Setup PDB for ADC0 at 24 kHz
|
|
||||||
SIM_SCGC6 |= SIM_SCGC6_PDB; // Enable PDB clock
|
|
||||||
PDB0_MOD = (F_BUS / 24000) - 1; // Timer period - 1
|
|
||||||
PDB0_IDLY = 0; // Interrupt delay
|
|
||||||
PDB0_CH0C1 = PDB_CHnC1_TOS | PDB_CHnC1_EN; // Enable pre-trigger for ADC0
|
|
||||||
PDB0_SC = PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | // SW trigger, enable interrupts, continuous mode
|
|
||||||
PDB_SC_PDBIE | PDB_SC_CONT | PDB_SC_LDOK; // No prescaling
|
|
||||||
PDB0_SC |= PDB_SC_SWTRIG; // Software trigger (reset and restart counter)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
digitalWrite(PIN_PTT, m_pttInvert ? HIGH : LOW);
|
|
||||||
digitalWrite(PIN_COSLED, LOW);
|
|
||||||
digitalWrite(PIN_LED, HIGH);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::interrupt(uint8_t source)
|
|
||||||
{
|
|
||||||
if (source == 0U) { // ADC0
|
|
||||||
uint8_t control = MARK_NONE;
|
|
||||||
uint16_t sample = DC_OFFSET;
|
|
||||||
|
|
||||||
m_txBuffer.get(sample, control);
|
|
||||||
*(int16_t *)&(DAC0_DAT0L) = sample;
|
|
||||||
|
|
||||||
if ((ADC0_SC1A & ADC_SC1_COCO) == ADC_SC1_COCO) {
|
|
||||||
sample = ADC0_RA;
|
|
||||||
m_rxBuffer.put(sample, control);
|
|
||||||
|
|
||||||
#if defined(SEND_RSSI_DATA)
|
|
||||||
ADC1_SC1A = ADC_SC1_AIEN | PIN_RSSI;
|
|
||||||
#else
|
|
||||||
m_rssiBuffer.put(0U);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
m_watchdog++;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(SEND_RSSI_DATA)
|
|
||||||
if (source == 1U) { // ADC1
|
|
||||||
if ((ADC1_SC1A & ADC_SC1_COCO) == ADC_SC1_COCO) {
|
|
||||||
uint16_t rssi = ADC1_RA;
|
|
||||||
m_rssiBuffer.put(rssi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CIO::getCOSInt()
|
|
||||||
{
|
|
||||||
return digitalRead(PIN_COS) == HIGH;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setLEDInt(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_LED, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setPTTInt(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_PTT, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setCOSInt(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_COSLED, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setDStarInt(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_DSTAR, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setDMRInt(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_DMR, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setYSFInt(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_YSF, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CIO::setP25Int(bool on)
|
|
||||||
{
|
|
||||||
digitalWrite(PIN_P25, on ? HIGH : LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
|
||||||
* Copyright (C) 2016 by Mathis Schmieder DB9MAT
|
|
||||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
|
||||||
*
|
|
||||||
* 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if defined(STM32F4XX) || defined(STM32F4)
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
|
|
||||||
// Global variables
|
|
||||||
MMDVM_STATE m_modemState = STATE_IDLE;
|
|
||||||
|
|
||||||
bool m_dstarEnable = true;
|
|
||||||
bool m_dmrEnable = true;
|
|
||||||
bool m_ysfEnable = true;
|
|
||||||
bool m_p25Enable = true;
|
|
||||||
|
|
||||||
bool m_duplex = true;
|
|
||||||
|
|
||||||
bool m_tx = false;
|
|
||||||
bool m_dcd = false;
|
|
||||||
|
|
||||||
uint32_t m_sampleCount = 0U;
|
|
||||||
bool m_sampleInsert = false;
|
|
||||||
|
|
||||||
CDStarRX dstarRX;
|
|
||||||
CDStarTX dstarTX;
|
|
||||||
|
|
||||||
CDMRIdleRX dmrIdleRX;
|
|
||||||
CDMRRX dmrRX;
|
|
||||||
CDMRTX dmrTX;
|
|
||||||
|
|
||||||
CDMRDMORX dmrDMORX;
|
|
||||||
CDMRDMOTX dmrDMOTX;
|
|
||||||
|
|
||||||
CYSFRX ysfRX;
|
|
||||||
CYSFTX ysfTX;
|
|
||||||
|
|
||||||
CP25RX p25RX;
|
|
||||||
CP25TX p25TX;
|
|
||||||
|
|
||||||
CCalDStarRX calDStarRX;
|
|
||||||
CCalDStarTX calDStarTX;
|
|
||||||
CCalDMR calDMR;
|
|
||||||
|
|
||||||
CCWIdTX cwIdTX;
|
|
||||||
|
|
||||||
CSerialPort serial;
|
|
||||||
CIO io;
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
serial.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
serial.process();
|
|
||||||
|
|
||||||
io.process();
|
|
||||||
|
|
||||||
// The following is for transmitting
|
|
||||||
if (m_dstarEnable && m_modemState == STATE_DSTAR)
|
|
||||||
dstarTX.process();
|
|
||||||
|
|
||||||
if (m_dmrEnable && m_modemState == STATE_DMR) {
|
|
||||||
if (m_duplex)
|
|
||||||
dmrTX.process();
|
|
||||||
else
|
|
||||||
dmrDMOTX.process();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_ysfEnable && m_modemState == STATE_YSF)
|
|
||||||
ysfTX.process();
|
|
||||||
|
|
||||||
if (m_p25Enable && m_modemState == STATE_P25)
|
|
||||||
p25TX.process();
|
|
||||||
|
|
||||||
if (m_modemState == STATE_DSTARCAL)
|
|
||||||
calDStarTX.process();
|
|
||||||
|
|
||||||
if (m_modemState == STATE_DMRCAL)
|
|
||||||
calDMR.process();
|
|
||||||
|
|
||||||
if (m_modemState == STATE_IDLE)
|
|
||||||
cwIdTX.process();
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
setup();
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
|
||||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
|
||||||
*
|
|
||||||
* 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
|
|
||||||
// Global variables
|
|
||||||
MMDVM_STATE m_modemState = STATE_IDLE;
|
|
||||||
|
|
||||||
bool m_dstarEnable = true;
|
|
||||||
bool m_dmrEnable = true;
|
|
||||||
bool m_ysfEnable = true;
|
|
||||||
bool m_p25Enable = true;
|
|
||||||
|
|
||||||
bool m_duplex = true;
|
|
||||||
|
|
||||||
bool m_tx = false;
|
|
||||||
bool m_dcd = false;
|
|
||||||
|
|
||||||
uint32_t m_sampleCount = 0U;
|
|
||||||
bool m_sampleInsert = false;
|
|
||||||
|
|
||||||
CDStarRX dstarRX;
|
|
||||||
CDStarTX dstarTX;
|
|
||||||
|
|
||||||
CDMRIdleRX dmrIdleRX;
|
|
||||||
CDMRRX dmrRX;
|
|
||||||
CDMRTX dmrTX;
|
|
||||||
|
|
||||||
CDMRDMORX dmrDMORX;
|
|
||||||
CDMRDMOTX dmrDMOTX;
|
|
||||||
|
|
||||||
CYSFRX ysfRX;
|
|
||||||
CYSFTX ysfTX;
|
|
||||||
|
|
||||||
CP25RX p25RX;
|
|
||||||
CP25TX p25TX;
|
|
||||||
|
|
||||||
CCalDStarRX calDStarRX;
|
|
||||||
CCalDStarTX calDStarTX;
|
|
||||||
CCalDMR calDMR;
|
|
||||||
|
|
||||||
CCWIdTX cwIdTX;
|
|
||||||
|
|
||||||
CSerialPort serial;
|
|
||||||
CIO io;
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
serial.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
serial.process();
|
|
||||||
|
|
||||||
io.process();
|
|
||||||
|
|
||||||
// The following is for transmitting
|
|
||||||
if (m_dstarEnable && m_modemState == STATE_DSTAR)
|
|
||||||
dstarTX.process();
|
|
||||||
|
|
||||||
if (m_dmrEnable && m_modemState == STATE_DMR) {
|
|
||||||
if (m_duplex)
|
|
||||||
dmrTX.process();
|
|
||||||
else
|
|
||||||
dmrDMOTX.process();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_ysfEnable && m_modemState == STATE_YSF)
|
|
||||||
ysfTX.process();
|
|
||||||
|
|
||||||
if (m_p25Enable && m_modemState == STATE_P25)
|
|
||||||
p25TX.process();
|
|
||||||
|
|
||||||
if (m_modemState == STATE_DSTARCAL)
|
|
||||||
calDStarTX.process();
|
|
||||||
|
|
||||||
if (m_modemState == STATE_DMRCAL)
|
|
||||||
calDMR.process();
|
|
||||||
|
|
||||||
if (m_modemState == STATE_IDLE)
|
|
||||||
cwIdTX.process();
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,338 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<Project version="2G - 1.7.8" name="MMDVM_STM32F4xx">
|
|
||||||
<Target name="MMDVM_Discovery_407" isCurrent="1">
|
|
||||||
<Device manufacturerId="9" manufacturerName="ST" chipId="344" chipName="STM32F407VG" boardId="" boardName=""/>
|
|
||||||
<BuildOption>
|
|
||||||
<Compile>
|
|
||||||
<Option name="OptimizationLevel" value="4"/>
|
|
||||||
<Option name="UseFPU" value="0"/>
|
|
||||||
<Option name="UserEditCompiler" value=""/>
|
|
||||||
<Option name="SupportCPlusplus" value="1"/>
|
|
||||||
<Option name="FPU" value="2"/>
|
|
||||||
<Includepaths>
|
|
||||||
<Includepath path="."/>
|
|
||||||
</Includepaths>
|
|
||||||
<DefinedSymbols>
|
|
||||||
<Define name="STM32F4XX"/>
|
|
||||||
<Define name="USE_STDPERIPH_DRIVER"/>
|
|
||||||
<Define name="__ASSEMBLY__"/>
|
|
||||||
<Define name="SUPPORT_CPLUSPLUS"/>
|
|
||||||
<Define name="STM32F40_41xxx"/>
|
|
||||||
<Define name="HSE_VALUE=8000000"/>
|
|
||||||
<Define name="STM32F4_DISCOVERY"/>
|
|
||||||
<Define name="__FPU_USED"/>
|
|
||||||
</DefinedSymbols>
|
|
||||||
</Compile>
|
|
||||||
<Link useDefault="0">
|
|
||||||
<Option name="DiscardUnusedSection" value="0"/>
|
|
||||||
<Option name="UserEditLinkder" value=""/>
|
|
||||||
<Option name="UseMemoryLayout" value="0"/>
|
|
||||||
<Option name="nostartfiles" value="1"/>
|
|
||||||
<Option name="LTO" value="0"/>
|
|
||||||
<Option name="IsNewStartupCode" value="1"/>
|
|
||||||
<Option name="Library" value="Semihosting"/>
|
|
||||||
<Option name="UserEditLinker" value="-lstdc++; --specs=nano.specs;"/>
|
|
||||||
<LinkedLibraries>
|
|
||||||
<Libset dir="stm32f4xx_lib\cmsis\lib\gcc\" libs="arm_cortexm4lf_math"/>
|
|
||||||
</LinkedLibraries>
|
|
||||||
<MemoryAreas debugInFlashNotRAM="1">
|
|
||||||
<Memory name="IROM1" type="ReadOnly" size="0x00100000" startValue="0x08000000"/>
|
|
||||||
<Memory name="IRAM1" type="ReadWrite" size="0x00020000" startValue="0x20000000"/>
|
|
||||||
<Memory name="IROM2" type="ReadOnly" size="" startValue=""/>
|
|
||||||
<Memory name="IRAM2" type="ReadWrite" size="0x00010000" startValue="0x10000000"/>
|
|
||||||
</MemoryAreas>
|
|
||||||
<LocateLinkFile path="./stm32f4xx_link.ld" type="0"/>
|
|
||||||
</Link>
|
|
||||||
<Output>
|
|
||||||
<Option name="OutputFileType" value="0"/>
|
|
||||||
<Option name="Path" value="./"/>
|
|
||||||
<Option name="Name" value="MMDVM_DIS"/>
|
|
||||||
<Option name="HEX" value="1"/>
|
|
||||||
<Option name="BIN" value="1"/>
|
|
||||||
</Output>
|
|
||||||
<User>
|
|
||||||
<UserRun name="Run#1" type="Before" checked="0" value=""/>
|
|
||||||
<UserRun name="Run#1" type="After" checked="0" value=""/>
|
|
||||||
</User>
|
|
||||||
</BuildOption>
|
|
||||||
<DebugOption>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.adapter" value="ST-Link"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.debugMode" value="SWD"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.clockDiv" value="1M"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.corerunToMain" value="1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.jlinkgdbserver" value=""/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.userDefineGDBScript" value=""/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.targetEndianess" value="0"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.jlinkResetMode" value="Type 0: Normal"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.resetMode" value="SYSRESETREQ"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.ifSemihost" value="0"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.ifCacheRom" value="1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.ipAddress" value="127.0.0.1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.portNumber" value="2009"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.autoDownload" value="1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.verify" value="1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.downloadFuction" value="Erase Effected"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.defaultAlgorithm" value="STM32F4xx_1024.elf"/>
|
|
||||||
</DebugOption>
|
|
||||||
<ExcludeFile/>
|
|
||||||
</Target>
|
|
||||||
<Target name="MMDVM_Pi_446" isCurrent="0">
|
|
||||||
<Device manufacturerId="9" manufacturerName="ST" chipId="344" chipName="STM32F407VG" boardId="" boardName=""/>
|
|
||||||
<BuildOption>
|
|
||||||
<Compile>
|
|
||||||
<Option name="OptimizationLevel" value="4"/>
|
|
||||||
<Option name="UseFPU" value="0"/>
|
|
||||||
<Option name="UserEditCompiler" value=""/>
|
|
||||||
<Option name="SupportCPlusplus" value="1"/>
|
|
||||||
<Option name="FPU" value="2"/>
|
|
||||||
<Includepaths>
|
|
||||||
<Includepath path="."/>
|
|
||||||
</Includepaths>
|
|
||||||
<DefinedSymbols>
|
|
||||||
<Define name="STM32F4XX"/>
|
|
||||||
<Define name="USE_STDPERIPH_DRIVER"/>
|
|
||||||
<Define name="__ASSEMBLY__"/>
|
|
||||||
<Define name="SUPPORT_CPLUSPLUS"/>
|
|
||||||
<Define name="__FPU_USED"/>
|
|
||||||
<Define name="STM32F446xx"/>
|
|
||||||
<Define name="HSE_VALUE=12000000"/>
|
|
||||||
<Define name="STM32F4_PI"/>
|
|
||||||
<Define name="ARDUINO_MODE_PINS"/>
|
|
||||||
<Define name="SEND_RSSI_DATA"/>
|
|
||||||
<Define name="SERIAL_REPEATER"/>
|
|
||||||
</DefinedSymbols>
|
|
||||||
</Compile>
|
|
||||||
<Link useDefault="0">
|
|
||||||
<Option name="DiscardUnusedSection" value="0"/>
|
|
||||||
<Option name="UserEditLinkder" value=""/>
|
|
||||||
<Option name="UseMemoryLayout" value="0"/>
|
|
||||||
<Option name="nostartfiles" value="1"/>
|
|
||||||
<Option name="LTO" value="0"/>
|
|
||||||
<Option name="IsNewStartupCode" value="1"/>
|
|
||||||
<Option name="Library" value="Semihosting"/>
|
|
||||||
<Option name="UserEditLinker" value="-lstdc++; --specs=nano.specs;"/>
|
|
||||||
<LinkedLibraries>
|
|
||||||
<Libset dir="stm32f4xx_lib\cmsis\lib\gcc\" libs="arm_cortexm4lf_math"/>
|
|
||||||
</LinkedLibraries>
|
|
||||||
<MemoryAreas debugInFlashNotRAM="1">
|
|
||||||
<Memory name="IROM1" type="ReadOnly" size="0x00100000" startValue="0x08000000"/>
|
|
||||||
<Memory name="IRAM1" type="ReadWrite" size="0x00020000" startValue="0x20000000"/>
|
|
||||||
<Memory name="IROM2" type="ReadOnly" size="" startValue=""/>
|
|
||||||
<Memory name="IRAM2" type="ReadWrite" size="0x00010000" startValue="0x10000000"/>
|
|
||||||
</MemoryAreas>
|
|
||||||
<LocateLinkFile path="./stm32f4xx_link.ld" type="0"/>
|
|
||||||
</Link>
|
|
||||||
<Output>
|
|
||||||
<Option name="OutputFileType" value="0"/>
|
|
||||||
<Option name="Path" value="./"/>
|
|
||||||
<Option name="Name" value="MMDVM_PI"/>
|
|
||||||
<Option name="HEX" value="1"/>
|
|
||||||
<Option name="BIN" value="1"/>
|
|
||||||
</Output>
|
|
||||||
<User>
|
|
||||||
<UserRun name="Run#1" type="Before" checked="0" value=""/>
|
|
||||||
<UserRun name="Run#1" type="After" checked="0" value=""/>
|
|
||||||
</User>
|
|
||||||
</BuildOption>
|
|
||||||
<DebugOption>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.adapter" value="ST-Link"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.debugMode" value="SWD"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.clockDiv" value="1M"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.corerunToMain" value="1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.jlinkgdbserver" value=""/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.userDefineGDBScript" value=""/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.targetEndianess" value="0"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.jlinkResetMode" value="Type 0: Normal"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.resetMode" value="SYSRESETREQ"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.ifSemihost" value="0"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.ifCacheRom" value="1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.ipAddress" value="127.0.0.1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.portNumber" value="2009"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.autoDownload" value="1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.verify" value="1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.downloadFuction" value="Erase Effected"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.defaultAlgorithm" value="STM32F4xx_1024.elf"/>
|
|
||||||
</DebugOption>
|
|
||||||
<ExcludeFile/>
|
|
||||||
</Target>
|
|
||||||
<Target name="MMDVM_Nucleo_446" isCurrent="0">
|
|
||||||
<Device manufacturerId="9" manufacturerName="ST" chipId="344" chipName="STM32F407VG" boardId="" boardName=""/>
|
|
||||||
<BuildOption>
|
|
||||||
<Compile>
|
|
||||||
<Option name="OptimizationLevel" value="4"/>
|
|
||||||
<Option name="UseFPU" value="0"/>
|
|
||||||
<Option name="UserEditCompiler" value=""/>
|
|
||||||
<Option name="SupportCPlusplus" value="1"/>
|
|
||||||
<Option name="FPU" value="2"/>
|
|
||||||
<Includepaths>
|
|
||||||
<Includepath path="."/>
|
|
||||||
</Includepaths>
|
|
||||||
<DefinedSymbols>
|
|
||||||
<Define name="STM32F4XX"/>
|
|
||||||
<Define name="USE_STDPERIPH_DRIVER"/>
|
|
||||||
<Define name="__ASSEMBLY__"/>
|
|
||||||
<Define name="SUPPORT_CPLUSPLUS"/>
|
|
||||||
<Define name="HSE_VALUE=8000000"/>
|
|
||||||
<Define name="__FPU_USED"/>
|
|
||||||
<Define name="STM32F4_NUCLEO"/>
|
|
||||||
<Define name="STM32F446xx"/>
|
|
||||||
</DefinedSymbols>
|
|
||||||
</Compile>
|
|
||||||
<Link useDefault="0">
|
|
||||||
<Option name="DiscardUnusedSection" value="0"/>
|
|
||||||
<Option name="UserEditLinkder" value=""/>
|
|
||||||
<Option name="UseMemoryLayout" value="0"/>
|
|
||||||
<Option name="nostartfiles" value="1"/>
|
|
||||||
<Option name="LTO" value="0"/>
|
|
||||||
<Option name="IsNewStartupCode" value="1"/>
|
|
||||||
<Option name="Library" value="Semihosting"/>
|
|
||||||
<Option name="UserEditLinker" value="-lstdc++; --specs=nano.specs;"/>
|
|
||||||
<LinkedLibraries>
|
|
||||||
<Libset dir="stm32f4xx_lib\cmsis\lib\gcc\" libs="arm_cortexm4lf_math"/>
|
|
||||||
</LinkedLibraries>
|
|
||||||
<MemoryAreas debugInFlashNotRAM="1">
|
|
||||||
<Memory name="IROM1" type="ReadOnly" size="0x00100000" startValue="0x08000000"/>
|
|
||||||
<Memory name="IRAM1" type="ReadWrite" size="0x00020000" startValue="0x20000000"/>
|
|
||||||
<Memory name="IROM2" type="ReadOnly" size="" startValue=""/>
|
|
||||||
<Memory name="IRAM2" type="ReadWrite" size="0x00010000" startValue="0x10000000"/>
|
|
||||||
</MemoryAreas>
|
|
||||||
<LocateLinkFile path="./stm32f4xx_link.ld" type="0"/>
|
|
||||||
</Link>
|
|
||||||
<Output>
|
|
||||||
<Option name="OutputFileType" value="0"/>
|
|
||||||
<Option name="Path" value="./"/>
|
|
||||||
<Option name="Name" value="MMDVM_NUCLEO"/>
|
|
||||||
<Option name="HEX" value="1"/>
|
|
||||||
<Option name="BIN" value="1"/>
|
|
||||||
</Output>
|
|
||||||
<User>
|
|
||||||
<UserRun name="Run#1" type="Before" checked="0" value=""/>
|
|
||||||
<UserRun name="Run#1" type="After" checked="0" value=""/>
|
|
||||||
</User>
|
|
||||||
</BuildOption>
|
|
||||||
<DebugOption>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.adapter" value="ST-Link"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.debugMode" value="SWD"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.clockDiv" value="1M"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.corerunToMain" value="1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.jlinkgdbserver" value=""/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.userDefineGDBScript" value=""/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.targetEndianess" value="0"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.jlinkResetMode" value="Type 0: Normal"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.resetMode" value="SYSRESETREQ"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.ifSemihost" value="0"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.ifCacheRom" value="1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.ipAddress" value="127.0.0.1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.portNumber" value="2009"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.autoDownload" value="1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.verify" value="1"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.downloadFuction" value="Erase Effected"/>
|
|
||||||
<Option name="org.coocox.codebugger.gdbjtag.core.defaultAlgorithm" value="STM32F4xx_1024.elf"/>
|
|
||||||
</DebugOption>
|
|
||||||
<ExcludeFile/>
|
|
||||||
</Target>
|
|
||||||
<Components path="./">
|
|
||||||
</Components>
|
|
||||||
<Files>
|
|
||||||
<File name="DMRIdleRX.h" path="DMRIdleRX.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/stm32f4xx_dac.c" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/stm32f4xx_dac.c" type="1"/>
|
|
||||||
<File name="DMRDMOTX.cpp" path="DMRDMOTX.cpp" type="1"/>
|
|
||||||
<File name="DMRRX.cpp" path="DMRRX.cpp" type="1"/>
|
|
||||||
<File name="YSFRX.cpp" path="YSFRX.cpp" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/Device/stm32f4xx.h" path="STM32F4XX_Lib/Device/stm32f4xx.h" type="1"/>
|
|
||||||
<File name="SampleRB.cpp" path="SampleRB.cpp" type="1"/>
|
|
||||||
<File name="CalDStarTX.h" path="CalDStarTX.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS/Include/core_cm4.h" path="STM32F4XX_Lib/CMSIS/Include/core_cm4.h" type="1"/>
|
|
||||||
<File name="DMRSlotRX.h" path="DMRSlotRX.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/stm32f4xx_tim.c" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/stm32f4xx_tim.c" type="1"/>
|
|
||||||
<File name="YSFRX.h" path="YSFRX.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS/Include" path="" type="2"/>
|
|
||||||
<File name="IO.cpp" path="IO.cpp" type="1"/>
|
|
||||||
<File name="DMRDMORX.cpp" path="DMRDMORX.cpp" type="1"/>
|
|
||||||
<File name="P25RX.h" path="P25RX.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/misc.c" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/misc.c" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS" path="" type="2"/>
|
|
||||||
<File name="SerialArduino.cpp" path="SerialArduino.cpp" type="1"/>
|
|
||||||
<File name="SerialPort.cpp" path="SerialPort.cpp" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/Device/stm32f4xx_conf.h" path="STM32F4XX_Lib/Device/stm32f4xx_conf.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/stm32f4xx_adc.c" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/stm32f4xx_adc.c" type="1"/>
|
|
||||||
<File name="DStarTX.cpp" path="DStarTX.cpp" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/stm32f4xx_usart.c" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/stm32f4xx_usart.c" type="1"/>
|
|
||||||
<File name="SampleRB.h" path="SampleRB.h" type="1"/>
|
|
||||||
<File name="YSFTX.h" path="YSFTX.h" type="1"/>
|
|
||||||
<File name="DMRRX.h" path="DMRRX.h" type="1"/>
|
|
||||||
<File name="IOTeensy.cpp" path="IOTeensy.cpp" type="1"/>
|
|
||||||
<File name="MMDVM.cpp" path="MMDVM.cpp" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS/Include/core_cmSimd.h" path="STM32F4XX_Lib/CMSIS/Include/core_cmSimd.h" type="1"/>
|
|
||||||
<File name="DMRDMORX.h" path="DMRDMORX.h" type="1"/>
|
|
||||||
<File name="SerialPort.h" path="SerialPort.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/stm32f4xx_dac.h" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/stm32f4xx_dac.h" type="1"/>
|
|
||||||
<File name="CWIdTX.h" path="CWIdTX.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/Device/system_stm32f4xx.h" path="STM32F4XX_Lib/Device/system_stm32f4xx.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS/Include/core_cmFunc.h" path="STM32F4XX_Lib/CMSIS/Include/core_cmFunc.h" type="1"/>
|
|
||||||
<File name="CalDStarTX.cpp" path="CalDStarTX.cpp" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source" path="" type="2"/>
|
|
||||||
<File name="CalDMR.h" path="CalDMR.h" type="1"/>
|
|
||||||
<File name="DMRDMOTX.h" path="DMRDMOTX.h" type="1"/>
|
|
||||||
<File name="IODue.cpp" path="IODue.cpp" type="1"/>
|
|
||||||
<File name="DMRTX.cpp" path="DMRTX.cpp" type="1"/>
|
|
||||||
<File name="P25RX.cpp" path="P25RX.cpp" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS/Lib/GCC" path="" type="2"/>
|
|
||||||
<File name="STM32F4XX_Lib" path="" type="2"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS/Include/core_cmInstr.h" path="STM32F4XX_Lib/CMSIS/Include/core_cmInstr.h" type="1"/>
|
|
||||||
<File name="DMRTX.h" path="DMRTX.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/Device/system_stm32f4xx.c" path="STM32F4XX_Lib/Device/system_stm32f4xx.c" type="1"/>
|
|
||||||
<File name="RSSIRB.h" path="RSSIRB.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/Device/startup/startup_stm32f4xx.c" path="STM32F4XX_Lib/Device/startup/startup_stm32f4xx.c" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/Device/startup" path="" type="2"/>
|
|
||||||
<File name="SerialRB.h" path="SerialRB.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/stm32f4xx_gpio.h" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/stm32f4xx_gpio.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver" path="" type="2"/>
|
|
||||||
<File name="DMRDefines.h" path="DMRDefines.h" type="1"/>
|
|
||||||
<File name="CalDMR.cpp" path="CalDMR.cpp" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/stm32f4xx_adc.h" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/stm32f4xx_adc.h" type="1"/>
|
|
||||||
<File name="SerialSTM.cpp" path="SerialSTM.cpp" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS/Lib/GCC/libarm_cortexM4lf_math.a" path="STM32F4XX_Lib/CMSIS/Lib/GCC/libarm_cortexM4lf_math.a" type="1"/>
|
|
||||||
<File name="DStarDefines.h" path="DStarDefines.h" type="1"/>
|
|
||||||
<File name="DStarRX.h" path="DStarRX.h" type="1"/>
|
|
||||||
<File name="Config.h" path="Config.h" type="1"/>
|
|
||||||
<File name="DMRSlotType.cpp" path="DMRSlotType.cpp" type="1"/>
|
|
||||||
<File name="Globals.h" path="Globals.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/Device" path="" type="2"/>
|
|
||||||
<File name="CalDStarRX.cpp" path="CalDStarRX.cpp" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/stm32f4xx_tim.h" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/stm32f4xx_tim.h" type="1"/>
|
|
||||||
<File name="CalDStarRX.h" path="CalDStarRX.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include" path="" type="2"/>
|
|
||||||
<File name="IO.h" path="IO.h" type="1"/>
|
|
||||||
<File name="Utils.cpp" path="Utils.cpp" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS/.DS_Store" path="STM32F4XX_Lib/CMSIS/.DS_Store" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS/Lib/.DS_Store" path="STM32F4XX_Lib/CMSIS/Lib/.DS_Store" type="1"/>
|
|
||||||
<File name="Utils.h" path="Utils.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS/Lib" path="" type="2"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/.DS_Store" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/.DS_Store" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS/Include/.DS_Store" path="STM32F4XX_Lib/CMSIS/Include/.DS_Store" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/CMSIS/Include/arm_math.h" path="STM32F4XX_Lib/CMSIS/Include/arm_math.h" type="1"/>
|
|
||||||
<File name="YSFDefines.h" path="YSFDefines.h" type="1"/>
|
|
||||||
<File name="DStarTX.h" path="DStarTX.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/stm32f4xx_rcc.c" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/stm32f4xx_rcc.c" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/Device/.DS_Store" path="STM32F4XX_Lib/Device/.DS_Store" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/stm32f4xx_rcc.h" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/stm32f4xx_rcc.h" type="1"/>
|
|
||||||
<File name="P25Defines.h" path="P25Defines.h" type="1"/>
|
|
||||||
<File name="DMRSlotType.h" path="DMRSlotType.h" type="1"/>
|
|
||||||
<File name="Debug.h" path="Debug.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/misc.h" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/misc.h" type="1"/>
|
|
||||||
<File name="P25TX.h" path="P25TX.h" type="1"/>
|
|
||||||
<File name="RSSIRB.cpp" path="RSSIRB.cpp" type="1"/>
|
|
||||||
<File name="DStarRX.cpp" path="DStarRX.cpp" type="1"/>
|
|
||||||
<File name="DMRSlotRX.cpp" path="DMRSlotRX.cpp" type="1"/>
|
|
||||||
<File name="IOSTM.cpp" path="IOSTM.cpp" type="1"/>
|
|
||||||
<File name="P25TX.cpp" path="P25TX.cpp" type="1"/>
|
|
||||||
<File name="SerialRB.cpp" path="SerialRB.cpp" type="1"/>
|
|
||||||
<File name="CWIdTX.cpp" path="CWIdTX.cpp" type="1"/>
|
|
||||||
<File name="DMRIdleRX.cpp" path="DMRIdleRX.cpp" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/stm32f4xx_usart.h" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/stm32f4xx_usart.h" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/.DS_Store" path="STM32F4XX_Lib/.DS_Store" type="1"/>
|
|
||||||
<File name="YSFTX.cpp" path="YSFTX.cpp" type="1"/>
|
|
||||||
<File name="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/stm32f4xx_gpio.c" path="STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/source/stm32f4xx_gpio.c" type="1"/>
|
|
||||||
</Files>
|
|
||||||
</Project>
|
|
||||||
Binary file not shown.
@@ -1,173 +0,0 @@
|
|||||||
# Copyright (C) 2016 by Andy Uribe CA6JAU
|
|
||||||
# Copyright (C) 2016 by Jim McLaughlin KI6ZUM
|
|
||||||
|
|
||||||
# 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; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
|
|
||||||
# GNU ARM Embedded Toolchain
|
|
||||||
CC=arm-none-eabi-gcc
|
|
||||||
CXX=arm-none-eabi-g++
|
|
||||||
LD=arm-none-eabi-ld
|
|
||||||
AR=arm-none-eabi-ar
|
|
||||||
AS=arm-none-eabi-as
|
|
||||||
CP=arm-none-eabi-objcopy
|
|
||||||
OD=arm-none-eabi-objdump
|
|
||||||
NM=arm-none-eabi-nm
|
|
||||||
SIZE=arm-none-eabi-size
|
|
||||||
A2L=arm-none-eabi-addr2line
|
|
||||||
|
|
||||||
# Directory Structure
|
|
||||||
BINDIR=bin
|
|
||||||
|
|
||||||
# Find source files
|
|
||||||
# "SystemRoot" is only defined in Windows
|
|
||||||
ifdef SYSTEMROOT
|
|
||||||
ASOURCES=$(shell dir /S /B *.s)
|
|
||||||
CSOURCES=$(shell dir /S /B *.c)
|
|
||||||
CXXSOURCES=$(shell dir /S /B *.cpp)
|
|
||||||
CLEANCMD=del /S *.o *.hex *.bin *.elf
|
|
||||||
MDBIN=md $@
|
|
||||||
else ifdef SystemRoot
|
|
||||||
ASOURCES=$(shell dir /S /B *.s)
|
|
||||||
CSOURCES=$(shell dir /S /B *.c)
|
|
||||||
CXXSOURCES=$(shell dir /S /B *.cpp)
|
|
||||||
CLEANCMD=del /S *.o *.hex *.bin *.elf
|
|
||||||
MDBIN=md $@
|
|
||||||
else
|
|
||||||
ASOURCES=$(shell find . -name '*.s')
|
|
||||||
CSOURCES=$(shell find . -name '*.c')
|
|
||||||
CXXSOURCES=$(shell find . -name '*.cpp')
|
|
||||||
CLEANCMD=rm -f $(OBJECTS) $(BINDIR)/$(BINELF) $(BINDIR)/$(BINHEX) $(BINDIR)/$(BINBIN)
|
|
||||||
MDBIN=mkdir $@
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Default reference oscillator frequencies
|
|
||||||
ifndef $(OSC)
|
|
||||||
ifeq ($(MAKECMDGOALS),pi)
|
|
||||||
OSC=12000000
|
|
||||||
else
|
|
||||||
OSC=8000000
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Find header directories
|
|
||||||
INC= . STM32F4XX_Lib/CMSIS/Include/ STM32F4XX_Lib/Device/ STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/
|
|
||||||
INCLUDES=$(INC:%=-I%)
|
|
||||||
|
|
||||||
# Find libraries
|
|
||||||
INCLUDES_LIBS=STM32F4XX_Lib/CMSIS/Lib/GCC/libarm_cortexM4lf_math.a
|
|
||||||
LINK_LIBS=
|
|
||||||
|
|
||||||
# Create object list
|
|
||||||
OBJECTS=$(ASOURCES:%.s=%.o)
|
|
||||||
OBJECTS+=$(CSOURCES:%.c=%.o)
|
|
||||||
OBJECTS+=$(CXXSOURCES:%.cpp=%.o)
|
|
||||||
|
|
||||||
# Define output files ELF & IHEX
|
|
||||||
BINELF=outp.elf
|
|
||||||
BINHEX=outp.hex
|
|
||||||
BINBIN=outp.bin
|
|
||||||
|
|
||||||
# MCU FLAGS
|
|
||||||
MCFLAGS=-mcpu=cortex-m4 -mthumb -mlittle-endian \
|
|
||||||
-mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb-interwork
|
|
||||||
|
|
||||||
# COMPILE FLAGS
|
|
||||||
# STM32F4 Discovery board:
|
|
||||||
DEFS_DIS=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F40_41xxx -DSTM32F4_DISCOVERY -DHSE_VALUE=$(OSC)
|
|
||||||
# Pi board:
|
|
||||||
DEFS_PI=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_PI -DARDUINO_MODE_PINS -DSEND_RSSI_DATA -DSERIAL_REPEATER -DHSE_VALUE=$(OSC)
|
|
||||||
# STM32F4 Nucleo 446 board:
|
|
||||||
DEFS_NUCLEO=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_NUCLEO -DHSE_VALUE=$(OSC)
|
|
||||||
|
|
||||||
CFLAGS=-c $(MCFLAGS) $(INCLUDES)
|
|
||||||
CXXFLAGS=-c $(MCFLAGS) $(INCLUDES)
|
|
||||||
|
|
||||||
# LINKER FLAGS
|
|
||||||
LDSCRIPT=stm32f4xx_link.ld
|
|
||||||
LDFLAGS =-T $(LDSCRIPT) $(MCFLAGS) --specs=nosys.specs $(INCLUDES_LIBS) $(LINK_LIBS)
|
|
||||||
|
|
||||||
# Build Rules
|
|
||||||
.PHONY: all release dis pi nucleo debug clean
|
|
||||||
|
|
||||||
# Default target: STM32F4 Discovery board
|
|
||||||
all: dis
|
|
||||||
|
|
||||||
pi: CFLAGS+=$(DEFS_PI) -Os -ffunction-sections -fdata-sections -fno-builtin -Wno-implicit -DCUSTOM_NEW -DNO_EXCEPTIONS
|
|
||||||
pi: CXXFLAGS+=$(DEFS_PI) -Os -fno-exceptions -ffunction-sections -fdata-sections -fno-builtin -fno-rtti -DCUSTOM_NEW -DNO_EXCEPTIONS
|
|
||||||
pi: LDFLAGS+=-Os --specs=nano.specs
|
|
||||||
pi: release
|
|
||||||
|
|
||||||
nucleo: CFLAGS+=$(DEFS_NUCLEO) -Os -ffunction-sections -fdata-sections -fno-builtin -Wno-implicit -DCUSTOM_NEW -DNO_EXCEPTIONS
|
|
||||||
nucleo: CXXFLAGS+=$(DEFS_NUCLEO) -Os -fno-exceptions -ffunction-sections -fdata-sections -fno-builtin -fno-rtti -DCUSTOM_NEW -DNO_EXCEPTIONS
|
|
||||||
nucleo: LDFLAGS+=-Os --specs=nano.specs
|
|
||||||
nucleo: release
|
|
||||||
|
|
||||||
dis: CFLAGS+=$(DEFS_DIS) -Os -ffunction-sections -fdata-sections -fno-builtin -Wno-implicit -DCUSTOM_NEW -DNO_EXCEPTIONS
|
|
||||||
dis: CXXFLAGS+=$(DEFS_DIS) -Os -fno-exceptions -ffunction-sections -fdata-sections -fno-builtin -fno-rtti -DCUSTOM_NEW -DNO_EXCEPTIONS
|
|
||||||
dis: LDFLAGS+=-Os --specs=nano.specs
|
|
||||||
dis: release
|
|
||||||
|
|
||||||
debug: CFLAGS+=-g
|
|
||||||
debug: CXXFLAGS+=-g
|
|
||||||
debug: LDFLAGS+=-g
|
|
||||||
debug: release
|
|
||||||
|
|
||||||
release: $(BINDIR)
|
|
||||||
release: $(BINDIR)/$(BINHEX)
|
|
||||||
release: $(BINDIR)/$(BINBIN)
|
|
||||||
|
|
||||||
$(BINDIR):
|
|
||||||
$(MDBIN)
|
|
||||||
|
|
||||||
$(BINDIR)/$(BINHEX): $(BINDIR)/$(BINELF)
|
|
||||||
$(CP) -O ihex $< $@
|
|
||||||
@echo "Objcopy from ELF to IHEX complete!\n"
|
|
||||||
|
|
||||||
$(BINDIR)/$(BINBIN): $(BINDIR)/$(BINELF)
|
|
||||||
$(CP) -O binary $< $@
|
|
||||||
@echo "Objcopy from ELF to BINARY complete!\n"
|
|
||||||
|
|
||||||
$(BINDIR)/$(BINELF): $(OBJECTS)
|
|
||||||
$(CXX) $(OBJECTS) $(LDFLAGS) -o $@
|
|
||||||
@echo "Linking complete!\n"
|
|
||||||
$(SIZE) $(BINDIR)/$(BINELF)
|
|
||||||
|
|
||||||
%.o: %.cpp
|
|
||||||
$(CXX) $(CXXFLAGS) $< -o $@
|
|
||||||
@echo "Compiled "$<"!\n"
|
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
$(CC) $(CFLAGS) $< -o $@
|
|
||||||
@echo "Compiled "$<"!\n"
|
|
||||||
|
|
||||||
%.o: %.s
|
|
||||||
$(CC) $(CFLAGS) $< -o $@
|
|
||||||
@echo "Assambled "$<"!\n"
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(CLEANCMD)
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
ifneq ($(wildcard /usr/bin/openocd),)
|
|
||||||
/usr/bin/openocd -f /usr/share/openocd/scripts/board/stm32f4discovery.cfg -c "program bin/$(BINELF) verify reset exit"
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ($(wildcard /usr/local/bin/openocd),)
|
|
||||||
/usr/local/bin/openocd -f /usr/local/share/openocd/scripts/board/stm32f4discovery.cfg -c "program bin/$(BINELF) verify reset exit"
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ($(wildcard /opt/openocd/bin/openocd),)
|
|
||||||
/opt/openocd/bin/openocd -f /opt/openocd/share/openocd/scripts/board/stm32f4discovery.cfg -c "program bin/$(BINELF) verify reset exit"
|
|
||||||
endif
|
|
||||||
@@ -1,262 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// #define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
#include "P25RX.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
|
|
||||||
const unsigned int BUFFER_LENGTH = 200U;
|
|
||||||
|
|
||||||
const q15_t SCALING_FACTOR = 18750; // Q15(0.55)
|
|
||||||
|
|
||||||
const uint32_t PLLMAX = 0x10000U;
|
|
||||||
const uint32_t PLLINC = PLLMAX / P25_RADIO_SYMBOL_LENGTH;
|
|
||||||
const uint32_t INC = PLLINC / 32U;
|
|
||||||
|
|
||||||
const uint8_t SYNC_SYMBOL_ERRS = 0U;
|
|
||||||
|
|
||||||
const uint8_t SYNC_BIT_START_ERRS = 2U;
|
|
||||||
const uint8_t SYNC_BIT_RUN_ERRS = 4U;
|
|
||||||
|
|
||||||
const unsigned int MAX_SYNC_FRAMES = 3U + 1U;
|
|
||||||
|
|
||||||
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
|
||||||
|
|
||||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
|
||||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
|
||||||
|
|
||||||
CP25RX::CP25RX() :
|
|
||||||
m_pll(0U),
|
|
||||||
m_prev(false),
|
|
||||||
m_state(P25RXS_NONE),
|
|
||||||
m_bitBuffer(0x00U),
|
|
||||||
m_symbols(),
|
|
||||||
m_outBuffer(),
|
|
||||||
m_buffer(NULL),
|
|
||||||
m_bufferPtr(0U),
|
|
||||||
m_symbolPtr(0U),
|
|
||||||
m_lostCount(0U),
|
|
||||||
m_centre(0),
|
|
||||||
m_threshold(0)
|
|
||||||
{
|
|
||||||
m_buffer = m_outBuffer + 1U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CP25RX::reset()
|
|
||||||
{
|
|
||||||
m_pll = 0U;
|
|
||||||
m_prev = false;
|
|
||||||
m_state = P25RXS_NONE;
|
|
||||||
m_bitBuffer = 0x00U;
|
|
||||||
m_bufferPtr = 0U;
|
|
||||||
m_symbolPtr = 0U;
|
|
||||||
m_lostCount = 0U;
|
|
||||||
m_centre = 0;
|
|
||||||
m_threshold = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CP25RX::samples(const q15_t* samples, uint8_t length)
|
|
||||||
{
|
|
||||||
for (uint16_t i = 0U; i < length; i++) {
|
|
||||||
bool bit = samples[i] < 0;
|
|
||||||
|
|
||||||
if (bit != m_prev) {
|
|
||||||
if (m_pll < (PLLMAX / 2U))
|
|
||||||
m_pll += INC;
|
|
||||||
else
|
|
||||||
m_pll -= INC;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_prev = bit;
|
|
||||||
|
|
||||||
m_pll += PLLINC;
|
|
||||||
|
|
||||||
if (m_pll >= PLLMAX) {
|
|
||||||
m_pll -= PLLMAX;
|
|
||||||
|
|
||||||
if (m_state == P25RXS_NONE)
|
|
||||||
processNone(samples[i]);
|
|
||||||
else
|
|
||||||
processData(samples[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CP25RX::processNone(q15_t sample)
|
|
||||||
{
|
|
||||||
m_symbolBuffer <<= 1;
|
|
||||||
if (sample < 0)
|
|
||||||
m_symbolBuffer |= 0x01U;
|
|
||||||
|
|
||||||
m_symbols[m_symbolPtr] = sample;
|
|
||||||
|
|
||||||
// Fuzzy matching of the data sync bit sequence
|
|
||||||
if (countBits32((m_symbolBuffer & P25_SYNC_SYMBOLS_MASK) ^ P25_SYNC_SYMBOLS) <= SYNC_SYMBOL_ERRS) {
|
|
||||||
q15_t max = -16000;
|
|
||||||
q15_t min = 16000;
|
|
||||||
|
|
||||||
for (uint8_t i = 0U; i < P25_SYNC_LENGTH_SYMBOLS; i++) {
|
|
||||||
q15_t val = m_symbols[i];
|
|
||||||
if (val > max)
|
|
||||||
max = val;
|
|
||||||
if (val < min)
|
|
||||||
min = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
q15_t centre = (max + min) >> 1;
|
|
||||||
|
|
||||||
q31_t v1 = (max - centre) * SCALING_FACTOR;
|
|
||||||
q15_t threshold = q15_t(v1 >> 15);
|
|
||||||
|
|
||||||
uint16_t ptr = m_symbolPtr + 1U;
|
|
||||||
if (ptr >= P25_SYNC_LENGTH_SYMBOLS)
|
|
||||||
ptr = 0U;
|
|
||||||
|
|
||||||
for (uint8_t i = 0U; i < P25_SYNC_LENGTH_SYMBOLS; i++) {
|
|
||||||
q15_t sample = m_symbols[ptr] - centre;
|
|
||||||
|
|
||||||
if (sample < -threshold) {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x01U;
|
|
||||||
} else if (sample < 0) {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x00U;
|
|
||||||
} else if (sample < threshold) {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x02U;
|
|
||||||
} else {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x03U;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr++;
|
|
||||||
if (ptr >= P25_SYNC_LENGTH_SYMBOLS)
|
|
||||||
ptr = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fuzzy matching of the data sync bit sequence
|
|
||||||
if (countBits64((m_bitBuffer & P25_SYNC_BITS_MASK) ^ P25_SYNC_BITS) <= SYNC_BIT_START_ERRS) {
|
|
||||||
DEBUG5("P25RX: sync found in None min/max/centre/threshold", min, max, centre, threshold);
|
|
||||||
for (uint8_t i = 0U; i < P25_SYNC_LENGTH_BYTES; i++)
|
|
||||||
m_buffer[i] = P25_SYNC_BYTES[i];
|
|
||||||
m_centre = centre;
|
|
||||||
m_threshold = threshold;
|
|
||||||
m_lostCount = MAX_SYNC_FRAMES;
|
|
||||||
m_bufferPtr = P25_SYNC_LENGTH_BITS;
|
|
||||||
m_state = P25RXS_DATA;
|
|
||||||
|
|
||||||
io.setDecode(true);
|
|
||||||
io.setADCDetection(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_symbolPtr++;
|
|
||||||
if (m_symbolPtr >= P25_SYNC_LENGTH_SYMBOLS)
|
|
||||||
m_symbolPtr = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CP25RX::processData(q15_t sample)
|
|
||||||
{
|
|
||||||
sample -= m_centre;
|
|
||||||
|
|
||||||
if (sample < -m_threshold) {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x01U;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
|
||||||
m_bufferPtr++;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
|
||||||
m_bufferPtr++;
|
|
||||||
} else if (sample < 0) {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x00U;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
|
||||||
m_bufferPtr++;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
|
||||||
m_bufferPtr++;
|
|
||||||
} else if (sample < m_threshold) {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x02U;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
|
||||||
m_bufferPtr++;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
|
||||||
m_bufferPtr++;
|
|
||||||
} else {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x03U;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
|
||||||
m_bufferPtr++;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
|
||||||
m_bufferPtr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for an early sync to indicate an LDU following a header
|
|
||||||
if (m_bufferPtr >= (P25_HDR_FRAME_LENGTH_BITS + P25_SYNC_LENGTH_BITS - 1U) && m_bufferPtr <= (P25_HDR_FRAME_LENGTH_BITS + P25_SYNC_LENGTH_BITS + 1U)) {
|
|
||||||
// Fuzzy matching of the data sync bit sequence
|
|
||||||
if (countBits64((m_bitBuffer & P25_SYNC_BITS_MASK) ^ P25_SYNC_BITS) <= SYNC_BIT_RUN_ERRS) {
|
|
||||||
DEBUG2("P25RX: found LDU sync in Data, pos", m_bufferPtr - P25_SYNC_LENGTH_BITS);
|
|
||||||
|
|
||||||
m_outBuffer[0U] = 0x01U;
|
|
||||||
serial.writeP25Hdr(m_outBuffer, P25_HDR_FRAME_LENGTH_BYTES + 1U);
|
|
||||||
|
|
||||||
// Restore the sync that's now in the wrong place
|
|
||||||
for (uint8_t i = 0U; i < P25_SYNC_LENGTH_BYTES; i++)
|
|
||||||
m_buffer[i] = P25_SYNC_BYTES[i];
|
|
||||||
|
|
||||||
m_lostCount = MAX_SYNC_FRAMES;
|
|
||||||
m_bufferPtr = P25_SYNC_LENGTH_BITS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only search for a sync in the right place +-2 symbols
|
|
||||||
if (m_bufferPtr >= (P25_SYNC_LENGTH_BITS - 2U) && m_bufferPtr <= (P25_SYNC_LENGTH_BITS + 2U)) {
|
|
||||||
// Fuzzy matching of the data sync bit sequence
|
|
||||||
if (countBits64((m_bitBuffer & P25_SYNC_BITS_MASK) ^ P25_SYNC_BITS) <= SYNC_BIT_RUN_ERRS) {
|
|
||||||
DEBUG2("P25RX: found sync in Data, pos", m_bufferPtr - P25_SYNC_LENGTH_BITS);
|
|
||||||
m_lostCount = MAX_SYNC_FRAMES;
|
|
||||||
m_bufferPtr = P25_SYNC_LENGTH_BITS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a data frame to the host if the required number of bits have been received
|
|
||||||
if (m_bufferPtr == P25_LDU_FRAME_LENGTH_BITS) {
|
|
||||||
// We've not seen a data sync for too long, signal RXLOST and change to RX_NONE
|
|
||||||
m_lostCount--;
|
|
||||||
if (m_lostCount == 0U) {
|
|
||||||
DEBUG1("P25RX: sync timed out, lost lock");
|
|
||||||
|
|
||||||
io.setDecode(false);
|
|
||||||
io.setADCDetection(false);
|
|
||||||
|
|
||||||
serial.writeP25Lost();
|
|
||||||
|
|
||||||
m_state = P25RXS_NONE;
|
|
||||||
} else {
|
|
||||||
m_outBuffer[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
|
|
||||||
|
|
||||||
serial.writeP25Ldu(m_outBuffer, P25_LDU_FRAME_LENGTH_BYTES + 1U);
|
|
||||||
|
|
||||||
// Start the next frame
|
|
||||||
::memset(m_outBuffer, 0x00U, P25_LDU_FRAME_LENGTH_BYTES + 3U);
|
|
||||||
m_bufferPtr = 0U;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// #define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
#include "P25TX.h"
|
|
||||||
|
|
||||||
#include "P25Defines.h"
|
|
||||||
|
|
||||||
#if defined(WIDE_C4FSK_FILTERS_TX)
|
|
||||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
|
||||||
static q15_t P25_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
|
||||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
|
||||||
const uint16_t P25_C4FSK_FILTER_LEN = 22U;
|
|
||||||
#else
|
|
||||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
|
||||||
static q15_t P25_C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
|
||||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
|
||||||
-553, -847, -731, -340, 104, 401, 0};
|
|
||||||
const uint16_t P25_C4FSK_FILTER_LEN = 42U;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Generated in MATLAB using the following commands, and then normalised for unity gain
|
|
||||||
// shape2 = 'Inverse-sinc Lowpass';
|
|
||||||
// d2 = fdesign.interpolator(2, shape2);
|
|
||||||
// h2 = design(d2, 'SystemObject', true);
|
|
||||||
static q15_t P25_LP_FILTER[] = {170, 401, 340, -203, -715, -478, 281, 419, -440, -1002, -103, 1114, 528, -1389, -1520, 1108, 2674, -388, -4662,
|
|
||||||
-2132, 9168, 20241, 20241, 9168, -2132, -4662, -388, 2674, 1108, -1520, -1389, 528, 1114, -103, -1002, -440, 419,
|
|
||||||
281, -478, -715, -203, 340, 401, 170};
|
|
||||||
const uint16_t P25_LP_FILTER_LEN = 44U;
|
|
||||||
|
|
||||||
const q15_t P25_LEVELA[] = { 495, 495, 495, 495, 495};
|
|
||||||
const q15_t P25_LEVELB[] = { 165, 165, 165, 165, 165};
|
|
||||||
const q15_t P25_LEVELC[] = {-165, -165, -165, -165, -165};
|
|
||||||
const q15_t P25_LEVELD[] = {-495, -495, -495, -495, -495};
|
|
||||||
|
|
||||||
const uint8_t P25_START_SYNC = 0x77U;
|
|
||||||
|
|
||||||
CP25TX::CP25TX() :
|
|
||||||
m_buffer(1500U),
|
|
||||||
m_modFilter(),
|
|
||||||
m_lpFilter(),
|
|
||||||
m_modState(),
|
|
||||||
m_lpState(),
|
|
||||||
m_poBuffer(),
|
|
||||||
m_poLen(0U),
|
|
||||||
m_poPtr(0U),
|
|
||||||
m_txDelay(240U), // 200ms
|
|
||||||
m_count(0U)
|
|
||||||
{
|
|
||||||
::memset(m_modState, 0x00U, 70U * sizeof(q15_t));
|
|
||||||
::memset(m_lpState, 0x00U, 70U * sizeof(q15_t));
|
|
||||||
|
|
||||||
m_modFilter.numTaps = P25_C4FSK_FILTER_LEN;
|
|
||||||
m_modFilter.pState = m_modState;
|
|
||||||
m_modFilter.pCoeffs = P25_C4FSK_FILTER;
|
|
||||||
|
|
||||||
m_lpFilter.numTaps = P25_LP_FILTER_LEN;
|
|
||||||
m_lpFilter.pState = m_lpState;
|
|
||||||
m_lpFilter.pCoeffs = P25_LP_FILTER;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CP25TX::process()
|
|
||||||
{
|
|
||||||
if (m_buffer.getData() == 0U && m_poLen == 0U)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_poLen == 0U) {
|
|
||||||
if (!m_tx) {
|
|
||||||
m_count = 0U;
|
|
||||||
|
|
||||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
|
||||||
m_poBuffer[m_poLen++] = P25_START_SYNC;
|
|
||||||
} else {
|
|
||||||
uint8_t length = m_buffer.get();
|
|
||||||
for (uint8_t i = 0U; i < length; i++) {
|
|
||||||
uint8_t c = m_buffer.get();
|
|
||||||
m_poBuffer[m_poLen++] = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_poPtr = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_poLen > 0U) {
|
|
||||||
uint16_t space = io.getSpace();
|
|
||||||
|
|
||||||
while (space > (4U * P25_RADIO_SYMBOL_LENGTH)) {
|
|
||||||
uint8_t c = m_poBuffer[m_poPtr++];
|
|
||||||
writeByte(c);
|
|
||||||
|
|
||||||
space -= 4U * P25_RADIO_SYMBOL_LENGTH;
|
|
||||||
|
|
||||||
if (m_poPtr >= m_poLen) {
|
|
||||||
m_poPtr = 0U;
|
|
||||||
m_poLen = 0U;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t CP25TX::writeData(const uint8_t* data, uint8_t length)
|
|
||||||
{
|
|
||||||
if (length < (P25_TERM_FRAME_LENGTH_BYTES + 1U))
|
|
||||||
return 4U;
|
|
||||||
|
|
||||||
uint16_t space = m_buffer.getSpace();
|
|
||||||
if (space < length)
|
|
||||||
return 5U;
|
|
||||||
|
|
||||||
m_buffer.put(length - 1U);
|
|
||||||
for (uint8_t i = 0U; i < (length - 1U); i++)
|
|
||||||
m_buffer.put(data[i + 1U]);
|
|
||||||
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CP25TX::writeByte(uint8_t c)
|
|
||||||
{
|
|
||||||
q15_t inBuffer[P25_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
|
||||||
q15_t intBuffer[P25_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
|
||||||
q15_t outBuffer[P25_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
|
||||||
|
|
||||||
const uint8_t MASK = 0xC0U;
|
|
||||||
|
|
||||||
q15_t* p = inBuffer;
|
|
||||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += P25_RADIO_SYMBOL_LENGTH) {
|
|
||||||
switch (c & MASK) {
|
|
||||||
case 0xC0U:
|
|
||||||
::memcpy(p, P25_LEVELA, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
|
||||||
break;
|
|
||||||
case 0x80U:
|
|
||||||
::memcpy(p, P25_LEVELB, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
|
||||||
break;
|
|
||||||
case 0x00U:
|
|
||||||
::memcpy(p, P25_LEVELC, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
::memcpy(p, P25_LEVELD, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t blockSize = P25_RADIO_SYMBOL_LENGTH * 4U;
|
|
||||||
|
|
||||||
// Handle the case of the oscillator not being accurate enough
|
|
||||||
if (m_sampleCount > 0U) {
|
|
||||||
m_count += P25_RADIO_SYMBOL_LENGTH * 4U;
|
|
||||||
|
|
||||||
if (m_count >= m_sampleCount) {
|
|
||||||
if (m_sampleInsert) {
|
|
||||||
inBuffer[P25_RADIO_SYMBOL_LENGTH * 4U] = inBuffer[P25_RADIO_SYMBOL_LENGTH * 4U - 1U];
|
|
||||||
blockSize++;
|
|
||||||
} else {
|
|
||||||
blockSize--;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_count -= m_sampleCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, intBuffer, blockSize);
|
|
||||||
|
|
||||||
::arm_fir_fast_q15(&m_lpFilter, intBuffer, outBuffer, blockSize);
|
|
||||||
|
|
||||||
io.write(STATE_P25, outBuffer, blockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CP25TX::setTXDelay(uint8_t delay)
|
|
||||||
{
|
|
||||||
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t CP25TX::getSpace() const
|
|
||||||
{
|
|
||||||
return m_buffer.getSpace() / P25_LDU_FRAME_LENGTH_BYTES;
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,35 @@
|
|||||||
This is the source code of the MMDVM firmware that supports D-Star, DMR, System Fusion and P25.
|
This is the source code of the MMDVM firmware that supports D-Star, DMR, System Fusion, P25, NXDN, M17, POCSAG, AX.25, and FM modes.
|
||||||
|
|
||||||
It runs on the Arduino Due and the ST-Micro STM32F4xx platforms. Support for the Teensy (3.1/3.2 and 3.5/3.6) is being added.
|
It runs on the Arduino Due, the ST-Micro STM32F4xxx and STM32F7xxx processors, as well as the Teensy 3.5/3.6. What these platforms have in common is the use of an ARM Cortex-M3, M4, or M7 processors with a minimum clock speed greater of 80 MHz, and access to at least one analogue to digital converter, one digital to analogue converter, as well as a number of GPIO pins.
|
||||||
|
|
||||||
In order to build this software for the Arduino Due, you will need to edit a file within the Arduino GUI and that is detailed in the BUILD.txt file. The STM32 support is being supplied via the Coocox IDE with ARM GCC. The Teensy support is via the latest beta of Teensyduino.
|
In order to build this software for the Arduino Due, you will need to edit a file within the Arduino GUI and that is detailed in the BUILD.txt file. The STM32 support is supplied via the ARM GCC compiler. The Teensy support uses Teensyduino.
|
||||||
|
|
||||||
This software is licenced under the GPL v2 and is intended for amateur and educational use only. Use of this software for commercial purposes is strictly forbidden.
|
This software is licenced under the GPL v2 and is primarily intended for amateur and educational use.
|
||||||
|
|
||||||
|
Portions of the ARM support code include the following copyright:
|
||||||
|
|
||||||
|
Copyright (c) 2011 - 2013 ARM LIMITED
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
- Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
- Neither the name of ARM nor the names of its contributors may be used
|
||||||
|
to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|||||||
-104
@@ -1,104 +0,0 @@
|
|||||||
/*
|
|
||||||
TX fifo control - Copyright (C) KI6ZUM 2015
|
|
||||||
Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the
|
|
||||||
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "RSSIRB.h"
|
|
||||||
|
|
||||||
CRSSIRB::CRSSIRB(uint16_t length) :
|
|
||||||
m_length(length),
|
|
||||||
m_rssi(NULL),
|
|
||||||
m_head(0U),
|
|
||||||
m_tail(0U),
|
|
||||||
m_full(false),
|
|
||||||
m_overflow(false)
|
|
||||||
{
|
|
||||||
m_rssi = new uint16_t[length];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t CRSSIRB::getSpace() const
|
|
||||||
{
|
|
||||||
uint16_t n = 0U;
|
|
||||||
|
|
||||||
if (m_tail == m_head)
|
|
||||||
n = m_full ? 0U : m_length;
|
|
||||||
else if (m_tail < m_head)
|
|
||||||
n = m_length - m_head + m_tail;
|
|
||||||
else
|
|
||||||
n = m_tail - m_head;
|
|
||||||
|
|
||||||
if (n > m_length)
|
|
||||||
n = 0U;
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t CRSSIRB::getData() const
|
|
||||||
{
|
|
||||||
if (m_tail == m_head)
|
|
||||||
return m_full ? m_length : 0U;
|
|
||||||
else if (m_tail < m_head)
|
|
||||||
return m_head - m_tail;
|
|
||||||
else
|
|
||||||
return m_length - m_tail + m_head;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CRSSIRB::put(uint16_t rssi)
|
|
||||||
{
|
|
||||||
if (m_full) {
|
|
||||||
m_overflow = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_rssi[m_head] = rssi;
|
|
||||||
|
|
||||||
m_head++;
|
|
||||||
if (m_head >= m_length)
|
|
||||||
m_head = 0U;
|
|
||||||
|
|
||||||
if (m_head == m_tail)
|
|
||||||
m_full = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CRSSIRB::get(uint16_t& rssi)
|
|
||||||
{
|
|
||||||
if (m_head == m_tail && !m_full)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
rssi = m_rssi[m_tail];
|
|
||||||
|
|
||||||
m_full = false;
|
|
||||||
|
|
||||||
m_tail++;
|
|
||||||
if (m_tail >= m_length)
|
|
||||||
m_tail = 0U;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CRSSIRB::hasOverflowed()
|
|
||||||
{
|
|
||||||
bool overflow = m_overflow;
|
|
||||||
|
|
||||||
m_overflow = false;
|
|
||||||
|
|
||||||
return overflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
Serial fifo control - Copyright (C) KI6ZUM 2015
|
|
||||||
Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the
|
|
||||||
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(RSSIRB_H)
|
|
||||||
#define RSSIRB_H
|
|
||||||
|
|
||||||
#if defined(STM32F4XX) || defined(STM32F4)
|
|
||||||
#include "stm32f4xx.h"
|
|
||||||
#include <cstddef>
|
|
||||||
#else
|
|
||||||
#include <Arduino.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class CRSSIRB {
|
|
||||||
public:
|
|
||||||
CRSSIRB(uint16_t length);
|
|
||||||
|
|
||||||
uint16_t getSpace() const;
|
|
||||||
|
|
||||||
uint16_t getData() const;
|
|
||||||
|
|
||||||
bool put(uint16_t rssi);
|
|
||||||
|
|
||||||
bool get(uint16_t& rssi);
|
|
||||||
|
|
||||||
bool hasOverflowed();
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint16_t m_length;
|
|
||||||
volatile uint16_t* m_rssi;
|
|
||||||
volatile uint16_t m_head;
|
|
||||||
volatile uint16_t m_tail;
|
|
||||||
volatile bool m_full;
|
|
||||||
bool m_overflow;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
-108
@@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
TX fifo control - Copyright (C) KI6ZUM 2015
|
|
||||||
Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the
|
|
||||||
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "SampleRB.h"
|
|
||||||
|
|
||||||
CSampleRB::CSampleRB(uint16_t length) :
|
|
||||||
m_length(length),
|
|
||||||
m_samples(NULL),
|
|
||||||
m_control(NULL),
|
|
||||||
m_head(0U),
|
|
||||||
m_tail(0U),
|
|
||||||
m_full(false),
|
|
||||||
m_overflow(false)
|
|
||||||
{
|
|
||||||
m_samples = new uint16_t[length];
|
|
||||||
m_control = new uint8_t[length];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t CSampleRB::getSpace() const
|
|
||||||
{
|
|
||||||
uint16_t n = 0U;
|
|
||||||
|
|
||||||
if (m_tail == m_head)
|
|
||||||
n = m_full ? 0U : m_length;
|
|
||||||
else if (m_tail < m_head)
|
|
||||||
n = m_length - m_head + m_tail;
|
|
||||||
else
|
|
||||||
n = m_tail - m_head;
|
|
||||||
|
|
||||||
if (n > m_length)
|
|
||||||
n = 0U;
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t CSampleRB::getData() const
|
|
||||||
{
|
|
||||||
if (m_tail == m_head)
|
|
||||||
return m_full ? m_length : 0U;
|
|
||||||
else if (m_tail < m_head)
|
|
||||||
return m_head - m_tail;
|
|
||||||
else
|
|
||||||
return m_length - m_tail + m_head;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CSampleRB::put(uint16_t sample, uint8_t control)
|
|
||||||
{
|
|
||||||
if (m_full) {
|
|
||||||
m_overflow = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_samples[m_head] = sample;
|
|
||||||
m_control[m_head] = control;
|
|
||||||
|
|
||||||
m_head++;
|
|
||||||
if (m_head >= m_length)
|
|
||||||
m_head = 0U;
|
|
||||||
|
|
||||||
if (m_head == m_tail)
|
|
||||||
m_full = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CSampleRB::get(uint16_t& sample, uint8_t& control)
|
|
||||||
{
|
|
||||||
if (m_head == m_tail && !m_full)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
sample = m_samples[m_tail];
|
|
||||||
control = m_control[m_tail];
|
|
||||||
|
|
||||||
m_full = false;
|
|
||||||
|
|
||||||
m_tail++;
|
|
||||||
if (m_tail >= m_length)
|
|
||||||
m_tail = 0U;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CSampleRB::hasOverflowed()
|
|
||||||
{
|
|
||||||
bool overflow = m_overflow;
|
|
||||||
|
|
||||||
m_overflow = false;
|
|
||||||
|
|
||||||
return overflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
-56
@@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
Serial fifo control - Copyright (C) KI6ZUM 2015
|
|
||||||
Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the
|
|
||||||
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(SAMPLERB_H)
|
|
||||||
#define SAMPLERB_H
|
|
||||||
|
|
||||||
#if defined(STM32F4XX) || defined(STM32F4)
|
|
||||||
#include "stm32f4xx.h"
|
|
||||||
#include <cstddef>
|
|
||||||
#else
|
|
||||||
#include <Arduino.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class CSampleRB {
|
|
||||||
public:
|
|
||||||
CSampleRB(uint16_t length);
|
|
||||||
|
|
||||||
uint16_t getSpace() const;
|
|
||||||
|
|
||||||
uint16_t getData() const;
|
|
||||||
|
|
||||||
bool put(uint16_t sample, uint8_t control);
|
|
||||||
|
|
||||||
bool get(uint16_t& sample, uint8_t& control);
|
|
||||||
|
|
||||||
bool hasOverflowed();
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint16_t m_length;
|
|
||||||
volatile uint16_t* m_samples;
|
|
||||||
volatile uint8_t* m_control;
|
|
||||||
volatile uint16_t m_head;
|
|
||||||
volatile uint16_t m_tail;
|
|
||||||
volatile bool m_full;
|
|
||||||
bool m_overflow;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
|
|
||||||
#include "SerialPort.h"
|
|
||||||
|
|
||||||
#if defined(__SAM3X8E__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
|
|
||||||
|
|
||||||
void CSerialPort::beginInt(uint8_t n, int speed)
|
|
||||||
{
|
|
||||||
switch (n) {
|
|
||||||
case 1U:
|
|
||||||
Serial.begin(speed);
|
|
||||||
break;
|
|
||||||
case 2U:
|
|
||||||
Serial2.begin(speed);
|
|
||||||
break;
|
|
||||||
case 3U:
|
|
||||||
Serial3.begin(speed);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int CSerialPort::availableInt(uint8_t n)
|
|
||||||
{
|
|
||||||
switch (n) {
|
|
||||||
case 1U:
|
|
||||||
return Serial.available();
|
|
||||||
case 2U:
|
|
||||||
return Serial2.available();
|
|
||||||
case 3U:
|
|
||||||
return Serial3.available();
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t CSerialPort::readInt(uint8_t n)
|
|
||||||
{
|
|
||||||
switch (n) {
|
|
||||||
case 1U:
|
|
||||||
return Serial.read();
|
|
||||||
case 2U:
|
|
||||||
return Serial2.read();
|
|
||||||
case 3U:
|
|
||||||
return Serial3.read();
|
|
||||||
default:
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSerialPort::writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush)
|
|
||||||
{
|
|
||||||
switch (n) {
|
|
||||||
case 1U:
|
|
||||||
Serial.write(data, length);
|
|
||||||
if (flush)
|
|
||||||
Serial.flush();
|
|
||||||
break;
|
|
||||||
case 2U:
|
|
||||||
Serial2.write(data, length);
|
|
||||||
if (flush)
|
|
||||||
Serial2.flush();
|
|
||||||
break;
|
|
||||||
case 3U:
|
|
||||||
Serial3.write(data, length);
|
|
||||||
if (flush)
|
|
||||||
Serial3.flush();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
-1037
File diff suppressed because it is too large
Load Diff
@@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015,2016 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(SERIALPORT_H)
|
|
||||||
#define SERIALPORT_H
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
|
|
||||||
|
|
||||||
class CSerialPort {
|
|
||||||
public:
|
|
||||||
CSerialPort();
|
|
||||||
|
|
||||||
void start();
|
|
||||||
|
|
||||||
void process();
|
|
||||||
|
|
||||||
void writeDStarHeader(const uint8_t* header, uint8_t length);
|
|
||||||
void writeDStarData(const uint8_t* data, uint8_t length);
|
|
||||||
void writeDStarLost();
|
|
||||||
void writeDStarEOT();
|
|
||||||
|
|
||||||
void writeDMRData(bool slot, const uint8_t* data, uint8_t length);
|
|
||||||
void writeDMRLost(bool slot);
|
|
||||||
|
|
||||||
void writeYSFData(const uint8_t* data, uint8_t length);
|
|
||||||
void writeYSFLost();
|
|
||||||
|
|
||||||
void writeP25Hdr(const uint8_t* data, uint8_t length);
|
|
||||||
void writeP25Ldu(const uint8_t* data, uint8_t length);
|
|
||||||
void writeP25Lost();
|
|
||||||
|
|
||||||
void writeCalData(const uint8_t* data, uint8_t length);
|
|
||||||
|
|
||||||
void writeDebug(const char* text);
|
|
||||||
void writeDebug(const char* text, int16_t n1);
|
|
||||||
void writeDebug(const char* text, int16_t n1, int16_t n2);
|
|
||||||
void writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3);
|
|
||||||
void writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3, int16_t n4);
|
|
||||||
|
|
||||||
void writeAssert(bool cond, const char* text, const char* file, long line);
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint8_t m_buffer[256U];
|
|
||||||
uint8_t m_ptr;
|
|
||||||
uint8_t m_len;
|
|
||||||
|
|
||||||
void sendACK();
|
|
||||||
void sendNAK(uint8_t err);
|
|
||||||
void getStatus();
|
|
||||||
void getVersion();
|
|
||||||
uint8_t setConfig(const uint8_t* data, uint8_t length);
|
|
||||||
uint8_t setMode(const uint8_t* data, uint8_t length);
|
|
||||||
void setMode(MMDVM_STATE modemState);
|
|
||||||
|
|
||||||
// Hardware versions
|
|
||||||
void beginInt(uint8_t n, int speed);
|
|
||||||
int availableInt(uint8_t n);
|
|
||||||
uint8_t readInt(uint8_t n);
|
|
||||||
void writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush = false);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
-101
@@ -1,101 +0,0 @@
|
|||||||
/*
|
|
||||||
Serial RB control - Copyright (C) KI6ZUM 2015
|
|
||||||
Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the
|
|
||||||
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "SerialRB.h"
|
|
||||||
|
|
||||||
CSerialRB::CSerialRB(uint16_t length) :
|
|
||||||
m_length(length),
|
|
||||||
m_buffer(NULL),
|
|
||||||
m_head(0U),
|
|
||||||
m_tail(0U),
|
|
||||||
m_full(false)
|
|
||||||
{
|
|
||||||
m_buffer = new uint8_t[length];
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSerialRB::reset()
|
|
||||||
{
|
|
||||||
m_head = 0U;
|
|
||||||
m_tail = 0U;
|
|
||||||
m_full = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t CSerialRB::getSpace() const
|
|
||||||
{
|
|
||||||
uint16_t n = 0U;
|
|
||||||
|
|
||||||
if (m_tail == m_head)
|
|
||||||
n = m_full ? 0U : m_length;
|
|
||||||
else if (m_tail < m_head)
|
|
||||||
n = m_length - m_head + m_tail;
|
|
||||||
else
|
|
||||||
n = m_tail - m_head;
|
|
||||||
|
|
||||||
if (n > m_length)
|
|
||||||
n = 0U;
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t CSerialRB::getData() const
|
|
||||||
{
|
|
||||||
if (m_tail == m_head)
|
|
||||||
return m_full ? m_length : 0U;
|
|
||||||
else if (m_tail < m_head)
|
|
||||||
return m_head - m_tail;
|
|
||||||
else
|
|
||||||
return m_length - m_tail + m_head;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CSerialRB::put(uint8_t c)
|
|
||||||
{
|
|
||||||
if (m_full)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
m_buffer[m_head] = c;
|
|
||||||
|
|
||||||
m_head++;
|
|
||||||
if (m_head >= m_length)
|
|
||||||
m_head = 0U;
|
|
||||||
|
|
||||||
if (m_head == m_tail)
|
|
||||||
m_full = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t CSerialRB::peek() const
|
|
||||||
{
|
|
||||||
return m_buffer[m_tail];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t CSerialRB::get()
|
|
||||||
{
|
|
||||||
uint8_t value = m_buffer[m_tail];
|
|
||||||
|
|
||||||
m_full = false;
|
|
||||||
|
|
||||||
m_tail++;
|
|
||||||
if (m_tail >= m_length)
|
|
||||||
m_tail = 0U;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
-58
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
Serial fifo control - Copyright (C) KI6ZUM 2015
|
|
||||||
Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the
|
|
||||||
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(SERIALRB_H)
|
|
||||||
#define SERIALRB_H
|
|
||||||
|
|
||||||
#if defined(STM32F4XX) || defined(STM32F4)
|
|
||||||
#include "stm32f4xx.h"
|
|
||||||
#include <cstddef>
|
|
||||||
#else
|
|
||||||
#include <Arduino.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const uint16_t SERIAL_RINGBUFFER_SIZE = 370U;
|
|
||||||
|
|
||||||
class CSerialRB {
|
|
||||||
public:
|
|
||||||
CSerialRB(uint16_t length = SERIAL_RINGBUFFER_SIZE);
|
|
||||||
|
|
||||||
uint16_t getSpace() const;
|
|
||||||
|
|
||||||
uint16_t getData() const;
|
|
||||||
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
bool put(uint8_t c);
|
|
||||||
|
|
||||||
uint8_t peek() const;
|
|
||||||
|
|
||||||
uint8_t get();
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint16_t m_length;
|
|
||||||
volatile uint8_t* m_buffer;
|
|
||||||
volatile uint16_t m_head;
|
|
||||||
volatile uint16_t m_tail;
|
|
||||||
volatile bool m_full;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
-890
@@ -1,890 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
|
|
||||||
* Copyright (C) 2016 by Andy Uribe CA6JAU
|
|
||||||
*
|
|
||||||
* 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
|
|
||||||
#include "SerialPort.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
Pin definitions:
|
|
||||||
|
|
||||||
- Host communication:
|
|
||||||
USART1 - TXD PA9 - RXD PA10 (Pi board)
|
|
||||||
USART2 - TXD PA2 - RXD PA3 (Nucleo board)
|
|
||||||
USART3 - TXD PC10 - RXD PC11 (Discovery board)
|
|
||||||
|
|
||||||
- Serial repeater:
|
|
||||||
UART5 - TXD PC12 - RXD PD2
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if defined(STM32F4XX) || defined(STM32F4)
|
|
||||||
|
|
||||||
#define TX_SERIAL_FIFO_SIZE 256U
|
|
||||||
#define RX_SERIAL_FIFO_SIZE 256U
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
void USART1_IRQHandler();
|
|
||||||
void USART2_IRQHandler();
|
|
||||||
void USART3_IRQHandler();
|
|
||||||
void UART5_IRQHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************* USART1 ***************** */
|
|
||||||
#if defined(STM32F4_PI)
|
|
||||||
|
|
||||||
volatile uint32_t intcount1;
|
|
||||||
volatile uint8_t TXSerialfifo1[TX_SERIAL_FIFO_SIZE];
|
|
||||||
volatile uint8_t RXSerialfifo1[RX_SERIAL_FIFO_SIZE];
|
|
||||||
volatile uint16_t TXSerialfifohead1, TXSerialfifotail1;
|
|
||||||
volatile uint16_t RXSerialfifohead1, RXSerialfifotail1;
|
|
||||||
|
|
||||||
// Init queues
|
|
||||||
void TXSerialfifoinit1()
|
|
||||||
{
|
|
||||||
TXSerialfifohead1 = 0U;
|
|
||||||
TXSerialfifotail1 = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RXSerialfifoinit1()
|
|
||||||
{
|
|
||||||
RXSerialfifohead1 = 0U;
|
|
||||||
RXSerialfifotail1 = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
// How full is queue
|
|
||||||
// TODO decide if how full or how empty is preferred info to return
|
|
||||||
uint16_t TXSerialfifolevel1()
|
|
||||||
{
|
|
||||||
uint32_t tail = TXSerialfifotail1;
|
|
||||||
uint32_t head = TXSerialfifohead1;
|
|
||||||
|
|
||||||
if (tail > head)
|
|
||||||
return TX_SERIAL_FIFO_SIZE + head - tail;
|
|
||||||
else
|
|
||||||
return head - tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t RXSerialfifolevel1()
|
|
||||||
{
|
|
||||||
uint32_t tail = RXSerialfifotail1;
|
|
||||||
uint32_t head = RXSerialfifohead1;
|
|
||||||
|
|
||||||
if (tail > head)
|
|
||||||
return RX_SERIAL_FIFO_SIZE + head - tail;
|
|
||||||
else
|
|
||||||
return head - tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flushes the transmit shift register
|
|
||||||
// warning: this call is blocking
|
|
||||||
void TXSerialFlush1()
|
|
||||||
{
|
|
||||||
// wait until the TXE shows the shift register is empty
|
|
||||||
while (USART_GetITStatus(USART1, USART_FLAG_TXE))
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TXSerialfifoput1(uint8_t next)
|
|
||||||
{
|
|
||||||
if (TXSerialfifolevel1() < TX_SERIAL_FIFO_SIZE) {
|
|
||||||
TXSerialfifo1[TXSerialfifohead1] = next;
|
|
||||||
|
|
||||||
TXSerialfifohead1++;
|
|
||||||
if (TXSerialfifohead1 >= TX_SERIAL_FIFO_SIZE)
|
|
||||||
TXSerialfifohead1 = 0U;
|
|
||||||
|
|
||||||
// make sure transmit interrupts are enabled as long as there is data to send
|
|
||||||
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
|
|
||||||
return 1U;
|
|
||||||
} else {
|
|
||||||
return 0U; // signal an overflow occurred by returning a zero count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void USART1_IRQHandler()
|
|
||||||
{
|
|
||||||
uint8_t c;
|
|
||||||
|
|
||||||
if (USART_GetITStatus(USART1, USART_IT_RXNE)) {
|
|
||||||
c = (uint8_t) USART_ReceiveData(USART1);
|
|
||||||
|
|
||||||
if (RXSerialfifolevel1() < RX_SERIAL_FIFO_SIZE) {
|
|
||||||
RXSerialfifo1[RXSerialfifohead1] = c;
|
|
||||||
|
|
||||||
RXSerialfifohead1++;
|
|
||||||
if (RXSerialfifohead1 >= RX_SERIAL_FIFO_SIZE)
|
|
||||||
RXSerialfifohead1 = 0U;
|
|
||||||
} else {
|
|
||||||
// TODO - do something if rx fifo is full?
|
|
||||||
}
|
|
||||||
|
|
||||||
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
|
|
||||||
intcount1++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (USART_GetITStatus(USART1, USART_IT_TXE)) {
|
|
||||||
c = 0U;
|
|
||||||
|
|
||||||
if (TXSerialfifohead1 != TXSerialfifotail1) { // if the fifo is not empty
|
|
||||||
c = TXSerialfifo1[TXSerialfifotail1];
|
|
||||||
|
|
||||||
TXSerialfifotail1++;
|
|
||||||
if (TXSerialfifotail1 >= TX_SERIAL_FIFO_SIZE)
|
|
||||||
TXSerialfifotail1 = 0U;
|
|
||||||
|
|
||||||
USART_SendData(USART1, c);
|
|
||||||
} else { // if there's no more data to transmit then turn off TX interrupts
|
|
||||||
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
USART_ClearITPendingBit(USART1, USART_IT_TXE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InitUSART1(int speed)
|
|
||||||
{
|
|
||||||
// USART1 - TXD PA9 - RXD PA10 - pins on mmdvm pi board
|
|
||||||
GPIO_InitTypeDef GPIO_InitStructure;
|
|
||||||
USART_InitTypeDef USART_InitStructure;
|
|
||||||
NVIC_InitTypeDef NVIC_InitStructure;
|
|
||||||
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
|
|
||||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
|
|
||||||
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
|
|
||||||
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
|
|
||||||
|
|
||||||
// USART IRQ init
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
|
|
||||||
NVIC_Init(&NVIC_InitStructure);
|
|
||||||
|
|
||||||
// Configure USART as alternate function
|
|
||||||
GPIO_StructInit(&GPIO_InitStructure);
|
|
||||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
|
||||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
|
||||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
|
||||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // Tx | Rx
|
|
||||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
|
||||||
GPIO_Init(GPIOA, &GPIO_InitStructure);
|
|
||||||
|
|
||||||
// Configure USART baud rate
|
|
||||||
USART_StructInit(&USART_InitStructure);
|
|
||||||
USART_InitStructure.USART_BaudRate = speed;
|
|
||||||
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
|
||||||
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
|
||||||
USART_InitStructure.USART_Parity = USART_Parity_No;
|
|
||||||
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
|
||||||
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
|
|
||||||
USART_Init(USART1, &USART_InitStructure);
|
|
||||||
|
|
||||||
USART_Cmd(USART1, ENABLE);
|
|
||||||
|
|
||||||
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
|
|
||||||
|
|
||||||
// initialize the fifos
|
|
||||||
TXSerialfifoinit1();
|
|
||||||
RXSerialfifoinit1();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t AvailUSART1(void)
|
|
||||||
{
|
|
||||||
if (RXSerialfifolevel1() > 0U)
|
|
||||||
return 1U;
|
|
||||||
else
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t ReadUSART1(void)
|
|
||||||
{
|
|
||||||
uint8_t data_c = RXSerialfifo1[RXSerialfifotail1];
|
|
||||||
|
|
||||||
RXSerialfifotail1++;
|
|
||||||
if (RXSerialfifotail1 >= RX_SERIAL_FIFO_SIZE)
|
|
||||||
RXSerialfifotail1 = 0U;
|
|
||||||
|
|
||||||
return data_c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteUSART1(const uint8_t* data, uint16_t length)
|
|
||||||
{
|
|
||||||
for (uint16_t i = 0U; i < length; i++)
|
|
||||||
TXSerialfifoput1(data[i]);
|
|
||||||
|
|
||||||
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* ************* USART2 ***************** */
|
|
||||||
#if defined(STM32F4_NUCLEO)
|
|
||||||
|
|
||||||
volatile uint32_t intcount2;
|
|
||||||
volatile uint8_t TXSerialfifo2[TX_SERIAL_FIFO_SIZE];
|
|
||||||
volatile uint8_t RXSerialfifo2[RX_SERIAL_FIFO_SIZE];
|
|
||||||
volatile uint16_t TXSerialfifohead2, TXSerialfifotail2;
|
|
||||||
volatile uint16_t RXSerialfifohead2, RXSerialfifotail2;
|
|
||||||
|
|
||||||
// Init queues
|
|
||||||
void TXSerialfifoinit2()
|
|
||||||
{
|
|
||||||
TXSerialfifohead2 = 0U;
|
|
||||||
TXSerialfifotail2 = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RXSerialfifoinit2()
|
|
||||||
{
|
|
||||||
RXSerialfifohead2 = 0U;
|
|
||||||
RXSerialfifotail2 = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
// How full is queue
|
|
||||||
// TODO decide if how full or how empty is preferred info to return
|
|
||||||
uint16_t TXSerialfifolevel2()
|
|
||||||
{
|
|
||||||
uint32_t tail = TXSerialfifotail2;
|
|
||||||
uint32_t head = TXSerialfifohead2;
|
|
||||||
|
|
||||||
if (tail > head)
|
|
||||||
return TX_SERIAL_FIFO_SIZE + head - tail;
|
|
||||||
else
|
|
||||||
return head - tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t RXSerialfifolevel2()
|
|
||||||
{
|
|
||||||
uint32_t tail = RXSerialfifotail2;
|
|
||||||
uint32_t head = RXSerialfifohead2;
|
|
||||||
|
|
||||||
if (tail > head)
|
|
||||||
return RX_SERIAL_FIFO_SIZE + head - tail;
|
|
||||||
else
|
|
||||||
return head - tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flushes the transmit shift register
|
|
||||||
// warning: this call is blocking
|
|
||||||
void TXSerialFlush2()
|
|
||||||
{
|
|
||||||
// wait until the TXE shows the shift register is empty
|
|
||||||
while (USART_GetITStatus(USART2, USART_FLAG_TXE))
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TXSerialfifoput2(uint8_t next)
|
|
||||||
{
|
|
||||||
if (TXSerialfifolevel2() < TX_SERIAL_FIFO_SIZE) {
|
|
||||||
TXSerialfifo2[TXSerialfifohead2] = next;
|
|
||||||
|
|
||||||
TXSerialfifohead2++;
|
|
||||||
if (TXSerialfifohead2 >= TX_SERIAL_FIFO_SIZE)
|
|
||||||
TXSerialfifohead2 = 0U;
|
|
||||||
|
|
||||||
// make sure transmit interrupts are enabled as long as there is data to send
|
|
||||||
USART_ITConfig(USART2, USART_IT_TXE, ENABLE);
|
|
||||||
return 1U;
|
|
||||||
} else {
|
|
||||||
return 0U; // signal an overflow occurred by returning a zero count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void USART2_IRQHandler()
|
|
||||||
{
|
|
||||||
uint8_t c;
|
|
||||||
|
|
||||||
if (USART_GetITStatus(USART2, USART_IT_RXNE)) {
|
|
||||||
c = (uint8_t) USART_ReceiveData(USART2);
|
|
||||||
|
|
||||||
if (RXSerialfifolevel2() < RX_SERIAL_FIFO_SIZE) {
|
|
||||||
RXSerialfifo2[RXSerialfifohead2] = c;
|
|
||||||
|
|
||||||
RXSerialfifohead2++;
|
|
||||||
if (RXSerialfifohead2 >= RX_SERIAL_FIFO_SIZE)
|
|
||||||
RXSerialfifohead2 = 0U;
|
|
||||||
} else {
|
|
||||||
// TODO - do something if rx fifo is full?
|
|
||||||
}
|
|
||||||
|
|
||||||
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
|
|
||||||
intcount2++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (USART_GetITStatus(USART2, USART_IT_TXE)) {
|
|
||||||
c = 0U;
|
|
||||||
|
|
||||||
if (TXSerialfifohead2 != TXSerialfifotail2) { // if the fifo is not empty
|
|
||||||
c = TXSerialfifo2[TXSerialfifotail2];
|
|
||||||
|
|
||||||
TXSerialfifotail2++;
|
|
||||||
if (TXSerialfifotail2 >= TX_SERIAL_FIFO_SIZE)
|
|
||||||
TXSerialfifotail2 = 0U;
|
|
||||||
|
|
||||||
USART_SendData(USART2, c);
|
|
||||||
} else { // if there's no more data to transmit then turn off TX interrupts
|
|
||||||
USART_ITConfig(USART2, USART_IT_TXE, DISABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
USART_ClearITPendingBit(USART2, USART_IT_TXE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InitUSART2(int speed)
|
|
||||||
{
|
|
||||||
// USART2 - TXD PA2 - RXD PA3
|
|
||||||
GPIO_InitTypeDef GPIO_InitStructure;
|
|
||||||
USART_InitTypeDef USART_InitStructure;
|
|
||||||
NVIC_InitTypeDef NVIC_InitStructure;
|
|
||||||
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
|
|
||||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
|
|
||||||
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
|
|
||||||
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);
|
|
||||||
|
|
||||||
// USART IRQ init
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
|
|
||||||
NVIC_Init(&NVIC_InitStructure);
|
|
||||||
|
|
||||||
// Configure USART as alternate function
|
|
||||||
GPIO_StructInit(&GPIO_InitStructure);
|
|
||||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
|
||||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
|
||||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
|
||||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; // Tx | Rx
|
|
||||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
|
||||||
GPIO_Init(GPIOA, &GPIO_InitStructure);
|
|
||||||
|
|
||||||
// Configure USART baud rate
|
|
||||||
USART_StructInit(&USART_InitStructure);
|
|
||||||
USART_InitStructure.USART_BaudRate = speed;
|
|
||||||
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
|
||||||
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
|
||||||
USART_InitStructure.USART_Parity = USART_Parity_No;
|
|
||||||
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
|
||||||
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
|
|
||||||
USART_Init(USART2, &USART_InitStructure);
|
|
||||||
|
|
||||||
USART_Cmd(USART2, ENABLE);
|
|
||||||
|
|
||||||
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
|
|
||||||
|
|
||||||
// initialize the fifos
|
|
||||||
TXSerialfifoinit2();
|
|
||||||
RXSerialfifoinit2();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t AvailUSART2(void)
|
|
||||||
{
|
|
||||||
if (RXSerialfifolevel2() > 0U)
|
|
||||||
return 1U;
|
|
||||||
else
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t ReadUSART2(void)
|
|
||||||
{
|
|
||||||
uint8_t data_c = RXSerialfifo2[RXSerialfifotail2];
|
|
||||||
|
|
||||||
RXSerialfifotail2++;
|
|
||||||
if (RXSerialfifotail2 >= RX_SERIAL_FIFO_SIZE)
|
|
||||||
RXSerialfifotail2 = 0U;
|
|
||||||
|
|
||||||
return data_c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteUSART2(const uint8_t* data, uint16_t length)
|
|
||||||
{
|
|
||||||
for (uint16_t i = 0U; i < length; i++)
|
|
||||||
TXSerialfifoput2(data[i]);
|
|
||||||
|
|
||||||
USART_ITConfig(USART2, USART_IT_TXE, ENABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* ************* USART3 ***************** */
|
|
||||||
#if defined(STM32F4_DISCOVERY) || defined(STM32F4_PI)
|
|
||||||
|
|
||||||
volatile uint32_t intcount3;
|
|
||||||
volatile uint8_t TXSerialfifo3[TX_SERIAL_FIFO_SIZE];
|
|
||||||
volatile uint8_t RXSerialfifo3[RX_SERIAL_FIFO_SIZE];
|
|
||||||
volatile uint16_t TXSerialfifohead3, TXSerialfifotail3;
|
|
||||||
volatile uint16_t RXSerialfifohead3, RXSerialfifotail3;
|
|
||||||
|
|
||||||
// Init queues
|
|
||||||
void TXSerialfifoinit3()
|
|
||||||
{
|
|
||||||
TXSerialfifohead3 = 0U;
|
|
||||||
TXSerialfifotail3 = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RXSerialfifoinit3()
|
|
||||||
{
|
|
||||||
RXSerialfifohead3 = 0U;
|
|
||||||
RXSerialfifotail3 = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
// How full is queue
|
|
||||||
// TODO decide if how full or how empty is preferred info to return
|
|
||||||
uint16_t TXSerialfifolevel3()
|
|
||||||
{
|
|
||||||
uint32_t tail = TXSerialfifotail3;
|
|
||||||
uint32_t head = TXSerialfifohead3;
|
|
||||||
|
|
||||||
if (tail > head)
|
|
||||||
return TX_SERIAL_FIFO_SIZE + head - tail;
|
|
||||||
else
|
|
||||||
return head - tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t RXSerialfifolevel3()
|
|
||||||
{
|
|
||||||
uint32_t tail = RXSerialfifotail3;
|
|
||||||
uint32_t head = RXSerialfifohead3;
|
|
||||||
|
|
||||||
if (tail > head)
|
|
||||||
return RX_SERIAL_FIFO_SIZE + head - tail;
|
|
||||||
else
|
|
||||||
return head - tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flushes the transmit shift register
|
|
||||||
// warning: this call is blocking
|
|
||||||
void TXSerialFlush3()
|
|
||||||
{
|
|
||||||
// wait until the TXE shows the shift register is empty
|
|
||||||
while (USART_GetITStatus(USART3, USART_FLAG_TXE))
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TXSerialfifoput3(uint8_t next)
|
|
||||||
{
|
|
||||||
if (TXSerialfifolevel3() < TX_SERIAL_FIFO_SIZE) {
|
|
||||||
TXSerialfifo3[TXSerialfifohead3] = next;
|
|
||||||
|
|
||||||
TXSerialfifohead3++;
|
|
||||||
if (TXSerialfifohead3 >= TX_SERIAL_FIFO_SIZE)
|
|
||||||
TXSerialfifohead3 = 0U;
|
|
||||||
|
|
||||||
// make sure transmit interrupts are enabled as long as there is data to send
|
|
||||||
USART_ITConfig(USART3, USART_IT_TXE, ENABLE);
|
|
||||||
return 1U;
|
|
||||||
} else {
|
|
||||||
return 0U; // signal an overflow occurred by returning a zero count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void USART3_IRQHandler()
|
|
||||||
{
|
|
||||||
uint8_t c;
|
|
||||||
|
|
||||||
if (USART_GetITStatus(USART3, USART_IT_RXNE)) {
|
|
||||||
c = (uint8_t) USART_ReceiveData(USART3);
|
|
||||||
|
|
||||||
if (RXSerialfifolevel3() < RX_SERIAL_FIFO_SIZE) {
|
|
||||||
RXSerialfifo3[RXSerialfifohead3] = c;
|
|
||||||
|
|
||||||
RXSerialfifohead3++;
|
|
||||||
if (RXSerialfifohead3 >= RX_SERIAL_FIFO_SIZE)
|
|
||||||
RXSerialfifohead3 = 0U;
|
|
||||||
} else {
|
|
||||||
// TODO - do something if rx fifo is full?
|
|
||||||
}
|
|
||||||
|
|
||||||
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
|
|
||||||
intcount3++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (USART_GetITStatus(USART3, USART_IT_TXE)) {
|
|
||||||
c = 0U;
|
|
||||||
|
|
||||||
if (TXSerialfifohead3 != TXSerialfifotail3) { // if the fifo is not empty
|
|
||||||
c = TXSerialfifo3[TXSerialfifotail3];
|
|
||||||
|
|
||||||
TXSerialfifotail3++;
|
|
||||||
if (TXSerialfifotail3 >= TX_SERIAL_FIFO_SIZE)
|
|
||||||
TXSerialfifotail3 = 0U;
|
|
||||||
|
|
||||||
USART_SendData(USART3, c);
|
|
||||||
} else { // if there's no more data to transmit then turn off TX interrupts
|
|
||||||
USART_ITConfig(USART3, USART_IT_TXE, DISABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
USART_ClearITPendingBit(USART3, USART_IT_TXE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InitUSART3(int speed)
|
|
||||||
{
|
|
||||||
// USART3 - TXD PC10 - RXD PC11
|
|
||||||
GPIO_InitTypeDef GPIO_InitStructure;
|
|
||||||
USART_InitTypeDef USART_InitStructure;
|
|
||||||
NVIC_InitTypeDef NVIC_InitStructure;
|
|
||||||
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
|
|
||||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
|
|
||||||
GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3);
|
|
||||||
GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3);
|
|
||||||
|
|
||||||
// USART IRQ init
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
|
|
||||||
NVIC_Init(&NVIC_InitStructure);
|
|
||||||
|
|
||||||
// Configure USART as alternate function
|
|
||||||
GPIO_StructInit(&GPIO_InitStructure);
|
|
||||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
|
||||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
|
||||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
|
||||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; // Tx | Rx
|
|
||||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
|
||||||
GPIO_Init(GPIOC, &GPIO_InitStructure);
|
|
||||||
|
|
||||||
// Configure USART baud rate
|
|
||||||
USART_StructInit(&USART_InitStructure);
|
|
||||||
USART_InitStructure.USART_BaudRate = speed;
|
|
||||||
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
|
||||||
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
|
||||||
USART_InitStructure.USART_Parity = USART_Parity_No;
|
|
||||||
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
|
||||||
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
|
|
||||||
USART_Init(USART3, &USART_InitStructure);
|
|
||||||
|
|
||||||
USART_Cmd(USART3, ENABLE);
|
|
||||||
|
|
||||||
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
|
|
||||||
|
|
||||||
// initialize the fifos
|
|
||||||
TXSerialfifoinit3();
|
|
||||||
RXSerialfifoinit3();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t AvailUSART3(void)
|
|
||||||
{
|
|
||||||
if (RXSerialfifolevel3() > 0U)
|
|
||||||
return 1U;
|
|
||||||
else
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t ReadUSART3(void)
|
|
||||||
{
|
|
||||||
uint8_t data_c = RXSerialfifo3[RXSerialfifotail3];
|
|
||||||
|
|
||||||
RXSerialfifotail3++;
|
|
||||||
if (RXSerialfifotail3 >= RX_SERIAL_FIFO_SIZE)
|
|
||||||
RXSerialfifotail3 = 0U;
|
|
||||||
|
|
||||||
return data_c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteUSART3(const uint8_t* data, uint16_t length)
|
|
||||||
{
|
|
||||||
for (uint16_t i = 0U; i < length; i++)
|
|
||||||
TXSerialfifoput3(data[i]);
|
|
||||||
|
|
||||||
USART_ITConfig(USART3, USART_IT_TXE, ENABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* ************* UART5 ***************** */
|
|
||||||
|
|
||||||
volatile uint32_t intcount5;
|
|
||||||
volatile uint8_t TXSerialfifo5[TX_SERIAL_FIFO_SIZE];
|
|
||||||
volatile uint8_t RXSerialfifo5[RX_SERIAL_FIFO_SIZE];
|
|
||||||
volatile uint16_t TXSerialfifohead5, TXSerialfifotail5;
|
|
||||||
volatile uint16_t RXSerialfifohead5, RXSerialfifotail5;
|
|
||||||
|
|
||||||
// Init queues
|
|
||||||
void TXSerialfifoinit5()
|
|
||||||
{
|
|
||||||
TXSerialfifohead5 = 0U;
|
|
||||||
TXSerialfifotail5 = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RXSerialfifoinit5()
|
|
||||||
{
|
|
||||||
RXSerialfifohead5 = 0U;
|
|
||||||
RXSerialfifotail5 = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
// How full is queue
|
|
||||||
// TODO decide if how full or how empty is preferred info to return
|
|
||||||
uint16_t TXSerialfifolevel5()
|
|
||||||
{
|
|
||||||
uint32_t tail = TXSerialfifotail5;
|
|
||||||
uint32_t head = TXSerialfifohead5;
|
|
||||||
|
|
||||||
if (tail > head)
|
|
||||||
return TX_SERIAL_FIFO_SIZE + head - tail;
|
|
||||||
else
|
|
||||||
return head - tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t RXSerialfifolevel5()
|
|
||||||
{
|
|
||||||
uint32_t tail = RXSerialfifotail5;
|
|
||||||
uint32_t head = RXSerialfifohead5;
|
|
||||||
|
|
||||||
if (tail > head)
|
|
||||||
return RX_SERIAL_FIFO_SIZE + head - tail;
|
|
||||||
else
|
|
||||||
return head - tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flushes the transmit shift register
|
|
||||||
// warning: this call is blocking
|
|
||||||
void TXSerialFlush5()
|
|
||||||
{
|
|
||||||
// wait until the TXE shows the shift register is empty
|
|
||||||
while (USART_GetITStatus(UART5, USART_FLAG_TXE))
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TXSerialfifoput5(uint8_t next)
|
|
||||||
{
|
|
||||||
if (TXSerialfifolevel5() < TX_SERIAL_FIFO_SIZE) {
|
|
||||||
TXSerialfifo5[TXSerialfifohead5] = next;
|
|
||||||
|
|
||||||
TXSerialfifohead5++;
|
|
||||||
if (TXSerialfifohead5 >= TX_SERIAL_FIFO_SIZE)
|
|
||||||
TXSerialfifohead5 = 0U;
|
|
||||||
|
|
||||||
// make sure transmit interrupts are enabled as long as there is data to send
|
|
||||||
USART_ITConfig(UART5, USART_IT_TXE, ENABLE);
|
|
||||||
return 1U;
|
|
||||||
} else {
|
|
||||||
return 0U; // signal an overflow occurred by returning a zero count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UART5_IRQHandler()
|
|
||||||
{
|
|
||||||
uint8_t c;
|
|
||||||
|
|
||||||
if (USART_GetITStatus(UART5, USART_IT_RXNE)) {
|
|
||||||
c = (uint8_t) USART_ReceiveData(UART5);
|
|
||||||
|
|
||||||
if (RXSerialfifolevel5() < RX_SERIAL_FIFO_SIZE) {
|
|
||||||
RXSerialfifo5[RXSerialfifohead5] = c;
|
|
||||||
|
|
||||||
RXSerialfifohead5++;
|
|
||||||
if (RXSerialfifohead5 >= RX_SERIAL_FIFO_SIZE)
|
|
||||||
RXSerialfifohead5 = 0U;
|
|
||||||
} else {
|
|
||||||
// TODO - do something if rx fifo is full?
|
|
||||||
}
|
|
||||||
|
|
||||||
USART_ClearITPendingBit(UART5, USART_IT_RXNE);
|
|
||||||
intcount5++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (USART_GetITStatus(UART5, USART_IT_TXE)) {
|
|
||||||
c = 0U;
|
|
||||||
|
|
||||||
if (TXSerialfifohead5 != TXSerialfifotail5) { // if the fifo is not empty
|
|
||||||
c = TXSerialfifo5[TXSerialfifotail5];
|
|
||||||
|
|
||||||
TXSerialfifotail5++;
|
|
||||||
if (TXSerialfifotail5 >= TX_SERIAL_FIFO_SIZE)
|
|
||||||
TXSerialfifotail5 = 0U;
|
|
||||||
|
|
||||||
USART_SendData(UART5, c);
|
|
||||||
} else { // if there's no more data to transmit then turn off TX interrupts
|
|
||||||
USART_ITConfig(UART5, USART_IT_TXE, DISABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
USART_ClearITPendingBit(UART5, USART_IT_TXE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InitUART5(int speed)
|
|
||||||
{
|
|
||||||
// UART5 - TXD PC12 - RXD PD2
|
|
||||||
GPIO_InitTypeDef GPIO_InitStructure;
|
|
||||||
USART_InitTypeDef USART_InitStructure;
|
|
||||||
NVIC_InitTypeDef NVIC_InitStructure;
|
|
||||||
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
|
|
||||||
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
|
|
||||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);
|
|
||||||
GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5);
|
|
||||||
GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5);
|
|
||||||
|
|
||||||
// USART IRQ init
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn;
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
|
|
||||||
NVIC_Init(&NVIC_InitStructure);
|
|
||||||
|
|
||||||
// Configure USART as alternate function
|
|
||||||
GPIO_StructInit(&GPIO_InitStructure);
|
|
||||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
|
||||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
|
||||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
|
||||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; // Tx
|
|
||||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
|
||||||
GPIO_Init(GPIOC, &GPIO_InitStructure);
|
|
||||||
|
|
||||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; // Rx
|
|
||||||
GPIO_Init(GPIOD, &GPIO_InitStructure);
|
|
||||||
|
|
||||||
// Configure USART baud rate
|
|
||||||
USART_StructInit(&USART_InitStructure);
|
|
||||||
USART_InitStructure.USART_BaudRate = speed;
|
|
||||||
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
|
||||||
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
|
||||||
USART_InitStructure.USART_Parity = USART_Parity_No;
|
|
||||||
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
|
||||||
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
|
|
||||||
USART_Init(UART5, &USART_InitStructure);
|
|
||||||
|
|
||||||
USART_Cmd(UART5, ENABLE);
|
|
||||||
|
|
||||||
USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);
|
|
||||||
|
|
||||||
// initialize the fifos
|
|
||||||
TXSerialfifoinit5();
|
|
||||||
RXSerialfifoinit5();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t AvailUART5(void)
|
|
||||||
{
|
|
||||||
if (RXSerialfifolevel5() > 0U)
|
|
||||||
return 1U;
|
|
||||||
else
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t ReadUART5(void)
|
|
||||||
{
|
|
||||||
uint8_t data_c = RXSerialfifo5[RXSerialfifotail5];
|
|
||||||
|
|
||||||
RXSerialfifotail5++;
|
|
||||||
if (RXSerialfifotail5 >= RX_SERIAL_FIFO_SIZE)
|
|
||||||
RXSerialfifotail5 = 0U;
|
|
||||||
|
|
||||||
return data_c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteUART5(const uint8_t* data, uint16_t length)
|
|
||||||
{
|
|
||||||
for (uint16_t i = 0U; i < length; i++)
|
|
||||||
TXSerialfifoput5(data[i]);
|
|
||||||
|
|
||||||
USART_ITConfig(UART5, USART_IT_TXE, ENABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void CSerialPort::beginInt(uint8_t n, int speed)
|
|
||||||
{
|
|
||||||
switch (n) {
|
|
||||||
case 1U:
|
|
||||||
#if defined(STM32F4_DISCOVERY)
|
|
||||||
InitUSART3(speed);
|
|
||||||
#elif defined(STM32F4_PI)
|
|
||||||
InitUSART1(speed);
|
|
||||||
#elif defined(STM32F4_NUCLEO)
|
|
||||||
InitUSART2(speed);
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
case 3U:
|
|
||||||
InitUART5(speed);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int CSerialPort::availableInt(uint8_t n)
|
|
||||||
{
|
|
||||||
switch (n) {
|
|
||||||
case 1U:
|
|
||||||
#if defined(STM32F4_DISCOVERY)
|
|
||||||
return AvailUSART3();
|
|
||||||
#elif defined(STM32F4_PI)
|
|
||||||
return AvailUSART1();
|
|
||||||
#elif defined(STM32F4_NUCLEO)
|
|
||||||
return AvailUSART2();
|
|
||||||
#endif
|
|
||||||
case 3U:
|
|
||||||
return AvailUART5();
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t CSerialPort::readInt(uint8_t n)
|
|
||||||
{
|
|
||||||
switch (n) {
|
|
||||||
case 1U:
|
|
||||||
#if defined(STM32F4_DISCOVERY)
|
|
||||||
return ReadUSART3();
|
|
||||||
#elif defined(STM32F4_PI)
|
|
||||||
return ReadUSART1();
|
|
||||||
#elif defined(STM32F4_NUCLEO)
|
|
||||||
return ReadUSART2();
|
|
||||||
#endif
|
|
||||||
case 3U:
|
|
||||||
return ReadUART5();
|
|
||||||
default:
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSerialPort::writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush)
|
|
||||||
{
|
|
||||||
switch (n) {
|
|
||||||
case 1U:
|
|
||||||
#if defined(STM32F4_DISCOVERY)
|
|
||||||
WriteUSART3(data, length);
|
|
||||||
if (flush)
|
|
||||||
TXSerialFlush3();
|
|
||||||
#elif defined(STM32F4_PI)
|
|
||||||
WriteUSART1(data, length);
|
|
||||||
if (flush)
|
|
||||||
TXSerialFlush1();
|
|
||||||
#elif defined(STM32F4_NUCLEO)
|
|
||||||
WriteUSART2(data, length);
|
|
||||||
if (flush)
|
|
||||||
TXSerialFlush2();
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
case 3U:
|
|
||||||
WriteUART5(data, length);
|
|
||||||
if (flush)
|
|
||||||
TXSerialFlush5();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009-2016 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// #define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
#include "YSFRX.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
|
|
||||||
const unsigned int BUFFER_LENGTH = 200U;
|
|
||||||
|
|
||||||
const q15_t SCALING_FACTOR = 18750; // Q15(0.55)
|
|
||||||
|
|
||||||
const uint32_t PLLMAX = 0x10000U;
|
|
||||||
const uint32_t PLLINC = PLLMAX / YSF_RADIO_SYMBOL_LENGTH;
|
|
||||||
const uint32_t INC = PLLINC / 32U;
|
|
||||||
|
|
||||||
const uint8_t SYNC_SYMBOL_ERRS = 0U;
|
|
||||||
|
|
||||||
const uint8_t SYNC_BIT_START_ERRS = 2U;
|
|
||||||
const uint8_t SYNC_BIT_RUN_ERRS = 4U;
|
|
||||||
|
|
||||||
const unsigned int MAX_SYNC_FRAMES = 4U + 1U;
|
|
||||||
|
|
||||||
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
|
||||||
|
|
||||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
|
||||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
|
||||||
|
|
||||||
CYSFRX::CYSFRX() :
|
|
||||||
m_pll(0U),
|
|
||||||
m_prev(false),
|
|
||||||
m_state(YSFRXS_NONE),
|
|
||||||
m_bitBuffer(0x00U),
|
|
||||||
m_symbols(),
|
|
||||||
m_outBuffer(),
|
|
||||||
m_buffer(NULL),
|
|
||||||
m_bufferPtr(0U),
|
|
||||||
m_symbolPtr(0U),
|
|
||||||
m_lostCount(0U),
|
|
||||||
m_centre(0),
|
|
||||||
m_threshold(0)
|
|
||||||
{
|
|
||||||
m_buffer = m_outBuffer + 1U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CYSFRX::reset()
|
|
||||||
{
|
|
||||||
m_pll = 0U;
|
|
||||||
m_prev = false;
|
|
||||||
m_state = YSFRXS_NONE;
|
|
||||||
m_bitBuffer = 0x00U;
|
|
||||||
m_bufferPtr = 0U;
|
|
||||||
m_symbolPtr = 0U;
|
|
||||||
m_lostCount = 0U;
|
|
||||||
m_centre = 0;
|
|
||||||
m_threshold = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CYSFRX::samples(const q15_t* samples, uint8_t length)
|
|
||||||
{
|
|
||||||
for (uint16_t i = 0U; i < length; i++) {
|
|
||||||
bool bit = samples[i] < 0;
|
|
||||||
|
|
||||||
if (bit != m_prev) {
|
|
||||||
if (m_pll < (PLLMAX / 2U))
|
|
||||||
m_pll += INC;
|
|
||||||
else
|
|
||||||
m_pll -= INC;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_prev = bit;
|
|
||||||
|
|
||||||
m_pll += PLLINC;
|
|
||||||
|
|
||||||
if (m_pll >= PLLMAX) {
|
|
||||||
m_pll -= PLLMAX;
|
|
||||||
|
|
||||||
if (m_state == YSFRXS_NONE)
|
|
||||||
processNone(samples[i]);
|
|
||||||
else
|
|
||||||
processData(samples[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CYSFRX::processNone(q15_t sample)
|
|
||||||
{
|
|
||||||
m_symbolBuffer <<= 1;
|
|
||||||
if (sample < 0)
|
|
||||||
m_symbolBuffer |= 0x01U;
|
|
||||||
|
|
||||||
m_symbols[m_symbolPtr] = sample;
|
|
||||||
|
|
||||||
// Fuzzy matching of the data sync bit sequence
|
|
||||||
if (countBits32((m_symbolBuffer & YSF_SYNC_SYMBOLS_MASK) ^ YSF_SYNC_SYMBOLS) <= SYNC_SYMBOL_ERRS) {
|
|
||||||
q15_t max = -16000;
|
|
||||||
q15_t min = 16000;
|
|
||||||
|
|
||||||
for (uint8_t i = 0U; i < YSF_SYNC_LENGTH_SYMBOLS; i++) {
|
|
||||||
q15_t val = m_symbols[i];
|
|
||||||
if (val > max)
|
|
||||||
max = val;
|
|
||||||
if (val < min)
|
|
||||||
min = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
q15_t centre = (max + min) >> 1;
|
|
||||||
|
|
||||||
q31_t v1 = (max - centre) * SCALING_FACTOR;
|
|
||||||
q15_t threshold = q15_t(v1 >> 15);
|
|
||||||
|
|
||||||
uint16_t ptr = m_symbolPtr + 1U;
|
|
||||||
if (ptr >= YSF_SYNC_LENGTH_SYMBOLS)
|
|
||||||
ptr = 0U;
|
|
||||||
|
|
||||||
for (uint8_t i = 0U; i < YSF_SYNC_LENGTH_SYMBOLS; i++) {
|
|
||||||
q15_t sample = m_symbols[ptr] - centre;
|
|
||||||
|
|
||||||
if (sample < -threshold) {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x01U;
|
|
||||||
} else if (sample < 0) {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x00U;
|
|
||||||
} else if (sample < threshold) {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x02U;
|
|
||||||
} else {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x03U;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr++;
|
|
||||||
if (ptr >= YSF_SYNC_LENGTH_SYMBOLS)
|
|
||||||
ptr = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fuzzy matching of the data sync bit sequence
|
|
||||||
if (countBits64((m_bitBuffer & YSF_SYNC_BITS_MASK) ^ YSF_SYNC_BITS) <= SYNC_BIT_START_ERRS) {
|
|
||||||
DEBUG5("YSFRX: sync found in None min/max/centre/threshold", min, max, centre, threshold);
|
|
||||||
for (uint8_t i = 0U; i < YSF_SYNC_LENGTH_BYTES; i++)
|
|
||||||
m_buffer[i] = YSF_SYNC_BYTES[i];
|
|
||||||
m_centre = centre;
|
|
||||||
m_threshold = threshold;
|
|
||||||
m_lostCount = MAX_SYNC_FRAMES;
|
|
||||||
m_bufferPtr = YSF_SYNC_LENGTH_BITS;
|
|
||||||
m_state = YSFRXS_DATA;
|
|
||||||
|
|
||||||
io.setDecode(true);
|
|
||||||
io.setADCDetection(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_symbolPtr++;
|
|
||||||
if (m_symbolPtr >= YSF_SYNC_LENGTH_SYMBOLS)
|
|
||||||
m_symbolPtr = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CYSFRX::processData(q15_t sample)
|
|
||||||
{
|
|
||||||
sample -= m_centre;
|
|
||||||
|
|
||||||
if (sample < -m_threshold) {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x01U;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
|
||||||
m_bufferPtr++;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
|
||||||
m_bufferPtr++;
|
|
||||||
} else if (sample < 0) {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x00U;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
|
||||||
m_bufferPtr++;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
|
||||||
m_bufferPtr++;
|
|
||||||
} else if (sample < m_threshold) {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x02U;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
|
||||||
m_bufferPtr++;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
|
||||||
m_bufferPtr++;
|
|
||||||
} else {
|
|
||||||
m_bitBuffer <<= 2;
|
|
||||||
m_bitBuffer |= 0x03U;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
|
||||||
m_bufferPtr++;
|
|
||||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
|
||||||
m_bufferPtr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only search for a sync in the right place +-2 symbols
|
|
||||||
if (m_bufferPtr >= (YSF_SYNC_LENGTH_BITS - 2U) && m_bufferPtr <= (YSF_SYNC_LENGTH_BITS + 2U)) {
|
|
||||||
// Fuzzy matching of the data sync bit sequence
|
|
||||||
if (countBits64((m_bitBuffer & YSF_SYNC_BITS_MASK) ^ YSF_SYNC_BITS) <= SYNC_BIT_RUN_ERRS) {
|
|
||||||
DEBUG2("YSFRX: found sync in Data, pos", m_bufferPtr - YSF_SYNC_LENGTH_BITS);
|
|
||||||
m_lostCount = MAX_SYNC_FRAMES;
|
|
||||||
m_bufferPtr = YSF_SYNC_LENGTH_BITS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a data frame to the host if the required number of bits have been received
|
|
||||||
if (m_bufferPtr == YSF_FRAME_LENGTH_BITS) {
|
|
||||||
// We've not seen a data sync for too long, signal RXLOST and change to RX_NONE
|
|
||||||
m_lostCount--;
|
|
||||||
if (m_lostCount == 0U) {
|
|
||||||
DEBUG1("YSFRX: sync timed out, lost lock");
|
|
||||||
|
|
||||||
io.setDecode(false);
|
|
||||||
io.setADCDetection(false);
|
|
||||||
|
|
||||||
serial.writeYSFLost();
|
|
||||||
|
|
||||||
m_state = YSFRXS_NONE;
|
|
||||||
} else {
|
|
||||||
m_outBuffer[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
|
|
||||||
|
|
||||||
serial.writeYSFData(m_outBuffer, YSF_FRAME_LENGTH_BYTES + 1U);
|
|
||||||
|
|
||||||
// Start the next frame
|
|
||||||
::memset(m_outBuffer, 0x00U, YSF_FRAME_LENGTH_BYTES + 3U);
|
|
||||||
m_bufferPtr = 0U;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009-2016 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; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// #define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Globals.h"
|
|
||||||
#include "YSFTX.h"
|
|
||||||
|
|
||||||
#include "YSFDefines.h"
|
|
||||||
|
|
||||||
#if defined(WIDE_C4FSK_FILTERS_TX)
|
|
||||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
|
||||||
static q15_t YSF_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
|
||||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
|
||||||
const uint16_t YSF_C4FSK_FILTER_LEN = 22U;
|
|
||||||
#else
|
|
||||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
|
||||||
static q15_t YSF_C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
|
||||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
|
||||||
-553, -847, -731, -340, 104, 401, 0};
|
|
||||||
const uint16_t YSF_C4FSK_FILTER_LEN = 42U;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const q15_t YSF_LEVELA[] = { 809, 809, 809, 809, 809};
|
|
||||||
const q15_t YSF_LEVELB[] = { 269, 269, 269, 269, 269};
|
|
||||||
const q15_t YSF_LEVELC[] = {-269, -269, -269, -269, -269};
|
|
||||||
const q15_t YSF_LEVELD[] = {-809, -809, -809, -809, -809};
|
|
||||||
|
|
||||||
const uint8_t YSF_START_SYNC = 0x77U;
|
|
||||||
const uint8_t YSF_END_SYNC = 0xFFU;
|
|
||||||
|
|
||||||
CYSFTX::CYSFTX() :
|
|
||||||
m_buffer(1500U),
|
|
||||||
m_modFilter(),
|
|
||||||
m_modState(),
|
|
||||||
m_poBuffer(),
|
|
||||||
m_poLen(0U),
|
|
||||||
m_poPtr(0U),
|
|
||||||
m_txDelay(240U), // 200ms
|
|
||||||
m_count(0U)
|
|
||||||
{
|
|
||||||
::memset(m_modState, 0x00U, 70U * sizeof(q15_t));
|
|
||||||
|
|
||||||
m_modFilter.numTaps = YSF_C4FSK_FILTER_LEN;
|
|
||||||
m_modFilter.pState = m_modState;
|
|
||||||
m_modFilter.pCoeffs = YSF_C4FSK_FILTER;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CYSFTX::process()
|
|
||||||
{
|
|
||||||
if (m_buffer.getData() == 0U && m_poLen == 0U)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_poLen == 0U) {
|
|
||||||
if (!m_tx) {
|
|
||||||
m_count = 0U;
|
|
||||||
|
|
||||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
|
||||||
m_poBuffer[m_poLen++] = YSF_START_SYNC;
|
|
||||||
} else {
|
|
||||||
for (uint8_t i = 0U; i < YSF_FRAME_LENGTH_BYTES; i++) {
|
|
||||||
uint8_t c = m_buffer.get();
|
|
||||||
m_poBuffer[m_poLen++] = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_poPtr = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_poLen > 0U) {
|
|
||||||
uint16_t space = io.getSpace();
|
|
||||||
|
|
||||||
while (space > (4U * YSF_RADIO_SYMBOL_LENGTH)) {
|
|
||||||
uint8_t c = m_poBuffer[m_poPtr++];
|
|
||||||
writeByte(c);
|
|
||||||
|
|
||||||
space -= 4U * YSF_RADIO_SYMBOL_LENGTH;
|
|
||||||
|
|
||||||
if (m_poPtr >= m_poLen) {
|
|
||||||
m_poPtr = 0U;
|
|
||||||
m_poLen = 0U;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t CYSFTX::writeData(const uint8_t* data, uint8_t length)
|
|
||||||
{
|
|
||||||
if (length != (YSF_FRAME_LENGTH_BYTES + 1U))
|
|
||||||
return 4U;
|
|
||||||
|
|
||||||
uint16_t space = m_buffer.getSpace();
|
|
||||||
if (space < YSF_FRAME_LENGTH_BYTES)
|
|
||||||
return 5U;
|
|
||||||
|
|
||||||
for (uint8_t i = 0U; i < YSF_FRAME_LENGTH_BYTES; i++)
|
|
||||||
m_buffer.put(data[i + 1U]);
|
|
||||||
|
|
||||||
return 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CYSFTX::writeByte(uint8_t c)
|
|
||||||
{
|
|
||||||
q15_t inBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
|
||||||
q15_t outBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
|
||||||
|
|
||||||
const uint8_t MASK = 0xC0U;
|
|
||||||
|
|
||||||
q15_t* p = inBuffer;
|
|
||||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += YSF_RADIO_SYMBOL_LENGTH) {
|
|
||||||
switch (c & MASK) {
|
|
||||||
case 0xC0U:
|
|
||||||
::memcpy(p, YSF_LEVELA, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
|
||||||
break;
|
|
||||||
case 0x80U:
|
|
||||||
::memcpy(p, YSF_LEVELB, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
|
||||||
break;
|
|
||||||
case 0x00U:
|
|
||||||
::memcpy(p, YSF_LEVELC, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
::memcpy(p, YSF_LEVELD, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t blockSize = YSF_RADIO_SYMBOL_LENGTH * 4U;
|
|
||||||
|
|
||||||
// Handle the case of the oscillator not being accurate enough
|
|
||||||
if (m_sampleCount > 0U) {
|
|
||||||
m_count += YSF_RADIO_SYMBOL_LENGTH * 4U;
|
|
||||||
|
|
||||||
if (m_count >= m_sampleCount) {
|
|
||||||
if (m_sampleInsert) {
|
|
||||||
inBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U] = inBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U - 1U];
|
|
||||||
blockSize++;
|
|
||||||
} else {
|
|
||||||
blockSize--;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_count -= m_sampleCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
|
|
||||||
|
|
||||||
io.write(STATE_YSF, outBuffer, blockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CYSFTX::setTXDelay(uint8_t delay)
|
|
||||||
{
|
|
||||||
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t CYSFTX::getSpace() const
|
|
||||||
{
|
|
||||||
return m_buffer.getSpace() / YSF_FRAME_LENGTH_BYTES;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
#
|
|
||||||
# mmdvmmenu.sh
|
|
||||||
#
|
|
||||||
# Copyright (C) 2016 by Paul Nannery KC2VRJ
|
|
||||||
#
|
|
||||||
# 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; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
#
|
|
||||||
###############################################################################
|
|
||||||
#
|
|
||||||
# On a Linux based system, such as a Raspberry Pi, this script will perform
|
|
||||||
# Modification of the Config.h file for most options. It makes a Back up when
|
|
||||||
# you start the script if none is present. You must recompile and load firmware
|
|
||||||
# onto the Arduino Due if changes are made.
|
|
||||||
#
|
|
||||||
###############################################################################
|
|
||||||
#
|
|
||||||
# CONFIGURATION
|
|
||||||
#
|
|
||||||
# Location of Config.h
|
|
||||||
conf=Config.h
|
|
||||||
#Location of backup file
|
|
||||||
confbak=Config.h.bak
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
#
|
|
||||||
# Do not edit below here
|
|
||||||
#
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Check for backup file and make one if not present
|
|
||||||
|
|
||||||
if [ ! -f $confbak ];then
|
|
||||||
|
|
||||||
cp -f $conf $confbak
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
while :
|
|
||||||
do
|
|
||||||
clear
|
|
||||||
cat<<EOF
|
|
||||||
==============================================================
|
|
||||||
MMDVM Configuration Options
|
|
||||||
--------------------------------------------------------------
|
|
||||||
Please enter your choice:
|
|
||||||
|
|
||||||
(1) Enable 12.000 MHZ Clock
|
|
||||||
(2) Enable 12.288 MHZ Clock
|
|
||||||
(3) Enable 14.400 MHz Clock
|
|
||||||
(4) Enable 19.200 MHz Clock
|
|
||||||
(5) Use the COS to lockout the modem
|
|
||||||
(6) Use pins to output the current mode
|
|
||||||
(7) Use layout for the PAPA board
|
|
||||||
(8) Use layout for ZUM V1.0 and V1.0.1 boards
|
|
||||||
(9) Use layout for SP8NTH board
|
|
||||||
(0) Use modem as display driver
|
|
||||||
(A) Return to Default
|
|
||||||
|
|
||||||
(Q)uit
|
|
||||||
---------------------------------------------------------------
|
|
||||||
EOF
|
|
||||||
read -n1 -s
|
|
||||||
case "$REPLY" in
|
|
||||||
"1") sed -e 's/\/\/ #define EXTERNAL_OSC 12000000/#define EXTERNAL_OSC 12000000/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "12.000 MHz clock enabled";;
|
|
||||||
"2") sed -e 's/\/\/ #define EXTERNAL_OSC 12288000/#define EXTERNAL_OSC 12288000/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "12.288 MHz clock enabled";;
|
|
||||||
"3") sed -e 's/\/\/ #define EXTERNAL_OSC 14400000/#define EXTERNAL_OSC 14400000/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "14.400 MHz clock enabled";;
|
|
||||||
"4") sed -e 's/\/\/ #define EXTERNAL_OSC 19200000/#define EXTERNAL_OSC 19200000/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "19.200 MHz clock enabled";;
|
|
||||||
"5") sed -e 's/\/\/ #define USE_COS_AS_LOCKOUT /#define USE_COS_AS_LOCKOUT/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "COS as Lockout enabled";;
|
|
||||||
"6") sed -e 's/\/\/ #define ARDUINO_MODE_PINS/#define ARDUINO_MODE_PINS/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Mode pins enabled";;
|
|
||||||
"7") sed -e 's/\/\/ #define ARDUINO_DUE_PAPA/#define ARDUINO_DUE_PAPA/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Layout for the PAPA board enabled";;
|
|
||||||
"8") sed -e 's/\/\/ #define ARDUINO_DUE_ZUM_V10/#define ARDUINO_DUE_ZUM_V10/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Layout for ZUM V1.0 and V1.0.1 boards enabled";;
|
|
||||||
"9") sed -e 's/\/\/ #define ARDUINO_DUE_NTH/#define ARDUINO_DUE_NTH/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Layout for SP8NTH board enabled";;
|
|
||||||
"0") sed -e 's/\/\/ #define SERIAL_REPEATER/#define SERIAL_REPEATER/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Modem display driver enabled";;
|
|
||||||
"A") mv -f $confbak $conf ;;
|
|
||||||
"a") mv -f $confbak $conf ;;
|
|
||||||
"Q") echo "If any changes are made you need to (re-)upload the firmware to MMDVM" && exit;;
|
|
||||||
"q") echo "If any changes are made you need to (re-)upload the firmware to MMDVM" && exit;;
|
|
||||||
* ) echo "invalid option" ;;
|
|
||||||
esac
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:MMDVM]
|
||||||
|
platform = ststm32
|
||||||
|
board = genericSTM32F446RE
|
||||||
|
framework = arduino
|
||||||
|
build_unflags = -O0
|
||||||
|
monitor_speed = 460800
|
||||||
|
build_flags =
|
||||||
|
-O3
|
||||||
|
-Wall
|
||||||
|
-D HSE_VALUE=12000000U
|
||||||
|
-D ARM_MATH_CM4
|
||||||
|
-mfpu=fpv4-sp-d16
|
||||||
|
-mfloat-abi=hard
|
||||||
|
-D SERIAL_RX_BUFFER_SIZE=2048
|
||||||
|
-D SERIAL_TX_BUFFER_SIZE=2048
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(AX25DEFINES_H)
|
||||||
|
#define AX25DEFINES_H
|
||||||
|
|
||||||
|
const uint8_t AX25_RADIO_SYMBOL_LENGTH = 20U; // At 24 kHz sample rate
|
||||||
|
|
||||||
|
const uint8_t AX25_FRAME_START = 0x7EU;
|
||||||
|
const uint8_t AX25_FRAME_END = 0x7EU;
|
||||||
|
const uint8_t AX25_FRAME_ABORT = 0xFEU;
|
||||||
|
|
||||||
|
const uint8_t AX25_MAX_ONES = 5U;
|
||||||
|
|
||||||
|
const uint16_t AX25_MIN_FRAME_LENGTH = 17U; // Callsign (7) + Callsign (7) + Control (1) + Checksum (2)
|
||||||
|
|
||||||
|
const uint16_t AX25_MAX_FRAME_LENGTH = 330U; // Callsign (7) + Callsign (7) + 8 Digipeaters (56) +
|
||||||
|
// Control (1) + PID (1) + Data (256) + Checksum (2)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,318 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 by Jonathan Naylor G4KLX
|
||||||
|
* Copyright 2015-2019 Mobilinkd LLC <[email protected]>
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_AX25)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "AX25Demodulator.h"
|
||||||
|
#include "AX25Defines.h"
|
||||||
|
|
||||||
|
const uint32_t SYMBOL_RATE = 1200U;
|
||||||
|
|
||||||
|
const uint16_t DELAY_LEN = 11U;
|
||||||
|
|
||||||
|
const float32_t SAMPLES_PER_SYMBOL = float32_t(SAMPLE_RATE / SYMBOL_RATE);
|
||||||
|
const float32_t PLL_LIMIT = SAMPLES_PER_SYMBOL / 2.0F;
|
||||||
|
|
||||||
|
const uint32_t LPF_FILTER_LEN = 48U;
|
||||||
|
|
||||||
|
q15_t LPF_FILTER_COEFFS[] = {
|
||||||
|
-2, -8, -17, -28, -40, -47, -47, -34,
|
||||||
|
-5, 46, 122, 224, 354, 510, 689, 885,
|
||||||
|
1092, 1302, 1506, 1693, 1856, 1987, 2077, 2124,
|
||||||
|
2124, 2077, 1987, 1856, 1693, 1506, 1302, 1092,
|
||||||
|
885, 689, 510, 354, 224, 122, 46, -5,
|
||||||
|
-34, -47, -47, -40, -28, -17, -8, -2
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lock low-pass filter taps (80Hz Bessel)
|
||||||
|
// scipy.signal:
|
||||||
|
// b, a = bessel(4, [80.0/(1200/2)], 'lowpass')
|
||||||
|
//
|
||||||
|
const uint8_t PLL_IIR_SIZE = 5U;
|
||||||
|
|
||||||
|
const float32_t PLL_LOCK_B[] = {
|
||||||
|
1.077063e-03,4.308253e-03,6.462379e-03,4.308253e-03,1.077063e-03};
|
||||||
|
|
||||||
|
const float32_t PLL_LOCK_A[] = {
|
||||||
|
1.000000e+00,-2.774567e+00,2.962960e+00,-1.437990e+00,2.668296e-01};
|
||||||
|
|
||||||
|
// 64 Hz loop filter.
|
||||||
|
// scipy.signal:
|
||||||
|
// loop_coeffs = firwin(9, [64.0/(1200/2)], width = None,
|
||||||
|
// pass_zero = True, scale = True, window='hann')
|
||||||
|
//
|
||||||
|
const uint32_t PLL_FILTER_LEN = 7U;
|
||||||
|
|
||||||
|
float32_t PLL_FILTER_COEFFS[] = {3.196252e-02F, 1.204223e-01F, 2.176819e-01F, 2.598666e-01F, 2.176819e-01F, 1.204223e-01F, 3.196252e-02F};
|
||||||
|
|
||||||
|
CAX25Demodulator::CAX25Demodulator(int8_t n) :
|
||||||
|
m_frame(),
|
||||||
|
m_twist(n),
|
||||||
|
m_lpfFilter(),
|
||||||
|
m_lpfState(),
|
||||||
|
m_delayLine(NULL),
|
||||||
|
m_delayPos(0U),
|
||||||
|
m_nrziState(false),
|
||||||
|
m_pllFilter(),
|
||||||
|
m_pllState(),
|
||||||
|
m_pllLast(false),
|
||||||
|
m_pllBits(1U),
|
||||||
|
m_pllCount(0.0F),
|
||||||
|
m_pllJitter(0.0F),
|
||||||
|
m_pllDCD(false),
|
||||||
|
m_iirHistory(),
|
||||||
|
m_hdlcOnes(0U),
|
||||||
|
m_hdlcFlag(false),
|
||||||
|
m_hdlcBuffer(0U),
|
||||||
|
m_hdlcBits(0U),
|
||||||
|
m_hdlcState(AX25_IDLE)
|
||||||
|
{
|
||||||
|
m_delayLine = new bool[DELAY_LEN];
|
||||||
|
|
||||||
|
m_lpfFilter.numTaps = LPF_FILTER_LEN;
|
||||||
|
m_lpfFilter.pState = m_lpfState;
|
||||||
|
m_lpfFilter.pCoeffs = LPF_FILTER_COEFFS;
|
||||||
|
|
||||||
|
m_pllFilter.numTaps = PLL_FILTER_LEN;
|
||||||
|
m_pllFilter.pState = m_pllState;
|
||||||
|
m_pllFilter.pCoeffs = PLL_FILTER_COEFFS;
|
||||||
|
|
||||||
|
for (uint8_t i = 0U; i < PLL_IIR_SIZE; i++)
|
||||||
|
m_iirHistory[i] = 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAX25Demodulator::process(q15_t* samples, uint8_t length, CAX25Frame& frame)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
q15_t fa[RX_BLOCK_SIZE];
|
||||||
|
m_twist.process(samples, fa, RX_BLOCK_SIZE);
|
||||||
|
|
||||||
|
int16_t buffer[RX_BLOCK_SIZE];
|
||||||
|
for (uint8_t i = 0; i < length; i++) {
|
||||||
|
bool level = (fa[i] >= 0);
|
||||||
|
bool delayed = delay(level);
|
||||||
|
buffer[i] = (int16_t(level ^ delayed) << 1) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
q15_t fc[RX_BLOCK_SIZE];
|
||||||
|
::arm_fir_fast_q15(&m_lpfFilter, buffer, fc, RX_BLOCK_SIZE);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < length; i++) {
|
||||||
|
bool bit = fc[i] >= 0;
|
||||||
|
bool sample = PLL(bit);
|
||||||
|
|
||||||
|
if (sample) {
|
||||||
|
// We will only ever get one frame because there are
|
||||||
|
// not enough bits in a block for more than one.
|
||||||
|
if (result) {
|
||||||
|
HDLC(NRZI(bit));
|
||||||
|
} else {
|
||||||
|
result = HDLC(NRZI(bit));
|
||||||
|
if (result) {
|
||||||
|
// Copy the frame data.
|
||||||
|
::memcpy(frame.m_data, m_frame.m_data, AX25_MAX_PACKET_LEN);
|
||||||
|
frame.m_length = m_frame.m_length;
|
||||||
|
frame.m_fcs = m_frame.m_fcs;
|
||||||
|
m_frame.m_length = 0U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAX25Demodulator::delay(bool b)
|
||||||
|
{
|
||||||
|
bool r = m_delayLine[m_delayPos];
|
||||||
|
|
||||||
|
m_delayLine[m_delayPos++] = b;
|
||||||
|
|
||||||
|
if (m_delayPos >= DELAY_LEN)
|
||||||
|
m_delayPos = 0U;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAX25Demodulator::NRZI(bool b)
|
||||||
|
{
|
||||||
|
bool result = (b == m_nrziState);
|
||||||
|
|
||||||
|
m_nrziState = b;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAX25Demodulator::PLL(bool input)
|
||||||
|
{
|
||||||
|
bool sample = false;
|
||||||
|
|
||||||
|
if (input != m_pllLast || m_pllBits > 16U) {
|
||||||
|
// Record transition.
|
||||||
|
m_pllLast = input;
|
||||||
|
|
||||||
|
if (m_pllCount > PLL_LIMIT)
|
||||||
|
m_pllCount -= SAMPLES_PER_SYMBOL;
|
||||||
|
|
||||||
|
float32_t adjust = m_pllBits > 16U ? 5.0F : 0.0F;
|
||||||
|
float32_t offset = m_pllCount / float32_t(m_pllBits);
|
||||||
|
float32_t jitter;
|
||||||
|
::arm_fir_f32(&m_pllFilter, &offset, &jitter, 1U);
|
||||||
|
|
||||||
|
float32_t absOffset = adjust;
|
||||||
|
if (offset < 0.0F)
|
||||||
|
absOffset -= offset;
|
||||||
|
else
|
||||||
|
absOffset += offset;
|
||||||
|
m_pllJitter = iir(absOffset);
|
||||||
|
|
||||||
|
m_pllCount -= jitter / 2.0F;
|
||||||
|
m_pllBits = 1U;
|
||||||
|
} else {
|
||||||
|
if (m_pllCount > PLL_LIMIT) {
|
||||||
|
sample = true;
|
||||||
|
m_pllCount -= SAMPLES_PER_SYMBOL;
|
||||||
|
m_pllBits++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pllCount += 1.0F;
|
||||||
|
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAX25Demodulator::HDLC(bool b)
|
||||||
|
{
|
||||||
|
if (m_hdlcOnes == AX25_MAX_ONES) {
|
||||||
|
if (b) {
|
||||||
|
// flag byte
|
||||||
|
m_hdlcFlag = true;
|
||||||
|
} else {
|
||||||
|
// bit stuffing...
|
||||||
|
m_hdlcFlag = false;
|
||||||
|
m_hdlcOnes = 0U;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hdlcBuffer >>= 1;
|
||||||
|
m_hdlcBuffer |= b ? 128U : 0U;
|
||||||
|
m_hdlcBits++; // Free-running until Sync byte.
|
||||||
|
|
||||||
|
if (b)
|
||||||
|
m_hdlcOnes++;
|
||||||
|
else
|
||||||
|
m_hdlcOnes = 0U;
|
||||||
|
|
||||||
|
if (m_hdlcFlag) {
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
switch (m_hdlcBuffer) {
|
||||||
|
case AX25_FRAME_END:
|
||||||
|
if (m_frame.m_length >= AX25_MIN_FRAME_LENGTH) {
|
||||||
|
result = m_frame.checkCRC();
|
||||||
|
if (!result)
|
||||||
|
m_frame.m_length = 0U;
|
||||||
|
} else {
|
||||||
|
m_frame.m_length = 0U;
|
||||||
|
}
|
||||||
|
m_hdlcState = AX25_SYNC;
|
||||||
|
m_hdlcFlag = false;
|
||||||
|
m_hdlcBits = 0U;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AX25_FRAME_ABORT:
|
||||||
|
// Frame aborted
|
||||||
|
m_frame.m_length = 0U;
|
||||||
|
m_hdlcState = AX25_IDLE;
|
||||||
|
m_hdlcFlag = false;
|
||||||
|
m_hdlcBits = 0U;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_hdlcState) {
|
||||||
|
case AX25_IDLE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AX25_SYNC:
|
||||||
|
if (m_hdlcBits == 8U) { // 8th bit.
|
||||||
|
// Start of frame data.
|
||||||
|
m_hdlcState = AX25_RECEIVE;
|
||||||
|
m_frame.append(m_hdlcBuffer);
|
||||||
|
m_hdlcBits = 0U;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AX25_RECEIVE:
|
||||||
|
if (m_hdlcBits == 8U) { // 8th bit.
|
||||||
|
m_frame.append(m_hdlcBuffer);
|
||||||
|
m_hdlcBits = 0U;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAX25Demodulator::setTwist(int8_t n)
|
||||||
|
{
|
||||||
|
m_twist.setTwist(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAX25Demodulator::isDCD()
|
||||||
|
{
|
||||||
|
if (m_pllJitter <= (SAMPLES_PER_SYMBOL * 0.03F))
|
||||||
|
m_pllDCD = true;
|
||||||
|
else if (m_pllJitter >= (SAMPLES_PER_SYMBOL * 0.15F))
|
||||||
|
m_pllDCD = false;
|
||||||
|
|
||||||
|
return m_pllDCD;
|
||||||
|
}
|
||||||
|
|
||||||
|
float32_t CAX25Demodulator::iir(float32_t input)
|
||||||
|
{
|
||||||
|
for (int8_t i = int8_t(PLL_IIR_SIZE) - 1; i != 0; i--)
|
||||||
|
m_iirHistory[i] = m_iirHistory[i - 1];
|
||||||
|
|
||||||
|
m_iirHistory[0] = input;
|
||||||
|
for (uint8_t i = 1U; i < PLL_IIR_SIZE; i++)
|
||||||
|
m_iirHistory[0] -= PLL_LOCK_A[i] * m_iirHistory[i];
|
||||||
|
|
||||||
|
float32_t result = 0.0F;
|
||||||
|
for (uint8_t i = 0U; i < PLL_IIR_SIZE; i++)
|
||||||
|
result += PLL_LOCK_B[i] * m_iirHistory[i];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_AX25)
|
||||||
|
|
||||||
|
#if !defined(AX25Demodulator_H)
|
||||||
|
#define AX25Demodulator_H
|
||||||
|
|
||||||
|
#include "AX25Frame.h"
|
||||||
|
#include "AX25Twist.h"
|
||||||
|
|
||||||
|
enum AX25_STATE {
|
||||||
|
AX25_IDLE,
|
||||||
|
AX25_SYNC,
|
||||||
|
AX25_RECEIVE
|
||||||
|
};
|
||||||
|
|
||||||
|
class CAX25Demodulator {
|
||||||
|
public:
|
||||||
|
CAX25Demodulator(int8_t n);
|
||||||
|
|
||||||
|
bool process(q15_t* samples, uint8_t length, CAX25Frame& frame);
|
||||||
|
|
||||||
|
void setTwist(int8_t n);
|
||||||
|
|
||||||
|
bool isDCD();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CAX25Frame m_frame;
|
||||||
|
CAX25Twist m_twist;
|
||||||
|
arm_fir_instance_q15 m_lpfFilter;
|
||||||
|
q15_t m_lpfState[70U]; // NoTaps + BlockSize - 1, 48 + 20 - 1 plus some spare
|
||||||
|
bool* m_delayLine;
|
||||||
|
uint16_t m_delayPos;
|
||||||
|
bool m_nrziState;
|
||||||
|
arm_fir_instance_f32 m_pllFilter;
|
||||||
|
float32_t m_pllState[20U]; // NoTaps + BlockSize - 1, 7 + 1 - 1 plus some spare
|
||||||
|
bool m_pllLast;
|
||||||
|
uint8_t m_pllBits;
|
||||||
|
float32_t m_pllCount;
|
||||||
|
float32_t m_pllJitter;
|
||||||
|
bool m_pllDCD;
|
||||||
|
float32_t m_iirHistory[5U];
|
||||||
|
uint16_t m_hdlcOnes;
|
||||||
|
bool m_hdlcFlag;
|
||||||
|
uint16_t m_hdlcBuffer;
|
||||||
|
uint16_t m_hdlcBits;
|
||||||
|
AX25_STATE m_hdlcState;
|
||||||
|
|
||||||
|
bool delay(bool b);
|
||||||
|
bool NRZI(bool b);
|
||||||
|
bool PLL(bool b);
|
||||||
|
bool HDLC(bool b);
|
||||||
|
float32_t iir(float32_t input);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_AX25)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "AX25Frame.h"
|
||||||
|
|
||||||
|
const uint16_t CCITT_TABLE[] = {
|
||||||
|
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 };
|
||||||
|
|
||||||
|
CAX25Frame::CAX25Frame(const uint8_t* data, uint16_t length) :
|
||||||
|
m_data(),
|
||||||
|
m_length(0U),
|
||||||
|
m_fcs(0U)
|
||||||
|
{
|
||||||
|
for (uint16_t i = 0U; i < length && i < (AX25_MAX_PACKET_LEN - 2U); i++)
|
||||||
|
m_data[m_length++] = data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
CAX25Frame::CAX25Frame() :
|
||||||
|
m_data(),
|
||||||
|
m_length(0U),
|
||||||
|
m_fcs(0U)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAX25Frame::append(uint16_t c)
|
||||||
|
{
|
||||||
|
if (m_length == AX25_MAX_PACKET_LEN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_data[m_length++] = uint8_t(c);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAX25Frame::checkCRC()
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
uint16_t crc16;
|
||||||
|
uint8_t crc8[2U];
|
||||||
|
};
|
||||||
|
|
||||||
|
crc16 = 0xFFFFU;
|
||||||
|
for (uint16_t i = 0U; i < (m_length - 2U); i++)
|
||||||
|
crc16 = uint16_t(crc8[1U]) ^ CCITT_TABLE[crc8[0U] ^ m_data[i]];
|
||||||
|
|
||||||
|
crc16 = ~crc16;
|
||||||
|
|
||||||
|
if (crc8[0U] == m_data[m_length - 2U] && crc8[1U] == m_data[m_length - 1U]) {
|
||||||
|
m_fcs = crc16;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAX25Frame::addCRC()
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
uint16_t crc16;
|
||||||
|
uint8_t crc8[2U];
|
||||||
|
};
|
||||||
|
|
||||||
|
crc16 = 0xFFFFU;
|
||||||
|
for (uint16_t i = 0U; i < m_length; i++)
|
||||||
|
crc16 = uint16_t(crc8[1U]) ^ CCITT_TABLE[crc8[0U] ^ m_data[i]];
|
||||||
|
|
||||||
|
crc16 = ~crc16;
|
||||||
|
|
||||||
|
m_fcs = crc16;
|
||||||
|
|
||||||
|
m_data[m_length++] = crc8[0U];
|
||||||
|
m_data[m_length++] = crc8[1U];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_AX25)
|
||||||
|
|
||||||
|
#if !defined(AX25Frame_H)
|
||||||
|
#define AX25Frame_H
|
||||||
|
|
||||||
|
const uint16_t AX25_MAX_PACKET_LEN = 300U;
|
||||||
|
|
||||||
|
class CAX25Frame {
|
||||||
|
public:
|
||||||
|
CAX25Frame(const uint8_t* data, uint16_t length);
|
||||||
|
CAX25Frame();
|
||||||
|
|
||||||
|
bool append(uint16_t c);
|
||||||
|
|
||||||
|
bool checkCRC();
|
||||||
|
|
||||||
|
void addCRC();
|
||||||
|
|
||||||
|
uint8_t m_data[AX25_MAX_PACKET_LEN];
|
||||||
|
uint16_t m_length;
|
||||||
|
uint16_t m_fcs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_AX25)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "AX25RX.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generated with Scipy Filter, 152 coefficients, 1100-2300Hz bandpass,
|
||||||
|
* Hann window, starting and ending 0 value coefficients removed.
|
||||||
|
*
|
||||||
|
* np.array(
|
||||||
|
* firwin2(152,
|
||||||
|
* [
|
||||||
|
* 0.0,
|
||||||
|
* 1000.0/(sample_rate/2),
|
||||||
|
* 1100.0/(sample_rate/2),
|
||||||
|
* 2350.0/(sample_rate/2),
|
||||||
|
* 2500.0/(sample_rate/2),
|
||||||
|
* 1.0
|
||||||
|
* ],
|
||||||
|
* [0,0,1,1,0,0],
|
||||||
|
* antisymmetric = False,
|
||||||
|
* window='hann') * 32768,
|
||||||
|
* dtype=int)[10:-10]
|
||||||
|
*/
|
||||||
|
|
||||||
|
const uint32_t FILTER_LEN = 130U;
|
||||||
|
|
||||||
|
q15_t FILTER_COEFFS[] = {
|
||||||
|
5, 12, 18, 21, 19, 11, -2, -15, -25, -27,
|
||||||
|
-21, -11, -3, -5, -19, -43, -69, -83, -73, -35,
|
||||||
|
27, 98, 155, 180, 163, 109, 39, -20, -45, -26,
|
||||||
|
23, 74, 89, 39, -81, -247, -407, -501, -480, -334,
|
||||||
|
-92, 175, 388, 479, 429, 275, 99, 5, 68, 298,
|
||||||
|
626, 913, 994, 740, 115, -791, -1770, -2544, -2847, -2509,
|
||||||
|
-1527, -76, 1518, 2875, 3653, 3653, 2875, 1518, -76, -1527,
|
||||||
|
-2509, -2847, -2544, -1770, -791, 115, 740, 994, 913, 626,
|
||||||
|
298, 68, 5, 99, 275, 429, 479, 388, 175, -92,
|
||||||
|
-334, -480, -501, -407, -247, -81, 39, 89, 74, 23,
|
||||||
|
-26, -45, -20, 39, 109, 163, 180, 155, 98, 27,
|
||||||
|
-35, -73, -83, -69, -43, -19, -5, -3, -11, -21,
|
||||||
|
-27, -25, -15, -2, 11, 19, 21, 18, 12, 5
|
||||||
|
};
|
||||||
|
|
||||||
|
CAX25RX::CAX25RX() :
|
||||||
|
m_filter(),
|
||||||
|
m_state(),
|
||||||
|
m_demod1(3),
|
||||||
|
m_demod2(6),
|
||||||
|
m_demod3(9),
|
||||||
|
m_lastFCS(0U),
|
||||||
|
m_count(0U),
|
||||||
|
m_slotTime(30U),
|
||||||
|
m_slotCount(0U),
|
||||||
|
m_pPersist(128U),
|
||||||
|
m_dcd(false),
|
||||||
|
m_canTX(false),
|
||||||
|
m_x(1U),
|
||||||
|
m_a(0xB7U),
|
||||||
|
m_b(0x73U),
|
||||||
|
m_c(0xF6U)
|
||||||
|
{
|
||||||
|
m_filter.numTaps = FILTER_LEN;
|
||||||
|
m_filter.pState = m_state;
|
||||||
|
m_filter.pCoeffs = FILTER_COEFFS;
|
||||||
|
|
||||||
|
initRand();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAX25RX::samples(q15_t* samples, uint8_t length)
|
||||||
|
{
|
||||||
|
q15_t output[RX_BLOCK_SIZE];
|
||||||
|
::arm_fir_fast_q15(&m_filter, samples, output, RX_BLOCK_SIZE);
|
||||||
|
|
||||||
|
m_count++;
|
||||||
|
|
||||||
|
CAX25Frame frame;
|
||||||
|
|
||||||
|
bool ret = m_demod1.process(output, length, frame);
|
||||||
|
if (ret) {
|
||||||
|
if (frame.m_fcs != m_lastFCS || m_count > 2U) {
|
||||||
|
m_lastFCS = frame.m_fcs;
|
||||||
|
m_count = 0U;
|
||||||
|
serial.writeAX25Data(frame.m_data, frame.m_length - 2U);
|
||||||
|
}
|
||||||
|
DEBUG1("Decoder 1 reported");
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = m_demod2.process(output, length, frame);
|
||||||
|
if (ret) {
|
||||||
|
if (frame.m_fcs != m_lastFCS || m_count > 2U) {
|
||||||
|
m_lastFCS = frame.m_fcs;
|
||||||
|
m_count = 0U;
|
||||||
|
serial.writeAX25Data(frame.m_data, frame.m_length - 2U);
|
||||||
|
}
|
||||||
|
DEBUG1("Decoder 2 reported");
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = m_demod3.process(output, length, frame);
|
||||||
|
if (ret) {
|
||||||
|
if (frame.m_fcs != m_lastFCS || m_count > 2U) {
|
||||||
|
m_lastFCS = frame.m_fcs;
|
||||||
|
m_count = 0U;
|
||||||
|
serial.writeAX25Data(frame.m_data, frame.m_length - 2U);
|
||||||
|
}
|
||||||
|
DEBUG1("Decoder 3 reported");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_slotCount += RX_BLOCK_SIZE;
|
||||||
|
if (m_slotCount >= m_slotTime) {
|
||||||
|
m_slotCount = 0U;
|
||||||
|
|
||||||
|
bool dcd1 = m_demod1.isDCD();
|
||||||
|
bool dcd2 = m_demod2.isDCD();
|
||||||
|
bool dcd3 = m_demod3.isDCD();
|
||||||
|
|
||||||
|
if (dcd1 || dcd2 || dcd3) {
|
||||||
|
if (!m_dcd) {
|
||||||
|
io.setDecode(true);
|
||||||
|
io.setADCDetection(true);
|
||||||
|
m_dcd = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_canTX = false;
|
||||||
|
} else {
|
||||||
|
if (m_dcd) {
|
||||||
|
io.setDecode(false);
|
||||||
|
io.setADCDetection(false);
|
||||||
|
m_dcd = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_canTX = m_pPersist >= rand();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAX25RX::canTX() const
|
||||||
|
{
|
||||||
|
return m_canTX;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAX25RX::setParams(int8_t twist, uint8_t slotTime, uint8_t pPersist)
|
||||||
|
{
|
||||||
|
m_demod1.setTwist(twist - 3);
|
||||||
|
m_demod2.setTwist(twist);
|
||||||
|
m_demod3.setTwist(twist + 3);
|
||||||
|
|
||||||
|
m_slotTime = slotTime * 240U; // Slot time in samples
|
||||||
|
m_pPersist = pPersist;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taken from https://www.electro-tech-online.com/threads/ultra-fast-pseudorandom-number-generator-for-8-bit.124249/
|
||||||
|
//X ABC Algorithm Random Number Generator for 8-Bit Devices:
|
||||||
|
//This is a small PRNG, experimentally verified to have at least a 50 million byte period
|
||||||
|
//by generating 50 million bytes and observing that there were no overapping sequences and repeats.
|
||||||
|
//This generator passes serial correlation, entropy , Monte Carlo Pi value, arithmetic mean,
|
||||||
|
//And many other statistical tests. This generator may have a period of up to 2^32, but this has
|
||||||
|
//not been verified.
|
||||||
|
//
|
||||||
|
// By XORing 3 bytes into the a,b, and c registers, you can add in entropy from
|
||||||
|
//an external source easily.
|
||||||
|
//
|
||||||
|
//This generator is free to use, but is not suitable for cryptography due to its short period(by //cryptographic standards) and simple construction. No attempt was made to make this generator
|
||||||
|
// suitable for cryptographic use.
|
||||||
|
//
|
||||||
|
//Due to the use of a constant counter, the generator should be resistant to latching up.
|
||||||
|
//A significant performance gain is had in that the x variable is only ever incremented.
|
||||||
|
//
|
||||||
|
//Only 4 bytes of ram are needed for the internal state, and generating a byte requires 3 XORs , //2 ADDs, one bit shift right , and one increment. Difficult or slow operations like multiply, etc
|
||||||
|
//were avoided for maximum speed on ultra low power devices.
|
||||||
|
|
||||||
|
|
||||||
|
void CAX25RX::initRand() //Can also be used to seed the rng with more entropy during use.
|
||||||
|
{
|
||||||
|
m_a = (m_a ^ m_c ^ m_x);
|
||||||
|
m_b = (m_b + m_a);
|
||||||
|
m_c = (m_c + ((m_b >> 1) ^ m_a));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CAX25RX::rand()
|
||||||
|
{
|
||||||
|
m_x++; //x is incremented every round and is not affected by any other variable
|
||||||
|
|
||||||
|
m_a = (m_a ^ m_c ^ m_x); //note the mix of addition and XOR
|
||||||
|
m_b = (m_b + m_a); //And the use of very few instructions
|
||||||
|
m_c = (m_c + ((m_b >> 1) ^ m_a)); //the right shift is to ensure that high-order bits from b can affect
|
||||||
|
|
||||||
|
return uint8_t(m_c); //low order bits of other variables
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_AX25)
|
||||||
|
|
||||||
|
#if !defined(AX25RX_H)
|
||||||
|
#define AX25RX_H
|
||||||
|
|
||||||
|
#include "AX25Demodulator.h"
|
||||||
|
|
||||||
|
class CAX25RX {
|
||||||
|
public:
|
||||||
|
CAX25RX();
|
||||||
|
|
||||||
|
void samples(q15_t* samples, uint8_t length);
|
||||||
|
|
||||||
|
void setParams(int8_t twist, uint8_t slotTime, uint8_t pPersist);
|
||||||
|
|
||||||
|
bool canTX() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
arm_fir_instance_q15 m_filter;
|
||||||
|
q15_t m_state[160U]; // NoTaps + BlockSize - 1, 130 + 20 - 1 plus some spare
|
||||||
|
CAX25Demodulator m_demod1;
|
||||||
|
CAX25Demodulator m_demod2;
|
||||||
|
CAX25Demodulator m_demod3;
|
||||||
|
uint16_t m_lastFCS;
|
||||||
|
uint32_t m_count;
|
||||||
|
uint32_t m_slotTime;
|
||||||
|
uint32_t m_slotCount;
|
||||||
|
uint8_t m_pPersist;
|
||||||
|
bool m_dcd;
|
||||||
|
bool m_canTX;
|
||||||
|
uint8_t m_x;
|
||||||
|
uint8_t m_a;
|
||||||
|
uint8_t m_b;
|
||||||
|
uint8_t m_c;
|
||||||
|
|
||||||
|
void initRand();
|
||||||
|
uint8_t rand();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_AX25)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "AX25TX.h"
|
||||||
|
|
||||||
|
#include "AX25Defines.h"
|
||||||
|
#include "AX25Frame.h"
|
||||||
|
|
||||||
|
|
||||||
|
const uint8_t START_FLAG[] = { AX25_FRAME_START };
|
||||||
|
const uint8_t END_FLAG[] = { AX25_FRAME_END };
|
||||||
|
|
||||||
|
const uint8_t BIT_MASK_TABLE1[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
|
||||||
|
|
||||||
|
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE1[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE1[(i)&7])
|
||||||
|
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE1[(i)&7])
|
||||||
|
|
||||||
|
const uint8_t BIT_MASK_TABLE2[] = { 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U };
|
||||||
|
|
||||||
|
#define WRITE_BIT2(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE2[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE2[(i)&7])
|
||||||
|
#define READ_BIT2(p,i) (p[(i)>>3] & BIT_MASK_TABLE2[(i)&7])
|
||||||
|
|
||||||
|
const uint16_t AUDIO_TABLE_LEN = 120U;
|
||||||
|
|
||||||
|
const q15_t AUDIO_TABLE_DATA[] = {
|
||||||
|
0, 214, 428, 641, 851, 1060, 1265, 1468, 1666, 1859, 2048, 2230, 2407, 2577, 2740, 2896, 3043, 3182, 3313, 3434, 3546, 3649,
|
||||||
|
3741, 3823, 3895, 3955, 4006, 4045, 4073, 4089, 4095, 4089, 4073, 4045, 4006, 3955, 3895, 3823, 3741, 3649, 3546, 3434, 3313,
|
||||||
|
3182, 3043, 2896, 2740, 2577, 2407, 2230, 2048, 1859, 1666, 1468, 1265, 1060, 851, 641, 428, 214, 0, -214, -428, -641, -851,
|
||||||
|
-1060, -1265, -1468, -1666, -1859, -2047, -2230, -2407, -2577, -2740, -2896, -3043, -3182, -3313, -3434, -3546, -3649, -3741,
|
||||||
|
-3823, -3895, -3955, -4006, -4045, -4073, -4089, -4095, -4089, -4073, -4045, -4006, -3955, -3895, -3823, -3741, -3649, -3546,
|
||||||
|
-3434, -3313, -3182, -3043, -2896, -2740, -2577, -2407, -2230, -2047, -1859, -1666, -1468, -1265, -1060, -851, -641, -428, -214
|
||||||
|
};
|
||||||
|
|
||||||
|
CAX25TX::CAX25TX() :
|
||||||
|
m_poBuffer(),
|
||||||
|
m_poLen(0U),
|
||||||
|
m_poPtr(0U),
|
||||||
|
m_txDelay(360U),
|
||||||
|
m_tablePtr(0U),
|
||||||
|
m_nrzi(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAX25TX::process()
|
||||||
|
{
|
||||||
|
if (m_poLen == 0U)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!m_duplex) {
|
||||||
|
if (m_poPtr == 0U) {
|
||||||
|
bool tx = ax25RX.canTX();
|
||||||
|
if (!tx)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t space = io.getSpace();
|
||||||
|
|
||||||
|
while (space > AX25_RADIO_SYMBOL_LENGTH) {
|
||||||
|
bool b = READ_BIT1(m_poBuffer, m_poPtr) != 0U;
|
||||||
|
m_poPtr++;
|
||||||
|
|
||||||
|
writeBit(b);
|
||||||
|
|
||||||
|
space -= AX25_RADIO_SYMBOL_LENGTH;
|
||||||
|
|
||||||
|
if (m_poPtr >= m_poLen) {
|
||||||
|
m_poPtr = 0U;
|
||||||
|
m_poLen = 0U;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CAX25TX::writeData(const uint8_t* data, uint16_t length)
|
||||||
|
{
|
||||||
|
CAX25Frame frame(data, length);
|
||||||
|
frame.addCRC();
|
||||||
|
|
||||||
|
m_poLen = 0U;
|
||||||
|
m_poPtr = 0U;
|
||||||
|
m_nrzi = false;
|
||||||
|
m_tablePtr = 0U;
|
||||||
|
|
||||||
|
// Add TX delay
|
||||||
|
for (uint16_t i = 0U; i < m_txDelay; i++, m_poLen++) {
|
||||||
|
bool preamble = NRZI(false);
|
||||||
|
WRITE_BIT1(m_poBuffer, m_poLen, preamble);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the Start Flag
|
||||||
|
for (uint16_t i = 0U; i < 8U; i++, m_poLen++) {
|
||||||
|
bool b1 = READ_BIT1(START_FLAG, i) != 0U;
|
||||||
|
bool b2 = NRZI(b1);
|
||||||
|
WRITE_BIT1(m_poBuffer, m_poLen, b2);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ones = 0U;
|
||||||
|
for (uint16_t i = 0U; i < (frame.m_length * 8U); i++) {
|
||||||
|
bool b1 = READ_BIT2(frame.m_data, i) != 0U;
|
||||||
|
bool b2 = NRZI(b1);
|
||||||
|
WRITE_BIT1(m_poBuffer, m_poLen, b2);
|
||||||
|
m_poLen++;
|
||||||
|
|
||||||
|
if (b1) {
|
||||||
|
ones++;
|
||||||
|
if (ones == AX25_MAX_ONES) {
|
||||||
|
// Bit stuffing
|
||||||
|
bool b = NRZI(false);
|
||||||
|
WRITE_BIT1(m_poBuffer, m_poLen, b);
|
||||||
|
m_poLen++;
|
||||||
|
ones = 0U;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ones = 0U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the End Flag
|
||||||
|
for (uint16_t i = 0U; i < 8U; i++, m_poLen++) {
|
||||||
|
bool b1 = READ_BIT1(END_FLAG, i) != 0U;
|
||||||
|
bool b2 = NRZI(b1);
|
||||||
|
WRITE_BIT1(m_poBuffer, m_poLen, b2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAX25TX::writeBit(bool b)
|
||||||
|
{
|
||||||
|
q15_t buffer[AX25_RADIO_SYMBOL_LENGTH];
|
||||||
|
for (uint8_t i = 0U; i < AX25_RADIO_SYMBOL_LENGTH; i++) {
|
||||||
|
q15_t value = AUDIO_TABLE_DATA[m_tablePtr];
|
||||||
|
|
||||||
|
if (b) {
|
||||||
|
// De-emphasise the lower frequency by 6dB
|
||||||
|
value >>= 2;
|
||||||
|
m_tablePtr += 6U;
|
||||||
|
} else {
|
||||||
|
m_tablePtr += 11U;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[i] = value >> 1;
|
||||||
|
|
||||||
|
if (m_tablePtr >= AUDIO_TABLE_LEN)
|
||||||
|
m_tablePtr -= AUDIO_TABLE_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
io.write(STATE_AX25, buffer, AX25_RADIO_SYMBOL_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAX25TX::setTXDelay(uint8_t delay)
|
||||||
|
{
|
||||||
|
m_txDelay = delay * 12U;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CAX25TX::getSpace() const
|
||||||
|
{
|
||||||
|
return m_poLen == 0U ? 255U : 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAX25TX::NRZI(bool b)
|
||||||
|
{
|
||||||
|
if (!b)
|
||||||
|
m_nrzi = !m_nrzi;
|
||||||
|
|
||||||
|
return m_nrzi;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_AX25)
|
||||||
|
|
||||||
|
#if !defined(AX25TX_H)
|
||||||
|
#define AX25TX_H
|
||||||
|
|
||||||
|
class CAX25TX {
|
||||||
|
public:
|
||||||
|
CAX25TX();
|
||||||
|
|
||||||
|
uint8_t writeData(const uint8_t* data, uint16_t length);
|
||||||
|
|
||||||
|
void process();
|
||||||
|
|
||||||
|
void setTXDelay(uint8_t delay);
|
||||||
|
|
||||||
|
uint8_t getSpace() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t m_poBuffer[600U];
|
||||||
|
uint16_t m_poLen;
|
||||||
|
uint16_t m_poPtr;
|
||||||
|
uint16_t m_txDelay;
|
||||||
|
uint16_t m_tablePtr;
|
||||||
|
bool m_nrzi;
|
||||||
|
|
||||||
|
void writeBit(bool b);
|
||||||
|
bool NRZI(bool b);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,316 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_AX25)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "AX25Twist.h"
|
||||||
|
|
||||||
|
// 1200Hz = -12dB, 2200Hz = 0dB; 3381Hz cutoff; cosine.
|
||||||
|
q15_t dB12[] = {
|
||||||
|
176,
|
||||||
|
-812,
|
||||||
|
-3916,
|
||||||
|
-7586,
|
||||||
|
23536,
|
||||||
|
-7586,
|
||||||
|
-3916,
|
||||||
|
-812,
|
||||||
|
176
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = -11dB, 2200Hz = 0dB; 3258Hz cutoff; cosine.
|
||||||
|
q15_t dB11[] = {
|
||||||
|
121,
|
||||||
|
-957,
|
||||||
|
-3959,
|
||||||
|
-7383,
|
||||||
|
23871,
|
||||||
|
-7383,
|
||||||
|
-3959,
|
||||||
|
-957,
|
||||||
|
121
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = -10dB, 2200Hz = 0dB; 3118Hz cutoff; cosine.
|
||||||
|
q15_t dB10[] = {
|
||||||
|
56,
|
||||||
|
-1110,
|
||||||
|
-3987,
|
||||||
|
-7141,
|
||||||
|
24254,
|
||||||
|
-7141,
|
||||||
|
-3987,
|
||||||
|
-1110,
|
||||||
|
56
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = -9dB, 2200Hz = 0dB; 2959Hz cutoff; cosine.
|
||||||
|
q15_t dB9[] = {
|
||||||
|
-19,
|
||||||
|
-1268,
|
||||||
|
-3994,
|
||||||
|
-6856,
|
||||||
|
24688,
|
||||||
|
-6856,
|
||||||
|
-3994,
|
||||||
|
-1268,
|
||||||
|
-19
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = -8dB, 2200Hz = 0dB; 2778Hz cutoff; cosine.
|
||||||
|
q15_t dB8[] = {
|
||||||
|
-104,
|
||||||
|
-1424,
|
||||||
|
-3968,
|
||||||
|
-6516,
|
||||||
|
25182,
|
||||||
|
-6516,
|
||||||
|
-3968,
|
||||||
|
-1424,
|
||||||
|
-104
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = -7dB, 2200Hz = 0dB; 2573Hz cutoff; cosine.
|
||||||
|
q15_t dB7[] = {
|
||||||
|
-196,
|
||||||
|
-1565,
|
||||||
|
-3896,
|
||||||
|
-6114,
|
||||||
|
25742,
|
||||||
|
-6114,
|
||||||
|
-3896,
|
||||||
|
-1565,
|
||||||
|
-196
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = -6dB, 2200Hz = 0dB; 2343Hz cutoff; cosine.
|
||||||
|
q15_t dB6[] = {
|
||||||
|
-288,
|
||||||
|
-1676,
|
||||||
|
-3761,
|
||||||
|
-5642,
|
||||||
|
26370,
|
||||||
|
-5642,
|
||||||
|
-3761,
|
||||||
|
-1676,
|
||||||
|
-288
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = -5dB, 2200Hz = 0dB; 2085Hz cutoff; cosine.
|
||||||
|
q15_t dB5[] = {
|
||||||
|
-370,
|
||||||
|
-1735,
|
||||||
|
-3545,
|
||||||
|
-5088,
|
||||||
|
27075,
|
||||||
|
-5088,
|
||||||
|
-3545,
|
||||||
|
-1735,
|
||||||
|
-370
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = -4dB, 2200Hz = 0dB; 1790Hz cutoff; cosine.
|
||||||
|
q15_t dB4[] = {
|
||||||
|
-432,
|
||||||
|
-1715,
|
||||||
|
-3220,
|
||||||
|
-4427,
|
||||||
|
27880,
|
||||||
|
-4427,
|
||||||
|
-3220,
|
||||||
|
-1715,
|
||||||
|
-432
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = -3dB, 2200Hz = 0dB; 1456Hz cutoff; cosine.
|
||||||
|
q15_t dB3[] = {
|
||||||
|
-452,
|
||||||
|
-1582,
|
||||||
|
-2759,
|
||||||
|
-3646,
|
||||||
|
28792,
|
||||||
|
-3646,
|
||||||
|
-2759,
|
||||||
|
-1582,
|
||||||
|
-452
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = -2dB, 2200Hz = 0dB; 1070Hz cutoff; cosine.
|
||||||
|
q15_t dB2[] = {
|
||||||
|
-408,
|
||||||
|
-1295,
|
||||||
|
-2123,
|
||||||
|
-2710,
|
||||||
|
29846,
|
||||||
|
-2710,
|
||||||
|
-2123,
|
||||||
|
-1295,
|
||||||
|
-408
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = -1dB, 2200Hz = 0dB; 605Hz cutoff; cosine.
|
||||||
|
q15_t dB1[] = {
|
||||||
|
-268,
|
||||||
|
-795,
|
||||||
|
-1244,
|
||||||
|
-1546,
|
||||||
|
31116,
|
||||||
|
-1546,
|
||||||
|
-1244,
|
||||||
|
-795,
|
||||||
|
-268
|
||||||
|
};
|
||||||
|
|
||||||
|
q15_t dB0[] = {
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
32767,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = 0dB, 2200Hz = -1dB; 4130Hz cutoff; cosine.
|
||||||
|
q15_t dB_1[] = {
|
||||||
|
-419,
|
||||||
|
-177,
|
||||||
|
3316,
|
||||||
|
8650,
|
||||||
|
11278,
|
||||||
|
8650,
|
||||||
|
3316,
|
||||||
|
-177,
|
||||||
|
-419
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = 0dB, 2200Hz = -2dB; 3190Hz cutoff; cosine.
|
||||||
|
q15_t dB_2[] = {
|
||||||
|
-90,
|
||||||
|
1033,
|
||||||
|
3975,
|
||||||
|
7267,
|
||||||
|
8711,
|
||||||
|
7267,
|
||||||
|
3975,
|
||||||
|
1033,
|
||||||
|
-90
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = 0dB, 2200Hz = -3dB; 2330Hz cutoff; cosine.
|
||||||
|
q15_t dB_3[] = {
|
||||||
|
292,
|
||||||
|
1680,
|
||||||
|
3752,
|
||||||
|
5615,
|
||||||
|
6362,
|
||||||
|
5615,
|
||||||
|
3752,
|
||||||
|
1680,
|
||||||
|
292
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = 0dB, 2200Hz = -4dB; 2657Hz cutoff; boxcar.
|
||||||
|
q15_t dB_4[] = {
|
||||||
|
917,
|
||||||
|
3024,
|
||||||
|
5131,
|
||||||
|
6684,
|
||||||
|
7255,
|
||||||
|
6684,
|
||||||
|
5131,
|
||||||
|
3024,
|
||||||
|
917
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = 0dB, 2200Hz = -5dB; 2360Hz cutoff; boxcar.
|
||||||
|
q15_t dB_5[] = {
|
||||||
|
1620,
|
||||||
|
3339,
|
||||||
|
4925,
|
||||||
|
6042,
|
||||||
|
6444,
|
||||||
|
6042,
|
||||||
|
4925,
|
||||||
|
3339,
|
||||||
|
1620
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1200Hz = 0dB, 2200Hz = -6dB; 2067Hz cutoff; boxcar.
|
||||||
|
q15_t dB_6[] = {
|
||||||
|
2161,
|
||||||
|
3472,
|
||||||
|
4605,
|
||||||
|
5373,
|
||||||
|
5644,
|
||||||
|
5373,
|
||||||
|
4605,
|
||||||
|
3472,
|
||||||
|
2161
|
||||||
|
};
|
||||||
|
|
||||||
|
q15_t* coeffs[] = {
|
||||||
|
dB12,
|
||||||
|
dB11,
|
||||||
|
dB10,
|
||||||
|
dB9,
|
||||||
|
dB8,
|
||||||
|
dB7,
|
||||||
|
dB6,
|
||||||
|
dB5,
|
||||||
|
dB4,
|
||||||
|
dB3,
|
||||||
|
dB2,
|
||||||
|
dB1,
|
||||||
|
dB0,
|
||||||
|
dB_1,
|
||||||
|
dB_2,
|
||||||
|
dB_3,
|
||||||
|
dB_4,
|
||||||
|
dB_5,
|
||||||
|
dB_6
|
||||||
|
};
|
||||||
|
|
||||||
|
CAX25Twist::CAX25Twist(int8_t n) :
|
||||||
|
m_filter(),
|
||||||
|
m_state()
|
||||||
|
{
|
||||||
|
setTwist(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAX25Twist::process(q15_t* in, q15_t* out, uint8_t length)
|
||||||
|
{
|
||||||
|
::arm_fir_fast_q15(&m_filter, in, out, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAX25Twist::setTwist(int8_t n)
|
||||||
|
{
|
||||||
|
uint8_t twist = uint8_t(n + 6);
|
||||||
|
|
||||||
|
m_filter.numTaps = 9;
|
||||||
|
m_filter.pState = m_state;
|
||||||
|
m_filter.pCoeffs = coeffs[twist];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_AX25)
|
||||||
|
|
||||||
|
#if !defined(AX25Twist_H)
|
||||||
|
#define AX25Twist_H
|
||||||
|
|
||||||
|
class CAX25Twist {
|
||||||
|
public:
|
||||||
|
CAX25Twist(int8_t n);
|
||||||
|
|
||||||
|
void process(q15_t* in, q15_t* out, uint8_t length);
|
||||||
|
|
||||||
|
void setTwist(int8_t n);
|
||||||
|
|
||||||
|
private:
|
||||||
|
arm_fir_instance_q15 m_filter;
|
||||||
|
q15_t m_state[40U]; // NoTaps + BlockSize - 1, 9 + 20 - 1 plus some spare
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,509 @@
|
|||||||
|
/* ----------------------------------------------------------------------
|
||||||
|
* Project: CMSIS DSP Library
|
||||||
|
* Title: arm_biquad_cascade_df1_q31.c
|
||||||
|
* Description: Processing function for the Q31 Biquad cascade filter
|
||||||
|
*
|
||||||
|
* $Date: 23 April 2021
|
||||||
|
* $Revision: V1.9.0
|
||||||
|
*
|
||||||
|
* Target Processor: Cortex-M and Cortex-A cores
|
||||||
|
* -------------------------------------------------------------------- */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dsp/filtering_functions.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
@ingroup groupFilters
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@addtogroup BiquadCascadeDF1
|
||||||
|
@{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Processing function for the Q31 Biquad cascade filter.
|
||||||
|
@param[in] S points to an instance of the Q31 Biquad cascade structure
|
||||||
|
@param[in] pSrc points to the block of input data
|
||||||
|
@param[out] pDst points to the block of output data
|
||||||
|
@param[in] blockSize number of samples to process
|
||||||
|
|
||||||
|
@par Scaling and Overflow Behavior
|
||||||
|
The function is implemented using an internal 64-bit accumulator.
|
||||||
|
The accumulator has a 2.62 format and maintains full precision of the intermediate multiplication results but provides only a single guard bit.
|
||||||
|
Thus, if the accumulator result overflows it wraps around rather than clip.
|
||||||
|
In order to avoid overflows completely the input signal must be scaled down by 2 bits and lie in the range [-0.25 +0.25).
|
||||||
|
After all 5 multiply-accumulates are performed, the 2.62 accumulator is shifted by <code>postShift</code> bits and the result truncated to
|
||||||
|
1.31 format by discarding the low 32 bits.
|
||||||
|
@remark
|
||||||
|
Refer to \ref arm_biquad_cascade_df1_fast_q31() for a faster but less precise implementation of this filter.
|
||||||
|
*/
|
||||||
|
#if defined(ARM_MATH_MVEI) && !defined(ARM_MATH_AUTOVECTORIZE)
|
||||||
|
|
||||||
|
__STATIC_INLINE void arm_biquad_cascade_df1_q31(
|
||||||
|
const arm_biquad_casd_df1_inst_q31 * S,
|
||||||
|
const q31_t * pSrc,
|
||||||
|
q31_t * pDst,
|
||||||
|
uint32_t blockSize)
|
||||||
|
{
|
||||||
|
const q31_t *pIn = pSrc; /* input pointer initialization */
|
||||||
|
q31_t *pOut = pDst; /* output pointer initialization */
|
||||||
|
int shift;
|
||||||
|
uint32_t stages = S->numStages; /* loop counters */
|
||||||
|
int postShift = S->postShift;
|
||||||
|
q31x4_t b0Coeffs, b1Coeffs, a0Coeffs, a1Coeffs; /* Coefficients vector */
|
||||||
|
q31x4_t stateVec = { 0 };
|
||||||
|
q31_t *pState = S->pState; /* pState pointer initialization */
|
||||||
|
q31x4_t inVec0;
|
||||||
|
int64_t acc;
|
||||||
|
const q31_t *pCoeffs = S->pCoeffs; /* coeff pointer initialization */
|
||||||
|
q31_t out, out1;
|
||||||
|
|
||||||
|
|
||||||
|
shift = (postShift + 1 + 8);
|
||||||
|
|
||||||
|
do {
|
||||||
|
/*
|
||||||
|
* Reading the coefficients
|
||||||
|
* generates :
|
||||||
|
* Fwd0 { b2 b1 b0 0 }
|
||||||
|
* Fwd1 { 0 b2 b1 b0 }
|
||||||
|
* Bwd0 { 0 0 a2 a1 }
|
||||||
|
* Bwd0 { 0 0 a1 a2 }
|
||||||
|
* (can be moved in init)
|
||||||
|
*/
|
||||||
|
b0Coeffs = vdupq_n_s32(0);
|
||||||
|
a0Coeffs = vdupq_n_s32(0);
|
||||||
|
|
||||||
|
b0Coeffs[0] = pCoeffs[2]; // b2
|
||||||
|
b0Coeffs[1] = pCoeffs[1]; // b1
|
||||||
|
b0Coeffs[2] = pCoeffs[0]; // b0
|
||||||
|
|
||||||
|
b1Coeffs = b0Coeffs;
|
||||||
|
uint32_t zero = 0;
|
||||||
|
b1Coeffs = vshlcq_s32(b1Coeffs, &zero, 32);
|
||||||
|
|
||||||
|
a0Coeffs[2] = pCoeffs[4];
|
||||||
|
a0Coeffs[3] = pCoeffs[3];
|
||||||
|
a1Coeffs = vrev64q_s32(a0Coeffs);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* prologue consumes history samples
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 2 first elements are garbage, will be updated with history */
|
||||||
|
inVec0 = vld1q(pIn - 2);
|
||||||
|
|
||||||
|
inVec0[0] = pState[1];
|
||||||
|
inVec0[1] = pState[0];
|
||||||
|
|
||||||
|
stateVec[2] = pState[3];
|
||||||
|
stateVec[3] = pState[2];
|
||||||
|
|
||||||
|
acc = vrmlaldavhq(b0Coeffs, inVec0);
|
||||||
|
acc = vrmlaldavhaq(acc, a0Coeffs, stateVec);
|
||||||
|
acc = lsll(acc, shift);
|
||||||
|
out = (q31_t) ((acc >> 32) & 0xffffffff);
|
||||||
|
|
||||||
|
stateVec[2] = out;
|
||||||
|
acc = vrmlaldavhq(b1Coeffs, inVec0);
|
||||||
|
acc = vrmlaldavhaq(acc, a1Coeffs, stateVec);
|
||||||
|
|
||||||
|
acc = lsll(acc, shift);
|
||||||
|
out1 = (q31_t) ((acc >> 32) & 0xffffffff);
|
||||||
|
|
||||||
|
|
||||||
|
inVec0 = vld1q(pIn);
|
||||||
|
pIn += 2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* main loop
|
||||||
|
*/
|
||||||
|
uint32_t sample = (blockSize - 2) >> 2U;
|
||||||
|
/*
|
||||||
|
* First part of the processing with loop unrolling.
|
||||||
|
* Compute 4 outputs at a time.
|
||||||
|
*/
|
||||||
|
while (sample > 0U) {
|
||||||
|
|
||||||
|
stateVec[3] = out1;
|
||||||
|
|
||||||
|
*pOut++ = out;
|
||||||
|
*pOut++ = out1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* in { x0 x1 x2 x3 }
|
||||||
|
* x
|
||||||
|
* b0Coeffs { b2 b1 b0 0 }
|
||||||
|
*/
|
||||||
|
acc = vrmlaldavhq(b0Coeffs, inVec0);
|
||||||
|
/*
|
||||||
|
* out { 0 0 yn2 yn1 }
|
||||||
|
* x
|
||||||
|
* a0Coeffs { 0 0 a2 a1 }
|
||||||
|
*/
|
||||||
|
acc = vrmlaldavhaq(acc, a0Coeffs, stateVec);
|
||||||
|
acc = lsll(acc, shift);
|
||||||
|
out = (q31_t) ((acc >> 32) & 0xffffffff);
|
||||||
|
|
||||||
|
stateVec[2] = out;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* in { x0 x1 x2 x3 }
|
||||||
|
* x
|
||||||
|
* b0Coeffs { 0 b2 b1 b0 }
|
||||||
|
*/
|
||||||
|
acc = vrmlaldavhq(b1Coeffs, inVec0);
|
||||||
|
/*
|
||||||
|
* out { 0 0 y0 yn1 }
|
||||||
|
* x
|
||||||
|
* a0Coeffs { 0 0 a1 a2 }
|
||||||
|
*/
|
||||||
|
acc = vrmlaldavhaq(acc, a1Coeffs, stateVec);
|
||||||
|
acc = lsll(acc, shift);
|
||||||
|
out1 = (q31_t) ((acc >> 32) & 0xffffffff);
|
||||||
|
|
||||||
|
stateVec[3] = out1;
|
||||||
|
|
||||||
|
inVec0 = vld1q(pIn);
|
||||||
|
pIn += 2;
|
||||||
|
|
||||||
|
/* unrolled part */
|
||||||
|
*pOut++ = out;
|
||||||
|
*pOut++ = out1;
|
||||||
|
|
||||||
|
acc = vrmlaldavhq(b0Coeffs, inVec0);
|
||||||
|
acc = vrmlaldavhaq(acc, a0Coeffs, stateVec);
|
||||||
|
acc = lsll(acc, shift);
|
||||||
|
out = (q31_t) ((acc >> 32) & 0xffffffff);
|
||||||
|
|
||||||
|
stateVec[2] = out;
|
||||||
|
|
||||||
|
acc = vrmlaldavhq(b1Coeffs, inVec0);
|
||||||
|
acc = vrmlaldavhaq(acc, a1Coeffs, stateVec);
|
||||||
|
acc = lsll(acc, shift);
|
||||||
|
out1 = (q31_t) ((acc >> 32) & 0xffffffff);
|
||||||
|
|
||||||
|
inVec0 = vld1q(pIn);
|
||||||
|
pIn += 2;
|
||||||
|
|
||||||
|
sample--;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pOut++ = out;
|
||||||
|
*pOut++ = out1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tail handling
|
||||||
|
*/
|
||||||
|
int32_t loopRemainder = blockSize & 3;
|
||||||
|
if (loopRemainder == 2) {
|
||||||
|
/*
|
||||||
|
* Store the updated state variables back into the pState array
|
||||||
|
*/
|
||||||
|
pState[0] = inVec0[1];
|
||||||
|
pState[1] = inVec0[0];
|
||||||
|
pState[3] = out;
|
||||||
|
pState[2] = out1;
|
||||||
|
} else if (loopRemainder == 1) {
|
||||||
|
stateVec[3] = out1;
|
||||||
|
|
||||||
|
acc = vrmlaldavhq(b0Coeffs, inVec0);
|
||||||
|
acc = vrmlaldavhaq(acc, a0Coeffs, stateVec);
|
||||||
|
acc = lsll(acc, shift);
|
||||||
|
out = (q31_t) ((acc >> 32) & 0xffffffff);
|
||||||
|
|
||||||
|
stateVec[2] = out;
|
||||||
|
|
||||||
|
acc = vrmlaldavhq(b1Coeffs, inVec0);
|
||||||
|
acc = vrmlaldavhaq(acc, a1Coeffs, stateVec);
|
||||||
|
acc = lsll(acc, shift);
|
||||||
|
out1 = (q31_t) ((acc >> 32) & 0xffffffff);
|
||||||
|
|
||||||
|
stateVec[3] = out1;
|
||||||
|
|
||||||
|
inVec0 = vld1q(pIn);
|
||||||
|
pIn += 2;
|
||||||
|
|
||||||
|
*pOut++ = out;
|
||||||
|
*pOut++ = out1;
|
||||||
|
|
||||||
|
acc = vrmlaldavhq(b0Coeffs, inVec0);
|
||||||
|
acc = vrmlaldavhaq(acc, a0Coeffs, stateVec);
|
||||||
|
acc = lsll(acc, shift);
|
||||||
|
out = (q31_t) ((acc >> 32) & 0xffffffff);
|
||||||
|
|
||||||
|
*pOut++ = out;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store the updated state variables back into the pState array
|
||||||
|
*/
|
||||||
|
pState[0] = inVec0[2];
|
||||||
|
pState[1] = inVec0[1];
|
||||||
|
pState[3] = out1;
|
||||||
|
pState[2] = out;
|
||||||
|
} else if (loopRemainder == 0) {
|
||||||
|
stateVec[3] = out1;
|
||||||
|
|
||||||
|
acc = vrmlaldavhq(b0Coeffs, inVec0);
|
||||||
|
acc = vrmlaldavhaq(acc, a0Coeffs, stateVec);
|
||||||
|
acc = lsll(acc, shift);
|
||||||
|
out = (q31_t) ((acc >> 32) & 0xffffffff);
|
||||||
|
|
||||||
|
stateVec[2] = out;
|
||||||
|
|
||||||
|
acc = vrmlaldavhq(b1Coeffs, inVec0);
|
||||||
|
acc = vrmlaldavhaq(acc, a1Coeffs, stateVec);
|
||||||
|
acc = lsll(acc, shift);
|
||||||
|
out1 = (q31_t) ((acc >> 32) & 0xffffffff);
|
||||||
|
|
||||||
|
*pOut++ = out;
|
||||||
|
*pOut++ = out1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store the updated state variables back into the pState array
|
||||||
|
*/
|
||||||
|
pState[0] = inVec0[3];
|
||||||
|
pState[1] = inVec0[2];
|
||||||
|
pState[3] = out;
|
||||||
|
pState[2] = out1;
|
||||||
|
} else {
|
||||||
|
stateVec[3] = out1;
|
||||||
|
|
||||||
|
acc = vrmlaldavhq(b0Coeffs, inVec0);
|
||||||
|
acc = vrmlaldavhaq(acc, a0Coeffs, stateVec);
|
||||||
|
acc = lsll(acc, shift);
|
||||||
|
out = (q31_t) ((acc >> 32) & 0xffffffff);
|
||||||
|
|
||||||
|
*pOut++ = out;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store the updated state variables back into the pState array
|
||||||
|
*/
|
||||||
|
pState[0] = inVec0[2];
|
||||||
|
pState[1] = inVec0[1];
|
||||||
|
pState[3] = out1;
|
||||||
|
pState[2] = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pCoeffs += 5;
|
||||||
|
pState += 4;
|
||||||
|
|
||||||
|
/* The first stage goes from the input buffer to the output buffer. */
|
||||||
|
/* Subsequent stages occur in-place in the output buffer */
|
||||||
|
pIn = pDst;
|
||||||
|
|
||||||
|
/* Reset to destination pointer */
|
||||||
|
pOut = pDst;
|
||||||
|
}
|
||||||
|
while (--stages);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void arm_biquad_cascade_df1_q31(
|
||||||
|
const arm_biquad_casd_df1_inst_q31 * S,
|
||||||
|
const q31_t * pSrc,
|
||||||
|
q31_t * pDst,
|
||||||
|
uint32_t blockSize)
|
||||||
|
{
|
||||||
|
const q31_t *pIn = pSrc; /* Source pointer */
|
||||||
|
q31_t *pOut = pDst; /* Destination pointer */
|
||||||
|
q31_t *pState = S->pState; /* pState pointer */
|
||||||
|
const q31_t *pCoeffs = S->pCoeffs; /* Coefficient pointer */
|
||||||
|
q63_t acc; /* Accumulator */
|
||||||
|
q31_t b0, b1, b2, a1, a2; /* Filter coefficients */
|
||||||
|
q31_t Xn1, Xn2, Yn1, Yn2; /* Filter pState variables */
|
||||||
|
q31_t Xn; /* Temporary input */
|
||||||
|
uint32_t uShift = ((uint32_t) S->postShift + 1U);
|
||||||
|
uint32_t lShift = 32U - uShift; /* Shift to be applied to the output */
|
||||||
|
uint32_t sample, stage = S->numStages; /* Loop counters */
|
||||||
|
|
||||||
|
#if defined (ARM_MATH_LOOPUNROLL)
|
||||||
|
q31_t acc_l, acc_h; /* temporary output variables */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
/* Reading the coefficients */
|
||||||
|
b0 = *pCoeffs++;
|
||||||
|
b1 = *pCoeffs++;
|
||||||
|
b2 = *pCoeffs++;
|
||||||
|
a1 = *pCoeffs++;
|
||||||
|
a2 = *pCoeffs++;
|
||||||
|
|
||||||
|
/* Reading the pState values */
|
||||||
|
Xn1 = pState[0];
|
||||||
|
Xn2 = pState[1];
|
||||||
|
Yn1 = pState[2];
|
||||||
|
Yn2 = pState[3];
|
||||||
|
|
||||||
|
#if defined (ARM_MATH_LOOPUNROLL)
|
||||||
|
|
||||||
|
/* Apply loop unrolling and compute 4 output values simultaneously. */
|
||||||
|
/* Variable acc hold output values that are being computed:
|
||||||
|
*
|
||||||
|
* acc = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2]
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute 4 outputs at a time */
|
||||||
|
sample = blockSize >> 2U;
|
||||||
|
|
||||||
|
while (sample > 0U)
|
||||||
|
{
|
||||||
|
/* Read the first input */
|
||||||
|
Xn = *pIn++;
|
||||||
|
|
||||||
|
/* acc = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2] */
|
||||||
|
acc = ((q63_t) b0 * Xn) + ((q63_t) b1 * Xn1) + ((q63_t) b2 * Xn2) + ((q63_t) a1 * Yn1) + ((q63_t) a2 * Yn2);
|
||||||
|
|
||||||
|
/* The result is converted to 1.31 , Yn2 variable is reused */
|
||||||
|
acc_l = (acc ) & 0xffffffff; /* Calc lower part of acc */
|
||||||
|
acc_h = (acc >> 32) & 0xffffffff; /* Calc upper part of acc */
|
||||||
|
|
||||||
|
/* Apply shift for lower part of acc and upper part of acc */
|
||||||
|
Yn2 = (uint32_t) acc_l >> lShift | acc_h << uShift;
|
||||||
|
|
||||||
|
/* Store output in destination buffer. */
|
||||||
|
*pOut++ = Yn2;
|
||||||
|
|
||||||
|
/* Read the second input */
|
||||||
|
Xn2 = *pIn++;
|
||||||
|
|
||||||
|
/* acc = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2] */
|
||||||
|
acc = ((q63_t) b0 * Xn2) + ((q63_t) b1 * Xn) + ((q63_t) b2 * Xn1) + ((q63_t) a1 * Yn2) + ((q63_t) a2 * Yn1);
|
||||||
|
|
||||||
|
/* The result is converted to 1.31, Yn1 variable is reused */
|
||||||
|
acc_l = (acc ) & 0xffffffff; /* Calc lower part of acc */
|
||||||
|
acc_h = (acc >> 32) & 0xffffffff; /* Calc upper part of acc */
|
||||||
|
|
||||||
|
/* Apply shift for lower part of acc and upper part of acc */
|
||||||
|
Yn1 = (uint32_t) acc_l >> lShift | acc_h << uShift;
|
||||||
|
|
||||||
|
/* Store output in destination buffer. */
|
||||||
|
*pOut++ = Yn1;
|
||||||
|
|
||||||
|
/* Read the third input */
|
||||||
|
Xn1 = *pIn++;
|
||||||
|
|
||||||
|
/* acc = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2] */
|
||||||
|
acc = ((q63_t) b0 * Xn1) + ((q63_t) b1 * Xn2) + ((q63_t) b2 * Xn) + ((q63_t) a1 * Yn1) + ((q63_t) a2 * Yn2);
|
||||||
|
|
||||||
|
/* The result is converted to 1.31, Yn2 variable is reused */
|
||||||
|
acc_l = (acc ) & 0xffffffff; /* Calc lower part of acc */
|
||||||
|
acc_h = (acc >> 32) & 0xffffffff; /* Calc upper part of acc */
|
||||||
|
|
||||||
|
/* Apply shift for lower part of acc and upper part of acc */
|
||||||
|
Yn2 = (uint32_t) acc_l >> lShift | acc_h << uShift;
|
||||||
|
|
||||||
|
/* Store output in destination buffer. */
|
||||||
|
*pOut++ = Yn2;
|
||||||
|
|
||||||
|
/* Read the forth input */
|
||||||
|
Xn = *pIn++;
|
||||||
|
|
||||||
|
/* acc = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2] */
|
||||||
|
acc = ((q63_t) b0 * Xn) + ((q63_t) b1 * Xn1) + ((q63_t) b2 * Xn2) + ((q63_t) a1 * Yn2) + ((q63_t) a2 * Yn1);
|
||||||
|
|
||||||
|
/* The result is converted to 1.31, Yn1 variable is reused */
|
||||||
|
acc_l = (acc ) & 0xffffffff; /* Calc lower part of acc */
|
||||||
|
acc_h = (acc >> 32) & 0xffffffff; /* Calc upper part of acc */
|
||||||
|
|
||||||
|
/* Apply shift for lower part of acc and upper part of acc */
|
||||||
|
Yn1 = (uint32_t) acc_l >> lShift | acc_h << uShift;
|
||||||
|
|
||||||
|
/* Store output in destination buffer. */
|
||||||
|
*pOut++ = Yn1;
|
||||||
|
|
||||||
|
/* Every time after the output is computed state should be updated. */
|
||||||
|
/* The states should be updated as: */
|
||||||
|
/* Xn2 = Xn1 */
|
||||||
|
/* Xn1 = Xn */
|
||||||
|
/* Yn2 = Yn1 */
|
||||||
|
/* Yn1 = acc */
|
||||||
|
Xn2 = Xn1;
|
||||||
|
Xn1 = Xn;
|
||||||
|
|
||||||
|
/* decrement loop counter */
|
||||||
|
sample--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute remaining outputs */
|
||||||
|
sample = blockSize & 0x3U;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Initialize blkCnt with number of samples */
|
||||||
|
sample = blockSize;
|
||||||
|
|
||||||
|
#endif /* #if defined (ARM_MATH_LOOPUNROLL) */
|
||||||
|
|
||||||
|
while (sample > 0U)
|
||||||
|
{
|
||||||
|
/* Read the input */
|
||||||
|
Xn = *pIn++;
|
||||||
|
|
||||||
|
/* acc = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + a1 * y[n-1] + a2 * y[n-2] */
|
||||||
|
acc = ((q63_t) b0 * Xn) + ((q63_t) b1 * Xn1) + ((q63_t) b2 * Xn2) + ((q63_t) a1 * Yn1) + ((q63_t) a2 * Yn2);
|
||||||
|
|
||||||
|
/* The result is converted to 1.31 */
|
||||||
|
acc = acc >> lShift;
|
||||||
|
|
||||||
|
/* Store output in destination buffer. */
|
||||||
|
*pOut++ = (q31_t) acc;
|
||||||
|
|
||||||
|
/* Every time after the output is computed state should be updated. */
|
||||||
|
/* The states should be updated as: */
|
||||||
|
/* Xn2 = Xn1 */
|
||||||
|
/* Xn1 = Xn */
|
||||||
|
/* Yn2 = Yn1 */
|
||||||
|
/* Yn1 = acc */
|
||||||
|
Xn2 = Xn1;
|
||||||
|
Xn1 = Xn;
|
||||||
|
Yn2 = Yn1;
|
||||||
|
Yn1 = (q31_t) acc;
|
||||||
|
|
||||||
|
/* decrement loop counter */
|
||||||
|
sample--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store the updated state variables back into the pState array */
|
||||||
|
*pState++ = Xn1;
|
||||||
|
*pState++ = Xn2;
|
||||||
|
*pState++ = Yn1;
|
||||||
|
*pState++ = Yn2;
|
||||||
|
|
||||||
|
/* The first stage goes from the input buffer to the output buffer. */
|
||||||
|
/* Subsequent numStages occur in-place in the output buffer */
|
||||||
|
pIn = pDst;
|
||||||
|
|
||||||
|
/* Reset output pointer */
|
||||||
|
pOut = pDst;
|
||||||
|
|
||||||
|
/* decrement loop counter */
|
||||||
|
stage--;
|
||||||
|
|
||||||
|
} while (stage > 0U);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif /* defined(ARM_MATH_MVEI) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
@} end of BiquadCascadeDF1 group
|
||||||
|
*/
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,332 @@
|
|||||||
|
/* ----------------------------------------------------------------------
|
||||||
|
* Project: CMSIS DSP Library
|
||||||
|
* Title: arm_fir_fast_q15.c
|
||||||
|
* Description: Q15 Fast FIR filter processing function
|
||||||
|
*
|
||||||
|
* $Date: 23 April 2021
|
||||||
|
* $Revision: V1.9.0
|
||||||
|
*
|
||||||
|
* Target Processor: Cortex-M and Cortex-A cores
|
||||||
|
* -------------------------------------------------------------------- */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dsp/filtering_functions.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
@ingroup groupFilters
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@addtogroup FIR
|
||||||
|
@{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Processing function for the Q15 FIR filter (fast version).
|
||||||
|
@param[in] S points to an instance of the Q15 FIR filter structure
|
||||||
|
@param[in] pSrc points to the block of input data
|
||||||
|
@param[out] pDst points to the block of output data
|
||||||
|
@param[in] blockSize number of samples to process
|
||||||
|
@return none
|
||||||
|
|
||||||
|
@par Scaling and Overflow Behavior
|
||||||
|
This fast version uses a 32-bit accumulator with 2.30 format.
|
||||||
|
The accumulator maintains full precision of the intermediate multiplication results but provides only a single guard bit.
|
||||||
|
Thus, if the accumulator result overflows it wraps around and distorts the result.
|
||||||
|
In order to avoid overflows completely the input signal must be scaled down by log2(numTaps) bits.
|
||||||
|
The 2.30 accumulator is then truncated to 2.15 format and saturated to yield the 1.15 result.
|
||||||
|
|
||||||
|
@remark
|
||||||
|
Refer to \ref arm_fir_q15() for a slower implementation of this function which uses 64-bit accumulation to avoid wrap around distortion. Both the slow and the fast versions use the same instance structure.
|
||||||
|
Use function \ref arm_fir_init_q15() to initialize the filter structure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void arm_fir_fast_q15(
|
||||||
|
const arm_fir_instance_q15 * S,
|
||||||
|
const q15_t * pSrc,
|
||||||
|
q15_t * pDst,
|
||||||
|
uint32_t blockSize)
|
||||||
|
{
|
||||||
|
q15_t *pState = S->pState; /* State pointer */
|
||||||
|
const q15_t *pCoeffs = S->pCoeffs; /* Coefficient pointer */
|
||||||
|
q15_t *pStateCurnt; /* Points to the current sample of the state */
|
||||||
|
q15_t *px; /* Temporary pointer for state buffer */
|
||||||
|
const q15_t *pb; /* Temporary pointer for coefficient buffer */
|
||||||
|
q31_t acc0; /* Accumulators */
|
||||||
|
uint32_t numTaps = S->numTaps; /* Number of filter coefficients in the filter */
|
||||||
|
uint32_t tapCnt, blkCnt; /* Loop counters */
|
||||||
|
|
||||||
|
#if defined (ARM_MATH_LOOPUNROLL)
|
||||||
|
q31_t acc1, acc2, acc3; /* Accumulators */
|
||||||
|
q31_t x0, x1, x2, c0; /* Temporary variables to hold state and coefficient values */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* S->pState points to state array which contains previous frame (numTaps - 1) samples */
|
||||||
|
/* pStateCurnt points to the location where the new input data should be written */
|
||||||
|
pStateCurnt = &(S->pState[(numTaps - 1U)]);
|
||||||
|
|
||||||
|
#if defined (ARM_MATH_LOOPUNROLL)
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute 4 output values simultaneously.
|
||||||
|
* The variables acc0 ... acc3 hold output values that are being computed:
|
||||||
|
*
|
||||||
|
* acc0 = b[numTaps-1] * x[n-numTaps-1] + b[numTaps-2] * x[n-numTaps-2] + b[numTaps-3] * x[n-numTaps-3] +...+ b[0] * x[0]
|
||||||
|
* acc1 = b[numTaps-1] * x[n-numTaps] + b[numTaps-2] * x[n-numTaps-1] + b[numTaps-3] * x[n-numTaps-2] +...+ b[0] * x[1]
|
||||||
|
* acc2 = b[numTaps-1] * x[n-numTaps+1] + b[numTaps-2] * x[n-numTaps] + b[numTaps-3] * x[n-numTaps-1] +...+ b[0] * x[2]
|
||||||
|
* acc3 = b[numTaps-1] * x[n-numTaps+2] + b[numTaps-2] * x[n-numTaps+1] + b[numTaps-3] * x[n-numTaps] +...+ b[0] * x[3]
|
||||||
|
*/
|
||||||
|
blkCnt = blockSize >> 2U;
|
||||||
|
|
||||||
|
while (blkCnt > 0U)
|
||||||
|
{
|
||||||
|
/* Copy 4 new input samples into the state buffer. */
|
||||||
|
*pStateCurnt++ = *pSrc++;
|
||||||
|
*pStateCurnt++ = *pSrc++;
|
||||||
|
*pStateCurnt++ = *pSrc++;
|
||||||
|
*pStateCurnt++ = *pSrc++;
|
||||||
|
|
||||||
|
/* Set all accumulators to zero */
|
||||||
|
acc0 = 0;
|
||||||
|
acc1 = 0;
|
||||||
|
acc2 = 0;
|
||||||
|
acc3 = 0;
|
||||||
|
|
||||||
|
/* Typecast q15_t pointer to q31_t pointer for state reading in q31_t */
|
||||||
|
px = pState;
|
||||||
|
|
||||||
|
/* Typecast q15_t pointer to q31_t pointer for coefficient reading in q31_t */
|
||||||
|
pb = pCoeffs;
|
||||||
|
|
||||||
|
/* Read the first two samples from the state buffer: x[n-N], x[n-N-1] */
|
||||||
|
x0 = read_q15x2_ia (&px);
|
||||||
|
|
||||||
|
/* Read the third and forth samples from the state buffer: x[n-N-2], x[n-N-3] */
|
||||||
|
x2 = read_q15x2_ia (&px);
|
||||||
|
|
||||||
|
/* Loop over the number of taps. Unroll by a factor of 4.
|
||||||
|
Repeat until we've computed numTaps-(numTaps%4) coefficients. */
|
||||||
|
tapCnt = numTaps >> 2U;
|
||||||
|
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
/* Read the first two coefficients using SIMD: b[N] and b[N-1] coefficients */
|
||||||
|
c0 = read_q15x2_ia ((q15_t **) &pb);
|
||||||
|
|
||||||
|
/* acc0 += b[N] * x[n-N] + b[N-1] * x[n-N-1] */
|
||||||
|
acc0 = __SMLAD(x0, c0, acc0);
|
||||||
|
|
||||||
|
/* acc2 += b[N] * x[n-N-2] + b[N-1] * x[n-N-3] */
|
||||||
|
acc2 = __SMLAD(x2, c0, acc2);
|
||||||
|
|
||||||
|
/* pack x[n-N-1] and x[n-N-2] */
|
||||||
|
#ifndef ARM_MATH_BIG_ENDIAN
|
||||||
|
x1 = __PKHBT(x2, x0, 0);
|
||||||
|
#else
|
||||||
|
x1 = __PKHBT(x0, x2, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Read state x[n-N-4], x[n-N-5] */
|
||||||
|
x0 = read_q15x2_ia (&px);
|
||||||
|
|
||||||
|
/* acc1 += b[N] * x[n-N-1] + b[N-1] * x[n-N-2] */
|
||||||
|
acc1 = __SMLADX(x1, c0, acc1);
|
||||||
|
|
||||||
|
/* pack x[n-N-3] and x[n-N-4] */
|
||||||
|
#ifndef ARM_MATH_BIG_ENDIAN
|
||||||
|
x1 = __PKHBT(x0, x2, 0);
|
||||||
|
#else
|
||||||
|
x1 = __PKHBT(x2, x0, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* acc3 += b[N] * x[n-N-3] + b[N-1] * x[n-N-4] */
|
||||||
|
acc3 = __SMLADX(x1, c0, acc3);
|
||||||
|
|
||||||
|
/* Read coefficients b[N-2], b[N-3] */
|
||||||
|
c0 = read_q15x2_ia ((q15_t **) &pb);
|
||||||
|
|
||||||
|
/* acc0 += b[N-2] * x[n-N-2] + b[N-3] * x[n-N-3] */
|
||||||
|
acc0 = __SMLAD(x2, c0, acc0);
|
||||||
|
|
||||||
|
/* Read state x[n-N-6], x[n-N-7] with offset */
|
||||||
|
x2 = read_q15x2_ia (&px);
|
||||||
|
|
||||||
|
/* acc2 += b[N-2] * x[n-N-4] + b[N-3] * x[n-N-5] */
|
||||||
|
acc2 = __SMLAD(x0, c0, acc2);
|
||||||
|
|
||||||
|
/* acc1 += b[N-2] * x[n-N-3] + b[N-3] * x[n-N-4] */
|
||||||
|
acc1 = __SMLADX(x1, c0, acc1);
|
||||||
|
|
||||||
|
/* pack x[n-N-5] and x[n-N-6] */
|
||||||
|
#ifndef ARM_MATH_BIG_ENDIAN
|
||||||
|
x1 = __PKHBT(x2, x0, 0);
|
||||||
|
#else
|
||||||
|
x1 = __PKHBT(x0, x2, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* acc3 += b[N-2] * x[n-N-5] + b[N-3] * x[n-N-6] */
|
||||||
|
acc3 = __SMLADX(x1, c0, acc3);
|
||||||
|
|
||||||
|
/* Decrement tap count */
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the filter length is not a multiple of 4, compute the remaining filter taps.
|
||||||
|
This is always be 2 taps since the filter length is even. */
|
||||||
|
if ((numTaps & 0x3U) != 0U)
|
||||||
|
{
|
||||||
|
/* Read last two coefficients */
|
||||||
|
c0 = read_q15x2_ia ((q15_t **) &pb);
|
||||||
|
|
||||||
|
/* Perform the multiply-accumulates */
|
||||||
|
acc0 = __SMLAD(x0, c0, acc0);
|
||||||
|
acc2 = __SMLAD(x2, c0, acc2);
|
||||||
|
|
||||||
|
/* pack state variables */
|
||||||
|
#ifndef ARM_MATH_BIG_ENDIAN
|
||||||
|
x1 = __PKHBT(x2, x0, 0);
|
||||||
|
#else
|
||||||
|
x1 = __PKHBT(x0, x2, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Read last state variables */
|
||||||
|
x0 = read_q15x2 (px);
|
||||||
|
|
||||||
|
/* Perform the multiply-accumulates */
|
||||||
|
acc1 = __SMLADX(x1, c0, acc1);
|
||||||
|
|
||||||
|
/* pack state variables */
|
||||||
|
#ifndef ARM_MATH_BIG_ENDIAN
|
||||||
|
x1 = __PKHBT(x0, x2, 0);
|
||||||
|
#else
|
||||||
|
x1 = __PKHBT(x2, x0, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Perform the multiply-accumulates */
|
||||||
|
acc3 = __SMLADX(x1, c0, acc3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The results in the 4 accumulators are in 2.30 format. Convert to 1.15 with saturation.
|
||||||
|
Then store the 4 outputs in the destination buffer. */
|
||||||
|
#ifndef ARM_MATH_BIG_ENDIAN
|
||||||
|
write_q15x2_ia (&pDst, __PKHBT(__SSAT((acc0 >> 15), 16), __SSAT((acc1 >> 15), 16), 16));
|
||||||
|
write_q15x2_ia (&pDst, __PKHBT(__SSAT((acc2 >> 15), 16), __SSAT((acc3 >> 15), 16), 16));
|
||||||
|
#else
|
||||||
|
write_q15x2_ia (&pDst, __PKHBT(__SSAT((acc1 >> 15), 16), __SSAT((acc0 >> 15), 16), 16));
|
||||||
|
write_q15x2_ia (&pDst, __PKHBT(__SSAT((acc3 >> 15), 16), __SSAT((acc2 >> 15), 16), 16));
|
||||||
|
#endif /* #ifndef ARM_MATH_BIG_ENDIAN */
|
||||||
|
|
||||||
|
/* Advance the state pointer by 4 to process the next group of 4 samples */
|
||||||
|
pState = pState + 4U;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
blkCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute remaining output samples */
|
||||||
|
blkCnt = blockSize % 0x4U;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Initialize blkCnt with number of taps */
|
||||||
|
blkCnt = blockSize;
|
||||||
|
|
||||||
|
#endif /* #if defined (ARM_MATH_LOOPUNROLL) */
|
||||||
|
|
||||||
|
while (blkCnt > 0U)
|
||||||
|
{
|
||||||
|
/* Copy two samples into state buffer */
|
||||||
|
*pStateCurnt++ = *pSrc++;
|
||||||
|
|
||||||
|
/* Set the accumulator to zero */
|
||||||
|
acc0 = 0;
|
||||||
|
|
||||||
|
/* Use SIMD to hold states and coefficients */
|
||||||
|
px = pState;
|
||||||
|
pb = pCoeffs;
|
||||||
|
|
||||||
|
tapCnt = numTaps >> 1U;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
acc0 += (q31_t) *px++ * *pb++;
|
||||||
|
acc0 += (q31_t) *px++ * *pb++;
|
||||||
|
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
while (tapCnt > 0U);
|
||||||
|
|
||||||
|
/* The result is in 2.30 format. Convert to 1.15 with saturation.
|
||||||
|
Then store the output in the destination buffer. */
|
||||||
|
*pDst++ = (q15_t) (__SSAT((acc0 >> 15), 16));
|
||||||
|
|
||||||
|
/* Advance state pointer by 1 for the next sample */
|
||||||
|
pState = pState + 1U;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
blkCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Processing is complete.
|
||||||
|
Now copy the last numTaps - 1 samples to the start of the state buffer.
|
||||||
|
This prepares the state buffer for the next function call. */
|
||||||
|
|
||||||
|
/* Points to the start of the state buffer */
|
||||||
|
pStateCurnt = S->pState;
|
||||||
|
|
||||||
|
#if defined (ARM_MATH_LOOPUNROLL)
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute 4 taps at a time */
|
||||||
|
tapCnt = (numTaps - 1U) >> 2U;
|
||||||
|
|
||||||
|
/* Copy data */
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
*pStateCurnt++ = *pState++;
|
||||||
|
*pStateCurnt++ = *pState++;
|
||||||
|
*pStateCurnt++ = *pState++;
|
||||||
|
*pStateCurnt++ = *pState++;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate remaining number of copies */
|
||||||
|
tapCnt = (numTaps - 1U) % 0x4U;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Initialize tapCnt with number of taps */
|
||||||
|
tapCnt = (numTaps - 1U);
|
||||||
|
|
||||||
|
#endif /* #if defined (ARM_MATH_LOOPUNROLL) */
|
||||||
|
|
||||||
|
/* Copy remaining data */
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
*pStateCurnt++ = *pState++;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@} end of FIR group
|
||||||
|
*/
|
||||||
@@ -0,0 +1,774 @@
|
|||||||
|
/* ----------------------------------------------------------------------
|
||||||
|
* Project: CMSIS DSP Library
|
||||||
|
* Title: arm_fir_interpolate_q15.c
|
||||||
|
* Description: Q15 FIR interpolation
|
||||||
|
*
|
||||||
|
* $Date: 23 April 2021
|
||||||
|
* $Revision: V1.9.0
|
||||||
|
*
|
||||||
|
* Target Processor: Cortex-M and Cortex-A cores
|
||||||
|
* -------------------------------------------------------------------- */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dsp/filtering_functions.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
@ingroup groupFilters
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@addtogroup FIR_Interpolate
|
||||||
|
@{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Processing function for the Q15 FIR interpolator.
|
||||||
|
@param[in] S points to an instance of the Q15 FIR interpolator structure
|
||||||
|
@param[in] pSrc points to the block of input data
|
||||||
|
@param[out] pDst points to the block of output data
|
||||||
|
@param[in] blockSize number of samples to process
|
||||||
|
@return none
|
||||||
|
|
||||||
|
@par Scaling and Overflow Behavior
|
||||||
|
The function is implemented using a 64-bit internal accumulator.
|
||||||
|
Both coefficients and state variables are represented in 1.15 format and multiplications yield a 2.30 result.
|
||||||
|
The 2.30 intermediate results are accumulated in a 64-bit accumulator in 34.30 format.
|
||||||
|
There is no risk of internal overflow with this approach and the full precision of intermediate multiplications is preserved.
|
||||||
|
After all additions have been performed, the accumulator is truncated to 34.15 format by discarding low 15 bits.
|
||||||
|
Lastly, the accumulator is saturated to yield a result in 1.15 format.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(ARM_MATH_MVEI) && !defined(ARM_MATH_AUTOVECTORIZE)
|
||||||
|
|
||||||
|
#include "arm_helium_utils.h"
|
||||||
|
__STATIC_INLINE void arm_fir_interpolate_q15(
|
||||||
|
const arm_fir_interpolate_instance_q15 * S,
|
||||||
|
const q15_t * pSrc,
|
||||||
|
q15_t * pDst,
|
||||||
|
uint32_t blockSize)
|
||||||
|
{
|
||||||
|
q15_t *pState = S->pState; /* State pointer */
|
||||||
|
const q15_t *pCoeffs = S->pCoeffs; /* Coefficient pointer */
|
||||||
|
q15_t *pStateCurnt; /* Points to the current sample of the state */
|
||||||
|
const q15_t *ptr1, *ptr2; /* Temporary pointers for state and coefficient buffers */
|
||||||
|
|
||||||
|
uint32_t i, blkCnt; /* Loop counters */
|
||||||
|
uint16_t phaseLen = S->phaseLength; /* Length of each polyphase filter component */
|
||||||
|
uint16_t strides[8] = {
|
||||||
|
0, 1 * S->L, 2 * S->L, 3 * S->L,
|
||||||
|
4 * S->L, 5 * S->L, 6 * S->L, 7 * S->L
|
||||||
|
};
|
||||||
|
uint16x8_t vec_strides0 = *(uint16x8_t *) strides;
|
||||||
|
uint16x8_t vec_strides1 = vec_strides0 + 1;
|
||||||
|
uint16x8_t vec_strides2 = vec_strides0 + 2;
|
||||||
|
uint16x8_t vec_strides3 = vec_strides0 + 3;
|
||||||
|
q15x8_t vecState, vecCoef;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* S->pState buffer contains previous frame (phaseLen - 1) samples
|
||||||
|
* pStateCurnt points to the location where the new input data should be written
|
||||||
|
*/
|
||||||
|
pStateCurnt = S->pState + ((q15_t) phaseLen - 1);
|
||||||
|
/*
|
||||||
|
* Total number of intput samples
|
||||||
|
*/
|
||||||
|
blkCnt = blockSize;
|
||||||
|
/*
|
||||||
|
* Loop over the blockSize.
|
||||||
|
*/
|
||||||
|
while (blkCnt > 0U)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Copy new input sample into the state buffer
|
||||||
|
*/
|
||||||
|
*pStateCurnt++ = *pSrc++;
|
||||||
|
/*
|
||||||
|
* Loop over the Interpolation factor.
|
||||||
|
*/
|
||||||
|
i = S->L;
|
||||||
|
while (i > 0U)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Initialize state pointer
|
||||||
|
*/
|
||||||
|
ptr1 = pState;
|
||||||
|
if (i >= 4)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Initialize coefficient pointer
|
||||||
|
*/
|
||||||
|
ptr2 = pCoeffs + (i - 1 - 3U);
|
||||||
|
|
||||||
|
q63_t acc0 = 0LL;
|
||||||
|
q63_t acc1 = 0LL;
|
||||||
|
q63_t acc2 = 0LL;
|
||||||
|
q63_t acc3 = 0LL;
|
||||||
|
|
||||||
|
uint32_t tapCnt = phaseLen >> 3;
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
vecState = vldrhq_s16(ptr1);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_s16(ptr2, vec_strides3);
|
||||||
|
acc0 = vmlaldavaq(acc0, vecState, vecCoef);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_s16(ptr2, vec_strides2);
|
||||||
|
acc1 = vmlaldavaq(acc1, vecState, vecCoef);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_s16(ptr2, vec_strides1);
|
||||||
|
acc2 = vmlaldavaq(acc2, vecState, vecCoef);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_s16(ptr2, vec_strides0);
|
||||||
|
acc3 = vmlaldavaq(acc3, vecState, vecCoef);
|
||||||
|
|
||||||
|
ptr1 += 8;
|
||||||
|
ptr2 = ptr2 + S->L * 8;
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
tapCnt = phaseLen & 7;
|
||||||
|
if (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
mve_pred16_t p0 = vctp16q(tapCnt);
|
||||||
|
|
||||||
|
vecState = vldrhq_z_s16(ptr1, p0);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_z_s16(ptr2, vec_strides3, p0);
|
||||||
|
acc0 = vmlaldavaq(acc0, vecState, vecCoef);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_z_s16(ptr2, vec_strides2, p0);
|
||||||
|
acc1 = vmlaldavaq(acc1, vecState, vecCoef);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_z_s16(ptr2, vec_strides1, p0);
|
||||||
|
acc2 = vmlaldavaq(acc2, vecState, vecCoef);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_z_s16(ptr2, vec_strides0, p0);
|
||||||
|
acc3 = vmlaldavaq(acc3, vecState, vecCoef);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc0 = asrl(acc0, 15);
|
||||||
|
acc1 = asrl(acc1, 15);
|
||||||
|
acc2 = asrl(acc2, 15);
|
||||||
|
acc3 = asrl(acc3, 15);
|
||||||
|
|
||||||
|
*pDst++ = (q15_t) __SSAT(acc0, 16);
|
||||||
|
*pDst++ = (q15_t) __SSAT(acc1, 16);
|
||||||
|
*pDst++ = (q15_t) __SSAT(acc2, 16);
|
||||||
|
*pDst++ = (q15_t) __SSAT(acc3, 16);
|
||||||
|
i -= 4;
|
||||||
|
}
|
||||||
|
else if (i >= 3)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Initialize coefficient pointer
|
||||||
|
*/
|
||||||
|
ptr2 = pCoeffs + (i - 1U - 2);
|
||||||
|
|
||||||
|
q63_t acc0 = 0LL;
|
||||||
|
q63_t acc1 = 0LL;
|
||||||
|
q63_t acc2 = 0LL;
|
||||||
|
|
||||||
|
uint32_t tapCnt = phaseLen >> 3;
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
vecState = vldrhq_s16(ptr1);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_s16(ptr2, vec_strides2);
|
||||||
|
acc0 = vmlaldavaq(acc0, vecState, vecCoef);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_s16(ptr2, vec_strides1);
|
||||||
|
acc1 = vmlaldavaq(acc1, vecState, vecCoef);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_s16(ptr2, vec_strides0);
|
||||||
|
acc2 = vmlaldavaq(acc2, vecState, vecCoef);
|
||||||
|
|
||||||
|
ptr1 += 8;
|
||||||
|
ptr2 = ptr2 + S->L * 8;
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
tapCnt = phaseLen & 7;
|
||||||
|
if (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
mve_pred16_t p0 = vctp16q(tapCnt);
|
||||||
|
|
||||||
|
vecState = vldrhq_z_s16(ptr1, p0);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_z_s16(ptr2, vec_strides2, p0);
|
||||||
|
acc0 = vmlaldavaq(acc0, vecState, vecCoef);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_z_s16(ptr2, vec_strides1, p0);
|
||||||
|
acc1 = vmlaldavaq(acc1, vecState, vecCoef);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_z_s16(ptr2, vec_strides0, p0);
|
||||||
|
acc2 = vmlaldavaq(acc2, vecState, vecCoef);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc0 = asrl(acc0, 15);
|
||||||
|
acc1 = asrl(acc1, 15);
|
||||||
|
acc2 = asrl(acc2, 15);
|
||||||
|
|
||||||
|
*pDst++ = (q15_t) __SSAT(acc0, 16);;
|
||||||
|
*pDst++ = (q15_t) __SSAT(acc1, 16);;
|
||||||
|
*pDst++ = (q15_t) __SSAT(acc2, 16);;
|
||||||
|
i -= 3;
|
||||||
|
}
|
||||||
|
else if (i >= 2)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Initialize coefficient pointer
|
||||||
|
*/
|
||||||
|
ptr2 = pCoeffs + (i - 1U - 1);
|
||||||
|
|
||||||
|
q63_t acc0 = 0LL;
|
||||||
|
q63_t acc1 = 0LL;
|
||||||
|
|
||||||
|
uint32_t tapCnt = phaseLen >> 3;
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
vecState = vldrhq_s16(ptr1);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_s16(ptr2, vec_strides1);
|
||||||
|
acc0 = vmlaldavaq(acc0, vecState, vecCoef);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_s16(ptr2, vec_strides0);
|
||||||
|
acc1 = vmlaldavaq(acc1, vecState, vecCoef);
|
||||||
|
|
||||||
|
ptr1 += 8;
|
||||||
|
ptr2 = ptr2 + S->L * 8;
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
tapCnt = phaseLen & 7;
|
||||||
|
if (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
mve_pred16_t p0 = vctp16q(tapCnt);
|
||||||
|
|
||||||
|
vecState = vldrhq_z_s16(ptr1, p0);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_z_s16(ptr2, vec_strides1, p0);
|
||||||
|
acc0 = vmlaldavaq(acc0, vecState, vecCoef);
|
||||||
|
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_z_s16(ptr2, vec_strides0, p0);
|
||||||
|
acc1 = vmlaldavaq(acc1, vecState, vecCoef);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc0 = asrl(acc0, 15);
|
||||||
|
acc1 = asrl(acc1, 15);
|
||||||
|
|
||||||
|
*pDst++ = (q15_t) __SSAT(acc0, 16);
|
||||||
|
*pDst++ = (q15_t) __SSAT(acc1, 16);
|
||||||
|
i -= 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Initialize coefficient pointer
|
||||||
|
*/
|
||||||
|
ptr2 = pCoeffs + (i - 1U);
|
||||||
|
|
||||||
|
q63_t acc0 = 0LL;
|
||||||
|
|
||||||
|
uint32_t tapCnt = phaseLen >> 3;
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
vecState = vldrhq_s16(ptr1);
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_s16(ptr2, vec_strides0);
|
||||||
|
|
||||||
|
acc0 = vmlaldavaq(acc0, vecState, vecCoef);
|
||||||
|
|
||||||
|
ptr1 += 8;
|
||||||
|
ptr2 = ptr2 + S->L * 8;
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
tapCnt = phaseLen & 7;
|
||||||
|
if (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
mve_pred16_t p0 = vctp16q(tapCnt);
|
||||||
|
|
||||||
|
vecState = vldrhq_z_s16(ptr1, p0);
|
||||||
|
vecCoef = vldrhq_gather_shifted_offset_z_s16(ptr2, vec_strides0, p0);
|
||||||
|
acc0 = vmlaldavaq(acc0, vecState, vecCoef);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc0 = asrl(acc0, 15);
|
||||||
|
*pDst++ = (q15_t) __SSAT(acc0, 16);
|
||||||
|
/*
|
||||||
|
* Decrement the loop counter
|
||||||
|
*/
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Advance the state pointer by 1
|
||||||
|
* * to process the next group of interpolation factor number samples
|
||||||
|
*/
|
||||||
|
pState = pState + 1;
|
||||||
|
/*
|
||||||
|
* Decrement the loop counter
|
||||||
|
*/
|
||||||
|
blkCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Processing is complete.
|
||||||
|
* Now copy the last phaseLen - 1 samples to the satrt of the state buffer.
|
||||||
|
* This prepares the state buffer for the next function call.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Points to the start of the state buffer
|
||||||
|
*/
|
||||||
|
pStateCurnt = S->pState;
|
||||||
|
blkCnt = (phaseLen - 1U) >> 3;
|
||||||
|
while (blkCnt > 0U)
|
||||||
|
{
|
||||||
|
vstrhq_s16(pStateCurnt, vldrhq_s16(pState));
|
||||||
|
pState += 8;
|
||||||
|
pStateCurnt += 8;
|
||||||
|
blkCnt--;
|
||||||
|
}
|
||||||
|
blkCnt = (phaseLen - 1U) & 7;
|
||||||
|
if (blkCnt > 0U)
|
||||||
|
{
|
||||||
|
mve_pred16_t p0 = vctp16q(blkCnt);
|
||||||
|
vstrhq_p_s16(pStateCurnt, vldrhq_s16(pState), p0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void arm_fir_interpolate_q15(
|
||||||
|
const arm_fir_interpolate_instance_q15 * S,
|
||||||
|
const q15_t * pSrc,
|
||||||
|
q15_t * pDst,
|
||||||
|
uint32_t blockSize)
|
||||||
|
{
|
||||||
|
#if (1)
|
||||||
|
//#if !defined(ARM_MATH_CM0_FAMILY)
|
||||||
|
|
||||||
|
q15_t *pState = S->pState; /* State pointer */
|
||||||
|
const q15_t *pCoeffs = S->pCoeffs; /* Coefficient pointer */
|
||||||
|
q15_t *pStateCur; /* Points to the current sample of the state */
|
||||||
|
q15_t *ptr1; /* Temporary pointer for state buffer */
|
||||||
|
const q15_t *ptr2; /* Temporary pointer for coefficient buffer */
|
||||||
|
q63_t sum0; /* Accumulators */
|
||||||
|
uint32_t i, blkCnt, tapCnt; /* Loop counters */
|
||||||
|
uint32_t phaseLen = S->phaseLength; /* Length of each polyphase filter component */
|
||||||
|
uint32_t j;
|
||||||
|
|
||||||
|
#if defined (ARM_MATH_LOOPUNROLL)
|
||||||
|
q63_t acc0, acc1, acc2, acc3;
|
||||||
|
q15_t x0, x1, x2, x3;
|
||||||
|
q15_t c0, c1, c2, c3;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* S->pState buffer contains previous frame (phaseLen - 1) samples */
|
||||||
|
/* pStateCur points to the location where the new input data should be written */
|
||||||
|
pStateCur = S->pState + (phaseLen - 1U);
|
||||||
|
|
||||||
|
#if defined (ARM_MATH_LOOPUNROLL)
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute 4 outputs at a time */
|
||||||
|
blkCnt = blockSize >> 2U;
|
||||||
|
|
||||||
|
while (blkCnt > 0U)
|
||||||
|
{
|
||||||
|
/* Copy new input sample into the state buffer */
|
||||||
|
*pStateCur++ = *pSrc++;
|
||||||
|
*pStateCur++ = *pSrc++;
|
||||||
|
*pStateCur++ = *pSrc++;
|
||||||
|
*pStateCur++ = *pSrc++;
|
||||||
|
|
||||||
|
/* Address modifier index of coefficient buffer */
|
||||||
|
j = 1U;
|
||||||
|
|
||||||
|
/* Loop over the Interpolation factor. */
|
||||||
|
i = (S->L);
|
||||||
|
|
||||||
|
while (i > 0U)
|
||||||
|
{
|
||||||
|
/* Set accumulator to zero */
|
||||||
|
acc0 = 0;
|
||||||
|
acc1 = 0;
|
||||||
|
acc2 = 0;
|
||||||
|
acc3 = 0;
|
||||||
|
|
||||||
|
/* Initialize state pointer */
|
||||||
|
ptr1 = pState;
|
||||||
|
|
||||||
|
/* Initialize coefficient pointer */
|
||||||
|
ptr2 = pCoeffs + (S->L - j);
|
||||||
|
|
||||||
|
/* Loop over the polyPhase length. Unroll by a factor of 4.
|
||||||
|
Repeat until we've computed numTaps-(4*S->L) coefficients. */
|
||||||
|
tapCnt = phaseLen >> 2U;
|
||||||
|
|
||||||
|
x0 = *(ptr1++);
|
||||||
|
x1 = *(ptr1++);
|
||||||
|
x2 = *(ptr1++);
|
||||||
|
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
/* Read the input sample */
|
||||||
|
x3 = *(ptr1++);
|
||||||
|
|
||||||
|
/* Read the coefficient */
|
||||||
|
c0 = *(ptr2);
|
||||||
|
|
||||||
|
/* Perform the multiply-accumulate */
|
||||||
|
acc0 += (q63_t) x0 * c0;
|
||||||
|
acc1 += (q63_t) x1 * c0;
|
||||||
|
acc2 += (q63_t) x2 * c0;
|
||||||
|
acc3 += (q63_t) x3 * c0;
|
||||||
|
|
||||||
|
/* Read the coefficient */
|
||||||
|
c1 = *(ptr2 + S->L);
|
||||||
|
|
||||||
|
/* Read the input sample */
|
||||||
|
x0 = *(ptr1++);
|
||||||
|
|
||||||
|
/* Perform the multiply-accumulate */
|
||||||
|
acc0 += (q63_t) x1 * c1;
|
||||||
|
acc1 += (q63_t) x2 * c1;
|
||||||
|
acc2 += (q63_t) x3 * c1;
|
||||||
|
acc3 += (q63_t) x0 * c1;
|
||||||
|
|
||||||
|
/* Read the coefficient */
|
||||||
|
c2 = *(ptr2 + S->L * 2);
|
||||||
|
|
||||||
|
/* Read the input sample */
|
||||||
|
x1 = *(ptr1++);
|
||||||
|
|
||||||
|
/* Perform the multiply-accumulate */
|
||||||
|
acc0 += (q63_t) x2 * c2;
|
||||||
|
acc1 += (q63_t) x3 * c2;
|
||||||
|
acc2 += (q63_t) x0 * c2;
|
||||||
|
acc3 += (q63_t) x1 * c2;
|
||||||
|
|
||||||
|
/* Read the coefficient */
|
||||||
|
c3 = *(ptr2 + S->L * 3);
|
||||||
|
|
||||||
|
/* Read the input sample */
|
||||||
|
x2 = *(ptr1++);
|
||||||
|
|
||||||
|
/* Perform the multiply-accumulate */
|
||||||
|
acc0 += (q63_t) x3 * c3;
|
||||||
|
acc1 += (q63_t) x0 * c3;
|
||||||
|
acc2 += (q63_t) x1 * c3;
|
||||||
|
acc3 += (q63_t) x2 * c3;
|
||||||
|
|
||||||
|
|
||||||
|
/* Upsampling is done by stuffing L-1 zeros between each sample.
|
||||||
|
* So instead of multiplying zeros with coefficients,
|
||||||
|
* Increment the coefficient pointer by interpolation factor times. */
|
||||||
|
ptr2 += 4 * S->L;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the polyPhase length is not a multiple of 4, compute the remaining filter taps */
|
||||||
|
tapCnt = phaseLen % 0x4U;
|
||||||
|
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
/* Read the input sample */
|
||||||
|
x3 = *(ptr1++);
|
||||||
|
|
||||||
|
/* Read the coefficient */
|
||||||
|
c0 = *(ptr2);
|
||||||
|
|
||||||
|
/* Perform the multiply-accumulate */
|
||||||
|
acc0 += (q63_t) x0 * c0;
|
||||||
|
acc1 += (q63_t) x1 * c0;
|
||||||
|
acc2 += (q63_t) x2 * c0;
|
||||||
|
acc3 += (q63_t) x3 * c0;
|
||||||
|
|
||||||
|
/* Increment the coefficient pointer by interpolation factor times. */
|
||||||
|
ptr2 += S->L;
|
||||||
|
|
||||||
|
/* update states for next sample processing */
|
||||||
|
x0 = x1;
|
||||||
|
x1 = x2;
|
||||||
|
x2 = x3;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The result is in the accumulator, store in the destination buffer. */
|
||||||
|
*(pDst ) = (q15_t) (__SSAT((acc0 >> 15), 16));
|
||||||
|
*(pDst + S->L) = (q15_t) (__SSAT((acc1 >> 15), 16));
|
||||||
|
*(pDst + 2 * S->L) = (q15_t) (__SSAT((acc2 >> 15), 16));
|
||||||
|
*(pDst + 3 * S->L) = (q15_t) (__SSAT((acc3 >> 15), 16));
|
||||||
|
|
||||||
|
pDst++;
|
||||||
|
|
||||||
|
/* Increment the address modifier index of coefficient buffer */
|
||||||
|
j++;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance the state pointer by 1
|
||||||
|
* to process the next group of interpolation factor number samples */
|
||||||
|
pState = pState + 4;
|
||||||
|
|
||||||
|
pDst += S->L * 3;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
blkCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute remaining outputs */
|
||||||
|
blkCnt = blockSize % 0x4U;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Initialize blkCnt with number of samples */
|
||||||
|
blkCnt = blockSize;
|
||||||
|
|
||||||
|
#endif /* #if defined (ARM_MATH_LOOPUNROLL) */
|
||||||
|
|
||||||
|
while (blkCnt > 0U)
|
||||||
|
{
|
||||||
|
/* Copy new input sample into the state buffer */
|
||||||
|
*pStateCur++ = *pSrc++;
|
||||||
|
|
||||||
|
/* Address modifier index of coefficient buffer */
|
||||||
|
j = 1U;
|
||||||
|
|
||||||
|
/* Loop over the Interpolation factor. */
|
||||||
|
i = S->L;
|
||||||
|
while (i > 0U)
|
||||||
|
{
|
||||||
|
/* Set accumulator to zero */
|
||||||
|
sum0 = 0;
|
||||||
|
|
||||||
|
/* Initialize state pointer */
|
||||||
|
ptr1 = pState;
|
||||||
|
|
||||||
|
/* Initialize coefficient pointer */
|
||||||
|
ptr2 = pCoeffs + (S->L - j);
|
||||||
|
|
||||||
|
/* Loop over the polyPhase length.
|
||||||
|
Repeat until we've computed numTaps-(4*S->L) coefficients. */
|
||||||
|
|
||||||
|
#if defined (ARM_MATH_LOOPUNROLL)
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute 4 outputs at a time */
|
||||||
|
tapCnt = phaseLen >> 2U;
|
||||||
|
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
/* Perform the multiply-accumulate */
|
||||||
|
sum0 += (q63_t) *ptr1++ * *ptr2;
|
||||||
|
|
||||||
|
/* Upsampling is done by stuffing L-1 zeros between each sample.
|
||||||
|
* So instead of multiplying zeros with coefficients,
|
||||||
|
* Increment the coefficient pointer by interpolation factor times. */
|
||||||
|
ptr2 += S->L;
|
||||||
|
|
||||||
|
sum0 += (q63_t) *ptr1++ * *ptr2;
|
||||||
|
ptr2 += S->L;
|
||||||
|
|
||||||
|
sum0 += (q63_t) *ptr1++ * *ptr2;
|
||||||
|
ptr2 += S->L;
|
||||||
|
|
||||||
|
sum0 += (q63_t) *ptr1++ * *ptr2;
|
||||||
|
ptr2 += S->L;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute remaining outputs */
|
||||||
|
tapCnt = phaseLen % 0x4U;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Initialize tapCnt with number of samples */
|
||||||
|
tapCnt = phaseLen;
|
||||||
|
|
||||||
|
#endif /* #if defined (ARM_MATH_LOOPUNROLL) */
|
||||||
|
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
/* Perform the multiply-accumulate */
|
||||||
|
sum0 += (q63_t) *ptr1++ * *ptr2;
|
||||||
|
|
||||||
|
/* Upsampling is done by stuffing L-1 zeros between each sample.
|
||||||
|
* So instead of multiplying zeros with coefficients,
|
||||||
|
* Increment the coefficient pointer by interpolation factor times. */
|
||||||
|
ptr2 += S->L;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The result is in the accumulator, store in the destination buffer. */
|
||||||
|
*pDst++ = (q15_t) (__SSAT((sum0 >> 15), 16));
|
||||||
|
|
||||||
|
/* Increment the address modifier index of coefficient buffer */
|
||||||
|
j++;
|
||||||
|
|
||||||
|
/* Decrement the loop counter */
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance the state pointer by 1
|
||||||
|
* to process the next group of interpolation factor number samples */
|
||||||
|
pState = pState + 1;
|
||||||
|
|
||||||
|
/* Decrement the loop counter */
|
||||||
|
blkCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Processing is complete.
|
||||||
|
Now copy the last phaseLen - 1 samples to the satrt of the state buffer.
|
||||||
|
This prepares the state buffer for the next function call. */
|
||||||
|
|
||||||
|
/* Points to the start of the state buffer */
|
||||||
|
pStateCur = S->pState;
|
||||||
|
|
||||||
|
#if defined (ARM_MATH_LOOPUNROLL)
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute 4 outputs at a time */
|
||||||
|
tapCnt = (phaseLen - 1U) >> 2U;
|
||||||
|
|
||||||
|
/* copy data */
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
write_q15x2_ia (&pStateCur, read_q15x2_ia (&pState));
|
||||||
|
write_q15x2_ia (&pStateCur, read_q15x2_ia (&pState));
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute remaining outputs */
|
||||||
|
tapCnt = (phaseLen - 1U) % 0x04U;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Initialize tapCnt with number of samples */
|
||||||
|
tapCnt = (phaseLen - 1U);
|
||||||
|
|
||||||
|
#endif /* #if defined (ARM_MATH_LOOPUNROLL) */
|
||||||
|
|
||||||
|
/* Copy data */
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
*pStateCur++ = *pState++;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
/* alternate version for CM0_FAMILY */
|
||||||
|
|
||||||
|
q15_t *pState = S->pState; /* State pointer */
|
||||||
|
const q15_t *pCoeffs = S->pCoeffs; /* Coefficient pointer */
|
||||||
|
q15_t *pStateCur; /* Points to the current sample of the state */
|
||||||
|
q15_t *ptr1; /* Temporary pointer for state buffer */
|
||||||
|
const q15_t *ptr2; /* Temporary pointer for coefficient buffer */
|
||||||
|
q63_t sum0; /* Accumulators */
|
||||||
|
uint32_t i, blkCnt, tapCnt; /* Loop counters */
|
||||||
|
uint32_t phaseLen = S->phaseLength; /* Length of each polyphase filter component */
|
||||||
|
|
||||||
|
/* S->pState buffer contains previous frame (phaseLen - 1) samples */
|
||||||
|
/* pStateCur points to the location where the new input data should be written */
|
||||||
|
pStateCur = S->pState + (phaseLen - 1U);
|
||||||
|
|
||||||
|
/* Total number of intput samples */
|
||||||
|
blkCnt = blockSize;
|
||||||
|
|
||||||
|
/* Loop over the blockSize. */
|
||||||
|
while (blkCnt > 0U)
|
||||||
|
{
|
||||||
|
/* Copy new input sample into the state buffer */
|
||||||
|
*pStateCur++ = *pSrc++;
|
||||||
|
|
||||||
|
/* Loop over the Interpolation factor. */
|
||||||
|
i = S->L;
|
||||||
|
|
||||||
|
while (i > 0U)
|
||||||
|
{
|
||||||
|
/* Set accumulator to zero */
|
||||||
|
sum0 = 0;
|
||||||
|
|
||||||
|
/* Initialize state pointer */
|
||||||
|
ptr1 = pState;
|
||||||
|
|
||||||
|
/* Initialize coefficient pointer */
|
||||||
|
ptr2 = pCoeffs + (i - 1U);
|
||||||
|
|
||||||
|
/* Loop over the polyPhase length */
|
||||||
|
tapCnt = phaseLen;
|
||||||
|
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
/* Perform the multiply-accumulate */
|
||||||
|
sum0 += ((q63_t) *ptr1++ * *ptr2);
|
||||||
|
|
||||||
|
/* Increment the coefficient pointer by interpolation factor times. */
|
||||||
|
ptr2 += S->L;
|
||||||
|
|
||||||
|
/* Decrement the loop counter */
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store the result after converting to 1.15 format in the destination buffer. */
|
||||||
|
*pDst++ = (q15_t) (__SSAT((sum0 >> 15), 16));
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance the state pointer by 1
|
||||||
|
* to process the next group of interpolation factor number samples */
|
||||||
|
pState = pState + 1;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
blkCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Processing is complete.
|
||||||
|
** Now copy the last phaseLen - 1 samples to the start of the state buffer.
|
||||||
|
** This prepares the state buffer for the next function call. */
|
||||||
|
|
||||||
|
/* Points to the start of the state buffer */
|
||||||
|
pStateCur = S->pState;
|
||||||
|
|
||||||
|
tapCnt = phaseLen - 1U;
|
||||||
|
|
||||||
|
/* Copy data */
|
||||||
|
while (tapCnt > 0U)
|
||||||
|
{
|
||||||
|
*pStateCur++ = *pState++;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
tapCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* #if !defined(ARM_MATH_CM0_FAMILY) */
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif /* defined(ARM_MATH_MVEI)*/
|
||||||
|
/**
|
||||||
|
@} end of FIR_Interpolate group
|
||||||
|
*/
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
/* ----------------------------------------------------------------------
|
||||||
|
* Project: CMSIS DSP Library
|
||||||
|
* Title: arm_q15_to_q31.c
|
||||||
|
* Description: Converts the elements of the Q15 vector to Q31 vector
|
||||||
|
*
|
||||||
|
* $Date: 23 April 2021
|
||||||
|
* $Revision: V1.9.0
|
||||||
|
*
|
||||||
|
* Target Processor: Cortex-M and Cortex-A cores
|
||||||
|
* -------------------------------------------------------------------- */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dsp/support_functions.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
@ingroup groupSupport
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@addtogroup q15_to_x
|
||||||
|
@{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Converts the elements of the Q15 vector to Q31 vector.
|
||||||
|
@param[in] pSrc points to the Q15 input vector
|
||||||
|
@param[out] pDst points to the Q31 output vector
|
||||||
|
@param[in] blockSize number of samples in each vector
|
||||||
|
|
||||||
|
@par Details
|
||||||
|
The equation used for the conversion process is:
|
||||||
|
<pre>
|
||||||
|
pDst[n] = (q31_t) pSrc[n] << 16; 0 <= n < blockSize.
|
||||||
|
</pre>
|
||||||
|
*/
|
||||||
|
#if defined(ARM_MATH_MVEI) && !defined(ARM_MATH_AUTOVECTORIZE)
|
||||||
|
__STATIC_INLINE void arm_q15_to_q31(
|
||||||
|
const q15_t * pSrc,
|
||||||
|
q31_t * pDst,
|
||||||
|
uint32_t blockSize)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t blkCnt;
|
||||||
|
|
||||||
|
q31x4_t vecDst;
|
||||||
|
|
||||||
|
blkCnt = blockSize>> 2;
|
||||||
|
while (blkCnt > 0U)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* C = (q31_t)A << 16 */
|
||||||
|
/* convert from q15 to q31 and then store the results in the destination buffer */
|
||||||
|
/* load q15 + 32-bit widening */
|
||||||
|
vecDst = vldrhq_s32((q15_t const *) pSrc);
|
||||||
|
vecDst = vshlq_n(vecDst, 16);
|
||||||
|
vstrwq_s32(pDst, vecDst);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decrement the blockSize loop counter
|
||||||
|
* Advance vector source and destination pointers
|
||||||
|
*/
|
||||||
|
pDst += 4;
|
||||||
|
pSrc += 4;
|
||||||
|
blkCnt --;
|
||||||
|
}
|
||||||
|
|
||||||
|
blkCnt = blockSize & 3;
|
||||||
|
while (blkCnt > 0U)
|
||||||
|
{
|
||||||
|
/* C = (q31_t) A << 16 */
|
||||||
|
|
||||||
|
/* Convert from q15 to q31 and store result in destination buffer */
|
||||||
|
*pDst++ = (q31_t) *pSrc++ << 16;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
blkCnt--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void arm_q15_to_q31(
|
||||||
|
const q15_t * pSrc,
|
||||||
|
q31_t * pDst,
|
||||||
|
uint32_t blockSize)
|
||||||
|
{
|
||||||
|
uint32_t blkCnt; /* Loop counter */
|
||||||
|
const q15_t *pIn = pSrc; /* Source pointer */
|
||||||
|
|
||||||
|
#if defined (ARM_MATH_LOOPUNROLL)
|
||||||
|
q31_t in1, in2;
|
||||||
|
q31_t out1, out2, out3, out4;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined (ARM_MATH_LOOPUNROLL)
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute 4 outputs at a time */
|
||||||
|
blkCnt = blockSize >> 2U;
|
||||||
|
|
||||||
|
while (blkCnt > 0U)
|
||||||
|
{
|
||||||
|
/* C = (q31_t)A << 16 */
|
||||||
|
|
||||||
|
/* Convert from q15 to q31 and store result in destination buffer */
|
||||||
|
in1 = read_q15x2_ia (&pIn);
|
||||||
|
in2 = read_q15x2_ia (&pIn);
|
||||||
|
|
||||||
|
#ifndef ARM_MATH_BIG_ENDIAN
|
||||||
|
|
||||||
|
/* extract lower 16 bits to 32 bit result */
|
||||||
|
out1 = in1 << 16U;
|
||||||
|
/* extract upper 16 bits to 32 bit result */
|
||||||
|
out2 = in1 & 0xFFFF0000;
|
||||||
|
/* extract lower 16 bits to 32 bit result */
|
||||||
|
out3 = in2 << 16U;
|
||||||
|
/* extract upper 16 bits to 32 bit result */
|
||||||
|
out4 = in2 & 0xFFFF0000;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* extract upper 16 bits to 32 bit result */
|
||||||
|
out1 = in1 & 0xFFFF0000;
|
||||||
|
/* extract lower 16 bits to 32 bit result */
|
||||||
|
out2 = in1 << 16U;
|
||||||
|
/* extract upper 16 bits to 32 bit result */
|
||||||
|
out3 = in2 & 0xFFFF0000;
|
||||||
|
/* extract lower 16 bits to 32 bit result */
|
||||||
|
out4 = in2 << 16U;
|
||||||
|
|
||||||
|
#endif /* #ifndef ARM_MATH_BIG_ENDIAN */
|
||||||
|
|
||||||
|
*pDst++ = out1;
|
||||||
|
*pDst++ = out2;
|
||||||
|
*pDst++ = out3;
|
||||||
|
*pDst++ = out4;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
blkCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop unrolling: Compute remaining outputs */
|
||||||
|
blkCnt = blockSize % 0x4U;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Initialize blkCnt with number of samples */
|
||||||
|
blkCnt = blockSize;
|
||||||
|
|
||||||
|
#endif /* #if defined (ARM_MATH_LOOPUNROLL) */
|
||||||
|
|
||||||
|
while (blkCnt > 0U)
|
||||||
|
{
|
||||||
|
/* C = (q31_t) A << 16 */
|
||||||
|
|
||||||
|
/* Convert from q15 to q31 and store result in destination buffer */
|
||||||
|
*pDst++ = (q31_t) *pIn++ << 16;
|
||||||
|
|
||||||
|
/* Decrement loop counter */
|
||||||
|
blkCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif /* defined(ARM_MATH_MVEI) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
@} end of q15_to_x group
|
||||||
|
*/
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/* ----------------------------------------------------------------------
|
||||||
|
* Project: CMSIS DSP Library
|
||||||
|
* Title: arm_sin_q31.c
|
||||||
|
* Description: Fast sine calculation for Q31 values
|
||||||
|
*
|
||||||
|
* $Date: 23 April 2021
|
||||||
|
* $Revision: V1.9.0
|
||||||
|
*
|
||||||
|
* Target Processor: Cortex-M and Cortex-A cores
|
||||||
|
* -------------------------------------------------------------------- */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dsp/fast_math_functions.h"
|
||||||
|
#include "arm_common_tables.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
@ingroup groupFastMath
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@addtogroup sin
|
||||||
|
@{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Fast approximation to the trigonometric sine function for Q31 data.
|
||||||
|
@param[in] x Scaled input value in radians
|
||||||
|
@return sin(x)
|
||||||
|
|
||||||
|
The Q31 input value is in the range [0 +0.9999] and is mapped to a radian value in the range [0 2*PI).
|
||||||
|
*/
|
||||||
|
|
||||||
|
q31_t arm_sin_q31(
|
||||||
|
q31_t x)
|
||||||
|
{
|
||||||
|
q31_t sinVal; /* Temporary variables for input, output */
|
||||||
|
int32_t index; /* Index variable */
|
||||||
|
q31_t a, b; /* Two nearest output values */
|
||||||
|
q31_t fract; /* Temporary values for fractional values */
|
||||||
|
|
||||||
|
if (x < 0)
|
||||||
|
{ /* convert negative numbers to corresponding positive ones */
|
||||||
|
x = (uint32_t)x + 0x80000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the nearest index */
|
||||||
|
index = (uint32_t)x >> FAST_MATH_Q31_SHIFT;
|
||||||
|
|
||||||
|
/* Calculation of fractional value */
|
||||||
|
fract = (x - (index << FAST_MATH_Q31_SHIFT)) << 9;
|
||||||
|
|
||||||
|
/* Read two nearest values of input value from the sin table */
|
||||||
|
a = sinTable_q31[index];
|
||||||
|
b = sinTable_q31[index+1];
|
||||||
|
|
||||||
|
/* Linear interpolation process */
|
||||||
|
sinVal = (q63_t) (0x80000000 - fract) * a >> 32;
|
||||||
|
sinVal = (q31_t) ((((q63_t) sinVal << 32) + ((q63_t) fract * b)) >> 32);
|
||||||
|
|
||||||
|
/* Return output value */
|
||||||
|
return (sinVal << 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@} end of sin group
|
||||||
|
*/
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2017,2020 by Jonathan Naylor G4KLX
|
||||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
@@ -17,8 +17,6 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// #define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "CWIdTX.h"
|
#include "CWIdTX.h"
|
||||||
@@ -77,6 +75,9 @@ const struct {
|
|||||||
{'/', 0xEAE80000U, 16U},
|
{'/', 0xEAE80000U, 16U},
|
||||||
{'?', 0xAEEA0000U, 18U},
|
{'?', 0xAEEA0000U, 18U},
|
||||||
{',', 0xEEAEE000U, 22U},
|
{',', 0xEEAEE000U, 22U},
|
||||||
|
{'-', 0xEAAE0000U, 18U},
|
||||||
|
{'=', 0xEAB80000U, 16U},
|
||||||
|
{'.', 0xBAEB8000U, 20U},
|
||||||
{' ', 0x00000000U, 4U},
|
{' ', 0x00000000U, 4U},
|
||||||
{0U, 0x00000000U, 0U}
|
{0U, 0x00000000U, 0U}
|
||||||
};
|
};
|
||||||
@@ -124,7 +125,7 @@ void CCWIdTX::process()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t CCWIdTX::write(const uint8_t* data, uint8_t length)
|
uint8_t CCWIdTX::write(const uint8_t* data, uint16_t length)
|
||||||
{
|
{
|
||||||
::memset(m_poBuffer, 0x00U, 1000U * sizeof(uint8_t));
|
::memset(m_poBuffer, 0x00U, 1000U * sizeof(uint8_t));
|
||||||
|
|
||||||
@@ -170,4 +171,3 @@ void CCWIdTX::reset()
|
|||||||
m_poPtr = 0U;
|
m_poPtr = 0U;
|
||||||
m_n = 0U;
|
m_n = 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
|
||||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
* Copyright (C) 2016,2020 by Colin Durbridge G4EML
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -28,7 +28,7 @@ public:
|
|||||||
|
|
||||||
void process();
|
void process();
|
||||||
|
|
||||||
uint8_t write(const uint8_t* data, uint8_t length);
|
uint8_t write(const uint8_t* data, uint16_t length);
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "CalRSSI.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
CCalRSSI::CCalRSSI() :
|
||||||
|
m_count(0U),
|
||||||
|
m_accum(0U),
|
||||||
|
m_min(0xFFFFU),
|
||||||
|
m_max(0x0000U)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCalRSSI::samples(const uint16_t* rssi, uint8_t length)
|
||||||
|
{
|
||||||
|
for (uint16_t i = 0U; i < length; i++) {
|
||||||
|
uint16_t ss = rssi[i];
|
||||||
|
|
||||||
|
m_accum += ss;
|
||||||
|
|
||||||
|
if (ss > m_max)
|
||||||
|
m_max = ss;
|
||||||
|
if (ss < m_min)
|
||||||
|
m_min = ss;
|
||||||
|
|
||||||
|
m_count++;
|
||||||
|
if (m_count >= 24000U) {
|
||||||
|
uint16_t ave = m_accum / m_count;
|
||||||
|
|
||||||
|
uint8_t buffer[6U];
|
||||||
|
buffer[0U] = (m_max >> 8) & 0xFFU;
|
||||||
|
buffer[1U] = (m_max >> 0) & 0xFFU;
|
||||||
|
buffer[2U] = (m_min >> 8) & 0xFFU;
|
||||||
|
buffer[3U] = (m_min >> 0) & 0xFFU;
|
||||||
|
buffer[4U] = (ave >> 8) & 0xFFU;
|
||||||
|
buffer[5U] = (ave >> 0) & 0xFFU;
|
||||||
|
|
||||||
|
serial.writeRSSIData(buffer, 6U);
|
||||||
|
|
||||||
|
m_count = 0U;
|
||||||
|
m_accum = 0U;
|
||||||
|
m_min = 0xFFFFU;
|
||||||
|
m_max = 0x0000U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(CALRSSI_H)
|
||||||
|
#define CALRSSI_H
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
class CCalRSSI {
|
||||||
|
public:
|
||||||
|
CCalRSSI();
|
||||||
|
|
||||||
|
void samples(const uint16_t* rssi, uint8_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t m_count;
|
||||||
|
uint32_t m_accum;
|
||||||
|
uint16_t m_min;
|
||||||
|
uint16_t m_max;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015,2016,2017,2018,2020,2024 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(CONFIG_H)
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
// Allow for the selection of which modes to compile into the firmware. This is particularly useful for processors
|
||||||
|
// which have limited code space and processing power.
|
||||||
|
|
||||||
|
// Enable D-Star support.
|
||||||
|
#define MODE_DSTAR
|
||||||
|
|
||||||
|
// Enable DMR support.
|
||||||
|
#define MODE_DMR
|
||||||
|
|
||||||
|
// Enable System Fusion support.
|
||||||
|
#define MODE_YSF
|
||||||
|
|
||||||
|
// Enable P25 phase 1 support.
|
||||||
|
#define MODE_P25
|
||||||
|
|
||||||
|
// Enable NXDN support, the boxcar filter sometimes improves the performance of NXDN receive on some systems.
|
||||||
|
#define MODE_NXDN
|
||||||
|
#define USE_NXDN_BOXCAR
|
||||||
|
|
||||||
|
// Enable M17 support.
|
||||||
|
#define MODE_M17
|
||||||
|
|
||||||
|
// Enable POCSAG support.
|
||||||
|
#define MODE_POCSAG
|
||||||
|
|
||||||
|
// Enable FM support.
|
||||||
|
#define MODE_FM
|
||||||
|
|
||||||
|
// Enable AX.25 support, this is only enabled if FM is also enabled.
|
||||||
|
#define MODE_AX25
|
||||||
|
|
||||||
|
// Select a baud rate for host communication. The faster speeds are needed for external FM to work.
|
||||||
|
// #define SERIAL_SPEED 115200 // Suitable for most older boards. External FM will NOT work with this!
|
||||||
|
// #define SERIAL_SPEED 230400 // Only works on newer boards like fast M4, M7. External FM might work with this
|
||||||
|
#define SERIAL_SPEED 460800 // Only works on newer boards like fast M4, M7. External FM will work with this
|
||||||
|
//#define SERIAL_SPEED 500000 // Used with newer boards and Armbian on AllWinner SOCs (H2, H3) that do not support 460800
|
||||||
|
|
||||||
|
// Use pins to output the current mode via LEDs
|
||||||
|
#define MODE_LEDS
|
||||||
|
|
||||||
|
// Pass RSSI information to the host
|
||||||
|
#define SEND_RSSI_DATA
|
||||||
|
|
||||||
|
// Use the modem as a serial repeater for Nextion displays
|
||||||
|
#define SERIAL_REPEATER
|
||||||
|
|
||||||
|
// Set the baud rate of the modem serial repeater for Nextion displays
|
||||||
|
#define SERIAL_REPEATER_BAUD_RATE 9600
|
||||||
|
|
||||||
|
// Use the modem as an I2C repeater for OLED displays
|
||||||
|
// #define I2C_REPEATER
|
||||||
|
|
||||||
|
// To reduce CPU load, you can remove the DC blocker by commenting out the next line
|
||||||
|
#define USE_DCBLOCKER
|
||||||
|
|
||||||
|
// Choose the type of modem board in use, only one.
|
||||||
|
// The KI6ZUM v0.9 Pi hat, STM32F446 CPU
|
||||||
|
#define ZUM_V09_PI
|
||||||
|
// The KI6ZUM v1.0 Pi hat, STM32F722 CPU
|
||||||
|
// #define ZUM_V10_PI
|
||||||
|
// The Repeater Builder V3 board, STM32F446 CPU
|
||||||
|
// #define RB_V3_PI
|
||||||
|
// The Repeater Builder V5 board, STM32F722 CPU
|
||||||
|
// #define RB_V5_PI
|
||||||
|
|
||||||
|
// Constant Service LED once repeater is running
|
||||||
|
// Do not use if employing an external hardware watchdog
|
||||||
|
// #define CONSTANT_SRV_LED
|
||||||
|
|
||||||
|
// Use the YSF and P25 LEDs for NXDN
|
||||||
|
// #define USE_ALTERNATE_NXDN_LEDS
|
||||||
|
|
||||||
|
// Use the D-Star and P25 LEDs for M17
|
||||||
|
#define USE_ALTERNATE_M17_LEDS
|
||||||
|
|
||||||
|
// Use the D-Star and DMR LEDs for POCSAG
|
||||||
|
#define USE_ALTERNATE_POCSAG_LEDS
|
||||||
|
|
||||||
|
// Use the D-Star and YSF LEDs for FM
|
||||||
|
#define USE_ALTERNATE_FM_LEDS
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2015,2020 by Jonathan Naylor G4KLX
|
||||||
|
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "CalDMR.h"
|
||||||
|
|
||||||
|
// Voice LC BS Header, CC: 1, srcID: 1, dstID: TG9
|
||||||
|
const uint8_t VH_1K[] = {0x00U,
|
||||||
|
0x00U, 0x20U, 0x08U, 0x08U, 0x02U, 0x38U, 0x15U, 0x00U, 0x2CU, 0xA0U, 0x14U,
|
||||||
|
0x60U, 0x84U, 0x6DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xDEU, 0x30U, 0x30U,
|
||||||
|
0x01U, 0x10U, 0x01U, 0x40U, 0x03U, 0xC0U, 0x13U, 0xC1U, 0x1EU, 0x80U, 0x6FU};
|
||||||
|
|
||||||
|
// Voice Term BS with LC, CC: 1, srcID: 1, dstID: TG9
|
||||||
|
const uint8_t VT_1K[] = {0x00U,
|
||||||
|
0x00U, 0x4FU, 0x08U, 0xDCU, 0x02U, 0x88U, 0x15U, 0x78U, 0x2CU, 0xD0U, 0x14U,
|
||||||
|
0xC0U, 0x84U, 0xADU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD9U, 0x65U, 0x24U,
|
||||||
|
0x02U, 0x28U, 0x06U, 0x20U, 0x0FU, 0x80U, 0x1BU, 0xC1U, 0x07U, 0x80U, 0x5CU};
|
||||||
|
|
||||||
|
// Voice LC MS Header, CC: 1, srcID: 1, dstID: TG9
|
||||||
|
const uint8_t VH_DMO1K[] = {0x00U,
|
||||||
|
0x00U, 0x20U, 0x08U, 0x08U, 0x02U, 0x38U, 0x15U, 0x00U, 0x2CU, 0xA0U, 0x14U,
|
||||||
|
0x60U, 0x84U, 0x6DU, 0x5DU, 0x7FU, 0x77U, 0xFDU, 0x75U, 0x7EU, 0x30U, 0x30U,
|
||||||
|
0x01U, 0x10U, 0x01U, 0x40U, 0x03U, 0xC0U, 0x13U, 0xC1U, 0x1EU, 0x80U, 0x6FU};
|
||||||
|
|
||||||
|
// Voice Term MS with LC, CC: 1, srcID: 1, dstID: TG9
|
||||||
|
const uint8_t VT_DMO1K[] = {0x00U,
|
||||||
|
0x00U, 0x4FU, 0x08U, 0xDCU, 0x02U, 0x88U, 0x15U, 0x78U, 0x2CU, 0xD0U, 0x14U,
|
||||||
|
0xC0U, 0x84U, 0xADU, 0x5DU, 0x7FU, 0x77U, 0xFDU, 0x75U, 0x79U, 0x65U, 0x24U,
|
||||||
|
0x02U, 0x28U, 0x06U, 0x20U, 0x0FU, 0x80U, 0x1BU, 0xC1U, 0x07U, 0x80U, 0x5CU};
|
||||||
|
|
||||||
|
// Voice coding data + FEC, 1031 Hz Test Pattern
|
||||||
|
const uint8_t VOICE_1K[] = {0x00U,
|
||||||
|
0xCEU, 0xA8U, 0xFEU, 0x83U, 0xACU, 0xC4U, 0x58U, 0x20U, 0x0AU, 0xCEU, 0xA8U,
|
||||||
|
0xFEU, 0x83U, 0xA0U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x0CU, 0xC4U, 0x58U,
|
||||||
|
0x20U, 0x0AU, 0xCEU, 0xA8U, 0xFEU, 0x83U, 0xACU, 0xC4U, 0x58U, 0x20U, 0x0AU};
|
||||||
|
|
||||||
|
// Embedded LC BS: CC: 1, srcID: 1, dstID: TG9
|
||||||
|
const uint8_t SYNCEMB_1K[6][7] = {
|
||||||
|
{0x07U, 0x55U, 0xFDU, 0x7DU, 0xF7U, 0x5FU, 0x70U}, // BS VOICE SYNC (audio seq 0)
|
||||||
|
{0x01U, 0x30U, 0x00U, 0x00U, 0x90U, 0x09U, 0x10U}, // EMB + Embedded LC1 (audio seq 1)
|
||||||
|
{0x01U, 0x70U, 0x00U, 0x90U, 0x00U, 0x07U, 0x40U}, // EMB + Embedded LC2 (audio seq 2)
|
||||||
|
{0x01U, 0x70U, 0x00U, 0x31U, 0x40U, 0x07U, 0x40U}, // EMB + Embedded LC3 (audio seq 3)
|
||||||
|
{0x01U, 0x50U, 0xA1U, 0x71U, 0xD1U, 0x70U, 0x70U}, // EMB + Embedded LC4 (audio seq 4)
|
||||||
|
{0x01U, 0x10U, 0x00U, 0x00U, 0x00U, 0x0EU, 0x20U}}; // EMB (audio seq 5)
|
||||||
|
|
||||||
|
// Embedded LC MS: CC: 1, srcID: 1, dstID: TG9
|
||||||
|
const uint8_t SYNCEMB_DMO1K[6][7] = {
|
||||||
|
{0x07U, 0xF7U, 0xD5U, 0xDDU, 0x57U, 0xDFU, 0xD0U}, // MS VOICE SYNC (audio seq 0)
|
||||||
|
{0x01U, 0x30U, 0x00U, 0x00U, 0x90U, 0x09U, 0x10U}, // EMB + Embedded LC1 (audio seq 1)
|
||||||
|
{0x01U, 0x70U, 0x00U, 0x90U, 0x00U, 0x07U, 0x40U}, // EMB + Embedded LC2 (audio seq 2)
|
||||||
|
{0x01U, 0x70U, 0x00U, 0x31U, 0x40U, 0x07U, 0x40U}, // EMB + Embedded LC3 (audio seq 3)
|
||||||
|
{0x01U, 0x50U, 0xA1U, 0x71U, 0xD1U, 0x70U, 0x70U}, // EMB + Embedded LC4 (audio seq 4)
|
||||||
|
{0x01U, 0x10U, 0x00U, 0x00U, 0x00U, 0x0EU, 0x20U}}; // EMB (audio seq 5)
|
||||||
|
|
||||||
|
// Short LC:
|
||||||
|
// TS1: dstID: 0, ACTIVITY_NONE
|
||||||
|
// TS2: dstID: TG9, ACTIVITY_VOICE
|
||||||
|
const uint8_t SHORTLC_1K[] = {0x33U, 0x3AU, 0xA0U, 0x30U, 0x00U, 0x55U, 0xA6U, 0x5FU, 0x50U};
|
||||||
|
|
||||||
|
CCalDMR::CCalDMR() :
|
||||||
|
m_transmit(false),
|
||||||
|
m_state(DMRCAL1K_IDLE),
|
||||||
|
m_frame_start(0U),
|
||||||
|
m_dmr1k(),
|
||||||
|
m_audioSeq(0)
|
||||||
|
{
|
||||||
|
::memcpy(m_dmr1k, VOICE_1K, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCalDMR::process()
|
||||||
|
{
|
||||||
|
switch (m_modemState) {
|
||||||
|
case STATE_DMRCAL:
|
||||||
|
case STATE_LFCAL:
|
||||||
|
if (m_transmit) {
|
||||||
|
dmrTX.setCal(true);
|
||||||
|
dmrTX.process();
|
||||||
|
} else {
|
||||||
|
dmrTX.setCal(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STATE_DMRCAL1K:
|
||||||
|
dmr1kcal();
|
||||||
|
break;
|
||||||
|
case STATE_DMRDMO1K:
|
||||||
|
dmrdmo1k();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCalDMR::createData1k(uint8_t n)
|
||||||
|
{
|
||||||
|
for(uint8_t i = 0; i < 5U; i++)
|
||||||
|
m_dmr1k[i + 15U] = SYNCEMB_1K[n][i + 1U];
|
||||||
|
|
||||||
|
m_dmr1k[14U] &= 0xF0U;
|
||||||
|
m_dmr1k[20U] &= 0x0FU;
|
||||||
|
m_dmr1k[14U] |= SYNCEMB_1K[n][0] & 0x0FU;
|
||||||
|
m_dmr1k[20U] |= SYNCEMB_1K[n][6] & 0xF0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCalDMR::createDataDMO1k(uint8_t n)
|
||||||
|
{
|
||||||
|
for(uint8_t i = 0; i < 5U; i++)
|
||||||
|
m_dmr1k[i + 15U] = SYNCEMB_DMO1K[n][i + 1U];
|
||||||
|
|
||||||
|
m_dmr1k[14U] &= 0xF0U;
|
||||||
|
m_dmr1k[20U] &= 0x0FU;
|
||||||
|
m_dmr1k[14U] |= SYNCEMB_DMO1K[n][0] & 0x0FU;
|
||||||
|
m_dmr1k[20U] |= SYNCEMB_DMO1K[n][6] & 0xF0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCalDMR::dmr1kcal()
|
||||||
|
{
|
||||||
|
dmrTX.process();
|
||||||
|
|
||||||
|
uint16_t space = dmrTX.getSpace2();
|
||||||
|
if (space < 1U)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (m_state) {
|
||||||
|
case DMRCAL1K_VH:
|
||||||
|
dmrTX.setColorCode(1U);
|
||||||
|
dmrTX.writeShortLC(SHORTLC_1K, 9U);
|
||||||
|
dmrTX.writeData2(VH_1K, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||||
|
dmrTX.setStart(true);
|
||||||
|
m_state = DMRCAL1K_VOICE;
|
||||||
|
break;
|
||||||
|
case DMRCAL1K_VOICE:
|
||||||
|
createData1k(m_audioSeq);
|
||||||
|
dmrTX.writeData2(m_dmr1k, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||||
|
if(m_audioSeq == 5U) {
|
||||||
|
m_audioSeq = 0U;
|
||||||
|
if(!m_transmit)
|
||||||
|
m_state = DMRCAL1K_VT;
|
||||||
|
} else
|
||||||
|
m_audioSeq++;
|
||||||
|
break;
|
||||||
|
case DMRCAL1K_VT:
|
||||||
|
dmrTX.writeData2(VT_1K, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||||
|
m_frame_start = dmrTX.getFrameCount();
|
||||||
|
m_state = DMRCAL1K_WAIT;
|
||||||
|
break;
|
||||||
|
case DMRCAL1K_WAIT:
|
||||||
|
if (dmrTX.getFrameCount() > (m_frame_start + 30U)) {
|
||||||
|
dmrTX.setStart(false);
|
||||||
|
dmrTX.resetFifo2();
|
||||||
|
m_audioSeq = 0U;
|
||||||
|
m_state = DMRCAL1K_IDLE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
m_state = DMRCAL1K_IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCalDMR::dmrdmo1k()
|
||||||
|
{
|
||||||
|
dmrDMOTX.process();
|
||||||
|
|
||||||
|
uint16_t space = dmrDMOTX.getSpace();
|
||||||
|
if (space < 1U)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (m_state) {
|
||||||
|
case DMRCAL1K_VH:
|
||||||
|
dmrDMOTX.writeData(VH_DMO1K, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||||
|
m_state = DMRCAL1K_VOICE;
|
||||||
|
break;
|
||||||
|
case DMRCAL1K_VOICE:
|
||||||
|
createDataDMO1k(m_audioSeq);
|
||||||
|
dmrDMOTX.writeData(m_dmr1k, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||||
|
if(m_audioSeq == 5U) {
|
||||||
|
m_audioSeq = 0U;
|
||||||
|
if(!m_transmit)
|
||||||
|
m_state = DMRCAL1K_VT;
|
||||||
|
} else
|
||||||
|
m_audioSeq++;
|
||||||
|
break;
|
||||||
|
case DMRCAL1K_VT:
|
||||||
|
dmrDMOTX.writeData(VT_DMO1K, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||||
|
m_state = DMRCAL1K_IDLE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
m_state = DMRCAL1K_IDLE;
|
||||||
|
m_audioSeq = 0U;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CCalDMR::write(const uint8_t* data, uint16_t length)
|
||||||
|
{
|
||||||
|
if (length != 1U)
|
||||||
|
return 4U;
|
||||||
|
|
||||||
|
m_transmit = data[0U] == 1U;
|
||||||
|
|
||||||
|
if(m_transmit && m_state == DMRCAL1K_IDLE && (m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K))
|
||||||
|
m_state = DMRCAL1K_VH;
|
||||||
|
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2015,2020 by Jonathan Naylor G4KLX
|
||||||
|
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
|
#if !defined(CALDMR_H)
|
||||||
|
#define CALDMR_H
|
||||||
|
|
||||||
|
#include "DMRDefines.h"
|
||||||
|
|
||||||
|
enum DMRCAL1K {
|
||||||
|
DMRCAL1K_IDLE,
|
||||||
|
DMRCAL1K_VH,
|
||||||
|
DMRCAL1K_VOICE,
|
||||||
|
DMRCAL1K_VT,
|
||||||
|
DMRCAL1K_WAIT
|
||||||
|
};
|
||||||
|
|
||||||
|
class CCalDMR {
|
||||||
|
public:
|
||||||
|
CCalDMR();
|
||||||
|
|
||||||
|
void process();
|
||||||
|
void dmr1kcal();
|
||||||
|
void dmrdmo1k();
|
||||||
|
void createData1k(uint8_t n);
|
||||||
|
void createDataDMO1k(uint8_t n);
|
||||||
|
|
||||||
|
uint8_t write(const uint8_t* data, uint16_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_transmit;
|
||||||
|
DMRCAL1K m_state;
|
||||||
|
uint32_t m_frame_start;
|
||||||
|
uint8_t m_dmr1k[DMR_FRAME_LENGTH_BYTES + 1U];
|
||||||
|
uint8_t m_audioSeq;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2017,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,9 +16,10 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "DMRDMORX.h"
|
#include "DMRDMORX.h"
|
||||||
#include "DMRSlotType.h"
|
#include "DMRSlotType.h"
|
||||||
@@ -59,8 +60,7 @@ m_colorCode(0U),
|
|||||||
m_state(DMORXS_NONE),
|
m_state(DMORXS_NONE),
|
||||||
m_n(0U),
|
m_n(0U),
|
||||||
m_type(0U),
|
m_type(0U),
|
||||||
m_rssiCount(0U),
|
m_rssi()
|
||||||
m_rssi(0U)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +73,6 @@ void CDMRDMORX::reset()
|
|||||||
m_state = DMORXS_NONE;
|
m_state = DMORXS_NONE;
|
||||||
m_startPtr = 0U;
|
m_startPtr = 0U;
|
||||||
m_endPtr = NOENDPTR;
|
m_endPtr = NOENDPTR;
|
||||||
m_rssiCount = 0U;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDMRDMORX::samples(const q15_t* samples, const uint16_t* rssi, uint8_t length)
|
void CDMRDMORX::samples(const q15_t* samples, const uint16_t* rssi, uint8_t length)
|
||||||
@@ -89,6 +88,7 @@ void CDMRDMORX::samples(const q15_t* samples, const uint16_t* rssi, uint8_t leng
|
|||||||
bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
||||||
{
|
{
|
||||||
m_buffer[m_dataPtr] = sample;
|
m_buffer[m_dataPtr] = sample;
|
||||||
|
m_rssi[m_dataPtr] = rssi;
|
||||||
|
|
||||||
m_bitBuffer[m_bitPtr] <<= 1;
|
m_bitBuffer[m_bitPtr] <<= 1;
|
||||||
if (sample < 0)
|
if (sample < 0)
|
||||||
@@ -97,9 +97,6 @@ bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
|||||||
if (m_state == DMORXS_NONE) {
|
if (m_state == DMORXS_NONE) {
|
||||||
correlateSync(true);
|
correlateSync(true);
|
||||||
} else {
|
} else {
|
||||||
// Grab the RSSI data during the frame
|
|
||||||
if (m_state == DMORXS_VOICE && m_dataPtr == m_syncPtr)
|
|
||||||
m_rssi = rssi;
|
|
||||||
|
|
||||||
uint16_t min = m_syncPtr + DMO_BUFFER_LENGTH_SAMPLES - 1U;
|
uint16_t min = m_syncPtr + DMO_BUFFER_LENGTH_SAMPLES - 1U;
|
||||||
uint16_t max = m_syncPtr + 1U;
|
uint16_t max = m_syncPtr + 1U;
|
||||||
@@ -148,7 +145,7 @@ bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
|||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
case DT_DATA_HEADER:
|
case DT_DATA_HEADER:
|
||||||
DEBUG4("DMRDMORX: data header found pos/centre/threshold", m_syncPtr, centre, threshold);
|
DEBUG4("DMRDMORX: data header found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
m_state = DMORXS_DATA;
|
m_state = DMORXS_DATA;
|
||||||
m_type = 0x00U;
|
m_type = 0x00U;
|
||||||
break;
|
break;
|
||||||
@@ -157,32 +154,32 @@ bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
|||||||
case DT_RATE_1_DATA:
|
case DT_RATE_1_DATA:
|
||||||
if (m_state == DMORXS_DATA) {
|
if (m_state == DMORXS_DATA) {
|
||||||
DEBUG4("DMRDMORX: data payload found pos/centre/threshold", m_syncPtr, centre, threshold);
|
DEBUG4("DMRDMORX: data payload found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
m_type = dataType;
|
m_type = dataType;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DT_VOICE_LC_HEADER:
|
case DT_VOICE_LC_HEADER:
|
||||||
DEBUG4("DMRDMORX: voice header found pos/centre/threshold", m_syncPtr, centre, threshold);
|
DEBUG4("DMRDMORX: voice header found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
m_state = DMORXS_VOICE;
|
m_state = DMORXS_VOICE;
|
||||||
break;
|
break;
|
||||||
case DT_VOICE_PI_HEADER:
|
case DT_VOICE_PI_HEADER:
|
||||||
if (m_state == DMORXS_VOICE) {
|
if (m_state == DMORXS_VOICE) {
|
||||||
DEBUG4("DMRDMORX: voice pi header found pos/centre/threshold", m_syncPtr, centre, threshold);
|
DEBUG4("DMRDMORX: voice pi header found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
}
|
}
|
||||||
m_state = DMORXS_VOICE;
|
m_state = DMORXS_VOICE;
|
||||||
break;
|
break;
|
||||||
case DT_TERMINATOR_WITH_LC:
|
case DT_TERMINATOR_WITH_LC:
|
||||||
if (m_state == DMORXS_VOICE) {
|
if (m_state == DMORXS_VOICE) {
|
||||||
DEBUG4("DMRDMORX: voice terminator found pos/centre/threshold", m_syncPtr, centre, threshold);
|
DEBUG4("DMRDMORX: voice terminator found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: // DT_CSBK
|
default: // DT_CSBK
|
||||||
DEBUG4("DMRDMORX: csbk found pos/centre/threshold", m_syncPtr, centre, threshold);
|
DEBUG4("DMRDMORX: csbk found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
reset();
|
reset();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -190,22 +187,7 @@ bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
|||||||
} else if (m_control == CONTROL_VOICE) {
|
} else if (m_control == CONTROL_VOICE) {
|
||||||
// Voice sync
|
// Voice sync
|
||||||
DEBUG4("DMRDMORX: voice sync found pos/centre/threshold", m_syncPtr, centre, threshold);
|
DEBUG4("DMRDMORX: voice sync found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||||
#if defined(SEND_RSSI_DATA)
|
writeRSSIData(frame);
|
||||||
// Send RSSI data approximately every second
|
|
||||||
if (m_rssiCount == 2U) {
|
|
||||||
frame[34U] = (m_rssi >> 8) & 0xFFU;
|
|
||||||
frame[35U] = (m_rssi >> 0) & 0xFFU;
|
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 3U);
|
|
||||||
} else {
|
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_rssiCount++;
|
|
||||||
if (m_rssiCount >= 16U)
|
|
||||||
m_rssiCount = 0U;
|
|
||||||
#else
|
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
|
||||||
#endif
|
|
||||||
m_state = DMORXS_VOICE;
|
m_state = DMORXS_VOICE;
|
||||||
m_syncCount = 0U;
|
m_syncCount = 0U;
|
||||||
m_n = 0U;
|
m_n = 0U;
|
||||||
@@ -225,26 +207,12 @@ bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
|||||||
} else {
|
} else {
|
||||||
frame[0U] = ++m_n;
|
frame[0U] = ++m_n;
|
||||||
}
|
}
|
||||||
#if defined(SEND_RSSI_DATA)
|
|
||||||
// Send RSSI data approximately every second
|
|
||||||
if (m_rssiCount == 2U) {
|
|
||||||
frame[34U] = (m_rssi >> 8) & 0xFFU;
|
|
||||||
frame[35U] = (m_rssi >> 0) & 0xFFU;
|
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 3U);
|
|
||||||
} else {
|
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_rssiCount++;
|
|
||||||
if (m_rssiCount >= 16U)
|
|
||||||
m_rssiCount = 0U;
|
|
||||||
#else
|
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||||
#endif
|
|
||||||
} else if (m_state == DMORXS_DATA) {
|
} else if (m_state == DMORXS_DATA) {
|
||||||
if (m_type != 0x00U) {
|
if (m_type != 0x00U) {
|
||||||
frame[0U] = CONTROL_DATA | m_type;
|
frame[0U] = CONTROL_DATA | m_type;
|
||||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,16 +235,13 @@ bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
|||||||
|
|
||||||
void CDMRDMORX::correlateSync(bool first)
|
void CDMRDMORX::correlateSync(bool first)
|
||||||
{
|
{
|
||||||
uint8_t errs1 = countBits32((m_bitBuffer[m_bitPtr] & DMR_SYNC_SYMBOLS_MASK) ^ DMR_S2_DATA_SYNC_SYMBOLS);
|
uint8_t errs = countBits32((m_bitBuffer[m_bitPtr] & DMR_SYNC_SYMBOLS_MASK) ^ DMR_MS_DATA_SYNC_SYMBOLS);
|
||||||
uint8_t errs2 = countBits32((m_bitBuffer[m_bitPtr] & DMR_SYNC_SYMBOLS_MASK) ^ DMR_MS_DATA_SYNC_SYMBOLS);
|
|
||||||
|
|
||||||
// The voice sync is the complement of the data sync
|
// The voice sync is the complement of the data sync
|
||||||
bool data1 = (errs1 <= MAX_SYNC_SYMBOLS_ERRS);
|
bool data = (errs <= MAX_SYNC_SYMBOLS_ERRS);
|
||||||
bool data2 = (errs2 <= MAX_SYNC_SYMBOLS_ERRS);
|
bool voice = (errs >= (DMR_SYNC_LENGTH_SYMBOLS - MAX_SYNC_SYMBOLS_ERRS));
|
||||||
bool voice1 = (errs1 >= (DMR_SYNC_LENGTH_SYMBOLS - MAX_SYNC_SYMBOLS_ERRS));
|
|
||||||
bool voice2 = (errs2 >= (DMR_SYNC_LENGTH_SYMBOLS - MAX_SYNC_SYMBOLS_ERRS));
|
|
||||||
|
|
||||||
if (data1 || data2 || voice1 || voice2) {
|
if (data || voice) {
|
||||||
uint16_t ptr = m_dataPtr + DMO_BUFFER_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES + DMR_RADIO_SYMBOL_LENGTH;
|
uint16_t ptr = m_dataPtr + DMO_BUFFER_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES + DMR_RADIO_SYMBOL_LENGTH;
|
||||||
if (ptr >= DMO_BUFFER_LENGTH_SAMPLES)
|
if (ptr >= DMO_BUFFER_LENGTH_SAMPLES)
|
||||||
ptr -= DMO_BUFFER_LENGTH_SAMPLES;
|
ptr -= DMO_BUFFER_LENGTH_SAMPLES;
|
||||||
@@ -285,23 +250,34 @@ void CDMRDMORX::correlateSync(bool first)
|
|||||||
q15_t min = 16000;
|
q15_t min = 16000;
|
||||||
q15_t max = -16000;
|
q15_t max = -16000;
|
||||||
|
|
||||||
uint32_t mask = 0x00800000U;
|
for (uint8_t i = 0U; i < DMR_SYNC_LENGTH_SYMBOLS; i++) {
|
||||||
for (uint8_t i = 0U; i < DMR_SYNC_LENGTH_SYMBOLS; i++, mask >>= 1) {
|
q15_t val = m_buffer[ptr];
|
||||||
bool b;
|
|
||||||
if (data1 || voice1)
|
if (val > max)
|
||||||
b = (DMR_S2_DATA_SYNC_SYMBOLS & mask) == mask;
|
max = val;
|
||||||
|
if (val < min)
|
||||||
|
min = val;
|
||||||
|
|
||||||
|
int8_t corrVal;
|
||||||
|
if (data)
|
||||||
|
corrVal = DMR_MS_DATA_SYNC_SYMBOLS_VALUES[i];
|
||||||
else
|
else
|
||||||
b = (DMR_MS_DATA_SYNC_SYMBOLS & mask) == mask;
|
corrVal = DMR_MS_VOICE_SYNC_SYMBOLS_VALUES[i];
|
||||||
|
|
||||||
if (m_buffer[ptr] > max)
|
switch (corrVal) {
|
||||||
max = m_buffer[ptr];
|
case +3:
|
||||||
if (m_buffer[ptr] < min)
|
corr -= (val + val + val);
|
||||||
min = m_buffer[ptr];
|
break;
|
||||||
|
case +1:
|
||||||
if (data1 || data2)
|
corr -= val;
|
||||||
corr += b ? -m_buffer[ptr] : m_buffer[ptr];
|
break;
|
||||||
else // if (voice)
|
case -1:
|
||||||
corr += b ? m_buffer[ptr] : -m_buffer[ptr];
|
corr += val;
|
||||||
|
break;
|
||||||
|
default: // -3
|
||||||
|
corr += (val + val + val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
ptr += DMR_RADIO_SYMBOL_LENGTH;
|
ptr += DMR_RADIO_SYMBOL_LENGTH;
|
||||||
if (ptr >= DMO_BUFFER_LENGTH_SAMPLES)
|
if (ptr >= DMO_BUFFER_LENGTH_SAMPLES)
|
||||||
@@ -321,21 +297,16 @@ void CDMRDMORX::correlateSync(bool first)
|
|||||||
|
|
||||||
samplesToBits(ptr, DMR_SYNC_LENGTH_SYMBOLS, sync, 4U, centre, threshold);
|
samplesToBits(ptr, DMR_SYNC_LENGTH_SYMBOLS, sync, 4U, centre, threshold);
|
||||||
|
|
||||||
if (data1 || data2) {
|
if (data) {
|
||||||
uint8_t errs = 0U;
|
uint8_t errs = 0U;
|
||||||
for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++) {
|
for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++)
|
||||||
if (data1)
|
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_DATA_SYNC_BYTES[i]);
|
||||||
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_S2_DATA_SYNC_BYTES[i]);
|
|
||||||
else
|
|
||||||
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_DATA_SYNC_BYTES[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errs <= MAX_SYNC_BYTES_ERRS) {
|
if (errs <= MAX_SYNC_BYTES_ERRS) {
|
||||||
if (first) {
|
if (first) {
|
||||||
m_threshold[0U] = m_threshold[1U] = m_threshold[2U] = m_threshold[3U] = threshold;
|
m_threshold[0U] = m_threshold[1U] = m_threshold[2U] = m_threshold[3U] = threshold;
|
||||||
m_centre[0U] = m_centre[1U] = m_centre[2U] = m_centre[3U] = centre;
|
m_centre[0U] = m_centre[1U] = m_centre[2U] = m_centre[3U] = centre;
|
||||||
m_averagePtr = 0U;
|
m_averagePtr = 0U;
|
||||||
m_rssiCount = 0U;
|
|
||||||
} else {
|
} else {
|
||||||
m_threshold[m_averagePtr] = threshold;
|
m_threshold[m_averagePtr] = threshold;
|
||||||
m_centre[m_averagePtr] = centre;
|
m_centre[m_averagePtr] = centre;
|
||||||
@@ -359,19 +330,14 @@ void CDMRDMORX::correlateSync(bool first)
|
|||||||
}
|
}
|
||||||
} else { // if (voice1 || voice2)
|
} else { // if (voice1 || voice2)
|
||||||
uint8_t errs = 0U;
|
uint8_t errs = 0U;
|
||||||
for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++) {
|
for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++)
|
||||||
if (voice1)
|
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_VOICE_SYNC_BYTES[i]);
|
||||||
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_S2_VOICE_SYNC_BYTES[i]);
|
|
||||||
else
|
|
||||||
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_VOICE_SYNC_BYTES[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errs <= MAX_SYNC_BYTES_ERRS) {
|
if (errs <= MAX_SYNC_BYTES_ERRS) {
|
||||||
if (first) {
|
if (first) {
|
||||||
m_threshold[0U] = m_threshold[1U] = m_threshold[2U] = m_threshold[3U] = threshold;
|
m_threshold[0U] = m_threshold[1U] = m_threshold[2U] = m_threshold[3U] = threshold;
|
||||||
m_centre[0U] = m_centre[1U] = m_centre[2U] = m_centre[3U] = centre;
|
m_centre[0U] = m_centre[1U] = m_centre[2U] = m_centre[3U] = centre;
|
||||||
m_averagePtr = 0U;
|
m_averagePtr = 0U;
|
||||||
m_rssiCount = 0U;
|
|
||||||
} else {
|
} else {
|
||||||
m_threshold[m_averagePtr] = threshold;
|
m_threshold[m_averagePtr] = threshold;
|
||||||
m_centre[m_averagePtr] = centre;
|
m_centre[m_averagePtr] = centre;
|
||||||
@@ -436,3 +402,30 @@ void CDMRDMORX::setColorCode(uint8_t colorCode)
|
|||||||
m_colorCode = colorCode;
|
m_colorCode = colorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDMRDMORX::writeRSSIData(uint8_t* frame)
|
||||||
|
{
|
||||||
|
#if defined(SEND_RSSI_DATA)
|
||||||
|
// Calculate RSSI average over a burst period. We don't take into account 2.5 ms at the beginning and 2.5 ms at the end
|
||||||
|
uint16_t start = m_startPtr + DMR_SYNC_LENGTH_SAMPLES / 2U;
|
||||||
|
|
||||||
|
uint32_t accum = 0U;
|
||||||
|
for (uint16_t i = 0U; i < (DMR_FRAME_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES); i++) {
|
||||||
|
accum += m_rssi[start];
|
||||||
|
|
||||||
|
start++;
|
||||||
|
if (start >= DMO_BUFFER_LENGTH_SAMPLES)
|
||||||
|
start -= DMO_BUFFER_LENGTH_SAMPLES;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t avg = accum / (DMR_FRAME_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES);
|
||||||
|
frame[34U] = (avg >> 8) & 0xFFU;
|
||||||
|
frame[35U] = (avg >> 0) & 0xFFU;
|
||||||
|
|
||||||
|
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 3U);
|
||||||
|
#else
|
||||||
|
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2016,2017,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,10 +16,13 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#if !defined(DMRDMORX_H)
|
#if !defined(DMRDMORX_H)
|
||||||
#define DMRDMORX_H
|
#define DMRDMORX_H
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "DMRDefines.h"
|
#include "DMRDefines.h"
|
||||||
|
|
||||||
const uint16_t DMO_BUFFER_LENGTH_SAMPLES = 1440U; // 60ms at 24 kHz
|
const uint16_t DMO_BUFFER_LENGTH_SAMPLES = 1440U; // 60ms at 24 kHz
|
||||||
@@ -58,13 +61,15 @@ private:
|
|||||||
DMORX_STATE m_state;
|
DMORX_STATE m_state;
|
||||||
uint8_t m_n;
|
uint8_t m_n;
|
||||||
uint8_t m_type;
|
uint8_t m_type;
|
||||||
uint16_t m_rssiCount;
|
uint16_t m_rssi[DMO_BUFFER_LENGTH_SAMPLES];
|
||||||
uint16_t m_rssi;
|
|
||||||
|
|
||||||
bool processSample(q15_t sample, uint16_t rssi);
|
bool processSample(q15_t sample, uint16_t rssi);
|
||||||
void correlateSync(bool first);
|
void correlateSync(bool first);
|
||||||
void samplesToBits(uint16_t start, uint8_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
void samplesToBits(uint16_t start, uint8_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
||||||
|
void writeRSSIData(uint8_t* frame);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2017,2020 by Jonathan Naylor G4KLX
|
||||||
|
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||||
|
* Copyright (C) 2017 by Andy Uribe CA6JAU
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "DMRSlotType.h"
|
||||||
|
|
||||||
|
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||||
|
static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172, 237, 1927, 3120, 3073, 1447, -1431, -4544, -6442,
|
||||||
|
-5735, -1633, 5651, 14822, 23810, 30367, 32767, 30367, 23810, 14822, 5651, -1633, -5735, -6442,
|
||||||
|
-4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5
|
||||||
|
const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
|
||||||
|
|
||||||
|
const q15_t DMR_LEVELA = 1362;
|
||||||
|
const q15_t DMR_LEVELB = 454;
|
||||||
|
const q15_t DMR_LEVELC = -454;
|
||||||
|
const q15_t DMR_LEVELD = -1362;
|
||||||
|
|
||||||
|
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||||
|
|
||||||
|
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
||||||
|
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||||
|
|
||||||
|
// PR FILL pattern
|
||||||
|
const uint8_t PR_FILL[] =
|
||||||
|
{0x63U, 0xEAU, 0x00U, 0x76U, 0x6CU, 0x76U, 0xC4U, 0x52U, 0xC8U, 0x78U,
|
||||||
|
0x09U, 0x2DU, 0xB8U, 0x79U, 0x27U, 0x57U, 0x9BU, 0x31U, 0xBCU, 0x3EU,
|
||||||
|
0xEAU, 0x45U, 0xC3U, 0x30U, 0x49U, 0x17U, 0x93U, 0xAEU, 0x8BU, 0x6DU,
|
||||||
|
0xA4U, 0xA5U, 0xADU, 0xA2U, 0xF1U, 0x35U, 0xB5U, 0x3CU, 0x1EU};
|
||||||
|
|
||||||
|
const uint8_t DMR_SYNC = 0x5FU;
|
||||||
|
|
||||||
|
CDMRDMOTX::CDMRDMOTX() :
|
||||||
|
m_fifo(),
|
||||||
|
m_modFilter(),
|
||||||
|
m_modState(),
|
||||||
|
m_poBuffer(),
|
||||||
|
m_poLen(0U),
|
||||||
|
m_poPtr(0U),
|
||||||
|
m_txDelay(240U) // 200ms
|
||||||
|
{
|
||||||
|
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||||
|
|
||||||
|
m_modFilter.L = DMR_RADIO_SYMBOL_LENGTH;
|
||||||
|
m_modFilter.phaseLength = RRC_0_2_FILTER_PHASE_LEN;
|
||||||
|
m_modFilter.pCoeffs = RRC_0_2_FILTER;
|
||||||
|
m_modFilter.pState = m_modState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDMRDMOTX::process()
|
||||||
|
{
|
||||||
|
if (m_poLen == 0U && m_fifo.getData() > 0U) {
|
||||||
|
if (!m_tx) {
|
||||||
|
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||||
|
m_poBuffer[i] = DMR_SYNC;
|
||||||
|
|
||||||
|
m_poLen = m_txDelay;
|
||||||
|
} else {
|
||||||
|
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
|
||||||
|
m_fifo.get(m_poBuffer[i]);
|
||||||
|
|
||||||
|
for (unsigned int i = 0U; i < 39U; i++)
|
||||||
|
m_poBuffer[i + DMR_FRAME_LENGTH_BYTES] = PR_FILL[i];
|
||||||
|
|
||||||
|
m_poLen = 72U;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_poPtr = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_poLen > 0U) {
|
||||||
|
uint16_t space = io.getSpace();
|
||||||
|
|
||||||
|
while (space > (4U * DMR_RADIO_SYMBOL_LENGTH)) {
|
||||||
|
uint8_t c = m_poBuffer[m_poPtr++];
|
||||||
|
|
||||||
|
writeByte(c);
|
||||||
|
|
||||||
|
space -= 4U * DMR_RADIO_SYMBOL_LENGTH;
|
||||||
|
|
||||||
|
if (m_poPtr >= m_poLen) {
|
||||||
|
m_poPtr = 0U;
|
||||||
|
m_poLen = 0U;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CDMRDMOTX::writeData(const uint8_t* data, uint16_t length)
|
||||||
|
{
|
||||||
|
if (length != (DMR_FRAME_LENGTH_BYTES + 1U))
|
||||||
|
return 4U;
|
||||||
|
|
||||||
|
uint16_t space = m_fifo.getSpace();
|
||||||
|
if (space < DMR_FRAME_LENGTH_BYTES)
|
||||||
|
return 5U;
|
||||||
|
|
||||||
|
for (uint8_t i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
|
||||||
|
m_fifo.put(data[i + 1U]);
|
||||||
|
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDMRDMOTX::writeByte(uint8_t c)
|
||||||
|
{
|
||||||
|
q15_t inBuffer[4U];
|
||||||
|
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U];
|
||||||
|
|
||||||
|
const uint8_t MASK = 0xC0U;
|
||||||
|
|
||||||
|
for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
|
||||||
|
switch (c & MASK) {
|
||||||
|
case 0xC0U:
|
||||||
|
inBuffer[i] = DMR_LEVELA;
|
||||||
|
break;
|
||||||
|
case 0x80U:
|
||||||
|
inBuffer[i] = DMR_LEVELB;
|
||||||
|
break;
|
||||||
|
case 0x00U:
|
||||||
|
inBuffer[i] = DMR_LEVELC;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
inBuffer[i] = DMR_LEVELD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
|
||||||
|
|
||||||
|
io.write(STATE_DMR, outBuffer, DMR_RADIO_SYMBOL_LENGTH * 4U);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CDMRDMOTX::getSpace() const
|
||||||
|
{
|
||||||
|
return m_fifo.getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDMRDMOTX::setTXDelay(uint8_t delay)
|
||||||
|
{
|
||||||
|
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay
|
||||||
|
|
||||||
|
if (m_txDelay > 1200U)
|
||||||
|
m_txDelay = 1200U;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2016,2017,2020 by Jonathan Naylor G4KLX
|
||||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
@@ -17,38 +17,42 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#if !defined(DMRDMOTX_H)
|
#if !defined(DMRDMOTX_H)
|
||||||
#define DMRDMOTX_H
|
#define DMRDMOTX_H
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "DMRDefines.h"
|
#include "DMRDefines.h"
|
||||||
|
|
||||||
#include "SerialRB.h"
|
#include "RingBuffer.h"
|
||||||
|
|
||||||
class CDMRDMOTX {
|
class CDMRDMOTX {
|
||||||
public:
|
public:
|
||||||
CDMRDMOTX();
|
CDMRDMOTX();
|
||||||
|
|
||||||
uint8_t writeData(const uint8_t* data, uint8_t length);
|
uint8_t writeData(const uint8_t* data, uint16_t length);
|
||||||
|
|
||||||
void process();
|
void process();
|
||||||
|
|
||||||
void setTXDelay(uint8_t delay);
|
void setTXDelay(uint8_t delay);
|
||||||
|
|
||||||
uint16_t getSpace() const;
|
uint8_t getSpace() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CSerialRB m_fifo;
|
CRingBuffer<uint8_t> m_fifo;
|
||||||
arm_fir_instance_q15 m_modFilter;
|
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||||
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
|
||||||
uint8_t m_poBuffer[80U];
|
uint8_t m_poBuffer[1200U];
|
||||||
uint16_t m_poLen;
|
uint16_t m_poLen;
|
||||||
uint16_t m_poPtr;
|
uint16_t m_poPtr;
|
||||||
uint16_t m_txDelay;
|
uint32_t m_txDelay;
|
||||||
uint32_t m_count;
|
|
||||||
|
|
||||||
void writeByte(uint8_t c);
|
void writeByte(uint8_t c);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -61,32 +61,32 @@ const uint8_t DMR_MS_DATA_SYNC_BYTES[] = {0x0DU, 0x5DU, 0x7FU, 0x77U, 0xFDU, 0
|
|||||||
const uint8_t DMR_MS_VOICE_SYNC_BYTES[] = {0x07U, 0xF7U, 0xD5U, 0xDDU, 0x57U, 0xDFU, 0xD0U};
|
const uint8_t DMR_MS_VOICE_SYNC_BYTES[] = {0x07U, 0xF7U, 0xD5U, 0xDDU, 0x57U, 0xDFU, 0xD0U};
|
||||||
const uint8_t DMR_BS_DATA_SYNC_BYTES[] = {0x0DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD0U};
|
const uint8_t DMR_BS_DATA_SYNC_BYTES[] = {0x0DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD0U};
|
||||||
const uint8_t DMR_BS_VOICE_SYNC_BYTES[] = {0x07U, 0x55U, 0xFDU, 0x7DU, 0xF7U, 0x5FU, 0x70U};
|
const uint8_t DMR_BS_VOICE_SYNC_BYTES[] = {0x07U, 0x55U, 0xFDU, 0x7DU, 0xF7U, 0x5FU, 0x70U};
|
||||||
const uint8_t DMR_S1_DATA_SYNC_BYTES[] = {0x0FU, 0x7FU, 0xDDU, 0x5DU, 0xDFU, 0xD5U, 0x50U};
|
|
||||||
const uint8_t DMR_S1_VOICE_SYNC_BYTES[] = {0x05U, 0xD5U, 0x77U, 0xF7U, 0x75U, 0x7FU, 0xF0U};
|
|
||||||
const uint8_t DMR_S2_DATA_SYNC_BYTES[] = {0x0DU, 0x75U, 0x57U, 0xF5U, 0xFFU, 0x7FU, 0x50U};
|
|
||||||
const uint8_t DMR_S2_VOICE_SYNC_BYTES[] = {0x07U, 0xDFU, 0xFDU, 0x5FU, 0x55U, 0xD5U, 0xF0U};
|
|
||||||
const uint8_t DMR_SYNC_BYTES_MASK[] = {0x0FU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xF0U};
|
const uint8_t DMR_SYNC_BYTES_MASK[] = {0x0FU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xF0U};
|
||||||
|
|
||||||
const uint64_t DMR_MS_DATA_SYNC_BITS = 0x0000D5D7F77FD757U;
|
const uint64_t DMR_MS_DATA_SYNC_BITS = 0x0000D5D7F77FD757U;
|
||||||
const uint64_t DMR_MS_VOICE_SYNC_BITS = 0x00007F7D5DD57DFDU;
|
const uint64_t DMR_MS_VOICE_SYNC_BITS = 0x00007F7D5DD57DFDU;
|
||||||
const uint64_t DMR_BS_DATA_SYNC_BITS = 0x0000DFF57D75DF5DU;
|
const uint64_t DMR_BS_DATA_SYNC_BITS = 0x0000DFF57D75DF5DU;
|
||||||
const uint64_t DMR_BS_VOICE_SYNC_BITS = 0x0000755FD7DF75F7U;
|
const uint64_t DMR_BS_VOICE_SYNC_BITS = 0x0000755FD7DF75F7U;
|
||||||
const uint64_t DMR_S1_DATA_SYNC_BITS = 0x0000F7FDD5DDFD55U;
|
|
||||||
const uint64_t DMR_S1_VOICE_SYNC_BITS = 0x00005D577F7757FFU;
|
|
||||||
const uint64_t DMR_S2_DATA_SYNC_BITS = 0x0000D7557F5FF7F5U;
|
|
||||||
const uint64_t DMR_S2_VOICE_SYNC_BITS = 0x00007DFFD5F55D5FU;
|
|
||||||
const uint64_t DMR_SYNC_BITS_MASK = 0x0000FFFFFFFFFFFFU;
|
const uint64_t DMR_SYNC_BITS_MASK = 0x0000FFFFFFFFFFFFU;
|
||||||
|
|
||||||
const uint32_t DMR_MS_DATA_SYNC_SYMBOLS = 0x0076286EU;
|
const uint32_t DMR_MS_DATA_SYNC_SYMBOLS = 0x0076286EU;
|
||||||
const uint32_t DMR_MS_VOICE_SYNC_SYMBOLS = 0x0089D791U;
|
const uint32_t DMR_MS_VOICE_SYNC_SYMBOLS = 0x0089D791U;
|
||||||
const uint32_t DMR_BS_DATA_SYNC_SYMBOLS = 0x00439B4DU;
|
const uint32_t DMR_BS_DATA_SYNC_SYMBOLS = 0x00439B4DU;
|
||||||
const uint32_t DMR_BS_VOICE_SYNC_SYMBOLS = 0x00BC64B2U;
|
const uint32_t DMR_BS_VOICE_SYNC_SYMBOLS = 0x00BC64B2U;
|
||||||
const uint32_t DMR_S1_DATA_SYNC_SYMBOLS = 0x0021751FU;
|
|
||||||
const uint32_t DMR_S1_VOICE_SYNC_SYMBOLS = 0x00DE8AE0U;
|
|
||||||
const uint32_t DMR_S2_DATA_SYNC_SYMBOLS = 0x006F8C23U;
|
|
||||||
const uint32_t DMR_S2_VOICE_SYNC_SYMBOLS = 0x009073DCU;
|
|
||||||
const uint32_t DMR_SYNC_SYMBOLS_MASK = 0x00FFFFFFU;
|
const uint32_t DMR_SYNC_SYMBOLS_MASK = 0x00FFFFFFU;
|
||||||
|
|
||||||
|
// D 5 D 7 F 7 7 F D 7 5 7
|
||||||
|
// 11 01 01 01 11 01 01 11 11 11 01 11 01 11 11 11 11 01 01 11 01 01 01 11
|
||||||
|
// -3 +3 +3 +3 -3 +3 +3 -3 -3 -3 +3 -3 +3 -3 -3 -3 -3 +3 +3 -3 +3 +3 +3 -3
|
||||||
|
|
||||||
|
const int8_t DMR_MS_DATA_SYNC_SYMBOLS_VALUES[] = {-3, +3, +3, +3, -3, +3, +3, -3, -3, -3, +3, -3, +3, -3, -3, -3, -3, +3, +3, -3, +3, +3, +3, -3};
|
||||||
|
|
||||||
|
// 7 F 7 D 5 D D 5 7 D F D
|
||||||
|
// 01 11 11 11 01 11 11 01 01 01 11 01 11 01 01 01 01 11 11 01 11 11 11 01
|
||||||
|
// +3 -3 -3 -3 +3 -3 -3 +3 +3 +3 -3 +3 -3 +3 +3 +3 +3 -3 -3 +3 -3 -3 -3 +3
|
||||||
|
|
||||||
|
const int8_t DMR_MS_VOICE_SYNC_SYMBOLS_VALUES[] = {+3, -3, -3, -3, +3, -3, -3, +3, +3, +3, -3, +3, -3, +3, +3, +3, +3, -3, -3, +3, -3, -3, -3, +3};
|
||||||
|
|
||||||
const uint8_t DT_VOICE_PI_HEADER = 0U;
|
const uint8_t DT_VOICE_PI_HEADER = 0U;
|
||||||
const uint8_t DT_VOICE_LC_HEADER = 1U;
|
const uint8_t DT_VOICE_LC_HEADER = 1U;
|
||||||
const uint8_t DT_TERMINATOR_WITH_LC = 2U;
|
const uint8_t DT_TERMINATOR_WITH_LC = 2U;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2017,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,9 +16,10 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "DMRIdleRX.h"
|
#include "DMRIdleRX.h"
|
||||||
#include "DMRSlotType.h"
|
#include "DMRSlotType.h"
|
||||||
@@ -84,16 +85,28 @@ void CDMRIdleRX::processSample(q15_t sample)
|
|||||||
q15_t max = -16000;
|
q15_t max = -16000;
|
||||||
q15_t min = 16000;
|
q15_t min = 16000;
|
||||||
|
|
||||||
uint32_t mask = 0x00800000U;
|
for (uint8_t i = 0U; i < DMR_SYNC_LENGTH_SYMBOLS; i++) {
|
||||||
for (uint8_t i = 0U; i < DMR_SYNC_LENGTH_SYMBOLS; i++, mask >>= 1) {
|
q15_t val = m_buffer[ptr];
|
||||||
bool b = (DMR_MS_DATA_SYNC_SYMBOLS & mask) == mask;
|
|
||||||
|
|
||||||
if (m_buffer[ptr] > max)
|
if (val > max)
|
||||||
max = m_buffer[ptr];
|
max = val;
|
||||||
if (m_buffer[ptr] < min)
|
if (val < min)
|
||||||
min = m_buffer[ptr];
|
min = val;
|
||||||
|
|
||||||
corr += b ? -m_buffer[ptr] : m_buffer[ptr];
|
switch (DMR_MS_DATA_SYNC_SYMBOLS_VALUES[i]) {
|
||||||
|
case +3:
|
||||||
|
corr -= (val + val + val);
|
||||||
|
break;
|
||||||
|
case +1:
|
||||||
|
corr -= val;
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
corr += val;
|
||||||
|
break;
|
||||||
|
default: // -3
|
||||||
|
corr += (val + val + val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
ptr += DMR_RADIO_SYMBOL_LENGTH;
|
ptr += DMR_RADIO_SYMBOL_LENGTH;
|
||||||
if (ptr >= DMR_FRAME_LENGTH_SAMPLES)
|
if (ptr >= DMR_FRAME_LENGTH_SAMPLES)
|
||||||
@@ -150,7 +163,7 @@ void CDMRIdleRX::processSample(q15_t sample)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_endPtr = NOENDPTR;
|
m_endPtr = NOENDPTR;
|
||||||
m_maxCorr = 0U;
|
m_maxCorr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_dataPtr++;
|
m_dataPtr++;
|
||||||
@@ -200,3 +213,5 @@ void CDMRIdleRX::setColorCode(uint8_t colorCode)
|
|||||||
m_colorCode = colorCode;
|
m_colorCode = colorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,10 +16,13 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#if !defined(DMRIDLERX_H)
|
#if !defined(DMRIDLERX_H)
|
||||||
#define DMRIDLERX_H
|
#define DMRIDLERX_H
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "DMRDefines.h"
|
#include "DMRDefines.h"
|
||||||
|
|
||||||
class CDMRIdleRX {
|
class CDMRIdleRX {
|
||||||
@@ -43,9 +46,11 @@ private:
|
|||||||
q15_t m_threshold;
|
q15_t m_threshold;
|
||||||
uint8_t m_colorCode;
|
uint8_t m_colorCode;
|
||||||
|
|
||||||
void processSample(q15_t sample);
|
void processSample(q15_t sample);
|
||||||
void samplesToBits(uint16_t start, uint8_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
void samplesToBits(uint16_t start, uint8_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "DMRRX.h"
|
#include "DMRRX.h"
|
||||||
|
|
||||||
@@ -68,3 +71,5 @@ void CDMRRX::reset()
|
|||||||
m_slot2RX.reset();
|
m_slot2RX.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,10 +16,13 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#if !defined(DMRRX_H)
|
#if !defined(DMRRX_H)
|
||||||
#define DMRRX_H
|
#define DMRRX_H
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "DMRSlotRX.h"
|
#include "DMRSlotRX.h"
|
||||||
|
|
||||||
class CDMRRX {
|
class CDMRRX {
|
||||||
@@ -40,3 +43,5 @@ private:
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2017,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,9 +16,10 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "DMRSlotRX.h"
|
#include "DMRSlotRX.h"
|
||||||
#include "DMRSlotType.h"
|
#include "DMRSlotType.h"
|
||||||
@@ -65,8 +66,7 @@ m_delay(0U),
|
|||||||
m_state(DMRRXS_NONE),
|
m_state(DMRRXS_NONE),
|
||||||
m_n(0U),
|
m_n(0U),
|
||||||
m_type(0U),
|
m_type(0U),
|
||||||
m_rssiCount(0U),
|
m_rssi()
|
||||||
m_rssi(0U)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +91,6 @@ void CDMRSlotRX::reset()
|
|||||||
m_state = DMRRXS_NONE;
|
m_state = DMRRXS_NONE;
|
||||||
m_startPtr = 0U;
|
m_startPtr = 0U;
|
||||||
m_endPtr = NOENDPTR;
|
m_endPtr = NOENDPTR;
|
||||||
m_rssiCount = 0U;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
||||||
@@ -105,6 +104,7 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
|||||||
return m_state != DMRRXS_NONE;
|
return m_state != DMRRXS_NONE;
|
||||||
|
|
||||||
m_buffer[m_dataPtr] = sample;
|
m_buffer[m_dataPtr] = sample;
|
||||||
|
m_rssi[m_dataPtr] = rssi;
|
||||||
|
|
||||||
m_bitBuffer[m_bitPtr] <<= 1;
|
m_bitBuffer[m_bitPtr] <<= 1;
|
||||||
if (sample < 0)
|
if (sample < 0)
|
||||||
@@ -114,9 +114,6 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
|||||||
if (m_dataPtr >= SCAN_START && m_dataPtr <= SCAN_END)
|
if (m_dataPtr >= SCAN_START && m_dataPtr <= SCAN_END)
|
||||||
correlateSync(true);
|
correlateSync(true);
|
||||||
} else {
|
} else {
|
||||||
// Grab the RSSI data during the frame
|
|
||||||
if (m_state == DMRRXS_VOICE && m_dataPtr == m_syncPtr)
|
|
||||||
m_rssi = rssi;
|
|
||||||
|
|
||||||
uint16_t min = m_syncPtr - 1U;
|
uint16_t min = m_syncPtr - 1U;
|
||||||
uint16_t max = m_syncPtr + 1U;
|
uint16_t max = m_syncPtr + 1U;
|
||||||
@@ -151,7 +148,7 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
|||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
case DT_DATA_HEADER:
|
case DT_DATA_HEADER:
|
||||||
DEBUG5("DMRSlotRX: data header found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
DEBUG5("DMRSlotRX: data header found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
m_state = DMRRXS_DATA;
|
m_state = DMRRXS_DATA;
|
||||||
m_type = 0x00U;
|
m_type = 0x00U;
|
||||||
break;
|
break;
|
||||||
@@ -160,33 +157,33 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
|||||||
case DT_RATE_1_DATA:
|
case DT_RATE_1_DATA:
|
||||||
if (m_state == DMRRXS_DATA) {
|
if (m_state == DMRRXS_DATA) {
|
||||||
DEBUG5("DMRSlotRX: data payload found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
DEBUG5("DMRSlotRX: data payload found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
m_type = dataType;
|
m_type = dataType;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DT_VOICE_LC_HEADER:
|
case DT_VOICE_LC_HEADER:
|
||||||
DEBUG5("DMRSlotRX: voice header found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
DEBUG5("DMRSlotRX: voice header found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
m_state = DMRRXS_VOICE;
|
m_state = DMRRXS_VOICE;
|
||||||
break;
|
break;
|
||||||
case DT_VOICE_PI_HEADER:
|
case DT_VOICE_PI_HEADER:
|
||||||
if (m_state == DMRRXS_VOICE) {
|
if (m_state == DMRRXS_VOICE) {
|
||||||
DEBUG5("DMRSlotRX: voice pi header found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
DEBUG5("DMRSlotRX: voice pi header found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
}
|
}
|
||||||
m_state = DMRRXS_VOICE;
|
m_state = DMRRXS_VOICE;
|
||||||
break;
|
break;
|
||||||
case DT_TERMINATOR_WITH_LC:
|
case DT_TERMINATOR_WITH_LC:
|
||||||
if (m_state == DMRRXS_VOICE) {
|
if (m_state == DMRRXS_VOICE) {
|
||||||
DEBUG5("DMRSlotRX: voice terminator found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
DEBUG5("DMRSlotRX: voice terminator found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
m_state = DMRRXS_NONE;
|
m_state = DMRRXS_NONE;
|
||||||
m_endPtr = NOENDPTR;
|
m_endPtr = NOENDPTR;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: // DT_CSBK
|
default: // DT_CSBK
|
||||||
DEBUG5("DMRSlotRX: csbk found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
DEBUG5("DMRSlotRX: csbk found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
m_state = DMRRXS_NONE;
|
m_state = DMRRXS_NONE;
|
||||||
m_endPtr = NOENDPTR;
|
m_endPtr = NOENDPTR;
|
||||||
break;
|
break;
|
||||||
@@ -195,22 +192,7 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
|||||||
} else if (m_control == CONTROL_VOICE) {
|
} else if (m_control == CONTROL_VOICE) {
|
||||||
// Voice sync
|
// Voice sync
|
||||||
DEBUG5("DMRSlotRX: voice sync found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
DEBUG5("DMRSlotRX: voice sync found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||||
#if defined(SEND_RSSI_DATA)
|
writeRSSIData(frame);
|
||||||
// Send RSSI data approximately every second
|
|
||||||
if (m_rssiCount == 2U) {
|
|
||||||
frame[34U] = (m_rssi >> 8) & 0xFFU;
|
|
||||||
frame[35U] = (m_rssi >> 0) & 0xFFU;
|
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 3U);
|
|
||||||
} else {
|
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_rssiCount++;
|
|
||||||
if (m_rssiCount >= 16U)
|
|
||||||
m_rssiCount = 0U;
|
|
||||||
#else
|
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
|
||||||
#endif
|
|
||||||
m_state = DMRRXS_VOICE;
|
m_state = DMRRXS_VOICE;
|
||||||
m_syncCount = 0U;
|
m_syncCount = 0U;
|
||||||
m_n = 0U;
|
m_n = 0U;
|
||||||
@@ -231,26 +213,12 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
|||||||
} else {
|
} else {
|
||||||
frame[0U] = ++m_n;
|
frame[0U] = ++m_n;
|
||||||
}
|
}
|
||||||
#if defined(SEND_RSSI_DATA)
|
|
||||||
// Send RSSI data approximately every second
|
|
||||||
if (m_rssiCount == 2U) {
|
|
||||||
frame[34U] = (m_rssi >> 8) & 0xFFU;
|
|
||||||
frame[35U] = (m_rssi >> 0) & 0xFFU;
|
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 3U);
|
|
||||||
} else {
|
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_rssiCount++;
|
|
||||||
if (m_rssiCount >= 16U)
|
|
||||||
m_rssiCount = 0U;
|
|
||||||
#else
|
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||||
#endif
|
|
||||||
} else if (m_state == DMRRXS_DATA) {
|
} else if (m_state == DMRRXS_DATA) {
|
||||||
if (m_type != 0x00U) {
|
if (m_type != 0x00U) {
|
||||||
frame[0U] = CONTROL_DATA | m_type;
|
frame[0U] = CONTROL_DATA | m_type;
|
||||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
writeRSSIData(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,19 +248,34 @@ void CDMRSlotRX::correlateSync(bool first)
|
|||||||
q15_t min = 16000;
|
q15_t min = 16000;
|
||||||
q15_t max = -16000;
|
q15_t max = -16000;
|
||||||
|
|
||||||
uint32_t mask = 0x00800000U;
|
for (uint8_t i = 0U; i < DMR_SYNC_LENGTH_SYMBOLS; i++) {
|
||||||
for (uint8_t i = 0U; i < DMR_SYNC_LENGTH_SYMBOLS; i++, mask >>= 1) {
|
q15_t val = m_buffer[ptr];
|
||||||
bool b = (DMR_MS_DATA_SYNC_SYMBOLS & mask) == mask;
|
|
||||||
|
|
||||||
if (m_buffer[ptr] > max)
|
if (val > max)
|
||||||
max = m_buffer[ptr];
|
max = val;
|
||||||
if (m_buffer[ptr] < min)
|
if (val < min)
|
||||||
min = m_buffer[ptr];
|
min = val;
|
||||||
|
|
||||||
|
int8_t corrVal;
|
||||||
if (data)
|
if (data)
|
||||||
corr += b ? -m_buffer[ptr] : m_buffer[ptr];
|
corrVal = DMR_MS_DATA_SYNC_SYMBOLS_VALUES[i];
|
||||||
else // if (voice)
|
else
|
||||||
corr += b ? m_buffer[ptr] : -m_buffer[ptr];
|
corrVal = DMR_MS_VOICE_SYNC_SYMBOLS_VALUES[i];
|
||||||
|
|
||||||
|
switch (corrVal) {
|
||||||
|
case +3:
|
||||||
|
corr -= (val + val + val);
|
||||||
|
break;
|
||||||
|
case +1:
|
||||||
|
corr -= val;
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
corr += val;
|
||||||
|
break;
|
||||||
|
default: // -3
|
||||||
|
corr += (val + val + val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
ptr += DMR_RADIO_SYMBOL_LENGTH;
|
ptr += DMR_RADIO_SYMBOL_LENGTH;
|
||||||
}
|
}
|
||||||
@@ -317,7 +300,6 @@ void CDMRSlotRX::correlateSync(bool first)
|
|||||||
m_threshold[0U] = m_threshold[1U] = m_threshold[2U] = m_threshold[3U] = threshold;
|
m_threshold[0U] = m_threshold[1U] = m_threshold[2U] = m_threshold[3U] = threshold;
|
||||||
m_centre[0U] = m_centre[1U] = m_centre[2U] = m_centre[3U] = centre;
|
m_centre[0U] = m_centre[1U] = m_centre[2U] = m_centre[3U] = centre;
|
||||||
m_averagePtr = 0U;
|
m_averagePtr = 0U;
|
||||||
m_rssiCount = 0U;
|
|
||||||
} else {
|
} else {
|
||||||
m_threshold[m_averagePtr] = threshold;
|
m_threshold[m_averagePtr] = threshold;
|
||||||
m_centre[m_averagePtr] = centre;
|
m_centre[m_averagePtr] = centre;
|
||||||
@@ -343,7 +325,6 @@ void CDMRSlotRX::correlateSync(bool first)
|
|||||||
m_threshold[0U] = m_threshold[1U] = m_threshold[2U] = m_threshold[3U] = threshold;
|
m_threshold[0U] = m_threshold[1U] = m_threshold[2U] = m_threshold[3U] = threshold;
|
||||||
m_centre[0U] = m_centre[1U] = m_centre[2U] = m_centre[3U] = centre;
|
m_centre[0U] = m_centre[1U] = m_centre[2U] = m_centre[3U] = centre;
|
||||||
m_averagePtr = 0U;
|
m_averagePtr = 0U;
|
||||||
m_rssiCount = 0U;
|
|
||||||
} else {
|
} else {
|
||||||
m_threshold[m_averagePtr] = threshold;
|
m_threshold[m_averagePtr] = threshold;
|
||||||
m_centre[m_averagePtr] = centre;
|
m_centre[m_averagePtr] = centre;
|
||||||
@@ -403,3 +384,25 @@ void CDMRSlotRX::setDelay(uint8_t delay)
|
|||||||
m_delay = delay;
|
m_delay = delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDMRSlotRX::writeRSSIData(uint8_t* frame)
|
||||||
|
{
|
||||||
|
#if defined(SEND_RSSI_DATA)
|
||||||
|
// Calculate RSSI average over a burst period. We don't take into account 2.5 ms at the beginning and 2.5 ms at the end
|
||||||
|
uint16_t start = m_startPtr + DMR_SYNC_LENGTH_SAMPLES / 2U;
|
||||||
|
|
||||||
|
uint32_t accum = 0U;
|
||||||
|
for (uint16_t i = 0U; i < (DMR_FRAME_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES); i++)
|
||||||
|
accum += m_rssi[start++];
|
||||||
|
|
||||||
|
uint16_t avg = accum / (DMR_FRAME_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES);
|
||||||
|
frame[34U] = (avg >> 8) & 0xFFU;
|
||||||
|
frame[35U] = (avg >> 0) & 0xFFU;
|
||||||
|
|
||||||
|
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 3U);
|
||||||
|
#else
|
||||||
|
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2016,2017,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,10 +16,13 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#if !defined(DMRSLOTRX_H)
|
#if !defined(DMRSLOTRX_H)
|
||||||
#define DMRSLOTRX_H
|
#define DMRSLOTRX_H
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "DMRDefines.h"
|
#include "DMRDefines.h"
|
||||||
|
|
||||||
enum DMRRX_STATE {
|
enum DMRRX_STATE {
|
||||||
@@ -62,12 +65,14 @@ private:
|
|||||||
DMRRX_STATE m_state;
|
DMRRX_STATE m_state;
|
||||||
uint8_t m_n;
|
uint8_t m_n;
|
||||||
uint8_t m_type;
|
uint8_t m_type;
|
||||||
uint16_t m_rssiCount;
|
uint16_t m_rssi[900U];
|
||||||
uint16_t m_rssi;
|
|
||||||
|
|
||||||
void correlateSync(bool first);
|
void correlateSync(bool first);
|
||||||
void samplesToBits(uint16_t start, uint8_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
void samplesToBits(uint16_t start, uint8_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
||||||
|
void writeRSSIData(uint8_t* frame);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "DMRSlotType.h"
|
#include "DMRSlotType.h"
|
||||||
|
|
||||||
@@ -285,3 +288,5 @@ void CDMRSlotType::encode(uint8_t colorCode, uint8_t dataType, uint8_t* frame) c
|
|||||||
frame[20U] = (frame[20U] & 0x03U) | ((slotType[1U] << 6) & 0xC0U) | ((slotType[2U] >> 2) & 0x3CU);
|
frame[20U] = (frame[20U] & 0x03U) | ((slotType[1U] << 6) & 0xC0U) | ((slotType[2U] >> 2) & 0x3CU);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,6 +16,10 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#if !defined(DMRSLOTTYPE_H)
|
#if !defined(DMRSLOTTYPE_H)
|
||||||
#define DMRSLOTTYPE_H
|
#define DMRSLOTTYPE_H
|
||||||
|
|
||||||
@@ -35,3 +39,5 @@ private:
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2017,2020 by Jonathan Naylor G4KLX
|
||||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||||
|
* Copyright (C) 2017 by Andy Uribe CA6JAU
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,31 +18,25 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// #define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "DMRSlotType.h"
|
#include "DMRSlotType.h"
|
||||||
|
|
||||||
#if defined(WIDE_C4FSK_FILTERS_TX)
|
|
||||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
|
||||||
static q15_t DMR_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
|
||||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
|
||||||
const uint16_t DMR_C4FSK_FILTER_LEN = 22U;
|
|
||||||
#else
|
|
||||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||||
static q15_t DMR_C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172, 237, 1927, 3120, 3073, 1447, -1431, -4544, -6442,
|
||||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
-5735, -1633, 5651, 14822, 23810, 30367, 32767, 30367, 23810, 14822, 5651, -1633, -5735, -6442,
|
||||||
-553, -847, -731, -340, 104, 401, 0};
|
-4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5
|
||||||
const uint16_t DMR_C4FSK_FILTER_LEN = 42U;
|
const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
|
||||||
#endif
|
|
||||||
|
|
||||||
const q15_t DMR_LEVELA[] = { 640, 640 , 640, 640, 640};
|
const q15_t DMR_LEVELA = 1362;
|
||||||
const q15_t DMR_LEVELB[] = { 213, 213, 213, 213, 213};
|
const q15_t DMR_LEVELB = 454;
|
||||||
const q15_t DMR_LEVELC[] = {-213, -213, -213, -213, -213};
|
const q15_t DMR_LEVELC = -454;
|
||||||
const q15_t DMR_LEVELD[] = {-640, -640, -640, -640, -640};
|
const q15_t DMR_LEVELD = -1362;
|
||||||
|
|
||||||
// The PR FILL and Data Sync pattern.
|
// The PR FILL and BS Data Sync pattern.
|
||||||
const uint8_t IDLE_DATA[] =
|
const uint8_t IDLE_DATA[] =
|
||||||
{0x53U, 0xC2U, 0x5EU, 0xABU, 0xA8U, 0x67U, 0x1DU, 0xC7U, 0x38U, 0x3BU, 0xD9U,
|
{0x53U, 0xC2U, 0x5EU, 0xABU, 0xA8U, 0x67U, 0x1DU, 0xC7U, 0x38U, 0x3BU, 0xD9U,
|
||||||
0x36U, 0x00U, 0x0DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD0U, 0x03U, 0xF6U,
|
0x36U, 0x00U, 0x0DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD0U, 0x03U, 0xF6U,
|
||||||
@@ -61,6 +56,9 @@ const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02
|
|||||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
||||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||||
|
|
||||||
|
const uint32_t STARTUP_COUNT = 20U;
|
||||||
|
const uint32_t ABORT_COUNT = 6U;
|
||||||
|
|
||||||
CDMRTX::CDMRTX() :
|
CDMRTX::CDMRTX() :
|
||||||
m_fifo(),
|
m_fifo(),
|
||||||
m_modFilter(),
|
m_modFilter(),
|
||||||
@@ -74,20 +72,25 @@ m_markBuffer(),
|
|||||||
m_poBuffer(),
|
m_poBuffer(),
|
||||||
m_poLen(0U),
|
m_poLen(0U),
|
||||||
m_poPtr(0U),
|
m_poPtr(0U),
|
||||||
m_count(0U),
|
m_frameCount(0U),
|
||||||
|
m_abortCount(),
|
||||||
m_abort()
|
m_abort()
|
||||||
{
|
{
|
||||||
::memset(m_modState, 0x00U, 70U * sizeof(q15_t));
|
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||||
|
|
||||||
m_modFilter.numTaps = DMR_C4FSK_FILTER_LEN;
|
m_modFilter.L = DMR_RADIO_SYMBOL_LENGTH;
|
||||||
m_modFilter.pState = m_modState;
|
m_modFilter.phaseLength = RRC_0_2_FILTER_PHASE_LEN;
|
||||||
m_modFilter.pCoeffs = DMR_C4FSK_FILTER;
|
m_modFilter.pCoeffs = RRC_0_2_FILTER;
|
||||||
|
m_modFilter.pState = m_modState;
|
||||||
|
|
||||||
::memcpy(m_newShortLC, EMPTY_SHORT_LC, 12U);
|
::memcpy(m_newShortLC, EMPTY_SHORT_LC, 12U);
|
||||||
::memcpy(m_shortLC, EMPTY_SHORT_LC, 12U);
|
::memcpy(m_shortLC, EMPTY_SHORT_LC, 12U);
|
||||||
|
|
||||||
m_abort[0U] = false;
|
m_abort[0U] = false;
|
||||||
m_abort[1U] = false;
|
m_abort[1U] = false;
|
||||||
|
|
||||||
|
m_abortCount[0U] = 0U;
|
||||||
|
m_abortCount[1U] = 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDMRTX::process()
|
void CDMRTX::process()
|
||||||
@@ -144,7 +147,7 @@ void CDMRTX::process()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t CDMRTX::writeData1(const uint8_t* data, uint8_t length)
|
uint8_t CDMRTX::writeData1(const uint8_t* data, uint16_t length)
|
||||||
{
|
{
|
||||||
if (length != (DMR_FRAME_LENGTH_BYTES + 1U))
|
if (length != (DMR_FRAME_LENGTH_BYTES + 1U))
|
||||||
return 4U;
|
return 4U;
|
||||||
@@ -168,7 +171,7 @@ uint8_t CDMRTX::writeData1(const uint8_t* data, uint8_t length)
|
|||||||
return 0U;
|
return 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t CDMRTX::writeData2(const uint8_t* data, uint8_t length)
|
uint8_t CDMRTX::writeData2(const uint8_t* data, uint16_t length)
|
||||||
{
|
{
|
||||||
if (length != (DMR_FRAME_LENGTH_BYTES + 1U))
|
if (length != (DMR_FRAME_LENGTH_BYTES + 1U))
|
||||||
return 4U;
|
return 4U;
|
||||||
@@ -192,7 +195,7 @@ uint8_t CDMRTX::writeData2(const uint8_t* data, uint8_t length)
|
|||||||
return 0U;
|
return 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t CDMRTX::writeShortLC(const uint8_t* data, uint8_t length)
|
uint8_t CDMRTX::writeShortLC(const uint8_t* data, uint16_t length)
|
||||||
{
|
{
|
||||||
if (length != 9U)
|
if (length != 9U)
|
||||||
return 4U;
|
return 4U;
|
||||||
@@ -208,17 +211,19 @@ uint8_t CDMRTX::writeShortLC(const uint8_t* data, uint8_t length)
|
|||||||
return 0U;
|
return 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t CDMRTX::writeAbort(const uint8_t* data, uint8_t length)
|
uint8_t CDMRTX::writeAbort(const uint8_t* data, uint16_t length)
|
||||||
{
|
{
|
||||||
if (length != 1U)
|
if (length != 1U)
|
||||||
return 4U;
|
return 4U;
|
||||||
|
|
||||||
switch (data[0U]) {
|
switch (data[0U]) {
|
||||||
case 1U:
|
case 1U:
|
||||||
|
m_abortCount[0U] = 0U;
|
||||||
m_abort[0U] = true;
|
m_abort[0U] = true;
|
||||||
return 0U;
|
return 0U;
|
||||||
|
|
||||||
case 2U:
|
case 2U:
|
||||||
|
m_abortCount[1U] = 0U;
|
||||||
m_abort[1U] = true;
|
m_abort[1U] = true;
|
||||||
return 0U;
|
return 0U;
|
||||||
|
|
||||||
@@ -231,7 +236,9 @@ void CDMRTX::setStart(bool start)
|
|||||||
{
|
{
|
||||||
m_state = start ? DMRTXSTATE_SLOT1 : DMRTXSTATE_IDLE;
|
m_state = start ? DMRTXSTATE_SLOT1 : DMRTXSTATE_IDLE;
|
||||||
|
|
||||||
m_count = 0U;
|
m_frameCount = 0U;
|
||||||
|
m_abortCount[0U] = 0U;
|
||||||
|
m_abortCount[1U] = 0U;
|
||||||
|
|
||||||
m_abort[0U] = false;
|
m_abort[0U] = false;
|
||||||
m_abort[1U] = false;
|
m_abort[1U] = false;
|
||||||
@@ -240,82 +247,56 @@ void CDMRTX::setStart(bool start)
|
|||||||
void CDMRTX::setCal(bool start)
|
void CDMRTX::setCal(bool start)
|
||||||
{
|
{
|
||||||
m_state = start ? DMRTXSTATE_CAL : DMRTXSTATE_IDLE;
|
m_state = start ? DMRTXSTATE_CAL : DMRTXSTATE_IDLE;
|
||||||
|
|
||||||
m_count = 0U;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDMRTX::writeByte(uint8_t c, uint8_t control)
|
void CDMRTX::writeByte(uint8_t c, uint8_t control)
|
||||||
{
|
{
|
||||||
q15_t inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
q15_t inBuffer[4U];
|
||||||
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U];
|
||||||
|
|
||||||
const uint8_t MASK = 0xC0U;
|
const uint8_t MASK = 0xC0U;
|
||||||
|
|
||||||
q15_t* p = inBuffer;
|
for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
|
||||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += DMR_RADIO_SYMBOL_LENGTH) {
|
|
||||||
switch (c & MASK) {
|
switch (c & MASK) {
|
||||||
case 0xC0U:
|
case 0xC0U:
|
||||||
::memcpy(p, DMR_LEVELA, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
inBuffer[i] = DMR_LEVELA;
|
||||||
break;
|
break;
|
||||||
case 0x80U:
|
case 0x80U:
|
||||||
::memcpy(p, DMR_LEVELB, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
inBuffer[i] = DMR_LEVELB;
|
||||||
break;
|
break;
|
||||||
case 0x00U:
|
case 0x00U:
|
||||||
::memcpy(p, DMR_LEVELC, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
inBuffer[i] = DMR_LEVELC;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
::memcpy(p, DMR_LEVELD, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
inBuffer[i] = DMR_LEVELD;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t blockSize = DMR_RADIO_SYMBOL_LENGTH * 4U;
|
uint8_t controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U];
|
||||||
|
|
||||||
uint8_t controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
|
||||||
::memset(controlBuffer, MARK_NONE, DMR_RADIO_SYMBOL_LENGTH * 4U * sizeof(uint8_t));
|
::memset(controlBuffer, MARK_NONE, DMR_RADIO_SYMBOL_LENGTH * 4U * sizeof(uint8_t));
|
||||||
controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 2U] = control;
|
controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 2U] = control;
|
||||||
|
|
||||||
// Handle the case of the oscillator not being accurate enough
|
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
|
||||||
if (m_sampleCount > 0U) {
|
|
||||||
m_count += DMR_RADIO_SYMBOL_LENGTH * 4U;
|
|
||||||
|
|
||||||
if (m_count >= m_sampleCount) {
|
io.write(STATE_DMR, outBuffer, DMR_RADIO_SYMBOL_LENGTH * 4U, controlBuffer);
|
||||||
if (m_sampleInsert) {
|
|
||||||
inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U] = inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U - 1U];
|
|
||||||
for (int8_t i = DMR_RADIO_SYMBOL_LENGTH * 4U - 1; i >= 0; i--)
|
|
||||||
controlBuffer[i + 1] = controlBuffer[i];
|
|
||||||
blockSize++;
|
|
||||||
} else {
|
|
||||||
controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 2U - 1U] = control;
|
|
||||||
for (uint8_t i = 0U; i < (DMR_RADIO_SYMBOL_LENGTH * 4U - 1U); i++)
|
|
||||||
controlBuffer[i] = controlBuffer[i + 1U];
|
|
||||||
blockSize--;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_count -= m_sampleCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
|
|
||||||
|
|
||||||
io.write(STATE_DMR, outBuffer, blockSize, controlBuffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CDMRTX::getSpace1() const
|
uint8_t CDMRTX::getSpace1() const
|
||||||
{
|
{
|
||||||
return m_fifo[0U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
|
return m_fifo[0U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CDMRTX::getSpace2() const
|
uint8_t CDMRTX::getSpace2() const
|
||||||
{
|
{
|
||||||
return m_fifo[1U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
|
return m_fifo[1U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDMRTX::createData(uint8_t slotIndex)
|
void CDMRTX::createData(uint8_t slotIndex)
|
||||||
{
|
{
|
||||||
if (m_fifo[slotIndex].getData()> 0U) {
|
if (m_fifo[slotIndex].getData() >= DMR_FRAME_LENGTH_BYTES && m_frameCount >= STARTUP_COUNT && m_abortCount[slotIndex] >= ABORT_COUNT) {
|
||||||
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
|
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
|
||||||
m_poBuffer[i] = m_fifo[slotIndex].get();
|
m_fifo[slotIndex].get(m_poBuffer[i]);
|
||||||
m_markBuffer[i] = MARK_NONE;
|
m_markBuffer[i] = MARK_NONE;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -333,17 +314,42 @@ void CDMRTX::createData(uint8_t slotIndex)
|
|||||||
|
|
||||||
void CDMRTX::createCal()
|
void CDMRTX::createCal()
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
|
// 1.2 kHz sine wave generation
|
||||||
m_poBuffer[i] = 0x5FU; // +3, +3, -3, -3 pattern for deviation cal.
|
if (m_modemState == STATE_DMRCAL) {
|
||||||
m_markBuffer[i] = MARK_NONE;
|
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
|
||||||
|
m_poBuffer[i] = 0x5FU; // +3, +3, -3, -3 pattern for deviation cal.
|
||||||
|
m_markBuffer[i] = MARK_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_poLen = DMR_FRAME_LENGTH_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 80 Hz square wave generation
|
||||||
|
if (m_modemState == STATE_LFCAL) {
|
||||||
|
for (unsigned int i = 0U; i < 7U; i++) {
|
||||||
|
m_poBuffer[i] = 0x55U; // +3, +3, ... pattern
|
||||||
|
m_markBuffer[i] = MARK_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_poBuffer[7U] = 0x5FU; // +3, +3, -3, -3 pattern
|
||||||
|
|
||||||
|
for (unsigned int i = 8U; i < 15U; i++) {
|
||||||
|
m_poBuffer[i] = 0xFFU; // -3, -3, ... pattern
|
||||||
|
m_markBuffer[i] = MARK_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_poLen = 15U;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_poLen = DMR_FRAME_LENGTH_BYTES;
|
|
||||||
m_poPtr = 0U;
|
m_poPtr = 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
|
void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
|
||||||
{
|
{
|
||||||
|
m_frameCount++;
|
||||||
|
m_abortCount[0U]++;
|
||||||
|
m_abortCount[1U]++;
|
||||||
|
|
||||||
if (m_cachPtr >= 12U)
|
if (m_cachPtr >= 12U)
|
||||||
m_cachPtr = 0U;
|
m_cachPtr = 0U;
|
||||||
|
|
||||||
@@ -359,7 +365,9 @@ void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
|
|||||||
m_markBuffer[1U] = MARK_NONE;
|
m_markBuffer[1U] = MARK_NONE;
|
||||||
m_markBuffer[2U] = rxSlotIndex == 1U ? MARK_SLOT1 : MARK_SLOT2;
|
m_markBuffer[2U] = rxSlotIndex == 1U ? MARK_SLOT1 : MARK_SLOT2;
|
||||||
|
|
||||||
bool at = m_fifo[rxSlotIndex].getData() > 0U;
|
bool at = false;
|
||||||
|
if (m_frameCount >= STARTUP_COUNT)
|
||||||
|
at = m_fifo[rxSlotIndex].getData() > 0U;
|
||||||
bool tc = txSlotIndex == 1U;
|
bool tc = txSlotIndex == 1U;
|
||||||
bool ls0 = true; // For 1 and 2
|
bool ls0 = true; // For 1 and 2
|
||||||
bool ls1 = true;
|
bool ls1 = true;
|
||||||
@@ -395,3 +403,20 @@ void CDMRTX::setColorCode(uint8_t colorCode)
|
|||||||
slotType.encode(colorCode, DT_IDLE, m_idle);
|
slotType.encode(colorCode, DT_IDLE, m_idle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDMRTX::resetFifo1()
|
||||||
|
{
|
||||||
|
m_fifo[0U].reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDMRTX::resetFifo2()
|
||||||
|
{
|
||||||
|
m_fifo[1U].reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t CDMRTX::getFrameCount()
|
||||||
|
{
|
||||||
|
return m_frameCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
+33
-23
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2016,2017,2020 by Jonathan Naylor G4KLX
|
||||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
@@ -17,13 +17,16 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DMR)
|
||||||
|
|
||||||
#if !defined(DMRTX_H)
|
#if !defined(DMRTX_H)
|
||||||
#define DMRTX_H
|
#define DMRTX_H
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "DMRDefines.h"
|
#include "DMRDefines.h"
|
||||||
|
|
||||||
#include "SerialRB.h"
|
#include "RingBuffer.h"
|
||||||
|
|
||||||
enum DMRTXSTATE {
|
enum DMRTXSTATE {
|
||||||
DMRTXSTATE_IDLE,
|
DMRTXSTATE_IDLE,
|
||||||
@@ -38,37 +41,42 @@ class CDMRTX {
|
|||||||
public:
|
public:
|
||||||
CDMRTX();
|
CDMRTX();
|
||||||
|
|
||||||
uint8_t writeData1(const uint8_t* data, uint8_t length);
|
uint8_t writeData1(const uint8_t* data, uint16_t length);
|
||||||
uint8_t writeData2(const uint8_t* data, uint8_t length);
|
uint8_t writeData2(const uint8_t* data, uint16_t length);
|
||||||
|
|
||||||
uint8_t writeShortLC(const uint8_t* data, uint8_t length);
|
uint8_t writeShortLC(const uint8_t* data, uint16_t length);
|
||||||
uint8_t writeAbort(const uint8_t* data, uint8_t length);
|
uint8_t writeAbort(const uint8_t* data, uint16_t length);
|
||||||
|
|
||||||
void setStart(bool start);
|
void setStart(bool start);
|
||||||
void setCal(bool start);
|
void setCal(bool start);
|
||||||
|
|
||||||
void process();
|
void process();
|
||||||
|
|
||||||
uint16_t getSpace1() const;
|
void resetFifo1();
|
||||||
uint16_t getSpace2() const;
|
void resetFifo2();
|
||||||
|
uint32_t getFrameCount();
|
||||||
|
|
||||||
|
uint8_t getSpace1() const;
|
||||||
|
uint8_t getSpace2() const;
|
||||||
|
|
||||||
void setColorCode(uint8_t colorCode);
|
void setColorCode(uint8_t colorCode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CSerialRB m_fifo[2U];
|
CRingBuffer<uint8_t> m_fifo[2U];
|
||||||
arm_fir_instance_q15 m_modFilter;
|
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||||
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
|
||||||
DMRTXSTATE m_state;
|
DMRTXSTATE m_state;
|
||||||
uint8_t m_idle[DMR_FRAME_LENGTH_BYTES];
|
uint8_t m_idle[DMR_FRAME_LENGTH_BYTES];
|
||||||
uint8_t m_cachPtr;
|
uint8_t m_cachPtr;
|
||||||
uint8_t m_shortLC[12U];
|
uint8_t m_shortLC[12U];
|
||||||
uint8_t m_newShortLC[12U];
|
uint8_t m_newShortLC[12U];
|
||||||
uint8_t m_markBuffer[40U];
|
uint8_t m_markBuffer[40U];
|
||||||
uint8_t m_poBuffer[40U];
|
uint8_t m_poBuffer[40U];
|
||||||
uint16_t m_poLen;
|
uint16_t m_poLen;
|
||||||
uint16_t m_poPtr;
|
uint16_t m_poPtr;
|
||||||
uint32_t m_count;
|
uint32_t m_frameCount;
|
||||||
bool m_abort[2U];
|
uint32_t m_abortCount[2U];
|
||||||
|
bool m_abort[2U];
|
||||||
|
|
||||||
void createData(uint8_t slotIndex);
|
void createData(uint8_t slotIndex);
|
||||||
void createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex);
|
void createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex);
|
||||||
@@ -78,3 +86,5 @@ private:
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2016,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DSTAR)
|
||||||
|
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "CalDStarRX.h"
|
#include "CalDStarRX.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
@@ -24,7 +27,7 @@
|
|||||||
const unsigned int BUFFER_LENGTH = 200U;
|
const unsigned int BUFFER_LENGTH = 200U;
|
||||||
|
|
||||||
const uint32_t PLLMAX = 0x10000U;
|
const uint32_t PLLMAX = 0x10000U;
|
||||||
const uint32_t PLLINC = PLLMAX / DSTAR_RADIO_BIT_LENGTH;
|
const uint32_t PLLINC = PLLMAX / DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
const uint32_t INC = PLLINC / 32U;
|
const uint32_t INC = PLLINC / 32U;
|
||||||
|
|
||||||
// D-Star bit order version of 0x55 0x2D 0x16
|
// D-Star bit order version of 0x55 0x2D 0x16
|
||||||
@@ -126,3 +129,5 @@ void CCalDStarRX::process(q15_t value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,10 +16,13 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DSTAR)
|
||||||
|
|
||||||
#if !defined(CALDSTARRX_H)
|
#if !defined(CALDSTARRX_H)
|
||||||
#define CALDSTARRX_H
|
#define CALDSTARRX_H
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "DStarDefines.h"
|
#include "DStarDefines.h"
|
||||||
|
|
||||||
class CCalDStarRX {
|
class CCalDStarRX {
|
||||||
@@ -40,3 +43,5 @@ private:
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2016,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DSTAR)
|
||||||
|
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "CalDStarTX.h"
|
#include "CalDStarTX.h"
|
||||||
|
|
||||||
@@ -162,7 +165,7 @@ void CCalDStarTX::process()
|
|||||||
m_count = (m_count + 1U) % (30U * 21U);
|
m_count = (m_count + 1U) % (30U * 21U);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t CCalDStarTX::write(const uint8_t* data, uint8_t length)
|
uint8_t CCalDStarTX::write(const uint8_t* data, uint16_t length)
|
||||||
{
|
{
|
||||||
if (length != 1U)
|
if (length != 1U)
|
||||||
return 4U;
|
return 4U;
|
||||||
@@ -181,3 +184,5 @@ uint8_t CCalDStarTX::write(const uint8_t* data, uint8_t length)
|
|||||||
return 0U;
|
return 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,17 +16,20 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DSTAR)
|
||||||
|
|
||||||
#if !defined(CALDSTARTX_H)
|
#if !defined(CALDSTARTX_H)
|
||||||
#define CALDSTARTX_H
|
#define CALDSTARTX_H
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "DStarDefines.h"
|
#include "DStarDefines.h"
|
||||||
|
|
||||||
class CCalDStarTX {
|
class CCalDStarTX {
|
||||||
public:
|
public:
|
||||||
CCalDStarTX();
|
CCalDStarTX();
|
||||||
|
|
||||||
uint8_t write(const uint8_t* data, uint8_t length);
|
uint8_t write(const uint8_t* data, uint16_t length);
|
||||||
|
|
||||||
void process();
|
void process();
|
||||||
|
|
||||||
@@ -37,3 +40,5 @@ private:
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2015,2017,2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(DSTARDEFINES_H)
|
||||||
|
#define DSTARDEFINES_H
|
||||||
|
|
||||||
|
const unsigned int DSTAR_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate
|
||||||
|
|
||||||
|
const unsigned int DSTAR_HEADER_LENGTH_BYTES = 41U;
|
||||||
|
|
||||||
|
const unsigned int DSTAR_FEC_SECTION_LENGTH_BYTES = 83U;
|
||||||
|
const unsigned int DSTAR_FEC_SECTION_LENGTH_SYMBOLS = 660U;
|
||||||
|
const unsigned int DSTAR_FEC_SECTION_LENGTH_SAMPLES = DSTAR_FEC_SECTION_LENGTH_SYMBOLS * DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
|
|
||||||
|
const unsigned int DSTAR_DATA_LENGTH_BYTES = 12U;
|
||||||
|
const unsigned int DSTAR_DATA_LENGTH_SYMBOLS = DSTAR_DATA_LENGTH_BYTES * 8U;
|
||||||
|
const unsigned int DSTAR_DATA_LENGTH_SAMPLES = DSTAR_DATA_LENGTH_SYMBOLS * DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
|
|
||||||
|
const unsigned int DSTAR_END_SYNC_LENGTH_BYTES = 6U;
|
||||||
|
const unsigned int DSTAR_END_SYNC_LENGTH_BITS = DSTAR_END_SYNC_LENGTH_BYTES * 8U;
|
||||||
|
|
||||||
|
const unsigned int DSTAR_FRAME_SYNC_LENGTH_BYTES = 3U;
|
||||||
|
const unsigned int DSTAR_FRAME_SYNC_LENGTH_SYMBOLS = DSTAR_FRAME_SYNC_LENGTH_BYTES * 8U;
|
||||||
|
const unsigned int DSTAR_FRAME_SYNC_LENGTH_SAMPLES = DSTAR_FRAME_SYNC_LENGTH_SYMBOLS * DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
|
|
||||||
|
const unsigned int DSTAR_DATA_SYNC_LENGTH_BYTES = 3U;
|
||||||
|
const unsigned int DSTAR_DATA_SYNC_LENGTH_SYMBOLS = DSTAR_DATA_SYNC_LENGTH_BYTES * 8U;
|
||||||
|
const unsigned int DSTAR_DATA_SYNC_LENGTH_SAMPLES = DSTAR_DATA_SYNC_LENGTH_SYMBOLS * DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
|
|
||||||
|
const uint8_t DSTAR_DATA_SYNC_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8, 0x55, 0x2D, 0x16};
|
||||||
|
|
||||||
|
// D-Star bit order version of 0x55 0x6E 0x0A
|
||||||
|
const uint32_t DSTAR_FRAME_SYNC_DATA = 0x00557650U;
|
||||||
|
const uint32_t DSTAR_FRAME_SYNC_MASK = 0x00FFFFFFU;
|
||||||
|
const bool DSTAR_FRAME_SYNC_SYMBOLS[] = {false, true, false, true, false, true, false, true, false, true, true, true, false, true, true, false, false, true, false, true, false, false, false, false};
|
||||||
|
|
||||||
|
// D-Star bit order version of 0x55 0x2D 0x16
|
||||||
|
const uint32_t DSTAR_DATA_SYNC_DATA = 0x00AAB468U;
|
||||||
|
const uint32_t DSTAR_DATA_SYNC_MASK = 0x00FFFFFFU;
|
||||||
|
const bool DSTAR_DATA_SYNC_SYMBOLS[] = {true, false, true, false, true, false, true, false, true, false, true, true, false, true, false, false, false, true, true, false, true, false, false, false};
|
||||||
|
|
||||||
|
// D-Star bit order version of 0x55 0x55 0xC8 0x7A
|
||||||
|
const uint32_t DSTAR_END_SYNC_DATA = 0xAAAA135EU;
|
||||||
|
const uint32_t DSTAR_END_SYNC_MASK = 0xFFFFFFFFU;
|
||||||
|
const uint8_t DSTAR_END_SYNC_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
|
const uint8_t DSTAR_SLOW_DATA_TYPE_TEXT = 0x40U;
|
||||||
|
const uint8_t DSTAR_SLOW_DATA_TYPE_HEADER = 0x50U;
|
||||||
|
|
||||||
|
const uint8_t DSTAR_SCRAMBLER_BYTES[] = {0x70U, 0x4FU, 0x93U};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
+298
-159
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2017,2020 by Jonathan Naylor G4KLX
|
||||||
|
* Copyright (C) 2017 by Andy Uribe CA6JAU
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,41 +17,30 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// #define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DSTAR)
|
||||||
|
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "DStarRX.h"
|
#include "DStarRX.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
const unsigned int BUFFER_LENGTH = 200U;
|
const unsigned int MAX_FRAMES = 150U;
|
||||||
|
|
||||||
const uint32_t PLLMAX = 0x10000U;
|
|
||||||
const uint32_t PLLINC = PLLMAX / DSTAR_RADIO_BIT_LENGTH;
|
|
||||||
const uint32_t INC = PLLINC / 32U;
|
|
||||||
|
|
||||||
const unsigned int MAX_SYNC_BITS = 50U * DSTAR_DATA_LENGTH_BITS;
|
|
||||||
|
|
||||||
const unsigned int SYNC_POS = 21U * DSTAR_DATA_LENGTH_BITS;
|
|
||||||
const unsigned int SYNC_SCAN_START = SYNC_POS - 3U;
|
|
||||||
const unsigned int SYNC_SCAN_END = SYNC_POS + 3U;
|
|
||||||
|
|
||||||
const q15_t THRESHOLD = 0;
|
|
||||||
|
|
||||||
// D-Star bit order version of 0x55 0x55 0x6E 0x0A
|
// D-Star bit order version of 0x55 0x55 0x6E 0x0A
|
||||||
const uint32_t FRAME_SYNC_DATA = 0x00557650U;
|
const uint64_t FRAME_SYNC_DATA = 0x0000000000557650U;
|
||||||
const uint32_t FRAME_SYNC_MASK = 0x00FFFFFFU;
|
const uint64_t FRAME_SYNC_MASK = 0x0000000000FFFFFFU;
|
||||||
const uint8_t FRAME_SYNC_ERRS = 2U;
|
const uint8_t FRAME_SYNC_ERRS = 2U;
|
||||||
|
|
||||||
// D-Star bit order version of 0x55 0x2D 0x16
|
// D-Star bit order version of 0x55 0x2D 0x16
|
||||||
const uint32_t DATA_SYNC_DATA = 0x00AAB468U;
|
const uint64_t DATA_SYNC_DATA = 0x0000000000AAB468U;
|
||||||
const uint32_t DATA_SYNC_MASK = 0x00FFFFFFU;
|
const uint64_t DATA_SYNC_MASK = 0x0000000000FFFFFFU;
|
||||||
const uint8_t DATA_SYNC_ERRS = 2U;
|
const uint8_t DATA_SYNC_ERRS = 2U;
|
||||||
|
|
||||||
// D-Star bit order version of 0x55 0x55 0xC8 0x7A
|
// D-Star bit order version of 0x55 0x55 0xC8 0x7A
|
||||||
const uint32_t END_SYNC_DATA = 0xAAAA135EU;
|
const uint64_t END_SYNC_DATA = 0x0000AAAAAAAA135EU;
|
||||||
const uint32_t END_SYNC_MASK = 0xFFFFFFFFU;
|
const uint64_t END_SYNC_MASK = 0x0000FFFFFFFFFFFFU;
|
||||||
const uint8_t END_SYNC_ERRS = 3U;
|
const uint8_t END_SYNC_ERRS = 1U;
|
||||||
|
|
||||||
const uint8_t BIT_MASK_TABLE0[] = {0x7FU, 0xBFU, 0xDFU, 0xEFU, 0xF7U, 0xFBU, 0xFDU, 0xFEU};
|
const uint8_t BIT_MASK_TABLE0[] = {0x7FU, 0xBFU, 0xDFU, 0xEFU, 0xF7U, 0xFBU, 0xFDU, 0xFEU};
|
||||||
const uint8_t BIT_MASK_TABLE1[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
const uint8_t BIT_MASK_TABLE1[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||||
@@ -244,14 +234,24 @@ const uint16_t CCITT_TABLE[] = {
|
|||||||
0xf78fU, 0xe606U, 0xd49dU, 0xc514U, 0xb1abU, 0xa022U, 0x92b9U, 0x8330U,
|
0xf78fU, 0xe606U, 0xd49dU, 0xc514U, 0xb1abU, 0xa022U, 0x92b9U, 0x8330U,
|
||||||
0x7bc7U, 0x6a4eU, 0x58d5U, 0x495cU, 0x3de3U, 0x2c6aU, 0x1ef1U, 0x0f78U};
|
0x7bc7U, 0x6a4eU, 0x58d5U, 0x495cU, 0x3de3U, 0x2c6aU, 0x1ef1U, 0x0f78U};
|
||||||
|
|
||||||
|
const uint16_t NOENDPTR = 9999U;
|
||||||
|
|
||||||
CDStarRX::CDStarRX() :
|
CDStarRX::CDStarRX() :
|
||||||
m_pll(0U),
|
|
||||||
m_prev(false),
|
|
||||||
m_rxState(DSRXS_NONE),
|
m_rxState(DSRXS_NONE),
|
||||||
m_patternBuffer(0x00U),
|
m_bitBuffer(),
|
||||||
m_rxBuffer(),
|
m_headerBuffer(),
|
||||||
m_rxBufferBits(0U),
|
m_dataBuffer(),
|
||||||
m_dataBits(0U),
|
m_bitPtr(0U),
|
||||||
|
m_headerPtr(0U),
|
||||||
|
m_dataPtr(0U),
|
||||||
|
m_startPtr(NOENDPTR),
|
||||||
|
m_syncPtr(NOENDPTR),
|
||||||
|
m_minSyncPtr(NOENDPTR),
|
||||||
|
m_maxSyncPtr(NOENDPTR),
|
||||||
|
m_maxFrameCorr(0),
|
||||||
|
m_maxDataCorr(0),
|
||||||
|
m_frameCount(0U),
|
||||||
|
m_countdown(0U),
|
||||||
m_mar(0U),
|
m_mar(0U),
|
||||||
m_pathMetric(),
|
m_pathMetric(),
|
||||||
m_pathMemory0(),
|
m_pathMemory0(),
|
||||||
@@ -259,142 +259,146 @@ m_pathMemory1(),
|
|||||||
m_pathMemory2(),
|
m_pathMemory2(),
|
||||||
m_pathMemory3(),
|
m_pathMemory3(),
|
||||||
m_fecOutput(),
|
m_fecOutput(),
|
||||||
m_samples(),
|
m_rssiAccum(0U),
|
||||||
m_samplesPtr(0U)
|
m_rssiCount(0U)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDStarRX::reset()
|
void CDStarRX::reset()
|
||||||
{
|
{
|
||||||
m_pll = 0U;
|
m_rxState = DSRXS_NONE;
|
||||||
m_prev = false;
|
m_headerPtr = 0U;
|
||||||
m_rxState = DSRXS_NONE;
|
m_dataPtr = 0U;
|
||||||
m_patternBuffer = 0x00U;
|
m_bitPtr = 0U;
|
||||||
m_rxBufferBits = 0U;
|
m_maxFrameCorr = 0;
|
||||||
m_dataBits = 0U;
|
m_maxDataCorr = 0;
|
||||||
m_samplesPtr = 0U;
|
m_startPtr = NOENDPTR;
|
||||||
|
m_syncPtr = NOENDPTR;
|
||||||
|
m_minSyncPtr = NOENDPTR;
|
||||||
|
m_maxSyncPtr = NOENDPTR;
|
||||||
|
m_frameCount = 0U;
|
||||||
|
m_countdown = 0U;
|
||||||
|
m_rssiAccum = 0U;
|
||||||
|
m_rssiCount = 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDStarRX::samples(const q15_t* samples, uint8_t length)
|
void CDStarRX::samples(const q15_t* samples, const uint16_t* rssi, uint8_t length)
|
||||||
{
|
{
|
||||||
for (uint16_t i = 0U; i < length; i++) {
|
for (uint16_t i = 0U; i < length; i++) {
|
||||||
m_samples[m_samplesPtr] = samples[i];
|
m_rssiAccum += rssi[i];
|
||||||
|
m_rssiCount++;
|
||||||
|
|
||||||
bool bit = samples[i] < THRESHOLD;
|
q15_t sample = samples[i];
|
||||||
|
|
||||||
if (bit != m_prev) {
|
m_bitBuffer[m_bitPtr] <<= 1;
|
||||||
if (m_pll < (PLLMAX / 2U))
|
if (sample < 0)
|
||||||
m_pll += INC;
|
m_bitBuffer[m_bitPtr] |= 0x01U;
|
||||||
else
|
|
||||||
m_pll -= INC;
|
m_dataBuffer[m_dataPtr] = sample;
|
||||||
|
|
||||||
|
switch (m_rxState) {
|
||||||
|
case DSRXS_HEADER:
|
||||||
|
processHeader(sample);
|
||||||
|
break;
|
||||||
|
case DSRXS_DATA:
|
||||||
|
processData();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
processNone(sample);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_prev = bit;
|
m_dataPtr++;
|
||||||
|
if (m_dataPtr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||||
|
m_dataPtr = 0U;
|
||||||
|
|
||||||
m_pll += PLLINC;
|
m_bitPtr++;
|
||||||
|
if (m_bitPtr >= DSTAR_RADIO_SYMBOL_LENGTH)
|
||||||
if (m_pll >= PLLMAX) {
|
m_bitPtr = 0U;
|
||||||
m_pll -= PLLMAX;
|
|
||||||
|
|
||||||
switch (m_rxState) {
|
|
||||||
case DSRXS_NONE:
|
|
||||||
processNone(bit);
|
|
||||||
break;
|
|
||||||
case DSRXS_HEADER:
|
|
||||||
processHeader(bit);
|
|
||||||
break;
|
|
||||||
case DSRXS_DATA:
|
|
||||||
processData(bit);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_samplesPtr++;
|
|
||||||
if (m_samplesPtr >= DSTAR_DATA_SYNC_LENGTH_BITS)
|
|
||||||
m_samplesPtr = 0U;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDStarRX::processNone(bool bit)
|
void CDStarRX::processNone(q15_t sample)
|
||||||
{
|
{
|
||||||
m_patternBuffer <<= 1;
|
|
||||||
if (bit)
|
|
||||||
m_patternBuffer |= 0x01U;
|
|
||||||
|
|
||||||
// Fuzzy matching of the frame sync sequence
|
// Fuzzy matching of the frame sync sequence
|
||||||
if (countBits32((m_patternBuffer & FRAME_SYNC_MASK) ^ FRAME_SYNC_DATA) <= FRAME_SYNC_ERRS) {
|
bool ret = correlateFrameSync();
|
||||||
DEBUG1("DStarRX: found frame sync in None");
|
if (ret) {
|
||||||
|
m_countdown = 5U;
|
||||||
|
|
||||||
::memset(m_rxBuffer, 0x00U, DSTAR_FEC_SECTION_LENGTH_BYTES);
|
m_headerBuffer[m_headerPtr] = sample;
|
||||||
m_rxBufferBits = 0U;
|
m_headerPtr++;
|
||||||
|
|
||||||
|
m_rssiAccum = 0U;
|
||||||
|
m_rssiCount = 0U;
|
||||||
|
|
||||||
m_rxState = DSRXS_HEADER;
|
m_rxState = DSRXS_HEADER;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exact matching of the data sync bit sequence
|
// Fuzzy matching of the data sync bit sequence
|
||||||
if (countBits32((m_patternBuffer & DATA_SYNC_MASK) ^ DATA_SYNC_DATA) == 0U) {
|
ret = correlateDataSync();
|
||||||
|
if (ret) {
|
||||||
DEBUG1("DStarRX: found data sync in None");
|
DEBUG1("DStarRX: found data sync in None");
|
||||||
|
|
||||||
io.setDecode(true);
|
io.setDecode(true);
|
||||||
io.setADCDetection(true);
|
io.setADCDetection(true);
|
||||||
|
|
||||||
serial.writeDStarData(DSTAR_DATA_SYNC_BYTES, DSTAR_DATA_LENGTH_BYTES);
|
m_rxState = DSRXS_DATA;
|
||||||
|
|
||||||
::memset(m_rxBuffer, 0x00U, DSTAR_DATA_LENGTH_BYTES + 2U);
|
|
||||||
m_rxBufferBits = 0U;
|
|
||||||
|
|
||||||
m_dataBits = 0U;
|
|
||||||
m_rxState = DSRXS_DATA;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDStarRX::processHeader(bool bit)
|
void CDStarRX::processHeader(q15_t sample)
|
||||||
{
|
{
|
||||||
m_patternBuffer <<= 1;
|
if (m_countdown > 0U) {
|
||||||
if (bit)
|
correlateFrameSync();
|
||||||
m_patternBuffer |= 0x01U;
|
m_countdown--;
|
||||||
|
}
|
||||||
|
|
||||||
WRITE_BIT2(m_rxBuffer, m_rxBufferBits, bit);
|
m_headerBuffer[m_headerPtr] = sample;
|
||||||
m_rxBufferBits++;
|
m_headerPtr++;
|
||||||
|
|
||||||
// A full FEC header
|
// A full FEC header
|
||||||
if (m_rxBufferBits == DSTAR_FEC_SECTION_LENGTH_BITS) {
|
if (m_headerPtr == (DSTAR_FEC_SECTION_LENGTH_SAMPLES + DSTAR_RADIO_SYMBOL_LENGTH)) {
|
||||||
// Process the scrambling, interleaving and FEC, then return if the chcksum was correct
|
uint8_t buffer[DSTAR_FEC_SECTION_LENGTH_BYTES];
|
||||||
unsigned char header[DSTAR_HEADER_LENGTH_BYTES];
|
samplesToBits(m_headerBuffer, DSTAR_RADIO_SYMBOL_LENGTH, DSTAR_FEC_SECTION_LENGTH_SYMBOLS, buffer, DSTAR_FEC_SECTION_LENGTH_SAMPLES);
|
||||||
bool ok = rxHeader(m_rxBuffer, header);
|
|
||||||
if (ok) {
|
// Process the scrambling, interleaving and FEC, then return true if the chcksum was correct
|
||||||
|
uint8_t header[DSTAR_HEADER_LENGTH_BYTES];
|
||||||
|
bool ok = rxHeader(buffer, header);
|
||||||
|
if (!ok) {
|
||||||
|
// The checksum failed, return to looking for syncs
|
||||||
|
m_rxState = DSRXS_NONE;
|
||||||
|
m_maxFrameCorr = 0;
|
||||||
|
m_maxDataCorr = 0;
|
||||||
|
} else {
|
||||||
io.setDecode(true);
|
io.setDecode(true);
|
||||||
io.setADCDetection(true);
|
io.setADCDetection(true);
|
||||||
|
|
||||||
serial.writeDStarHeader(header, DSTAR_HEADER_LENGTH_BYTES);
|
writeRSSIHeader(header);
|
||||||
|
|
||||||
::memset(m_rxBuffer, 0x00U, DSTAR_DATA_LENGTH_BYTES + 2U);
|
|
||||||
m_rxBufferBits = 0U;
|
|
||||||
|
|
||||||
m_rxState = DSRXS_DATA;
|
|
||||||
m_dataBits = SYNC_POS - DSTAR_DATA_LENGTH_BITS + 1U;
|
|
||||||
} else {
|
|
||||||
// The checksum failed, return to looking for syncs
|
|
||||||
m_rxState = DSRXS_NONE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ready to start the first data section
|
||||||
|
if (m_headerPtr == (DSTAR_FEC_SECTION_LENGTH_SAMPLES + 2U * DSTAR_RADIO_SYMBOL_LENGTH)) {
|
||||||
|
m_frameCount = 0U;
|
||||||
|
m_dataPtr = 0U;
|
||||||
|
|
||||||
|
m_startPtr = 476U;
|
||||||
|
m_syncPtr = 471U;
|
||||||
|
m_maxSyncPtr = 472U;
|
||||||
|
m_minSyncPtr = 470U;
|
||||||
|
|
||||||
|
DEBUG5("DStarRX: calc start/sync/max/min", m_startPtr, m_syncPtr, m_maxSyncPtr, m_minSyncPtr);
|
||||||
|
|
||||||
|
m_rxState = DSRXS_DATA;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDStarRX::processData(bool bit)
|
void CDStarRX::processData()
|
||||||
{
|
{
|
||||||
m_patternBuffer <<= 1;
|
|
||||||
if (bit)
|
|
||||||
m_patternBuffer |= 0x01U;
|
|
||||||
|
|
||||||
WRITE_BIT2(m_rxBuffer, m_rxBufferBits, bit);
|
|
||||||
m_rxBufferBits++;
|
|
||||||
|
|
||||||
// Fuzzy matching of the end frame sequences
|
// Fuzzy matching of the end frame sequences
|
||||||
if (countBits32((m_patternBuffer & END_SYNC_MASK) ^ END_SYNC_DATA) <= END_SYNC_ERRS) {
|
if (countBits64((m_bitBuffer[m_bitPtr] & DSTAR_END_SYNC_MASK) ^ DSTAR_END_SYNC_DATA) <= END_SYNC_ERRS) {
|
||||||
DEBUG1("DStarRX: Found end sync in Data");
|
DEBUG1("DStarRX: Found end sync in Data");
|
||||||
|
|
||||||
io.setDecode(false);
|
io.setDecode(false);
|
||||||
@@ -402,44 +406,24 @@ void CDStarRX::processData(bool bit)
|
|||||||
|
|
||||||
serial.writeDStarEOT();
|
serial.writeDStarEOT();
|
||||||
|
|
||||||
|
m_maxFrameCorr = 0;
|
||||||
|
m_maxDataCorr = 0;
|
||||||
|
|
||||||
m_rxState = DSRXS_NONE;
|
m_rxState = DSRXS_NONE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fuzzy matching of the data sync bit sequence
|
// Fuzzy matching of the data sync bit sequence
|
||||||
bool syncSeen = false;
|
if (m_minSyncPtr < m_maxSyncPtr) {
|
||||||
if (m_dataBits >= SYNC_SCAN_START && m_dataBits <= (SYNC_POS + 1U)) {
|
if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr)
|
||||||
if (countBits32((m_patternBuffer & DATA_SYNC_MASK) ^ DATA_SYNC_DATA) <= DATA_SYNC_ERRS) {
|
correlateDataSync();
|
||||||
#if defined(WANT_DEBUG)
|
} else {
|
||||||
if (m_dataBits < SYNC_POS)
|
if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr)
|
||||||
DEBUG2("DStarRX: found data sync in Data, early", SYNC_POS - m_dataBits);
|
correlateDataSync();
|
||||||
else
|
|
||||||
DEBUG1("DStarRX: found data sync in Data");
|
|
||||||
#endif
|
|
||||||
m_rxBufferBits = DSTAR_DATA_LENGTH_BITS;
|
|
||||||
m_dataBits = 0U;
|
|
||||||
syncSeen = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if the sync is arriving late
|
|
||||||
if (m_dataBits == SYNC_POS) {
|
|
||||||
for (uint8_t i = 1U; i <= 3U; i++) {
|
|
||||||
uint32_t syncMask = DATA_SYNC_MASK >> i;
|
|
||||||
uint32_t syncData = DATA_SYNC_DATA >> i;
|
|
||||||
if (countBits32((m_patternBuffer & syncMask) ^ syncData) <= DATA_SYNC_ERRS) {
|
|
||||||
DEBUG2("DStarRX: found data sync in Data, late", i);
|
|
||||||
m_rxBufferBits -= i;
|
|
||||||
m_dataBits -= i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_dataBits++;
|
|
||||||
|
|
||||||
// We've not seen a data sync for too long, signal RXLOST and change to RX_NONE
|
// We've not seen a data sync for too long, signal RXLOST and change to RX_NONE
|
||||||
if (m_dataBits >= MAX_SYNC_BITS) {
|
if (m_frameCount >= MAX_FRAMES) {
|
||||||
DEBUG1("DStarRX: data sync timed out, lost lock");
|
DEBUG1("DStarRX: data sync timed out, lost lock");
|
||||||
|
|
||||||
io.setDecode(false);
|
io.setDecode(false);
|
||||||
@@ -447,23 +431,176 @@ void CDStarRX::processData(bool bit)
|
|||||||
|
|
||||||
serial.writeDStarLost();
|
serial.writeDStarLost();
|
||||||
|
|
||||||
|
m_maxFrameCorr = 0;
|
||||||
|
m_maxDataCorr = 0;
|
||||||
|
|
||||||
m_rxState = DSRXS_NONE;
|
m_rxState = DSRXS_NONE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a data frame to the host if the required number of bits have been received, or if a data sync has been seen
|
// Send a data frame to the host if the required number of bits have been received
|
||||||
if (m_rxBufferBits == DSTAR_DATA_LENGTH_BITS) {
|
if (m_dataPtr == m_maxSyncPtr) {
|
||||||
if (syncSeen) {
|
uint8_t buffer[DSTAR_DATA_LENGTH_BYTES + 2U];
|
||||||
m_rxBuffer[9U] = DSTAR_DATA_SYNC_BYTES[9U];
|
samplesToBits(m_dataBuffer, m_startPtr, DSTAR_DATA_LENGTH_SYMBOLS, buffer, DSTAR_DATA_LENGTH_SAMPLES);
|
||||||
m_rxBuffer[10U] = DSTAR_DATA_SYNC_BYTES[10U];
|
|
||||||
m_rxBuffer[11U] = DSTAR_DATA_SYNC_BYTES[11U];
|
if ((m_frameCount % 21U) == 0U) {
|
||||||
|
if (m_frameCount == 0U) {
|
||||||
|
buffer[9U] = DSTAR_DATA_SYNC_BYTES[9U];
|
||||||
|
buffer[10U] = DSTAR_DATA_SYNC_BYTES[10U];
|
||||||
|
buffer[11U] = DSTAR_DATA_SYNC_BYTES[11U];
|
||||||
|
DEBUG5("DStarRX: found start/sync/max/min", m_startPtr, m_syncPtr, m_maxSyncPtr, m_minSyncPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeRSSIData(buffer);
|
||||||
|
} else {
|
||||||
|
serial.writeDStarData(buffer, DSTAR_DATA_LENGTH_BYTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
serial.writeDStarData(m_rxBuffer, DSTAR_DATA_LENGTH_BYTES);
|
m_frameCount++;
|
||||||
|
|
||||||
// Start the next frame
|
m_maxFrameCorr = 0;
|
||||||
::memset(m_rxBuffer, 0x00U, DSTAR_DATA_LENGTH_BYTES + 2U);
|
m_maxDataCorr = 0;
|
||||||
m_rxBufferBits = 0U;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDStarRX::writeRSSIHeader(unsigned char* header)
|
||||||
|
{
|
||||||
|
#if defined(SEND_RSSI_DATA)
|
||||||
|
if (m_rssiCount > 0U) {
|
||||||
|
uint16_t rssi = m_rssiAccum / m_rssiCount;
|
||||||
|
|
||||||
|
header[41U] = (rssi >> 8) & 0xFFU;
|
||||||
|
header[42U] = (rssi >> 0) & 0xFFU;
|
||||||
|
|
||||||
|
serial.writeDStarHeader(header, DSTAR_HEADER_LENGTH_BYTES + 2U);
|
||||||
|
} else {
|
||||||
|
serial.writeDStarHeader(header, DSTAR_HEADER_LENGTH_BYTES + 0U);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
serial.writeDStarHeader(header, DSTAR_HEADER_LENGTH_BYTES + 0U);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_rssiAccum = 0U;
|
||||||
|
m_rssiCount = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDStarRX::writeRSSIData(unsigned char* data)
|
||||||
|
{
|
||||||
|
#if defined(SEND_RSSI_DATA)
|
||||||
|
if (m_rssiCount > 0U) {
|
||||||
|
uint16_t rssi = m_rssiAccum / m_rssiCount;
|
||||||
|
|
||||||
|
data[12U] = (rssi >> 8) & 0xFFU;
|
||||||
|
data[13U] = (rssi >> 0) & 0xFFU;
|
||||||
|
|
||||||
|
serial.writeDStarData(data, DSTAR_DATA_LENGTH_BYTES + 2U);
|
||||||
|
} else {
|
||||||
|
serial.writeDStarData(data, DSTAR_DATA_LENGTH_BYTES + 0U);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
serial.writeDStarData(data, DSTAR_DATA_LENGTH_BYTES + 0U);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_rssiAccum = 0U;
|
||||||
|
m_rssiCount = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDStarRX::correlateFrameSync()
|
||||||
|
{
|
||||||
|
if (countBits64((m_bitBuffer[m_bitPtr] & DSTAR_FRAME_SYNC_MASK) ^ DSTAR_FRAME_SYNC_DATA) <= FRAME_SYNC_ERRS) {
|
||||||
|
uint16_t ptr = m_dataPtr + DSTAR_DATA_LENGTH_SAMPLES - DSTAR_FRAME_SYNC_LENGTH_SAMPLES + DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
|
if (ptr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||||
|
ptr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||||
|
|
||||||
|
q31_t corr = 0;
|
||||||
|
|
||||||
|
for (uint8_t i = 0U; i < DSTAR_FRAME_SYNC_LENGTH_SYMBOLS; i++) {
|
||||||
|
q15_t val = m_dataBuffer[ptr];
|
||||||
|
|
||||||
|
if (DSTAR_FRAME_SYNC_SYMBOLS[i])
|
||||||
|
corr -= val;
|
||||||
|
else
|
||||||
|
corr += val;
|
||||||
|
|
||||||
|
ptr += DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
|
if (ptr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||||
|
ptr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (corr > m_maxFrameCorr) {
|
||||||
|
m_maxFrameCorr = corr;
|
||||||
|
m_headerPtr = 0U;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDStarRX::correlateDataSync()
|
||||||
|
{
|
||||||
|
uint8_t maxErrs = 0U;
|
||||||
|
if (m_rxState == DSRXS_DATA)
|
||||||
|
maxErrs = DATA_SYNC_ERRS;
|
||||||
|
|
||||||
|
if (countBits64((m_bitBuffer[m_bitPtr] & DSTAR_DATA_SYNC_MASK) ^ DSTAR_DATA_SYNC_DATA) <= maxErrs) {
|
||||||
|
uint16_t ptr = m_dataPtr + DSTAR_DATA_LENGTH_SAMPLES - DSTAR_DATA_SYNC_LENGTH_SAMPLES + DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
|
if (ptr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||||
|
ptr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||||
|
|
||||||
|
q31_t corr = 0;
|
||||||
|
|
||||||
|
for (uint8_t i = 0U; i < DSTAR_DATA_SYNC_LENGTH_SYMBOLS; i++) {
|
||||||
|
q15_t val = m_dataBuffer[ptr];
|
||||||
|
|
||||||
|
if (DSTAR_DATA_SYNC_SYMBOLS[i])
|
||||||
|
corr -= val;
|
||||||
|
else
|
||||||
|
corr += val;
|
||||||
|
|
||||||
|
ptr += DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
|
if (ptr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||||
|
ptr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (corr > m_maxDataCorr) {
|
||||||
|
m_maxDataCorr = corr;
|
||||||
|
m_frameCount = 0U;
|
||||||
|
|
||||||
|
m_syncPtr = m_dataPtr;
|
||||||
|
|
||||||
|
m_startPtr = m_dataPtr + DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
|
if (m_startPtr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||||
|
m_startPtr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||||
|
|
||||||
|
m_maxSyncPtr = m_syncPtr + 1U;
|
||||||
|
if (m_maxSyncPtr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||||
|
m_maxSyncPtr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||||
|
|
||||||
|
m_minSyncPtr = m_syncPtr + DSTAR_DATA_LENGTH_SAMPLES - 1U;
|
||||||
|
if (m_minSyncPtr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||||
|
m_minSyncPtr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDStarRX::samplesToBits(const q15_t* inBuffer, uint16_t start, uint16_t count, uint8_t* outBuffer, uint16_t limit)
|
||||||
|
{
|
||||||
|
for (uint16_t i = 0U; i < count; i++) {
|
||||||
|
q15_t sample = inBuffer[start];
|
||||||
|
|
||||||
|
if (sample < 0)
|
||||||
|
WRITE_BIT2(outBuffer, i, true);
|
||||||
|
else
|
||||||
|
WRITE_BIT2(outBuffer, i, false);
|
||||||
|
|
||||||
|
start += DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
|
if (start >= limit)
|
||||||
|
start -= limit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,3 +820,5 @@ bool CDStarRX::checksum(const uint8_t* header) const
|
|||||||
return crc8[0U] == header[DSTAR_HEADER_LENGTH_BYTES - 2U] && crc8[1U] == header[DSTAR_HEADER_LENGTH_BYTES - 1U];
|
return crc8[0U] == header[DSTAR_HEADER_LENGTH_BYTES - 2U] && crc8[1U] == header[DSTAR_HEADER_LENGTH_BYTES - 1U];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2016,2017,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,10 +16,13 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DSTAR)
|
||||||
|
|
||||||
#if !defined(DSTARRX_H)
|
#if !defined(DSTARRX_H)
|
||||||
#define DSTARRX_H
|
#define DSTARRX_H
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
#include "DStarDefines.h"
|
#include "DStarDefines.h"
|
||||||
|
|
||||||
enum DSRX_STATE {
|
enum DSRX_STATE {
|
||||||
@@ -32,18 +35,26 @@ class CDStarRX {
|
|||||||
public:
|
public:
|
||||||
CDStarRX();
|
CDStarRX();
|
||||||
|
|
||||||
void samples(const q15_t* samples, uint8_t length);
|
void samples(const q15_t* samples, const uint16_t* rssi, uint8_t length);
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t m_pll;
|
|
||||||
bool m_prev;
|
|
||||||
DSRX_STATE m_rxState;
|
DSRX_STATE m_rxState;
|
||||||
uint32_t m_patternBuffer;
|
uint64_t m_bitBuffer[DSTAR_RADIO_SYMBOL_LENGTH];
|
||||||
uint8_t m_rxBuffer[100U];
|
q15_t m_headerBuffer[DSTAR_FEC_SECTION_LENGTH_SAMPLES + 2U * DSTAR_RADIO_SYMBOL_LENGTH];
|
||||||
unsigned int m_rxBufferBits;
|
q15_t m_dataBuffer[DSTAR_DATA_LENGTH_SAMPLES];
|
||||||
unsigned int m_dataBits;
|
uint16_t m_bitPtr;
|
||||||
|
uint16_t m_headerPtr;
|
||||||
|
uint16_t m_dataPtr;
|
||||||
|
uint16_t m_startPtr;
|
||||||
|
uint16_t m_syncPtr;
|
||||||
|
uint16_t m_minSyncPtr;
|
||||||
|
uint16_t m_maxSyncPtr;
|
||||||
|
q31_t m_maxFrameCorr;
|
||||||
|
q31_t m_maxDataCorr;
|
||||||
|
uint16_t m_frameCount;
|
||||||
|
uint8_t m_countdown;
|
||||||
unsigned int m_mar;
|
unsigned int m_mar;
|
||||||
int m_pathMetric[4U];
|
int m_pathMetric[4U];
|
||||||
unsigned int m_pathMemory0[42U];
|
unsigned int m_pathMemory0[42U];
|
||||||
@@ -51,12 +62,17 @@ private:
|
|||||||
unsigned int m_pathMemory2[42U];
|
unsigned int m_pathMemory2[42U];
|
||||||
unsigned int m_pathMemory3[42U];
|
unsigned int m_pathMemory3[42U];
|
||||||
uint8_t m_fecOutput[42U];
|
uint8_t m_fecOutput[42U];
|
||||||
q15_t m_samples[DSTAR_DATA_SYNC_LENGTH_BITS];
|
uint32_t m_rssiAccum;
|
||||||
uint8_t m_samplesPtr;
|
uint16_t m_rssiCount;
|
||||||
|
|
||||||
void processNone(bool bit);
|
void processNone(q15_t sample);
|
||||||
void processHeader(bool bit);
|
void processHeader(q15_t sample);
|
||||||
void processData(bool bit);
|
void processData();
|
||||||
|
bool correlateFrameSync();
|
||||||
|
bool correlateDataSync();
|
||||||
|
void samplesToBits(const q15_t* inBuffer, uint16_t start, uint16_t count, uint8_t* outBuffer, uint16_t limit);
|
||||||
|
void writeRSSIHeader(unsigned char* header);
|
||||||
|
void writeRSSIData(unsigned char* data);
|
||||||
bool rxHeader(uint8_t* in, uint8_t* out);
|
bool rxHeader(uint8_t* in, uint8_t* out);
|
||||||
void acs(int* metric);
|
void acs(int* metric);
|
||||||
void viterbiDecode(int* data);
|
void viterbiDecode(int* data);
|
||||||
@@ -66,3 +82,5 @@ private:
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2009-2017,2020 by Jonathan Naylor G4KLX
|
||||||
|
* Copyright (C) 2017 by Andy Uribe CA6JAU
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,9 +17,10 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define WANT_DEBUG
|
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DSTAR)
|
||||||
|
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "DStarTX.h"
|
#include "DStarTX.h"
|
||||||
|
|
||||||
@@ -28,12 +30,12 @@ const uint8_t BIT_SYNC = 0xAAU;
|
|||||||
|
|
||||||
const uint8_t FRAME_SYNC[] = {0xEAU, 0xA6U, 0x00U};
|
const uint8_t FRAME_SYNC[] = {0xEAU, 0xA6U, 0x00U};
|
||||||
|
|
||||||
// Generated using gaussfir(0.5, 4, 5) in MATLAB
|
// Generated using gaussfir(0.35, 1, 5) in MATLAB
|
||||||
static q15_t DSTAR_GMSK_FILTER[] = {8, 104, 760, 3158, 7421, 9866, 7421, 3158, 760, 104, 8, 0};
|
static q15_t GAUSSIAN_0_35_FILTER[] = {0, 0, 0, 0, 1001, 3514, 9333, 18751, 28499, 32767, 28499, 18751, 9333, 3514, 1001}; // numTaps = 15, L = 5
|
||||||
const uint16_t DSTAR_GMSK_FILTER_LEN = 12U;
|
const uint16_t GAUSSIAN_0_35_FILTER_PHASE_LEN = 3U; // phaseLength = numTaps/L
|
||||||
|
|
||||||
const q15_t DSTAR_LEVEL0[] = {-800, -800, -800, -800, -800};
|
const q15_t DSTAR_LEVEL0 = -841;
|
||||||
const q15_t DSTAR_LEVEL1[] = { 800, 800, 800, 800, 800};
|
const q15_t DSTAR_LEVEL1 = 841;
|
||||||
|
|
||||||
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||||
|
|
||||||
@@ -195,14 +197,14 @@ m_modState(),
|
|||||||
m_poBuffer(),
|
m_poBuffer(),
|
||||||
m_poLen(0U),
|
m_poLen(0U),
|
||||||
m_poPtr(0U),
|
m_poPtr(0U),
|
||||||
m_txDelay(60U), // 100ms
|
m_txDelay(60U) // 100ms
|
||||||
m_count(0U)
|
|
||||||
{
|
{
|
||||||
::memset(m_modState, 0x00U, 60U * sizeof(q15_t));
|
::memset(m_modState, 0x00U, 20U * sizeof(q15_t));
|
||||||
|
|
||||||
m_modFilter.numTaps = DSTAR_GMSK_FILTER_LEN;
|
m_modFilter.L = DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
m_modFilter.pState = m_modState;
|
m_modFilter.phaseLength = GAUSSIAN_0_35_FILTER_PHASE_LEN;
|
||||||
m_modFilter.pCoeffs = DSTAR_GMSK_FILTER;
|
m_modFilter.pCoeffs = GAUSSIAN_0_35_FILTER;
|
||||||
|
m_modFilter.pState = m_modState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDStarTX::process()
|
void CDStarTX::process()
|
||||||
@@ -214,17 +216,16 @@ void CDStarTX::process()
|
|||||||
|
|
||||||
if (type == DSTAR_HEADER && m_poLen == 0U) {
|
if (type == DSTAR_HEADER && m_poLen == 0U) {
|
||||||
if (!m_tx) {
|
if (!m_tx) {
|
||||||
m_count = 0U;
|
|
||||||
|
|
||||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||||
m_poBuffer[m_poLen++] = BIT_SYNC;
|
m_poBuffer[m_poLen++] = BIT_SYNC;
|
||||||
} else {
|
} else {
|
||||||
// Pop the type byte off
|
// Pop the type byte off
|
||||||
m_buffer.get();
|
uint8_t dummy;
|
||||||
|
m_buffer.get(dummy);
|
||||||
|
|
||||||
uint8_t header[DSTAR_HEADER_LENGTH_BYTES];
|
uint8_t header[DSTAR_HEADER_LENGTH_BYTES];
|
||||||
for (uint8_t i = 0U; i < DSTAR_HEADER_LENGTH_BYTES; i++)
|
for (uint8_t i = 0U; i < DSTAR_HEADER_LENGTH_BYTES; i++)
|
||||||
header[i] = m_buffer.get();
|
m_buffer.get(header[i]);
|
||||||
|
|
||||||
uint8_t buffer[86U];
|
uint8_t buffer[86U];
|
||||||
txHeader(header, buffer + 2U);
|
txHeader(header, buffer + 2U);
|
||||||
@@ -241,25 +242,24 @@ void CDStarTX::process()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type == DSTAR_DATA && m_poLen == 0U) {
|
if (type == DSTAR_DATA && m_poLen == 0U) {
|
||||||
if (!m_tx)
|
|
||||||
m_count = 0U;
|
|
||||||
|
|
||||||
// Pop the type byte off
|
// Pop the type byte off
|
||||||
m_buffer.get();
|
uint8_t dummy;
|
||||||
|
m_buffer.get(dummy);
|
||||||
|
|
||||||
for (uint8_t i = 0U; i < DSTAR_DATA_LENGTH_BYTES; i++)
|
for (uint8_t i = 0U; i < DSTAR_DATA_LENGTH_BYTES; i++)
|
||||||
m_poBuffer[m_poLen++] = m_buffer.get();
|
m_buffer.get(m_poBuffer[m_poLen++]);
|
||||||
|
|
||||||
m_poPtr = 0U;
|
m_poPtr = 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == DSTAR_EOT && m_poLen == 0U) {
|
if (type == DSTAR_EOT && m_poLen == 0U) {
|
||||||
// Pop the type byte off
|
// Pop the type byte off
|
||||||
m_buffer.get();
|
uint8_t dummy;
|
||||||
|
m_buffer.get(dummy);
|
||||||
|
|
||||||
for (uint8_t j = 0U; j < 3U; j++) {
|
for (uint8_t j = 0U; j < 3U; j++) {
|
||||||
for (uint8_t i = 0U; i < DSTAR_EOT_LENGTH_BYTES; i++)
|
for (uint8_t i = 0U; i < DSTAR_END_SYNC_LENGTH_BYTES; i++)
|
||||||
m_poBuffer[m_poLen++] = DSTAR_EOT_BYTES[i];
|
m_poBuffer[m_poLen++] = DSTAR_END_SYNC_BYTES[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
m_poPtr = 0U;
|
m_poPtr = 0U;
|
||||||
@@ -268,11 +268,11 @@ void CDStarTX::process()
|
|||||||
if (m_poLen > 0U) {
|
if (m_poLen > 0U) {
|
||||||
uint16_t space = io.getSpace();
|
uint16_t space = io.getSpace();
|
||||||
|
|
||||||
while (space > (8U * DSTAR_RADIO_BIT_LENGTH)) {
|
while (space > (8U * DSTAR_RADIO_SYMBOL_LENGTH)) {
|
||||||
uint8_t c = m_poBuffer[m_poPtr++];
|
uint8_t c = m_poBuffer[m_poPtr++];
|
||||||
writeByte(c);
|
writeByte(c);
|
||||||
|
|
||||||
space -= 8U * DSTAR_RADIO_BIT_LENGTH;
|
space -= 8U * DSTAR_RADIO_SYMBOL_LENGTH;
|
||||||
|
|
||||||
if (m_poPtr >= m_poLen) {
|
if (m_poPtr >= m_poLen) {
|
||||||
m_poPtr = 0U;
|
m_poPtr = 0U;
|
||||||
@@ -283,7 +283,7 @@ void CDStarTX::process()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t CDStarTX::writeHeader(const uint8_t* header, uint8_t length)
|
uint8_t CDStarTX::writeHeader(const uint8_t* header, uint16_t length)
|
||||||
{
|
{
|
||||||
if (length != DSTAR_HEADER_LENGTH_BYTES)
|
if (length != DSTAR_HEADER_LENGTH_BYTES)
|
||||||
return 4U;
|
return 4U;
|
||||||
@@ -302,7 +302,7 @@ uint8_t CDStarTX::writeHeader(const uint8_t* header, uint8_t length)
|
|||||||
return 0U;
|
return 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t CDStarTX::writeData(const uint8_t* data, uint8_t length)
|
uint8_t CDStarTX::writeData(const uint8_t* data, uint16_t length)
|
||||||
{
|
{
|
||||||
if (length != DSTAR_DATA_LENGTH_BYTES)
|
if (length != DSTAR_DATA_LENGTH_BYTES)
|
||||||
return 4U;
|
return 4U;
|
||||||
@@ -394,7 +394,7 @@ void CDStarTX::txHeader(const uint8_t* in, uint8_t* out) const
|
|||||||
if (i < 660U) {
|
if (i < 660U) {
|
||||||
if (d & 0x08U)
|
if (d & 0x08U)
|
||||||
out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]);
|
out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]);
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
if (d & 0x04U)
|
if (d & 0x04U)
|
||||||
out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]);
|
out[INTERLEAVE_TABLE_TX[i * 2U]] |= (0x01U << INTERLEAVE_TABLE_TX[i * 2U + 1U]);
|
||||||
@@ -417,51 +417,37 @@ void CDStarTX::txHeader(const uint8_t* in, uint8_t* out) const
|
|||||||
|
|
||||||
void CDStarTX::writeByte(uint8_t c)
|
void CDStarTX::writeByte(uint8_t c)
|
||||||
{
|
{
|
||||||
q15_t inBuffer[DSTAR_RADIO_BIT_LENGTH * 8U + 1U];
|
q15_t inBuffer[8U];
|
||||||
q15_t outBuffer[DSTAR_RADIO_BIT_LENGTH * 8U + 1U];
|
q15_t outBuffer[DSTAR_RADIO_SYMBOL_LENGTH * 8U];
|
||||||
|
|
||||||
uint8_t mask = 0x01U;
|
uint8_t mask = 0x01U;
|
||||||
|
|
||||||
q15_t* p = inBuffer;
|
for (uint8_t i = 0U; i < 8U; i++) {
|
||||||
for (uint8_t i = 0U; i < 8U; i++, p += DSTAR_RADIO_BIT_LENGTH) {
|
|
||||||
if ((c & mask) == mask)
|
if ((c & mask) == mask)
|
||||||
::memcpy(p, DSTAR_LEVEL0, DSTAR_RADIO_BIT_LENGTH * sizeof(q15_t));
|
inBuffer[i] = DSTAR_LEVEL0;
|
||||||
else
|
else
|
||||||
::memcpy(p, DSTAR_LEVEL1, DSTAR_RADIO_BIT_LENGTH * sizeof(q15_t));
|
inBuffer[i] = DSTAR_LEVEL1;
|
||||||
|
|
||||||
mask <<= 1;
|
mask <<= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t blockSize = DSTAR_RADIO_BIT_LENGTH * 8U;
|
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 8U);
|
||||||
|
|
||||||
// Handle the case of the oscillator not being accurate enough
|
io.write(STATE_DSTAR, outBuffer, DSTAR_RADIO_SYMBOL_LENGTH * 8U);
|
||||||
if (m_sampleCount > 0U) {
|
|
||||||
m_count += DSTAR_RADIO_BIT_LENGTH * 8U;
|
|
||||||
|
|
||||||
if (m_count >= m_sampleCount) {
|
|
||||||
if (m_sampleInsert) {
|
|
||||||
inBuffer[DSTAR_RADIO_BIT_LENGTH * 8U] = inBuffer[DSTAR_RADIO_BIT_LENGTH * 8U - 1U];
|
|
||||||
blockSize++;
|
|
||||||
} else {
|
|
||||||
blockSize--;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_count -= m_sampleCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
|
|
||||||
|
|
||||||
io.write(STATE_DSTAR, outBuffer, blockSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDStarTX::setTXDelay(uint8_t delay)
|
void CDStarTX::setTXDelay(uint8_t delay)
|
||||||
{
|
{
|
||||||
m_txDelay = 150U + uint16_t(delay) * 6U; // 250ms + tx delay
|
m_txDelay = 300U + uint16_t(delay) * 6U; // 250ms + tx delay
|
||||||
|
|
||||||
|
if (m_txDelay > 600U)
|
||||||
|
m_txDelay = 600U;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CDStarTX::getSpace() const
|
uint8_t CDStarTX::getSpace() const
|
||||||
{
|
{
|
||||||
return m_buffer.getSpace() / (DSTAR_DATA_LENGTH_BYTES + 1U);
|
return m_buffer.getSpace() / (DSTAR_DATA_LENGTH_BYTES + 1U);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2016,2017,2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,36 +16,37 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_DSTAR)
|
||||||
|
|
||||||
#if !defined(DSTARTX_H)
|
#if !defined(DSTARTX_H)
|
||||||
#define DSTARTX_H
|
#define DSTARTX_H
|
||||||
|
|
||||||
#include "Config.h"
|
#include "RingBuffer.h"
|
||||||
|
|
||||||
#include "SerialRB.h"
|
|
||||||
|
|
||||||
class CDStarTX {
|
class CDStarTX {
|
||||||
public:
|
public:
|
||||||
CDStarTX();
|
CDStarTX();
|
||||||
|
|
||||||
uint8_t writeHeader(const uint8_t* header, uint8_t length);
|
uint8_t writeHeader(const uint8_t* header, uint16_t length);
|
||||||
uint8_t writeData(const uint8_t* data, uint8_t length);
|
uint8_t writeData(const uint8_t* data, uint16_t length);
|
||||||
uint8_t writeEOT();
|
uint8_t writeEOT();
|
||||||
|
|
||||||
void process();
|
void process();
|
||||||
|
|
||||||
void setTXDelay(uint8_t delay);
|
void setTXDelay(uint8_t delay);
|
||||||
|
|
||||||
uint16_t getSpace() const;
|
uint8_t getSpace() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CSerialRB m_buffer;
|
CRingBuffer<uint8_t> m_buffer;
|
||||||
arm_fir_instance_q15 m_modFilter;
|
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||||
q15_t m_modState[60U]; // NoTaps + BlockSize - 1, 12 + 40 - 1 plus some spare
|
q15_t m_modState[20U]; // blockSize + phaseLength - 1, 8 + 9 - 1 plus some spare
|
||||||
uint8_t m_poBuffer[500U];
|
uint8_t m_poBuffer[600U];
|
||||||
uint16_t m_poLen;
|
uint16_t m_poLen;
|
||||||
uint16_t m_poPtr;
|
uint16_t m_poPtr;
|
||||||
uint16_t m_txDelay; // In bytes
|
uint16_t m_txDelay; // In bytes
|
||||||
uint32_t m_count;
|
|
||||||
|
|
||||||
void txHeader(const uint8_t* in, uint8_t* out) const;
|
void txHeader(const uint8_t* in, uint8_t* out) const;
|
||||||
void writeByte(uint8_t c);
|
void writeByte(uint8_t c);
|
||||||
@@ -53,3 +54,5 @@ private:
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
+2
-16
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2015,2016,2017,2021 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,9 +20,6 @@
|
|||||||
#define DEBUG_H
|
#define DEBUG_H
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
#if defined(WANT_DEBUG)
|
|
||||||
|
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
|
|
||||||
#define DEBUG1(a) serial.writeDebug((a))
|
#define DEBUG1(a) serial.writeDebug((a))
|
||||||
@@ -30,18 +27,7 @@
|
|||||||
#define DEBUG3(a,b,c) serial.writeDebug((a),(b),(c))
|
#define DEBUG3(a,b,c) serial.writeDebug((a),(b),(c))
|
||||||
#define DEBUG4(a,b,c,d) serial.writeDebug((a),(b),(c),(d))
|
#define DEBUG4(a,b,c,d) serial.writeDebug((a),(b),(c),(d))
|
||||||
#define DEBUG5(a,b,c,d,e) serial.writeDebug((a),(b),(c),(d),(e))
|
#define DEBUG5(a,b,c,d,e) serial.writeDebug((a),(b),(c),(d),(e))
|
||||||
#define ASSERT(a) serial.writeAssert((a),#a,__FILE__,__LINE__)
|
#define DEBUG_DUMP(a,b) serial.writeDebugDump((a),(b))
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define DEBUG1(a)
|
|
||||||
#define DEBUG2(a,b)
|
|
||||||
#define DEBUG3(a,b,c)
|
|
||||||
#define DEBUG4(a,b,c,d)
|
|
||||||
#define DEBUG5(a,b,c,d,e)
|
|
||||||
#define ASSERT(a)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2015,2020 by Jonathan Naylor G4KLX
|
||||||
|
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||||
|
* Copyright (C) 2020 by Phil Taylor M0VSE
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "CalFM.h"
|
||||||
|
|
||||||
|
|
||||||
|
const struct TONE_TABLE {
|
||||||
|
uint16_t frequency;
|
||||||
|
uint16_t length;
|
||||||
|
q31_t increment;
|
||||||
|
} TONE_TABLE_DATA[] = {
|
||||||
|
{2495U, 10U, 223248821},
|
||||||
|
{2079U, 12U, 186025772},
|
||||||
|
{1633U, 15U, 146118367},
|
||||||
|
{1247U, 19U, 111579672},
|
||||||
|
{1039U, 23U, 93012886},
|
||||||
|
{956U, 25U, 85541432}};
|
||||||
|
|
||||||
|
const uint8_t TONE_TABLE_DATA_LEN = 6U;
|
||||||
|
|
||||||
|
CCalFM::CCalFM() :
|
||||||
|
m_frequency(0),
|
||||||
|
m_length(0),
|
||||||
|
m_tone(NULL),
|
||||||
|
m_level(128 * 12),
|
||||||
|
m_transmit(false),
|
||||||
|
m_audioSeq(0),
|
||||||
|
m_lastState(STATE_IDLE)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCalFM::process()
|
||||||
|
{
|
||||||
|
const TONE_TABLE* entry = NULL;
|
||||||
|
|
||||||
|
if (m_modemState != m_lastState)
|
||||||
|
{
|
||||||
|
switch (m_modemState) {
|
||||||
|
case STATE_FMCAL10K:
|
||||||
|
m_frequency = 956U;
|
||||||
|
break;
|
||||||
|
case STATE_FMCAL12K:
|
||||||
|
m_frequency = 1039U;
|
||||||
|
break;
|
||||||
|
case STATE_FMCAL15K:
|
||||||
|
m_frequency = 1247U;
|
||||||
|
break;
|
||||||
|
case STATE_FMCAL20K:
|
||||||
|
m_frequency = 1633U;
|
||||||
|
break;
|
||||||
|
case STATE_FMCAL25K:
|
||||||
|
m_frequency = 2079U;
|
||||||
|
break;
|
||||||
|
case STATE_FMCAL30K:
|
||||||
|
m_frequency = 2495U;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
m_frequency = 0U;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0U; i < TONE_TABLE_DATA_LEN; i++) {
|
||||||
|
if (TONE_TABLE_DATA[i].frequency == m_frequency) {
|
||||||
|
entry = TONE_TABLE_DATA + i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_length = entry->length;
|
||||||
|
|
||||||
|
delete[] m_tone;
|
||||||
|
m_tone = new q15_t[m_length];
|
||||||
|
|
||||||
|
q31_t arg = 0;
|
||||||
|
for (uint16_t i = 0U; i < m_length; i++) {
|
||||||
|
q63_t value = ::arm_sin_q31(arg) * q63_t(m_level);
|
||||||
|
m_tone[i] = q15_t(__SSAT((value >> 31), 16));
|
||||||
|
|
||||||
|
arg += entry->increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lastState=m_modemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_transmit)
|
||||||
|
{
|
||||||
|
uint16_t space = io.getSpace();
|
||||||
|
while (space > m_length)
|
||||||
|
{
|
||||||
|
io.write(m_modemState,m_tone,m_length);
|
||||||
|
space -= m_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t CCalFM::write(const uint8_t* data, uint16_t length)
|
||||||
|
{
|
||||||
|
if (length != 1U)
|
||||||
|
return 4U;
|
||||||
|
|
||||||
|
m_transmit = data[0U] == 1U;
|
||||||
|
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2015,2020 by Jonathan Naylor G4KLX
|
||||||
|
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||||
|
* Copyright (C) 2020 by Phil Taylor M0VSE
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#if !defined(CALFM_H)
|
||||||
|
#define CALFM_H
|
||||||
|
|
||||||
|
class CCalFM {
|
||||||
|
public:
|
||||||
|
CCalFM();
|
||||||
|
|
||||||
|
void process();
|
||||||
|
void fm10kcal();
|
||||||
|
void fm12k5cal();
|
||||||
|
void fm15kcal();
|
||||||
|
void fm20kcal();
|
||||||
|
void fm25kcal();
|
||||||
|
void fm30kcal();
|
||||||
|
|
||||||
|
uint8_t write(const uint8_t* data, uint16_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t m_frequency;
|
||||||
|
uint16_t m_length;
|
||||||
|
q15_t* m_tone;
|
||||||
|
q15_t m_level;
|
||||||
|
bool m_transmit;
|
||||||
|
uint8_t m_audioSeq;
|
||||||
|
uint8_t m_lastState;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
+1232
File diff suppressed because it is too large
Load Diff
+158
@@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020,2021 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#if !defined(FM_H)
|
||||||
|
#define FM_H
|
||||||
|
|
||||||
|
#include "FMBlanking.h"
|
||||||
|
#include "FMCTCSSRX.h"
|
||||||
|
#include "FMCTCSSTX.h"
|
||||||
|
#include "FMTimeout.h"
|
||||||
|
#include "FMKeyer.h"
|
||||||
|
#include "FMTimer.h"
|
||||||
|
#include "RingBuffer.h"
|
||||||
|
#include "FMDirectForm1.h"
|
||||||
|
#include "FMDownSampler.h"
|
||||||
|
#include "FMUpSampler.h"
|
||||||
|
#include "FMNoiseSquelch.h"
|
||||||
|
|
||||||
|
enum FM_STATE {
|
||||||
|
FS_LISTENING,
|
||||||
|
FS_KERCHUNK_RF,
|
||||||
|
FS_RELAYING_RF,
|
||||||
|
FS_RELAYING_WAIT_RF,
|
||||||
|
FS_TIMEOUT_RF,
|
||||||
|
FS_TIMEOUT_WAIT_RF,
|
||||||
|
FS_KERCHUNK_EXT,
|
||||||
|
FS_RELAYING_EXT,
|
||||||
|
FS_RELAYING_WAIT_EXT,
|
||||||
|
FS_TIMEOUT_EXT,
|
||||||
|
FS_TIMEOUT_WAIT_EXT,
|
||||||
|
FS_HANG
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CFM {
|
||||||
|
public:
|
||||||
|
CFM();
|
||||||
|
|
||||||
|
void samples(bool cos, q15_t* samples, uint8_t length);
|
||||||
|
|
||||||
|
void process();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch);
|
||||||
|
uint8_t setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level);
|
||||||
|
uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, uint8_t accessMode, bool linkMode, bool cosInvert, bool noiseSquelch, uint8_t squelchHighThreshold, uint8_t squelchLowThreshold, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel);
|
||||||
|
uint8_t setExt(const char* ack, uint8_t audioBoost, uint8_t speed, uint16_t frequency, uint8_t level);
|
||||||
|
|
||||||
|
uint8_t getSpace() const;
|
||||||
|
|
||||||
|
uint8_t writeData(const uint8_t* data, uint8_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CFMKeyer m_callsign;
|
||||||
|
CFMKeyer m_rfAck;
|
||||||
|
CFMKeyer m_extAck;
|
||||||
|
CFMCTCSSRX m_ctcssRX;
|
||||||
|
CFMCTCSSTX m_ctcssTX;
|
||||||
|
CFMNoiseSquelch m_squelch;
|
||||||
|
CFMTimeout m_timeoutTone;
|
||||||
|
FM_STATE m_state;
|
||||||
|
bool m_callsignAtStart;
|
||||||
|
bool m_callsignAtEnd;
|
||||||
|
bool m_callsignAtLatch;
|
||||||
|
CFMTimer m_callsignTimer;
|
||||||
|
CFMTimer m_timeoutTimer;
|
||||||
|
CFMTimer m_holdoffTimer;
|
||||||
|
CFMTimer m_kerchunkTimer;
|
||||||
|
CFMTimer m_ackMinTimer;
|
||||||
|
CFMTimer m_ackDelayTimer;
|
||||||
|
CFMTimer m_hangTimer;
|
||||||
|
CFMTimer m_statusTimer;
|
||||||
|
CFMTimer m_reverseTimer;
|
||||||
|
bool m_needReverse;
|
||||||
|
CFMDirectFormI m_filterStage1;
|
||||||
|
CFMDirectFormI m_filterStage2;
|
||||||
|
CFMDirectFormI m_filterStage3;
|
||||||
|
CFMBlanking m_blanking;
|
||||||
|
uint8_t m_accessMode;
|
||||||
|
bool m_linkMode;
|
||||||
|
bool m_cosInvert;
|
||||||
|
bool m_noiseSquelch;
|
||||||
|
q15_t m_rfAudioBoost;
|
||||||
|
q15_t m_extAudioBoost;
|
||||||
|
CFMDownSampler m_downSampler;
|
||||||
|
bool m_extEnabled;
|
||||||
|
q15_t m_rxLevel;
|
||||||
|
CRingBuffer<q15_t> m_inputRFRB;
|
||||||
|
CRingBuffer<q15_t> m_outputRFRB;
|
||||||
|
CFMUpSampler m_inputExtRB;
|
||||||
|
bool m_rfSignal;
|
||||||
|
bool m_extSignal;
|
||||||
|
|
||||||
|
void stateMachine(bool validRFSignal, bool validExtSignal);
|
||||||
|
|
||||||
|
void repeaterSamples(bool cos, q15_t* samples, uint8_t length);
|
||||||
|
void linkSamples(bool cos, q15_t* samples, uint8_t length);
|
||||||
|
|
||||||
|
void duplexStateMachine(bool validRFSignal, bool validExtSignal);
|
||||||
|
void listeningStateDuplex(bool validRFSignal, bool validExtSignal);
|
||||||
|
void kerchunkRFStateDuplex(bool validSignal);
|
||||||
|
void relayingRFStateDuplex(bool validSignal);
|
||||||
|
void relayingRFWaitStateDuplex(bool validSignal);
|
||||||
|
void timeoutRFStateDuplex(bool validSignal);
|
||||||
|
void timeoutRFWaitStateDuplex(bool validSignal);
|
||||||
|
void kerchunkExtStateDuplex(bool validSignal);
|
||||||
|
void relayingExtStateDuplex(bool validSignal);
|
||||||
|
void relayingExtWaitStateDuplex(bool validSignal);
|
||||||
|
void timeoutExtStateDuplex(bool validSignal);
|
||||||
|
void timeoutExtWaitStateDuplex(bool validSignal);
|
||||||
|
void hangStateDuplex(bool validRFSignal, bool validExtSignal);
|
||||||
|
|
||||||
|
void simplexStateMachine(bool validRFSignal, bool validExtSignal);
|
||||||
|
void listeningStateSimplex(bool validRFSignal, bool validExtSignal);
|
||||||
|
void relayingRFStateSimplex(bool validSignal);
|
||||||
|
void relayingRFWaitStateSimplex(bool validSignal);
|
||||||
|
void timeoutRFStateSimplex(bool validSignal);
|
||||||
|
void timeoutRFWaitStateSimplex(bool validSignal);
|
||||||
|
void relayingExtStateSimplex(bool validSignal);
|
||||||
|
void relayingExtWaitStateSimplex(bool validSignal);
|
||||||
|
void timeoutExtStateSimplex(bool validSignal);
|
||||||
|
void timeoutExtWaitStateSimplex(bool validSignal);
|
||||||
|
|
||||||
|
void linkStateMachine(bool validRFSignal, bool validExtSignal);
|
||||||
|
|
||||||
|
void clock(uint8_t length);
|
||||||
|
|
||||||
|
void sendCallsign();
|
||||||
|
void beginRelaying();
|
||||||
|
|
||||||
|
void insertDelay(uint16_t ms);
|
||||||
|
void insertSilence(uint16_t ms);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "FMBlanking.h"
|
||||||
|
|
||||||
|
// 2000 Hz sine wave at 24000 Hz sample rate
|
||||||
|
const q31_t TONE[] = {0, 16384, 28378, 32767, 28378, 16384, 0, -16383, -28377, -32767, -28377, -16383};
|
||||||
|
|
||||||
|
const uint8_t TONE_LEN = 12U;
|
||||||
|
|
||||||
|
const uint32_t BLEEP_LEN = 2400U; // 100ms
|
||||||
|
|
||||||
|
CFMBlanking::CFMBlanking() :
|
||||||
|
m_posValue(0),
|
||||||
|
m_negValue(0),
|
||||||
|
m_level(128 * 128),
|
||||||
|
m_running(false),
|
||||||
|
m_pos(0U),
|
||||||
|
m_n(0U)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFMBlanking::setParams(uint8_t value, uint8_t level)
|
||||||
|
{
|
||||||
|
m_posValue = q15_t(value * 128);
|
||||||
|
m_negValue = -m_posValue;
|
||||||
|
|
||||||
|
m_level = q15_t(level * 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
q15_t CFMBlanking::process(q15_t sample)
|
||||||
|
{
|
||||||
|
if (m_posValue == 0)
|
||||||
|
return sample;
|
||||||
|
|
||||||
|
if (!m_running) {
|
||||||
|
if (sample >= m_posValue) {
|
||||||
|
m_running = true;
|
||||||
|
m_pos = 0U;
|
||||||
|
m_n = 0U;
|
||||||
|
} else if (sample <= m_negValue) {
|
||||||
|
m_running = true;
|
||||||
|
m_pos = 0U;
|
||||||
|
m_n = 0U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_running)
|
||||||
|
return sample;
|
||||||
|
|
||||||
|
if (m_pos <= BLEEP_LEN) {
|
||||||
|
q31_t value = TONE[m_n++] * m_level;
|
||||||
|
sample = q15_t(__SSAT((value >> 15), 16));
|
||||||
|
if (m_n >= TONE_LEN)
|
||||||
|
m_n = 0U;
|
||||||
|
} else {
|
||||||
|
sample = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pos++;
|
||||||
|
if (m_pos >= 12000U)
|
||||||
|
m_running = false;
|
||||||
|
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#if !defined(FMBlanking_H)
|
||||||
|
#define FMBlanking_H
|
||||||
|
|
||||||
|
class CFMBlanking {
|
||||||
|
public:
|
||||||
|
CFMBlanking();
|
||||||
|
|
||||||
|
void setParams(uint8_t value, uint8_t level);
|
||||||
|
|
||||||
|
q15_t process(q15_t sample);
|
||||||
|
|
||||||
|
private:
|
||||||
|
q15_t m_posValue;
|
||||||
|
q15_t m_negValue;
|
||||||
|
q15_t m_level;
|
||||||
|
bool m_running;
|
||||||
|
uint32_t m_pos;
|
||||||
|
uint8_t m_n;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "FMCTCSSRX.h"
|
||||||
|
|
||||||
|
const struct RX_CTCSS_TABLE {
|
||||||
|
uint8_t frequency;
|
||||||
|
q63_t coeffDivTwo;
|
||||||
|
} RX_CTCSS_TABLE_DATA[] = {
|
||||||
|
{ 67U, 2147153298},
|
||||||
|
{ 69U, 2147130228},
|
||||||
|
{ 71U, 2147103212},
|
||||||
|
{ 74U, 2147076297},
|
||||||
|
{ 77U, 2147047330},
|
||||||
|
{ 79U, 2147016195},
|
||||||
|
{ 82U, 2146982775},
|
||||||
|
{ 85U, 2146946945},
|
||||||
|
{ 88U, 2146907275},
|
||||||
|
{ 91U, 2146867538},
|
||||||
|
{ 94U, 2146822298},
|
||||||
|
{ 97U, 2146785526},
|
||||||
|
{100U, 2146747759},
|
||||||
|
{103U, 2146695349},
|
||||||
|
{107U, 2146637984},
|
||||||
|
{110U, 2146578604},
|
||||||
|
{114U, 2146513835},
|
||||||
|
{118U, 2146445080},
|
||||||
|
{123U, 2146370355},
|
||||||
|
{127U, 2146291161},
|
||||||
|
{131U, 2146205372},
|
||||||
|
{136U, 2146112589},
|
||||||
|
{141U, 2146014479},
|
||||||
|
{146U, 2145910829},
|
||||||
|
{151U, 2145796971},
|
||||||
|
{156U, 2145676831},
|
||||||
|
{159U, 2145604646},
|
||||||
|
{162U, 2145547790},
|
||||||
|
{165U, 2145468230},
|
||||||
|
{167U, 2145409363},
|
||||||
|
{171U, 2145324517},
|
||||||
|
{173U, 2145261046},
|
||||||
|
{177U, 2145170643},
|
||||||
|
{179U, 2145102321},
|
||||||
|
{183U, 2145006080},
|
||||||
|
{186U, 2144932648},
|
||||||
|
{189U, 2144830280},
|
||||||
|
{192U, 2144748638},
|
||||||
|
{196U, 2144639788},
|
||||||
|
{199U, 2144555290},
|
||||||
|
{203U, 2144436713},
|
||||||
|
{206U, 2144346237},
|
||||||
|
{210U, 2144217348},
|
||||||
|
{218U, 2143983951},
|
||||||
|
{225U, 2143735870},
|
||||||
|
{229U, 2143622139},
|
||||||
|
{233U, 2143469001},
|
||||||
|
{241U, 2143182299},
|
||||||
|
{250U, 2142874683},
|
||||||
|
{254U, 2142733729}};
|
||||||
|
|
||||||
|
const uint8_t CTCSS_TABLE_DATA_LEN = 50U;
|
||||||
|
|
||||||
|
// 4Hz bandwidth
|
||||||
|
const uint16_t N = 24000U / 4U;
|
||||||
|
|
||||||
|
CFMCTCSSRX::CFMCTCSSRX() :
|
||||||
|
m_coeffDivTwo(0),
|
||||||
|
m_highThreshold(0),
|
||||||
|
m_lowThreshold(0),
|
||||||
|
m_count(0U),
|
||||||
|
m_q0(0),
|
||||||
|
m_q1(0),
|
||||||
|
m_state(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t highThreshold, uint8_t lowThreshold)
|
||||||
|
{
|
||||||
|
m_coeffDivTwo = 0;
|
||||||
|
|
||||||
|
for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) {
|
||||||
|
if (RX_CTCSS_TABLE_DATA[i].frequency == frequency) {
|
||||||
|
m_coeffDivTwo = RX_CTCSS_TABLE_DATA[i].coeffDivTwo;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_coeffDivTwo == 0)
|
||||||
|
return 4U;
|
||||||
|
|
||||||
|
m_highThreshold = q31_t(highThreshold);
|
||||||
|
m_lowThreshold = q31_t(lowThreshold);
|
||||||
|
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFMCTCSSRX::process(q15_t sample)
|
||||||
|
{
|
||||||
|
//get more dynamic into the decoder by multiplying the sample by 1.5
|
||||||
|
q31_t sample31 = q31_t(sample) + (q31_t(sample) >> 1);
|
||||||
|
|
||||||
|
q31_t q2 = m_q1;
|
||||||
|
m_q1 = m_q0;
|
||||||
|
|
||||||
|
// Q31 multiplication, t3 = m_coeffDivTwo * 2 * m_q1
|
||||||
|
q63_t t1 = m_coeffDivTwo * m_q1;
|
||||||
|
q31_t t2 = __SSAT((t1 >> 31), 31);
|
||||||
|
q31_t t3 = t2 * 2;
|
||||||
|
|
||||||
|
// m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + sample
|
||||||
|
m_q0 = t3 - q2 + sample31;
|
||||||
|
|
||||||
|
m_count++;
|
||||||
|
if (m_count == N) {
|
||||||
|
// Q31 multiplication, t2 = m_q0 * m_q0
|
||||||
|
q63_t t1 = q63_t(m_q0) * q63_t(m_q0);
|
||||||
|
q31_t t2 = __SSAT((t1 >> 31), 31);
|
||||||
|
|
||||||
|
// Q31 multiplication, t4 = m_q0 * m_q0
|
||||||
|
q63_t t3 = q63_t(m_q1) * q63_t(m_q1);
|
||||||
|
q31_t t4 = __SSAT((t3 >> 31), 31);
|
||||||
|
|
||||||
|
// Q31 multiplication, t9 = m_q0 * m_q1 * m_coeffDivTwo * 2
|
||||||
|
q63_t t5 = q63_t(m_q0) * q63_t(m_q1);
|
||||||
|
q31_t t6 = __SSAT((t5 >> 31), 31);
|
||||||
|
q63_t t7 = t6 * m_coeffDivTwo;
|
||||||
|
q31_t t8 = __SSAT((t7 >> 31), 31);
|
||||||
|
q31_t t9 = t8 * 2;
|
||||||
|
|
||||||
|
// value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeffDivTwo * 2
|
||||||
|
q31_t value = t2 + t4 - t9;
|
||||||
|
|
||||||
|
bool previousState = m_state;
|
||||||
|
|
||||||
|
q31_t threshold = m_highThreshold;
|
||||||
|
if (previousState)
|
||||||
|
threshold = m_lowThreshold;
|
||||||
|
|
||||||
|
m_state = value >= threshold;
|
||||||
|
|
||||||
|
if (previousState != m_state)
|
||||||
|
DEBUG4("CTCSS Value / Threshold / Valid", value, threshold, m_state);
|
||||||
|
|
||||||
|
m_count = 0U;
|
||||||
|
m_q0 = 0;
|
||||||
|
m_q1 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFMCTCSSRX::reset()
|
||||||
|
{
|
||||||
|
m_q0 = 0;
|
||||||
|
m_q1 = 0;
|
||||||
|
m_state = false;
|
||||||
|
m_count = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
* Copyright (C) 2020 by Jonathan Naylor G4KLX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,43 +16,34 @@
|
|||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if !defined(YSFRX_H)
|
|
||||||
#define YSFRX_H
|
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "YSFDefines.h"
|
|
||||||
|
|
||||||
enum YSFRX_STATE {
|
#if defined(MODE_FM)
|
||||||
YSFRXS_NONE,
|
|
||||||
YSFRXS_DATA
|
|
||||||
};
|
|
||||||
|
|
||||||
class CYSFRX {
|
#if !defined(FMCTCSSRX_H)
|
||||||
|
#define FMCTCSSRX_H
|
||||||
|
|
||||||
|
class CFMCTCSSRX {
|
||||||
public:
|
public:
|
||||||
CYSFRX();
|
CFMCTCSSRX();
|
||||||
|
|
||||||
void samples(const q15_t* samples, uint8_t length);
|
uint8_t setParams(uint8_t frequency, uint8_t highThreshold, uint8_t lowThreshold);
|
||||||
|
|
||||||
|
bool process(q15_t sample);
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t m_pll;
|
q63_t m_coeffDivTwo;
|
||||||
bool m_prev;
|
q31_t m_highThreshold;
|
||||||
YSFRX_STATE m_state;
|
q31_t m_lowThreshold;
|
||||||
uint32_t m_symbolBuffer;
|
uint16_t m_count;
|
||||||
uint64_t m_bitBuffer;
|
q31_t m_q0;
|
||||||
q15_t m_symbols[YSF_SYNC_LENGTH_SYMBOLS];
|
q31_t m_q1;
|
||||||
uint8_t m_outBuffer[YSF_FRAME_LENGTH_BYTES + 3U];
|
bool m_state;
|
||||||
uint8_t* m_buffer;
|
|
||||||
uint16_t m_bufferPtr;
|
|
||||||
uint16_t m_symbolPtr;
|
|
||||||
uint16_t m_lostCount;
|
|
||||||
q15_t m_centre;
|
|
||||||
q15_t m_threshold;
|
|
||||||
|
|
||||||
void processNone(q15_t sample);
|
|
||||||
void processData(q15_t sample);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "FMCTCSSTX.h"
|
||||||
|
|
||||||
|
const struct TX_CTCSS_TABLE {
|
||||||
|
uint8_t frequency;
|
||||||
|
uint16_t length;
|
||||||
|
q31_t increment;
|
||||||
|
} TX_CTCSS_TABLE_DATA[] = {
|
||||||
|
{ 67U, 358U, 5995059},
|
||||||
|
{ 69U, 346U, 6200860},
|
||||||
|
{ 71U, 334U, 6433504},
|
||||||
|
{ 74U, 323U, 6657200},
|
||||||
|
{ 77U, 312U, 6889844},
|
||||||
|
{ 79U, 301U, 7131436},
|
||||||
|
{ 82U, 291U, 7381976},
|
||||||
|
{ 85U, 281U, 7641463},
|
||||||
|
{ 88U, 271U, 7918846},
|
||||||
|
{ 91U, 262U, 8187282},
|
||||||
|
{ 94U, 253U, 8482561},
|
||||||
|
{ 97U, 246U, 8715205},
|
||||||
|
{100U, 240U, 8947849},
|
||||||
|
{103U, 232U, 9261024},
|
||||||
|
{107U, 224U, 9592094},
|
||||||
|
{110U, 216U, 9923165},
|
||||||
|
{114U, 209U, 10272131},
|
||||||
|
{118U, 202U, 10630045},
|
||||||
|
{123U, 195U, 11005854},
|
||||||
|
{127U, 189U, 11390612},
|
||||||
|
{131U, 182U, 11793265},
|
||||||
|
{136U, 176U, 12213814},
|
||||||
|
{141U, 170U, 12643310},
|
||||||
|
{146U, 164U, 13081755},
|
||||||
|
{151U, 159U, 13547043},
|
||||||
|
{156U, 153U, 14021279},
|
||||||
|
{159U, 150U, 14298662},
|
||||||
|
{162U, 148U, 14513411},
|
||||||
|
{165U, 145U, 14808690},
|
||||||
|
{167U, 143U, 15023438},
|
||||||
|
{171U, 140U, 15327665},
|
||||||
|
{173U, 138U, 15551361},
|
||||||
|
{177U, 135U, 15864536},
|
||||||
|
{179U, 133U, 16097180},
|
||||||
|
{183U, 131U, 16419303},
|
||||||
|
{186U, 129U, 16660894},
|
||||||
|
{189U, 126U, 16991965},
|
||||||
|
{192U, 124U, 17251452},
|
||||||
|
{196U, 122U, 17591471},
|
||||||
|
{199U, 120U, 17850958},
|
||||||
|
{203U, 118U, 18208872},
|
||||||
|
{206U, 116U, 18477308},
|
||||||
|
{210U, 114U, 18853117},
|
||||||
|
{218U, 110U, 19515258},
|
||||||
|
{225U, 106U, 20195295},
|
||||||
|
{229U, 105U, 20499521},
|
||||||
|
{233U, 103U, 20902175},
|
||||||
|
{241U, 99U, 21635898},
|
||||||
|
{250U, 96U, 22396465},
|
||||||
|
{254U, 94U, 22736484}};
|
||||||
|
|
||||||
|
const uint8_t CTCSS_TABLE_DATA_LEN = 50U;
|
||||||
|
|
||||||
|
CFMCTCSSTX::CFMCTCSSTX() :
|
||||||
|
m_values(NULL),
|
||||||
|
m_length(0U),
|
||||||
|
m_n(0U)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level)
|
||||||
|
{
|
||||||
|
const TX_CTCSS_TABLE* entry = NULL;
|
||||||
|
|
||||||
|
for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) {
|
||||||
|
if (TX_CTCSS_TABLE_DATA[i].frequency == frequency) {
|
||||||
|
entry = TX_CTCSS_TABLE_DATA + i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry == NULL)
|
||||||
|
return 4U;
|
||||||
|
|
||||||
|
m_length = entry->length;
|
||||||
|
|
||||||
|
delete[] m_values;
|
||||||
|
m_values = new q15_t[m_length];
|
||||||
|
|
||||||
|
q31_t arg = 0;
|
||||||
|
for (uint16_t i = 0U; i < m_length; i++) {
|
||||||
|
q63_t value = ::arm_sin_q31(arg) * q63_t(level * 13);
|
||||||
|
m_values[i] = q15_t(__SSAT((value >> 31), 16));
|
||||||
|
|
||||||
|
arg += entry->increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
q15_t CFMCTCSSTX::getAudio(bool reverse)
|
||||||
|
{
|
||||||
|
q15_t sample = m_values[m_n++];
|
||||||
|
if (m_n >= m_length)
|
||||||
|
m_n = 0U;
|
||||||
|
|
||||||
|
if (reverse)
|
||||||
|
return -sample;
|
||||||
|
else
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#if !defined(FMCTCSSTX_H)
|
||||||
|
#define FMCTCSSTX_H
|
||||||
|
|
||||||
|
class CFMCTCSSTX {
|
||||||
|
public:
|
||||||
|
CFMCTCSSTX();
|
||||||
|
|
||||||
|
uint8_t setParams(uint8_t frequency, uint8_t level);
|
||||||
|
|
||||||
|
q15_t getAudio(bool reverse);
|
||||||
|
|
||||||
|
private:
|
||||||
|
q15_t* m_values;
|
||||||
|
uint16_t m_length;
|
||||||
|
uint16_t m_n;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
This header file has been taken from:
|
||||||
|
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||||
|
By Vinnie Falco
|
||||||
|
Bernd Porr adapted it for Linux and turned it into a filter using
|
||||||
|
fixed point arithmetic.
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||||
|
Copyright (c) 2009 by Vinnie Falco
|
||||||
|
Copyright (C) 2013-2017, Bernd Porr, [email protected]
|
||||||
|
Copyright (C) 2020 , Mario Molitor , [email protected]
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
// based on https://raw.githubusercontent.com/berndporr/iir_fixed_point/master/DirectFormI.h
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#ifndef DIRECTFORMI_H_
|
||||||
|
#define DIRECTFORMI_H_
|
||||||
|
class CFMDirectFormI
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// convenience function which takes the a0 argument but ignores it!
|
||||||
|
CFMDirectFormI(const q31_t b0, const q31_t b1, const q31_t b2, const q31_t, const q31_t a1, const q31_t a2)
|
||||||
|
{
|
||||||
|
// FIR coefficients
|
||||||
|
c_b0 = b0;
|
||||||
|
c_b1 = b1;
|
||||||
|
c_b2 = b2;
|
||||||
|
// IIR coefficients
|
||||||
|
c_a1 = a1;
|
||||||
|
c_a2 = a2;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
CFMDirectFormI(const CFMDirectFormI &my)
|
||||||
|
{
|
||||||
|
// delay line
|
||||||
|
m_x2 = my.m_x2; // x[n-2]
|
||||||
|
m_y2 = my.m_y2; // y[n-2]
|
||||||
|
m_x1 = my.m_x1; // x[n-1]
|
||||||
|
m_y1 = my.m_y1; // y[n-1]
|
||||||
|
|
||||||
|
// coefficients
|
||||||
|
c_b0 = my.c_b0;
|
||||||
|
c_b1 = my.c_b1;
|
||||||
|
c_b2 = my.c_b2; // FIR
|
||||||
|
c_a1 = my.c_a1;
|
||||||
|
c_a2 = my.c_a2; // IIR
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
m_x1 = 0;
|
||||||
|
m_x2 = 0;
|
||||||
|
m_y1 = 0;
|
||||||
|
m_y2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// filtering operation: one sample in and one out
|
||||||
|
inline q15_t filter(const q15_t in)
|
||||||
|
{
|
||||||
|
// calculate the output
|
||||||
|
q31_t out_upscaled = c_b0 * in + c_b1 * m_x1 + c_b2 * m_x2 - c_a1 * m_y1 - c_a2 * m_y2;
|
||||||
|
|
||||||
|
q15_t out = __SSAT(out_upscaled >> 15, 15);
|
||||||
|
|
||||||
|
// update the delay lines
|
||||||
|
m_x2 = m_x1;
|
||||||
|
m_y2 = m_y1;
|
||||||
|
m_x1 = in;
|
||||||
|
m_y1 = out;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// delay line
|
||||||
|
q31_t m_x2; // x[n-2]
|
||||||
|
q31_t m_y2; // y[n-2]
|
||||||
|
q31_t m_x1; // x[n-1]
|
||||||
|
q31_t m_y1; // y[n-1]
|
||||||
|
|
||||||
|
// coefficients
|
||||||
|
q31_t c_b0,c_b1,c_b2; // FIR
|
||||||
|
q31_t c_a1,c_a2; // IIR
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DIRECTFORMI_H */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 by Jonathan Naylor G4KLX
|
||||||
|
* Copyright (C) 2020 by Geoffrey Merck F4FXL - KC3FRA
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#include "FMDownSampler.h"
|
||||||
|
|
||||||
|
CFMDownSampler::CFMDownSampler(uint16_t length) :
|
||||||
|
m_ringBuffer(length),
|
||||||
|
m_samplePack(0U),
|
||||||
|
m_samplePackPointer(NULL),
|
||||||
|
m_sampleIndex(0U)
|
||||||
|
{
|
||||||
|
m_samplePackPointer = (uint8_t*)&m_samplePack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFMDownSampler::addSample(q15_t sample)
|
||||||
|
{
|
||||||
|
uint32_t usample = uint32_t(int32_t(sample) + 2048);
|
||||||
|
//only take one of three samples
|
||||||
|
switch(m_sampleIndex){
|
||||||
|
case 0:
|
||||||
|
m_samplePack = usample << 12;
|
||||||
|
break;
|
||||||
|
case 3:{
|
||||||
|
m_samplePack |= usample;
|
||||||
|
|
||||||
|
//we did not use MSB; skip it
|
||||||
|
TSamplePairPack pair{m_samplePackPointer[0U], m_samplePackPointer[1U], m_samplePackPointer[2U]};
|
||||||
|
|
||||||
|
m_ringBuffer.put(pair);
|
||||||
|
|
||||||
|
m_samplePack = 0U;//reset the sample pack
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//Just skip this sample
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sampleIndex++;
|
||||||
|
if (m_sampleIndex >= 6U)//did we pack two samples ?
|
||||||
|
m_sampleIndex = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFMDownSampler::getPackedData(TSamplePairPack& data)
|
||||||
|
{
|
||||||
|
return m_ringBuffer.get(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t CFMDownSampler::getData()
|
||||||
|
{
|
||||||
|
return m_ringBuffer.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFMDownSampler::reset()
|
||||||
|
{
|
||||||
|
m_sampleIndex = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 by Jonathan Naylor G4KLX
|
||||||
|
* Copyright (C) 2020 by Geoffrey Merck F4FXL - KC3FRA
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#if !defined(FMDOWNSAMPLER_H)
|
||||||
|
#define FMDOWNSAMPLER_H
|
||||||
|
|
||||||
|
#include "RingBuffer.h"
|
||||||
|
#include "FMSamplePairPack.h"
|
||||||
|
|
||||||
|
class CFMDownSampler {
|
||||||
|
public:
|
||||||
|
CFMDownSampler(uint16_t length);
|
||||||
|
|
||||||
|
void addSample(q15_t sample);
|
||||||
|
|
||||||
|
bool getPackedData(TSamplePairPack& data);
|
||||||
|
|
||||||
|
uint16_t getData();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CRingBuffer<TSamplePairPack> m_ringBuffer;
|
||||||
|
uint32_t m_samplePack;
|
||||||
|
uint8_t* m_samplePackPointer;
|
||||||
|
uint8_t m_sampleIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "FMKeyer.h"
|
||||||
|
|
||||||
|
const struct {
|
||||||
|
char c;
|
||||||
|
uint32_t pattern;
|
||||||
|
uint8_t length;
|
||||||
|
} SYMBOL_LIST[] = {
|
||||||
|
{'A', 0xB8000000U, 8U},
|
||||||
|
{'B', 0xEA800000U, 12U},
|
||||||
|
{'C', 0xEBA00000U, 14U},
|
||||||
|
{'D', 0xEA000000U, 10U},
|
||||||
|
{'E', 0x80000000U, 4U},
|
||||||
|
{'F', 0xAE800000U, 12U},
|
||||||
|
{'G', 0xEE800000U, 12U},
|
||||||
|
{'H', 0xAA000000U, 10U},
|
||||||
|
{'I', 0xA0000000U, 6U},
|
||||||
|
{'J', 0xBBB80000U, 16U},
|
||||||
|
{'K', 0xEB800000U, 12U},
|
||||||
|
{'L', 0xBA800000U, 12U},
|
||||||
|
{'M', 0xEE000000U, 10U},
|
||||||
|
{'N', 0xE8000000U, 8U},
|
||||||
|
{'O', 0xEEE00000U, 14U},
|
||||||
|
{'P', 0xBBA00000U, 14U},
|
||||||
|
{'Q', 0xEEB80000U, 16U},
|
||||||
|
{'R', 0xBA000000U, 10U},
|
||||||
|
{'S', 0xA8000000U, 8U},
|
||||||
|
{'T', 0xE0000000U, 6U},
|
||||||
|
{'U', 0xAE000000U, 10U},
|
||||||
|
{'V', 0xAB800000U, 12U},
|
||||||
|
{'W', 0xBB800000U, 12U},
|
||||||
|
{'X', 0xEAE00000U, 14U},
|
||||||
|
{'Y', 0xEBB80000U, 16U},
|
||||||
|
{'Z', 0xEEA00000U, 14U},
|
||||||
|
{'1', 0xBBBB8000U, 20U},
|
||||||
|
{'2', 0xAEEE0000U, 18U},
|
||||||
|
{'3', 0xABB80000U, 16U},
|
||||||
|
{'4', 0xAAE00000U, 14U},
|
||||||
|
{'5', 0xAA800000U, 12U},
|
||||||
|
{'6', 0xEAA00000U, 14U},
|
||||||
|
{'7', 0xEEA80000U, 16U},
|
||||||
|
{'8', 0xEEEA0000U, 18U},
|
||||||
|
{'9', 0xEEEE8000U, 20U},
|
||||||
|
{'0', 0xEEEEE000U, 22U},
|
||||||
|
{'/', 0xEAE80000U, 16U},
|
||||||
|
{'?', 0xAEEA0000U, 18U},
|
||||||
|
{',', 0xEEAEE000U, 22U},
|
||||||
|
{'-', 0xEAAE0000U, 18U},
|
||||||
|
{'=', 0xEAB80000U, 16U},
|
||||||
|
{'.', 0xBAEB8000U, 20U},
|
||||||
|
{' ', 0x00000000U, 4U},
|
||||||
|
{0U, 0x00000000U, 0U}
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||||
|
|
||||||
|
#define WRITE_BIT_FM(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
||||||
|
#define READ_BIT_FM(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||||
|
|
||||||
|
CFMKeyer::CFMKeyer() :
|
||||||
|
m_wanted(false),
|
||||||
|
m_poBuffer(),
|
||||||
|
m_poLen(0U),
|
||||||
|
m_poPos(0U),
|
||||||
|
m_dotLen(0U),
|
||||||
|
m_dotPos(0U),
|
||||||
|
m_audio(NULL),
|
||||||
|
m_audioLen(0U),
|
||||||
|
m_audioPos(0U),
|
||||||
|
m_highLevel(0U),
|
||||||
|
m_lowLevel(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t highLevel, uint8_t lowLevel)
|
||||||
|
{
|
||||||
|
m_poLen = 0U;
|
||||||
|
|
||||||
|
for (uint8_t i = 0U; text[i] != '\0'; i++) {
|
||||||
|
for (uint8_t j = 0U; SYMBOL_LIST[j].c != 0U; j++) {
|
||||||
|
if (SYMBOL_LIST[j].c == text[i]) {
|
||||||
|
uint32_t MASK = 0x80000000U;
|
||||||
|
for (uint8_t k = 0U; k < SYMBOL_LIST[j].length; k++, m_poLen++, MASK >>= 1) {
|
||||||
|
bool b = (SYMBOL_LIST[j].pattern & MASK) == MASK;
|
||||||
|
WRITE_BIT_FM(m_poBuffer, m_poLen, b);
|
||||||
|
|
||||||
|
if (m_poLen >= 995U) {
|
||||||
|
m_poLen = 0U;
|
||||||
|
return 4U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_highLevel = q15_t(highLevel);
|
||||||
|
m_lowLevel = q15_t(lowLevel);
|
||||||
|
|
||||||
|
m_dotLen = 24000U / speed; // In samples
|
||||||
|
|
||||||
|
m_audioLen = 24000U / frequency; // In samples
|
||||||
|
|
||||||
|
delete[] m_audio;
|
||||||
|
m_audio = new bool[m_audioLen];
|
||||||
|
|
||||||
|
for (uint16_t i = 0U; i < m_audioLen; i++) {
|
||||||
|
if (i < (m_audioLen / 2U))
|
||||||
|
m_audio[i] = true;
|
||||||
|
else
|
||||||
|
m_audio[i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
q15_t CFMKeyer::getHighAudio()
|
||||||
|
{
|
||||||
|
q15_t output = 0U;
|
||||||
|
if (!m_wanted)
|
||||||
|
return 0U;
|
||||||
|
|
||||||
|
bool b = READ_BIT_FM(m_poBuffer, m_poPos);
|
||||||
|
if (b)
|
||||||
|
output = m_audio[m_audioPos] ? m_highLevel : -m_highLevel;
|
||||||
|
|
||||||
|
m_audioPos++;
|
||||||
|
if (m_audioPos >= m_audioLen)
|
||||||
|
m_audioPos = 0U;
|
||||||
|
m_dotPos++;
|
||||||
|
if (m_dotPos >= m_dotLen) {
|
||||||
|
m_dotPos = 0U;
|
||||||
|
m_poPos++;
|
||||||
|
if (m_poPos >= m_poLen) {
|
||||||
|
stop();
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
q15_t CFMKeyer::getLowAudio()
|
||||||
|
{
|
||||||
|
q15_t output = 0U;
|
||||||
|
if (!m_wanted)
|
||||||
|
return 0U;
|
||||||
|
|
||||||
|
bool b = READ_BIT_FM(m_poBuffer, m_poPos);
|
||||||
|
if (b)
|
||||||
|
output = m_audio[m_audioPos] ? m_lowLevel : -m_lowLevel;
|
||||||
|
|
||||||
|
m_audioPos++;
|
||||||
|
if (m_audioPos >= m_audioLen)
|
||||||
|
m_audioPos = 0U;
|
||||||
|
m_dotPos++;
|
||||||
|
if (m_dotPos >= m_dotLen) {
|
||||||
|
m_dotPos = 0U;
|
||||||
|
m_poPos++;
|
||||||
|
if (m_poPos >= m_poLen) {
|
||||||
|
stop();
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFMKeyer::start()
|
||||||
|
{
|
||||||
|
if (isRunning())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_wanted = true;
|
||||||
|
m_poPos = 0U;
|
||||||
|
m_dotPos = 0U;
|
||||||
|
m_audioPos = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFMKeyer::stop()
|
||||||
|
{
|
||||||
|
m_wanted = false;
|
||||||
|
m_poPos = 0U;
|
||||||
|
m_dotPos = 0U;
|
||||||
|
m_audioPos = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFMKeyer::isWanted() const
|
||||||
|
{
|
||||||
|
return m_wanted;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFMKeyer::isRunning() const
|
||||||
|
{
|
||||||
|
return m_poPos > 0U || m_dotPos > 0U || m_audioPos > 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#if !defined(FMKeyer_H)
|
||||||
|
#define FMKeyer_H
|
||||||
|
|
||||||
|
class CFMKeyer {
|
||||||
|
public:
|
||||||
|
CFMKeyer();
|
||||||
|
|
||||||
|
uint8_t setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t highLevel, uint8_t lowLevel);
|
||||||
|
|
||||||
|
q15_t getHighAudio();
|
||||||
|
q15_t getLowAudio();
|
||||||
|
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
bool isRunning() const;
|
||||||
|
|
||||||
|
bool isWanted() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_wanted;
|
||||||
|
uint8_t m_poBuffer[1000U];
|
||||||
|
uint16_t m_poLen;
|
||||||
|
uint16_t m_poPos;
|
||||||
|
uint16_t m_dotLen;
|
||||||
|
uint16_t m_dotPos;
|
||||||
|
bool* m_audio;
|
||||||
|
uint16_t m_audioLen;
|
||||||
|
uint16_t m_audioPos;
|
||||||
|
q15_t m_highLevel;
|
||||||
|
q15_t m_lowLevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#if defined(MODE_FM)
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "FMNoiseSquelch.h"
|
||||||
|
|
||||||
|
// 4500Hz centre frequency
|
||||||
|
const q31_t COEFF_DIV_TWO = 821806413;
|
||||||
|
|
||||||
|
// 400Hz bandwidth
|
||||||
|
const uint16_t N = 24000U / 400U;
|
||||||
|
|
||||||
|
CFMNoiseSquelch::CFMNoiseSquelch() :
|
||||||
|
m_highThreshold(0),
|
||||||
|
m_lowThreshold(0),
|
||||||
|
m_count(0U),
|
||||||
|
m_q0(0),
|
||||||
|
m_q1(0),
|
||||||
|
m_state(false),
|
||||||
|
m_validCount(0U)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFMNoiseSquelch::setParams(uint8_t highThreshold, uint8_t lowThreshold)
|
||||||
|
{
|
||||||
|
m_highThreshold = q31_t(highThreshold);
|
||||||
|
m_lowThreshold = q31_t(lowThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFMNoiseSquelch::process(q15_t sample)
|
||||||
|
{
|
||||||
|
//get more dynamic into the decoder by multiplying the sample by 64
|
||||||
|
q31_t sample31 = q31_t(sample) << 6; //+ (q31_t(sample) >> 1);
|
||||||
|
|
||||||
|
q31_t q2 = m_q1;
|
||||||
|
m_q1 = m_q0;
|
||||||
|
|
||||||
|
// Q31 multiplication, t3 = m_coeffDivTwo * 2 * m_q1
|
||||||
|
q63_t t1 = COEFF_DIV_TWO * m_q1;
|
||||||
|
q31_t t2 = __SSAT((t1 >> 31), 31);
|
||||||
|
q31_t t3 = t2 * 2;
|
||||||
|
|
||||||
|
// m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + sample
|
||||||
|
m_q0 = t3 - q2 + sample31;
|
||||||
|
|
||||||
|
m_count++;
|
||||||
|
if (m_count == N) {
|
||||||
|
// Q31 multiplication, t2 = m_q0 * m_q0
|
||||||
|
q63_t t1 = q63_t(m_q0) * q63_t(m_q0);
|
||||||
|
q31_t t2 = __SSAT((t1 >> 31), 31);
|
||||||
|
|
||||||
|
// Q31 multiplication, t4 = m_q0 * m_q0
|
||||||
|
q63_t t3 = q63_t(m_q1) * q63_t(m_q1);
|
||||||
|
q31_t t4 = __SSAT((t3 >> 31), 31);
|
||||||
|
|
||||||
|
// Q31 multiplication, t9 = m_q0 * m_q1 * m_coeffDivTwo * 2
|
||||||
|
q63_t t5 = q63_t(m_q0) * q63_t(m_q1);
|
||||||
|
q31_t t6 = __SSAT((t5 >> 31), 31);
|
||||||
|
q63_t t7 = t6 * COEFF_DIV_TWO;
|
||||||
|
q31_t t8 = __SSAT((t7 >> 31), 31);
|
||||||
|
q31_t t9 = t8 * 2;
|
||||||
|
|
||||||
|
// value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeffDivTwo * 2
|
||||||
|
q31_t value = t2 + t4 - t9;
|
||||||
|
|
||||||
|
bool previousState = m_state;
|
||||||
|
|
||||||
|
q31_t threshold = m_highThreshold;
|
||||||
|
if (previousState)
|
||||||
|
threshold = m_lowThreshold;
|
||||||
|
|
||||||
|
if (!m_state) {
|
||||||
|
if (value < threshold)
|
||||||
|
m_validCount++;
|
||||||
|
else
|
||||||
|
m_validCount = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_state) {
|
||||||
|
if (value >= threshold)
|
||||||
|
m_invalidCount++;
|
||||||
|
else
|
||||||
|
m_invalidCount = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = m_validCount >= 10U && m_invalidCount < 10U;
|
||||||
|
|
||||||
|
if(previousState && !m_state)
|
||||||
|
m_invalidCount = 0U;
|
||||||
|
|
||||||
|
if (previousState != m_state) {
|
||||||
|
DEBUG4("Noise Squelch Value / Threshold / Valid", value, threshold, m_state);
|
||||||
|
DEBUG3("Valid Count / Invalid Count", m_validCount, m_invalidCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_count = 0U;
|
||||||
|
m_q0 = 0;
|
||||||
|
m_q1 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFMNoiseSquelch::reset()
|
||||||
|
{
|
||||||
|
m_q0 = 0;
|
||||||
|
m_q1 = 0;
|
||||||
|
m_state = false;
|
||||||
|
m_count = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user