mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2026-04-20 15:22:42 +00:00
Compare commits
592 Commits
0.2.5_beta
...
1.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3bda1b8fd | ||
|
|
2a6c742a51 | ||
|
|
604a559b54 | ||
|
|
d9a0243905 | ||
|
|
99096885f5 | ||
|
|
f16c296f38 | ||
|
|
85eb08e422 | ||
|
|
ee5b89c4aa | ||
|
|
c13eb950b2 | ||
|
|
011fdce237 | ||
|
|
79e79e78ac | ||
|
|
6114cf0f58 | ||
|
|
3350697875 | ||
|
|
7f4557527e | ||
|
|
1aa2c064f7 | ||
|
|
1f3dcc1beb | ||
|
|
e9cdf162fa | ||
|
|
a487c0aaea | ||
|
|
d84273a0db | ||
|
|
5d08f1018d | ||
|
|
b7a0f849cf | ||
|
|
7079ddb74e | ||
|
|
e744520d50 | ||
|
|
0741f265d7 | ||
|
|
2f61b190ca | ||
|
|
fc30287bed | ||
|
|
003ff37ab8 | ||
|
|
8290284586 | ||
|
|
b9e35e6558 | ||
|
|
679fb49743 | ||
|
|
5974839515 | ||
|
|
666b89c4c7 | ||
|
|
d21a61aefd | ||
|
|
5f29350dd1 | ||
|
|
8f43110c72 | ||
|
|
a4240ab8a1 | ||
|
|
d73a18ddcc | ||
|
|
6dc24a7fc7 | ||
|
|
cd74313bc8 | ||
|
|
14fae89a0b | ||
|
|
0498a48b93 | ||
|
|
22e9d25bed | ||
|
|
4092874f5c | ||
|
|
e27702c166 | ||
|
|
21f4c40e7f | ||
|
|
bd744d07ba | ||
|
|
8a4055920d | ||
|
|
6c05c11a62 | ||
|
|
e62351c206 | ||
|
|
ffed602246 | ||
|
|
03a2f41c04 | ||
|
|
df51dc7104 | ||
|
|
8bea72beb5 | ||
|
|
14813aa962 | ||
|
|
ce448d6852 | ||
|
|
7506e45d3b | ||
|
|
034ada1ed7 | ||
|
|
646fe4fd02 | ||
|
|
fa9347f2ee | ||
|
|
85de72a859 | ||
|
|
b327bfbe5d | ||
|
|
da8614438c | ||
|
|
5248688241 | ||
|
|
26dedec561 | ||
|
|
d8d45ec7af | ||
|
|
a888065624 | ||
|
|
8454b40d54 | ||
|
|
175e361ccd | ||
|
|
2baf607b8c | ||
|
|
a974658c98 | ||
|
|
465dc9e5dc | ||
|
|
8d3f646aec | ||
|
|
4418baba3f | ||
|
|
3d5047338c | ||
|
|
fb32b4d55a | ||
|
|
5f1a94c267 | ||
|
|
0d7f1265da | ||
|
|
09e332a8d1 | ||
|
|
e877844768 | ||
|
|
c9f1ec0a8a | ||
|
|
4b43da095e | ||
|
|
3103d2d168 | ||
|
|
027297933b | ||
|
|
1adcdea6d1 | ||
|
|
ebd74d1d1a | ||
|
|
0d993937b3 | ||
|
|
79e2747aed | ||
|
|
8d3557268f | ||
|
|
336d69c043 | ||
|
|
2ddb1b93c4 | ||
|
|
63ae56cb9b | ||
|
|
45e4c21870 | ||
|
|
ead7ee153a | ||
|
|
9cb6d96f8f | ||
|
|
0b7a7ca193 | ||
|
|
f6e0e2f39d | ||
|
|
def6036b30 | ||
|
|
943d23a7ce | ||
|
|
218844ed47 | ||
|
|
794c486352 | ||
|
|
e6293b6435 | ||
|
|
6ef58f2e7c | ||
|
|
c0f3babc49 | ||
|
|
0ff287cbfb | ||
|
|
0c40a2954d | ||
|
|
4735fd238a | ||
|
|
ace0f4a316 | ||
|
|
6583104a96 | ||
|
|
4a5a29a59a | ||
|
|
ff38cefe11 | ||
|
|
168226c634 | ||
|
|
3d7cfffe13 | ||
|
|
6f409b59c8 | ||
|
|
ff030397a4 | ||
|
|
1f1b0cd45e | ||
|
|
4aaf71f5cc | ||
|
|
5971d3d3b3 | ||
|
|
2151d1e6cc | ||
|
|
b2d8e19504 | ||
|
|
939197df6b | ||
|
|
eb48dd70fb | ||
|
|
6cca4c654f | ||
|
|
f86df07c36 | ||
|
|
73bbd69e3f | ||
|
|
fd9f4ebdc3 | ||
|
|
ac04432453 | ||
|
|
91c6823e0c | ||
|
|
cf3c976651 | ||
|
|
29ec14d3f0 | ||
|
|
ba208bf8b3 | ||
|
|
7ebfddc03c | ||
|
|
61f56b6e56 | ||
|
|
132f591288 | ||
|
|
7e6b9d8487 | ||
|
|
4c8b810bd6 | ||
|
|
f9ad86e312 | ||
|
|
7219e3a4de | ||
|
|
1bc49426e2 | ||
|
|
41a307fd35 | ||
|
|
d4849af171 | ||
|
|
ab376ea1aa | ||
|
|
6db8251e46 | ||
|
|
4dc0df74cf | ||
|
|
5b9bd56cf2 | ||
|
|
ecbb451763 | ||
|
|
78f079ca84 | ||
|
|
a408084237 | ||
|
|
9c0602f406 | ||
|
|
3f6b8dbe6c | ||
|
|
5a21b07269 | ||
|
|
b96206765a | ||
|
|
f0b2d80ba7 | ||
|
|
a19e47bd54 | ||
|
|
4a2774367f | ||
|
|
7d720e4d6f | ||
|
|
b87ec8f2cc | ||
|
|
2c4e221d1c | ||
|
|
0dbd89db19 | ||
|
|
8f4942bbe9 | ||
|
|
24892c854e | ||
|
|
5f84ecc4de | ||
|
|
659b9b1e8c | ||
|
|
8a1df1d712 | ||
|
|
aaa15315ce | ||
|
|
70cf463881 | ||
|
|
dff9ec2d37 | ||
|
|
192e86064c | ||
|
|
bfdeb77f7d | ||
|
|
da96ecaaba | ||
|
|
72a794df6f | ||
|
|
c39b9609be | ||
|
|
d31ed762c1 | ||
|
|
dad41e1574 | ||
|
|
a6aee1d9a4 | ||
|
|
64ed5058bf | ||
|
|
26079dba0a | ||
|
|
94fae2135d | ||
|
|
1e71a52727 | ||
|
|
da2f4fcf3a | ||
|
|
b5d38c71ce | ||
|
|
8a18bec55c | ||
|
|
45ff5dd0cc | ||
|
|
55017f876d | ||
|
|
c59b83e564 | ||
|
|
c0244e819e | ||
|
|
3467031bf4 | ||
|
|
51ef8b1891 | ||
|
|
bbff0036dc | ||
|
|
e6ab6f3cc9 | ||
|
|
99d14f7abb | ||
|
|
215c17ae20 | ||
|
|
b4e1eef8c9 | ||
|
|
0b276bed1d | ||
|
|
1b27916c24 | ||
|
|
85b9649a9a | ||
|
|
aa9ab8e1e8 | ||
|
|
1eca58605c | ||
|
|
9a1850bd61 | ||
|
|
c23b2bdc55 | ||
|
|
754a9ac406 | ||
|
|
3225f15419 | ||
|
|
b1bb863a7e | ||
|
|
b030c22c56 | ||
|
|
e1086e2d41 | ||
|
|
4634c8187f | ||
|
|
9913124a5c | ||
|
|
0bc1bd8549 | ||
|
|
c6c15a446b | ||
|
|
6e4f502454 | ||
|
|
66150922c7 | ||
|
|
d1acfeb496 | ||
|
|
233d5ed838 | ||
|
|
860afb2fbd | ||
|
|
d8c0c8649c | ||
|
|
55b2b050c8 | ||
|
|
96f83ee55c | ||
|
|
3e79d4dfad | ||
|
|
d3276a1546 | ||
|
|
ce8b4ceb44 | ||
|
|
bed0712be1 | ||
|
|
f483de1f7e | ||
|
|
a6df90785a | ||
|
|
ab4cde9bb8 | ||
|
|
1738706c59 | ||
|
|
c801b6547f | ||
|
|
490243e346 | ||
|
|
17db61c302 | ||
|
|
bef4a6efc1 | ||
|
|
26605b8d90 | ||
|
|
8b25d74dde | ||
|
|
8c8acf6955 | ||
|
|
6aade531a2 | ||
|
|
53f2caa9cc | ||
|
|
ac474902a4 | ||
|
|
20c47ae8f2 | ||
|
|
7d4fdad6f6 | ||
|
|
de3ee34fce | ||
|
|
7481f0432b | ||
|
|
41709ef916 | ||
|
|
935534905f | ||
|
|
b9a41c83bf | ||
|
|
54165c64ec | ||
|
|
0300be1c4b | ||
|
|
48932a5230 | ||
|
|
624817618c | ||
|
|
48399d341f | ||
|
|
48ae57ad2d | ||
|
|
128e52e33a | ||
|
|
27697bb638 | ||
|
|
ed29f9dcc5 | ||
|
|
78c7ef0242 | ||
|
|
95f7171256 | ||
|
|
f968725469 | ||
|
|
3d65e515ad | ||
|
|
1ba3756be0 | ||
|
|
061cb91c48 | ||
|
|
5feba74b9b | ||
|
|
6117635a8d | ||
|
|
8c70d816a0 | ||
|
|
c862882499 | ||
|
|
b9642f1e62 | ||
|
|
481d5a6b14 | ||
|
|
6deae14870 | ||
|
|
102eea134c | ||
|
|
1894e191d5 | ||
|
|
36f2b157e5 | ||
|
|
e4b4787cbb | ||
|
|
78e40f1f76 | ||
|
|
8596c26f7e | ||
|
|
a4ce0c8868 | ||
|
|
86702040a4 | ||
|
|
5322a4632c | ||
|
|
670b5aefce | ||
|
|
f316856682 | ||
|
|
1c18310f37 | ||
|
|
8c428be885 | ||
|
|
c9a247b64d | ||
|
|
95d2d2d7c8 | ||
|
|
8eb1067da5 | ||
|
|
d08611ee91 | ||
|
|
289eba7855 | ||
|
|
31e7867915 | ||
|
|
5fd1509c96 | ||
|
|
e2b897f1f2 | ||
|
|
2628700ea8 | ||
|
|
10d368444b | ||
|
|
75b54fb9b4 | ||
|
|
f0250f0cd1 | ||
|
|
db1ab774f0 | ||
|
|
28d189e35a | ||
|
|
5c487b73ab | ||
|
|
71eb2735f6 | ||
|
|
53ba9eddf7 | ||
|
|
3efdd51fa6 | ||
|
|
e115161da8 | ||
|
|
dec16eeb87 | ||
|
|
2a22a125bb | ||
|
|
8305750016 | ||
|
|
77ac94ff99 | ||
|
|
35122708be | ||
|
|
6152403faf | ||
|
|
cd4f64bc80 | ||
|
|
7a95229cc2 | ||
|
|
df42830d38 | ||
|
|
2df1869824 | ||
|
|
05440ed2fc | ||
|
|
7a9371c062 | ||
|
|
fc350871e4 | ||
|
|
88fe31fead | ||
|
|
fc7ecab5f2 | ||
|
|
0382b8aed8 | ||
|
|
a35d0252e7 | ||
|
|
dd5490cac6 | ||
|
|
75568a7bf7 | ||
|
|
b8347fd254 | ||
|
|
d43f501819 | ||
|
|
b8e4a79188 | ||
|
|
1dbdf48e9a | ||
|
|
ed83abaeef | ||
|
|
89e805b1a0 | ||
|
|
a4c25280b3 | ||
|
|
72cbf741b3 | ||
|
|
2c83d79836 | ||
|
|
9efdb6b150 | ||
|
|
20ce4efebb | ||
|
|
d3ad4a5035 | ||
|
|
5edb838f31 | ||
|
|
89dfeeb247 | ||
|
|
956969f679 | ||
|
|
7ee86a5d40 | ||
|
|
f8be28dcee | ||
|
|
bc11ef7e9c | ||
|
|
eb8cd09e65 | ||
|
|
77dada07da | ||
|
|
e236c42068 | ||
|
|
38c9e2c894 | ||
|
|
a4fa7d2ff6 | ||
|
|
61ebff209b | ||
|
|
4849d9cf09 | ||
|
|
a0316e57c5 | ||
|
|
6332b33f3e | ||
|
|
7d6ecf923b | ||
|
|
8e194ba5a9 | ||
|
|
99ec2a12f1 | ||
|
|
dd0ec72fb7 | ||
|
|
271e4a6f46 | ||
|
|
a9d1d9b9e7 | ||
|
|
9776191e83 | ||
|
|
a0f955e907 | ||
|
|
18eb29fabd | ||
|
|
f23faa72ec | ||
|
|
70c7060eaf | ||
|
|
6420553ae9 | ||
|
|
dc65ff3a05 | ||
|
|
c9a44d1ecf | ||
|
|
13949e0dde | ||
|
|
35d50c91c7 | ||
|
|
3a142b0d85 | ||
|
|
f119af5e6f | ||
|
|
a91ed266d5 | ||
|
|
bdce551a50 | ||
|
|
89599d0bf8 | ||
|
|
685a14a21e | ||
|
|
24e6ac8013 | ||
|
|
2b4cc46a53 | ||
|
|
30e8b3b60e | ||
|
|
026e5f9bcb | ||
|
|
3c9b8db090 | ||
|
|
234e1618c8 | ||
|
|
bc605f1351 | ||
|
|
06b524213f | ||
|
|
21cea65fbe | ||
|
|
000429c3b5 | ||
|
|
85d79f25d9 | ||
|
|
31b7d97097 | ||
|
|
16098ba717 | ||
|
|
dfd29bfa04 | ||
|
|
ba25eee09a | ||
|
|
cf4cfb50fc | ||
|
|
91176c9291 | ||
|
|
5aa9359236 | ||
|
|
f760aba7dd | ||
|
|
b0409ad033 | ||
|
|
d91934955b | ||
|
|
d1e553f05a | ||
|
|
3bf4f0ce01 | ||
|
|
892f957729 | ||
|
|
d903daa046 | ||
|
|
b16ab3f0c0 | ||
|
|
c2bccc9b04 | ||
|
|
0b9d5c2b69 | ||
|
|
098f09844b | ||
|
|
2c334c08ac | ||
|
|
11766a2c41 | ||
|
|
ce389dfd79 | ||
|
|
48bba00cb3 | ||
|
|
b74043e2ee | ||
|
|
64436f1034 | ||
|
|
58864b79e4 | ||
|
|
cb8bbd7ccc | ||
|
|
37ad6365e3 | ||
|
|
6c4af86b29 | ||
|
|
f492c6fc61 | ||
|
|
d41ae73e0d | ||
|
|
26e623bec4 | ||
|
|
aeab33127d | ||
|
|
7b6d0c1acd | ||
|
|
b16c6f50a6 | ||
|
|
edf3743f49 | ||
|
|
1675ee99a4 | ||
|
|
f88e2312b8 | ||
|
|
28aaeef2b6 | ||
|
|
669288385e | ||
|
|
4b0516966d | ||
|
|
a9cb6bfe79 | ||
|
|
bcc1744a76 | ||
|
|
ce56d03c3e | ||
|
|
38c1949538 | ||
|
|
27394a091f | ||
|
|
b72246d769 | ||
|
|
a6e58e7b1d | ||
|
|
ad579d514b | ||
|
|
3b657484b2 | ||
|
|
eff9cd4b71 | ||
|
|
5e9486ef4c | ||
|
|
f29d683918 | ||
|
|
f55d591cba | ||
|
|
20ee982e3d | ||
|
|
c21db4b6d1 | ||
|
|
f0ef239e22 | ||
|
|
a714370eb2 | ||
|
|
abf5ad2eec | ||
|
|
68fdd7e3f0 | ||
|
|
49e671802a | ||
|
|
da8fa813c5 | ||
|
|
c823f0759d | ||
|
|
c71f7898ed | ||
|
|
a24972843e | ||
|
|
9f0262fc05 | ||
|
|
4fc55f75a8 | ||
|
|
fcb1d94946 | ||
|
|
38acf81a76 | ||
|
|
eaae856a37 | ||
|
|
79df475c9d | ||
|
|
a6ce43ba71 | ||
|
|
9e0fee27ab | ||
|
|
d3a6ee6a94 | ||
|
|
a4ec53c90a | ||
|
|
0e7a8301f7 | ||
|
|
5b4c5bd5c1 | ||
|
|
b4ee6420c5 | ||
|
|
e4c062c9c2 | ||
|
|
72bd3e9cc1 | ||
|
|
49cf3af769 | ||
|
|
127d6bf0a7 | ||
|
|
044f8cbffd | ||
|
|
89d0f6b761 | ||
|
|
d0bea51cd4 | ||
|
|
d325dab035 | ||
|
|
4174554260 | ||
|
|
3a81bb9725 | ||
|
|
f96c31deab | ||
|
|
aae6f535f5 | ||
|
|
23dcc16829 | ||
|
|
8f9fdd8b70 | ||
|
|
b4d06697b4 | ||
|
|
cc9da905a6 | ||
|
|
e79fa4145b | ||
|
|
0769b61dd2 | ||
|
|
3a8a29402d | ||
|
|
e21b876104 | ||
|
|
36b5af45c6 | ||
|
|
d0c14efbd1 | ||
|
|
3e9e6de16d | ||
|
|
a754becb46 | ||
|
|
fe9af9ced6 | ||
|
|
5c9c8c2670 | ||
|
|
0a4c72c571 | ||
|
|
f765a07c3b | ||
|
|
3e81a07563 | ||
|
|
c2e376879c | ||
|
|
548b56cf16 | ||
|
|
8333912b24 | ||
|
|
f879a775f8 | ||
|
|
51573146f0 | ||
|
|
835ec716a0 | ||
|
|
b86cf9681c | ||
|
|
3b4cc26210 | ||
|
|
3422998bd1 | ||
|
|
c90772666e | ||
|
|
096c5edbd4 | ||
|
|
3541b8a0dd | ||
|
|
9e410e3856 | ||
|
|
35d079beb1 | ||
|
|
c846e0f400 | ||
|
|
7ff8f3f7b9 | ||
|
|
27eb2571a4 | ||
|
|
ff2ab3b27e | ||
|
|
9df90e5e75 | ||
|
|
49ec3d68d2 | ||
|
|
9def1843b2 | ||
|
|
c26855d07e | ||
|
|
a3f147a827 | ||
|
|
a2d93915e8 | ||
|
|
29e9db184f | ||
|
|
2f93c7ae58 | ||
|
|
4abfe407da | ||
|
|
9b27e81091 | ||
|
|
39787743fd | ||
|
|
22e47807b8 | ||
|
|
898525a6d8 | ||
|
|
1ebcfe7d80 | ||
|
|
1dbc39b970 | ||
|
|
80f5f6c288 | ||
|
|
b18acd469f | ||
|
|
cefcd18269 | ||
|
|
4de3ac176d | ||
|
|
afd5699ff1 | ||
|
|
b79461e3ce | ||
|
|
de6ab8ecdf | ||
|
|
d0180d42a8 | ||
|
|
2e504b40f6 | ||
|
|
9b00304c29 | ||
|
|
979928ded8 | ||
|
|
7c4e442432 | ||
|
|
0dd445f101 | ||
|
|
f217804838 | ||
|
|
9a630fff06 | ||
|
|
db508214d7 | ||
|
|
8e764f48ae | ||
|
|
2583063f5f | ||
|
|
dd4ec22b39 | ||
|
|
42dbcec93f | ||
|
|
9bbf634f5d | ||
|
|
d6b09759de | ||
|
|
5bb8a943ad | ||
|
|
b370eda0d5 | ||
|
|
69bcbf6f27 | ||
|
|
04823abb83 | ||
|
|
7269a0ea12 | ||
|
|
153b58fbbd | ||
|
|
149af55e61 | ||
|
|
9cac95fd82 | ||
|
|
09498f3b18 | ||
|
|
bb919d0f32 | ||
|
|
6d0abd73a5 | ||
|
|
717f2a822b | ||
|
|
8946b4b4b6 | ||
|
|
db279d2b36 | ||
|
|
bb7965b3c4 | ||
|
|
a33fe5a4cc | ||
|
|
4a03f0870c | ||
|
|
bfe15aff19 | ||
|
|
42bc2d01f7 | ||
|
|
0cb9fc0df8 | ||
|
|
450896b122 | ||
|
|
cc0b89dbe2 | ||
|
|
c887b96a77 | ||
|
|
22541ae0f4 | ||
|
|
d83da38d79 | ||
|
|
b21f8abbd6 | ||
|
|
e9aade4d0d | ||
|
|
2bf2fff3d6 | ||
|
|
463a22fdfb | ||
|
|
3175022b31 | ||
|
|
504d910226 | ||
|
|
c2769e1a72 | ||
|
|
7577253dbf | ||
|
|
e4c5b2dbd1 | ||
|
|
fafd76ff94 | ||
|
|
e354d11820 | ||
|
|
a3374c7eca | ||
|
|
552b886cea | ||
|
|
ff9a19381b | ||
|
|
77aacc2e5d | ||
|
|
6a1fa2c13b | ||
|
|
c3bb64bf6e | ||
|
|
da68ab4ed0 | ||
|
|
1aedf92bcd | ||
|
|
abcf484506 | ||
|
|
a93681a980 | ||
|
|
a08758ea54 | ||
|
|
0a0f5b8e8c | ||
|
|
84f67a3ac1 | ||
|
|
22d18a9e58 | ||
|
|
d1a8425d43 | ||
|
|
65d94f03e4 | ||
|
|
3a49041f27 | ||
|
|
eec2f7c4a0 | ||
|
|
720df5ce89 | ||
|
|
d7cea16d4a | ||
|
|
d5c0fdd525 |
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
patreon: ryzerth
|
||||||
247
.github/workflows/build_all.yml
vendored
Normal file
247
.github/workflows/build_all.yml
vendored
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
name: Build Binaries
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||||
|
BUILD_TYPE: Release
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Create Build Environment
|
||||||
|
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||||
|
|
||||||
|
- name: Download PothosSDR
|
||||||
|
run: Invoke-WebRequest -Uri "https://downloads.myriadrf.org/builds/PothosSDR/PothosSDR-2020.01.26-vc14-x64.exe" -OutFile ${{runner.workspace}}/pothos.exe
|
||||||
|
|
||||||
|
- name: Install PothosSDR
|
||||||
|
run: mkdir "C:/Program Files/PothosSDR" ; 7z x ${{runner.workspace}}/pothos.exe -o"C:/Program Files/PothosSDR/"
|
||||||
|
|
||||||
|
- name: Download libusb
|
||||||
|
run: Invoke-WebRequest -Uri "https://github.com/libusb/libusb/releases/download/v1.0.23/libusb-1.0.23.7z" -OutFile ${{runner.workspace}}/libusb.7z
|
||||||
|
|
||||||
|
- name: Patch Pothos with earlier libusb version
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/"
|
||||||
|
|
||||||
|
- name: Download SDRPlay API
|
||||||
|
run: Invoke-WebRequest -Uri "https://drive.google.com/uc?id=12UHPMwkfa67A11QZDmpCT4iwHnyJHWuu" -OutFile ${{runner.workspace}}/SDRPlay.zip
|
||||||
|
|
||||||
|
- name: Install SDRPlay API
|
||||||
|
run: 7z x ${{runner.workspace}}/SDRPlay.zip -o"C:/Program Files/"
|
||||||
|
|
||||||
|
- name: Install vcpkg dependencies
|
||||||
|
run: vcpkg install fftw3:x64-windows glew:x64-windows glfw3:x64-windows portaudio:x64-windows
|
||||||
|
|
||||||
|
- name: Install rtaudio
|
||||||
|
run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install .
|
||||||
|
|
||||||
|
- name: Prepare CMake
|
||||||
|
working-directory: ${{runner.workspace}}/build
|
||||||
|
run: cmake "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
working-directory: ${{runner.workspace}}/build
|
||||||
|
run: cmake --build . --config Release
|
||||||
|
|
||||||
|
- name: Create Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: '&($Env:GITHUB_WORKSPACE + "/make_windows_package.ps1") ./build ($Env:GITHUB_WORKSPACE + "/root")'
|
||||||
|
|
||||||
|
- name: Save Archive
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: sdrpp_windows_x64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_windows_x64.zip
|
||||||
|
|
||||||
|
build_macos:
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Create Build Environment
|
||||||
|
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: brew install fftw glew glfw volk airspy portaudio hackrf rtl-sdr libbladerf
|
||||||
|
|
||||||
|
- name: Prepare CMake
|
||||||
|
working-directory: ${{runner.workspace}}/build
|
||||||
|
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_AIRSPYHF_SOURCE=OFF -DOPT_BUILD_PLUTOSDR_SOURCE=OFF -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
working-directory: ${{runner.workspace}}/build
|
||||||
|
run: make -j3
|
||||||
|
|
||||||
|
- name: Create Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: sh $GITHUB_WORKSPACE/make_macos_package.sh ${{runner.workspace}}/build
|
||||||
|
|
||||||
|
- name: Save Archive
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: sdrpp_macos_amd64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_macos_amd64.pkg
|
||||||
|
|
||||||
|
build_debian_buster:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Create Docker Image
|
||||||
|
run: cd $GITHUB_WORKSPACE/docker_builds/debian_buster && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
|
- name: Run Container
|
||||||
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
|
|
||||||
|
- name: Recover Deb Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
|
- name: Save Deb Archive
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: sdrpp_debian_buster_amd64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
|
build_debian_bullseye:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Create Docker Image
|
||||||
|
run: cd $GITHUB_WORKSPACE/docker_builds/debian_bullseye && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
|
- name: Run Container
|
||||||
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
|
|
||||||
|
- name: Recover Deb Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
|
- name: Save Deb Archive
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: sdrpp_debian_bullseye_amd64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
|
build_debian_sid:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Create Docker Image
|
||||||
|
run: cd $GITHUB_WORKSPACE/docker_builds/debian_sid && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
|
- name: Run Container
|
||||||
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
|
|
||||||
|
- name: Recover Deb Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
|
- name: Save Deb Archive
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: sdrpp_debian_sid_amd64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
|
build_ubuntu_focal:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Create Docker Image
|
||||||
|
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_focal && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
|
- name: Run Container
|
||||||
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
|
|
||||||
|
- name: Recover Deb Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
|
- name: Save Deb Archive
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: sdrpp_ubuntu_focal_amd64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
|
build_ubuntu_groovy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Create Docker Image
|
||||||
|
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_groovy && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
|
- name: Run Container
|
||||||
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
|
|
||||||
|
- name: Recover Deb Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
|
- name: Save Deb Archive
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: sdrpp_ubuntu_groovy_amd64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
|
build_ubuntu_hirsute:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Create Docker Image
|
||||||
|
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_hirsute && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
|
- name: Run Container
|
||||||
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
|
|
||||||
|
- name: Recover Deb Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
|
- name: Save Deb Archive
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: sdrpp_ubuntu_hirsute_amd64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
|
create_full_archive:
|
||||||
|
needs: ['build_windows', 'build_macos', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_groovy', 'build_ubuntu_hirsute']
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download All Builds
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
|
||||||
|
- name: Create Archive
|
||||||
|
run: >
|
||||||
|
mkdir sdrpp_all &&
|
||||||
|
mv sdrpp_windows_x64/sdrpp_windows_x64.zip sdrpp_all/ &&
|
||||||
|
mv sdrpp_macos_amd64/sdrpp_macos_amd64.pkg sdrpp_all/ &&
|
||||||
|
mv sdrpp_debian_buster_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_buster_amd64.deb &&
|
||||||
|
mv sdrpp_debian_bullseye_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bullseye_amd64.deb &&
|
||||||
|
mv sdrpp_debian_sid_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_amd64.deb &&
|
||||||
|
mv sdrpp_ubuntu_focal_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_amd64.deb &&
|
||||||
|
mv sdrpp_ubuntu_groovy_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_groovy_amd64.deb &&
|
||||||
|
mv sdrpp_ubuntu_hirsute_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_hirsute_amd64.deb
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: sdrpp_all
|
||||||
|
path: sdrpp_all/
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,5 +1,7 @@
|
|||||||
build/
|
build/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.vs/
|
||||||
|
.idea/
|
||||||
*.old
|
*.old
|
||||||
*.dll
|
*.dll
|
||||||
*.exe
|
*.exe
|
||||||
@@ -7,5 +9,6 @@ build/
|
|||||||
*.wav
|
*.wav
|
||||||
.DS_Store
|
.DS_Store
|
||||||
root_dev/
|
root_dev/
|
||||||
sdrpp_v0.2.5_beta_x64
|
Folder.DotSettings.user
|
||||||
sdrpp_v0.2.5_beta_x64_new_wf
|
CMakeSettings.json
|
||||||
|
poggers_decoder
|
||||||
187
CMakeLists.txt
187
CMakeLists.txt
@@ -1,50 +1,165 @@
|
|||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
project(sdrpp)
|
project(sdrpp)
|
||||||
|
|
||||||
option(OPT_BUILD_RTL_TCP_SOURCE "Build RTL-TCP Source Module (no dependencies required)" ON)
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
option(OPT_BUILD_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencies required)" ON)
|
set(CMAKE_INSTALL_PREFIX "/usr/local")
|
||||||
option(OPT_BUILD_SOAPY_SOURCE "Build SoapySDR Source Module (Depedencies: soapysdr)" ON)
|
else()
|
||||||
|
set(CMAKE_INSTALL_PREFIX "/usr")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
option(OPT_BUILD_AIRSPY_SOURCE "Build Airspy Source Module (Depedencies: libairspy)" ON)
|
||||||
option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Depedencies: libairspyhf)" ON)
|
option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Depedencies: libairspyhf)" ON)
|
||||||
|
option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Depedencies: libbladeRF)" OFF)
|
||||||
|
option(OPT_BUILD_FILE_SOURCE "Wav file source" ON)
|
||||||
|
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Depedencies: libhackrf)" ON)
|
||||||
|
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Depedencies: liblimesuite)" OFF)
|
||||||
|
option(OPT_BUILD_SDDC_SOURCE "Build SDDC Source Module (Depedencies: libusb-1.0)" OFF)
|
||||||
|
option(OPT_BUILD_RTL_SDR_SOURCE "Build RTL-SDR Source Module (Depedencies: librtlsdr)" ON)
|
||||||
|
option(OPT_BUILD_RTL_TCP_SOURCE "Build RTL-TCP Source Module (no dependencies required)" ON)
|
||||||
|
option(OPT_BUILD_SDRPLAY_SOURCE "Build SDRplay Source Module (Depedencies: libsdrplay)" OFF)
|
||||||
|
option(OPT_BUILD_SOAPY_SOURCE "Build SoapySDR Source Module (Depedencies: soapysdr)" ON)
|
||||||
|
option(OPT_BUILD_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencies required)" ON)
|
||||||
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Depedencies: libiio, libad9361)" ON)
|
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Depedencies: libiio, libad9361)" ON)
|
||||||
option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Depedencies: portaudio)" ON)
|
|
||||||
|
# Sinks
|
||||||
|
option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Depedencies: rtaudio)" ON)
|
||||||
|
option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Depedencies: portaudio)" OFF)
|
||||||
|
option(OPT_BUILD_NETWORK_SINK "Build Audio Sink Module (no dependencies required)" ON)
|
||||||
|
option(OPT_BUILD_NEW_PORTAUDIO_SINK "Build the new PortAudio Sink Module (Depedencies: portaudio)" OFF)
|
||||||
|
|
||||||
|
# Decoders
|
||||||
|
option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF)
|
||||||
|
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
|
||||||
|
option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
|
||||||
|
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" ON)
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
option(OPT_BUILD_DISCORD_PRESENCE "Build the Discord Rich Presence module" ON)
|
||||||
|
option(OPT_BUILD_FREQUENCY_MANAGER "Build the Frequency Manager module" ON)
|
||||||
|
option(OPT_BUILD_RECORDER "Audio and baseband recorder" ON)
|
||||||
|
option(OPT_BUILD_RIGCTL_SERVER "Rigctl backend for controlling SDR++ with software like gpredict" ON)
|
||||||
|
|
||||||
|
# Compiler arguments for each platform
|
||||||
|
if (MSVC)
|
||||||
|
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
||||||
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
|
||||||
|
else ()
|
||||||
|
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
|
||||||
|
endif ()
|
||||||
|
|
||||||
# Core of SDR++
|
# Core of SDR++
|
||||||
add_subdirectory("core")
|
add_subdirectory("core")
|
||||||
|
|
||||||
# Base modules
|
|
||||||
add_subdirectory("radio")
|
|
||||||
add_subdirectory("recorder")
|
|
||||||
|
|
||||||
# Source modules
|
# Source modules
|
||||||
if (OPT_BUILD_RTL_TCP_SOURCE)
|
if (OPT_BUILD_AIRSPY_SOURCE)
|
||||||
add_subdirectory("rtl_tcp_source")
|
add_subdirectory("airspy_source")
|
||||||
endif (OPT_BUILD_RTL_TCP_SOURCE)
|
endif (OPT_BUILD_AIRSPY_SOURCE)
|
||||||
|
|
||||||
if (OPT_BUILD_SPYSERVER_SOURCE)
|
|
||||||
add_subdirectory("spyserver_source")
|
|
||||||
endif (OPT_BUILD_SPYSERVER_SOURCE)
|
|
||||||
|
|
||||||
if (OPT_BUILD_SOAPY_SOURCE)
|
|
||||||
add_subdirectory("soapy_source")
|
|
||||||
endif (OPT_BUILD_SOAPY_SOURCE)
|
|
||||||
|
|
||||||
if (OPT_BUILD_AIRSPYHF_SOURCE)
|
if (OPT_BUILD_AIRSPYHF_SOURCE)
|
||||||
add_subdirectory("airspyhf_source")
|
add_subdirectory("airspyhf_source")
|
||||||
endif (OPT_BUILD_AIRSPYHF_SOURCE)
|
endif (OPT_BUILD_AIRSPYHF_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_BLADERF_SOURCE)
|
||||||
|
add_subdirectory("bladerf_source")
|
||||||
|
endif (OPT_BUILD_BLADERF_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_FILE_SOURCE)
|
||||||
|
add_subdirectory("file_source")
|
||||||
|
endif (OPT_BUILD_FILE_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_HACKRF_SOURCE)
|
||||||
|
add_subdirectory("hackrf_source")
|
||||||
|
endif (OPT_BUILD_HACKRF_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_LIMESDR_SOURCE)
|
||||||
|
add_subdirectory("limesdr_source")
|
||||||
|
endif (OPT_BUILD_LIMESDR_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_SDDC_SOURCE)
|
||||||
|
add_subdirectory("sddc_source")
|
||||||
|
endif (OPT_BUILD_SDDC_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_RTL_SDR_SOURCE)
|
||||||
|
add_subdirectory("rtl_sdr_source")
|
||||||
|
endif (OPT_BUILD_RTL_SDR_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_RTL_TCP_SOURCE)
|
||||||
|
add_subdirectory("rtl_tcp_source")
|
||||||
|
endif (OPT_BUILD_RTL_TCP_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_SDRPLAY_SOURCE)
|
||||||
|
add_subdirectory("sdrplay_source")
|
||||||
|
endif (OPT_BUILD_SDRPLAY_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_SOAPY_SOURCE)
|
||||||
|
add_subdirectory("soapy_source")
|
||||||
|
endif (OPT_BUILD_SOAPY_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_SPYSERVER_SOURCE)
|
||||||
|
add_subdirectory("spyserver_source")
|
||||||
|
endif (OPT_BUILD_SPYSERVER_SOURCE)
|
||||||
|
|
||||||
if (OPT_BUILD_PLUTOSDR_SOURCE)
|
if (OPT_BUILD_PLUTOSDR_SOURCE)
|
||||||
add_subdirectory("plutosdr_source")
|
add_subdirectory("plutosdr_source")
|
||||||
endif (OPT_BUILD_PLUTOSDR_SOURCE)
|
endif (OPT_BUILD_PLUTOSDR_SOURCE)
|
||||||
|
|
||||||
|
|
||||||
|
# Sink modules
|
||||||
if (OPT_BUILD_AUDIO_SINK)
|
if (OPT_BUILD_AUDIO_SINK)
|
||||||
add_subdirectory("audio_sink")
|
add_subdirectory("audio_sink")
|
||||||
endif (OPT_BUILD_AUDIO_SINK)
|
endif (OPT_BUILD_AUDIO_SINK)
|
||||||
|
|
||||||
if (MSVC)
|
if (OPT_BUILD_PORTAUDIO_SINK)
|
||||||
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
add_subdirectory("portaudio_sink")
|
||||||
else()
|
endif (OPT_BUILD_PORTAUDIO_SINK)
|
||||||
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
|
|
||||||
endif (MSVC)
|
if (OPT_BUILD_NETWORK_SINK)
|
||||||
|
add_subdirectory("network_sink")
|
||||||
|
endif (OPT_BUILD_NETWORK_SINK)
|
||||||
|
|
||||||
|
if (OPT_BUILD_NEW_PORTAUDIO_SINK)
|
||||||
|
add_subdirectory("new_portaudio_sink")
|
||||||
|
endif (OPT_BUILD_NEW_PORTAUDIO_SINK)
|
||||||
|
|
||||||
|
|
||||||
|
# Decoders
|
||||||
|
if (OPT_BUILD_FALCON9_DECODER)
|
||||||
|
add_subdirectory("falcon9_decoder")
|
||||||
|
endif (OPT_BUILD_FALCON9_DECODER)
|
||||||
|
|
||||||
|
if (OPT_BUILD_METEOR_DEMODULATOR)
|
||||||
|
add_subdirectory("meteor_demodulator")
|
||||||
|
endif (OPT_BUILD_METEOR_DEMODULATOR)
|
||||||
|
|
||||||
|
if (OPT_BUILD_RADIO)
|
||||||
|
add_subdirectory("radio")
|
||||||
|
endif (OPT_BUILD_RADIO)
|
||||||
|
|
||||||
|
if (OPT_BUILD_WEATHER_SAT_DECODER)
|
||||||
|
add_subdirectory("weather_sat_decoder")
|
||||||
|
endif (OPT_BUILD_WEATHER_SAT_DECODER)
|
||||||
|
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
if (OPT_BUILD_DISCORD_PRESENCE)
|
||||||
|
add_subdirectory("discord_integration")
|
||||||
|
endif (OPT_BUILD_DISCORD_PRESENCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_FREQUENCY_MANAGER)
|
||||||
|
add_subdirectory("frequency_manager")
|
||||||
|
endif (OPT_BUILD_FREQUENCY_MANAGER)
|
||||||
|
|
||||||
|
if (OPT_BUILD_RECORDER)
|
||||||
|
add_subdirectory("recorder")
|
||||||
|
endif (OPT_BUILD_RECORDER)
|
||||||
|
|
||||||
|
if (OPT_BUILD_RIGCTL_SERVER)
|
||||||
|
add_subdirectory("rigctl_server")
|
||||||
|
endif (OPT_BUILD_RIGCTL_SERVER)
|
||||||
|
|
||||||
|
|
||||||
add_executable(sdrpp "src/main.cpp" "win32/resources.rc")
|
add_executable(sdrpp "src/main.cpp" "win32/resources.rc")
|
||||||
target_link_libraries(sdrpp PRIVATE sdrpp_core)
|
target_link_libraries(sdrpp PRIVATE sdrpp_core)
|
||||||
@@ -53,13 +168,18 @@ target_link_libraries(sdrpp PRIVATE sdrpp_core)
|
|||||||
if (MSVC)
|
if (MSVC)
|
||||||
add_custom_target(do_always ALL xcopy /s \"$<TARGET_FILE_DIR:sdrpp_core>\\*.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
|
add_custom_target(do_always ALL xcopy /s \"$<TARGET_FILE_DIR:sdrpp_core>\\*.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
|
||||||
add_custom_target(do_always_volk ALL xcopy /s \"C:/Program Files/PothosSDR/bin\\volk.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
|
add_custom_target(do_always_volk ALL xcopy /s \"C:/Program Files/PothosSDR/bin\\volk.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
|
||||||
endif (MSVC)
|
endif ()
|
||||||
|
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
|
||||||
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.so\" \"$<TARGET_FILE_DIR:sdrpp>\")
|
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.so\" \"$<TARGET_FILE_DIR:sdrpp>\")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||||
|
target_link_libraries(sdrpp PUBLIC pthread)
|
||||||
|
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.so\" \"$<TARGET_FILE_DIR:sdrpp>\")
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.so\" \"$<TARGET_FILE_DIR:sdrpp>\")
|
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.so\" \"$<TARGET_FILE_DIR:sdrpp>\")
|
||||||
endif ()
|
endif ()
|
||||||
@@ -68,4 +188,21 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
|||||||
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.dylib\" \"$<TARGET_FILE_DIR:sdrpp>\")
|
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.dylib\" \"$<TARGET_FILE_DIR:sdrpp>\")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" -G "Visual Studio 15 2017 Win64"
|
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON
|
||||||
|
|
||||||
|
# Install directives
|
||||||
|
install(TARGETS sdrpp DESTINATION bin)
|
||||||
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/bandplans DESTINATION share/sdrpp)
|
||||||
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/colormaps DESTINATION share/sdrpp)
|
||||||
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/fonts DESTINATION share/sdrpp)
|
||||||
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/icons DESTINATION share/sdrpp)
|
||||||
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/themes DESTINATION share/sdrpp)
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/sdrpp.desktop ${CMAKE_CURRENT_BINARY_DIR}/sdrpp.desktop @ONLY)
|
||||||
|
|
||||||
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/sdrpp.desktop DESTINATION /usr/share/applications)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Create uninstall target
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/cmake_uninstall.cmake ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake @ONLY)
|
||||||
|
add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
||||||
|
|||||||
42
airspy_source/CMakeLists.txt
Normal file
42
airspy_source/CMakeLists.txt
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(airspy_source)
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
||||||
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
|
||||||
|
else ()
|
||||||
|
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
include_directories("src/")
|
||||||
|
|
||||||
|
file(GLOB SRC "src/*.cpp")
|
||||||
|
|
||||||
|
add_library(airspy_source SHARED ${SRC})
|
||||||
|
target_link_libraries(airspy_source PRIVATE sdrpp_core)
|
||||||
|
set_target_properties(airspy_source PROPERTIES PREFIX "")
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# Lib path
|
||||||
|
target_link_directories(airspy_source PUBLIC "C:/Program Files/PothosSDR/bin/")
|
||||||
|
|
||||||
|
target_link_libraries(airspy_source PUBLIC airspy)
|
||||||
|
else (MSVC)
|
||||||
|
find_package(PkgConfig)
|
||||||
|
|
||||||
|
pkg_check_modules(LIBAIRSPY REQUIRED libairspy)
|
||||||
|
|
||||||
|
target_include_directories(airspy_source PUBLIC ${LIBAIRSPY_INCLUDE_DIRS})
|
||||||
|
target_link_directories(airspy_source PUBLIC ${LIBAIRSPY_LIBRARY_DIRS})
|
||||||
|
target_link_libraries(airspy_source PUBLIC ${LIBAIRSPY_LIBRARIES})
|
||||||
|
|
||||||
|
# Include it because for some reason pkgconfig doesn't look here?
|
||||||
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
|
target_include_directories(airspy_source PUBLIC "/usr/local/include")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Install directives
|
||||||
|
install(TARGETS airspy_source DESTINATION lib/sdrpp/plugins)
|
||||||
607
airspy_source/src/main.cpp
Normal file
607
airspy_source/src/main.cpp
Normal file
@@ -0,0 +1,607 @@
|
|||||||
|
#include <imgui.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <module.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <core.h>
|
||||||
|
#include <gui/style.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <options.h>
|
||||||
|
#include <libairspy/airspy.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO {
|
||||||
|
/* Name: */ "airspy_source",
|
||||||
|
/* Description: */ "Airspy source module for SDR++",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ 1
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigManager config;
|
||||||
|
|
||||||
|
class AirspySourceModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
AirspySourceModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
airspy_init();
|
||||||
|
|
||||||
|
sampleRate = 10000000.0;
|
||||||
|
|
||||||
|
handler.ctx = this;
|
||||||
|
handler.selectHandler = menuSelected;
|
||||||
|
handler.deselectHandler = menuDeselected;
|
||||||
|
handler.menuHandler = menuHandler;
|
||||||
|
handler.startHandler = start;
|
||||||
|
handler.stopHandler = stop;
|
||||||
|
handler.tuneHandler = tune;
|
||||||
|
handler.stream = &stream;
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
if (sampleRateList.size() > 0) {
|
||||||
|
sampleRate = sampleRateList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select device from config
|
||||||
|
config.acquire();
|
||||||
|
std::string devSerial = config.conf["device"];
|
||||||
|
config.release();
|
||||||
|
selectByString(devSerial);
|
||||||
|
|
||||||
|
sigpath::sourceManager.registerSource("Airspy", &handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
~AirspySourceModule() {
|
||||||
|
stop(this);
|
||||||
|
sigpath::sourceManager.unregisterSource("Airspy");
|
||||||
|
airspy_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
devList.clear();
|
||||||
|
devListTxt = "";
|
||||||
|
|
||||||
|
uint64_t serials[256];
|
||||||
|
int n = airspy_list_devices(serials, 256);
|
||||||
|
|
||||||
|
char buf[1024];
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
sprintf(buf, "%016" PRIX64, serials[i]);
|
||||||
|
devList.push_back(serials[i]);
|
||||||
|
devListTxt += buf;
|
||||||
|
devListTxt += '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectFirst() {
|
||||||
|
if (devList.size() != 0) {
|
||||||
|
selectBySerial(devList[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectByString(std::string serial) {
|
||||||
|
char buf[1024];
|
||||||
|
for (int i = 0; i < devList.size(); i++) {
|
||||||
|
sprintf(buf, "%016" PRIX64, devList[i]);
|
||||||
|
std::string str = buf;
|
||||||
|
if (serial == str) {
|
||||||
|
selectBySerial(devList[i]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectBySerial(uint64_t serial) {
|
||||||
|
airspy_device* dev;
|
||||||
|
try {
|
||||||
|
int err = airspy_open_sn(&dev, serial);
|
||||||
|
if (err != 0) {
|
||||||
|
char buf[1024];
|
||||||
|
sprintf(buf, "%016" PRIX64, serial);
|
||||||
|
spdlog::error("Could not open Airspy {0}", buf);
|
||||||
|
selectedSerial = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception e) {
|
||||||
|
char buf[1024];
|
||||||
|
sprintf(buf, "%016" PRIX64, serial);
|
||||||
|
spdlog::error("Could not open Airspy {0}", buf);
|
||||||
|
}
|
||||||
|
selectedSerial = serial;
|
||||||
|
|
||||||
|
uint32_t sampleRates[256];
|
||||||
|
airspy_get_samplerates(dev, sampleRates, 0);
|
||||||
|
int n = sampleRates[0];
|
||||||
|
airspy_get_samplerates(dev, sampleRates, n);
|
||||||
|
sampleRateList.clear();
|
||||||
|
sampleRateListTxt = "";
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
sampleRateList.push_back(sampleRates[i]);
|
||||||
|
sampleRateListTxt += getBandwdithScaled(sampleRates[i]);
|
||||||
|
sampleRateListTxt += '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf[1024];
|
||||||
|
sprintf(buf, "%016" PRIX64, serial);
|
||||||
|
selectedSerStr = std::string(buf);
|
||||||
|
|
||||||
|
// Load config here
|
||||||
|
config.acquire();
|
||||||
|
bool created = false;
|
||||||
|
if (!config.conf["devices"].contains(selectedSerStr)) {
|
||||||
|
created = true;
|
||||||
|
config.conf["devices"][selectedSerStr]["sampleRate"] = 10000000;
|
||||||
|
config.conf["devices"][selectedSerStr]["gainMode"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["sensitiveGain"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["linearGain"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["lnaGain"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["mixerGain"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["vgaGain"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["lnaAgc"] = false;
|
||||||
|
config.conf["devices"][selectedSerStr]["mixerAgc"] = false;
|
||||||
|
config.conf["devices"][selectedSerStr]["biasT"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load sample rate
|
||||||
|
srId = 0;
|
||||||
|
sampleRate = sampleRateList[0];
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("sampleRate")) {
|
||||||
|
int selectedSr = config.conf["devices"][selectedSerStr]["sampleRate"];
|
||||||
|
for (int i = 0; i < sampleRateList.size(); i++) {
|
||||||
|
if (sampleRateList[i] == selectedSr) {
|
||||||
|
srId = i;
|
||||||
|
sampleRate = selectedSr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load gains
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("gainMode")) {
|
||||||
|
gainMode = config.conf["devices"][selectedSerStr]["gainMode"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("sensitiveGain")) {
|
||||||
|
sensitiveGain = config.conf["devices"][selectedSerStr]["sensitiveGain"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("linearGain")) {
|
||||||
|
linearGain = config.conf["devices"][selectedSerStr]["linearGain"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("lnaGain")) {
|
||||||
|
lnaGain = config.conf["devices"][selectedSerStr]["lnaGain"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("mixerGain")) {
|
||||||
|
mixerGain = config.conf["devices"][selectedSerStr]["mixerGain"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("vgaGain")) {
|
||||||
|
vgaGain = config.conf["devices"][selectedSerStr]["vgaGain"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("lnaAgc")) {
|
||||||
|
lnaAgc = config.conf["devices"][selectedSerStr]["lnaAgc"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("mixerAgc")) {
|
||||||
|
mixerAgc = config.conf["devices"][selectedSerStr]["mixerAgc"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Bias-T
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("biasT")) {
|
||||||
|
biasT = config.conf["devices"][selectedSerStr]["biasT"];
|
||||||
|
}
|
||||||
|
|
||||||
|
config.release(created);
|
||||||
|
|
||||||
|
airspy_close(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string getBandwdithScaled(double bw) {
|
||||||
|
char buf[1024];
|
||||||
|
if (bw >= 1000000.0) {
|
||||||
|
sprintf(buf, "%.1lfMHz", bw / 1000000.0);
|
||||||
|
}
|
||||||
|
else if (bw >= 1000.0) {
|
||||||
|
sprintf(buf, "%.1lfKHz", bw / 1000.0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprintf(buf, "%.1lfHz", bw);
|
||||||
|
}
|
||||||
|
return std::string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuSelected(void* ctx) {
|
||||||
|
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
spdlog::info("AirspySourceModule '{0}': Menu Select!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuDeselected(void* ctx) {
|
||||||
|
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||||
|
spdlog::info("AirspySourceModule '{0}': Menu Deselect!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start(void* ctx) {
|
||||||
|
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||||
|
if (_this->running) { return; }
|
||||||
|
if (_this->selectedSerial == 0) {
|
||||||
|
spdlog::error("Tried to start Airspy source with null serial");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = airspy_open_sn(&_this->openDev, _this->selectedSerial);
|
||||||
|
if (err != 0) {
|
||||||
|
char buf[1024];
|
||||||
|
sprintf(buf, "%016" PRIX64, _this->selectedSerial);
|
||||||
|
spdlog::error("Could not open Airspy {0}", buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
airspy_set_samplerate(_this->openDev, _this->sampleRateList[_this->srId]);
|
||||||
|
airspy_set_freq(_this->openDev, _this->freq);
|
||||||
|
|
||||||
|
if (_this->gainMode == 0) {
|
||||||
|
airspy_set_lna_agc(_this->openDev, 0);
|
||||||
|
airspy_set_mixer_agc(_this->openDev, 0);
|
||||||
|
airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
|
||||||
|
}
|
||||||
|
else if (_this->gainMode == 1) {
|
||||||
|
airspy_set_lna_agc(_this->openDev, 0);
|
||||||
|
airspy_set_mixer_agc(_this->openDev, 0);
|
||||||
|
airspy_set_linearity_gain(_this->openDev, _this->linearGain);
|
||||||
|
}
|
||||||
|
else if (_this->gainMode == 2) {
|
||||||
|
if (_this->lnaAgc) {
|
||||||
|
airspy_set_lna_agc(_this->openDev, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
airspy_set_lna_agc(_this->openDev, 0);
|
||||||
|
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||||
|
}
|
||||||
|
if (_this->mixerAgc) {
|
||||||
|
airspy_set_mixer_agc(_this->openDev, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
airspy_set_mixer_agc(_this->openDev, 0);
|
||||||
|
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||||
|
}
|
||||||
|
airspy_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||||
|
}
|
||||||
|
|
||||||
|
airspy_set_rf_bias(_this->openDev, _this->biasT);
|
||||||
|
|
||||||
|
airspy_start_rx(_this->openDev, callback, _this);
|
||||||
|
|
||||||
|
_this->running = true;
|
||||||
|
spdlog::info("AirspySourceModule '{0}': Start!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop(void* ctx) {
|
||||||
|
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||||
|
if (!_this->running) { return; }
|
||||||
|
_this->running = false;
|
||||||
|
_this->stream.stopWriter();
|
||||||
|
airspy_close(_this->openDev);
|
||||||
|
_this->stream.clearWriteStop();
|
||||||
|
spdlog::info("AirspySourceModule '{0}': Stop!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tune(double freq, void* ctx) {
|
||||||
|
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||||
|
if (_this->running) {
|
||||||
|
airspy_set_freq(_this->openDev, freq);
|
||||||
|
}
|
||||||
|
_this->freq = freq;
|
||||||
|
spdlog::info("AirspySourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||||
|
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||||
|
|
||||||
|
if (_this->running) { style::beginDisabled(); }
|
||||||
|
|
||||||
|
ImGui::SetNextItemWidth(menuWidth);
|
||||||
|
if (ImGui::Combo(CONCAT("##_airspy_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) {
|
||||||
|
_this->selectBySerial(_this->devList[_this->devId]);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["device"] = _this->selectedSerStr;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Combo(CONCAT("##_airspy_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) {
|
||||||
|
_this->sampleRate = _this->sampleRateList[_this->srId];
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["sampleRate"] = _this->sampleRate;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX();
|
||||||
|
if (ImGui::Button(CONCAT("Refresh##_airspy_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) {
|
||||||
|
_this->refresh();
|
||||||
|
config.acquire();
|
||||||
|
std::string devSerial = config.conf["device"];
|
||||||
|
config.release();
|
||||||
|
_this->selectByString(devSerial);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->running) { style::endDisabled(); }
|
||||||
|
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
ImGui::Columns(3, CONCAT("AirspyGainModeColumns##_", _this->name), false);
|
||||||
|
if (ImGui::RadioButton(CONCAT("Sensitive##_airspy_gm_", _this->name), _this->gainMode == 0)) {
|
||||||
|
_this->gainMode = 0;
|
||||||
|
if (_this->running) {
|
||||||
|
airspy_set_lna_agc(_this->openDev, 0);
|
||||||
|
airspy_set_mixer_agc(_this->openDev, 0);
|
||||||
|
airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 0;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::NextColumn();
|
||||||
|
if (ImGui::RadioButton(CONCAT("Linear##_airspy_gm_", _this->name), _this->gainMode == 1)) {
|
||||||
|
_this->gainMode = 1;
|
||||||
|
if (_this->running) {
|
||||||
|
airspy_set_lna_agc(_this->openDev, 0);
|
||||||
|
airspy_set_mixer_agc(_this->openDev, 0);
|
||||||
|
airspy_set_linearity_gain(_this->openDev, _this->linearGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 1;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::NextColumn();
|
||||||
|
if (ImGui::RadioButton(CONCAT("Free##_airspy_gm_", _this->name), _this->gainMode == 2)) {
|
||||||
|
_this->gainMode = 2;
|
||||||
|
if (_this->running) {
|
||||||
|
if (_this->lnaAgc) {
|
||||||
|
airspy_set_lna_agc(_this->openDev, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
airspy_set_lna_agc(_this->openDev, 0);
|
||||||
|
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||||
|
}
|
||||||
|
if (_this->mixerAgc) {
|
||||||
|
airspy_set_mixer_agc(_this->openDev, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
airspy_set_mixer_agc(_this->openDev, 0);
|
||||||
|
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||||
|
}
|
||||||
|
airspy_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 2;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", _this->name), false);
|
||||||
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
// Gain menus
|
||||||
|
|
||||||
|
if (_this->gainMode == 0) {
|
||||||
|
ImGui::Text("Gain");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::SliderInt(CONCAT("##_airspy_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) {
|
||||||
|
if (_this->running) {
|
||||||
|
airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["sensitiveGain"] = _this->sensitiveGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_this->gainMode == 1) {
|
||||||
|
ImGui::Text("Gain");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::SliderInt(CONCAT("##_airspy_lin_gain_", _this->name), &_this->linearGain, 0, 21)) {
|
||||||
|
if (_this->running) {
|
||||||
|
airspy_set_linearity_gain(_this->openDev, _this->linearGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["linearGain"] = _this->linearGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_this->gainMode == 2) {
|
||||||
|
// Calculate position of sliders
|
||||||
|
float pos = ImGui::CalcTextSize("Mixer Gain").x + 10;
|
||||||
|
|
||||||
|
if (_this->lnaAgc) { style::beginDisabled(); }
|
||||||
|
ImGui::Text("LNA Gain");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetCursorPosX(pos);
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::SliderInt(CONCAT("##_airspy_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) {
|
||||||
|
if (_this->running) {
|
||||||
|
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["lnaGain"] = _this->lnaGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_this->lnaAgc) { style::endDisabled(); }
|
||||||
|
|
||||||
|
if (_this->mixerAgc) { style::beginDisabled(); }
|
||||||
|
ImGui::Text("Mixer Gain");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetCursorPosX(pos);
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::SliderInt(CONCAT("##_airspy_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) {
|
||||||
|
if (_this->running) {
|
||||||
|
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["mixerGain"] = _this->mixerGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_this->mixerAgc) { style::endDisabled(); }
|
||||||
|
|
||||||
|
ImGui::Text("VGA Gain");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetCursorPosX(pos);
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::SliderInt(CONCAT("##_airspy_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
|
||||||
|
if (_this->running) {
|
||||||
|
airspy_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["vgaGain"] = _this->vgaGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AGC Control
|
||||||
|
if (ImGui::Checkbox(CONCAT("LNA AGC##_airspy_", _this->name), &_this->lnaAgc)) {
|
||||||
|
if (_this->running) {
|
||||||
|
if (_this->lnaAgc) {
|
||||||
|
airspy_set_lna_agc(_this->openDev, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
airspy_set_lna_agc(_this->openDev, 0);
|
||||||
|
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["lnaAgc"] = _this->lnaAgc;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::Checkbox(CONCAT("Mixer AGC##_airspy_", _this->name), &_this->mixerAgc)) {
|
||||||
|
if (_this->running) {
|
||||||
|
if (_this->mixerAgc) {
|
||||||
|
airspy_set_mixer_agc(_this->openDev, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
airspy_set_mixer_agc(_this->openDev, 0);
|
||||||
|
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["mixerAgc"] = _this->mixerAgc;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bias T
|
||||||
|
|
||||||
|
if (ImGui::Checkbox(CONCAT("Bias T##_airspy_", _this->name), &_this->biasT)) {
|
||||||
|
if (_this->running) {
|
||||||
|
airspy_set_rf_bias(_this->openDev, _this->biasT);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["biasT"] = _this->biasT;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int callback(airspy_transfer_t* transfer) {
|
||||||
|
AirspySourceModule* _this = (AirspySourceModule*)transfer->ctx;
|
||||||
|
memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t));
|
||||||
|
if (!_this->stream.swap(transfer->sample_count)) { return -1; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
airspy_device* openDev;
|
||||||
|
bool enabled = true;
|
||||||
|
dsp::stream<dsp::complex_t> stream;
|
||||||
|
double sampleRate;
|
||||||
|
SourceManager::SourceHandler handler;
|
||||||
|
bool running = false;
|
||||||
|
double freq;
|
||||||
|
uint64_t selectedSerial = 0;
|
||||||
|
std::string selectedSerStr = "";
|
||||||
|
int devId = 0;
|
||||||
|
int srId = 0;
|
||||||
|
|
||||||
|
bool biasT = false;
|
||||||
|
|
||||||
|
int lnaGain = 0;
|
||||||
|
int vgaGain = 0;
|
||||||
|
int mixerGain = 0;
|
||||||
|
int linearGain = 0;
|
||||||
|
int sensitiveGain = 0;
|
||||||
|
|
||||||
|
int gainMode = 0;
|
||||||
|
|
||||||
|
bool lnaAgc = false;
|
||||||
|
bool mixerAgc = false;
|
||||||
|
|
||||||
|
std::vector<uint64_t> devList;
|
||||||
|
std::string devListTxt;
|
||||||
|
std::vector<uint32_t> sampleRateList;
|
||||||
|
std::string sampleRateListTxt;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
json def = json({});
|
||||||
|
def["devices"] = json({});
|
||||||
|
def["device"] = "";
|
||||||
|
config.setPath(options::opts.root + "/airspy_config.json");
|
||||||
|
config.load(def);
|
||||||
|
config.enableAutoSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new AirspySourceModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||||
|
delete (AirspySourceModule*)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _END_() {
|
||||||
|
config.disableAutoSave();
|
||||||
|
config.save();
|
||||||
|
}
|
||||||
@@ -3,9 +3,11 @@ project(airspyhf_source)
|
|||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
||||||
else()
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive")
|
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
|
||||||
endif (MSVC)
|
else ()
|
||||||
|
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
|
||||||
|
endif ()
|
||||||
|
|
||||||
include_directories("src/")
|
include_directories("src/")
|
||||||
|
|
||||||
@@ -17,15 +19,24 @@ set_target_properties(airspyhf_source PROPERTIES PREFIX "")
|
|||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
# Lib path
|
# Lib path
|
||||||
target_link_directories(sdrpp_core PUBLIC "C:/Program Files/PothosSDR/bin/")
|
target_link_directories(airspyhf_source PUBLIC "C:/Program Files/PothosSDR/bin/")
|
||||||
|
|
||||||
target_link_libraries(airspyhf_source PUBLIC airspyhf)
|
target_link_libraries(airspyhf_source PUBLIC airspyhf)
|
||||||
else (MSVC)
|
else (MSVC)
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
|
|
||||||
pkg_check_modules(SOAPY REQUIRED airspyhf)
|
pkg_check_modules(LIBAIRSPYHF REQUIRED libairspyhf)
|
||||||
|
|
||||||
target_include_directories(airspyhf_source PUBLIC ${AIRSPYHF_INCLUDE_DIRS})
|
target_include_directories(airspyhf_source PUBLIC ${LIBAIRSPYHF_INCLUDE_DIRS})
|
||||||
target_link_directories(airspyhf_source PUBLIC ${AIRSPYHF_LIBRARY_DIRS})
|
target_link_directories(airspyhf_source PUBLIC ${LIBAIRSPYHF_LIBRARY_DIRS})
|
||||||
target_link_libraries(airspyhf_source PUBLIC ${AIRSPYHF_LIBRARIES})
|
target_link_libraries(airspyhf_source PUBLIC ${LIBAIRSPYHF_LIBRARIES})
|
||||||
endif (MSVC)
|
|
||||||
|
# Include it because for some reason pkgconfig doesn't look here?
|
||||||
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
|
target_include_directories(airspyhf_source PUBLIC "/usr/local/include")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Install directives
|
||||||
|
install(TARGETS airspyhf_source DESTINATION lib/sdrpp/plugins)
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <new_module.h>
|
#include <module.h>
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <signal_path/signal_path.h>
|
#include <signal_path/signal_path.h>
|
||||||
#include <core.h>
|
#include <core.h>
|
||||||
#include <gui/style.h>
|
#include <gui/style.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
#include <options.h>
|
||||||
#include <libairspyhf/airspyhf.h>
|
#include <libairspyhf/airspyhf.h>
|
||||||
|
#include <gui/widgets/stepped_slider.h>
|
||||||
|
|
||||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
@@ -18,7 +20,7 @@ SDRPP_MOD_INFO {
|
|||||||
/* Max instances */ 1
|
/* Max instances */ 1
|
||||||
};
|
};
|
||||||
|
|
||||||
//ConfigManager config;
|
ConfigManager config;
|
||||||
|
|
||||||
const char* AGG_MODES_STR = "Off\0Low\0High\0";
|
const char* AGG_MODES_STR = "Off\0Low\0High\0";
|
||||||
|
|
||||||
@@ -40,17 +42,21 @@ public:
|
|||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
// config.aquire();
|
config.acquire();
|
||||||
// std::string serString = config.conf["device"];
|
std::string devSerial = config.conf["device"];
|
||||||
// config.release();
|
config.release();
|
||||||
|
selectByString(devSerial);
|
||||||
|
|
||||||
sigpath::sourceManager.registerSource("Airspy HF+", &handler);
|
sigpath::sourceManager.registerSource("Airspy HF+", &handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
~AirspyHFSourceModule() {
|
~AirspyHFSourceModule() {
|
||||||
|
stop(this);
|
||||||
|
sigpath::sourceManager.unregisterSource("Airspy HF+");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
enum AGCMode {
|
enum AGCMode {
|
||||||
AGC_MODE_OFF,
|
AGC_MODE_OFF,
|
||||||
AGC_MODE_LOW,
|
AGC_MODE_LOW,
|
||||||
@@ -105,39 +111,97 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void selectBySerial(uint64_t serial) {
|
void selectBySerial(uint64_t serial) {
|
||||||
selectedSerial = serial;
|
|
||||||
airspyhf_device_t* dev;
|
airspyhf_device_t* dev;
|
||||||
int err = airspyhf_open_sn(&dev, selectedSerial);
|
try {
|
||||||
if (err != 0) {
|
int err = airspyhf_open_sn(&dev, selectedSerial);
|
||||||
|
if (err != 0) {
|
||||||
|
char buf[1024];
|
||||||
|
sprintf(buf, "%016" PRIX64, selectedSerial);
|
||||||
|
spdlog::error("Could not open Airspy HF+ {0}", buf);
|
||||||
|
selectedSerial = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception e) {
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
sprintf(buf, "%016" PRIX64, selectedSerial);
|
sprintf(buf, "%016" PRIX64, selectedSerial);
|
||||||
spdlog::error("Could not open Airspy HF+ {0}", buf);
|
spdlog::error("Could not open Airspy HF+ {0}", buf);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectedSerial = serial;
|
||||||
|
|
||||||
uint32_t sampleRates[256];
|
uint32_t sampleRates[256];
|
||||||
airspyhf_get_samplerates(dev, sampleRates, 0);
|
airspyhf_get_samplerates(dev, sampleRates, 0);
|
||||||
int n = sampleRates[0];
|
int n = sampleRates[0];
|
||||||
airspyhf_get_samplerates(dev, sampleRates, n);
|
airspyhf_get_samplerates(dev, sampleRates, n);
|
||||||
char buf[1024];
|
|
||||||
sampleRateList.clear();
|
sampleRateList.clear();
|
||||||
sampleRateListTxt = "";
|
sampleRateListTxt = "";
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
sampleRateList.push_back(sampleRates[i]);
|
sampleRateList.push_back(sampleRates[i]);
|
||||||
sprintf(buf, "%d", sampleRates[i]);
|
sampleRateListTxt += getBandwdithScaled(sampleRates[i]);
|
||||||
sampleRateListTxt += buf;
|
|
||||||
sampleRateListTxt += '\0';
|
sampleRateListTxt += '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
blockSize = airspyhf_get_output_size(dev);
|
char buf[1024];
|
||||||
spdlog::info("AirspyHF block size {0}", blockSize);
|
sprintf(buf, "%016" PRIX64, serial);
|
||||||
|
selectedSerStr = std::string(buf);
|
||||||
|
|
||||||
|
// Load config here
|
||||||
|
config.acquire();
|
||||||
|
bool created = false;
|
||||||
|
if (!config.conf["devices"].contains(selectedSerStr)) {
|
||||||
|
created = true;
|
||||||
|
config.conf["devices"][selectedSerStr]["sampleRate"] = 768000;
|
||||||
|
config.conf["devices"][selectedSerStr]["agcMode"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["lna"] = false;
|
||||||
|
config.conf["devices"][selectedSerStr]["attenuation"] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load sample rate
|
||||||
srId = 0;
|
srId = 0;
|
||||||
|
sampleRate = sampleRateList[0];
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("sampleRate")) {
|
||||||
|
int selectedSr = config.conf["devices"][selectedSerStr]["sampleRate"];
|
||||||
|
for (int i = 0; i < sampleRateList.size(); i++) {
|
||||||
|
if (sampleRateList[i] == selectedSr) {
|
||||||
|
srId = i;
|
||||||
|
sampleRate = selectedSr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Gains
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("agcMode")) {
|
||||||
|
agcMode = config.conf["devices"][selectedSerStr]["agcMode"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("lna")) {
|
||||||
|
hfLNA = config.conf["devices"][selectedSerStr]["lna"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("attenuation")) {
|
||||||
|
atten = config.conf["devices"][selectedSerStr]["attenuation"];
|
||||||
|
}
|
||||||
|
|
||||||
|
config.release(created);
|
||||||
|
|
||||||
airspyhf_close(dev);
|
airspyhf_close(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::string getBandwdithScaled(double bw) {
|
||||||
|
char buf[1024];
|
||||||
|
if (bw >= 1000000.0) {
|
||||||
|
sprintf(buf, "%.1lfMHz", bw / 1000000.0);
|
||||||
|
}
|
||||||
|
else if (bw >= 1000.0) {
|
||||||
|
sprintf(buf, "%.1lfKHz", bw / 1000.0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprintf(buf, "%.1lfHz", bw);
|
||||||
|
}
|
||||||
|
return std::string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
static void menuSelected(void* ctx) {
|
static void menuSelected(void* ctx) {
|
||||||
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
|
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
|
||||||
core::setInputSampleRate(_this->sampleRate);
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
@@ -151,9 +215,7 @@ private:
|
|||||||
|
|
||||||
static void start(void* ctx) {
|
static void start(void* ctx) {
|
||||||
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
|
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
|
||||||
if (_this->running) {
|
if (_this->running) { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_this->selectedSerial == 0) {
|
if (_this->selectedSerial == 0) {
|
||||||
spdlog::error("Tried to start AirspyHF+ source with null serial");
|
spdlog::error("Tried to start AirspyHF+ source with null serial");
|
||||||
return;
|
return;
|
||||||
@@ -167,15 +229,13 @@ private:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
spdlog::warn("{0}", _this->sampleRateList[_this->srId]);
|
|
||||||
|
|
||||||
airspyhf_set_samplerate(_this->openDev, _this->sampleRateList[_this->srId]);
|
airspyhf_set_samplerate(_this->openDev, _this->sampleRateList[_this->srId]);
|
||||||
airspyhf_set_freq(_this->openDev, _this->freq);
|
airspyhf_set_freq(_this->openDev, _this->freq);
|
||||||
airspyhf_set_hf_agc(_this->openDev, (_this->agcMode != 0));
|
airspyhf_set_hf_agc(_this->openDev, (_this->agcMode != 0));
|
||||||
if (_this->agcMode > 0) {
|
if (_this->agcMode > 0) {
|
||||||
airspyhf_set_hf_agc_threshold(_this->openDev, _this->agcMode - 1);
|
airspyhf_set_hf_agc_threshold(_this->openDev, _this->agcMode - 1);
|
||||||
}
|
}
|
||||||
airspyhf_set_hf_att(_this->openDev, _this->atten / 6);
|
airspyhf_set_hf_att(_this->openDev, _this->atten / 6.0f);
|
||||||
airspyhf_set_hf_lna(_this->openDev, _this->hfLNA);
|
airspyhf_set_hf_lna(_this->openDev, _this->hfLNA);
|
||||||
|
|
||||||
airspyhf_start(_this->openDev, callback, _this);
|
airspyhf_start(_this->openDev, callback, _this);
|
||||||
@@ -186,9 +246,7 @@ private:
|
|||||||
|
|
||||||
static void stop(void* ctx) {
|
static void stop(void* ctx) {
|
||||||
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
|
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
|
||||||
if (!_this->running) {
|
if (!_this->running) { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
_this->running = false;
|
_this->running = false;
|
||||||
_this->stream.stopWriter();
|
_this->stream.stopWriter();
|
||||||
airspyhf_close(_this->openDev);
|
airspyhf_close(_this->openDev);
|
||||||
@@ -214,18 +272,33 @@ private:
|
|||||||
ImGui::SetNextItemWidth(menuWidth);
|
ImGui::SetNextItemWidth(menuWidth);
|
||||||
if (ImGui::Combo(CONCAT("##_airspyhf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) {
|
if (ImGui::Combo(CONCAT("##_airspyhf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) {
|
||||||
_this->selectBySerial(_this->devList[_this->devId]);
|
_this->selectBySerial(_this->devList[_this->devId]);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["device"] = _this->selectedSerStr;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::Combo(CONCAT("##_airspyhf_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) {
|
if (ImGui::Combo(CONCAT("##_airspyhf_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) {
|
||||||
_this->sampleRate = _this->sampleRateList[_this->srId];
|
_this->sampleRate = _this->sampleRateList[_this->srId];
|
||||||
core::setInputSampleRate(_this->sampleRate);
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["sampleRate"] = _this->sampleRate;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX();
|
float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX();
|
||||||
if (ImGui::Button(CONCAT("Refresh##_airspyhf_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) {
|
if (ImGui::Button(CONCAT("Refresh##_airspyhf_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) {
|
||||||
_this->refresh();
|
_this->refresh();
|
||||||
_this->selectFirst();
|
config.acquire();
|
||||||
|
std::string devSerial = config.conf["device"];
|
||||||
|
config.release();
|
||||||
|
_this->selectByString(devSerial);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_this->running) { style::endDisabled(); }
|
if (_this->running) { style::endDisabled(); }
|
||||||
@@ -240,6 +313,11 @@ private:
|
|||||||
airspyhf_set_hf_agc_threshold(_this->openDev, _this->agcMode - 1);
|
airspyhf_set_hf_agc_threshold(_this->openDev, _this->agcMode - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["agcMode"] = _this->agcMode;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Text("HF LNA");
|
ImGui::Text("HF LNA");
|
||||||
@@ -248,33 +326,38 @@ private:
|
|||||||
if (_this->running) {
|
if (_this->running) {
|
||||||
airspyhf_set_hf_lna(_this->openDev, _this->hfLNA);
|
airspyhf_set_hf_lna(_this->openDev, _this->hfLNA);
|
||||||
}
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["lna"] = _this->hfLNA;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Text("Attenuation");
|
ImGui::Text("Attenuation");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
if (ImGui::SliderInt(CONCAT("##_airspyhf_attn_", _this->name), &_this->atten, 0, 48, "%d dB")) {
|
if (ImGui::SliderFloatWithSteps(CONCAT("##_airspyhf_attn_", _this->name), &_this->atten, 0, 48, 6, "%.0f dB")) {
|
||||||
_this->atten = (_this->atten / 6) * 6;
|
|
||||||
if (_this->running) {
|
if (_this->running) {
|
||||||
airspyhf_set_hf_att(_this->openDev, _this->atten / 6);
|
airspyhf_set_hf_att(_this->openDev, _this->atten / 6.0f);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["attenuation"] = _this->atten;
|
||||||
|
config.release(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int callback(airspyhf_transfer_t* transfer) {
|
static int callback(airspyhf_transfer_t* transfer) {
|
||||||
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)transfer->ctx;
|
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)transfer->ctx;
|
||||||
if (_this->stream.aquire() < 0) {
|
memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t));
|
||||||
return -1;
|
if (!_this->stream.swap(transfer->sample_count)) { return -1; }
|
||||||
}
|
|
||||||
memcpy(_this->stream.data, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t));
|
|
||||||
_this->stream.write(transfer->sample_count);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
airspyhf_device_t* openDev;
|
airspyhf_device_t* openDev;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
int blockSize = 0;
|
|
||||||
dsp::stream<dsp::complex_t> stream;
|
dsp::stream<dsp::complex_t> stream;
|
||||||
double sampleRate;
|
double sampleRate;
|
||||||
SourceManager::SourceHandler handler;
|
SourceManager::SourceHandler handler;
|
||||||
@@ -285,7 +368,8 @@ private:
|
|||||||
int srId = 0;
|
int srId = 0;
|
||||||
int agcMode = AGC_MODE_OFF;
|
int agcMode = AGC_MODE_OFF;
|
||||||
bool hfLNA = false;
|
bool hfLNA = false;
|
||||||
int atten = 0;
|
float atten = 0.0f;
|
||||||
|
std::string selectedSerStr = "";
|
||||||
|
|
||||||
std::vector<uint64_t> devList;
|
std::vector<uint64_t> devList;
|
||||||
std::string devListTxt;
|
std::string devListTxt;
|
||||||
@@ -294,12 +378,12 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
MOD_EXPORT void _INIT_() {
|
MOD_EXPORT void _INIT_() {
|
||||||
// config.setPath(ROOT_DIR "/airspyhf_config.json");
|
json def = json({});
|
||||||
// json defConf;
|
def["devices"] = json({});
|
||||||
// defConf["device"] = "";
|
def["device"] = "";
|
||||||
// defConf["devices"] = json::object();
|
config.setPath(options::opts.root + "/airspyhf_config.json");
|
||||||
// config.load(defConf);
|
config.load(def);
|
||||||
// config.enableAutoSave();
|
config.enableAutoSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
@@ -311,6 +395,6 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MOD_EXPORT void _END_() {
|
MOD_EXPORT void _END_() {
|
||||||
// config.disableAutoSave();
|
config.disableAutoSave();
|
||||||
// config.save();
|
config.save();
|
||||||
}
|
}
|
||||||
@@ -3,9 +3,11 @@ project(audio_sink)
|
|||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
||||||
else()
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive")
|
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
|
||||||
endif (MSVC)
|
else ()
|
||||||
|
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
|
||||||
|
endif ()
|
||||||
|
|
||||||
file(GLOB SRC "src/*.cpp")
|
file(GLOB SRC "src/*.cpp")
|
||||||
|
|
||||||
@@ -14,3 +16,25 @@ include_directories("src/")
|
|||||||
add_library(audio_sink SHARED ${SRC})
|
add_library(audio_sink SHARED ${SRC})
|
||||||
target_link_libraries(audio_sink PRIVATE sdrpp_core)
|
target_link_libraries(audio_sink PRIVATE sdrpp_core)
|
||||||
set_target_properties(audio_sink PROPERTIES PREFIX "")
|
set_target_properties(audio_sink PROPERTIES PREFIX "")
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# Lib path
|
||||||
|
target_link_directories(audio_sink PUBLIC "C:/Program Files (x86)/RtAudio/lib")
|
||||||
|
|
||||||
|
# Misc headers
|
||||||
|
target_include_directories(audio_sink PUBLIC "C:/Program Files (x86)/RtAudio/include/rtaudio")
|
||||||
|
|
||||||
|
target_link_libraries(audio_sink PUBLIC rtaudio)
|
||||||
|
else (MSVC)
|
||||||
|
find_package(PkgConfig)
|
||||||
|
|
||||||
|
pkg_check_modules(RTAUDIO REQUIRED rtaudio)
|
||||||
|
|
||||||
|
target_include_directories(audio_sink PUBLIC ${RTAUDIO_INCLUDE_DIRS})
|
||||||
|
target_link_directories(audio_sink PUBLIC ${RTAUDIO_LIBRARY_DIRS})
|
||||||
|
target_link_libraries(audio_sink PUBLIC ${RTAUDIO_LIBRARIES})
|
||||||
|
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Install directives
|
||||||
|
install(TARGETS audio_sink DESTINATION lib/sdrpp/plugins)
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <new_module.h>
|
#include <module.h>
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <signal_path/signal_path.h>
|
#include <signal_path/signal_path.h>
|
||||||
#include <signal_path/sink.h>
|
#include <signal_path/sink.h>
|
||||||
#include <portaudio.h>
|
|
||||||
#include <dsp/audio.h>
|
#include <dsp/audio.h>
|
||||||
|
#include <dsp/processing.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <RtAudio.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <options.h>
|
||||||
|
|
||||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
@@ -17,82 +20,46 @@ SDRPP_MOD_INFO {
|
|||||||
/* Max instances */ 1
|
/* Max instances */ 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ConfigManager config;
|
||||||
|
|
||||||
class AudioSink : SinkManager::Sink {
|
class AudioSink : SinkManager::Sink {
|
||||||
public:
|
public:
|
||||||
struct AudioDevice_t {
|
|
||||||
std::string name;
|
|
||||||
int index;
|
|
||||||
int channels;
|
|
||||||
int srId;
|
|
||||||
std::vector<double> sampleRates;
|
|
||||||
std::string txtSampleRates;
|
|
||||||
};
|
|
||||||
|
|
||||||
AudioSink(SinkManager::Stream* stream, std::string streamName) {
|
AudioSink(SinkManager::Stream* stream, std::string streamName) {
|
||||||
_stream = stream;
|
_stream = stream;
|
||||||
_streamName = streamName;
|
_streamName = streamName;
|
||||||
s2m.init(_stream->sinkOut);
|
s2m.init(_stream->sinkOut);
|
||||||
monoRB.init(&s2m.out);
|
monoPacker.init(&s2m.out, 512);
|
||||||
stereoRB.init(_stream->sinkOut);
|
stereoPacker.init(_stream->sinkOut, 512);
|
||||||
|
|
||||||
// Initialize PortAudio
|
bool created = false;
|
||||||
Pa_Initialize();
|
std::string device = "";
|
||||||
devCount = Pa_GetDeviceCount();
|
config.acquire();
|
||||||
devId = Pa_GetDefaultOutputDevice();
|
if (!config.conf.contains(_streamName)) {
|
||||||
const PaDeviceInfo *deviceInfo;
|
created = true;
|
||||||
PaStreamParameters outputParams;
|
config.conf[_streamName]["device"] = "";
|
||||||
outputParams.sampleFormat = paFloat32;
|
config.conf[_streamName]["devices"] = json({});
|
||||||
outputParams.hostApiSpecificStreamInfo = NULL;
|
}
|
||||||
|
device = config.conf[_streamName]["device"];
|
||||||
|
config.release(created);
|
||||||
|
|
||||||
// Gather hardware info
|
int count = audio.getDeviceCount();
|
||||||
for(int i = 0; i < devCount; i++) {
|
RtAudio::DeviceInfo info;
|
||||||
deviceInfo = Pa_GetDeviceInfo(i);
|
for (int i = 0; i < count; i++) {
|
||||||
if (deviceInfo->maxOutputChannels < 1) {
|
info = audio.getDeviceInfo(i);
|
||||||
continue;
|
if (!info.probed) { continue; }
|
||||||
}
|
if (info.outputChannels == 0) { continue; }
|
||||||
AudioDevice_t dev;
|
if (info.isDefaultOutput) { defaultDevId = devList.size(); }
|
||||||
dev.name = deviceInfo->name;
|
devList.push_back(info);
|
||||||
dev.index = i;
|
deviceIds.push_back(i);
|
||||||
dev.channels = std::min<int>(deviceInfo->maxOutputChannels, 2);
|
txtDevList += info.name;
|
||||||
dev.sampleRates.clear();
|
|
||||||
dev.txtSampleRates = "";
|
|
||||||
for (int j = 0; j < 6; j++) {
|
|
||||||
outputParams.channelCount = dev.channels;
|
|
||||||
outputParams.device = dev.index;
|
|
||||||
outputParams.suggestedLatency = deviceInfo->defaultLowOutputLatency;
|
|
||||||
PaError err = Pa_IsFormatSupported(NULL, &outputParams, POSSIBLE_SAMP_RATE[j]);
|
|
||||||
if (err != paFormatIsSupported) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
dev.sampleRates.push_back(POSSIBLE_SAMP_RATE[j]);
|
|
||||||
dev.txtSampleRates += std::to_string((int)POSSIBLE_SAMP_RATE[j]);
|
|
||||||
dev.txtSampleRates += '\0';
|
|
||||||
}
|
|
||||||
if (dev.sampleRates.size() == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (i == devId) {
|
|
||||||
devListId = devices.size();
|
|
||||||
defaultDev = devListId;
|
|
||||||
}
|
|
||||||
dev.srId = 0;
|
|
||||||
|
|
||||||
AudioDevice_t* _dev = new AudioDevice_t;
|
|
||||||
*_dev = dev;
|
|
||||||
devices.push_back(_dev);
|
|
||||||
|
|
||||||
deviceNames.push_back(deviceInfo->name);
|
|
||||||
txtDevList += deviceInfo->name;
|
|
||||||
txtDevList += '\0';
|
txtDevList += '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load config from file
|
selectByName(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
~AudioSink() {
|
~AudioSink() {
|
||||||
for (auto const& dev : devices) {
|
|
||||||
delete dev;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void start() {
|
void start() {
|
||||||
@@ -111,125 +78,163 @@ public:
|
|||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void selectFirst() {
|
||||||
|
selectById(defaultDevId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectByName(std::string name) {
|
||||||
|
for (int i = 0; i < devList.size(); i++) {
|
||||||
|
if (devList[i].name == name) {
|
||||||
|
selectById(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectById(int id) {
|
||||||
|
devId = id;
|
||||||
|
bool created = false;
|
||||||
|
config.acquire();
|
||||||
|
if (!config.conf[_streamName]["devices"].contains(devList[id].name)) {
|
||||||
|
created = true;
|
||||||
|
config.conf[_streamName]["devices"][devList[id].name] = devList[id].preferredSampleRate;
|
||||||
|
}
|
||||||
|
sampleRate = config.conf[_streamName]["devices"][devList[id].name];
|
||||||
|
config.release(created);
|
||||||
|
|
||||||
|
sampleRates = devList[id].sampleRates;
|
||||||
|
sampleRatesTxt = "";
|
||||||
|
char buf[256];
|
||||||
|
bool found = false;
|
||||||
|
unsigned int defaultId = 0;
|
||||||
|
unsigned int defaultSr = devList[id].preferredSampleRate;
|
||||||
|
for (int i = 0; i < sampleRates.size(); i++) {
|
||||||
|
if (sampleRates[i] == sampleRate) {
|
||||||
|
found = true;
|
||||||
|
srId = i;
|
||||||
|
}
|
||||||
|
if (sampleRates[i] == defaultSr) {
|
||||||
|
defaultId = i;
|
||||||
|
}
|
||||||
|
sprintf(buf, "%d", sampleRates[i]);
|
||||||
|
sampleRatesTxt += buf;
|
||||||
|
sampleRatesTxt += '\0';
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
sampleRate = defaultSr;
|
||||||
|
srId = defaultId;
|
||||||
|
}
|
||||||
|
|
||||||
|
_stream->setSampleRate(sampleRate);
|
||||||
|
|
||||||
|
if (running) { doStop(); }
|
||||||
|
if (running) { doStart(); }
|
||||||
|
}
|
||||||
|
|
||||||
void menuHandler() {
|
void menuHandler() {
|
||||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(menuWidth);
|
ImGui::SetNextItemWidth(menuWidth);
|
||||||
if (ImGui::Combo(("##_audio_sink_dev_"+_streamName).c_str(), &devListId, txtDevList.c_str())) {
|
if (ImGui::Combo(("##_audio_sink_dev_"+_streamName).c_str(), &devId, txtDevList.c_str())) {
|
||||||
// TODO: Load SR from config
|
selectById(devId);
|
||||||
if (running) {
|
config.acquire();
|
||||||
doStop();
|
config.conf[_streamName]["device"] = devList[devId].name;
|
||||||
doStart();
|
config.release(true);
|
||||||
}
|
|
||||||
// TODO: Save to config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioDevice_t* dev = devices[devListId];
|
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(menuWidth);
|
ImGui::SetNextItemWidth(menuWidth);
|
||||||
if (ImGui::Combo(("##_audio_sink_sr_"+_streamName).c_str(), &dev->srId, dev->txtSampleRates.c_str())) {
|
if (ImGui::Combo(("##_audio_sink_sr_"+_streamName).c_str(), &srId, sampleRatesTxt.c_str())) {
|
||||||
_stream->setSampleRate(dev->sampleRates[dev->srId]);
|
sampleRate = sampleRates[srId];
|
||||||
|
_stream->setSampleRate(sampleRate);
|
||||||
if (running) {
|
if (running) {
|
||||||
doStop();
|
doStop();
|
||||||
doStart();
|
doStart();
|
||||||
}
|
}
|
||||||
// TODO: Save to config
|
config.acquire();
|
||||||
|
config.conf[_streamName]["devices"][devList[devId].name] = sampleRate;
|
||||||
|
config.release(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doStart() {
|
void doStart() {
|
||||||
const PaDeviceInfo *deviceInfo;
|
RtAudio::StreamParameters parameters;
|
||||||
AudioDevice_t* dev = devices[devListId];
|
parameters.deviceId = deviceIds[devId];
|
||||||
PaStreamParameters outputParams;
|
parameters.nChannels = 2;
|
||||||
deviceInfo = Pa_GetDeviceInfo(dev->index);
|
unsigned int bufferFrames = sampleRate / 60;
|
||||||
outputParams.channelCount = 2;
|
RtAudio::StreamOptions opts;
|
||||||
outputParams.sampleFormat = paFloat32;
|
opts.flags = RTAUDIO_MINIMIZE_LATENCY;
|
||||||
outputParams.hostApiSpecificStreamInfo = NULL;
|
opts.streamName = _streamName;
|
||||||
outputParams.device = dev->index;
|
|
||||||
outputParams.suggestedLatency = Pa_GetDeviceInfo(outputParams.device)->defaultLowOutputLatency;
|
|
||||||
PaError err;
|
|
||||||
|
|
||||||
float sampleRate = dev->sampleRates[dev->srId];
|
try {
|
||||||
int bufferSize = sampleRate / 60.0f;
|
audio.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &callback, this, &opts);
|
||||||
|
stereoPacker.setSampleCount(bufferFrames);
|
||||||
if (dev->channels == 2) {
|
audio.startStream();
|
||||||
stereoRB.data.setMaxLatency(bufferSize * 2);
|
stereoPacker.start();
|
||||||
stereoRB.start();
|
|
||||||
err = Pa_OpenStream(&stream, NULL, &outputParams, sampleRate, paFramesPerBufferUnspecified, 0, _stereo_cb, this);
|
|
||||||
}
|
}
|
||||||
else {
|
catch ( RtAudioError& e ) {
|
||||||
monoRB.data.setMaxLatency(bufferSize * 2);
|
spdlog::error("Could not open audio device");
|
||||||
monoRB.start();
|
|
||||||
err = Pa_OpenStream(&stream, NULL, &outputParams, sampleRate, paFramesPerBufferUnspecified, 0, _mono_cb, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err != 0) {
|
|
||||||
spdlog::error("Error while opening audio stream: ({0}) => {1}", err, Pa_GetErrorText(err));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = Pa_StartStream(stream);
|
spdlog::info("RtAudio stream open");
|
||||||
if (err != 0) {
|
|
||||||
spdlog::error("Error while starting audio stream: ({0}) => {1}", err, Pa_GetErrorText(err));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spdlog::info("Audio device open.");
|
|
||||||
running = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void doStop() {
|
void doStop() {
|
||||||
monoRB.stop();
|
s2m.stop();
|
||||||
stereoRB.stop();
|
monoPacker.stop();
|
||||||
monoRB.data.stopReader();
|
stereoPacker.stop();
|
||||||
stereoRB.data.stopReader();
|
monoPacker.out.stopReader();
|
||||||
Pa_StopStream(stream);
|
stereoPacker.out.stopReader();
|
||||||
Pa_CloseStream(stream);
|
audio.stopStream();
|
||||||
monoRB.data.clearReadStop();
|
audio.closeStream();
|
||||||
stereoRB.data.clearReadStop();
|
monoPacker.out.clearReadStop();
|
||||||
|
stereoPacker.out.clearReadStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _mono_cb(const void *input, void *output, unsigned long frameCount,
|
static int callback( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *userData) {
|
||||||
const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) {
|
|
||||||
AudioSink* _this = (AudioSink*)userData;
|
AudioSink* _this = (AudioSink*)userData;
|
||||||
_this->monoRB.data.read((float*)output, frameCount);
|
int count = _this->stereoPacker.out.read();
|
||||||
return 0;
|
if (count < 0) { return 0; }
|
||||||
}
|
|
||||||
|
|
||||||
static int _stereo_cb(const void *input, void *output, unsigned long frameCount,
|
// For debug purposes only...
|
||||||
const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) {
|
// if (nBufferFrames != count) { spdlog::warn("Buffer size missmatch, wanted {0}, was asked for {1}", count, nBufferFrames); }
|
||||||
AudioSink* _this = (AudioSink*)userData;
|
// for (int i = 0; i < count; i++) {
|
||||||
_this->stereoRB.data.read((dsp::stereo_t*)output, frameCount);
|
// if (_this->stereoPacker.out.readBuf[i].l == NAN || _this->stereoPacker.out.readBuf[i].r == NAN) { spdlog::error("NAN in audio data"); }
|
||||||
|
// if (_this->stereoPacker.out.readBuf[i].l == INFINITY || _this->stereoPacker.out.readBuf[i].r == INFINITY) { spdlog::error("INFINITY in audio data"); }
|
||||||
|
// if (_this->stereoPacker.out.readBuf[i].l == -INFINITY || _this->stereoPacker.out.readBuf[i].r == -INFINITY) { spdlog::error("-INFINITY in audio data"); }
|
||||||
|
// }
|
||||||
|
|
||||||
|
memcpy(outputBuffer, _this->stereoPacker.out.readBuf, nBufferFrames * sizeof(dsp::stereo_t));
|
||||||
|
_this->stereoPacker.out.flush();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SinkManager::Stream* _stream;
|
SinkManager::Stream* _stream;
|
||||||
dsp::StereoToMono s2m;
|
dsp::StereoToMono s2m;
|
||||||
dsp::RingBufferSink<float> monoRB;
|
dsp::Packer<float> monoPacker;
|
||||||
dsp::RingBufferSink<dsp::stereo_t> stereoRB;
|
dsp::Packer<dsp::stereo_t> stereoPacker;
|
||||||
|
|
||||||
std::string _streamName;
|
std::string _streamName;
|
||||||
PaStream *stream;
|
|
||||||
|
|
||||||
int srId = 0;
|
int srId = 0;
|
||||||
int devCount;
|
int devCount;
|
||||||
int devId = 0;
|
int devId = 0;
|
||||||
int devListId = 0;
|
|
||||||
int defaultDev = 0;
|
|
||||||
bool running = false;
|
bool running = false;
|
||||||
|
|
||||||
const double POSSIBLE_SAMP_RATE[6] = {
|
unsigned int defaultDevId = 0;
|
||||||
48000.0f,
|
|
||||||
44100.0f,
|
|
||||||
24000.0f,
|
|
||||||
22050.0f,
|
|
||||||
12000.0f,
|
|
||||||
11025.0f
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<AudioDevice_t*> devices;
|
std::vector<RtAudio::DeviceInfo> devList;
|
||||||
std::vector<std::string> deviceNames;
|
std::vector<unsigned int> deviceIds;
|
||||||
std::string txtDevList;
|
std::string txtDevList;
|
||||||
|
|
||||||
|
std::vector<unsigned int> sampleRates;
|
||||||
|
std::string sampleRatesTxt;
|
||||||
|
unsigned int sampleRate = 48000;
|
||||||
|
|
||||||
|
RtAudio audio;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AudioSinkModule : public ModuleManager::Instance {
|
class AudioSinkModule : public ModuleManager::Instance {
|
||||||
@@ -238,13 +243,17 @@ public:
|
|||||||
this->name = name;
|
this->name = name;
|
||||||
provider.create = create_sink;
|
provider.create = create_sink;
|
||||||
provider.ctx = this;
|
provider.ctx = this;
|
||||||
|
|
||||||
sigpath::sinkManager.registerSinkProvider("Audio", provider);
|
sigpath::sinkManager.registerSinkProvider("Audio", provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
~AudioSinkModule() {
|
~AudioSinkModule() {
|
||||||
|
// Unregister sink, this will automatically stop and delete all instances of the audio sink
|
||||||
|
sigpath::sinkManager.unregisterSinkProvider("Audio");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
void enable() {
|
void enable() {
|
||||||
enabled = true;
|
enabled = true;
|
||||||
}
|
}
|
||||||
@@ -269,8 +278,10 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
MOD_EXPORT void _INIT_() {
|
MOD_EXPORT void _INIT_() {
|
||||||
// Nothing here
|
json def = json({});
|
||||||
// TODO: Do instancing here (in source modules as well) to prevent multiple loads
|
config.setPath(options::opts.root + "/audio_sink_config.json");
|
||||||
|
config.load(def);
|
||||||
|
config.enableAutoSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) {
|
MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) {
|
||||||
@@ -278,10 +289,11 @@ MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOD_EXPORT void _DELETE_INSTANCE_() {
|
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||||
|
delete (AudioSinkModule*)instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOD_EXPORT void _END_() {
|
MOD_EXPORT void _END_() {
|
||||||
|
config.disableAutoSave();
|
||||||
|
config.save();
|
||||||
}
|
}
|
||||||
36
bladerf_source/CMakeLists.txt
Normal file
36
bladerf_source/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(bladerf_source)
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
||||||
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
|
||||||
|
else ()
|
||||||
|
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
include_directories("src/")
|
||||||
|
|
||||||
|
file(GLOB SRC "src/*.cpp")
|
||||||
|
|
||||||
|
add_library(bladerf_source SHARED ${SRC})
|
||||||
|
target_link_libraries(bladerf_source PRIVATE sdrpp_core)
|
||||||
|
set_target_properties(bladerf_source PROPERTIES PREFIX "")
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# Lib path
|
||||||
|
target_link_directories(bladerf_source PUBLIC "C:/Program Files/PothosSDR/bin/")
|
||||||
|
|
||||||
|
target_link_libraries(bladerf_source PUBLIC bladeRF)
|
||||||
|
else (MSVC)
|
||||||
|
# Not in pkg-config
|
||||||
|
target_link_libraries(bladerf_source PUBLIC bladeRF)
|
||||||
|
|
||||||
|
# Include it because for some reason pkgconfig doesn't look here?
|
||||||
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
|
target_include_directories(airspyhf_source PUBLIC "/usr/local/include")
|
||||||
|
endif()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Install directives
|
||||||
|
install(TARGETS bladerf_source DESTINATION lib/sdrpp/plugins)
|
||||||
579
bladerf_source/src/main.cpp
Normal file
579
bladerf_source/src/main.cpp
Normal file
@@ -0,0 +1,579 @@
|
|||||||
|
#include <imgui.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <module.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <core.h>
|
||||||
|
#include <gui/style.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <options.h>
|
||||||
|
#include <gui/widgets/stepped_slider.h>
|
||||||
|
#include <libbladeRF.h>
|
||||||
|
#include <dsp/processing.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
#define NUM_BUFFERS 128
|
||||||
|
#define NUM_TRANSFERS 1
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO {
|
||||||
|
/* Name: */ "bladerf_source",
|
||||||
|
/* Description: */ "BladeRF source module for SDR++",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ 1
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigManager config;
|
||||||
|
|
||||||
|
class BladeRFSourceModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
BladeRFSourceModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
sampleRate = 1000000.0;
|
||||||
|
|
||||||
|
handler.ctx = this;
|
||||||
|
handler.selectHandler = menuSelected;
|
||||||
|
handler.deselectHandler = menuDeselected;
|
||||||
|
handler.menuHandler = menuHandler;
|
||||||
|
handler.startHandler = start;
|
||||||
|
handler.stopHandler = stop;
|
||||||
|
handler.tuneHandler = tune;
|
||||||
|
handler.stream = &stream;
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
// Select device here
|
||||||
|
config.acquire();
|
||||||
|
std::string serial = config.conf["device"];
|
||||||
|
config.release();
|
||||||
|
selectBySerial(serial);
|
||||||
|
|
||||||
|
sigpath::sourceManager.registerSource("BladeRF", &handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
~BladeRFSourceModule() {
|
||||||
|
stop(this);
|
||||||
|
sigpath::sourceManager.unregisterSource("BladeRF");
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
devListTxt = "";
|
||||||
|
|
||||||
|
if (devInfoList != NULL) {
|
||||||
|
bladerf_free_device_list(devInfoList);
|
||||||
|
}
|
||||||
|
|
||||||
|
devCount = bladerf_get_device_list(&devInfoList);
|
||||||
|
if (devCount < 0) {
|
||||||
|
spdlog::error("Could not list devices");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < devCount; i++) {
|
||||||
|
// Keep only the first 32 character of the serial number for display
|
||||||
|
devListTxt += std::string(devInfoList[i].serial).substr(0, 16);
|
||||||
|
devListTxt += '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectFirst() {
|
||||||
|
if (devCount > 0) { selectByInfo(&devInfoList[0]); }
|
||||||
|
else { selectedSerial = ""; }
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectBySerial(std::string serial, bool reloadChannelId = true) {
|
||||||
|
if (serial == "") {
|
||||||
|
selectFirst();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < devCount; i++) {
|
||||||
|
bladerf_devinfo info = devInfoList[i];
|
||||||
|
if (serial == info.serial) {
|
||||||
|
devId = i;
|
||||||
|
selectByInfo(&info, reloadChannelId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectByInfo(bladerf_devinfo* info, bool reloadChannelId = true) {
|
||||||
|
int ret = bladerf_open_with_devinfo(&openDev, info);
|
||||||
|
if (ret != 0) {
|
||||||
|
spdlog::error("Could not open device {0}", info->serial);
|
||||||
|
selectedSerial = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedSerial = info->serial;
|
||||||
|
for (int i = 0; i < devCount; i++) {
|
||||||
|
if (selectedSerial == devInfoList[i].serial) { devId = i; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather info about the BladeRF's ranges
|
||||||
|
channelCount = bladerf_get_channel_count(openDev, BLADERF_RX);
|
||||||
|
|
||||||
|
// Load the channelId if there are more than 1 channel
|
||||||
|
if (reloadChannelId) {
|
||||||
|
config.acquire();
|
||||||
|
if (channelCount > 1 && config.conf["devices"].contains(info->serial)) {
|
||||||
|
if (config.conf["devices"][info->serial].contains("channelId")) {
|
||||||
|
chanId = config.conf["devices"][info->serial]["channelId"];
|
||||||
|
}
|
||||||
|
else { chanId = 0; }
|
||||||
|
}
|
||||||
|
else { chanId = 0; }
|
||||||
|
config.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
chanId = std::clamp<int>(chanId, 0, channelCount - 1);
|
||||||
|
|
||||||
|
bladerf_get_sample_rate_range(openDev, BLADERF_CHANNEL_RX(chanId), &srRange);
|
||||||
|
bladerf_get_bandwidth_range(openDev, BLADERF_CHANNEL_RX(chanId), &bwRange);
|
||||||
|
bladerf_get_gain_range(openDev, BLADERF_CHANNEL_RX(chanId), &gainRange);
|
||||||
|
int gainModeCount = bladerf_get_gain_modes(openDev, BLADERF_CHANNEL_RX(chanId), &gainModes);
|
||||||
|
|
||||||
|
// Generate sampleRate and Bandwidth lists
|
||||||
|
sampleRates.clear();
|
||||||
|
sampleRatesTxt = "";
|
||||||
|
sampleRates.push_back(srRange->min);
|
||||||
|
sampleRatesTxt += getBandwdithScaled(srRange->min);
|
||||||
|
sampleRatesTxt += '\0';
|
||||||
|
for (int i = 2000000; i < srRange->max; i += 2000000) {
|
||||||
|
sampleRates.push_back(i);
|
||||||
|
sampleRatesTxt += getBandwdithScaled(i);
|
||||||
|
sampleRatesTxt += '\0';
|
||||||
|
}
|
||||||
|
sampleRates.push_back(srRange->max);
|
||||||
|
sampleRatesTxt += getBandwdithScaled(srRange->max);
|
||||||
|
sampleRatesTxt += '\0';
|
||||||
|
|
||||||
|
// Generate bandwidth list
|
||||||
|
bandwidths.clear();
|
||||||
|
bandwidthsTxt = "";
|
||||||
|
bandwidths.push_back(bwRange->min);
|
||||||
|
bandwidthsTxt += getBandwdithScaled(bwRange->min);
|
||||||
|
bandwidthsTxt += '\0';
|
||||||
|
for (int i = 2000000; i < bwRange->max; i += 2000000) {
|
||||||
|
bandwidths.push_back(i);
|
||||||
|
bandwidthsTxt += getBandwdithScaled(i);
|
||||||
|
bandwidthsTxt += '\0';
|
||||||
|
}
|
||||||
|
bandwidths.push_back(bwRange->max);
|
||||||
|
bandwidthsTxt += getBandwdithScaled(bwRange->max);
|
||||||
|
bandwidthsTxt += '\0';
|
||||||
|
bandwidthsTxt += "Auto";
|
||||||
|
bandwidthsTxt += '\0';
|
||||||
|
|
||||||
|
// Generate list of channel names
|
||||||
|
channelNamesTxt = "";
|
||||||
|
char buf[32];
|
||||||
|
for (int i = 0; i < channelCount; i++) {
|
||||||
|
sprintf(buf, "RX %d", i+1);
|
||||||
|
channelNamesTxt += buf;
|
||||||
|
channelNamesTxt += '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate gain mode list
|
||||||
|
gainModeNames.clear();
|
||||||
|
gainModesTxt = "";
|
||||||
|
for (int i = 0; i < gainModeCount; i++) {
|
||||||
|
std::string gm = gainModes[i].name;
|
||||||
|
gm[0] = gm[0] & (~0x20);
|
||||||
|
gainModeNames.push_back(gm);
|
||||||
|
gainModesTxt += gm;
|
||||||
|
gainModesTxt += '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load settings here
|
||||||
|
config.acquire();
|
||||||
|
|
||||||
|
if (!config.conf["devices"].contains(selectedSerial)) {
|
||||||
|
config.conf["devices"][info->serial]["channelId"] = 0;
|
||||||
|
config.conf["devices"][selectedSerial]["sampleRate"] = sampleRates[0];
|
||||||
|
config.conf["devices"][selectedSerial]["bandwidth"] = bandwidths.size(); // Auto
|
||||||
|
config.conf["devices"][selectedSerial]["gainMode"] = "Manual";
|
||||||
|
config.conf["devices"][selectedSerial]["overallGain"] = gainRange->min;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load sample rate
|
||||||
|
if (config.conf["devices"][selectedSerial].contains("sampleRate")) {
|
||||||
|
bool found = false;
|
||||||
|
uint64_t sr = config.conf["devices"][selectedSerial]["sampleRate"];
|
||||||
|
for (int i = 0; i < sampleRates.size(); i++) {
|
||||||
|
if (sr == sampleRates[i]) {
|
||||||
|
srId = i;
|
||||||
|
sampleRate = sampleRates[i];
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
srId = 0;
|
||||||
|
sampleRate = sampleRates[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
srId = 0;
|
||||||
|
sampleRate = sampleRates[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load bandwidth
|
||||||
|
if (config.conf["devices"][selectedSerial].contains("bandwidth")) {
|
||||||
|
bwId = config.conf["devices"][selectedSerial]["bandwidth"];
|
||||||
|
bwId = std::clamp<int>(bwId, 0, bandwidths.size());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bwId = 0;
|
||||||
|
}
|
||||||
|
config.release(true);
|
||||||
|
|
||||||
|
// Load gain mode
|
||||||
|
if (config.conf["devices"][selectedSerial].contains("gainMode")) {
|
||||||
|
std::string gm = config.conf["devices"][selectedSerial]["gainMode"];
|
||||||
|
bool found = false;
|
||||||
|
for (int i = 0; i < gainModeNames.size(); i++) {
|
||||||
|
if (gainModeNames[i] == gm) {
|
||||||
|
gainMode = i;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
for (int i = 0; i < gainModeNames.size(); i++) {
|
||||||
|
if (gainModeNames[i] == "Manual") {
|
||||||
|
gainMode = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < gainModeNames.size(); i++) {
|
||||||
|
if (gainModeNames[i] == "Manual") {
|
||||||
|
gainMode = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load gain
|
||||||
|
if (config.conf["devices"][selectedSerial].contains("overallGain")) {
|
||||||
|
overallGain = config.conf["devices"][selectedSerial]["overallGain"];
|
||||||
|
overallGain = std::clamp<int>(overallGain, gainRange->min, gainRange->max);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
overallGain = gainRange->min;
|
||||||
|
}
|
||||||
|
|
||||||
|
bladerf_close(openDev);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string getBandwdithScaled(double bw) {
|
||||||
|
char buf[1024];
|
||||||
|
if (bw >= 1000000.0) {
|
||||||
|
sprintf(buf, "%.1lfMHz", bw / 1000000.0);
|
||||||
|
}
|
||||||
|
else if (bw >= 1000.0) {
|
||||||
|
sprintf(buf, "%.1lfKHz", bw / 1000.0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprintf(buf, "%.1lfHz", bw);
|
||||||
|
}
|
||||||
|
return std::string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuSelected(void* ctx) {
|
||||||
|
BladeRFSourceModule* _this = (BladeRFSourceModule*)ctx;
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
spdlog::info("BladeRFSourceModule '{0}': Menu Select!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuDeselected(void* ctx) {
|
||||||
|
BladeRFSourceModule* _this = (BladeRFSourceModule*)ctx;
|
||||||
|
spdlog::info("BladeRFSourceModule '{0}': Menu Deselect!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start(void* ctx) {
|
||||||
|
BladeRFSourceModule* _this = (BladeRFSourceModule*)ctx;
|
||||||
|
if (_this->running) { return; }
|
||||||
|
if (_this->devCount == 0) { return; }
|
||||||
|
|
||||||
|
// Open device
|
||||||
|
bladerf_devinfo info = _this->devInfoList[_this->devId];
|
||||||
|
int ret = bladerf_open_with_devinfo(&_this->openDev, &info);
|
||||||
|
if (ret != 0) {
|
||||||
|
spdlog::error("Could not open device {0}", info.serial);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate buffer size, must be a multiple of 1024
|
||||||
|
_this->bufferSize = _this->sampleRate / 200.0;
|
||||||
|
_this->bufferSize /= 1024;
|
||||||
|
_this->bufferSize *= 1024;
|
||||||
|
if (_this->bufferSize < 1024) { _this->bufferSize = 1024; }
|
||||||
|
|
||||||
|
// Setup device parameters
|
||||||
|
bladerf_set_sample_rate(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->sampleRate, NULL);
|
||||||
|
bladerf_set_frequency(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->freq);
|
||||||
|
bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), (_this->bwId == _this->bandwidths.size()) ?
|
||||||
|
std::clamp<uint64_t>(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL);
|
||||||
|
bladerf_set_gain_mode(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->gainModes[_this->gainMode].mode);
|
||||||
|
|
||||||
|
// If gain mode is manual, set the gain
|
||||||
|
if (_this->gainModes[_this->gainMode].mode == BLADERF_GAIN_MANUAL) {
|
||||||
|
bladerf_set_gain(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->overallGain);
|
||||||
|
}
|
||||||
|
|
||||||
|
_this->streamingEnabled = true;
|
||||||
|
|
||||||
|
// Setup syncronous transfer
|
||||||
|
bladerf_sync_config(_this->openDev, BLADERF_RX_X1, BLADERF_FORMAT_SC16_Q11, 16, _this->bufferSize, 8, 3500);
|
||||||
|
|
||||||
|
// Enable streaming
|
||||||
|
bladerf_enable_module(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), true);
|
||||||
|
|
||||||
|
_this->running = true;
|
||||||
|
_this->workerThread = std::thread(&BladeRFSourceModule::worker, _this);
|
||||||
|
|
||||||
|
spdlog::info("BladeRFSourceModule '{0}': Start!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop(void* ctx) {
|
||||||
|
BladeRFSourceModule* _this = (BladeRFSourceModule*)ctx;
|
||||||
|
if (!_this->running) { return; }
|
||||||
|
_this->running = false;
|
||||||
|
_this->stream.stopWriter();
|
||||||
|
|
||||||
|
_this->streamingEnabled = false;
|
||||||
|
// Wait for read worker to terminate
|
||||||
|
if (_this->workerThread.joinable()) {
|
||||||
|
_this->workerThread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable streaming
|
||||||
|
bladerf_enable_module(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), false);
|
||||||
|
|
||||||
|
// Close device
|
||||||
|
bladerf_close(_this->openDev);
|
||||||
|
|
||||||
|
_this->stream.clearWriteStop();
|
||||||
|
spdlog::info("BladeRFSourceModule '{0}': Stop!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tune(double freq, void* ctx) {
|
||||||
|
BladeRFSourceModule* _this = (BladeRFSourceModule*)ctx;
|
||||||
|
_this->freq = freq;
|
||||||
|
if (_this->running) {
|
||||||
|
bladerf_set_frequency(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->freq);
|
||||||
|
}
|
||||||
|
spdlog::info("BladeRFSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
BladeRFSourceModule* _this = (BladeRFSourceModule*)ctx;
|
||||||
|
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||||
|
|
||||||
|
if (_this->running) { style::beginDisabled(); }
|
||||||
|
|
||||||
|
ImGui::SetNextItemWidth(menuWidth);
|
||||||
|
if (ImGui::Combo(CONCAT("##_balderf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) {
|
||||||
|
bladerf_devinfo info = _this->devInfoList[_this->devId];
|
||||||
|
_this->selectByInfo(&info);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
config.acquire();
|
||||||
|
config.conf["device"] = _this->selectedSerial;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Combo(CONCAT("##_balderf_sr_sel_", _this->name), &_this->srId, _this->sampleRatesTxt.c_str())) {
|
||||||
|
_this->sampleRate = _this->sampleRates[_this->srId];
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
if (_this->selectedSerial != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerial]["sampleRate"] = _this->sampleRates[_this->srId];
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh button
|
||||||
|
ImGui::SameLine();
|
||||||
|
float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX();
|
||||||
|
if (ImGui::Button(CONCAT("Refresh##_balderf_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) {
|
||||||
|
_this->refresh();
|
||||||
|
_this->selectBySerial(_this->selectedSerial, false);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel selection (only show if more than one channel)
|
||||||
|
if (_this->channelCount > 1) {
|
||||||
|
ImGui::Text("RX Channel");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
ImGui::Combo(CONCAT("##_balderf_ch_sel_", _this->name), &_this->chanId, _this->channelNamesTxt.c_str());
|
||||||
|
if (_this->selectedSerial != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerial]["channelId"] = _this->chanId;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->running) { style::endDisabled(); }
|
||||||
|
|
||||||
|
ImGui::Text("Bandwidth");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::Combo(CONCAT("##_balderf_bw_sel_", _this->name), &_this->bwId, _this->bandwidthsTxt.c_str())) {
|
||||||
|
if (_this->running) {
|
||||||
|
bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), (_this->bwId == _this->bandwidths.size()) ?
|
||||||
|
std::clamp<uint64_t>(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerial != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerial]["bandwidth"] = _this->bwId;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// General config BS
|
||||||
|
ImGui::Text("Gain control mode");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::Combo(CONCAT("##_balderf_gm_sel_", _this->name), &_this->gainMode, _this->gainModesTxt.c_str()) && _this->selectedSerial != "") {
|
||||||
|
if (_this->running) {
|
||||||
|
bladerf_set_gain_mode(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->gainModes[_this->gainMode].mode);
|
||||||
|
}
|
||||||
|
// if switched to manual, reset gains
|
||||||
|
if (_this->gainModes[_this->gainMode].mode == BLADERF_GAIN_MANUAL && _this->running) {
|
||||||
|
bladerf_set_gain(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->overallGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerial != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerial]["gainMode"] = _this->gainModeNames[_this->gainMode];
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->selectedSerial != "") { if (_this->gainModes[_this->gainMode].mode != BLADERF_GAIN_MANUAL) { style::beginDisabled(); } }
|
||||||
|
ImGui::Text("Gain");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::SliderInt("##_balderf_oag_sel_", &_this->overallGain, (_this->gainRange != NULL) ? _this->gainRange->min : 0, (_this->gainRange != NULL) ? _this->gainRange->max : 60)) {
|
||||||
|
if (_this->running) {
|
||||||
|
spdlog::info("Setting gain to {0}", _this->overallGain);
|
||||||
|
bladerf_set_gain(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->overallGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerial != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerial]["overallGain"] = _this->overallGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_this->selectedSerial != "") { if (_this->gainModes[_this->gainMode].mode != BLADERF_GAIN_MANUAL) { style::endDisabled(); } }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker() {
|
||||||
|
int16_t* buffer = new int16_t[bufferSize * 2];
|
||||||
|
bladerf_metadata meta;
|
||||||
|
|
||||||
|
while (streamingEnabled) {
|
||||||
|
// Receive from the stream and break on error
|
||||||
|
int ret = bladerf_sync_rx(openDev, buffer, bufferSize, &meta, 3500);
|
||||||
|
if (ret != 0) { break; }
|
||||||
|
|
||||||
|
// Convert to complex float and swap buffers
|
||||||
|
volk_16i_s32f_convert_32f((float*)stream.writeBuf, buffer, 32768.0f, bufferSize * 2);
|
||||||
|
if (!stream.swap(bufferSize)) { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
bladerf* openDev;
|
||||||
|
bool enabled = true;
|
||||||
|
dsp::stream<dsp::complex_t> stream;
|
||||||
|
double sampleRate;
|
||||||
|
SourceManager::SourceHandler handler;
|
||||||
|
bool running = false;
|
||||||
|
double freq;
|
||||||
|
int devId = 0;
|
||||||
|
int srId = 0;
|
||||||
|
int bwId = 0;
|
||||||
|
int chanId = 0;
|
||||||
|
int gainMode = 0;
|
||||||
|
bool streamingEnabled = false;
|
||||||
|
|
||||||
|
int channelCount;
|
||||||
|
|
||||||
|
const bladerf_range* srRange = NULL;
|
||||||
|
const bladerf_range* bwRange = NULL;
|
||||||
|
const bladerf_range* gainRange = NULL;
|
||||||
|
|
||||||
|
std::vector<uint64_t> sampleRates;
|
||||||
|
std::string sampleRatesTxt;
|
||||||
|
std::vector<uint64_t> bandwidths;
|
||||||
|
std::string bandwidthsTxt;
|
||||||
|
|
||||||
|
std::string channelNamesTxt;
|
||||||
|
|
||||||
|
int bufferSize;
|
||||||
|
struct bladerf_stream* rxStream;
|
||||||
|
|
||||||
|
int overallGain = 0;
|
||||||
|
|
||||||
|
std::thread workerThread;
|
||||||
|
|
||||||
|
int devCount = 0;
|
||||||
|
bladerf_devinfo* devInfoList = NULL;
|
||||||
|
std::string devListTxt;
|
||||||
|
|
||||||
|
std::string selectedSerial;
|
||||||
|
|
||||||
|
bool isBlade1 = false;
|
||||||
|
|
||||||
|
const bladerf_gain_modes* gainModes;
|
||||||
|
std::vector<std::string> gainModeNames;
|
||||||
|
std::string gainModesTxt;
|
||||||
|
int gainModeCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
json def = json({});
|
||||||
|
def["devices"] = json({});
|
||||||
|
def["device"] = "";
|
||||||
|
config.setPath(options::opts.root + "/bladerf_config.json");
|
||||||
|
config.load(def);
|
||||||
|
config.enableAutoSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new BladeRFSourceModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||||
|
delete (BladeRFSourceModule*)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _END_() {
|
||||||
|
config.disableAutoSave();
|
||||||
|
config.save();
|
||||||
|
}
|
||||||
32
cmake_uninstall.cmake
Normal file
32
cmake_uninstall.cmake
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# http://www.vtk.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F
|
||||||
|
|
||||||
|
IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||||
|
MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
|
||||||
|
ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||||
|
|
||||||
|
FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
|
||||||
|
STRING(REGEX REPLACE "\n" ";" files "${files}")
|
||||||
|
FOREACH(file ${files})
|
||||||
|
MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
|
||||||
|
IF(EXISTS "$ENV{DESTDIR}${file}")
|
||||||
|
EXEC_PROGRAM(
|
||||||
|
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||||
|
OUTPUT_VARIABLE rm_out
|
||||||
|
RETURN_VALUE rm_retval
|
||||||
|
)
|
||||||
|
IF(NOT "${rm_retval}" STREQUAL 0)
|
||||||
|
MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
|
||||||
|
ENDIF(NOT "${rm_retval}" STREQUAL 0)
|
||||||
|
ELSEIF(IS_SYMLINK "$ENV{DESTDIR}${file}")
|
||||||
|
EXEC_PROGRAM(
|
||||||
|
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||||
|
OUTPUT_VARIABLE rm_out
|
||||||
|
RETURN_VALUE rm_retval
|
||||||
|
)
|
||||||
|
IF(NOT "${rm_retval}" STREQUAL 0)
|
||||||
|
MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
|
||||||
|
ENDIF(NOT "${rm_retval}" STREQUAL 0)
|
||||||
|
ELSE(EXISTS "$ENV{DESTDIR}${file}")
|
||||||
|
MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
|
||||||
|
ENDIF(EXISTS "$ENV{DESTDIR}${file}")
|
||||||
|
ENDFOREACH(file)
|
||||||
125
contributing.md
Normal file
125
contributing.md
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
# Pull Requests
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
# Code Style
|
||||||
|
|
||||||
|
## Naming Convention
|
||||||
|
|
||||||
|
- Files: `snake_case.h` `snake_case.cpp`
|
||||||
|
- Namespaces: `CamelCase`
|
||||||
|
- Classes: `CamelCase`
|
||||||
|
- Structs: `CamelCase_t`
|
||||||
|
- Members: `camelCase`
|
||||||
|
- Enum: `SNAKE_CASE`
|
||||||
|
- Macros: `SNAKE_CASE`
|
||||||
|
|
||||||
|
## Brace Style
|
||||||
|
|
||||||
|
```c++
|
||||||
|
int myFunction() {
|
||||||
|
if (shortIf) { shortFunctionName(); }
|
||||||
|
|
||||||
|
if (longIf) {
|
||||||
|
longFunction();
|
||||||
|
otherStuff();
|
||||||
|
myLongFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: If it makes the code cleaner, remember to use the `?` keyword instead of a `if else` statement.
|
||||||
|
|
||||||
|
## Pointers
|
||||||
|
|
||||||
|
Please use `type* name` for pointers.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
Headers and their associated C++ files shall be in the same directory. All headers must use `#pragma once` instead of other include guards. Only include files in a header that are being used in that header. Include the rest in the associated C++ file.
|
||||||
|
|
||||||
|
# Modules
|
||||||
|
|
||||||
|
## Module Naming Convention
|
||||||
|
|
||||||
|
All modules names must be `snake_case`. If the module is a source, it must end with `_source`. If it is a sink, it must end with `_sink`.
|
||||||
|
|
||||||
|
For example, lets take the module named `cool_source`:
|
||||||
|
|
||||||
|
- Directory: `cool_source`
|
||||||
|
- Class: `CoolSourceModule`
|
||||||
|
- Binary: `cool_source.<os dynlib extension>`
|
||||||
|
|
||||||
|
## Integration into main repository
|
||||||
|
|
||||||
|
If the module meets the code quality requirements, it may be added to the official repository. A module that doesn't require any external dependencies that the core doesn't already use may be enabled for build by default. Otherwise, they must be disabled for build by default with a `OPT_BUILD_MODULE_NAME` variable set to `OFF`.
|
||||||
|
|
||||||
|
# JSON Formatting
|
||||||
|
|
||||||
|
The ability to add new radio band allocation identifiers and color maps relies on JSON files. Proper formatting of these JSOn files is important for reference and readability. The folowing guides will show you how to properly format the JSON files for their respective uses.
|
||||||
|
|
||||||
|
**IMPORTANT: JSON File cannot contain comments, there are only in this example for clarity**
|
||||||
|
|
||||||
|
## Band Frequency Allocation
|
||||||
|
|
||||||
|
Please follow this guide to properly format the JSON files for custom radio band allocation identifiers.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Short name (has to fit in the menu)",
|
||||||
|
"country_name": "Name of country or area, if applicable (Use '--' otherwise)",
|
||||||
|
"country_code": "Two letter country code, if applicable (Use '--' otherwise)",
|
||||||
|
"author_name": "Name of the original/main creator of the JSON file",
|
||||||
|
"author_url": "URL the author wishes to be associated with the file (personal website, GitHub, Twitter, etc)",
|
||||||
|
"bands": [
|
||||||
|
// Bands in this array must be sorted by their starting frequency
|
||||||
|
{
|
||||||
|
"name": "Name of the band",
|
||||||
|
"type": "Type name ('amateur', 'broadcast', 'marine', 'military', or any type decalre in config.json)",
|
||||||
|
"start": 148500, //In Hz, must be an integer
|
||||||
|
"end": 283500 //In Hz, must be an integer
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Name of the band",
|
||||||
|
"type": "Type name ('amateur', 'broadcast', 'marine', 'military', or any type decalre in config.json)",
|
||||||
|
"start": 526500, //In Hz, must be an integer
|
||||||
|
"end": 1606500 //In Hz, must be an integer
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Color Maps
|
||||||
|
|
||||||
|
Please follow this guide to properly format the JSON files for custom color maps.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Short name (has to fit in the menu)",
|
||||||
|
"author": "Name of the original/main creator of the color map",
|
||||||
|
"map": [
|
||||||
|
// These are the color codes, in hexidecimal (#RRGGBB) format, for the custom color scales for the waterfall. They must be entered as strings, not integers, with the hastag/pound-symbol proceeding the 6 digit number.
|
||||||
|
"#000020",
|
||||||
|
"#000030",
|
||||||
|
"#000050",
|
||||||
|
"#000091",
|
||||||
|
"#1E90FF",
|
||||||
|
"#FFFFFF",
|
||||||
|
"#FFFF00",
|
||||||
|
"#FE6D16",
|
||||||
|
"#FE6D16",
|
||||||
|
"#FF0000",
|
||||||
|
"#FF0000",
|
||||||
|
"#C60000",
|
||||||
|
"#9F0000",
|
||||||
|
"#750000",
|
||||||
|
"#4A0000"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Best Practices
|
||||||
|
|
||||||
|
* All additions and/or bug fixes to the core must not add additional dependencies.
|
||||||
|
* Use VSCode for development, VS seems to cause issues.
|
||||||
|
* DO NOT use libboost for any code meant for this repository
|
||||||
@@ -5,9 +5,11 @@ project(sdrpp_core)
|
|||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
|
||||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||||
else()
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
set(CMAKE_CXX_FLAGS "-g -O3 -std=c++17 -fpermissive")
|
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
|
||||||
endif (MSVC)
|
else ()
|
||||||
|
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
|
||||||
|
endif ()
|
||||||
add_definitions(-DSDRPP_IS_CORE)
|
add_definitions(-DSDRPP_IS_CORE)
|
||||||
|
|
||||||
# Main code
|
# Main code
|
||||||
@@ -16,6 +18,9 @@ file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
|
|||||||
# Add code to dyn lib
|
# Add code to dyn lib
|
||||||
add_library(sdrpp_core SHARED ${SRC})
|
add_library(sdrpp_core SHARED ${SRC})
|
||||||
|
|
||||||
|
# Set the install prefix
|
||||||
|
target_compile_definitions(sdrpp_core PUBLIC INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}")
|
||||||
|
|
||||||
# Include core headers
|
# Include core headers
|
||||||
target_include_directories(sdrpp_core PUBLIC "src/")
|
target_include_directories(sdrpp_core PUBLIC "src/")
|
||||||
target_include_directories(sdrpp_core PUBLIC "src/imgui")
|
target_include_directories(sdrpp_core PUBLIC "src/imgui")
|
||||||
@@ -31,9 +36,6 @@ if (MSVC)
|
|||||||
# Volk
|
# Volk
|
||||||
target_link_libraries(sdrpp_core PUBLIC volk)
|
target_link_libraries(sdrpp_core PUBLIC volk)
|
||||||
|
|
||||||
# SoapySDR
|
|
||||||
target_link_libraries(sdrpp_core PUBLIC SoapySDR)
|
|
||||||
|
|
||||||
# Glew
|
# Glew
|
||||||
find_package(GLEW REQUIRED)
|
find_package(GLEW REQUIRED)
|
||||||
target_link_libraries(sdrpp_core PUBLIC GLEW::GLEW)
|
target_link_libraries(sdrpp_core PUBLIC GLEW::GLEW)
|
||||||
@@ -46,11 +48,8 @@ if (MSVC)
|
|||||||
find_package(FFTW3f CONFIG REQUIRED)
|
find_package(FFTW3f CONFIG REQUIRED)
|
||||||
target_link_libraries(sdrpp_core PUBLIC FFTW3::fftw3f)
|
target_link_libraries(sdrpp_core PUBLIC FFTW3::fftw3f)
|
||||||
|
|
||||||
# PortAudio
|
# WinSock2
|
||||||
find_package(portaudio CONFIG REQUIRED)
|
target_link_libraries(sdrpp_core PUBLIC wsock32 ws2_32)
|
||||||
target_link_libraries(sdrpp_core PUBLIC portaudio portaudio_static)
|
|
||||||
|
|
||||||
target_link_libraries(sdrpp_core PUBLIC volk)
|
|
||||||
|
|
||||||
else()
|
else()
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
@@ -60,32 +59,28 @@ else()
|
|||||||
pkg_check_modules(FFTW3 REQUIRED fftw3f)
|
pkg_check_modules(FFTW3 REQUIRED fftw3f)
|
||||||
pkg_check_modules(VOLK REQUIRED volk)
|
pkg_check_modules(VOLK REQUIRED volk)
|
||||||
pkg_check_modules(GLFW3 REQUIRED glfw3)
|
pkg_check_modules(GLFW3 REQUIRED glfw3)
|
||||||
pkg_check_modules(PORTAUDIO REQUIRED portaudio-2.0)
|
|
||||||
|
|
||||||
target_include_directories(sdrpp_core PUBLIC
|
target_include_directories(sdrpp_core PUBLIC
|
||||||
${GLEW_INCLUDE_DIRS}
|
${GLEW_INCLUDE_DIRS}
|
||||||
${FFTW3_INCLUDE_DIRS}
|
${FFTW3_INCLUDE_DIRS}
|
||||||
${GLFW3_INCLUDE_DIRS}
|
${GLFW3_INCLUDE_DIRS}
|
||||||
${VOLK_INCLUDE_DIRS}
|
${VOLK_INCLUDE_DIRS}
|
||||||
${PORTAUDIO_INCLUDE_DIRS}
|
)
|
||||||
)
|
|
||||||
|
|
||||||
target_link_directories(sdrpp_core PUBLIC
|
target_link_directories(sdrpp_core PUBLIC
|
||||||
${GLEW_LIBRARY_DIRS}
|
${GLEW_LIBRARY_DIRS}
|
||||||
${FFTW3_LIBRARY_DIRS}
|
${FFTW3_LIBRARY_DIRS}
|
||||||
${GLFW3_LIBRARY_DIRS}
|
${GLFW3_LIBRARY_DIRS}
|
||||||
${VOLK_LIBRARY_DIRS}
|
${VOLK_LIBRARY_DIRS}
|
||||||
${PORTAUDIO_LIBRARY_DIRS}
|
)
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(sdrpp_core PUBLIC
|
target_link_libraries(sdrpp_core PUBLIC
|
||||||
${OPENGL_LIBRARIES}
|
${OPENGL_LIBRARIES}
|
||||||
${GLEW_STATIC_LIBRARIES}
|
${GLEW_LIBRARIES}
|
||||||
${FFTW3_STATIC_LIBRARIES}
|
${FFTW3_LIBRARIES}
|
||||||
${GLFW3_STATIC_LIBRARIES}
|
${GLFW3_LIBRARIES}
|
||||||
${VOLK_STATIC_LIBRARIES}
|
${VOLK_LIBRARIES}
|
||||||
${PORTAUDIO_STATIC_LIBRARIES}
|
)
|
||||||
)
|
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
target_link_libraries(sdrpp_core PUBLIC stdc++fs)
|
target_link_libraries(sdrpp_core PUBLIC stdc++fs)
|
||||||
@@ -101,3 +96,6 @@ endif ()
|
|||||||
set(CORE_FILES ${RUNTIME_OUTPUT_DIRECTORY} PARENT_SCOPE)
|
set(CORE_FILES ${RUNTIME_OUTPUT_DIRECTORY} PARENT_SCOPE)
|
||||||
|
|
||||||
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" -G "Visual Studio 15 2017 Win64"
|
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" -G "Visual Studio 15 2017 Win64"
|
||||||
|
|
||||||
|
# Install directives
|
||||||
|
install(TARGETS sdrpp_core DESTINATION lib)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
ConfigManager::ConfigManager() {
|
ConfigManager::ConfigManager() {
|
||||||
@@ -31,9 +32,16 @@ void ConfigManager::load(json def, bool lock) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ifstream file(path.c_str());
|
try {
|
||||||
file >> conf;
|
std::ifstream file(path.c_str());
|
||||||
file.close();
|
file >> conf;
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
catch (std::exception e) {
|
||||||
|
spdlog::error("Config file '{0}' is corrupted, resetting it", path);
|
||||||
|
conf = def;
|
||||||
|
save(false);
|
||||||
|
}
|
||||||
if (lock) { mtx.unlock(); }
|
if (lock) { mtx.unlock(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,73 +54,49 @@ void ConfigManager::save(bool lock) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManager::enableAutoSave() {
|
void ConfigManager::enableAutoSave() {
|
||||||
if (!autoSaveEnabled) {
|
if (autoSaveEnabled) { return; }
|
||||||
autoSaveEnabled = true;
|
autoSaveEnabled = true;
|
||||||
autoSaveThread = std::thread(autoSaveWorker, this);
|
termFlag = false;
|
||||||
}
|
autoSaveThread = std::thread(&ConfigManager::autoSaveWorker, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManager::disableAutoSave() {
|
void ConfigManager::disableAutoSave() {
|
||||||
if (autoSaveEnabled) {
|
if (!autoSaveEnabled) { return; }
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(termMtx);
|
||||||
autoSaveEnabled = false;
|
autoSaveEnabled = false;
|
||||||
autoSaveThread.join();
|
termFlag = true;
|
||||||
}
|
}
|
||||||
|
termCond.notify_one();
|
||||||
|
if (autoSaveThread.joinable()) { autoSaveThread.join(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManager::aquire() {
|
void ConfigManager::acquire() {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManager::release(bool changed) {
|
void ConfigManager::release(bool modified) {
|
||||||
this->changed |= changed;
|
changed |= modified;
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManager::autoSaveWorker(ConfigManager* _this) {
|
void ConfigManager::autoSaveWorker() {
|
||||||
while (_this->autoSaveEnabled) {
|
while (autoSaveEnabled) {
|
||||||
if (!_this->mtx.try_lock()) {
|
if (!mtx.try_lock()) {
|
||||||
spdlog::warn("ConfigManager locked, waiting...");
|
spdlog::warn("ConfigManager locked, waiting...");
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (_this->changed) {
|
if (changed) {
|
||||||
_this->changed = false;
|
changed = false;
|
||||||
_this->save(false);
|
save(false);
|
||||||
|
}
|
||||||
|
mtx.unlock();
|
||||||
|
|
||||||
|
// Sleep but listen for wakeup call
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(termMtx);
|
||||||
|
termCond.wait_for(lock, std::chrono::milliseconds(1000), [this]() { return termFlag; } );
|
||||||
}
|
}
|
||||||
_this->mtx.unlock();
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// void ConfigManager::setResourceDir(std::string path) {
|
|
||||||
// if (!std::filesystem::exists(path)) {
|
|
||||||
// spdlog::error("Resource directory '{0}' does not exist", path);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (!std::filesystem::is_regular_file(path)) {
|
|
||||||
// spdlog::error("Resource directory '{0}' is not a directory", path);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// resDir = path;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// std::string ConfigManager::getResourceDir() {
|
|
||||||
// return resDir;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void ConfigManager::setConfigDir(std::string path) {
|
|
||||||
// if (!std::filesystem::exists(path)) {
|
|
||||||
// spdlog::error("Resource directory '{0}' does not exist", path);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (!std::filesystem::is_regular_file(path)) {
|
|
||||||
// spdlog::error("Resource directory '{0}' is not a directory", path);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// resDir = path;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// std::string ConfigManager::getConfigDir() {
|
|
||||||
// return configDir;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
@@ -15,18 +16,22 @@ public:
|
|||||||
void save(bool lock = true);
|
void save(bool lock = true);
|
||||||
void enableAutoSave();
|
void enableAutoSave();
|
||||||
void disableAutoSave();
|
void disableAutoSave();
|
||||||
void aquire();
|
void acquire();
|
||||||
void release(bool changed = false);
|
void release(bool modified = false);
|
||||||
|
|
||||||
json conf;
|
json conf;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void autoSaveWorker(ConfigManager* _this);
|
void autoSaveWorker();
|
||||||
|
|
||||||
std::string path = "";
|
std::string path = "";
|
||||||
bool changed = false;
|
volatile bool changed = false;
|
||||||
bool autoSaveEnabled = false;
|
volatile bool autoSaveEnabled = false;
|
||||||
std::thread autoSaveThread;
|
std::thread autoSaveThread;
|
||||||
std::mutex mtx;
|
std::mutex mtx;
|
||||||
|
|
||||||
|
std::mutex termMtx;
|
||||||
|
std::condition_variable termCond;
|
||||||
|
volatile bool termFlag = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -14,9 +14,11 @@
|
|||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <core.h>
|
#include <core.h>
|
||||||
|
#include <glfw_window.h>
|
||||||
#include <options.h>
|
#include <options.h>
|
||||||
#include <duktape/duktape.h>
|
#include <filesystem>
|
||||||
#include <duktape/duk_console.h>
|
#include <gui/menus/theme.h>
|
||||||
|
#include <server.h>
|
||||||
|
|
||||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||||
#include <stb_image_resize.h>
|
#include <stb_image_resize.h>
|
||||||
@@ -27,18 +29,30 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef INSTALL_PREFIX
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#define INSTALL_PREFIX "/usr/local"
|
||||||
|
#else
|
||||||
|
#define INSTALL_PREFIX "/usr"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace core {
|
namespace core {
|
||||||
ConfigManager configManager;
|
ConfigManager configManager;
|
||||||
ScriptManager scriptManager;
|
|
||||||
ModuleManager moduleManager;
|
ModuleManager moduleManager;
|
||||||
|
ModuleComManager modComManager;
|
||||||
|
GLFWwindow* window;
|
||||||
|
|
||||||
void setInputSampleRate(double samplerate) {
|
void setInputSampleRate(double samplerate) {
|
||||||
|
sigpath::signalPath.sourceSampleRate = samplerate;
|
||||||
|
double effectiveSr = samplerate / ((double)(1 << sigpath::signalPath.decimation));
|
||||||
// NOTE: Zoom controls won't work
|
// NOTE: Zoom controls won't work
|
||||||
gui::waterfall.setBandwidth(samplerate);
|
spdlog::info("New DSP samplerate: {0} (source samplerate is {1})", effectiveSr, samplerate);
|
||||||
|
gui::waterfall.setBandwidth(effectiveSr);
|
||||||
gui::waterfall.setViewOffset(0);
|
gui::waterfall.setViewOffset(0);
|
||||||
gui::waterfall.setViewBandwidth(samplerate);
|
gui::waterfall.setViewBandwidth(effectiveSr);
|
||||||
sigpath::signalPath.setSampleRate(samplerate);
|
sigpath::signalPath.setSampleRate(effectiveSr);
|
||||||
setViewBandwidthSlider(samplerate);
|
gui::mainWindow.setViewBandwidthSlider(effectiveSr);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -58,25 +72,32 @@ static void maximized_callback(GLFWwindow* window, int n) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
duk_ret_t test_func(duk_context *ctx) {
|
|
||||||
printf("Hello from C++\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// main
|
// main
|
||||||
int sdrpp_main(int argc, char *argv[]) {
|
int sdrpp_main(int argc, char *argv[]) {
|
||||||
#ifdef _WIN32
|
|
||||||
//FreeConsole();
|
|
||||||
// ConfigManager::setResourceDir("./res");
|
|
||||||
// ConfigManager::setConfigDir(".");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
spdlog::info("SDR++ v" VERSION_STR);
|
spdlog::info("SDR++ v" VERSION_STR);
|
||||||
|
|
||||||
// Load default options and parse command line
|
// Load default options and parse command line
|
||||||
options::loadDefaults();
|
options::loadDefaults();
|
||||||
if (!options::parse(argc, argv)) { return -1; }
|
if (!options::parse(argc, argv)) { return -1; }
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (!options::opts.showConsole) { FreeConsole(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check root directory
|
||||||
|
if (!std::filesystem::exists(options::opts.root)) {
|
||||||
|
spdlog::warn("Root directory {0} does not exist, creating it", options::opts.root);
|
||||||
|
if (!std::filesystem::create_directory(options::opts.root)) {
|
||||||
|
spdlog::error("Could not create root directory {0}", options::opts.root);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::filesystem::is_directory(options::opts.root)) {
|
||||||
|
spdlog::error("{0} is not a directory", options::opts.root);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// ======== DEFAULT CONFIG ========
|
// ======== DEFAULT CONFIG ========
|
||||||
json defConfig;
|
json defConfig;
|
||||||
defConfig["bandColors"]["amateur"] = "#FF0000FF";
|
defConfig["bandColors"]["amateur"] = "#FF0000FF";
|
||||||
@@ -86,78 +107,213 @@ int sdrpp_main(int argc, char *argv[]) {
|
|||||||
defConfig["bandColors"]["military"] = "#FFFF00FF";
|
defConfig["bandColors"]["military"] = "#FFFF00FF";
|
||||||
defConfig["bandPlan"] = "General";
|
defConfig["bandPlan"] = "General";
|
||||||
defConfig["bandPlanEnabled"] = true;
|
defConfig["bandPlanEnabled"] = true;
|
||||||
|
defConfig["bandPlanPos"] = 0;
|
||||||
defConfig["centerTuning"] = false;
|
defConfig["centerTuning"] = false;
|
||||||
|
defConfig["colorMap"] = "Classic";
|
||||||
|
defConfig["fastFFT"] = false;
|
||||||
defConfig["fftHeight"] = 300;
|
defConfig["fftHeight"] = 300;
|
||||||
|
defConfig["fftRate"] = 20;
|
||||||
|
defConfig["fftSize"] = 65536;
|
||||||
|
defConfig["fftWindow"] = 1;
|
||||||
defConfig["frequency"] = 100000000.0;
|
defConfig["frequency"] = 100000000.0;
|
||||||
|
defConfig["fullWaterfallUpdate"] = false;
|
||||||
defConfig["max"] = 0.0;
|
defConfig["max"] = 0.0;
|
||||||
defConfig["maximized"] = false;
|
defConfig["maximized"] = false;
|
||||||
defConfig["menuOrder"] = {
|
|
||||||
"Source",
|
// Menu
|
||||||
"Radio",
|
defConfig["menuElements"] = json::array();
|
||||||
"Recorder",
|
|
||||||
"Sinks",
|
defConfig["menuElements"][0]["name"] = "Source";
|
||||||
"Audio",
|
defConfig["menuElements"][0]["open"] = true;
|
||||||
"Scripting",
|
|
||||||
"Band Plan",
|
defConfig["menuElements"][1]["name"] = "Radio";
|
||||||
"Display"
|
defConfig["menuElements"][1]["open"] = true;
|
||||||
};
|
|
||||||
|
defConfig["menuElements"][2]["name"] = "Recorder";
|
||||||
|
defConfig["menuElements"][2]["open"] = true;
|
||||||
|
|
||||||
|
defConfig["menuElements"][3]["name"] = "Sinks";
|
||||||
|
defConfig["menuElements"][3]["open"] = true;
|
||||||
|
|
||||||
|
defConfig["menuElements"][3]["name"] = "Frequency Manager";
|
||||||
|
defConfig["menuElements"][3]["open"] = true;
|
||||||
|
|
||||||
|
defConfig["menuElements"][4]["name"] = "VFO Color";
|
||||||
|
defConfig["menuElements"][4]["open"] = true;
|
||||||
|
|
||||||
|
defConfig["menuElements"][5]["name"] = "Scripting";
|
||||||
|
defConfig["menuElements"][5]["open"] = false;
|
||||||
|
|
||||||
|
defConfig["menuElements"][6]["name"] = "Band Plan";
|
||||||
|
defConfig["menuElements"][6]["open"] = true;
|
||||||
|
|
||||||
|
defConfig["menuElements"][7]["name"] = "Display";
|
||||||
|
defConfig["menuElements"][7]["open"] = true;
|
||||||
|
|
||||||
defConfig["menuWidth"] = 300;
|
defConfig["menuWidth"] = 300;
|
||||||
defConfig["min"] = -70.0;
|
defConfig["min"] = -120.0;
|
||||||
|
|
||||||
|
// Module instances
|
||||||
|
defConfig["moduleInstances"]["Airspy Source"]["module"] = "airspy_source";
|
||||||
|
defConfig["moduleInstances"]["Airspy Source"]["enabled"] = true;
|
||||||
|
defConfig["moduleInstances"]["AirspyHF+ Source"]["module"] = "airspyhf_source";
|
||||||
|
defConfig["moduleInstances"]["AirspyHF+ Source"]["enabled"] = true;
|
||||||
|
defConfig["moduleInstances"]["BladeRF Source"]["module"] = "bladerf_source";
|
||||||
|
defConfig["moduleInstances"]["BladeRF Source"]["enabled"] = true;
|
||||||
|
defConfig["moduleInstances"]["File Source"]["module"] = "file_source";
|
||||||
|
defConfig["moduleInstances"]["File Source"]["enabled"] = true;
|
||||||
|
defConfig["moduleInstances"]["HackRF Source"]["module"] = "hackrf_source";
|
||||||
|
defConfig["moduleInstances"]["HackRF Source"]["enabled"] = true;
|
||||||
|
defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source";
|
||||||
|
defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
|
||||||
|
defConfig["moduleInstances"]["RTL-SDR Source"]["module"] = "rtl_sdr_source";
|
||||||
|
defConfig["moduleInstances"]["RTL-SDR Source"]["enabled"] = true;
|
||||||
|
defConfig["moduleInstances"]["RTL-TCP Source"]["module"] = "rtl_tcp_source";
|
||||||
|
defConfig["moduleInstances"]["RTL-TCP Source"]["enabled"] = true;
|
||||||
|
defConfig["moduleInstances"]["SDRplay Source"]["module"] = "sdrplay_source";
|
||||||
|
defConfig["moduleInstances"]["SDRplay Source"]["enabled"] = true;
|
||||||
|
defConfig["moduleInstances"]["SoapySDR Source"]["module"] = "soapy_source";
|
||||||
|
defConfig["moduleInstances"]["SoapySDR Source"]["enabled"] = true;
|
||||||
|
defConfig["moduleInstances"]["SpyServer Source"]["module"] = "spyserver_source";
|
||||||
|
defConfig["moduleInstances"]["SpyServer Source"]["enabled"] = true;
|
||||||
|
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
|
||||||
|
defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true;
|
||||||
|
|
||||||
defConfig["moduleInstances"]["Audio Sink"] = "audio_sink";
|
defConfig["moduleInstances"]["Audio Sink"] = "audio_sink";
|
||||||
defConfig["moduleInstances"]["PlutoSDR Source"] = "plutosdr_source";
|
|
||||||
defConfig["moduleInstances"]["RTL-TCP Source"] = "rtl_tcp_source";
|
|
||||||
defConfig["moduleInstances"]["Radio"] = "radio";
|
defConfig["moduleInstances"]["Radio"] = "radio";
|
||||||
|
|
||||||
|
defConfig["moduleInstances"]["Frequency Manager"] = "frequency_manager";
|
||||||
defConfig["moduleInstances"]["Recorder"] = "recorder";
|
defConfig["moduleInstances"]["Recorder"] = "recorder";
|
||||||
defConfig["moduleInstances"]["SoapySDR Source"] = "soapy_source";
|
defConfig["moduleInstances"]["Rigctl Server"] = "rigctl_server";
|
||||||
|
|
||||||
|
|
||||||
|
// Themes
|
||||||
|
defConfig["theme"] = "Dark";
|
||||||
|
|
||||||
defConfig["modules"] = json::array();
|
defConfig["modules"] = json::array();
|
||||||
|
defConfig["offsetMode"] = (int)0; // Off
|
||||||
defConfig["offset"] = 0.0;
|
defConfig["offset"] = 0.0;
|
||||||
|
defConfig["showMenu"] = true;
|
||||||
defConfig["showWaterfall"] = true;
|
defConfig["showWaterfall"] = true;
|
||||||
defConfig["source"] = "";
|
defConfig["source"] = "";
|
||||||
defConfig["streams"] = json::object();
|
defConfig["decimationPower"] = 0;
|
||||||
|
defConfig["iqCorrection"] = false;
|
||||||
|
|
||||||
|
defConfig["streams"]["Radio"]["muted"] = false;
|
||||||
|
defConfig["streams"]["Radio"]["sink"] = "Audio";
|
||||||
|
defConfig["streams"]["Radio"]["volume"] = 1.0f;
|
||||||
|
|
||||||
defConfig["windowSize"]["h"] = 720;
|
defConfig["windowSize"]["h"] = 720;
|
||||||
defConfig["windowSize"]["w"] = 1280;
|
defConfig["windowSize"]["w"] = 1280;
|
||||||
|
|
||||||
|
defConfig["vfoOffsets"] = json::object();
|
||||||
|
|
||||||
|
defConfig["vfoColors"]["Radio"] = "#FFFFFF";
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
defConfig["modulesDirectory"] = "./modules";
|
||||||
|
defConfig["resourcesDirectory"] = "./res";
|
||||||
|
#else
|
||||||
|
defConfig["modulesDirectory"] = INSTALL_PREFIX "/lib/sdrpp/plugins";
|
||||||
|
defConfig["resourcesDirectory"] = INSTALL_PREFIX "/share/sdrpp";
|
||||||
|
#endif
|
||||||
|
|
||||||
// Load config
|
// Load config
|
||||||
spdlog::info("Loading config");
|
spdlog::info("Loading config");
|
||||||
core::configManager.setPath(options::opts.root + "/config.json");
|
core::configManager.setPath(options::opts.root + "/config.json");
|
||||||
core::configManager.load(defConfig);
|
core::configManager.load(defConfig);
|
||||||
core::configManager.enableAutoSave();
|
core::configManager.enableAutoSave();
|
||||||
|
|
||||||
|
|
||||||
|
core::configManager.acquire();
|
||||||
|
// Fix missing elements in config
|
||||||
|
for (auto const& item : defConfig.items()) {
|
||||||
|
if (!core::configManager.conf.contains(item.key())) {
|
||||||
|
spdlog::info("Missing key in config {0}, repairing", item.key());
|
||||||
|
core::configManager.conf[item.key()] = defConfig[item.key()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove unused elements
|
||||||
|
auto items = core::configManager.conf.items();
|
||||||
|
for (auto const& item : items) {
|
||||||
|
if (!defConfig.contains(item.key())) {
|
||||||
|
spdlog::info("Unused key in config {0}, repairing", item.key());
|
||||||
|
core::configManager.conf.erase(item.key());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update to new module representation in config if needed
|
||||||
|
for (auto [_name, inst] : core::configManager.conf["moduleInstances"].items()) {
|
||||||
|
if (!inst.is_string()) { continue; }
|
||||||
|
std::string mod = inst;
|
||||||
|
json newMod;
|
||||||
|
newMod["module"] = mod;
|
||||||
|
newMod["enabled"] = true;
|
||||||
|
core::configManager.conf["moduleInstances"][_name] = newMod;
|
||||||
|
}
|
||||||
|
|
||||||
|
core::configManager.release(true);
|
||||||
|
|
||||||
|
if (options::opts.serverMode) { return server_main(); }
|
||||||
|
|
||||||
// Setup window
|
// Setup window
|
||||||
glfwSetErrorCallback(glfw_error_callback);
|
glfwSetErrorCallback(glfw_error_callback);
|
||||||
if (!glfwInit()) {
|
if (!glfwInit()) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// GL 3.2 + GLSL 150
|
||||||
|
const char* glsl_version = "#version 150";
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
||||||
|
#else
|
||||||
|
// GL 3.0 + GLSL 120
|
||||||
|
const char* glsl_version = "#version 120";
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
int winWidth = core::configManager.conf["windowSize"]["w"];
|
int winWidth = core::configManager.conf["windowSize"]["w"];
|
||||||
int winHeight = core::configManager.conf["windowSize"]["h"];
|
int winHeight = core::configManager.conf["windowSize"]["h"];
|
||||||
maximized = core::configManager.conf["maximized"];
|
maximized = core::configManager.conf["maximized"];
|
||||||
|
std::string resDir = core::configManager.conf["resourcesDirectory"];
|
||||||
|
json bandColors = core::configManager.conf["bandColors"];
|
||||||
core::configManager.release();
|
core::configManager.release();
|
||||||
|
|
||||||
|
if (!std::filesystem::is_directory(resDir)) {
|
||||||
|
spdlog::error("Resource directory doesn't exist! Please make sure that you've configured it correctly in config.json (check readme for details)");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Create window with graphics context
|
// Create window with graphics context
|
||||||
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
||||||
GLFWwindow* window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);
|
core::window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);
|
||||||
if (window == NULL)
|
if (core::window == NULL)
|
||||||
return 1;
|
return 1;
|
||||||
glfwMakeContextCurrent(window);
|
glfwMakeContextCurrent(core::window);
|
||||||
|
|
||||||
#if (GLFW_VERSION_MAJOR == 3) && (GLFW_VERSION_MINOR >= 3)
|
#if (GLFW_VERSION_MAJOR == 3) && (GLFW_VERSION_MINOR >= 3)
|
||||||
if (maximized) {
|
if (maximized) {
|
||||||
glfwMaximizeWindow(window);
|
glfwMaximizeWindow(core::window);
|
||||||
}
|
}
|
||||||
|
|
||||||
glfwSetWindowMaximizeCallback(window, maximized_callback);
|
glfwSetWindowMaximizeCallback(core::window, maximized_callback);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Load app icon
|
// Load app icon
|
||||||
|
if (!std::filesystem::is_regular_file(resDir + "/icons/sdrpp.png")) {
|
||||||
|
spdlog::error("Icon file '{0}' doesn't exist!", resDir + "/icons/sdrpp.png");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
GLFWimage icons[10];
|
GLFWimage icons[10];
|
||||||
icons[0].pixels = stbi_load(((std::string)(options::opts.root + "/res/icons/sdrpp.png")).c_str(), &icons[0].width, &icons[0].height, 0, 4);
|
icons[0].pixels = stbi_load(((std::string)(resDir + "/icons/sdrpp.png")).c_str(), &icons[0].width, &icons[0].height, 0, 4);
|
||||||
icons[1].pixels = (unsigned char*)malloc(16 * 16 * 4); icons[1].width = icons[1].height = 16;
|
icons[1].pixels = (unsigned char*)malloc(16 * 16 * 4); icons[1].width = icons[1].height = 16;
|
||||||
icons[2].pixels = (unsigned char*)malloc(24 * 24 * 4); icons[2].width = icons[2].height = 24;
|
icons[2].pixels = (unsigned char*)malloc(24 * 24 * 4); icons[2].width = icons[2].height = 24;
|
||||||
icons[3].pixels = (unsigned char*)malloc(32 * 32 * 4); icons[3].width = icons[3].height = 32;
|
icons[3].pixels = (unsigned char*)malloc(32 * 32 * 4); icons[3].width = icons[3].height = 32;
|
||||||
@@ -176,7 +332,7 @@ int sdrpp_main(int argc, char *argv[]) {
|
|||||||
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[7].pixels, 128, 128, 128 * 4, 4);
|
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[7].pixels, 128, 128, 128 * 4, 4);
|
||||||
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[8].pixels, 196, 196, 196 * 4, 4);
|
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[8].pixels, 196, 196, 196 * 4, 4);
|
||||||
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[9].pixels, 256, 256, 256 * 4, 4);
|
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[9].pixels, 256, 256, 256 * 4, 4);
|
||||||
glfwSetWindowIcon(window, 10, icons);
|
glfwSetWindowIcon(core::window, 10, icons);
|
||||||
stbi_image_free(icons[0].pixels);
|
stbi_image_free(icons[0].pixels);
|
||||||
for (int i = 1; i < 10; i++) {
|
for (int i = 1; i < 10; i++) {
|
||||||
free(icons[i].pixels);
|
free(icons[i].pixels);
|
||||||
@@ -195,26 +351,35 @@ int sdrpp_main(int argc, char *argv[]) {
|
|||||||
io.IniFilename = NULL;
|
io.IniFilename = NULL;
|
||||||
|
|
||||||
// Setup Platform/Renderer bindings
|
// Setup Platform/Renderer bindings
|
||||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
ImGui_ImplGlfw_InitForOpenGL(core::window, true);
|
||||||
ImGui_ImplOpenGL3_Init("#version 150");
|
|
||||||
|
|
||||||
style::setDarkStyle();
|
if (!ImGui_ImplOpenGL3_Init(glsl_version)) {
|
||||||
|
// If init fail, try to fall back on GLSL 1.2
|
||||||
|
spdlog::warn("Could not init using OpenGL with normal GLSL version, falling back to GLSL 1.2");
|
||||||
|
if (!ImGui_ImplOpenGL3_Init("#version 120")) {
|
||||||
|
spdlog::error("Failed to initialize OpenGL with GLSL 1.2");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LoadingScreen::setWindow(window);
|
if (!style::loadFonts(resDir)) { return -1; }
|
||||||
|
thememenu::init(resDir);
|
||||||
|
|
||||||
|
LoadingScreen::setWindow(core::window);
|
||||||
|
|
||||||
LoadingScreen::show("Loading icons");
|
LoadingScreen::show("Loading icons");
|
||||||
spdlog::info("Loading icons");
|
spdlog::info("Loading icons");
|
||||||
icons::load();
|
if (!icons::load(resDir)) { return -1; }
|
||||||
|
|
||||||
LoadingScreen::show("Loading band plans");
|
LoadingScreen::show("Loading band plans");
|
||||||
spdlog::info("Loading band plans");
|
spdlog::info("Loading band plans");
|
||||||
bandplan::loadFromDir(options::opts.root + "/bandplans");
|
bandplan::loadFromDir(resDir + "/bandplans");
|
||||||
|
|
||||||
LoadingScreen::show("Loading band plan colors");
|
LoadingScreen::show("Loading band plan colors");
|
||||||
spdlog::info("Loading band plans color table");
|
spdlog::info("Loading band plans color table");
|
||||||
bandplan::loadColorTable(options::opts.root + "/band_colors.json");
|
bandplan::loadColorTable(bandColors);
|
||||||
|
|
||||||
windowInit();
|
gui::mainWindow.init();
|
||||||
|
|
||||||
spdlog::info("Ready.");
|
spdlog::info("Ready.");
|
||||||
|
|
||||||
@@ -222,7 +387,7 @@ int sdrpp_main(int argc, char *argv[]) {
|
|||||||
int fsWidth, fsHeight, fsPosX, fsPosY;
|
int fsWidth, fsHeight, fsPosX, fsPosY;
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
while (!glfwWindowShouldClose(window)) {
|
while (!glfwWindowShouldClose(core::window)) {
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
|
|
||||||
// Start the Dear ImGui frame
|
// Start the Dear ImGui frame
|
||||||
@@ -234,16 +399,16 @@ int sdrpp_main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
if (_maximized != maximized) {
|
if (_maximized != maximized) {
|
||||||
_maximized = maximized;
|
_maximized = maximized;
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["maximized"]= _maximized;
|
core::configManager.conf["maximized"]= _maximized;
|
||||||
if (!maximized) {
|
if (!maximized) {
|
||||||
glfwSetWindowSize(window, core::configManager.conf["windowSize"]["w"], core::configManager.conf["windowSize"]["h"]);
|
glfwSetWindowSize(core::window, core::configManager.conf["windowSize"]["w"], core::configManager.conf["windowSize"]["h"]);
|
||||||
}
|
}
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _winWidth, _winHeight;
|
int _winWidth, _winHeight;
|
||||||
glfwGetWindowSize(window, &_winWidth, &_winHeight);
|
glfwGetWindowSize(core::window, &_winWidth, &_winHeight);
|
||||||
|
|
||||||
if (ImGui::IsKeyPressed(GLFW_KEY_F11)) {
|
if (ImGui::IsKeyPressed(GLFW_KEY_F11)) {
|
||||||
fullScreen = !fullScreen;
|
fullScreen = !fullScreen;
|
||||||
@@ -251,20 +416,20 @@ int sdrpp_main(int argc, char *argv[]) {
|
|||||||
spdlog::info("Fullscreen: ON");
|
spdlog::info("Fullscreen: ON");
|
||||||
fsWidth = _winWidth;
|
fsWidth = _winWidth;
|
||||||
fsHeight = _winHeight;
|
fsHeight = _winHeight;
|
||||||
glfwGetWindowPos(window, &fsPosX, &fsPosY);
|
glfwGetWindowPos(core::window, &fsPosX, &fsPosY);
|
||||||
const GLFWvidmode * mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
const GLFWvidmode * mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, 0);
|
glfwSetWindowMonitor(core::window, monitor, 0, 0, mode->width, mode->height, 0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
spdlog::info("Fullscreen: OFF");
|
spdlog::info("Fullscreen: OFF");
|
||||||
glfwSetWindowMonitor(window, nullptr, fsPosX, fsPosY, fsWidth, fsHeight, 0);
|
glfwSetWindowMonitor(core::window, nullptr, fsPosX, fsPosY, fsWidth, fsHeight, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((_winWidth != winWidth || _winHeight != winHeight) && !maximized && _winWidth > 0 && _winHeight > 0) {
|
if ((_winWidth != winWidth || _winHeight != winHeight) && !maximized && _winWidth > 0 && _winHeight > 0) {
|
||||||
winWidth = _winWidth;
|
winWidth = _winWidth;
|
||||||
winHeight = _winHeight;
|
winHeight = _winHeight;
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["windowSize"]["w"] = winWidth;
|
core::configManager.conf["windowSize"]["w"] = winWidth;
|
||||||
core::configManager.conf["windowSize"]["h"] = winHeight;
|
core::configManager.conf["windowSize"]["h"] = winHeight;
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
@@ -273,21 +438,26 @@ int sdrpp_main(int argc, char *argv[]) {
|
|||||||
if (winWidth > 0 && winHeight > 0) {
|
if (winWidth > 0 && winHeight > 0) {
|
||||||
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
||||||
ImGui::SetNextWindowSize(ImVec2(_winWidth, _winHeight));
|
ImGui::SetNextWindowSize(ImVec2(_winWidth, _winHeight));
|
||||||
drawWindow();
|
gui::mainWindow.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rendering
|
// Rendering
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
int display_w, display_h;
|
int display_w, display_h;
|
||||||
glfwGetFramebufferSize(window, &display_w, &display_h);
|
glfwGetFramebufferSize(core::window, &display_w, &display_h);
|
||||||
glViewport(0, 0, display_w, display_h);
|
glViewport(0, 0, display_w, display_h);
|
||||||
glClearColor(0.0666f, 0.0666f, 0.0666f, 1.0f);
|
//glClearColor(0.0666f, 0.0666f, 0.0666f, 1.0f);
|
||||||
//glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
|
glClearColor(gui::themeManager.clearColor.x, gui::themeManager.clearColor.y, gui::themeManager.clearColor.z, gui::themeManager.clearColor.w);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
glfwSwapInterval(1); // Enable vsync
|
glfwSwapInterval(1); // Enable vsync
|
||||||
glfwSwapBuffers(window);
|
glfwSwapBuffers(core::window);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shut down all modules
|
||||||
|
for (auto& [name, mod] : core::moduleManager.modules) {
|
||||||
|
mod.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
@@ -295,8 +465,13 @@ int sdrpp_main(int argc, char *argv[]) {
|
|||||||
ImGui_ImplGlfw_Shutdown();
|
ImGui_ImplGlfw_Shutdown();
|
||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext();
|
||||||
|
|
||||||
glfwDestroyWindow(window);
|
glfwDestroyWindow(core::window);
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
|
|
||||||
|
sigpath::signalPath.stop();
|
||||||
|
|
||||||
|
core::configManager.disableAutoSave();
|
||||||
|
core::configManager.save();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <new_module.h>
|
#include <module.h>
|
||||||
#include <scripting.h>
|
#include <module.h>
|
||||||
#include <new_module.h>
|
#include <module_com.h>
|
||||||
|
|
||||||
namespace core {
|
namespace core {
|
||||||
SDRPP_EXPORT ConfigManager configManager;
|
SDRPP_EXPORT ConfigManager configManager;
|
||||||
SDRPP_EXPORT ScriptManager scriptManager;
|
|
||||||
SDRPP_EXPORT ModuleManager moduleManager;
|
SDRPP_EXPORT ModuleManager moduleManager;
|
||||||
|
SDRPP_EXPORT ModuleComManager modComManager;
|
||||||
|
|
||||||
void setInputSampleRate(double samplerate);
|
void setInputSampleRate(double samplerate);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,26 +2,47 @@
|
|||||||
|
|
||||||
namespace sdrpp_credits {
|
namespace sdrpp_credits {
|
||||||
const char* contributors[] = {
|
const char* contributors[] = {
|
||||||
"Ryzerth (Author)",
|
"Aang23",
|
||||||
"aosync",
|
|
||||||
"Alexsey Shestacov",
|
"Alexsey Shestacov",
|
||||||
|
"Aosync",
|
||||||
"Benjamin Kyd",
|
"Benjamin Kyd",
|
||||||
"Tobias Mädel",
|
"Benjamin Vernoux",
|
||||||
|
"Cropinghigh",
|
||||||
|
"Fred F4EED",
|
||||||
|
"Howard0su",
|
||||||
|
"Joshua Kimsey",
|
||||||
|
"Martin Hauke",
|
||||||
|
"Marvin Sinister",
|
||||||
|
"Maxime Biette",
|
||||||
|
"Paulo Matias",
|
||||||
"Raov",
|
"Raov",
|
||||||
"Howard0su"
|
"Cam K.",
|
||||||
|
"Szymon Zakrent",
|
||||||
|
"Tobias Mädel",
|
||||||
|
"Zimm"
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* libraries[] = {
|
const char* libraries[] = {
|
||||||
"Dear ImGui (ocornut)",
|
"Dear ImGui (ocornut)",
|
||||||
|
"fftw3 (fftw.org)",
|
||||||
|
"glew (Nigel Stewart)",
|
||||||
|
"glfw (Camilla Löwy)",
|
||||||
"json (nlohmann)",
|
"json (nlohmann)",
|
||||||
"portaudio (P.A. comm.)",
|
|
||||||
"SoapySDR (PothosWare)",
|
|
||||||
"spdlog (gabime)",
|
"spdlog (gabime)",
|
||||||
|
"Portable File Dialogs"
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* patrons[] = {
|
const char* patrons[] = {
|
||||||
|
"Croccydile",
|
||||||
|
"Daniele D'Agnelli",
|
||||||
|
"Eric Johnson",
|
||||||
|
"W4IPA",
|
||||||
|
"Lee Donaghy",
|
||||||
|
"ON4MU",
|
||||||
|
"Passion-Radio.com",
|
||||||
|
"Scanner School",
|
||||||
"SignalsEverywhere",
|
"SignalsEverywhere",
|
||||||
"Lee Donaghy"
|
"Syne Ardwin (WI9SYN)"
|
||||||
};
|
};
|
||||||
|
|
||||||
const int contributorCount = sizeof(contributors) / sizeof(char*);
|
const int contributorCount = sizeof(contributors) / sizeof(char*);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <new_module.h>
|
#include <module.h>
|
||||||
|
|
||||||
namespace sdrpp_credits {
|
namespace sdrpp_credits {
|
||||||
SDRPP_EXPORT const char* contributors[];
|
SDRPP_EXPORT const char* contributors[];
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ namespace dsp {
|
|||||||
|
|
||||||
MonoToStereo(stream<float>* in) { init(in); }
|
MonoToStereo(stream<float>* in) { init(in); }
|
||||||
|
|
||||||
~MonoToStereo() { generic_block<MonoToStereo>::stop(); }
|
|
||||||
|
|
||||||
void init(stream<float>* in) {
|
void init(stream<float>* in) {
|
||||||
_in = in;
|
_in = in;
|
||||||
generic_block<MonoToStereo>::registerInput(_in);
|
generic_block<MonoToStereo>::registerInput(_in);
|
||||||
generic_block<MonoToStereo>::registerOutput(&out);
|
generic_block<MonoToStereo>::registerOutput(&out);
|
||||||
|
generic_block<MonoToStereo>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<float>* in) {
|
void setInput(stream<float>* in) {
|
||||||
|
assert(generic_block<MonoToStereo>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<MonoToStereo>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<MonoToStereo>::ctrlMtx);
|
||||||
generic_block<MonoToStereo>::tempStop();
|
generic_block<MonoToStereo>::tempStop();
|
||||||
generic_block<MonoToStereo>::unregisterInput(_in);
|
generic_block<MonoToStereo>::unregisterInput(_in);
|
||||||
@@ -26,43 +26,106 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
volk_32f_x2_interleave_32fc((lv_32fc_t*)out.writeBuf, _in->readBuf, _in->readBuf, count);
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
out.data[i].l = _in->data[i];
|
|
||||||
out.data[i].r = _in->data[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<stereo_t> out;
|
stream<stereo_t> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
|
||||||
stream<float>* _in;
|
stream<float>* _in;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ChannelsToStereo : public generic_block<ChannelsToStereo> {
|
||||||
|
public:
|
||||||
|
ChannelsToStereo() {}
|
||||||
|
|
||||||
|
ChannelsToStereo(stream<float>* in_left, stream<float>* in_right) { init(in_left, in_right); }
|
||||||
|
|
||||||
|
void init(stream<float>* in_left, stream<float>* in_right) {
|
||||||
|
_in_left = in_left;
|
||||||
|
_in_right = in_right;
|
||||||
|
nullbuf = new float[STREAM_BUFFER_SIZE];
|
||||||
|
for (int i = 0; i < STREAM_BUFFER_SIZE; i++) { nullbuf[i] = 0; }
|
||||||
|
generic_block<ChannelsToStereo>::registerInput(_in_left);
|
||||||
|
generic_block<ChannelsToStereo>::registerInput(_in_right);
|
||||||
|
generic_block<ChannelsToStereo>::registerOutput(&out);
|
||||||
|
generic_block<ChannelsToStereo>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<float>* in_left, stream<float>* in_right) {
|
||||||
|
assert(generic_block<ChannelsToStereo>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<ChannelsToStereo>::ctrlMtx);
|
||||||
|
generic_block<ChannelsToStereo>::tempStop();
|
||||||
|
generic_block<ChannelsToStereo>::unregisterInput(_in_left);
|
||||||
|
generic_block<ChannelsToStereo>::unregisterInput(_in_right);
|
||||||
|
_in_left = in_left;
|
||||||
|
_in_right = in_right;
|
||||||
|
generic_block<ChannelsToStereo>::registerInput(_in_left);
|
||||||
|
generic_block<ChannelsToStereo>::registerInput(_in_right);
|
||||||
|
generic_block<ChannelsToStereo>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count_l = _in_left->read();
|
||||||
|
if (count_l < 0) { return -1; }
|
||||||
|
int count_r = _in_right->read();
|
||||||
|
if (count_r < 0) { return -1; }
|
||||||
|
|
||||||
|
if (count_l != count_r) {
|
||||||
|
spdlog::warn("ChannelsToStereo block size missmatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
volk_32f_x2_interleave_32fc((lv_32fc_t*)out.writeBuf, _in_left->readBuf, _in_right->readBuf, count_l);
|
||||||
|
|
||||||
|
_in_left->flush();
|
||||||
|
_in_right->flush();
|
||||||
|
if (!out.swap(count_l)) { return -1; }
|
||||||
|
return count_l;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<stereo_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<float>* _in_left;
|
||||||
|
stream<float>* _in_right;
|
||||||
|
|
||||||
|
float* nullbuf;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class StereoToMono : public generic_block<StereoToMono> {
|
class StereoToMono : public generic_block<StereoToMono> {
|
||||||
public:
|
public:
|
||||||
StereoToMono() {}
|
StereoToMono() {}
|
||||||
|
|
||||||
StereoToMono(stream<stereo_t>* in) { init(in); }
|
StereoToMono(stream<stereo_t>* in) { init(in); }
|
||||||
|
|
||||||
~StereoToMono() { generic_block<StereoToMono>::stop(); }
|
~StereoToMono() {
|
||||||
|
if (!generic_block<StereoToMono>::_block_init) { return; }
|
||||||
|
generic_block<StereoToMono>::stop();
|
||||||
|
delete[] l_buf;
|
||||||
|
delete[] r_buf;
|
||||||
|
generic_block<StereoToMono>::_block_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
void init(stream<stereo_t>* in) {
|
void init(stream<stereo_t>* in) {
|
||||||
_in = in;
|
_in = in;
|
||||||
|
l_buf = new float[STREAM_BUFFER_SIZE];
|
||||||
|
r_buf = new float[STREAM_BUFFER_SIZE];
|
||||||
generic_block<StereoToMono>::registerInput(_in);
|
generic_block<StereoToMono>::registerInput(_in);
|
||||||
generic_block<StereoToMono>::registerOutput(&out);
|
generic_block<StereoToMono>::registerOutput(&out);
|
||||||
|
generic_block<StereoToMono>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<stereo_t>* in) {
|
void setInput(stream<stereo_t>* in) {
|
||||||
|
assert(generic_block<StereoToMono>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<StereoToMono>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<StereoToMono>::ctrlMtx);
|
||||||
generic_block<StereoToMono>::tempStop();
|
generic_block<StereoToMono>::tempStop();
|
||||||
generic_block<StereoToMono>::unregisterInput(_in);
|
generic_block<StereoToMono>::unregisterInput(_in);
|
||||||
@@ -72,23 +135,67 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
out.data[i] = (_in->data[i].l + _in->data[i].r) / 2.0f;
|
out.writeBuf[i] = (_in->readBuf[i].l + _in->readBuf[i].r) * 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<float> out;
|
stream<float> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
float* l_buf, *r_buf;
|
||||||
|
stream<stereo_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class StereoToChannels : public generic_block<StereoToChannels> {
|
||||||
|
public:
|
||||||
|
StereoToChannels() {}
|
||||||
|
|
||||||
|
StereoToChannels(stream<stereo_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<stereo_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
generic_block<StereoToChannels>::registerInput(_in);
|
||||||
|
generic_block<StereoToChannels>::registerOutput(&out_left);
|
||||||
|
generic_block<StereoToChannels>::registerOutput(&out_right);
|
||||||
|
generic_block<StereoToChannels>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<stereo_t>* in) {
|
||||||
|
assert(generic_block<StereoToChannels>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<StereoToChannels>::ctrlMtx);
|
||||||
|
generic_block<StereoToChannels>::tempStop();
|
||||||
|
generic_block<StereoToChannels>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<StereoToChannels>::registerInput(_in);
|
||||||
|
generic_block<StereoToChannels>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
volk_32fc_deinterleave_32f_x2(out_left.writeBuf, out_right.writeBuf, (lv_32fc_t*)_in->readBuf, count);
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out_left.swap(count)) { return -1; }
|
||||||
|
if (!out_right.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<float> out_left;
|
||||||
|
stream<float> out_right;
|
||||||
|
|
||||||
|
private:
|
||||||
stream<stereo_t>* _in;
|
stream<stereo_t>* _in;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,20 +6,31 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#define FL_M_PI 3.1415926535f
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
|
|
||||||
|
class generic_unnamed_block {
|
||||||
|
public:
|
||||||
|
virtual void start() {}
|
||||||
|
virtual void stop() {}
|
||||||
|
virtual int calcOutSize(int inSize) { return inSize; }
|
||||||
|
virtual int run() { return -1; }
|
||||||
|
};
|
||||||
|
|
||||||
template <class BLOCK>
|
template <class BLOCK>
|
||||||
class generic_block {
|
class generic_block : public generic_unnamed_block {
|
||||||
public:
|
public:
|
||||||
virtual void init() {}
|
virtual void init() {}
|
||||||
|
|
||||||
virtual ~generic_block() {
|
virtual ~generic_block() {
|
||||||
|
if (!_block_init) { return; }
|
||||||
stop();
|
stop();
|
||||||
|
_block_init = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void start() {
|
virtual void start() {
|
||||||
|
assert(_block_init);
|
||||||
std::lock_guard<std::mutex> lck(ctrlMtx);
|
std::lock_guard<std::mutex> lck(ctrlMtx);
|
||||||
if (running) {
|
if (running) {
|
||||||
return;
|
return;
|
||||||
@@ -29,6 +40,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void stop() {
|
virtual void stop() {
|
||||||
|
assert(_block_init);
|
||||||
std::lock_guard<std::mutex> lck(ctrlMtx);
|
std::lock_guard<std::mutex> lck(ctrlMtx);
|
||||||
if (!running) {
|
if (!running) {
|
||||||
return;
|
return;
|
||||||
@@ -37,7 +49,26 @@ namespace dsp {
|
|||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int calcOutSize(int inSize) { return inSize; }
|
void tempStart() {
|
||||||
|
assert(_block_init);
|
||||||
|
if (tempStopped) {
|
||||||
|
doStart();
|
||||||
|
tempStopped = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tempStop() {
|
||||||
|
assert(_block_init);
|
||||||
|
if (running && !tempStopped) {
|
||||||
|
doStop();
|
||||||
|
tempStopped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int calcOutSize(int inSize) {
|
||||||
|
assert(_block_init);
|
||||||
|
return inSize;
|
||||||
|
}
|
||||||
|
|
||||||
virtual int run() = 0;
|
virtual int run() = 0;
|
||||||
|
|
||||||
@@ -48,7 +79,7 @@ namespace dsp {
|
|||||||
while (run() >= 0);
|
while (run() >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void aquire() {
|
void acquire() {
|
||||||
ctrlMtx.lock();
|
ctrlMtx.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,19 +87,19 @@ namespace dsp {
|
|||||||
ctrlMtx.unlock();
|
ctrlMtx.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerInput(untyped_steam* inStream) {
|
void registerInput(untyped_stream* inStream) {
|
||||||
inputs.push_back(inStream);
|
inputs.push_back(inStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unregisterInput(untyped_steam* inStream) {
|
void unregisterInput(untyped_stream* inStream) {
|
||||||
inputs.erase(std::remove(inputs.begin(), inputs.end(), inStream), inputs.end());
|
inputs.erase(std::remove(inputs.begin(), inputs.end(), inStream), inputs.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerOutput(untyped_steam* outStream) {
|
void registerOutput(untyped_stream* outStream) {
|
||||||
outputs.push_back(outStream);
|
outputs.push_back(outStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unregisterOutput(untyped_steam* outStream) {
|
void unregisterOutput(untyped_stream* outStream) {
|
||||||
outputs.erase(std::remove(outputs.begin(), outputs.end(), outStream), outputs.end());
|
outputs.erase(std::remove(outputs.begin(), outputs.end(), outStream), outputs.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,10 +108,10 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void doStop() {
|
virtual void doStop() {
|
||||||
for (auto const& in : inputs) {
|
for (auto& in : inputs) {
|
||||||
in->stopReader();
|
in->stopReader();
|
||||||
}
|
}
|
||||||
for (auto const& out : outputs) {
|
for (auto& out : outputs) {
|
||||||
out->stopWriter();
|
out->stopWriter();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,15 +120,61 @@ namespace dsp {
|
|||||||
workerThread.join();
|
workerThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& in : inputs) {
|
for (auto& in : inputs) {
|
||||||
in->clearReadStop();
|
in->clearReadStop();
|
||||||
}
|
}
|
||||||
for (auto const& out : outputs) {
|
for (auto& out : outputs) {
|
||||||
out->clearWriteStop();
|
out->clearWriteStop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool _block_init = false;
|
||||||
|
|
||||||
|
std::mutex ctrlMtx;
|
||||||
|
|
||||||
|
std::vector<untyped_stream*> inputs;
|
||||||
|
std::vector<untyped_stream*> outputs;
|
||||||
|
|
||||||
|
bool running = false;
|
||||||
|
bool tempStopped = false;
|
||||||
|
std::thread workerThread;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class BLOCK>
|
||||||
|
class generic_hier_block {
|
||||||
|
public:
|
||||||
|
virtual void init() {}
|
||||||
|
|
||||||
|
virtual ~generic_hier_block() {
|
||||||
|
if (!_block_init) { return; }
|
||||||
|
stop();
|
||||||
|
_block_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void start() {
|
||||||
|
assert(_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(ctrlMtx);
|
||||||
|
if (running) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
running = true;
|
||||||
|
doStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void stop() {
|
||||||
|
assert(_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(ctrlMtx);
|
||||||
|
if (!running) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
doStop();
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
void tempStart() {
|
void tempStart() {
|
||||||
|
assert(_block_init);
|
||||||
if (tempStopped) {
|
if (tempStopped) {
|
||||||
doStart();
|
doStart();
|
||||||
tempStopped = false;
|
tempStopped = false;
|
||||||
@@ -105,21 +182,47 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void tempStop() {
|
void tempStop() {
|
||||||
|
assert(_block_init);
|
||||||
if (running && !tempStopped) {
|
if (running && !tempStopped) {
|
||||||
doStop();
|
doStop();
|
||||||
tempStopped = true;
|
tempStopped = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<untyped_steam*> inputs;
|
virtual int calcOutSize(int inSize) {
|
||||||
std::vector<untyped_steam*> outputs;
|
assert(_block_init);
|
||||||
|
return inSize;
|
||||||
|
}
|
||||||
|
|
||||||
bool running = false;
|
friend BLOCK;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void registerBlock(generic_unnamed_block* block) {
|
||||||
|
blocks.push_back(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregisterBlock(generic_unnamed_block* block) {
|
||||||
|
blocks.erase(std::remove(blocks.begin(), blocks.end(), block), blocks.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void doStart() {
|
||||||
|
for (auto& block : blocks) {
|
||||||
|
block->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void doStop() {
|
||||||
|
for (auto& block : blocks) {
|
||||||
|
block->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<generic_unnamed_block*> blocks;
|
||||||
bool tempStopped = false;
|
bool tempStopped = false;
|
||||||
|
bool running = false;
|
||||||
std::thread workerThread;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool _block_init = false;
|
||||||
std::mutex ctrlMtx;
|
std::mutex ctrlMtx;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <dsp/block.h>
|
#include <dsp/block.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#define RING_BUF_SZ 1000000
|
#define RING_BUF_SZ 1000000
|
||||||
|
|
||||||
@@ -7,13 +8,15 @@ namespace dsp {
|
|||||||
template <class T>
|
template <class T>
|
||||||
class RingBuffer {
|
class RingBuffer {
|
||||||
public:
|
public:
|
||||||
RingBuffer() {
|
RingBuffer() {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
RingBuffer(int maxLatency) { init(maxLatency); }
|
RingBuffer(int maxLatency) { init(maxLatency); }
|
||||||
|
|
||||||
~RingBuffer() { delete _buffer; }
|
~RingBuffer() {
|
||||||
|
if (!_init) { return; }
|
||||||
|
delete _buffer;
|
||||||
|
_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
void init(int maxLatency) {
|
void init(int maxLatency) {
|
||||||
size = RING_BUF_SZ;
|
size = RING_BUF_SZ;
|
||||||
@@ -26,9 +29,11 @@ namespace dsp {
|
|||||||
readable = 0;
|
readable = 0;
|
||||||
writable = size;
|
writable = size;
|
||||||
memset(_buffer, 0, size * sizeof(T));
|
memset(_buffer, 0, size * sizeof(T));
|
||||||
|
_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int read(T* data, int len) {
|
int read(T* data, int len) {
|
||||||
|
assert(_init);
|
||||||
int dataRead = 0;
|
int dataRead = 0;
|
||||||
int toRead = 0;
|
int toRead = 0;
|
||||||
while (dataRead < len) {
|
while (dataRead < len) {
|
||||||
@@ -58,6 +63,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int readAndSkip(T* data, int len, int skip) {
|
int readAndSkip(T* data, int len, int skip) {
|
||||||
|
assert(_init);
|
||||||
int dataRead = 0;
|
int dataRead = 0;
|
||||||
int toRead = 0;
|
int toRead = 0;
|
||||||
while (dataRead < len) {
|
while (dataRead < len) {
|
||||||
@@ -103,6 +109,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int waitUntilReadable() {
|
int waitUntilReadable() {
|
||||||
|
assert(_init);
|
||||||
if (_stopReader) { return -1; }
|
if (_stopReader) { return -1; }
|
||||||
int _r = getReadable();
|
int _r = getReadable();
|
||||||
if (_r != 0) { return _r; }
|
if (_r != 0) { return _r; }
|
||||||
@@ -113,6 +120,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int getReadable(bool lock = true) {
|
int getReadable(bool lock = true) {
|
||||||
|
assert(_init);
|
||||||
if (lock) { _readable_mtx.lock(); };
|
if (lock) { _readable_mtx.lock(); };
|
||||||
int _r = readable;
|
int _r = readable;
|
||||||
if (lock) { _readable_mtx.unlock(); };
|
if (lock) { _readable_mtx.unlock(); };
|
||||||
@@ -120,6 +128,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int write(T* data, int len) {
|
int write(T* data, int len) {
|
||||||
|
assert(_init);
|
||||||
int dataWritten = 0;
|
int dataWritten = 0;
|
||||||
int toWrite = 0;
|
int toWrite = 0;
|
||||||
while (dataWritten < len) {
|
while (dataWritten < len) {
|
||||||
@@ -150,6 +159,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int waitUntilwritable() {
|
int waitUntilwritable() {
|
||||||
|
assert(_init);
|
||||||
if (_stopWriter) { return -1; }
|
if (_stopWriter) { return -1; }
|
||||||
int _w = getWritable();
|
int _w = getWritable();
|
||||||
if (_w != 0) { return _w; }
|
if (_w != 0) { return _w; }
|
||||||
@@ -160,6 +170,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int getWritable(bool lock = true) {
|
int getWritable(bool lock = true) {
|
||||||
|
assert(_init);
|
||||||
if (lock) { _writable_mtx.lock(); };
|
if (lock) { _writable_mtx.lock(); };
|
||||||
int _w = writable;
|
int _w = writable;
|
||||||
if (lock) { _writable_mtx.unlock(); _readable_mtx.lock(); };
|
if (lock) { _writable_mtx.unlock(); _readable_mtx.lock(); };
|
||||||
@@ -169,36 +180,44 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void stopReader() {
|
void stopReader() {
|
||||||
|
assert(_init);
|
||||||
_stopReader = true;
|
_stopReader = true;
|
||||||
canReadVar.notify_one();
|
canReadVar.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopWriter() {
|
void stopWriter() {
|
||||||
|
assert(_init);
|
||||||
_stopWriter = true;
|
_stopWriter = true;
|
||||||
canWriteVar.notify_one();
|
canWriteVar.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getReadStop() {
|
bool getReadStop() {
|
||||||
|
assert(_init);
|
||||||
return _stopReader;
|
return _stopReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getWriteStop() {
|
bool getWriteStop() {
|
||||||
|
assert(_init);
|
||||||
return _stopWriter;
|
return _stopWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearReadStop() {
|
void clearReadStop() {
|
||||||
|
assert(_init);
|
||||||
_stopReader = false;
|
_stopReader = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearWriteStop() {
|
void clearWriteStop() {
|
||||||
|
assert(_init);
|
||||||
_stopWriter = false;
|
_stopWriter = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMaxLatency(int maxLatency) {
|
void setMaxLatency(int maxLatency) {
|
||||||
|
assert(_init);
|
||||||
this->maxLatency = maxLatency;
|
this->maxLatency = maxLatency;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool _init = false;
|
||||||
T* _buffer;
|
T* _buffer;
|
||||||
int size;
|
int size;
|
||||||
int readc;
|
int readc;
|
||||||
@@ -213,4 +232,128 @@ namespace dsp {
|
|||||||
std::condition_variable canReadVar;
|
std::condition_variable canReadVar;
|
||||||
std::condition_variable canWriteVar;
|
std::condition_variable canWriteVar;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define TEST_BUFFER_SIZE 32
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class SampleFrameBuffer : public generic_block<SampleFrameBuffer<T>> {
|
||||||
|
public:
|
||||||
|
SampleFrameBuffer() {}
|
||||||
|
|
||||||
|
SampleFrameBuffer(stream<T>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<T>* in) {
|
||||||
|
_in = in;
|
||||||
|
|
||||||
|
for (int i = 0; i < TEST_BUFFER_SIZE; i++) {
|
||||||
|
buffers[i] = new T[STREAM_BUFFER_SIZE];
|
||||||
|
}
|
||||||
|
|
||||||
|
generic_block<SampleFrameBuffer<T>>::registerInput(in);
|
||||||
|
generic_block<SampleFrameBuffer<T>>::registerOutput(&out);
|
||||||
|
generic_block<SampleFrameBuffer<T>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<SampleFrameBuffer<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<SampleFrameBuffer<T>>::ctrlMtx);
|
||||||
|
generic_block<SampleFrameBuffer<T>>::tempStop();
|
||||||
|
generic_block<SampleFrameBuffer<T>>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<SampleFrameBuffer<T>>::registerInput(_in);
|
||||||
|
generic_block<SampleFrameBuffer<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush() {
|
||||||
|
std::unique_lock lck(bufMtx);
|
||||||
|
readCur = writeCur;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
// Wait for data
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
if (bypass) {
|
||||||
|
memcpy(out.writeBuf, _in->readBuf, count * sizeof(T));
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push it on the ring buffer
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lck(bufMtx);
|
||||||
|
memcpy(buffers[writeCur], _in->readBuf, count * sizeof(T));
|
||||||
|
uintptr_t ptr = (uintptr_t)buffers[writeCur];
|
||||||
|
sizes[writeCur] = count;
|
||||||
|
writeCur++;
|
||||||
|
writeCur = ((writeCur) % TEST_BUFFER_SIZE);
|
||||||
|
|
||||||
|
// if (((writeCur - readCur + TEST_BUFFER_SIZE) % TEST_BUFFER_SIZE) >= (TEST_BUFFER_SIZE-2)) {
|
||||||
|
// spdlog::warn("Overflow");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
cnd.notify_all();
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker() {
|
||||||
|
while (true) {
|
||||||
|
// Wait for data
|
||||||
|
std::unique_lock lck(bufMtx);
|
||||||
|
cnd.wait(lck, [this](){ return (((writeCur - readCur + TEST_BUFFER_SIZE) % TEST_BUFFER_SIZE) > 0) || stopWorker; });
|
||||||
|
if (stopWorker) { break; }
|
||||||
|
|
||||||
|
// Write one to output buffer and unlock in preparation to swap buffers
|
||||||
|
int count = sizes[readCur];
|
||||||
|
memcpy(out.writeBuf, buffers[readCur], count * sizeof(T));
|
||||||
|
readCur++;
|
||||||
|
readCur = ((readCur) % TEST_BUFFER_SIZE);
|
||||||
|
lck.unlock();
|
||||||
|
|
||||||
|
// Swap
|
||||||
|
if (!out.swap(count)) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<T> out;
|
||||||
|
|
||||||
|
int writeCur = 0;
|
||||||
|
int readCur = 0;
|
||||||
|
|
||||||
|
bool bypass = false;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void doStart() {
|
||||||
|
generic_block<SampleFrameBuffer<T>>::workerThread = std::thread(&generic_block<SampleFrameBuffer<T>>::workerLoop, this);
|
||||||
|
readWorkerThread = std::thread(&SampleFrameBuffer<T>::worker, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void doStop() {
|
||||||
|
_in->stopReader();
|
||||||
|
out.stopWriter();
|
||||||
|
stopWorker = true;
|
||||||
|
cnd.notify_all();
|
||||||
|
|
||||||
|
if (generic_block<SampleFrameBuffer<T>>::workerThread.joinable()) { generic_block<SampleFrameBuffer<T>>::workerThread.join(); }
|
||||||
|
if (readWorkerThread.joinable()) { readWorkerThread.join(); }
|
||||||
|
|
||||||
|
_in->clearReadStop();
|
||||||
|
out.clearWriteStop();
|
||||||
|
stopWorker = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<T>* _in;
|
||||||
|
|
||||||
|
std::thread readWorkerThread;
|
||||||
|
std::mutex bufMtx;
|
||||||
|
std::condition_variable cnd;
|
||||||
|
T* buffers[TEST_BUFFER_SIZE];
|
||||||
|
int sizes[TEST_BUFFER_SIZE];
|
||||||
|
|
||||||
|
bool stopWorker = false;
|
||||||
|
|
||||||
|
};
|
||||||
};
|
};
|
||||||
255
core/src/dsp/clock_recovery.h
Normal file
255
core/src/dsp/clock_recovery.h
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <dsp/utils/macros.h>
|
||||||
|
#include <dsp/interpolation_taps.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
class EdgeTrigClockRecovery : public generic_block<EdgeTrigClockRecovery> {
|
||||||
|
public:
|
||||||
|
EdgeTrigClockRecovery() {}
|
||||||
|
|
||||||
|
EdgeTrigClockRecovery(stream<float>* in, int omega) { init(in, omega); }
|
||||||
|
|
||||||
|
void init(stream<float>* in, int omega) {
|
||||||
|
_in = in;
|
||||||
|
samplesPerSymbol = omega;
|
||||||
|
generic_block<EdgeTrigClockRecovery>::registerInput(_in);
|
||||||
|
generic_block<EdgeTrigClockRecovery>::registerOutput(&out);
|
||||||
|
generic_block<EdgeTrigClockRecovery>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<float>* in) {
|
||||||
|
assert(generic_block<EdgeTrigClockRecovery>::_block_init);
|
||||||
|
generic_block<EdgeTrigClockRecovery>::tempStop();
|
||||||
|
generic_block<EdgeTrigClockRecovery>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<EdgeTrigClockRecovery>::registerInput(_in);
|
||||||
|
generic_block<EdgeTrigClockRecovery>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
int outCount = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (DSP_SIGN(lastVal) != DSP_SIGN(_in->readBuf[i])) {
|
||||||
|
counter = samplesPerSymbol / 2;
|
||||||
|
lastVal = _in->readBuf[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter >= samplesPerSymbol) {
|
||||||
|
counter = 0;
|
||||||
|
out.writeBuf[outCount] = _in->readBuf[i];
|
||||||
|
outCount++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastVal = _in->readBuf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (outCount > 0 && !out.swap(outCount)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<float> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int count;
|
||||||
|
int samplesPerSymbol = 1;
|
||||||
|
int counter = 0;
|
||||||
|
float lastVal = 0;
|
||||||
|
stream<float>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class MMClockRecovery : public generic_block<MMClockRecovery<T>> {
|
||||||
|
public:
|
||||||
|
MMClockRecovery() {}
|
||||||
|
|
||||||
|
MMClockRecovery(stream<T>* in, float omega, float gainOmega, float muGain, float omegaRelLimit) {
|
||||||
|
init(in, omega, gainOmega, muGain, omegaRelLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<T>* in, float omega, float gainOmega, float muGain, float omegaRelLimit) {
|
||||||
|
_in = in;
|
||||||
|
_omega = omega;
|
||||||
|
_muGain = muGain;
|
||||||
|
_gainOmega = gainOmega;
|
||||||
|
_omegaRelLimit = omegaRelLimit;
|
||||||
|
|
||||||
|
omegaMin = _omega - (_omega * _omegaRelLimit);
|
||||||
|
omegaMax = _omega + (_omega * _omegaRelLimit);
|
||||||
|
_dynOmega = _omega;
|
||||||
|
|
||||||
|
memset(delay, 0, 1024 * sizeof(T));
|
||||||
|
|
||||||
|
generic_block<MMClockRecovery<T>>::registerInput(_in);
|
||||||
|
generic_block<MMClockRecovery<T>>::registerOutput(&out);
|
||||||
|
generic_block<MMClockRecovery<T>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOmega(float omega, float omegaRelLimit) {
|
||||||
|
assert(generic_block<MMClockRecovery<T>>::_block_init);
|
||||||
|
generic_block<MMClockRecovery<T>>::tempStop();
|
||||||
|
omegaMin = _omega - (_omega * _omegaRelLimit);
|
||||||
|
omegaMax = _omega + (_omega * _omegaRelLimit);
|
||||||
|
_omega = omega;
|
||||||
|
_dynOmega = _omega;
|
||||||
|
generic_block<MMClockRecovery<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setGains(float omegaGain, float muGain) {
|
||||||
|
assert(generic_block<MMClockRecovery<T>>::_block_init);
|
||||||
|
generic_block<MMClockRecovery<T>>::tempStop();
|
||||||
|
_gainOmega = omegaGain;
|
||||||
|
_muGain = muGain;
|
||||||
|
generic_block<MMClockRecovery<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOmegaRelLimit(float omegaRelLimit) {
|
||||||
|
assert(generic_block<MMClockRecovery<T>>::_block_init);
|
||||||
|
generic_block<MMClockRecovery<T>>::tempStop();
|
||||||
|
_omegaRelLimit = omegaRelLimit;
|
||||||
|
omegaMin = _omega - (_omega * _omegaRelLimit);
|
||||||
|
omegaMax = _omega + (_omega * _omegaRelLimit);
|
||||||
|
generic_block<MMClockRecovery<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<MMClockRecovery<T>>::_block_init);
|
||||||
|
generic_block<MMClockRecovery<T>>::tempStop();
|
||||||
|
generic_block<MMClockRecovery<T>>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<MMClockRecovery<T>>::registerInput(_in);
|
||||||
|
generic_block<MMClockRecovery<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
int outCount = 0;
|
||||||
|
float outVal;
|
||||||
|
float phaseError;
|
||||||
|
float roundedStep;
|
||||||
|
int maxOut = 2.0f * _omega * (float)count;
|
||||||
|
|
||||||
|
// Copy the first 7 values to the delay buffer for fast computing
|
||||||
|
memcpy(&delay[7], _in->readBuf, 7 * sizeof(T));
|
||||||
|
|
||||||
|
int i = nextOffset;
|
||||||
|
for (; i < count && outCount < maxOut;) {
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
|
// Calculate output value
|
||||||
|
// If we still need to use the old values, calculate using delay buf
|
||||||
|
// Otherwise, use normal buffer
|
||||||
|
if (i < 7) {
|
||||||
|
volk_32f_x2_dot_prod_32f(&outVal, &delay[i], INTERP_TAPS[(int)roundf(_mu * 128.0f)], 8);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
volk_32f_x2_dot_prod_32f(&outVal, &_in->readBuf[i - 7], INTERP_TAPS[(int)roundf(_mu * 128.0f)], 8);
|
||||||
|
}
|
||||||
|
out.writeBuf[outCount++] = outVal;
|
||||||
|
|
||||||
|
// Cursed phase detect approximation (don't ask me how this approximation works)
|
||||||
|
phaseError = (DSP_STEP(lastOutput)*outVal) - (lastOutput*DSP_STEP(outVal));
|
||||||
|
lastOutput = outVal;
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<T, complex_t> || std::is_same_v<T, stereo_t>) {
|
||||||
|
// Propagate delay
|
||||||
|
_p_2T = _p_1T;
|
||||||
|
_p_1T = _p_0T;
|
||||||
|
|
||||||
|
_c_2T = _c_1T;
|
||||||
|
_c_1T = _c_0T;
|
||||||
|
|
||||||
|
// Perfrom interpolation the same way as for float values
|
||||||
|
if (i < 7) {
|
||||||
|
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&_p_0T, (lv_32fc_t*)&delay[i], INTERP_TAPS[(int)roundf(_mu * 128.0f)], 8);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&_p_0T, (lv_32fc_t*)&_in->readBuf[i - 7], INTERP_TAPS[(int)roundf(_mu * 128.0f)], 8);
|
||||||
|
}
|
||||||
|
out.writeBuf[outCount++] = _p_0T;
|
||||||
|
|
||||||
|
// Slice output value
|
||||||
|
_c_0T = DSP_STEP_CPLX(_p_0T);
|
||||||
|
|
||||||
|
// Cursed math to calculate the phase error
|
||||||
|
phaseError = (((_p_0T - _p_2T) * _c_1T.conj()) - ((_c_0T - _c_2T) * _p_1T.conj())).re;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp phase error
|
||||||
|
if (phaseError > 1.0f) { phaseError = 1.0f; }
|
||||||
|
if (phaseError < -1.0f) { phaseError = -1.0f; }
|
||||||
|
|
||||||
|
// Adjust the symbol rate using the phase error approximation and clamp
|
||||||
|
// TODO: Branchless clamp
|
||||||
|
_dynOmega = _dynOmega + (_gainOmega * phaseError);
|
||||||
|
if (_dynOmega > omegaMax) { _dynOmega = omegaMax; }
|
||||||
|
else if (_dynOmega < omegaMin) { _dynOmega = omegaMin; }
|
||||||
|
|
||||||
|
// Adjust the symbol phase according to the phase error approximation
|
||||||
|
// It will now contain the phase delta needed to jump to the next symbol
|
||||||
|
// Rounded step will contain the rounded number of symbols
|
||||||
|
_mu = _mu + _dynOmega + (_muGain * phaseError);
|
||||||
|
roundedStep = floor(_mu);
|
||||||
|
|
||||||
|
// Step to where the next symbol should be, and check for bogus input
|
||||||
|
i += (int)roundedStep;
|
||||||
|
if (i < 0) { i = 0; }
|
||||||
|
|
||||||
|
// Now that we've stepped to the next symbol, keep only the offset inside the symbol
|
||||||
|
_mu -= roundedStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextOffset = i - count;
|
||||||
|
|
||||||
|
// Save the last 7 values for the next round
|
||||||
|
memcpy(delay, &_in->readBuf[count - 7], 7 * sizeof(T));
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (outCount > 0 && !out.swap(outCount)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<T> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int count;
|
||||||
|
|
||||||
|
// Delay buffer
|
||||||
|
T delay[1024];
|
||||||
|
int nextOffset = 0;
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
float _omega = 1.0f;
|
||||||
|
float _muGain = 1.0f;
|
||||||
|
float _gainOmega = 0.001f;
|
||||||
|
float _omegaRelLimit = 0.005;
|
||||||
|
|
||||||
|
// Precalculated values
|
||||||
|
float omegaMin = _omega + (_omega * _omegaRelLimit);
|
||||||
|
float omegaMax = _omega + (_omega * _omegaRelLimit);
|
||||||
|
|
||||||
|
// Runtime adjusted
|
||||||
|
float _dynOmega = _omega;
|
||||||
|
float _mu = 0.5f;
|
||||||
|
float lastOutput = 0.0f;
|
||||||
|
|
||||||
|
// Cursed complex stuff
|
||||||
|
complex_t _p_0T = {0,0}, _p_1T = {0,0}, _p_2T = {0,0};
|
||||||
|
complex_t _c_0T = {0,0}, _c_1T = {0,0}, _c_2T = {0,0};
|
||||||
|
|
||||||
|
stream<T>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -8,17 +8,17 @@ namespace dsp {
|
|||||||
|
|
||||||
ComplexToStereo(stream<complex_t>* in) { init(in); }
|
ComplexToStereo(stream<complex_t>* in) { init(in); }
|
||||||
|
|
||||||
~ComplexToStereo() { generic_block<ComplexToStereo>::stop(); }
|
|
||||||
|
|
||||||
static_assert(sizeof(complex_t) == sizeof(stereo_t));
|
static_assert(sizeof(complex_t) == sizeof(stereo_t));
|
||||||
|
|
||||||
void init(stream<complex_t>* in) {
|
void init(stream<complex_t>* in) {
|
||||||
_in = in;
|
_in = in;
|
||||||
generic_block<ComplexToStereo>::registerInput(_in);
|
generic_block<ComplexToStereo>::registerInput(_in);
|
||||||
generic_block<ComplexToStereo>::registerOutput(&out);
|
generic_block<ComplexToStereo>::registerOutput(&out);
|
||||||
|
generic_block<ComplexToStereo>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<complex_t>* in) {
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<ComplexToStereo>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<ComplexToStereo>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<ComplexToStereo>::ctrlMtx);
|
||||||
generic_block<ComplexToStereo>::tempStop();
|
generic_block<ComplexToStereo>::tempStop();
|
||||||
generic_block<ComplexToStereo>::unregisterInput(_in);
|
generic_block<ComplexToStereo>::unregisterInput(_in);
|
||||||
@@ -28,22 +28,19 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
memcpy(out.writeBuf, _in->readBuf, count * sizeof(complex_t));
|
||||||
memcpy(out.data, _in->data, count * sizeof(complex_t));
|
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<stereo_t> out;
|
stream<stereo_t> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float avg;
|
|
||||||
int count;
|
|
||||||
stream<complex_t>* _in;
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -54,15 +51,15 @@ namespace dsp {
|
|||||||
|
|
||||||
ComplexToReal(stream<complex_t>* in) { init(in); }
|
ComplexToReal(stream<complex_t>* in) { init(in); }
|
||||||
|
|
||||||
~ComplexToReal() { generic_block<ComplexToReal>::stop(); }
|
|
||||||
|
|
||||||
void init(stream<complex_t>* in) {
|
void init(stream<complex_t>* in) {
|
||||||
_in = in;
|
_in = in;
|
||||||
generic_block<ComplexToReal>::registerInput(_in);
|
generic_block<ComplexToReal>::registerInput(_in);
|
||||||
generic_block<ComplexToReal>::registerOutput(&out);
|
generic_block<ComplexToReal>::registerOutput(&out);
|
||||||
|
generic_block<ComplexToReal>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<complex_t>* in) {
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<ComplexToReal>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<ComplexToReal>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<ComplexToReal>::ctrlMtx);
|
||||||
generic_block<ComplexToReal>::tempStop();
|
generic_block<ComplexToReal>::tempStop();
|
||||||
generic_block<ComplexToReal>::unregisterInput(_in);
|
generic_block<ComplexToReal>::unregisterInput(_in);
|
||||||
@@ -72,22 +69,19 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
volk_32fc_deinterleave_real_32f(out.writeBuf, (lv_32fc_t*)_in->readBuf, count);
|
||||||
volk_32fc_deinterleave_real_32f(out.data, (lv_32fc_t*)_in->data, count);
|
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<float> out;
|
stream<float> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float avg;
|
|
||||||
int count;
|
|
||||||
stream<complex_t>* _in;
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -98,15 +92,15 @@ namespace dsp {
|
|||||||
|
|
||||||
ComplexToImag(stream<complex_t>* in) { init(in); }
|
ComplexToImag(stream<complex_t>* in) { init(in); }
|
||||||
|
|
||||||
~ComplexToImag() { generic_block<ComplexToImag>::stop(); }
|
|
||||||
|
|
||||||
void init(stream<complex_t>* in) {
|
void init(stream<complex_t>* in) {
|
||||||
_in = in;
|
_in = in;
|
||||||
generic_block<ComplexToImag>::registerInput(_in);
|
generic_block<ComplexToImag>::registerInput(_in);
|
||||||
generic_block<ComplexToImag>::registerOutput(&out);
|
generic_block<ComplexToImag>::registerOutput(&out);
|
||||||
|
generic_block<ComplexToImag>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<complex_t>* in) {
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<ComplexToImag>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<ComplexToImag>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<ComplexToImag>::ctrlMtx);
|
||||||
generic_block<ComplexToImag>::tempStop();
|
generic_block<ComplexToImag>::tempStop();
|
||||||
generic_block<ComplexToImag>::unregisterInput(_in);
|
generic_block<ComplexToImag>::unregisterInput(_in);
|
||||||
@@ -116,23 +110,236 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
volk_32fc_deinterleave_imag_32f(out.writeBuf, (lv_32fc_t*)_in->readBuf, count);
|
||||||
volk_32fc_deinterleave_imag_32f(out.data, (lv_32fc_t*)_in->data, count);
|
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
if(!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<float> out;
|
stream<float> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float avg;
|
|
||||||
int count;
|
|
||||||
stream<complex_t>* _in;
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class RealToComplex : public generic_block<RealToComplex> {
|
||||||
|
public:
|
||||||
|
RealToComplex() {}
|
||||||
|
|
||||||
|
RealToComplex(stream<float>* in) { init(in); }
|
||||||
|
|
||||||
|
~RealToComplex() {
|
||||||
|
if (!generic_block<RealToComplex>::_block_init) { return; }
|
||||||
|
generic_block<RealToComplex>::stop();
|
||||||
|
delete[] nullBuffer;
|
||||||
|
generic_block<RealToComplex>::_block_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<float>* in) {
|
||||||
|
_in = in;
|
||||||
|
nullBuffer = new float[STREAM_BUFFER_SIZE];
|
||||||
|
memset(nullBuffer, 0, STREAM_BUFFER_SIZE * sizeof(float));
|
||||||
|
generic_block<RealToComplex>::registerInput(_in);
|
||||||
|
generic_block<RealToComplex>::registerOutput(&out);
|
||||||
|
generic_block<RealToComplex>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<float>* in) {
|
||||||
|
assert(generic_block<RealToComplex>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<RealToComplex>::ctrlMtx);
|
||||||
|
generic_block<RealToComplex>::tempStop();
|
||||||
|
generic_block<RealToComplex>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<RealToComplex>::registerInput(_in);
|
||||||
|
generic_block<RealToComplex>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
volk_32f_x2_interleave_32fc((lv_32fc_t*)out.writeBuf, _in->readBuf, nullBuffer, count);
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<complex_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float* nullBuffer;
|
||||||
|
stream<float>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Int16CToComplex : public generic_block<Int16CToComplex> {
|
||||||
|
public:
|
||||||
|
Int16CToComplex() {}
|
||||||
|
|
||||||
|
Int16CToComplex(stream<int16_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<int16_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
generic_block<Int16CToComplex>::registerInput(_in);
|
||||||
|
generic_block<Int16CToComplex>::registerOutput(&out);
|
||||||
|
generic_block<Int16CToComplex>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<int16_t>* in) {
|
||||||
|
assert(generic_block<Int16CToComplex>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Int16CToComplex>::ctrlMtx);
|
||||||
|
generic_block<Int16CToComplex>::tempStop();
|
||||||
|
generic_block<Int16CToComplex>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<Int16CToComplex>::registerInput(_in);
|
||||||
|
generic_block<Int16CToComplex>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
volk_16i_s32f_convert_32f((float*)out.writeBuf, _in->readBuf, 32768.0f, count * 2);
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<complex_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<int16_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ComplexToInt16C : public generic_block<ComplexToInt16C> {
|
||||||
|
public:
|
||||||
|
ComplexToInt16C() {}
|
||||||
|
|
||||||
|
ComplexToInt16C(stream<complex_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<complex_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
generic_block<ComplexToInt16C>::registerInput(_in);
|
||||||
|
generic_block<ComplexToInt16C>::registerOutput(&out);
|
||||||
|
generic_block<ComplexToInt16C>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<ComplexToInt16C>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<ComplexToInt16C>::ctrlMtx);
|
||||||
|
generic_block<ComplexToInt16C>::tempStop();
|
||||||
|
generic_block<ComplexToInt16C>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<ComplexToInt16C>::registerInput(_in);
|
||||||
|
generic_block<ComplexToInt16C>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
volk_32f_s32f_convert_16i(out.writeBuf, (float*)_in->readBuf, 32768.0f, count * 2);
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<int16_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Int16ToFloat : public generic_block<Int16ToFloat> {
|
||||||
|
public:
|
||||||
|
Int16ToFloat() {}
|
||||||
|
|
||||||
|
Int16ToFloat(stream<int16_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<int16_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
generic_block<Int16ToFloat>::registerInput(_in);
|
||||||
|
generic_block<Int16ToFloat>::registerOutput(&out);
|
||||||
|
generic_block<Int16ToFloat>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<int16_t>* in) {
|
||||||
|
assert(generic_block<Int16ToFloat>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Int16ToFloat>::ctrlMtx);
|
||||||
|
generic_block<Int16ToFloat>::tempStop();
|
||||||
|
generic_block<Int16ToFloat>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<Int16ToFloat>::registerInput(_in);
|
||||||
|
generic_block<Int16ToFloat>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
volk_16i_s32f_convert_32f(out.writeBuf, _in->readBuf, 32768.0f, count);
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<float> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<int16_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class FloatToInt16 : public generic_block<FloatToInt16> {
|
||||||
|
public:
|
||||||
|
FloatToInt16() {}
|
||||||
|
|
||||||
|
FloatToInt16(stream<float>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<float>* in) {
|
||||||
|
_in = in;
|
||||||
|
generic_block<FloatToInt16>::registerInput(_in);
|
||||||
|
generic_block<FloatToInt16>::registerOutput(&out);
|
||||||
|
generic_block<FloatToInt16>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<float>* in) {
|
||||||
|
assert(generic_block<FloatToInt16>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<FloatToInt16>::ctrlMtx);
|
||||||
|
generic_block<FloatToInt16>::tempStop();
|
||||||
|
generic_block<FloatToInt16>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<FloatToInt16>::registerInput(_in);
|
||||||
|
generic_block<FloatToInt16>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
volk_32f_s32f_convert_16i(out.writeBuf, _in->readBuf, 32768.0f, count);
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<int16_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<float>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
76
core/src/dsp/correction.h
Normal file
76
core/src/dsp/correction.h
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <dsp/stream.h>
|
||||||
|
#include <dsp/types.h>
|
||||||
|
#include <dsp/window.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
class IQCorrector : public generic_block<IQCorrector> {
|
||||||
|
public:
|
||||||
|
IQCorrector() {}
|
||||||
|
|
||||||
|
IQCorrector(stream<complex_t>* in, float rate) { init(in, rate); }
|
||||||
|
|
||||||
|
void init(stream<complex_t>* in, float rate) {
|
||||||
|
_in = in;
|
||||||
|
correctionRate = rate;
|
||||||
|
offset.re = 0;
|
||||||
|
offset.im = 0;
|
||||||
|
generic_block<IQCorrector>::registerInput(_in);
|
||||||
|
generic_block<IQCorrector>::registerOutput(&out);
|
||||||
|
generic_block<IQCorrector>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<IQCorrector>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<IQCorrector>::ctrlMtx);
|
||||||
|
generic_block<IQCorrector>::tempStop();
|
||||||
|
generic_block<IQCorrector>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<IQCorrector>::registerInput(_in);
|
||||||
|
generic_block<IQCorrector>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCorrectionRate(float rate) {
|
||||||
|
correctionRate = rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
if (bypass) {
|
||||||
|
memcpy(out.writeBuf, _in->readBuf, count * sizeof(complex_t));
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
out.writeBuf[i] = _in->readBuf[i] - offset;
|
||||||
|
offset = offset + (out.writeBuf[i] * correctionRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<complex_t> out;
|
||||||
|
|
||||||
|
// TEMPORARY FOR DEBUG PURPOSES
|
||||||
|
bool bypass = false;
|
||||||
|
complex_t offset;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<complex_t>* _in;
|
||||||
|
float correctionRate = 0.00001;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
107
core/src/dsp/decimation.h
Normal file
107
core/src/dsp/decimation.h
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <dsp/stream.h>
|
||||||
|
#include <dsp/types.h>
|
||||||
|
#include <dsp/window.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
template <class T>
|
||||||
|
class HalfDecimator : public generic_block<HalfDecimator<T>> {
|
||||||
|
public:
|
||||||
|
HalfDecimator() {}
|
||||||
|
|
||||||
|
HalfDecimator(stream<T>* in, dsp::filter_window::generic_window* window) { init(in, window); }
|
||||||
|
|
||||||
|
~HalfDecimator() {
|
||||||
|
if (!generic_block<HalfDecimator<T>>::_block_init) { return; }
|
||||||
|
generic_block<HalfDecimator<T>>::stop();
|
||||||
|
volk_free(buffer);
|
||||||
|
volk_free(taps);
|
||||||
|
generic_block<HalfDecimator<T>>::_block_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<T>* in, dsp::filter_window::generic_window* window) {
|
||||||
|
_in = in;
|
||||||
|
|
||||||
|
tapCount = window->getTapCount();
|
||||||
|
taps = (float*)volk_malloc(tapCount * sizeof(float), volk_get_alignment());
|
||||||
|
window->createTaps(taps, tapCount);
|
||||||
|
|
||||||
|
buffer = (T*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(T) * 2, volk_get_alignment());
|
||||||
|
bufStart = &buffer[tapCount];
|
||||||
|
generic_block<HalfDecimator<T>>::registerInput(_in);
|
||||||
|
generic_block<HalfDecimator<T>>::registerOutput(&out);
|
||||||
|
generic_block<HalfDecimator<T>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<HalfDecimator<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<HalfDecimator<T>>::ctrlMtx);
|
||||||
|
generic_block<HalfDecimator<T>>::tempStop();
|
||||||
|
generic_block<HalfDecimator<T>>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<HalfDecimator<T>>::registerInput(_in);
|
||||||
|
generic_block<HalfDecimator<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateWindow(dsp::filter_window::generic_window* window) {
|
||||||
|
assert(generic_block<HalfDecimator<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<HalfDecimator<T>>::ctrlMtx);
|
||||||
|
std::lock_guard<std::mutex> lck2(bufMtx);
|
||||||
|
_window = window;
|
||||||
|
volk_free(taps);
|
||||||
|
tapCount = window->getTapCount();
|
||||||
|
taps = (float*)volk_malloc(tapCount * sizeof(float), volk_get_alignment());
|
||||||
|
bufStart = &buffer[tapCount];
|
||||||
|
window->createTaps(taps, tapCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
memcpy(bufStart, _in->readBuf, count * sizeof(T));
|
||||||
|
_in->flush();
|
||||||
|
|
||||||
|
int inIndex = _inIndex;
|
||||||
|
int outIndex = 0;
|
||||||
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
|
while (inIndex < count) {
|
||||||
|
volk_32f_x2_dot_prod_32f((float*)&out.writeBuf[outIndex], (float*)&buffer[inIndex+1], taps, tapCount);
|
||||||
|
inIndex += 2;
|
||||||
|
outIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<T, complex_t>) {
|
||||||
|
while (inIndex < count) {
|
||||||
|
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&out.writeBuf[outIndex], (lv_32fc_t*)&buffer[inIndex+1], taps, tapCount);
|
||||||
|
inIndex += 2;
|
||||||
|
outIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_inIndex = inIndex - count;
|
||||||
|
|
||||||
|
if (!out.swap(outIndex)) { return -1; }
|
||||||
|
|
||||||
|
memmove(buffer, &buffer[count], tapCount * sizeof(T));
|
||||||
|
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<T> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<T>* _in;
|
||||||
|
|
||||||
|
dsp::filter_window::generic_window* _window;
|
||||||
|
std::mutex bufMtx;
|
||||||
|
|
||||||
|
T* bufStart;
|
||||||
|
T* buffer;
|
||||||
|
int tapCount;
|
||||||
|
float* taps;
|
||||||
|
int _inIndex = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
422
core/src/dsp/deframing.h
Normal file
422
core/src/dsp/deframing.h
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#define DSP_SIGN(n) ((n) >= 0)
|
||||||
|
#define DSP_STEP(n) (((n) > 0.0f) ? 1.0f : -1.0f)
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
class Deframer : public generic_block<Deframer> {
|
||||||
|
public:
|
||||||
|
Deframer() {}
|
||||||
|
|
||||||
|
Deframer(stream<uint8_t>* in, int frameLen, uint8_t* syncWord, int syncLen) { init(in, frameLen, syncWord, syncLen); }
|
||||||
|
|
||||||
|
~Deframer() {
|
||||||
|
if (!generic_block<Deframer>::_block_init) { return; }
|
||||||
|
generic_block<Deframer>::stop();
|
||||||
|
generic_block<Deframer>::_block_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<uint8_t>* in, int frameLen, uint8_t* syncWord, int syncLen) {
|
||||||
|
_in = in;
|
||||||
|
_frameLen = frameLen;
|
||||||
|
_syncword = new uint8_t[syncLen];
|
||||||
|
_syncLen = syncLen;
|
||||||
|
memcpy(_syncword, syncWord, syncLen);
|
||||||
|
|
||||||
|
buffer = new uint8_t[STREAM_BUFFER_SIZE + syncLen];
|
||||||
|
memset(buffer, 0, syncLen);
|
||||||
|
bufferStart = buffer + syncLen;
|
||||||
|
|
||||||
|
generic_block<Deframer>::registerInput(_in);
|
||||||
|
generic_block<Deframer>::registerOutput(&out);
|
||||||
|
generic_block<Deframer>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<uint8_t>* in) {
|
||||||
|
assert(generic_block<Deframer>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Deframer>::ctrlMtx);
|
||||||
|
generic_block<Deframer>::tempStop();
|
||||||
|
generic_block<Deframer>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<Deframer>::registerInput(_in);
|
||||||
|
generic_block<Deframer>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
// Copy data into work buffer
|
||||||
|
memcpy(bufferStart, _in->readBuf, count - 1);
|
||||||
|
|
||||||
|
// Iterate through all symbols
|
||||||
|
for (int i = 0; i < count;) {
|
||||||
|
|
||||||
|
// If already in the process of reading bits
|
||||||
|
if (bitsRead >= 0) {
|
||||||
|
if ((bitsRead % 8) == 0) { out.writeBuf[bitsRead / 8] = 0; }
|
||||||
|
out.writeBuf[bitsRead / 8] |= (buffer[i] << (7 - (bitsRead % 8)));
|
||||||
|
i++;
|
||||||
|
bitsRead++;
|
||||||
|
|
||||||
|
if (bitsRead >= _frameLen) {
|
||||||
|
if (!out.swap((bitsRead / 8) + ((bitsRead % 8) > 0))) { return -1; }
|
||||||
|
bitsRead = -1;
|
||||||
|
if (allowSequential) { nextBitIsStartOfFrame = true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else, check for a header
|
||||||
|
else if (memcmp(buffer + i, _syncword, _syncLen) == 0) {
|
||||||
|
bitsRead = 0;
|
||||||
|
//printf("Frame found!\n");
|
||||||
|
badFrameCount = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (nextBitIsStartOfFrame) {
|
||||||
|
nextBitIsStartOfFrame = false;
|
||||||
|
|
||||||
|
// try to save
|
||||||
|
if (badFrameCount < 5) {
|
||||||
|
badFrameCount++;
|
||||||
|
//printf("Frame found!\n");
|
||||||
|
bitsRead = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else { i++; }
|
||||||
|
|
||||||
|
nextBitIsStartOfFrame = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep last _syncLen4 symbols
|
||||||
|
memcpy(buffer, &_in->readBuf[count - _syncLen], _syncLen);
|
||||||
|
|
||||||
|
//printf("Block processed\n");
|
||||||
|
callcount++;
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allowSequential = true;
|
||||||
|
|
||||||
|
stream<uint8_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t* buffer;
|
||||||
|
uint8_t* bufferStart;
|
||||||
|
uint8_t* _syncword;
|
||||||
|
int count;
|
||||||
|
int _frameLen;
|
||||||
|
int _syncLen;
|
||||||
|
int bitsRead = -1;
|
||||||
|
|
||||||
|
int badFrameCount = 5;
|
||||||
|
bool nextBitIsStartOfFrame = false;
|
||||||
|
|
||||||
|
int callcount = 0;
|
||||||
|
|
||||||
|
stream<uint8_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
inline int MachesterHammingDistance(float* data, uint8_t* syncBits, int n) {
|
||||||
|
int dist = 0;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if ((data[(2*i) + 1] > data[2*i]) != syncBits[i]) { dist++; }
|
||||||
|
}
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int HammingDistance(uint8_t* data, uint8_t* syncBits, int n) {
|
||||||
|
int dist = 0;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (data[i] != syncBits[i]) { dist++; }
|
||||||
|
}
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ManchesterDeframer : public generic_block<ManchesterDeframer> {
|
||||||
|
public:
|
||||||
|
ManchesterDeframer() {}
|
||||||
|
|
||||||
|
ManchesterDeframer(stream<float>* in, int frameLen, uint8_t* syncWord, int syncLen) { init(in, frameLen, syncWord, syncLen); }
|
||||||
|
|
||||||
|
void init(stream<float>* in, int frameLen, uint8_t* syncWord, int syncLen) {
|
||||||
|
_in = in;
|
||||||
|
_frameLen = frameLen;
|
||||||
|
_syncword = new uint8_t[syncLen];
|
||||||
|
_syncLen = syncLen;
|
||||||
|
memcpy(_syncword, syncWord, syncLen);
|
||||||
|
|
||||||
|
buffer = new float[STREAM_BUFFER_SIZE + (syncLen * 2)];
|
||||||
|
memset(buffer, 0, syncLen * 2 * sizeof(float));
|
||||||
|
bufferStart = &buffer[syncLen * 2];
|
||||||
|
|
||||||
|
generic_block<ManchesterDeframer>::registerInput(_in);
|
||||||
|
generic_block<ManchesterDeframer>::registerOutput(&out);
|
||||||
|
generic_block<ManchesterDeframer>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<float>* in) {
|
||||||
|
assert(generic_block<ManchesterDeframer>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<ManchesterDeframer>::ctrlMtx);
|
||||||
|
generic_block<ManchesterDeframer>::tempStop();
|
||||||
|
generic_block<ManchesterDeframer>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<ManchesterDeframer>::registerInput(_in);
|
||||||
|
generic_block<ManchesterDeframer>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
int readable;
|
||||||
|
|
||||||
|
// Copy data into work buffer
|
||||||
|
memcpy(bufferStart, _in->readBuf, (count - 1) * sizeof(float));
|
||||||
|
|
||||||
|
// Iterate through all symbols
|
||||||
|
for (int i = 0; i < count;) {
|
||||||
|
|
||||||
|
// If already in the process of reading bits
|
||||||
|
if (bitsRead >= 0) {
|
||||||
|
readable = std::min<int>(count - i, _frameLen - bitsRead);
|
||||||
|
memcpy(&out.writeBuf[bitsRead], &buffer[i], readable * sizeof(float));
|
||||||
|
bitsRead += readable;
|
||||||
|
i += readable;
|
||||||
|
if (bitsRead >= _frameLen) {
|
||||||
|
out.swap(_frameLen);
|
||||||
|
bitsRead = -1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else, check for a header
|
||||||
|
if (MachesterHammingDistance(&buffer[i], _syncword, _syncLen) <= 2) {
|
||||||
|
bitsRead = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep last _syncLen symbols
|
||||||
|
memcpy(buffer, &_in->readBuf[count - (_syncLen * 2)], _syncLen * 2 * sizeof(float));
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<float> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float* buffer;
|
||||||
|
float* bufferStart;
|
||||||
|
uint8_t* _syncword;
|
||||||
|
int count;
|
||||||
|
int _frameLen;
|
||||||
|
int _syncLen;
|
||||||
|
int bitsRead = -1;
|
||||||
|
|
||||||
|
stream<float>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class SymbolDeframer : public generic_block<SymbolDeframer> {
|
||||||
|
public:
|
||||||
|
SymbolDeframer() {}
|
||||||
|
|
||||||
|
SymbolDeframer(stream<uint8_t>* in, int frameLen, uint8_t* syncWord, int syncLen) { init(in, frameLen, syncWord, syncLen); }
|
||||||
|
|
||||||
|
void init(stream<uint8_t>* in, int frameLen, uint8_t* syncWord, int syncLen) {
|
||||||
|
_in = in;
|
||||||
|
_frameLen = frameLen;
|
||||||
|
_syncword = new uint8_t[syncLen];
|
||||||
|
_syncLen = syncLen;
|
||||||
|
memcpy(_syncword, syncWord, syncLen);
|
||||||
|
|
||||||
|
buffer = new uint8_t[STREAM_BUFFER_SIZE + syncLen];
|
||||||
|
memset(buffer, 0, syncLen);
|
||||||
|
bufferStart = &buffer[syncLen];
|
||||||
|
|
||||||
|
generic_block<SymbolDeframer>::registerInput(_in);
|
||||||
|
generic_block<SymbolDeframer>::registerOutput(&out);
|
||||||
|
generic_block<SymbolDeframer>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<uint8_t>* in) {
|
||||||
|
assert(generic_block<SymbolDeframer>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<SymbolDeframer>::ctrlMtx);
|
||||||
|
generic_block<SymbolDeframer>::tempStop();
|
||||||
|
generic_block<SymbolDeframer>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<SymbolDeframer>::registerInput(_in);
|
||||||
|
generic_block<SymbolDeframer>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
int readable;
|
||||||
|
|
||||||
|
// Copy data into work buffer
|
||||||
|
memcpy(bufferStart, _in->readBuf, count - 1);
|
||||||
|
|
||||||
|
// Iterate through all symbols
|
||||||
|
for (int i = 0; i < count;) {
|
||||||
|
|
||||||
|
// If already in the process of reading bits
|
||||||
|
if (bitsRead >= 0) {
|
||||||
|
readable = std::min<int>(count - i, _frameLen - bitsRead);
|
||||||
|
memcpy(&out.writeBuf[bitsRead], &buffer[i], readable);
|
||||||
|
bitsRead += readable;
|
||||||
|
i += readable;
|
||||||
|
if (bitsRead >= _frameLen) {
|
||||||
|
out.swap(_frameLen);
|
||||||
|
bitsRead = -1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else, check for a header
|
||||||
|
if (HammingDistance(&buffer[i], _syncword, _syncLen) <= 2) {
|
||||||
|
bitsRead = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep last _syncLen symbols
|
||||||
|
memcpy(buffer, &_in->readBuf[count - _syncLen], _syncLen);
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<uint8_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t* buffer;
|
||||||
|
uint8_t* bufferStart;
|
||||||
|
uint8_t* _syncword;
|
||||||
|
int count;
|
||||||
|
int _frameLen;
|
||||||
|
int _syncLen;
|
||||||
|
int bitsRead = -1;
|
||||||
|
|
||||||
|
stream<uint8_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ManchesterDecoder : public generic_block<ManchesterDecoder> {
|
||||||
|
public:
|
||||||
|
ManchesterDecoder() {}
|
||||||
|
|
||||||
|
ManchesterDecoder(stream<float>* in, bool inverted) { init(in, inverted); }
|
||||||
|
|
||||||
|
void init(stream<float>* in, bool inverted) {
|
||||||
|
_in = in;
|
||||||
|
_inverted = inverted;
|
||||||
|
generic_block<ManchesterDecoder>::registerInput(_in);
|
||||||
|
generic_block<ManchesterDecoder>::registerOutput(&out);
|
||||||
|
generic_block<ManchesterDecoder>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<float>* in) {
|
||||||
|
assert(generic_block<ManchesterDecoder>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<ManchesterDecoder>::ctrlMtx);
|
||||||
|
generic_block<ManchesterDecoder>::tempStop();
|
||||||
|
generic_block<ManchesterDecoder>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<ManchesterDecoder>::registerInput(_in);
|
||||||
|
generic_block<ManchesterDecoder>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
if (_inverted) {
|
||||||
|
for (int i = 0; i < count; i += 2) {
|
||||||
|
out.writeBuf[i/2] = (_in->readBuf[i + 1] < _in->readBuf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < count; i += 2) {
|
||||||
|
out.writeBuf[i/2] = (_in->readBuf[i + 1] > _in->readBuf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
out.swap(count / 2);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<uint8_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<float>* _in;
|
||||||
|
bool _inverted;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class BitPacker : public generic_block<BitPacker> {
|
||||||
|
public:
|
||||||
|
BitPacker() {}
|
||||||
|
|
||||||
|
BitPacker(stream<uint8_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<uint8_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
|
||||||
|
generic_block<BitPacker>::registerInput(_in);
|
||||||
|
generic_block<BitPacker>::registerOutput(&out);
|
||||||
|
generic_block<BitPacker>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<uint8_t>* in) {
|
||||||
|
assert(generic_block<BitPacker>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<BitPacker>::ctrlMtx);
|
||||||
|
generic_block<BitPacker>::tempStop();
|
||||||
|
generic_block<BitPacker>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<BitPacker>::registerInput(_in);
|
||||||
|
generic_block<BitPacker>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if ((i % 8) == 0) { out.writeBuf[i / 8] = 0; }
|
||||||
|
out.writeBuf[i / 8] |= (_in->readBuf[i] & 1) << (7 - (i % 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
out.swap((count / 8) + (((count % 8) == 0) ? 0 : 1));
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<uint8_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
stream<uint8_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,8 +1,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <dsp/block.h>
|
#include <dsp/block.h>
|
||||||
#include <volk/volk.h>
|
#include <volk/volk.h>
|
||||||
|
#include <dsp/filter.h>
|
||||||
|
#include <dsp/processing.h>
|
||||||
|
#include <dsp/routing.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <dsp/pll.h>
|
||||||
|
#include <dsp/clock_recovery.h>
|
||||||
|
#include <dsp/math.h>
|
||||||
|
#include <dsp/convertion.h>
|
||||||
|
#include <dsp/audio.h>
|
||||||
|
#include <dsp/stereo_fm.h>
|
||||||
|
|
||||||
#define FAST_ATAN2_COEF1 FL_M_PI / 4.0f
|
#define FAST_ATAN2_COEF1 FL_M_PI / 4.0f
|
||||||
#define FAST_ATAN2_COEF2 3.0f * FAST_ATAN2_COEF1
|
#define FAST_ATAN2_COEF2 3.0f * FAST_ATAN2_COEF1
|
||||||
@@ -22,18 +30,99 @@ inline float fast_arctan2(float y, float x) {
|
|||||||
if (y < 0.0f) {
|
if (y < 0.0f) {
|
||||||
return -angle;
|
return -angle;
|
||||||
}
|
}
|
||||||
return angle;
|
return angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
|
class FloatFMDemod : public generic_block<FloatFMDemod> {
|
||||||
|
public:
|
||||||
|
FloatFMDemod() {}
|
||||||
|
|
||||||
|
FloatFMDemod(stream<complex_t>* in, float sampleRate, float deviation) { init(in, sampleRate, deviation); }
|
||||||
|
|
||||||
|
void init(stream<complex_t>* in, float sampleRate, float deviation) {
|
||||||
|
_in = in;
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
_deviation = deviation;
|
||||||
|
phasorSpeed = (2 * FL_M_PI) / (_sampleRate / _deviation);
|
||||||
|
generic_block<FloatFMDemod>::registerInput(_in);
|
||||||
|
generic_block<FloatFMDemod>::registerOutput(&out);
|
||||||
|
generic_block<FloatFMDemod>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<FloatFMDemod>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<FloatFMDemod>::ctrlMtx);
|
||||||
|
generic_block<FloatFMDemod>::tempStop();
|
||||||
|
generic_block<FloatFMDemod>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<FloatFMDemod>::registerInput(_in);
|
||||||
|
generic_block<FloatFMDemod>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSampleRate(float sampleRate) {
|
||||||
|
assert(generic_block<FloatFMDemod>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<FloatFMDemod>::ctrlMtx);
|
||||||
|
generic_block<FloatFMDemod>::tempStop();
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
phasorSpeed = (2 * FL_M_PI) / (_sampleRate / _deviation);
|
||||||
|
generic_block<FloatFMDemod>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
float getSampleRate() {
|
||||||
|
assert(generic_block<FloatFMDemod>::_block_init);
|
||||||
|
return _sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDeviation(float deviation) {
|
||||||
|
assert(generic_block<FloatFMDemod>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<FloatFMDemod>::ctrlMtx);
|
||||||
|
generic_block<FloatFMDemod>::tempStop();
|
||||||
|
_deviation = deviation;
|
||||||
|
phasorSpeed = (2 * FL_M_PI) / (_sampleRate / _deviation);
|
||||||
|
generic_block<FloatFMDemod>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
float getDeviation() {
|
||||||
|
assert(generic_block<FloatFMDemod>::_block_init);
|
||||||
|
return _deviation;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
// This is somehow faster than volk...
|
||||||
|
float diff, currentPhase;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
currentPhase = fast_arctan2(_in->readBuf[i].im, _in->readBuf[i].re);
|
||||||
|
diff = currentPhase - phase;
|
||||||
|
if (diff > 3.1415926535f) { diff -= 2 * 3.1415926535f; }
|
||||||
|
else if (diff <= -3.1415926535f) { diff += 2 * 3.1415926535f; }
|
||||||
|
out.writeBuf[i] = diff / phasorSpeed;
|
||||||
|
phase = currentPhase;
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<float> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float phase = 0;
|
||||||
|
float phasorSpeed, _sampleRate, _deviation;
|
||||||
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class FMDemod : public generic_block<FMDemod> {
|
class FMDemod : public generic_block<FMDemod> {
|
||||||
public:
|
public:
|
||||||
FMDemod() {}
|
FMDemod() {}
|
||||||
|
|
||||||
FMDemod(stream<complex_t>* in, float sampleRate, float deviation) { init(in, sampleRate, deviation); }
|
FMDemod(stream<complex_t>* in, float sampleRate, float deviation) { init(in, sampleRate, deviation); }
|
||||||
|
|
||||||
~FMDemod() { generic_block<FMDemod>::stop(); }
|
|
||||||
|
|
||||||
void init(stream<complex_t>* in, float sampleRate, float deviation) {
|
void init(stream<complex_t>* in, float sampleRate, float deviation) {
|
||||||
_in = in;
|
_in = in;
|
||||||
_sampleRate = sampleRate;
|
_sampleRate = sampleRate;
|
||||||
@@ -41,9 +130,11 @@ namespace dsp {
|
|||||||
phasorSpeed = (2 * FL_M_PI) / (_sampleRate / _deviation);
|
phasorSpeed = (2 * FL_M_PI) / (_sampleRate / _deviation);
|
||||||
generic_block<FMDemod>::registerInput(_in);
|
generic_block<FMDemod>::registerInput(_in);
|
||||||
generic_block<FMDemod>::registerOutput(&out);
|
generic_block<FMDemod>::registerOutput(&out);
|
||||||
|
generic_block<FMDemod>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<complex_t>* in) {
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<FMDemod>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<FMDemod>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<FMDemod>::ctrlMtx);
|
||||||
generic_block<FMDemod>::tempStop();
|
generic_block<FMDemod>::tempStop();
|
||||||
generic_block<FMDemod>::unregisterInput(_in);
|
generic_block<FMDemod>::unregisterInput(_in);
|
||||||
@@ -53,6 +144,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setSampleRate(float sampleRate) {
|
void setSampleRate(float sampleRate) {
|
||||||
|
assert(generic_block<FMDemod>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<FMDemod>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<FMDemod>::ctrlMtx);
|
||||||
generic_block<FMDemod>::tempStop();
|
generic_block<FMDemod>::tempStop();
|
||||||
_sampleRate = sampleRate;
|
_sampleRate = sampleRate;
|
||||||
@@ -61,49 +153,48 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float getSampleRate() {
|
float getSampleRate() {
|
||||||
|
assert(generic_block<FMDemod>::_block_init);
|
||||||
return _sampleRate;
|
return _sampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDeviation(float deviation) {
|
void setDeviation(float deviation) {
|
||||||
std::lock_guard<std::mutex> lck(generic_block<FMDemod>::ctrlMtx);
|
assert(generic_block<FMDemod>::_block_init);
|
||||||
generic_block<FMDemod>::tempStop();
|
|
||||||
_deviation = deviation;
|
_deviation = deviation;
|
||||||
phasorSpeed = (2 * FL_M_PI) / (_sampleRate / _deviation);
|
phasorSpeed = (2 * FL_M_PI) / (_sampleRate / _deviation);
|
||||||
generic_block<FMDemod>::tempStart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float getDeviation() {
|
float getDeviation() {
|
||||||
|
assert(generic_block<FMDemod>::_block_init);
|
||||||
return _deviation;
|
return _deviation;
|
||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
// This is somehow faster than volk...
|
// This is somehow faster than volk...
|
||||||
|
|
||||||
float diff, currentPhase;
|
float diff, currentPhase;
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
currentPhase = fast_arctan2(_in->data[i].i, _in->data[i].q);
|
currentPhase = fast_arctan2(_in->readBuf[i].im, _in->readBuf[i].re);
|
||||||
diff = currentPhase - phase;
|
diff = currentPhase - phase;
|
||||||
if (diff > 3.1415926535f) { diff -= 2 * 3.1415926535f; }
|
if (diff > 3.1415926535f) { diff -= 2 * 3.1415926535f; }
|
||||||
else if (diff <= -3.1415926535f) { diff += 2 * 3.1415926535f; }
|
else if (diff <= -3.1415926535f) { diff += 2 * 3.1415926535f; }
|
||||||
out.data[i] = diff / phasorSpeed;
|
out.writeBuf[i].l = diff / phasorSpeed;
|
||||||
|
out.writeBuf[i].r = diff / phasorSpeed;
|
||||||
phase = currentPhase;
|
phase = currentPhase;
|
||||||
}
|
}
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<float> out;
|
stream<stereo_t> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
float phase = 0;
|
||||||
float phase, phasorSpeed, _sampleRate, _deviation;
|
float phasorSpeed, _sampleRate, _deviation;
|
||||||
stream<complex_t>* _in;
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -114,15 +205,15 @@ namespace dsp {
|
|||||||
|
|
||||||
AMDemod(stream<complex_t>* in) { init(in); }
|
AMDemod(stream<complex_t>* in) { init(in); }
|
||||||
|
|
||||||
~AMDemod() { generic_block<AMDemod>::stop(); }
|
|
||||||
|
|
||||||
void init(stream<complex_t>* in) {
|
void init(stream<complex_t>* in) {
|
||||||
_in = in;
|
_in = in;
|
||||||
generic_block<AMDemod>::registerInput(_in);
|
generic_block<AMDemod>::registerInput(_in);
|
||||||
generic_block<AMDemod>::registerOutput(&out);
|
generic_block<AMDemod>::registerOutput(&out);
|
||||||
|
generic_block<AMDemod>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<complex_t>* in) {
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<AMDemod>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<AMDemod>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<AMDemod>::ctrlMtx);
|
||||||
generic_block<AMDemod>::tempStop();
|
generic_block<AMDemod>::tempStop();
|
||||||
generic_block<AMDemod>::unregisterInput(_in);
|
generic_block<AMDemod>::unregisterInput(_in);
|
||||||
@@ -132,31 +223,27 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
volk_32fc_magnitude_32f(out.writeBuf, (lv_32fc_t*)_in->readBuf, count);
|
||||||
volk_32fc_magnitude_32f(out.data, (lv_32fc_t*)_in->data, count);
|
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
|
|
||||||
volk_32f_accumulator_s32f(&avg, out.data, count);
|
|
||||||
avg /= (float)count;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
out.data[i] -= avg;
|
out.writeBuf[i] -= avg;
|
||||||
|
avg += out.writeBuf[i] * 10e-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<float> out;
|
stream<float> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float avg;
|
|
||||||
int count;
|
|
||||||
stream<complex_t>* _in;
|
stream<complex_t>* _in;
|
||||||
|
float avg = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -167,8 +254,10 @@ namespace dsp {
|
|||||||
SSBDemod(stream<complex_t>* in, float sampleRate, float bandWidth, int mode) { init(in, sampleRate, bandWidth, mode); }
|
SSBDemod(stream<complex_t>* in, float sampleRate, float bandWidth, int mode) { init(in, sampleRate, bandWidth, mode); }
|
||||||
|
|
||||||
~SSBDemod() {
|
~SSBDemod() {
|
||||||
|
if (!generic_block<SSBDemod>::_block_init) { return; }
|
||||||
generic_block<SSBDemod>::stop();
|
generic_block<SSBDemod>::stop();
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
|
generic_block<SSBDemod>::_block_init = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -197,9 +286,11 @@ namespace dsp {
|
|||||||
buffer = new lv_32fc_t[STREAM_BUFFER_SIZE];
|
buffer = new lv_32fc_t[STREAM_BUFFER_SIZE];
|
||||||
generic_block<SSBDemod>::registerInput(_in);
|
generic_block<SSBDemod>::registerInput(_in);
|
||||||
generic_block<SSBDemod>::registerOutput(&out);
|
generic_block<SSBDemod>::registerOutput(&out);
|
||||||
|
generic_block<SSBDemod>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<complex_t>* in) {
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<SSBDemod>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<SSBDemod>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<SSBDemod>::ctrlMtx);
|
||||||
generic_block<SSBDemod>::tempStop();
|
generic_block<SSBDemod>::tempStop();
|
||||||
generic_block<SSBDemod>::unregisterInput(_in);
|
generic_block<SSBDemod>::unregisterInput(_in);
|
||||||
@@ -209,7 +300,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setSampleRate(float sampleRate) {
|
void setSampleRate(float sampleRate) {
|
||||||
// No need to restart
|
assert(generic_block<SSBDemod>::_block_init);
|
||||||
_sampleRate = sampleRate;
|
_sampleRate = sampleRate;
|
||||||
switch (_mode) {
|
switch (_mode) {
|
||||||
case MODE_USB:
|
case MODE_USB:
|
||||||
@@ -225,7 +316,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setBandWidth(float bandWidth) {
|
void setBandWidth(float bandWidth) {
|
||||||
// No need to restart
|
assert(generic_block<SSBDemod>::_block_init);
|
||||||
_bandWidth = bandWidth;
|
_bandWidth = bandWidth;
|
||||||
switch (_mode) {
|
switch (_mode) {
|
||||||
case MODE_USB:
|
case MODE_USB:
|
||||||
@@ -241,6 +332,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setMode(int mode) {
|
void setMode(int mode) {
|
||||||
|
assert(generic_block<SSBDemod>::_block_init);
|
||||||
_mode = mode;
|
_mode = mode;
|
||||||
switch (_mode) {
|
switch (_mode) {
|
||||||
case MODE_USB:
|
case MODE_USB:
|
||||||
@@ -256,22 +348,20 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
volk_32fc_s32fc_x2_rotator_32fc(buffer, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
|
||||||
volk_32fc_s32fc_x2_rotator_32fc(buffer, (lv_32fc_t*)_in->data, phaseDelta, &phase, count);
|
volk_32fc_deinterleave_real_32f(out.writeBuf, buffer, count);
|
||||||
volk_32fc_deinterleave_real_32f(out.data, buffer, count);
|
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<float> out;
|
stream<float> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
|
||||||
int _mode;
|
int _mode;
|
||||||
float _sampleRate, _bandWidth;
|
float _sampleRate, _bandWidth;
|
||||||
stream<complex_t>* _in;
|
stream<complex_t>* _in;
|
||||||
@@ -280,4 +370,365 @@ namespace dsp {
|
|||||||
lv_32fc_t phaseDelta;
|
lv_32fc_t phaseDelta;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MSKDemod : public generic_hier_block<MSKDemod> {
|
||||||
|
public:
|
||||||
|
MSKDemod() {}
|
||||||
|
|
||||||
|
MSKDemod(stream<complex_t>* input, float sampleRate, float deviation, float baudRate, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
|
||||||
|
init(input, sampleRate, deviation, baudRate, omegaGain, muGain, omegaRelLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<complex_t>* input, float sampleRate, float deviation, float baudRate, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
_deviation = deviation;
|
||||||
|
_baudRate = baudRate;
|
||||||
|
_omegaGain = omegaGain;
|
||||||
|
_muGain = muGain;
|
||||||
|
_omegaRelLimit = omegaRelLimit;
|
||||||
|
|
||||||
|
demod.init(input, _sampleRate, _deviation);
|
||||||
|
recov.init(&demod.out, _sampleRate / _baudRate, _omegaGain, _muGain, _omegaRelLimit);
|
||||||
|
out = &recov.out;
|
||||||
|
|
||||||
|
generic_hier_block<MSKDemod>::registerBlock(&demod);
|
||||||
|
generic_hier_block<MSKDemod>::registerBlock(&recov);
|
||||||
|
generic_hier_block<MSKDemod>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* input) {
|
||||||
|
assert((generic_hier_block<MSKDemod>::_block_init));
|
||||||
|
demod.setInput(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSampleRate(float sampleRate) {
|
||||||
|
assert(generic_hier_block<MSKDemod>::_block_init);
|
||||||
|
generic_hier_block<MSKDemod>::tempStop();
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
demod.setSampleRate(_sampleRate);
|
||||||
|
recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
|
||||||
|
generic_hier_block<MSKDemod>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDeviation(float deviation) {
|
||||||
|
assert(generic_hier_block<MSKDemod>::_block_init);
|
||||||
|
_deviation = deviation;
|
||||||
|
demod.setDeviation(deviation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBaudRate(float baudRate, float omegaRelLimit) {
|
||||||
|
assert(generic_hier_block<MSKDemod>::_block_init);
|
||||||
|
_baudRate = baudRate;
|
||||||
|
_omegaRelLimit = omegaRelLimit;
|
||||||
|
recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMMGains(float omegaGain, float myGain) {
|
||||||
|
assert(generic_hier_block<MSKDemod>::_block_init);
|
||||||
|
_omegaGain = omegaGain;
|
||||||
|
_muGain = myGain;
|
||||||
|
recov.setGains(_omegaGain, _muGain);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOmegaRelLimit(float omegaRelLimit) {
|
||||||
|
assert(generic_hier_block<MSKDemod>::_block_init);
|
||||||
|
_omegaRelLimit = omegaRelLimit;
|
||||||
|
recov.setOmegaRelLimit(_omegaRelLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<float>* out = NULL;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FloatFMDemod demod;
|
||||||
|
MMClockRecovery<float> recov;
|
||||||
|
|
||||||
|
float _sampleRate;
|
||||||
|
float _deviation;
|
||||||
|
float _baudRate;
|
||||||
|
float _omegaGain;
|
||||||
|
float _muGain;
|
||||||
|
float _omegaRelLimit;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<int ORDER, bool OFFSET>
|
||||||
|
class PSKDemod : public generic_hier_block<PSKDemod<ORDER, OFFSET>> {
|
||||||
|
public:
|
||||||
|
PSKDemod() {}
|
||||||
|
|
||||||
|
PSKDemod(stream<complex_t>* input, float sampleRate, float baudRate, int RRCTapCount = 31, float RRCAlpha = 0.32f, float agcRate = 10e-4, float costasLoopBw = 0.004f, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
|
||||||
|
init(input, sampleRate, baudRate, RRCTapCount, RRCAlpha, agcRate, costasLoopBw, omegaGain, muGain, omegaRelLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<complex_t>* input, float sampleRate, float baudRate, int RRCTapCount = 31, float RRCAlpha = 0.32f, float agcRate = 10e-4, float costasLoopBw = 0.004f, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
|
||||||
|
_RRCTapCount = RRCTapCount;
|
||||||
|
_RRCAlpha = RRCAlpha;
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
_agcRate = agcRate;
|
||||||
|
_costasLoopBw = costasLoopBw;
|
||||||
|
_baudRate = baudRate;
|
||||||
|
_omegaGain = omegaGain;
|
||||||
|
_muGain = muGain;
|
||||||
|
_omegaRelLimit = omegaRelLimit;
|
||||||
|
|
||||||
|
agc.init(input, 1.0f, 65535, _agcRate);
|
||||||
|
taps.init(_RRCTapCount, _sampleRate, _baudRate, _RRCAlpha);
|
||||||
|
rrc.init(&agc.out, &taps);
|
||||||
|
demod.init(&rrc.out, _costasLoopBw);
|
||||||
|
|
||||||
|
generic_hier_block<PSKDemod<ORDER, OFFSET>>::registerBlock(&agc);
|
||||||
|
generic_hier_block<PSKDemod<ORDER, OFFSET>>::registerBlock(&rrc);
|
||||||
|
generic_hier_block<PSKDemod<ORDER, OFFSET>>::registerBlock(&demod);
|
||||||
|
|
||||||
|
if constexpr (OFFSET) {
|
||||||
|
delay.init(&demod.out);
|
||||||
|
recov.init(&delay.out, _sampleRate / _baudRate, _omegaGain, _muGain, _omegaRelLimit);
|
||||||
|
generic_hier_block<PSKDemod<ORDER, OFFSET>>::registerBlock(&delay);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
recov.init(&demod.out, _sampleRate / _baudRate, _omegaGain, _muGain, _omegaRelLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
generic_hier_block<PSKDemod<ORDER, OFFSET>>::registerBlock(&recov);
|
||||||
|
|
||||||
|
out = &recov.out;
|
||||||
|
|
||||||
|
generic_hier_block<PSKDemod<ORDER, OFFSET>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* input) {
|
||||||
|
assert((generic_hier_block<PSKDemod<ORDER, OFFSET>>::_block_init));
|
||||||
|
agc.setInput(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSampleRate(float sampleRate) {
|
||||||
|
assert((generic_hier_block<PSKDemod<ORDER, OFFSET>>::_block_init));
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
rrc.tempStop();
|
||||||
|
recov.tempStop();
|
||||||
|
taps.setSampleRate(_sampleRate);
|
||||||
|
rrc.updateWindow(&taps);
|
||||||
|
recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
|
||||||
|
rrc.tempStart();
|
||||||
|
recov.tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBaudRate(float baudRate) {
|
||||||
|
assert((generic_hier_block<PSKDemod<ORDER, OFFSET>>::_block_init));
|
||||||
|
_baudRate = baudRate;
|
||||||
|
rrc.tempStop();
|
||||||
|
recov.tempStop();
|
||||||
|
taps.setBaudRate(_baudRate);
|
||||||
|
rrc.updateWindow(&taps);
|
||||||
|
recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
|
||||||
|
rrc.tempStart();
|
||||||
|
recov.tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRRCParams(int RRCTapCount, float RRCAlpha) {
|
||||||
|
assert((generic_hier_block<PSKDemod<ORDER, OFFSET>>::_block_init));
|
||||||
|
_RRCTapCount = RRCTapCount;
|
||||||
|
_RRCAlpha = RRCAlpha;
|
||||||
|
taps.setTapCount(_RRCTapCount);
|
||||||
|
taps.setAlpha(RRCAlpha);
|
||||||
|
rrc.updateWindow(&taps);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAgcRate(float agcRate) {
|
||||||
|
assert((generic_hier_block<PSKDemod<ORDER, OFFSET>>::_block_init));
|
||||||
|
_agcRate = agcRate;
|
||||||
|
agc.setRate(_agcRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCostasLoopBw(float costasLoopBw) {
|
||||||
|
assert((generic_hier_block<PSKDemod<ORDER, OFFSET>>::_block_init));
|
||||||
|
_costasLoopBw = costasLoopBw;
|
||||||
|
demod.setLoopBandwidth(_costasLoopBw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMMGains(float omegaGain, float myGain) {
|
||||||
|
assert((generic_hier_block<PSKDemod<ORDER, OFFSET>>::_block_init));
|
||||||
|
_omegaGain = omegaGain;
|
||||||
|
_muGain = myGain;
|
||||||
|
recov.setGains(_omegaGain, _muGain);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOmegaRelLimit(float omegaRelLimit) {
|
||||||
|
assert((generic_hier_block<PSKDemod<ORDER, OFFSET>>::_block_init));
|
||||||
|
_omegaRelLimit = omegaRelLimit;
|
||||||
|
recov.setOmegaRelLimit(_omegaRelLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<complex_t>* out = NULL;
|
||||||
|
|
||||||
|
private:
|
||||||
|
dsp::ComplexAGC agc;
|
||||||
|
dsp::RRCTaps taps;
|
||||||
|
dsp::FIR<dsp::complex_t> rrc;
|
||||||
|
CostasLoop<ORDER> demod;
|
||||||
|
DelayImag delay;
|
||||||
|
MMClockRecovery<dsp::complex_t> recov;
|
||||||
|
|
||||||
|
int _RRCTapCount;
|
||||||
|
float _RRCAlpha;
|
||||||
|
float _sampleRate;
|
||||||
|
float _agcRate;
|
||||||
|
float _baudRate;
|
||||||
|
float _costasLoopBw;
|
||||||
|
float _omegaGain;
|
||||||
|
float _muGain;
|
||||||
|
float _omegaRelLimit;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PMDemod : public generic_hier_block<PMDemod> {
|
||||||
|
public:
|
||||||
|
PMDemod() {}
|
||||||
|
|
||||||
|
PMDemod(stream<complex_t>* input, float sampleRate, float baudRate, float agcRate = 0.02e-3f, float pllLoopBandwidth = (0.06f*0.06f) / 4.0f, int rrcTapCount = 31, float rrcAlpha = 0.6f, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
|
||||||
|
init(input, sampleRate, baudRate, agcRate, pllLoopBandwidth, rrcTapCount, rrcAlpha, omegaGain, muGain, omegaRelLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<complex_t>* input, float sampleRate, float baudRate, float agcRate = 0.02e-3f, float pllLoopBandwidth = (0.06f*0.06f) / 4.0f, int rrcTapCount = 31, float rrcAlpha = 0.6f, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
_baudRate = baudRate;
|
||||||
|
_agcRate = agcRate;
|
||||||
|
_pllLoopBandwidth = pllLoopBandwidth;
|
||||||
|
_rrcTapCount = rrcTapCount;
|
||||||
|
_rrcAlpha = rrcAlpha;
|
||||||
|
_omegaGain = omegaGain;
|
||||||
|
_muGain = muGain;
|
||||||
|
_omegaRelLimit = omegaRelLimit;
|
||||||
|
|
||||||
|
agc.init(input, 1.0f, 65535, _agcRate);
|
||||||
|
pll.init(&agc.out, _pllLoopBandwidth);
|
||||||
|
rrcwin.init(_rrcTapCount, _sampleRate, _baudRate, _rrcAlpha);
|
||||||
|
rrc.init(&pll.out, &rrcwin);
|
||||||
|
recov.init(&rrc.out, _sampleRate / _baudRate, _omegaGain, _muGain, _omegaRelLimit);
|
||||||
|
|
||||||
|
out = &recov.out;
|
||||||
|
|
||||||
|
generic_hier_block<PMDemod>::registerBlock(&agc);
|
||||||
|
generic_hier_block<PMDemod>::registerBlock(&pll);
|
||||||
|
generic_hier_block<PMDemod>::registerBlock(&rrc);
|
||||||
|
generic_hier_block<PMDemod>::registerBlock(&recov);
|
||||||
|
generic_hier_block<PMDemod>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* input) {
|
||||||
|
assert(generic_hier_block<PMDemod>::_block_init);
|
||||||
|
agc.setInput(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAgcRate(float agcRate) {
|
||||||
|
assert(generic_hier_block<PMDemod>::_block_init);
|
||||||
|
_agcRate = agcRate;
|
||||||
|
agc.setRate(_agcRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPllLoopBandwidth(float pllLoopBandwidth) {
|
||||||
|
assert(generic_hier_block<PMDemod>::_block_init);
|
||||||
|
_pllLoopBandwidth = pllLoopBandwidth;
|
||||||
|
pll.setLoopBandwidth(_pllLoopBandwidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRRCParams(int rrcTapCount, float rrcAlpha) {
|
||||||
|
assert(generic_hier_block<PMDemod>::_block_init);
|
||||||
|
_rrcTapCount = rrcTapCount;
|
||||||
|
_rrcAlpha = rrcAlpha;
|
||||||
|
rrcwin.setTapCount(_rrcTapCount);
|
||||||
|
rrcwin.setAlpha(_rrcAlpha);
|
||||||
|
rrc.updateWindow(&rrcwin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMMGains(float omegaGain, float muGain) {
|
||||||
|
assert(generic_hier_block<PMDemod>::_block_init);
|
||||||
|
_omegaGain = omegaGain;
|
||||||
|
_muGain = muGain;
|
||||||
|
recov.setGains(_omegaGain, _muGain);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOmegaRelLimit(float omegaRelLimit) {
|
||||||
|
assert(generic_hier_block<PMDemod>::_block_init);
|
||||||
|
_omegaRelLimit = omegaRelLimit;
|
||||||
|
recov.setOmegaRelLimit(_omegaRelLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<float>* out = NULL;
|
||||||
|
|
||||||
|
private:
|
||||||
|
dsp::ComplexAGC agc;
|
||||||
|
dsp::CarrierTrackingPLL<float> pll;
|
||||||
|
dsp::RRCTaps rrcwin;
|
||||||
|
dsp::FIR<float> rrc;
|
||||||
|
dsp::MMClockRecovery<float> recov;
|
||||||
|
|
||||||
|
float _sampleRate;
|
||||||
|
float _baudRate;
|
||||||
|
float _agcRate;
|
||||||
|
float _pllLoopBandwidth;
|
||||||
|
int _rrcTapCount;
|
||||||
|
float _rrcAlpha;
|
||||||
|
float _omegaGain;
|
||||||
|
float _muGain;
|
||||||
|
float _omegaRelLimit;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StereoFMDemod : public generic_hier_block<StereoFMDemod> {
|
||||||
|
public:
|
||||||
|
StereoFMDemod() {}
|
||||||
|
|
||||||
|
StereoFMDemod(stream<complex_t>* input, float sampleRate, float deviation) {
|
||||||
|
init(input, sampleRate, deviation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<complex_t>* input, float sampleRate, float deviation) {
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
|
||||||
|
PilotFirWin.init(18750, 19250, 3000, _sampleRate);
|
||||||
|
|
||||||
|
demod.init(input, _sampleRate, deviation);
|
||||||
|
|
||||||
|
r2c.init(&demod.out);
|
||||||
|
|
||||||
|
pilotFilter.init(&r2c.out, &PilotFirWin);
|
||||||
|
|
||||||
|
demux.init(&pilotFilter.dataOut, &pilotFilter.pilotOut, 0.1f);
|
||||||
|
|
||||||
|
recon.init(&demux.AplusBOut, &demux.AminusBOut);
|
||||||
|
|
||||||
|
out = &recon.out;
|
||||||
|
|
||||||
|
generic_hier_block<StereoFMDemod>::registerBlock(&demod);
|
||||||
|
generic_hier_block<StereoFMDemod>::registerBlock(&r2c);
|
||||||
|
generic_hier_block<StereoFMDemod>::registerBlock(&pilotFilter);
|
||||||
|
generic_hier_block<StereoFMDemod>::registerBlock(&demux);
|
||||||
|
generic_hier_block<StereoFMDemod>::registerBlock(&recon);
|
||||||
|
generic_hier_block<StereoFMDemod>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<float>* input) {
|
||||||
|
assert(generic_hier_block<StereoFMDemod>::_block_init);
|
||||||
|
r2c.setInput(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDeviation(float deviation) {
|
||||||
|
demod.setDeviation(deviation);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<stereo_t>* out = NULL;
|
||||||
|
|
||||||
|
private:
|
||||||
|
filter_window::BandPassBlackmanWindow PilotFirWin;
|
||||||
|
|
||||||
|
FloatFMDemod demod;
|
||||||
|
|
||||||
|
RealToComplex r2c;
|
||||||
|
|
||||||
|
FMStereoDemuxPilotFilter pilotFilter;
|
||||||
|
|
||||||
|
FMStereoDemux demux;
|
||||||
|
|
||||||
|
FMStereoReconstruct recon;
|
||||||
|
|
||||||
|
float _sampleRate;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
134
core/src/dsp/falcon_fec.h
Normal file
134
core/src/dsp/falcon_fec.h
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
// WTF???
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include <correct.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t toDB[] = {
|
||||||
|
0x00, 0x7b, 0xaf, 0xd4, 0x99, 0xe2, 0x36, 0x4d, 0xfa, 0x81, 0x55, 0x2e, 0x63, 0x18, 0xcc, 0xb7, 0x86, 0xfd, 0x29, 0x52, 0x1f,
|
||||||
|
0x64, 0xb0, 0xcb, 0x7c, 0x07, 0xd3, 0xa8, 0xe5, 0x9e, 0x4a, 0x31, 0xec, 0x97, 0x43, 0x38, 0x75, 0x0e, 0xda, 0xa1, 0x16, 0x6d, 0xb9, 0xc2, 0x8f, 0xf4,
|
||||||
|
0x20, 0x5b, 0x6a, 0x11, 0xc5, 0xbe, 0xf3, 0x88, 0x5c, 0x27, 0x90, 0xeb, 0x3f, 0x44, 0x09, 0x72, 0xa6, 0xdd, 0xef, 0x94, 0x40, 0x3b, 0x76, 0x0d, 0xd9,
|
||||||
|
0xa2, 0x15, 0x6e, 0xba, 0xc1, 0x8c, 0xf7, 0x23, 0x58, 0x69, 0x12, 0xc6, 0xbd, 0xf0, 0x8b, 0x5f, 0x24, 0x93, 0xe8, 0x3c, 0x47, 0x0a, 0x71, 0xa5, 0xde,
|
||||||
|
0x03, 0x78, 0xac, 0xd7, 0x9a, 0xe1, 0x35, 0x4e, 0xf9, 0x82, 0x56, 0x2d, 0x60, 0x1b, 0xcf, 0xb4, 0x85, 0xfe, 0x2a, 0x51, 0x1c, 0x67, 0xb3, 0xc8, 0x7f,
|
||||||
|
0x04, 0xd0, 0xab, 0xe6, 0x9d, 0x49, 0x32, 0x8d, 0xf6, 0x22, 0x59, 0x14, 0x6f, 0xbb, 0xc0, 0x77, 0x0c, 0xd8, 0xa3, 0xee, 0x95, 0x41, 0x3a, 0x0b, 0x70,
|
||||||
|
0xa4, 0xdf, 0x92, 0xe9, 0x3d, 0x46, 0xf1, 0x8a, 0x5e, 0x25, 0x68, 0x13, 0xc7, 0xbc, 0x61, 0x1a, 0xce, 0xb5, 0xf8, 0x83, 0x57, 0x2c, 0x9b, 0xe0, 0x34,
|
||||||
|
0x4f, 0x02, 0x79, 0xad, 0xd6, 0xe7, 0x9c, 0x48, 0x33, 0x7e, 0x05, 0xd1, 0xaa, 0x1d, 0x66, 0xb2, 0xc9, 0x84, 0xff, 0x2b, 0x50, 0x62, 0x19, 0xcd, 0xb6,
|
||||||
|
0xfb, 0x80, 0x54, 0x2f, 0x98, 0xe3, 0x37, 0x4c, 0x01, 0x7a, 0xae, 0xd5, 0xe4, 0x9f, 0x4b, 0x30, 0x7d, 0x06, 0xd2, 0xa9, 0x1e, 0x65, 0xb1, 0xca, 0x87,
|
||||||
|
0xfc, 0x28, 0x53, 0x8e, 0xf5, 0x21, 0x5a, 0x17, 0x6c, 0xb8, 0xc3, 0x74, 0x0f, 0xdb, 0xa0, 0xed, 0x96, 0x42, 0x39, 0x08, 0x73, 0xa7, 0xdc, 0x91, 0xea,
|
||||||
|
0x3e, 0x45, 0xf2, 0x89, 0x5d, 0x26, 0x6b, 0x10, 0xc4, 0xbf
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t fromDB[] = {
|
||||||
|
0x00, 0xcc, 0xac, 0x60, 0x79, 0xb5, 0xd5, 0x19, 0xf0, 0x3c, 0x5c, 0x90, 0x89, 0x45, 0x25, 0xe9, 0xfd, 0x31, 0x51, 0x9d,
|
||||||
|
0x84, 0x48, 0x28, 0xe4, 0x0d, 0xc1, 0xa1, 0x6d, 0x74, 0xb8, 0xd8, 0x14, 0x2e, 0xe2, 0x82, 0x4e, 0x57, 0x9b, 0xfb, 0x37, 0xde, 0x12, 0x72, 0xbe, 0xa7,
|
||||||
|
0x6b, 0x0b, 0xc7, 0xd3, 0x1f, 0x7f, 0xb3, 0xaa, 0x66, 0x06, 0xca, 0x23, 0xef, 0x8f, 0x43, 0x5a, 0x96, 0xf6, 0x3a, 0x42, 0x8e, 0xee, 0x22, 0x3b, 0xf7,
|
||||||
|
0x97, 0x5b, 0xb2, 0x7e, 0x1e, 0xd2, 0xcb, 0x07, 0x67, 0xab, 0xbf, 0x73, 0x13, 0xdf, 0xc6, 0x0a, 0x6a, 0xa6, 0x4f, 0x83, 0xe3, 0x2f, 0x36, 0xfa, 0x9a,
|
||||||
|
0x56, 0x6c, 0xa0, 0xc0, 0x0c, 0x15, 0xd9, 0xb9, 0x75, 0x9c, 0x50, 0x30, 0xfc, 0xe5, 0x29, 0x49, 0x85, 0x91, 0x5d, 0x3d, 0xf1, 0xe8, 0x24, 0x44, 0x88,
|
||||||
|
0x61, 0xad, 0xcd, 0x01, 0x18, 0xd4, 0xb4, 0x78, 0xc5, 0x09, 0x69, 0xa5, 0xbc, 0x70, 0x10, 0xdc, 0x35, 0xf9, 0x99, 0x55, 0x4c, 0x80, 0xe0, 0x2c, 0x38,
|
||||||
|
0xf4, 0x94, 0x58, 0x41, 0x8d, 0xed, 0x21, 0xc8, 0x04, 0x64, 0xa8, 0xb1, 0x7d, 0x1d, 0xd1, 0xeb, 0x27, 0x47, 0x8b, 0x92, 0x5e, 0x3e, 0xf2, 0x1b, 0xd7,
|
||||||
|
0xb7, 0x7b, 0x62, 0xae, 0xce, 0x02, 0x16, 0xda, 0xba, 0x76, 0x6f, 0xa3, 0xc3, 0x0f, 0xe6, 0x2a, 0x4a, 0x86, 0x9f, 0x53, 0x33, 0xff, 0x87, 0x4b, 0x2b,
|
||||||
|
0xe7, 0xfe, 0x32, 0x52, 0x9e, 0x77, 0xbb, 0xdb, 0x17, 0x0e, 0xc2, 0xa2, 0x6e, 0x7a, 0xb6, 0xd6, 0x1a, 0x03, 0xcf, 0xaf, 0x63, 0x8a, 0x46, 0x26, 0xea,
|
||||||
|
0xf3, 0x3f, 0x5f, 0x93, 0xa9, 0x65, 0x05, 0xc9, 0xd0, 0x1c, 0x7c, 0xb0, 0x59, 0x95, 0xf5, 0x39, 0x20, 0xec, 0x8c, 0x40, 0x54, 0x98, 0xf8, 0x34, 0x2d,
|
||||||
|
0xe1, 0x81, 0x4d, 0xa4, 0x68, 0x08, 0xc4, 0xdd, 0x11, 0x71, 0xbd
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t randVals[] = {
|
||||||
|
0xFF, 0x48, 0x0E, 0xC0, 0x9A, 0x0D, 0x70, 0xBC, 0x8E, 0x2C, 0x93, 0xAD, 0xA7, 0xB7, 0x46, 0xCE,
|
||||||
|
0x5A, 0x97, 0x7D, 0xCC, 0x32, 0xA2, 0xBF, 0x3E, 0x0A, 0x10, 0xF1, 0x88, 0x94, 0xCD, 0xEA, 0xB1,
|
||||||
|
0xFE, 0x90, 0x1D, 0x81, 0x34, 0x1A, 0xE1, 0x79, 0x1C, 0x59, 0x27, 0x5B, 0x4F, 0x6E, 0x8D, 0x9C,
|
||||||
|
0xB5, 0x2E, 0xFB, 0x98, 0x65, 0x45, 0x7E, 0x7C, 0x14, 0x21, 0xE3, 0x11, 0x29, 0x9B, 0xD5, 0x63,
|
||||||
|
0xFD, 0x20, 0x3B, 0x02, 0x68, 0x35, 0xC2, 0xF2, 0x38, 0xB2, 0x4E, 0xB6, 0x9E, 0xDD, 0x1B, 0x39,
|
||||||
|
0x6A, 0x5D, 0xF7, 0x30, 0xCA, 0x8A, 0xFC, 0xF8, 0x28, 0x43, 0xC6, 0x22, 0x53, 0x37, 0xAA, 0xC7,
|
||||||
|
0xFA, 0x40, 0x76, 0x04, 0xD0, 0x6B, 0x85, 0xE4, 0x71, 0x64, 0x9D, 0x6D, 0x3D, 0xBA, 0x36, 0x72,
|
||||||
|
0xD4, 0xBB, 0xEE, 0x61, 0x95, 0x15, 0xF9, 0xF0, 0x50, 0x87, 0x8C, 0x44, 0xA6, 0x6F, 0x55, 0x8F,
|
||||||
|
0xF4, 0x80, 0xEC, 0x09, 0xA0, 0xD7, 0x0B, 0xC8, 0xE2, 0xC9, 0x3A, 0xDA, 0x7B, 0x74, 0x6C, 0xE5,
|
||||||
|
0xA9, 0x77, 0xDC, 0xC3, 0x2A, 0x2B, 0xF3, 0xE0, 0xA1, 0x0F, 0x18, 0x89, 0x4C, 0xDE, 0xAB, 0x1F,
|
||||||
|
0xE9, 0x01, 0xD8, 0x13, 0x41, 0xAE, 0x17, 0x91, 0xC5, 0x92, 0x75, 0xB4, 0xF6, 0xE8, 0xD9, 0xCB,
|
||||||
|
0x52, 0xEF, 0xB9, 0x86, 0x54, 0x57, 0xE7, 0xC1, 0x42, 0x1E, 0x31, 0x12, 0x99, 0xBD, 0x56, 0x3F,
|
||||||
|
0xD2, 0x03, 0xB0, 0x26, 0x83, 0x5C, 0x2F, 0x23, 0x8B, 0x24, 0xEB, 0x69, 0xED, 0xD1, 0xB3, 0x96,
|
||||||
|
0xA5, 0xDF, 0x73, 0x0C, 0xA8, 0xAF, 0xCF, 0x82, 0x84, 0x3C, 0x62, 0x25, 0x33, 0x7A, 0xAC, 0x7F,
|
||||||
|
0xA4, 0x07, 0x60, 0x4D, 0x06, 0xB8, 0x5E, 0x47, 0x16, 0x49, 0xD6, 0xD3, 0xDB, 0xA3, 0x67, 0x2D,
|
||||||
|
0x4B, 0xBE, 0xE6, 0x19, 0x51, 0x5F, 0x9F, 0x05, 0x08, 0x78, 0xC4, 0x4A, 0x66, 0xF5, 0x58
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
class FalconRS : public generic_block<FalconRS> {
|
||||||
|
public:
|
||||||
|
FalconRS() {}
|
||||||
|
|
||||||
|
FalconRS(stream<uint8_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<uint8_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) { memset(buffers[i], 0, 255); }
|
||||||
|
for (int i = 0; i < 5; i++) { memset(outBuffers[i], 0, 255); }
|
||||||
|
rs = correct_reed_solomon_create(correct_rs_primitive_polynomial_ccsds, 120, 11, 16);
|
||||||
|
if (rs == NULL) { printf("Error creating the reed solomon decoder\n"); }
|
||||||
|
|
||||||
|
generic_block<FalconRS>::registerInput(_in);
|
||||||
|
generic_block<FalconRS>::registerOutput(&out);
|
||||||
|
generic_block<FalconRS>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<uint8_t>* in) {
|
||||||
|
assert(generic_block<FalconRS>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<FalconRS>::ctrlMtx);
|
||||||
|
generic_block<FalconRS>::tempStop();
|
||||||
|
generic_block<FalconRS>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<FalconRS>::registerInput(_in);
|
||||||
|
generic_block<FalconRS>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
uint8_t* data = _in->readBuf + 4;
|
||||||
|
|
||||||
|
// Deinterleave
|
||||||
|
for (int i = 0; i < 255*5; i++) {
|
||||||
|
buffers[i%5][i/5] = fromDB[data[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reed the solomon :weary:
|
||||||
|
int result = 0;
|
||||||
|
result = correct_reed_solomon_decode(rs, buffers[0], 255, outBuffers[0]);
|
||||||
|
if (result == -1) { _in->flush(); return count; }
|
||||||
|
result = correct_reed_solomon_decode(rs, buffers[1], 255, outBuffers[1]);
|
||||||
|
if (result == -1) { _in->flush(); return count; }
|
||||||
|
result = correct_reed_solomon_decode(rs, buffers[2], 255, outBuffers[2]);
|
||||||
|
if (result == -1) { _in->flush(); return count; }
|
||||||
|
result = correct_reed_solomon_decode(rs, buffers[3], 255, outBuffers[3]);
|
||||||
|
if (result == -1) { _in->flush(); return count; }
|
||||||
|
result = correct_reed_solomon_decode(rs, buffers[4], 255, outBuffers[4]);
|
||||||
|
if (result == -1) { _in->flush(); return count; }
|
||||||
|
|
||||||
|
// Reinterleave
|
||||||
|
for (int i = 0; i < 255*5; i++) {
|
||||||
|
out.writeBuf[i] = toDB[outBuffers[i%5][i/5]] ^ randVals[i % 255];
|
||||||
|
}
|
||||||
|
|
||||||
|
out.swap(255*5);
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<uint8_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int count;
|
||||||
|
uint8_t buffers[5][255];
|
||||||
|
uint8_t outBuffers[5][255];
|
||||||
|
correct_reed_solomon* rs;
|
||||||
|
|
||||||
|
stream<uint8_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
128
core/src/dsp/falcon_packet.h
Normal file
128
core/src/dsp/falcon_packet.h
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
struct FalconFrameHeader {
|
||||||
|
uint32_t counter;
|
||||||
|
uint16_t packet;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FalconPacketSync : public generic_block<FalconPacketSync> {
|
||||||
|
public:
|
||||||
|
FalconPacketSync() {}
|
||||||
|
|
||||||
|
FalconPacketSync(stream<uint8_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<uint8_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
|
||||||
|
generic_block<FalconPacketSync>::registerInput(_in);
|
||||||
|
generic_block<FalconPacketSync>::registerOutput(&out);
|
||||||
|
generic_block<FalconPacketSync>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<uint8_t>* in) {
|
||||||
|
assert(generic_block<FalconPacketSync>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<FalconPacketSync>::ctrlMtx);
|
||||||
|
generic_block<FalconPacketSync>::tempStop();
|
||||||
|
generic_block<FalconPacketSync>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<FalconPacketSync>::registerInput(_in);
|
||||||
|
generic_block<FalconPacketSync>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
// Parse frame header
|
||||||
|
FalconFrameHeader header;
|
||||||
|
header.packet = (_in->readBuf[3] | ((_in->readBuf[2] & 0b111) << 8));
|
||||||
|
header.counter = ((_in->readBuf[2] >> 3) | (_in->readBuf[1] << 5) | ((_in->readBuf[0] & 0b111111) << 13));
|
||||||
|
|
||||||
|
// Pointer to the data aera of the frame
|
||||||
|
uint8_t* data = _in->readBuf + 4;
|
||||||
|
int dataLen = 1191;
|
||||||
|
|
||||||
|
// If a frame was missed, cancel reading the current packet
|
||||||
|
if (lastCounter + 1 != header.counter) {
|
||||||
|
packetRead = -1;
|
||||||
|
}
|
||||||
|
lastCounter = header.counter;
|
||||||
|
|
||||||
|
// If frame is just a continuation of a single packet, save it
|
||||||
|
// If we're not currently reading a packet
|
||||||
|
if (header.packet == 2047 && packetRead >= 0) {
|
||||||
|
memcpy(packet + packetRead, data, dataLen);
|
||||||
|
packetRead += dataLen;
|
||||||
|
_in->flush();
|
||||||
|
printf("Wow, all data\n");
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
else if (header.packet == 2047) {
|
||||||
|
printf("Wow, all data\n");
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish reading the last package and send it
|
||||||
|
if (packetRead >= 0) {
|
||||||
|
memcpy(packet + packetRead, data, header.packet);
|
||||||
|
memcpy(out.writeBuf, packet, packetRead + header.packet);
|
||||||
|
out.swap(packetRead + header.packet);
|
||||||
|
packetRead = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through every packet of the frame
|
||||||
|
for (int i = header.packet; i < dataLen;) {
|
||||||
|
// First, check if we can read the header. If not, save and wait for next frame
|
||||||
|
if (dataLen - i < 4) {
|
||||||
|
packetRead = dataLen - i;
|
||||||
|
memcpy(packet, &data[i], packetRead);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract packet length
|
||||||
|
uint16_t length = (((data[i] & 0b1111) << 8) | data[i + 1]) + 2;
|
||||||
|
|
||||||
|
// Check if it's not an invalid zero length packet
|
||||||
|
if (length <= 2) {
|
||||||
|
packetRead = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t pktId = ((uint64_t)data[i + 2] << 56) | ((uint64_t)data[i + 3] << 48) | ((uint64_t)data[i + 4] << 40) | ((uint64_t)data[i + 5] << 32)
|
||||||
|
| ((uint64_t)data[i + 6] << 24) | ((uint64_t)data[i + 7] << 16) | ((uint64_t)data[i + 8] << 8) | data[i + 9];
|
||||||
|
|
||||||
|
// If the packet doesn't fit the frame, save and go to next frame
|
||||||
|
if (dataLen - i < length) {
|
||||||
|
packetRead = dataLen - i;
|
||||||
|
memcpy(packet, &data[i], packetRead);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here, the package fits fully, read it and jump to the next
|
||||||
|
memcpy(out.writeBuf, &data[i], length);
|
||||||
|
out.swap(length);
|
||||||
|
i += length;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<uint8_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int count;
|
||||||
|
uint32_t lastCounter = 0;
|
||||||
|
|
||||||
|
int packetRead = -1;
|
||||||
|
uint8_t packet[0x4008];
|
||||||
|
|
||||||
|
stream<uint8_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -13,9 +13,11 @@ namespace dsp {
|
|||||||
FIR(stream<T>* in, dsp::filter_window::generic_window* window) { init(in, window); }
|
FIR(stream<T>* in, dsp::filter_window::generic_window* window) { init(in, window); }
|
||||||
|
|
||||||
~FIR() {
|
~FIR() {
|
||||||
|
if (!generic_block<FIR<T>>::_block_init) { return; }
|
||||||
generic_block<FIR<T>>::stop();
|
generic_block<FIR<T>>::stop();
|
||||||
volk_free(buffer);
|
volk_free(buffer);
|
||||||
volk_free(taps);
|
volk_free(taps);
|
||||||
|
generic_block<FIR<T>>::_block_init = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(stream<T>* in, dsp::filter_window::generic_window* window) {
|
void init(stream<T>* in, dsp::filter_window::generic_window* window) {
|
||||||
@@ -29,9 +31,11 @@ namespace dsp {
|
|||||||
bufStart = &buffer[tapCount];
|
bufStart = &buffer[tapCount];
|
||||||
generic_block<FIR<T>>::registerInput(_in);
|
generic_block<FIR<T>>::registerInput(_in);
|
||||||
generic_block<FIR<T>>::registerOutput(&out);
|
generic_block<FIR<T>>::registerOutput(&out);
|
||||||
|
generic_block<FIR<T>>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<T>* in) {
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<FIR<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<FIR<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<FIR<T>>::ctrlMtx);
|
||||||
generic_block<FIR<T>>::tempStop();
|
generic_block<FIR<T>>::tempStop();
|
||||||
generic_block<FIR<T>>::unregisterInput(_in);
|
generic_block<FIR<T>>::unregisterInput(_in);
|
||||||
@@ -41,45 +45,48 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateWindow(dsp::filter_window::generic_window* window) {
|
void updateWindow(dsp::filter_window::generic_window* window) {
|
||||||
|
assert(generic_block<FIR<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<FIR<T>>::ctrlMtx);
|
||||||
_window = window;
|
_window = window;
|
||||||
volk_free(taps);
|
volk_free(taps);
|
||||||
tapCount = window->getTapCount();
|
tapCount = window->getTapCount();
|
||||||
taps = (float*)volk_malloc(tapCount * sizeof(float), volk_get_alignment());
|
taps = (float*)volk_malloc(tapCount * sizeof(float), volk_get_alignment());
|
||||||
|
bufStart = &buffer[tapCount];
|
||||||
window->createTaps(taps, tapCount);
|
window->createTaps(taps, tapCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
memcpy(bufStart, _in->data, count * sizeof(T));
|
generic_block<FIR<T>>::ctrlMtx.lock();
|
||||||
_in->flush();
|
|
||||||
|
|
||||||
// Write to output
|
memcpy(bufStart, _in->readBuf, count * sizeof(T));
|
||||||
if (out.aquire() < 0) { return -1; }
|
_in->flush();
|
||||||
|
|
||||||
if constexpr (std::is_same_v<T, float>) {
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
volk_32f_x2_dot_prod_32f((float*)&out.data[i], (float*)&buffer[i+1], taps, tapCount);
|
volk_32f_x2_dot_prod_32f((float*)&out.writeBuf[i], (float*)&buffer[i+1], taps, tapCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if constexpr (std::is_same_v<T, complex_t>) {
|
if constexpr (std::is_same_v<T, complex_t>) {
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&out.data[i], (lv_32fc_t*)&buffer[i+1], taps, tapCount);
|
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&out.writeBuf[i], (lv_32fc_t*)&buffer[i+1], taps, tapCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
|
|
||||||
memmove(buffer, &buffer[count], tapCount * sizeof(T));
|
memmove(buffer, &buffer[count], tapCount * sizeof(T));
|
||||||
|
|
||||||
|
generic_block<FIR<T>>::ctrlMtx.unlock();
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<T> out;
|
stream<T> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
|
||||||
stream<T>* _in;
|
stream<T>* _in;
|
||||||
|
|
||||||
dsp::filter_window::generic_window* _window;
|
dsp::filter_window::generic_window* _window;
|
||||||
@@ -91,15 +98,98 @@ namespace dsp {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ComplexFIR : public generic_block<ComplexFIR> {
|
||||||
|
public:
|
||||||
|
ComplexFIR() {}
|
||||||
|
|
||||||
|
ComplexFIR(stream<complex_t>* in, dsp::filter_window::generic_complex_window* window) { init(in, window); }
|
||||||
|
|
||||||
|
~ComplexFIR() {
|
||||||
|
if (!generic_block<ComplexFIR>::_block_init) { return; }
|
||||||
|
generic_block<ComplexFIR>::stop();
|
||||||
|
volk_free(buffer);
|
||||||
|
volk_free(taps);
|
||||||
|
generic_block<ComplexFIR>::_block_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<complex_t>* in, dsp::filter_window::generic_complex_window* window) {
|
||||||
|
_in = in;
|
||||||
|
|
||||||
|
tapCount = window->getTapCount();
|
||||||
|
taps = (complex_t*)volk_malloc(tapCount * sizeof(complex_t), volk_get_alignment());
|
||||||
|
window->createTaps(taps, tapCount);
|
||||||
|
|
||||||
|
buffer = (complex_t*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(complex_t) * 2, volk_get_alignment());
|
||||||
|
bufStart = &buffer[tapCount];
|
||||||
|
generic_block<ComplexFIR>::registerInput(_in);
|
||||||
|
generic_block<ComplexFIR>::registerOutput(&out);
|
||||||
|
generic_block<ComplexFIR>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<ComplexFIR>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<ComplexFIR>::ctrlMtx);
|
||||||
|
generic_block<ComplexFIR>::tempStop();
|
||||||
|
generic_block<ComplexFIR>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<ComplexFIR>::registerInput(_in);
|
||||||
|
generic_block<ComplexFIR>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateWindow(dsp::filter_window::generic_complex_window* window) {
|
||||||
|
assert(generic_block<ComplexFIR>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<ComplexFIR>::ctrlMtx);
|
||||||
|
_window = window;
|
||||||
|
volk_free(taps);
|
||||||
|
tapCount = window->getTapCount();
|
||||||
|
taps = (complex_t*)volk_malloc(tapCount * sizeof(complex_t), volk_get_alignment());
|
||||||
|
bufStart = &buffer[tapCount];
|
||||||
|
window->createTaps(taps, tapCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
generic_block<ComplexFIR>::ctrlMtx.lock();
|
||||||
|
|
||||||
|
memcpy(bufStart, _in->readBuf, count * sizeof(complex_t));
|
||||||
|
_in->flush();
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
volk_32fc_x2_dot_prod_32fc((lv_32fc_t*)&out.writeBuf[i], (lv_32fc_t*)&buffer[i+1], (lv_32fc_t*)taps, tapCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
|
||||||
|
memmove(buffer, &buffer[count], tapCount * sizeof(complex_t));
|
||||||
|
|
||||||
|
generic_block<ComplexFIR>::ctrlMtx.unlock();
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<complex_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
|
dsp::filter_window::generic_complex_window* _window;
|
||||||
|
|
||||||
|
complex_t* bufStart;
|
||||||
|
complex_t* buffer;
|
||||||
|
int tapCount;
|
||||||
|
complex_t* taps;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class BFMDeemp : public generic_block<BFMDeemp> {
|
class BFMDeemp : public generic_block<BFMDeemp> {
|
||||||
public:
|
public:
|
||||||
BFMDeemp() {}
|
BFMDeemp() {}
|
||||||
|
|
||||||
BFMDeemp(stream<float>* in, float sampleRate, float tau) { init(in, sampleRate, tau); }
|
BFMDeemp(stream<stereo_t>* in, float sampleRate, float tau) { init(in, sampleRate, tau); }
|
||||||
|
|
||||||
~BFMDeemp() { generic_block<BFMDeemp>::stop(); }
|
void init(stream<stereo_t>* in, float sampleRate, float tau) {
|
||||||
|
|
||||||
void init(stream<float>* in, float sampleRate, float tau) {
|
|
||||||
_in = in;
|
_in = in;
|
||||||
_sampleRate = sampleRate;
|
_sampleRate = sampleRate;
|
||||||
_tau = tau;
|
_tau = tau;
|
||||||
@@ -107,9 +197,11 @@ namespace dsp {
|
|||||||
alpha = dt / (_tau + dt);
|
alpha = dt / (_tau + dt);
|
||||||
generic_block<BFMDeemp>::registerInput(_in);
|
generic_block<BFMDeemp>::registerInput(_in);
|
||||||
generic_block<BFMDeemp>::registerOutput(&out);
|
generic_block<BFMDeemp>::registerOutput(&out);
|
||||||
|
generic_block<BFMDeemp>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<float>* in) {
|
void setInput(stream<stereo_t>* in) {
|
||||||
|
assert(generic_block<BFMDeemp>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<BFMDeemp>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<BFMDeemp>::ctrlMtx);
|
||||||
generic_block<BFMDeemp>::tempStop();
|
generic_block<BFMDeemp>::tempStop();
|
||||||
generic_block<BFMDeemp>::unregisterInput(_in);
|
generic_block<BFMDeemp>::unregisterInput(_in);
|
||||||
@@ -119,12 +211,14 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setSampleRate(float sampleRate) {
|
void setSampleRate(float sampleRate) {
|
||||||
|
assert(generic_block<BFMDeemp>::_block_init);
|
||||||
_sampleRate = sampleRate;
|
_sampleRate = sampleRate;
|
||||||
float dt = 1.0f / _sampleRate;
|
float dt = 1.0f / _sampleRate;
|
||||||
alpha = dt / (_tau + dt);
|
alpha = dt / (_tau + dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTau(float tau) {
|
void setTau(float tau) {
|
||||||
|
assert(generic_block<BFMDeemp>::_block_init);
|
||||||
_tau = tau;
|
_tau = tau;
|
||||||
float dt = 1.0f / _sampleRate;
|
float dt = 1.0f / _sampleRate;
|
||||||
alpha = dt / (_tau + dt);
|
alpha = dt / (_tau + dt);
|
||||||
@@ -135,39 +229,44 @@ namespace dsp {
|
|||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
if (bypass) {
|
if (bypass) {
|
||||||
if (out.aquire() < 0) { return -1; }
|
memcpy(out.writeBuf, _in->readBuf, count * sizeof(stereo_t));
|
||||||
memcpy(out.data, _in->data, count * sizeof(float));
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isnan(lastOut)) {
|
if (isnan(lastOutL)) {
|
||||||
lastOut = 0.0f;
|
lastOutL = 0.0f;
|
||||||
}
|
}
|
||||||
if (out.aquire() < 0) { return -1; }
|
if (isnan(lastOutR)) {
|
||||||
out.data[0] = (alpha * _in->data[0]) + ((1-alpha) * lastOut);
|
lastOutR = 0.0f;
|
||||||
|
}
|
||||||
|
out.writeBuf[0].l = (alpha * _in->readBuf[0].l) + ((1-alpha) * lastOutL);
|
||||||
|
out.writeBuf[0].r = (alpha * _in->readBuf[0].r) + ((1-alpha) * lastOutR);
|
||||||
for (int i = 1; i < count; i++) {
|
for (int i = 1; i < count; i++) {
|
||||||
out.data[i] = (alpha * _in->data[i]) + ((1 - alpha) * out.data[i - 1]);
|
out.writeBuf[i].l = (alpha * _in->readBuf[i].l) + ((1 - alpha) * out.writeBuf[i - 1].l);
|
||||||
|
out.writeBuf[i].r = (alpha * _in->readBuf[i].r) + ((1 - alpha) * out.writeBuf[i - 1].r);
|
||||||
}
|
}
|
||||||
lastOut = out.data[count - 1];
|
lastOutL = out.writeBuf[count - 1].l;
|
||||||
|
lastOutR = out.writeBuf[count - 1].r;
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bypass = false;
|
bool bypass = false;
|
||||||
|
|
||||||
stream<float> out;
|
stream<stereo_t> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
int count;
|
||||||
float lastOut = 0.0f;
|
float lastOutL = 0.0f;
|
||||||
|
float lastOutR = 0.0f;
|
||||||
float alpha;
|
float alpha;
|
||||||
float _tau;
|
float _tau;
|
||||||
float _sampleRate;
|
float _sampleRate;
|
||||||
stream<float>* _in;
|
stream<stereo_t>* _in;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
136
core/src/dsp/interpolation_taps.h
Normal file
136
core/src/dsp/interpolation_taps.h
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
const int INTERP_TAP_COUNT = 8;
|
||||||
|
const int INTERP_STEPS = 128;
|
||||||
|
|
||||||
|
const float INTERP_TAPS[INTERP_STEPS + 1][INTERP_TAP_COUNT] = {
|
||||||
|
{ 0.00000e+00, 0.00000e+00, 0.00000e+00, 1.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00 },
|
||||||
|
{ -1.98993e-04, 1.24642e-03, -5.41054e-03, 9.98534e-01, 7.89295e-03, -2.76968e-03, 8.53777e-04, -1.54700e-04 },
|
||||||
|
{ -3.96391e-04, 2.47942e-03, -1.07209e-02, 9.96891e-01, 1.58840e-02, -5.55134e-03, 1.70888e-03, -3.09412e-04 },
|
||||||
|
{ -5.92100e-04, 3.69852e-03, -1.59305e-02, 9.95074e-01, 2.39714e-02, -8.34364e-03, 2.56486e-03, -4.64053e-04 },
|
||||||
|
{ -7.86031e-04, 4.90322e-03, -2.10389e-02, 9.93082e-01, 3.21531e-02, -1.11453e-02, 3.42130e-03, -6.18544e-04 },
|
||||||
|
{ -9.78093e-04, 6.09305e-03, -2.60456e-02, 9.90917e-01, 4.04274e-02, -1.39548e-02, 4.27773e-03, -7.72802e-04 },
|
||||||
|
{ -1.16820e-03, 7.26755e-03, -3.09503e-02, 9.88580e-01, 4.87921e-02, -1.67710e-02, 5.13372e-03, -9.26747e-04 },
|
||||||
|
{ -1.35627e-03, 8.42626e-03, -3.57525e-02, 9.86071e-01, 5.72454e-02, -1.95925e-02, 5.98883e-03, -1.08030e-03 },
|
||||||
|
{ -1.54221e-03, 9.56876e-03, -4.04519e-02, 9.83392e-01, 6.57852e-02, -2.24178e-02, 6.84261e-03, -1.23337e-03 },
|
||||||
|
{ -1.72594e-03, 1.06946e-02, -4.50483e-02, 9.80543e-01, 7.44095e-02, -2.52457e-02, 7.69462e-03, -1.38589e-03 },
|
||||||
|
{ -1.90738e-03, 1.18034e-02, -4.95412e-02, 9.77526e-01, 8.31162e-02, -2.80746e-02, 8.54441e-03, -1.53777e-03 },
|
||||||
|
{ -2.08645e-03, 1.28947e-02, -5.39305e-02, 9.74342e-01, 9.19033e-02, -3.09033e-02, 9.39154e-03, -1.68894e-03 },
|
||||||
|
{ -2.26307e-03, 1.39681e-02, -5.82159e-02, 9.70992e-01, 1.00769e-01, -3.37303e-02, 1.02356e-02, -1.83931e-03 },
|
||||||
|
{ -2.43718e-03, 1.50233e-02, -6.23972e-02, 9.67477e-01, 1.09710e-01, -3.65541e-02, 1.10760e-02, -1.98880e-03 },
|
||||||
|
{ -2.60868e-03, 1.60599e-02, -6.64743e-02, 9.63798e-01, 1.18725e-01, -3.93735e-02, 1.19125e-02, -2.13733e-03 },
|
||||||
|
{ -2.77751e-03, 1.70776e-02, -7.04471e-02, 9.59958e-01, 1.27812e-01, -4.21869e-02, 1.27445e-02, -2.28483e-03 },
|
||||||
|
{ -2.94361e-03, 1.80759e-02, -7.43154e-02, 9.55956e-01, 1.36968e-01, -4.49929e-02, 1.35716e-02, -2.43121e-03 },
|
||||||
|
{ -3.10689e-03, 1.90545e-02, -7.80792e-02, 9.51795e-01, 1.46192e-01, -4.77900e-02, 1.43934e-02, -2.57640e-03 },
|
||||||
|
{ -3.26730e-03, 2.00132e-02, -8.17385e-02, 9.47477e-01, 1.55480e-01, -5.05770e-02, 1.52095e-02, -2.72032e-03 },
|
||||||
|
{ -3.42477e-03, 2.09516e-02, -8.52933e-02, 9.43001e-01, 1.64831e-01, -5.33522e-02, 1.60193e-02, -2.86289e-03 },
|
||||||
|
{ -3.57923e-03, 2.18695e-02, -8.87435e-02, 9.38371e-01, 1.74242e-01, -5.61142e-02, 1.68225e-02, -3.00403e-03 },
|
||||||
|
{ -3.73062e-03, 2.27664e-02, -9.20893e-02, 9.33586e-01, 1.83711e-01, -5.88617e-02, 1.76185e-02, -3.14367e-03 },
|
||||||
|
{ -3.87888e-03, 2.36423e-02, -9.53307e-02, 9.28650e-01, 1.93236e-01, -6.15931e-02, 1.84071e-02, -3.28174e-03 },
|
||||||
|
{ -4.02397e-03, 2.44967e-02, -9.84679e-02, 9.23564e-01, 2.02814e-01, -6.43069e-02, 1.91877e-02, -3.41815e-03 },
|
||||||
|
{ -4.16581e-03, 2.53295e-02, -1.01501e-01, 9.18329e-01, 2.12443e-01, -6.70018e-02, 1.99599e-02, -3.55283e-03 },
|
||||||
|
{ -4.30435e-03, 2.61404e-02, -1.04430e-01, 9.12947e-01, 2.22120e-01, -6.96762e-02, 2.07233e-02, -3.68570e-03 },
|
||||||
|
{ -4.43955e-03, 2.69293e-02, -1.07256e-01, 9.07420e-01, 2.31843e-01, -7.23286e-02, 2.14774e-02, -3.81671e-03 },
|
||||||
|
{ -4.57135e-03, 2.76957e-02, -1.09978e-01, 9.01749e-01, 2.41609e-01, -7.49577e-02, 2.22218e-02, -3.94576e-03 },
|
||||||
|
{ -4.69970e-03, 2.84397e-02, -1.12597e-01, 8.95936e-01, 2.51417e-01, -7.75620e-02, 2.29562e-02, -4.07279e-03 },
|
||||||
|
{ -4.82456e-03, 2.91609e-02, -1.15113e-01, 8.89984e-01, 2.61263e-01, -8.01399e-02, 2.36801e-02, -4.19774e-03 },
|
||||||
|
{ -4.94589e-03, 2.98593e-02, -1.17526e-01, 8.83893e-01, 2.71144e-01, -8.26900e-02, 2.43930e-02, -4.32052e-03 },
|
||||||
|
{ -5.06363e-03, 3.05345e-02, -1.19837e-01, 8.77666e-01, 2.81060e-01, -8.52109e-02, 2.50946e-02, -4.44107e-03 },
|
||||||
|
{ -5.17776e-03, 3.11866e-02, -1.22047e-01, 8.71305e-01, 2.91006e-01, -8.77011e-02, 2.57844e-02, -4.55932e-03 },
|
||||||
|
{ -5.28823e-03, 3.18153e-02, -1.24154e-01, 8.64812e-01, 3.00980e-01, -9.01591e-02, 2.64621e-02, -4.67520e-03 },
|
||||||
|
{ -5.39500e-03, 3.24205e-02, -1.26161e-01, 8.58189e-01, 3.10980e-01, -9.25834e-02, 2.71272e-02, -4.78866e-03 },
|
||||||
|
{ -5.49804e-03, 3.30021e-02, -1.28068e-01, 8.51437e-01, 3.21004e-01, -9.49727e-02, 2.77794e-02, -4.89961e-03 },
|
||||||
|
{ -5.59731e-03, 3.35600e-02, -1.29874e-01, 8.44559e-01, 3.31048e-01, -9.73254e-02, 2.84182e-02, -5.00800e-03 },
|
||||||
|
{ -5.69280e-03, 3.40940e-02, -1.31581e-01, 8.37557e-01, 3.41109e-01, -9.96402e-02, 2.90433e-02, -5.11376e-03 },
|
||||||
|
{ -5.78446e-03, 3.46042e-02, -1.33189e-01, 8.30432e-01, 3.51186e-01, -1.01915e-01, 2.96543e-02, -5.21683e-03 },
|
||||||
|
{ -5.87227e-03, 3.50903e-02, -1.34699e-01, 8.23188e-01, 3.61276e-01, -1.04150e-01, 3.02507e-02, -5.31716e-03 },
|
||||||
|
{ -5.95620e-03, 3.55525e-02, -1.36111e-01, 8.15826e-01, 3.71376e-01, -1.06342e-01, 3.08323e-02, -5.41467e-03 },
|
||||||
|
{ -6.03624e-03, 3.59905e-02, -1.37426e-01, 8.08348e-01, 3.81484e-01, -1.08490e-01, 3.13987e-02, -5.50931e-03 },
|
||||||
|
{ -6.11236e-03, 3.64044e-02, -1.38644e-01, 8.00757e-01, 3.91596e-01, -1.10593e-01, 3.19495e-02, -5.60103e-03 },
|
||||||
|
{ -6.18454e-03, 3.67941e-02, -1.39767e-01, 7.93055e-01, 4.01710e-01, -1.12650e-01, 3.24843e-02, -5.68976e-03 },
|
||||||
|
{ -6.25277e-03, 3.71596e-02, -1.40794e-01, 7.85244e-01, 4.11823e-01, -1.14659e-01, 3.30027e-02, -5.77544e-03 },
|
||||||
|
{ -6.31703e-03, 3.75010e-02, -1.41727e-01, 7.77327e-01, 4.21934e-01, -1.16618e-01, 3.35046e-02, -5.85804e-03 },
|
||||||
|
{ -6.37730e-03, 3.78182e-02, -1.42566e-01, 7.69305e-01, 4.32038e-01, -1.18526e-01, 3.39894e-02, -5.93749e-03 },
|
||||||
|
{ -6.43358e-03, 3.81111e-02, -1.43313e-01, 7.61181e-01, 4.42134e-01, -1.20382e-01, 3.44568e-02, -6.01374e-03 },
|
||||||
|
{ -6.48585e-03, 3.83800e-02, -1.43968e-01, 7.52958e-01, 4.52218e-01, -1.22185e-01, 3.49066e-02, -6.08674e-03 },
|
||||||
|
{ -6.53412e-03, 3.86247e-02, -1.44531e-01, 7.44637e-01, 4.62289e-01, -1.23933e-01, 3.53384e-02, -6.15644e-03 },
|
||||||
|
{ -6.57836e-03, 3.88454e-02, -1.45004e-01, 7.36222e-01, 4.72342e-01, -1.25624e-01, 3.57519e-02, -6.22280e-03 },
|
||||||
|
{ -6.61859e-03, 3.90420e-02, -1.45387e-01, 7.27714e-01, 4.82377e-01, -1.27258e-01, 3.61468e-02, -6.28577e-03 },
|
||||||
|
{ -6.65479e-03, 3.92147e-02, -1.45682e-01, 7.19116e-01, 4.92389e-01, -1.28832e-01, 3.65227e-02, -6.34530e-03 },
|
||||||
|
{ -6.68698e-03, 3.93636e-02, -1.45889e-01, 7.10431e-01, 5.02377e-01, -1.30347e-01, 3.68795e-02, -6.40135e-03 },
|
||||||
|
{ -6.71514e-03, 3.94886e-02, -1.46009e-01, 7.01661e-01, 5.12337e-01, -1.31800e-01, 3.72167e-02, -6.45388e-03 },
|
||||||
|
{ -6.73929e-03, 3.95900e-02, -1.46043e-01, 6.92808e-01, 5.22267e-01, -1.33190e-01, 3.75341e-02, -6.50285e-03 },
|
||||||
|
{ -6.75943e-03, 3.96678e-02, -1.45993e-01, 6.83875e-01, 5.32164e-01, -1.34515e-01, 3.78315e-02, -6.54823e-03 },
|
||||||
|
{ -6.77557e-03, 3.97222e-02, -1.45859e-01, 6.74865e-01, 5.42025e-01, -1.35775e-01, 3.81085e-02, -6.58996e-03 },
|
||||||
|
{ -6.78771e-03, 3.97532e-02, -1.45641e-01, 6.65779e-01, 5.51849e-01, -1.36969e-01, 3.83650e-02, -6.62802e-03 },
|
||||||
|
{ -6.79588e-03, 3.97610e-02, -1.45343e-01, 6.56621e-01, 5.61631e-01, -1.38094e-01, 3.86006e-02, -6.66238e-03 },
|
||||||
|
{ -6.80007e-03, 3.97458e-02, -1.44963e-01, 6.47394e-01, 5.71370e-01, -1.39150e-01, 3.88151e-02, -6.69300e-03 },
|
||||||
|
{ -6.80032e-03, 3.97077e-02, -1.44503e-01, 6.38099e-01, 5.81063e-01, -1.40136e-01, 3.90083e-02, -6.71985e-03 },
|
||||||
|
{ -6.79662e-03, 3.96469e-02, -1.43965e-01, 6.28739e-01, 5.90706e-01, -1.41050e-01, 3.91800e-02, -6.74291e-03 },
|
||||||
|
{ -6.78902e-03, 3.95635e-02, -1.43350e-01, 6.19318e-01, 6.00298e-01, -1.41891e-01, 3.93299e-02, -6.76214e-03 },
|
||||||
|
{ -6.77751e-03, 3.94578e-02, -1.42658e-01, 6.09836e-01, 6.09836e-01, -1.42658e-01, 3.94578e-02, -6.77751e-03 },
|
||||||
|
{ -6.76214e-03, 3.93299e-02, -1.41891e-01, 6.00298e-01, 6.19318e-01, -1.43350e-01, 3.95635e-02, -6.78902e-03 },
|
||||||
|
{ -6.74291e-03, 3.91800e-02, -1.41050e-01, 5.90706e-01, 6.28739e-01, -1.43965e-01, 3.96469e-02, -6.79662e-03 },
|
||||||
|
{ -6.71985e-03, 3.90083e-02, -1.40136e-01, 5.81063e-01, 6.38099e-01, -1.44503e-01, 3.97077e-02, -6.80032e-03 },
|
||||||
|
{ -6.69300e-03, 3.88151e-02, -1.39150e-01, 5.71370e-01, 6.47394e-01, -1.44963e-01, 3.97458e-02, -6.80007e-03 },
|
||||||
|
{ -6.66238e-03, 3.86006e-02, -1.38094e-01, 5.61631e-01, 6.56621e-01, -1.45343e-01, 3.97610e-02, -6.79588e-03 },
|
||||||
|
{ -6.62802e-03, 3.83650e-02, -1.36969e-01, 5.51849e-01, 6.65779e-01, -1.45641e-01, 3.97532e-02, -6.78771e-03 },
|
||||||
|
{ -6.58996e-03, 3.81085e-02, -1.35775e-01, 5.42025e-01, 6.74865e-01, -1.45859e-01, 3.97222e-02, -6.77557e-03 },
|
||||||
|
{ -6.54823e-03, 3.78315e-02, -1.34515e-01, 5.32164e-01, 6.83875e-01, -1.45993e-01, 3.96678e-02, -6.75943e-03 },
|
||||||
|
{ -6.50285e-03, 3.75341e-02, -1.33190e-01, 5.22267e-01, 6.92808e-01, -1.46043e-01, 3.95900e-02, -6.73929e-03 },
|
||||||
|
{ -6.45388e-03, 3.72167e-02, -1.31800e-01, 5.12337e-01, 7.01661e-01, -1.46009e-01, 3.94886e-02, -6.71514e-03 },
|
||||||
|
{ -6.40135e-03, 3.68795e-02, -1.30347e-01, 5.02377e-01, 7.10431e-01, -1.45889e-01, 3.93636e-02, -6.68698e-03 },
|
||||||
|
{ -6.34530e-03, 3.65227e-02, -1.28832e-01, 4.92389e-01, 7.19116e-01, -1.45682e-01, 3.92147e-02, -6.65479e-03 },
|
||||||
|
{ -6.28577e-03, 3.61468e-02, -1.27258e-01, 4.82377e-01, 7.27714e-01, -1.45387e-01, 3.90420e-02, -6.61859e-03 },
|
||||||
|
{ -6.22280e-03, 3.57519e-02, -1.25624e-01, 4.72342e-01, 7.36222e-01, -1.45004e-01, 3.88454e-02, -6.57836e-03 },
|
||||||
|
{ -6.15644e-03, 3.53384e-02, -1.23933e-01, 4.62289e-01, 7.44637e-01, -1.44531e-01, 3.86247e-02, -6.53412e-03 },
|
||||||
|
{ -6.08674e-03, 3.49066e-02, -1.22185e-01, 4.52218e-01, 7.52958e-01, -1.43968e-01, 3.83800e-02, -6.48585e-03 },
|
||||||
|
{ -6.01374e-03, 3.44568e-02, -1.20382e-01, 4.42134e-01, 7.61181e-01, -1.43313e-01, 3.81111e-02, -6.43358e-03 },
|
||||||
|
{ -5.93749e-03, 3.39894e-02, -1.18526e-01, 4.32038e-01, 7.69305e-01, -1.42566e-01, 3.78182e-02, -6.37730e-03 },
|
||||||
|
{ -5.85804e-03, 3.35046e-02, -1.16618e-01, 4.21934e-01, 7.77327e-01, -1.41727e-01, 3.75010e-02, -6.31703e-03 },
|
||||||
|
{ -5.77544e-03, 3.30027e-02, -1.14659e-01, 4.11823e-01, 7.85244e-01, -1.40794e-01, 3.71596e-02, -6.25277e-03 },
|
||||||
|
{ -5.68976e-03, 3.24843e-02, -1.12650e-01, 4.01710e-01, 7.93055e-01, -1.39767e-01, 3.67941e-02, -6.18454e-03 },
|
||||||
|
{ -5.60103e-03, 3.19495e-02, -1.10593e-01, 3.91596e-01, 8.00757e-01, -1.38644e-01, 3.64044e-02, -6.11236e-03 },
|
||||||
|
{ -5.50931e-03, 3.13987e-02, -1.08490e-01, 3.81484e-01, 8.08348e-01, -1.37426e-01, 3.59905e-02, -6.03624e-03 },
|
||||||
|
{ -5.41467e-03, 3.08323e-02, -1.06342e-01, 3.71376e-01, 8.15826e-01, -1.36111e-01, 3.55525e-02, -5.95620e-03 },
|
||||||
|
{ -5.31716e-03, 3.02507e-02, -1.04150e-01, 3.61276e-01, 8.23188e-01, -1.34699e-01, 3.50903e-02, -5.87227e-03 },
|
||||||
|
{ -5.21683e-03, 2.96543e-02, -1.01915e-01, 3.51186e-01, 8.30432e-01, -1.33189e-01, 3.46042e-02, -5.78446e-03 },
|
||||||
|
{ -5.11376e-03, 2.90433e-02, -9.96402e-02, 3.41109e-01, 8.37557e-01, -1.31581e-01, 3.40940e-02, -5.69280e-03 },
|
||||||
|
{ -5.00800e-03, 2.84182e-02, -9.73254e-02, 3.31048e-01, 8.44559e-01, -1.29874e-01, 3.35600e-02, -5.59731e-03 },
|
||||||
|
{ -4.89961e-03, 2.77794e-02, -9.49727e-02, 3.21004e-01, 8.51437e-01, -1.28068e-01, 3.30021e-02, -5.49804e-03 },
|
||||||
|
{ -4.78866e-03, 2.71272e-02, -9.25834e-02, 3.10980e-01, 8.58189e-01, -1.26161e-01, 3.24205e-02, -5.39500e-03 },
|
||||||
|
{ -4.67520e-03, 2.64621e-02, -9.01591e-02, 3.00980e-01, 8.64812e-01, -1.24154e-01, 3.18153e-02, -5.28823e-03 },
|
||||||
|
{ -4.55932e-03, 2.57844e-02, -8.77011e-02, 2.91006e-01, 8.71305e-01, -1.22047e-01, 3.11866e-02, -5.17776e-03 },
|
||||||
|
{ -4.44107e-03, 2.50946e-02, -8.52109e-02, 2.81060e-01, 8.77666e-01, -1.19837e-01, 3.05345e-02, -5.06363e-03 },
|
||||||
|
{ -4.32052e-03, 2.43930e-02, -8.26900e-02, 2.71144e-01, 8.83893e-01, -1.17526e-01, 2.98593e-02, -4.94589e-03 },
|
||||||
|
{ -4.19774e-03, 2.36801e-02, -8.01399e-02, 2.61263e-01, 8.89984e-01, -1.15113e-01, 2.91609e-02, -4.82456e-03 },
|
||||||
|
{ -4.07279e-03, 2.29562e-02, -7.75620e-02, 2.51417e-01, 8.95936e-01, -1.12597e-01, 2.84397e-02, -4.69970e-03 },
|
||||||
|
{ -3.94576e-03, 2.22218e-02, -7.49577e-02, 2.41609e-01, 9.01749e-01, -1.09978e-01, 2.76957e-02, -4.57135e-03 },
|
||||||
|
{ -3.81671e-03, 2.14774e-02, -7.23286e-02, 2.31843e-01, 9.07420e-01, -1.07256e-01, 2.69293e-02, -4.43955e-03 },
|
||||||
|
{ -3.68570e-03, 2.07233e-02, -6.96762e-02, 2.22120e-01, 9.12947e-01, -1.04430e-01, 2.61404e-02, -4.30435e-03 },
|
||||||
|
{ -3.55283e-03, 1.99599e-02, -6.70018e-02, 2.12443e-01, 9.18329e-01, -1.01501e-01, 2.53295e-02, -4.16581e-03 },
|
||||||
|
{ -3.41815e-03, 1.91877e-02, -6.43069e-02, 2.02814e-01, 9.23564e-01, -9.84679e-02, 2.44967e-02, -4.02397e-03 },
|
||||||
|
{ -3.28174e-03, 1.84071e-02, -6.15931e-02, 1.93236e-01, 9.28650e-01, -9.53307e-02, 2.36423e-02, -3.87888e-03 },
|
||||||
|
{ -3.14367e-03, 1.76185e-02, -5.88617e-02, 1.83711e-01, 9.33586e-01, -9.20893e-02, 2.27664e-02, -3.73062e-03 },
|
||||||
|
{ -3.00403e-03, 1.68225e-02, -5.61142e-02, 1.74242e-01, 9.38371e-01, -8.87435e-02, 2.18695e-02, -3.57923e-03 },
|
||||||
|
{ -2.86289e-03, 1.60193e-02, -5.33522e-02, 1.64831e-01, 9.43001e-01, -8.52933e-02, 2.09516e-02, -3.42477e-03 },
|
||||||
|
{ -2.72032e-03, 1.52095e-02, -5.05770e-02, 1.55480e-01, 9.47477e-01, -8.17385e-02, 2.00132e-02, -3.26730e-03 },
|
||||||
|
{ -2.57640e-03, 1.43934e-02, -4.77900e-02, 1.46192e-01, 9.51795e-01, -7.80792e-02, 1.90545e-02, -3.10689e-03 },
|
||||||
|
{ -2.43121e-03, 1.35716e-02, -4.49929e-02, 1.36968e-01, 9.55956e-01, -7.43154e-02, 1.80759e-02, -2.94361e-03 },
|
||||||
|
{ -2.28483e-03, 1.27445e-02, -4.21869e-02, 1.27812e-01, 9.59958e-01, -7.04471e-02, 1.70776e-02, -2.77751e-03 },
|
||||||
|
{ -2.13733e-03, 1.19125e-02, -3.93735e-02, 1.18725e-01, 9.63798e-01, -6.64743e-02, 1.60599e-02, -2.60868e-03 },
|
||||||
|
{ -1.98880e-03, 1.10760e-02, -3.65541e-02, 1.09710e-01, 9.67477e-01, -6.23972e-02, 1.50233e-02, -2.43718e-03 },
|
||||||
|
{ -1.83931e-03, 1.02356e-02, -3.37303e-02, 1.00769e-01, 9.70992e-01, -5.82159e-02, 1.39681e-02, -2.26307e-03 },
|
||||||
|
{ -1.68894e-03, 9.39154e-03, -3.09033e-02, 9.19033e-02, 9.74342e-01, -5.39305e-02, 1.28947e-02, -2.08645e-03 },
|
||||||
|
{ -1.53777e-03, 8.54441e-03, -2.80746e-02, 8.31162e-02, 9.77526e-01, -4.95412e-02, 1.18034e-02, -1.90738e-03 },
|
||||||
|
{ -1.38589e-03, 7.69462e-03, -2.52457e-02, 7.44095e-02, 9.80543e-01, -4.50483e-02, 1.06946e-02, -1.72594e-03 },
|
||||||
|
{ -1.23337e-03, 6.84261e-03, -2.24178e-02, 6.57852e-02, 9.83392e-01, -4.04519e-02, 9.56876e-03, -1.54221e-03 },
|
||||||
|
{ -1.08030e-03, 5.98883e-03, -1.95925e-02, 5.72454e-02, 9.86071e-01, -3.57525e-02, 8.42626e-03, -1.35627e-03 },
|
||||||
|
{ -9.26747e-04, 5.13372e-03, -1.67710e-02, 4.87921e-02, 9.88580e-01, -3.09503e-02, 7.26755e-03, -1.16820e-03 },
|
||||||
|
{ -7.72802e-04, 4.27773e-03, -1.39548e-02, 4.04274e-02, 9.90917e-01, -2.60456e-02, 6.09305e-03, -9.78093e-04 },
|
||||||
|
{ -6.18544e-04, 3.42130e-03, -1.11453e-02, 3.21531e-02, 9.93082e-01, -2.10389e-02, 4.90322e-03, -7.86031e-04 },
|
||||||
|
{ -4.64053e-04, 2.56486e-03, -8.34364e-03, 2.39714e-02, 9.95074e-01, -1.59305e-02, 3.69852e-03, -5.92100e-04 },
|
||||||
|
{ -3.09412e-04, 1.70888e-03, -5.55134e-03, 1.58840e-02, 9.96891e-01, -1.07209e-02, 2.47942e-03, -3.96391e-04 },
|
||||||
|
{ -1.54700e-04, 8.53777e-04, -2.76968e-03, 7.89295e-03, 9.98534e-01, -5.41054e-03, 1.24642e-03, -1.98993e-04 },
|
||||||
|
{ 0.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00, 1.00000e+00, 0.00000e+00, 0.00000e+00, 0.00000e+00 },
|
||||||
|
};
|
||||||
@@ -10,20 +10,52 @@ namespace dsp {
|
|||||||
|
|
||||||
Add(stream<T>* a, stream<T>* b) { init(a, b); }
|
Add(stream<T>* a, stream<T>* b) { init(a, b); }
|
||||||
|
|
||||||
~Add() { generic_block<Add>::stop(); }
|
|
||||||
|
|
||||||
void init(stream<T>* a, stream<T>* b) {
|
void init(stream<T>* a, stream<T>* b) {
|
||||||
_a = a;
|
_a = a;
|
||||||
_b = b;
|
_b = b;
|
||||||
generic_block<Add>::registerInput(a);
|
generic_block<Add<T>>::registerInput(a);
|
||||||
generic_block<Add>::registerInput(b);
|
generic_block<Add<T>>::registerInput(b);
|
||||||
generic_block<Add>::registerOutput(&out);
|
generic_block<Add<T>>::registerOutput(&out);
|
||||||
|
generic_block<Add<T>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputs(stream<T>* a, stream<T>* b) {
|
||||||
|
assert(generic_block<Add<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Add<T>>::ctrlMtx);
|
||||||
|
generic_block<Add<T>>::tempStop();
|
||||||
|
generic_block<Add<T>>::unregisterInput(_a);
|
||||||
|
generic_block<Add<T>>::unregisterInput(_b);
|
||||||
|
_a = a;
|
||||||
|
_b = b;
|
||||||
|
generic_block<Add<T>>::registerInput(_a);
|
||||||
|
generic_block<Add<T>>::registerInput(_b);
|
||||||
|
generic_block<Add<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputA(stream<T>* a) {
|
||||||
|
assert(generic_block<Add<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Add<T>>::ctrlMtx);
|
||||||
|
generic_block<Add<T>>::tempStop();
|
||||||
|
generic_block<Add<T>>::unregisterInput(_a);
|
||||||
|
_a = a;
|
||||||
|
generic_block<Add<T>>::registerInput(_a);
|
||||||
|
generic_block<Add<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputB(stream<T>* b) {
|
||||||
|
assert(generic_block<Add<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Add<T>>::ctrlMtx);
|
||||||
|
generic_block<Add<T>>::tempStop();
|
||||||
|
generic_block<Add<T>>::unregisterInput(_b);
|
||||||
|
_b = b;
|
||||||
|
generic_block<Add<T>>::registerInput(_b);
|
||||||
|
generic_block<Add<T>>::tempStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
a_count = _a->read();
|
int a_count = _a->read();
|
||||||
if (a_count < 0) { return -1; }
|
if (a_count < 0) { return -1; }
|
||||||
b_count = _b->read();
|
int b_count = _b->read();
|
||||||
if (b_count < 0) { return -1; }
|
if (b_count < 0) { return -1; }
|
||||||
if (a_count != b_count) {
|
if (a_count != b_count) {
|
||||||
_a->flush();
|
_a->flush();
|
||||||
@@ -31,24 +63,103 @@ namespace dsp {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
|
||||||
if constexpr (std::is_same_v<T, complex_t> || std::is_same_v<T, stereo_t>) {
|
if constexpr (std::is_same_v<T, complex_t> || std::is_same_v<T, stereo_t>) {
|
||||||
volk_32fc_x2_add_32fc(out.data, _a->data, _b->data, a_count);
|
volk_32fc_x2_add_32fc((lv_32fc_t*)out.writeBuf, (lv_32fc_t*)_a->readBuf, (lv_32fc_t*)_b->readBuf, a_count);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
volk_32f_x2_add_32f(out.data, _a->data, _b->data, a_count);
|
volk_32f_x2_add_32f(out.writeBuf, _a->readBuf, _b->readBuf, a_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
_a->flush();
|
_a->flush();
|
||||||
_b->flush();
|
_b->flush();
|
||||||
out.write(a_count);
|
if (!out.swap(a_count)) { return -1; }
|
||||||
|
return a_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<T> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<T>* _a;
|
||||||
|
stream<T>* _b;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class Subtract : public generic_block<Subtract<T>> {
|
||||||
|
public:
|
||||||
|
Subtract() {}
|
||||||
|
|
||||||
|
Subtract(stream<T>* a, stream<T>* b) { init(a, b); }
|
||||||
|
|
||||||
|
void init(stream<T>* a, stream<T>* b) {
|
||||||
|
_a = a;
|
||||||
|
_b = b;
|
||||||
|
generic_block<Subtract<T>>::registerInput(a);
|
||||||
|
generic_block<Subtract<T>>::registerInput(b);
|
||||||
|
generic_block<Subtract<T>>::registerOutput(&out);
|
||||||
|
generic_block<Subtract<T>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputs(stream<T>* a, stream<T>* b) {
|
||||||
|
assert(generic_block<Subtract<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Subtract<T>>::ctrlMtx);
|
||||||
|
generic_block<Subtract<T>>::tempStop();
|
||||||
|
generic_block<Subtract<T>>::unregisterInput(_a);
|
||||||
|
generic_block<Subtract<T>>::unregisterInput(_b);
|
||||||
|
_a = a;
|
||||||
|
_b = b;
|
||||||
|
generic_block<Subtract<T>>::registerInput(_a);
|
||||||
|
generic_block<Subtract<T>>::registerInput(_b);
|
||||||
|
generic_block<Subtract<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputA(stream<T>* a) {
|
||||||
|
assert(generic_block<Subtract<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Subtract<T>>::ctrlMtx);
|
||||||
|
generic_block<Subtract<T>>::tempStop();
|
||||||
|
generic_block<Subtract<T>>::unregisterInput(_a);
|
||||||
|
_a = a;
|
||||||
|
generic_block<Subtract<T>>::registerInput(_a);
|
||||||
|
generic_block<Subtract<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputB(stream<T>* b) {
|
||||||
|
assert(generic_block<Subtract<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Subtract<T>>::ctrlMtx);
|
||||||
|
generic_block<Subtract<T>>::tempStop();
|
||||||
|
generic_block<Subtract<T>>::unregisterInput(_b);
|
||||||
|
_b = b;
|
||||||
|
generic_block<Subtract<T>>::registerInput(_b);
|
||||||
|
generic_block<Subtract<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int a_count = _a->read();
|
||||||
|
if (a_count < 0) { return -1; }
|
||||||
|
int b_count = _b->read();
|
||||||
|
if (b_count < 0) { return -1; }
|
||||||
|
if (a_count != b_count) {
|
||||||
|
_a->flush();
|
||||||
|
_b->flush();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<T, complex_t> || std::is_same_v<T, stereo_t>) {
|
||||||
|
volk_32f_x2_subtract_32f((float*)out.writeBuf, (float*)_a->readBuf, (float*)_b->readBuf, a_count * 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
volk_32f_x2_subtract_32f(out.writeBuf, _a->readBuf, _b->readBuf, a_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
_a->flush();
|
||||||
|
_b->flush();
|
||||||
|
if (!out.swap(a_count)) { return -1; }
|
||||||
return a_count;
|
return a_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<T> out;
|
stream<T> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int a_count, b_count;
|
|
||||||
stream<T>* _a;
|
stream<T>* _a;
|
||||||
stream<T>* _b;
|
stream<T>* _b;
|
||||||
|
|
||||||
@@ -61,20 +172,52 @@ namespace dsp {
|
|||||||
|
|
||||||
Multiply(stream<T>* a, stream<T>* b) { init(a, b); }
|
Multiply(stream<T>* a, stream<T>* b) { init(a, b); }
|
||||||
|
|
||||||
~Multiply() { generic_block<Multiply>::stop(); }
|
|
||||||
|
|
||||||
void init(stream<T>* a, stream<T>* b) {
|
void init(stream<T>* a, stream<T>* b) {
|
||||||
_a = a;
|
_a = a;
|
||||||
_b = b;
|
_b = b;
|
||||||
generic_block<Multiply>::registerInput(a);
|
generic_block<Multiply<T>>::registerInput(a);
|
||||||
generic_block<Multiply>::registerInput(b);
|
generic_block<Multiply<T>>::registerInput(b);
|
||||||
generic_block<Multiply>::registerOutput(&out);
|
generic_block<Multiply<T>>::registerOutput(&out);
|
||||||
|
generic_block<Multiply<T>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputs(stream<T>* a, stream<T>* b) {
|
||||||
|
assert(generic_block<Multiply<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Multiply<T>>::ctrlMtx);
|
||||||
|
generic_block<Multiply<T>>::tempStop();
|
||||||
|
generic_block<Multiply<T>>::unregisterInput(_a);
|
||||||
|
generic_block<Multiply<T>>::unregisterInput(_b);
|
||||||
|
_a = a;
|
||||||
|
_b = b;
|
||||||
|
generic_block<Multiply<T>>::registerInput(_a);
|
||||||
|
generic_block<Multiply<T>>::registerInput(_b);
|
||||||
|
generic_block<Multiply<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputA(stream<T>* a) {
|
||||||
|
assert(generic_block<Multiply<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Multiply<T>>::ctrlMtx);
|
||||||
|
generic_block<Multiply<T>>::tempStop();
|
||||||
|
generic_block<Multiply<T>>::unregisterInput(_a);
|
||||||
|
_a = a;
|
||||||
|
generic_block<Multiply<T>>::registerInput(_a);
|
||||||
|
generic_block<Multiply<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputB(stream<T>* b) {
|
||||||
|
assert(generic_block<Multiply<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Multiply<T>>::ctrlMtx);
|
||||||
|
generic_block<Multiply<T>>::tempStop();
|
||||||
|
generic_block<Multiply<T>>::unregisterInput(_b);
|
||||||
|
_b = b;
|
||||||
|
generic_block<Multiply<T>>::registerInput(_b);
|
||||||
|
generic_block<Multiply<T>>::tempStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
a_count = _a->read();
|
int a_count = _a->read();
|
||||||
if (a_count < 0) { return -1; }
|
if (a_count < 0) { return -1; }
|
||||||
b_count = _b->read();
|
int b_count = _b->read();
|
||||||
if (b_count < 0) { return -1; }
|
if (b_count < 0) { return -1; }
|
||||||
if (a_count != b_count) {
|
if (a_count != b_count) {
|
||||||
_a->flush();
|
_a->flush();
|
||||||
@@ -82,24 +225,22 @@ namespace dsp {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
|
||||||
if constexpr (std::is_same_v<T, complex_t>) {
|
if constexpr (std::is_same_v<T, complex_t>) {
|
||||||
volk_32fc_x2_multiply_32fc(out.data, _a->data, _b->data, a_count);
|
volk_32fc_x2_multiply_32fc((lv_32fc_t*)out.writeBuf, (lv_32fc_t*)_a->readBuf, (lv_32fc_t*)_b->readBuf, a_count);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
volk_32f_x2_multiply_32f(out.data, _a->data, _b->data, a_count);
|
volk_32f_x2_multiply_32f(out.writeBuf, _a->readBuf, _b->readBuf, a_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
_a->flush();
|
_a->flush();
|
||||||
_b->flush();
|
_b->flush();
|
||||||
out.write(a_count);
|
if (!out.swap(a_count)) { return -1; }
|
||||||
return a_count;
|
return a_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<T> out;
|
stream<T> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int a_count, b_count;
|
|
||||||
stream<T>* _a;
|
stream<T>* _a;
|
||||||
stream<T>* _b;
|
stream<T>* _b;
|
||||||
|
|
||||||
|
|||||||
82
core/src/dsp/measure.h
Normal file
82
core/src/dsp/measure.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <fftw3.h>
|
||||||
|
#include <volk/volk.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <dsp/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
class LevelMeter : public generic_block<LevelMeter> {
|
||||||
|
public:
|
||||||
|
LevelMeter() {}
|
||||||
|
|
||||||
|
LevelMeter(stream<stereo_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<stereo_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
generic_block<LevelMeter>::registerInput(_in);
|
||||||
|
generic_block<LevelMeter>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<stereo_t>* in) {
|
||||||
|
assert(generic_block<LevelMeter>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<LevelMeter>::ctrlMtx);
|
||||||
|
generic_block<LevelMeter>::tempStop();
|
||||||
|
generic_block<LevelMeter>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<LevelMeter>::registerInput(_in);
|
||||||
|
generic_block<LevelMeter>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
float maxL = 0, maxR = 0;
|
||||||
|
float absL, absR;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
absL = fabs(_in->readBuf[i].l);
|
||||||
|
absR = fabs(_in->readBuf[i].r);
|
||||||
|
if (absL > maxL) { maxL = absL; }
|
||||||
|
if (absR > maxR) { maxR = absR; }
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
|
||||||
|
float _lvlL = 10.0f * logf(maxL);
|
||||||
|
float _lvlR = 10.0f * logf(maxR);
|
||||||
|
|
||||||
|
// Update max values
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lck(lvlMtx);
|
||||||
|
if (_lvlL > lvlL) { lvlL = _lvlL; }
|
||||||
|
if (_lvlR > lvlR) { lvlR = _lvlR; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getLeftLevel() {
|
||||||
|
std::lock_guard<std::mutex> lck(lvlMtx);
|
||||||
|
float ret = lvlL;
|
||||||
|
lvlL = -90.0f;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getRightLevel() {
|
||||||
|
std::lock_guard<std::mutex> lck(lvlMtx);
|
||||||
|
float ret = lvlR;
|
||||||
|
lvlR = -90.0f;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
float lvlL = -90.0f;
|
||||||
|
float lvlR = -90.0f;
|
||||||
|
stream<stereo_t>* _in;
|
||||||
|
std::mutex lvlMtx;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
67
core/src/dsp/meteor/hrpt.h
Normal file
67
core/src/dsp/meteor/hrpt.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <dsp/utils/bitstream.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
namespace meteor {
|
||||||
|
class HRPTDemux : public generic_block<HRPTDemux> {
|
||||||
|
public:
|
||||||
|
HRPTDemux() {}
|
||||||
|
|
||||||
|
HRPTDemux(stream<uint8_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<uint8_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
generic_block<HRPTDemux>::registerInput(_in);
|
||||||
|
generic_block<HRPTDemux>::registerOutput(&telemOut);
|
||||||
|
generic_block<HRPTDemux>::registerOutput(&BISMout);
|
||||||
|
generic_block<HRPTDemux>::registerOutput(&SSPDOut);
|
||||||
|
generic_block<HRPTDemux>::registerOutput(&MTVZAOut);
|
||||||
|
generic_block<HRPTDemux>::registerOutput(&MSUMROut);
|
||||||
|
generic_block<HRPTDemux>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<uint8_t>* in) {
|
||||||
|
assert(generic_block<HRPTDemux>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<HRPTDemux>::ctrlMtx);
|
||||||
|
generic_block<HRPTDemux>::tempStop();
|
||||||
|
generic_block<HRPTDemux>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<HRPTDemux>::registerInput(_in);
|
||||||
|
generic_block<HRPTDemux>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
memcpy(telemOut.writeBuf + (i * 2), _in->readBuf + 4 + (i * 256), 2);
|
||||||
|
memcpy(BISMout.writeBuf + (i * 4), _in->readBuf + 4 + (i * 256) + 2, 4);
|
||||||
|
memcpy(SSPDOut.writeBuf + (i * 4), _in->readBuf + 4 + (i * 256) + 6, 4);
|
||||||
|
memcpy(MTVZAOut.writeBuf + (i * 8), _in->readBuf + 4 + (i * 256) + 10, 8);
|
||||||
|
memcpy(MSUMROut.writeBuf + (i * 238), _in->readBuf + 4 + (i * 256) + 18, (i == 3) ? 234 : 238);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!telemOut.swap(8)) { return -1; }
|
||||||
|
if (!BISMout.swap(16)) { return -1; }
|
||||||
|
if (!SSPDOut.swap(16)) { return -1; }
|
||||||
|
if (!MTVZAOut.swap(32)) { return -1; }
|
||||||
|
if (!MSUMROut.swap(948)) { return -1; }
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<uint8_t> telemOut;
|
||||||
|
stream<uint8_t> BISMout;
|
||||||
|
stream<uint8_t> SSPDOut;
|
||||||
|
stream<uint8_t> MTVZAOut;
|
||||||
|
stream<uint8_t> MSUMROut;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<uint8_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
78
core/src/dsp/meteor/msumr.h
Normal file
78
core/src/dsp/meteor/msumr.h
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <dsp/utils/bitstream.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
namespace meteor {
|
||||||
|
const uint64_t MSUMR_SYNC_WORD = 0x0218A7A392DD9ABF;
|
||||||
|
const uint8_t MSUMR_SYNC_BYTES[8] = { 0x02, 0x18, 0xA7, 0xA3, 0x92, 0xDD, 0x9A, 0xBF };
|
||||||
|
|
||||||
|
class MSUMRDemux : public generic_block<MSUMRDemux> {
|
||||||
|
public:
|
||||||
|
MSUMRDemux() {}
|
||||||
|
|
||||||
|
MSUMRDemux(stream<uint8_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<uint8_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
generic_block<MSUMRDemux>::registerInput(_in);
|
||||||
|
generic_block<MSUMRDemux>::registerOutput(&msumr1Out);
|
||||||
|
generic_block<MSUMRDemux>::registerOutput(&msumr2Out);
|
||||||
|
generic_block<MSUMRDemux>::registerOutput(&msumr3Out);
|
||||||
|
generic_block<MSUMRDemux>::registerOutput(&msumr4Out);
|
||||||
|
generic_block<MSUMRDemux>::registerOutput(&msumr5Out);
|
||||||
|
generic_block<MSUMRDemux>::registerOutput(&msumr6Out);
|
||||||
|
generic_block<MSUMRDemux>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<uint8_t>* in) {
|
||||||
|
assert(generic_block<MSUMRDemux>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<MSUMRDemux>::ctrlMtx);
|
||||||
|
generic_block<MSUMRDemux>::tempStop();
|
||||||
|
generic_block<MSUMRDemux>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<MSUMRDemux>::registerInput(_in);
|
||||||
|
generic_block<MSUMRDemux>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
int pixels = 0;
|
||||||
|
for (int i = 0; i < 11790; i += 30) {
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
msumr1Out.writeBuf[pixels + j] = (uint16_t)readBits((50 * 8) + (i * 8) + (j * 10), 10, _in->readBuf);
|
||||||
|
msumr2Out.writeBuf[pixels + j] = (uint16_t)readBits((50 * 8) + (i * 8) + (j * 10) + (40 * 1), 10, _in->readBuf);
|
||||||
|
msumr3Out.writeBuf[pixels + j] = (uint16_t)readBits((50 * 8) + (i * 8) + (j * 10) + (40 * 2), 10, _in->readBuf);
|
||||||
|
msumr4Out.writeBuf[pixels + j] = (uint16_t)readBits((50 * 8) + (i * 8) + (j * 10) + (40 * 3), 10, _in->readBuf);
|
||||||
|
msumr5Out.writeBuf[pixels + j] = (uint16_t)readBits((50 * 8) + (i * 8) + (j * 10) + (40 * 4), 10, _in->readBuf);
|
||||||
|
msumr6Out.writeBuf[pixels + j] = (uint16_t)readBits((50 * 8) + (i * 8) + (j * 10) + (40 * 5), 10, _in->readBuf);
|
||||||
|
}
|
||||||
|
pixels += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!msumr1Out.swap(1572)) { return -1; }
|
||||||
|
if (!msumr2Out.swap(1572)) { return -1; }
|
||||||
|
if (!msumr3Out.swap(1572)) { return -1; }
|
||||||
|
if (!msumr4Out.swap(1572)) { return -1; }
|
||||||
|
if (!msumr5Out.swap(1572)) { return -1; }
|
||||||
|
if (!msumr6Out.swap(1572)) { return -1; }
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<uint16_t> msumr1Out;
|
||||||
|
stream<uint16_t> msumr2Out;
|
||||||
|
stream<uint16_t> msumr3Out;
|
||||||
|
stream<uint16_t> msumr4Out;
|
||||||
|
stream<uint16_t> msumr5Out;
|
||||||
|
stream<uint16_t> msumr6Out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<uint8_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
111
core/src/dsp/noaa/hrpt.h
Normal file
111
core/src/dsp/noaa/hrpt.h
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <dsp/utils/bitstream.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
namespace noaa {
|
||||||
|
inline uint16_t HRPTReadWord(int offset, uint8_t* buffer) {
|
||||||
|
return (uint16_t)readBits(offset * 10, 10, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t HRPTSyncWord[] = {
|
||||||
|
1,0,1,0,0,0,0,1,0,0,
|
||||||
|
0,1,0,1,1,0,1,1,1,1,
|
||||||
|
1,1,0,1,0,1,1,1,0,0,
|
||||||
|
0,1,1,0,0,1,1,1,0,1,
|
||||||
|
1,0,0,0,0,0,1,1,1,1,
|
||||||
|
0,0,1,0,0,1,0,1,0,1
|
||||||
|
};
|
||||||
|
|
||||||
|
class HRPTDemux : public generic_block<HRPTDemux> {
|
||||||
|
public:
|
||||||
|
HRPTDemux() {}
|
||||||
|
|
||||||
|
HRPTDemux(stream<uint8_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<uint8_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
generic_block<HRPTDemux>::registerInput(_in);
|
||||||
|
generic_block<HRPTDemux>::registerOutput(&AVHRRChan1Out);
|
||||||
|
generic_block<HRPTDemux>::registerOutput(&AVHRRChan2Out);
|
||||||
|
generic_block<HRPTDemux>::registerOutput(&AVHRRChan3Out);
|
||||||
|
generic_block<HRPTDemux>::registerOutput(&AVHRRChan4Out);
|
||||||
|
generic_block<HRPTDemux>::registerOutput(&AVHRRChan5Out);
|
||||||
|
generic_block<HRPTDemux>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<uint8_t>* in) {
|
||||||
|
assert(generic_block<HRPTDemux>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<HRPTDemux>::ctrlMtx);
|
||||||
|
generic_block<HRPTDemux>::tempStop();
|
||||||
|
generic_block<HRPTDemux>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<HRPTDemux>::registerInput(_in);
|
||||||
|
generic_block<HRPTDemux>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
int minFrame = readBits(61, 2, _in->readBuf);
|
||||||
|
|
||||||
|
// If GAC frame, reject
|
||||||
|
if (minFrame == 0) {
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract TIP frames if present
|
||||||
|
if (minFrame == 1) {
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
for (int j = 0; j < 104; j++) {
|
||||||
|
TIPOut.writeBuf[j] = (HRPTReadWord(103 + (i * 104) + j, _in->readBuf) >> 2) & 0xFF;
|
||||||
|
}
|
||||||
|
if (!TIPOut.swap(104)) { return -1; };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exctact AIP otherwise
|
||||||
|
else if (minFrame == 3) {
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
for (int j = 0; j < 104; j++) {
|
||||||
|
AIPOut.writeBuf[j] = (HRPTReadWord(103 + (i * 104) + j, _in->readBuf) >> 2) & 0xFF;
|
||||||
|
}
|
||||||
|
if (!AIPOut.swap(104)) { return -1; };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract AVHRR data
|
||||||
|
for (int i = 0; i < 2048; i++) {
|
||||||
|
AVHRRChan1Out.writeBuf[i] = HRPTReadWord(750 + (i * 5), _in->readBuf);
|
||||||
|
AVHRRChan2Out.writeBuf[i] = HRPTReadWord(750 + (i * 5) + 1, _in->readBuf);
|
||||||
|
AVHRRChan3Out.writeBuf[i] = HRPTReadWord(750 + (i * 5) + 2, _in->readBuf);
|
||||||
|
AVHRRChan4Out.writeBuf[i] = HRPTReadWord(750 + (i * 5) + 3, _in->readBuf);
|
||||||
|
AVHRRChan5Out.writeBuf[i] = HRPTReadWord(750 + (i * 5) + 4, _in->readBuf);
|
||||||
|
}
|
||||||
|
if (!AVHRRChan1Out.swap(2048)) { return -1; };
|
||||||
|
if (!AVHRRChan2Out.swap(2048)) { return -1; };
|
||||||
|
if (!AVHRRChan3Out.swap(2048)) { return -1; };
|
||||||
|
if (!AVHRRChan4Out.swap(2048)) { return -1; };
|
||||||
|
if (!AVHRRChan5Out.swap(2048)) { return -1; };
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<uint8_t> TIPOut;
|
||||||
|
stream<uint8_t> AIPOut;
|
||||||
|
|
||||||
|
stream<uint16_t> AVHRRChan1Out;
|
||||||
|
stream<uint16_t> AVHRRChan2Out;
|
||||||
|
stream<uint16_t> AVHRRChan3Out;
|
||||||
|
stream<uint16_t> AVHRRChan4Out;
|
||||||
|
stream<uint16_t> AVHRRChan5Out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<uint8_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
242
core/src/dsp/noaa/tip.h
Normal file
242
core/src/dsp/noaa/tip.h
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <dsp/utils/bitstream.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
namespace noaa {
|
||||||
|
class TIPDemux : public generic_block<TIPDemux> {
|
||||||
|
public:
|
||||||
|
TIPDemux() {}
|
||||||
|
|
||||||
|
TIPDemux(stream<uint8_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<uint8_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
generic_block<TIPDemux>::registerInput(_in);
|
||||||
|
generic_block<TIPDemux>::registerOutput(&HIRSOut);
|
||||||
|
generic_block<TIPDemux>::registerOutput(&SEMOut);
|
||||||
|
generic_block<TIPDemux>::registerOutput(&DCSOut);
|
||||||
|
generic_block<TIPDemux>::registerOutput(&SBUVOut);
|
||||||
|
generic_block<TIPDemux>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<uint8_t>* in) {
|
||||||
|
assert(generic_block<TIPDemux>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<TIPDemux>::ctrlMtx);
|
||||||
|
generic_block<TIPDemux>::tempStop();
|
||||||
|
generic_block<TIPDemux>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<TIPDemux>::registerInput(_in);
|
||||||
|
generic_block<TIPDemux>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
// Extract HIRS
|
||||||
|
HIRSOut.writeBuf[0] = _in->readBuf[16];
|
||||||
|
HIRSOut.writeBuf[1] = _in->readBuf[17];
|
||||||
|
HIRSOut.writeBuf[2] = _in->readBuf[22];
|
||||||
|
HIRSOut.writeBuf[3] = _in->readBuf[23];
|
||||||
|
HIRSOut.writeBuf[4] = _in->readBuf[26];
|
||||||
|
HIRSOut.writeBuf[5] = _in->readBuf[27];
|
||||||
|
HIRSOut.writeBuf[6] = _in->readBuf[30];
|
||||||
|
HIRSOut.writeBuf[7] = _in->readBuf[31];
|
||||||
|
HIRSOut.writeBuf[8] = _in->readBuf[34];
|
||||||
|
HIRSOut.writeBuf[9] = _in->readBuf[35];
|
||||||
|
HIRSOut.writeBuf[10] = _in->readBuf[38];
|
||||||
|
HIRSOut.writeBuf[11] = _in->readBuf[39];
|
||||||
|
HIRSOut.writeBuf[12] = _in->readBuf[42];
|
||||||
|
HIRSOut.writeBuf[13] = _in->readBuf[43];
|
||||||
|
HIRSOut.writeBuf[14] = _in->readBuf[54];
|
||||||
|
HIRSOut.writeBuf[15] = _in->readBuf[55];
|
||||||
|
HIRSOut.writeBuf[16] = _in->readBuf[58];
|
||||||
|
HIRSOut.writeBuf[17] = _in->readBuf[59];
|
||||||
|
HIRSOut.writeBuf[18] = _in->readBuf[62];
|
||||||
|
HIRSOut.writeBuf[19] = _in->readBuf[63];
|
||||||
|
HIRSOut.writeBuf[20] = _in->readBuf[66];
|
||||||
|
HIRSOut.writeBuf[21] = _in->readBuf[67];
|
||||||
|
HIRSOut.writeBuf[22] = _in->readBuf[70];
|
||||||
|
HIRSOut.writeBuf[23] = _in->readBuf[71];
|
||||||
|
HIRSOut.writeBuf[24] = _in->readBuf[74];
|
||||||
|
HIRSOut.writeBuf[25] = _in->readBuf[75];
|
||||||
|
HIRSOut.writeBuf[26] = _in->readBuf[78];
|
||||||
|
HIRSOut.writeBuf[27] = _in->readBuf[79];
|
||||||
|
HIRSOut.writeBuf[28] = _in->readBuf[82];
|
||||||
|
HIRSOut.writeBuf[29] = _in->readBuf[83];
|
||||||
|
HIRSOut.writeBuf[30] = _in->readBuf[84];
|
||||||
|
HIRSOut.writeBuf[31] = _in->readBuf[85];
|
||||||
|
HIRSOut.writeBuf[32] = _in->readBuf[88];
|
||||||
|
HIRSOut.writeBuf[33] = _in->readBuf[89];
|
||||||
|
HIRSOut.writeBuf[34] = _in->readBuf[92];
|
||||||
|
HIRSOut.writeBuf[35] = _in->readBuf[93];
|
||||||
|
if (!HIRSOut.swap(36)) { return -1; };
|
||||||
|
|
||||||
|
// Extract SEM
|
||||||
|
SEMOut.writeBuf[0] = _in->readBuf[20];
|
||||||
|
SEMOut.writeBuf[1] = _in->readBuf[21];
|
||||||
|
if (!SEMOut.swap(2)) { return -1; };
|
||||||
|
|
||||||
|
// Extract DCS
|
||||||
|
DCSOut.writeBuf[0] = _in->readBuf[18];
|
||||||
|
DCSOut.writeBuf[1] = _in->readBuf[19];
|
||||||
|
DCSOut.writeBuf[2] = _in->readBuf[24];
|
||||||
|
DCSOut.writeBuf[3] = _in->readBuf[25];
|
||||||
|
DCSOut.writeBuf[4] = _in->readBuf[28];
|
||||||
|
DCSOut.writeBuf[5] = _in->readBuf[29];
|
||||||
|
DCSOut.writeBuf[6] = _in->readBuf[32];
|
||||||
|
DCSOut.writeBuf[7] = _in->readBuf[33];
|
||||||
|
DCSOut.writeBuf[8] = _in->readBuf[40];
|
||||||
|
DCSOut.writeBuf[9] = _in->readBuf[41];
|
||||||
|
DCSOut.writeBuf[10] = _in->readBuf[44];
|
||||||
|
DCSOut.writeBuf[11] = _in->readBuf[45];
|
||||||
|
DCSOut.writeBuf[12] = _in->readBuf[52];
|
||||||
|
DCSOut.writeBuf[13] = _in->readBuf[53];
|
||||||
|
DCSOut.writeBuf[14] = _in->readBuf[56];
|
||||||
|
DCSOut.writeBuf[15] = _in->readBuf[57];
|
||||||
|
DCSOut.writeBuf[16] = _in->readBuf[60];
|
||||||
|
DCSOut.writeBuf[17] = _in->readBuf[61];
|
||||||
|
DCSOut.writeBuf[18] = _in->readBuf[64];
|
||||||
|
DCSOut.writeBuf[19] = _in->readBuf[65];
|
||||||
|
DCSOut.writeBuf[20] = _in->readBuf[68];
|
||||||
|
DCSOut.writeBuf[21] = _in->readBuf[69];
|
||||||
|
DCSOut.writeBuf[22] = _in->readBuf[72];
|
||||||
|
DCSOut.writeBuf[23] = _in->readBuf[73];
|
||||||
|
DCSOut.writeBuf[24] = _in->readBuf[76];
|
||||||
|
DCSOut.writeBuf[25] = _in->readBuf[77];
|
||||||
|
DCSOut.writeBuf[26] = _in->readBuf[86];
|
||||||
|
DCSOut.writeBuf[27] = _in->readBuf[87];
|
||||||
|
DCSOut.writeBuf[28] = _in->readBuf[90];
|
||||||
|
DCSOut.writeBuf[29] = _in->readBuf[91];
|
||||||
|
DCSOut.writeBuf[30] = _in->readBuf[94];
|
||||||
|
DCSOut.writeBuf[31] = _in->readBuf[95];
|
||||||
|
if (!DCSOut.swap(32)) { return -1; };
|
||||||
|
|
||||||
|
// Extract SBUV
|
||||||
|
SBUVOut.writeBuf[0] = _in->readBuf[36];
|
||||||
|
SBUVOut.writeBuf[1] = _in->readBuf[37];
|
||||||
|
SBUVOut.writeBuf[2] = _in->readBuf[80];
|
||||||
|
SBUVOut.writeBuf[3] = _in->readBuf[81];
|
||||||
|
if (!SBUVOut.swap(4)) { return -1; };
|
||||||
|
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<uint8_t> HIRSOut;
|
||||||
|
stream<uint8_t> SEMOut;
|
||||||
|
stream<uint8_t> DCSOut;
|
||||||
|
stream<uint8_t> SBUVOut;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<uint8_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
inline uint16_t HIRSSignedToUnsigned(uint16_t n) {
|
||||||
|
return (n & 0x1000) ? (0x1000 + (n & 0xFFF)) : (0xFFF - (n & 0xFFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
class HIRSDemux : public generic_block<HIRSDemux> {
|
||||||
|
public:
|
||||||
|
HIRSDemux() {}
|
||||||
|
|
||||||
|
HIRSDemux(stream<uint8_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<uint8_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
generic_block<HIRSDemux>::registerInput(_in);
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
generic_block<HIRSDemux>::registerOutput(&radChannels[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear buffers
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
for (int j = 0; j < 56; j++) { radChannels[i].writeBuf[j] = 0xFFF; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<uint8_t>* in) {
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<HIRSDemux>::ctrlMtx);
|
||||||
|
generic_block<HIRSDemux>::tempStop();
|
||||||
|
generic_block<HIRSDemux>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<HIRSDemux>::registerInput(_in);
|
||||||
|
generic_block<HIRSDemux>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
int element = readBits(19, 6, _in->readBuf);
|
||||||
|
|
||||||
|
// If we've skipped or are on a non image element and there's data avilable, send it
|
||||||
|
if ((element < lastElement || element > 55) && newImageData) {
|
||||||
|
newImageData = false;
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
if (!radChannels[i].swap(56)) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear buffers
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
for (int j = 0; j < 56; j++) { radChannels[i].writeBuf[j] = 0xFFF; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastElement = element;
|
||||||
|
|
||||||
|
// If data is part of a line, save it
|
||||||
|
if (element <= 55) {
|
||||||
|
newImageData = true;
|
||||||
|
|
||||||
|
radChannels[0].writeBuf[element] = HIRSSignedToUnsigned(readBits(26, 13, _in->readBuf));
|
||||||
|
radChannels[1].writeBuf[element] = HIRSSignedToUnsigned(readBits(52, 13, _in->readBuf));
|
||||||
|
radChannels[2].writeBuf[element] = HIRSSignedToUnsigned(readBits(65, 13, _in->readBuf));
|
||||||
|
radChannels[3].writeBuf[element] = HIRSSignedToUnsigned(readBits(91, 13, _in->readBuf));
|
||||||
|
radChannels[4].writeBuf[element] = HIRSSignedToUnsigned(readBits(221, 13, _in->readBuf));
|
||||||
|
radChannels[5].writeBuf[element] = HIRSSignedToUnsigned(readBits(208, 13, _in->readBuf));
|
||||||
|
radChannels[6].writeBuf[element] = HIRSSignedToUnsigned(readBits(143, 13, _in->readBuf));
|
||||||
|
radChannels[7].writeBuf[element] = HIRSSignedToUnsigned(readBits(156, 13, _in->readBuf));
|
||||||
|
radChannels[8].writeBuf[element] = HIRSSignedToUnsigned(readBits(273, 13, _in->readBuf));
|
||||||
|
radChannels[9].writeBuf[element] = HIRSSignedToUnsigned(readBits(182, 13, _in->readBuf));
|
||||||
|
radChannels[10].writeBuf[element] = HIRSSignedToUnsigned(readBits(119, 13, _in->readBuf));
|
||||||
|
radChannels[11].writeBuf[element] = HIRSSignedToUnsigned(readBits(247, 13, _in->readBuf));
|
||||||
|
radChannels[12].writeBuf[element] = HIRSSignedToUnsigned(readBits(78, 13, _in->readBuf));
|
||||||
|
radChannels[13].writeBuf[element] = HIRSSignedToUnsigned(readBits(195, 13, _in->readBuf));
|
||||||
|
radChannels[14].writeBuf[element] = HIRSSignedToUnsigned(readBits(234, 13, _in->readBuf));
|
||||||
|
radChannels[15].writeBuf[element] = HIRSSignedToUnsigned(readBits(260, 13, _in->readBuf));
|
||||||
|
radChannels[16].writeBuf[element] = HIRSSignedToUnsigned(readBits(39, 13, _in->readBuf));
|
||||||
|
radChannels[17].writeBuf[element] = HIRSSignedToUnsigned(readBits(104, 13, _in->readBuf));
|
||||||
|
radChannels[18].writeBuf[element] = HIRSSignedToUnsigned(readBits(130, 13, _in->readBuf));
|
||||||
|
radChannels[19].writeBuf[element] = HIRSSignedToUnsigned(readBits(169, 13, _in->readBuf));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are writing the last pixel of a line, send it already
|
||||||
|
if (element == 55) {
|
||||||
|
newImageData = false;
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
if (!radChannels[i].swap(56)) { return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear buffers
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
for (int j = 0; j < 56; j++) { radChannels[i].writeBuf[j] = 0xFFF; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<uint16_t> radChannels[20];
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<uint8_t>* _in;
|
||||||
|
int lastElement = 0;
|
||||||
|
bool newImageData = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
325
core/src/dsp/pll.h
Normal file
325
core/src/dsp/pll.h
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <dsp/interpolation_taps.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <dsp/utils/macros.h>
|
||||||
|
#include <dsp/math.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
template <int ORDER>
|
||||||
|
class CostasLoop: public generic_block<CostasLoop<ORDER>> {
|
||||||
|
public:
|
||||||
|
CostasLoop() {}
|
||||||
|
|
||||||
|
CostasLoop(stream<complex_t>* in, float loopBandwidth) { init(in, loopBandwidth); }
|
||||||
|
|
||||||
|
void init(stream<complex_t>* in, float loopBandwidth) {
|
||||||
|
_in = in;
|
||||||
|
lastVCO.re = 1.0f;
|
||||||
|
lastVCO.im = 0.0f;
|
||||||
|
_loopBandwidth = loopBandwidth;
|
||||||
|
|
||||||
|
float dampningFactor = sqrtf(2.0f) / 2.0f;
|
||||||
|
float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth);
|
||||||
|
_alpha = (4 * dampningFactor * _loopBandwidth) / denominator;
|
||||||
|
_beta = (4 * _loopBandwidth * _loopBandwidth) / denominator;
|
||||||
|
|
||||||
|
generic_block<CostasLoop<ORDER>>::registerInput(_in);
|
||||||
|
generic_block<CostasLoop<ORDER>>::registerOutput(&out);
|
||||||
|
generic_block<CostasLoop<ORDER>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<CostasLoop<ORDER>>::_block_init);
|
||||||
|
generic_block<CostasLoop<ORDER>>::tempStop();
|
||||||
|
generic_block<CostasLoop<ORDER>>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<CostasLoop<ORDER>>::registerInput(_in);
|
||||||
|
generic_block<CostasLoop<ORDER>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLoopBandwidth(float loopBandwidth) {
|
||||||
|
assert(generic_block<CostasLoop<ORDER>>::_block_init);
|
||||||
|
generic_block<CostasLoop<ORDER>>::tempStop();
|
||||||
|
_loopBandwidth = loopBandwidth;
|
||||||
|
float dampningFactor = sqrtf(2.0f) / 2.0f;
|
||||||
|
float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth);
|
||||||
|
_alpha = (4 * dampningFactor * _loopBandwidth) / denominator;
|
||||||
|
_beta = (4 * _loopBandwidth * _loopBandwidth) / denominator;
|
||||||
|
generic_block<CostasLoop<ORDER>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
complex_t outVal;
|
||||||
|
float error;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
|
||||||
|
// Mix the VFO with the input to create the output value
|
||||||
|
outVal.re = (lastVCO.re*_in->readBuf[i].re) - (lastVCO.im*_in->readBuf[i].im);
|
||||||
|
outVal.im = (lastVCO.im*_in->readBuf[i].re) + (lastVCO.re*_in->readBuf[i].im);
|
||||||
|
out.writeBuf[i] = outVal;
|
||||||
|
|
||||||
|
// Calculate the phase error estimation
|
||||||
|
if constexpr (ORDER == 2) {
|
||||||
|
error = outVal.re * outVal.im;
|
||||||
|
}
|
||||||
|
if constexpr (ORDER == 4) {
|
||||||
|
error = (DSP_STEP(outVal.re) * outVal.im) - (DSP_STEP(outVal.im) * outVal.re);
|
||||||
|
}
|
||||||
|
if constexpr (ORDER == 8) {
|
||||||
|
// This is taken from GR, I have no idea how it works but it does...
|
||||||
|
const float K = (sqrtf(2.0) - 1);
|
||||||
|
if (fabsf(outVal.re) >= fabsf(outVal.im)) {
|
||||||
|
error = ((outVal.re > 0.0f ? 1.0f : -1.0f) * outVal.im -
|
||||||
|
(outVal.im > 0.0f ? 1.0f : -1.0f) * outVal.re * K);
|
||||||
|
} else {
|
||||||
|
error = ((outVal.re > 0.0f ? 1.0f : -1.0f) * outVal.im * K -
|
||||||
|
(outVal.im > 0.0f ? 1.0f : -1.0f) * outVal.re);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error > 1.0f) { error = 1.0f; }
|
||||||
|
else if (error < -1.0f) { error = -1.0f; }
|
||||||
|
|
||||||
|
// Integrate frequency and clamp it
|
||||||
|
vcoFrequency += _beta * error;
|
||||||
|
if (vcoFrequency > 1.0f) { vcoFrequency = 1.0f; }
|
||||||
|
else if (vcoFrequency < -1.0f) { vcoFrequency = -1.0f; }
|
||||||
|
|
||||||
|
// Calculate new phase and wrap it
|
||||||
|
vcoPhase += vcoFrequency + (_alpha * error);
|
||||||
|
while (vcoPhase > (2.0f * FL_M_PI)) { vcoPhase -= (2.0f * FL_M_PI); }
|
||||||
|
while (vcoPhase < (-2.0f * FL_M_PI)) { vcoPhase += (2.0f * FL_M_PI); }
|
||||||
|
|
||||||
|
// Calculate output
|
||||||
|
lastVCO.re = cosf(-vcoPhase);
|
||||||
|
lastVCO.im = sinf(-vcoPhase);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<complex_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float _loopBandwidth = 1.0f;
|
||||||
|
|
||||||
|
float _alpha; // Integral coefficient
|
||||||
|
float _beta; // Proportional coefficient
|
||||||
|
float vcoFrequency = 0.0f;
|
||||||
|
float vcoPhase = 0.0f;
|
||||||
|
complex_t lastVCO;
|
||||||
|
|
||||||
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class CarrierTrackingPLL: public generic_block<CarrierTrackingPLL<T>> {
|
||||||
|
public:
|
||||||
|
CarrierTrackingPLL() {}
|
||||||
|
|
||||||
|
CarrierTrackingPLL(stream<complex_t>* in, float loopBandwidth) { init(in, loopBandwidth); }
|
||||||
|
|
||||||
|
void init(stream<complex_t>* in, float loopBandwidth) {
|
||||||
|
_in = in;
|
||||||
|
lastVCO.re = 1.0f;
|
||||||
|
lastVCO.im = 0.0f;
|
||||||
|
_loopBandwidth = loopBandwidth;
|
||||||
|
|
||||||
|
float dampningFactor = sqrtf(2.0f) / 2.0f;
|
||||||
|
float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth);
|
||||||
|
_alpha = (4 * dampningFactor * _loopBandwidth) / denominator;
|
||||||
|
_beta = (4 * _loopBandwidth * _loopBandwidth) / denominator;
|
||||||
|
|
||||||
|
generic_block<CarrierTrackingPLL<T>>::registerInput(_in);
|
||||||
|
generic_block<CarrierTrackingPLL<T>>::registerOutput(&out);
|
||||||
|
generic_block<CarrierTrackingPLL<T>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<CarrierTrackingPLL<T>>::_block_init);
|
||||||
|
generic_block<CarrierTrackingPLL<T>>::tempStop();
|
||||||
|
generic_block<CarrierTrackingPLL<T>>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<CarrierTrackingPLL<T>>::registerInput(_in);
|
||||||
|
generic_block<CarrierTrackingPLL<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLoopBandwidth(float loopBandwidth) {
|
||||||
|
assert(generic_block<CarrierTrackingPLL<T>>::_block_init);
|
||||||
|
generic_block<CarrierTrackingPLL<T>>::tempStop();
|
||||||
|
_loopBandwidth = loopBandwidth;
|
||||||
|
float dampningFactor = sqrtf(2.0f) / 2.0f;
|
||||||
|
float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth);
|
||||||
|
_alpha = (4 * dampningFactor * _loopBandwidth) / denominator;
|
||||||
|
_beta = (4 * _loopBandwidth * _loopBandwidth) / denominator;
|
||||||
|
generic_block<CarrierTrackingPLL<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
complex_t outVal;
|
||||||
|
float error;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
|
||||||
|
// Mix the VFO with the input to create the output value
|
||||||
|
outVal.re = (lastVCO.re*_in->readBuf[i].re) - ((-lastVCO.im)*_in->readBuf[i].im);
|
||||||
|
outVal.im = ((-lastVCO.im)*_in->readBuf[i].re) + (lastVCO.re*_in->readBuf[i].im);
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
|
out.writeBuf[i] = outVal.fastPhase();
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<T, complex_t>) {
|
||||||
|
out.writeBuf[i] = outVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the phase error estimation
|
||||||
|
// TODO: Figure out why fastPhase doesn't work
|
||||||
|
error = _in->readBuf[i].phase() - vcoPhase;
|
||||||
|
if (error > 3.1415926535f) { error -= 2.0f * 3.1415926535f; }
|
||||||
|
else if (error <= -3.1415926535f) { error += 2.0f * 3.1415926535f; }
|
||||||
|
|
||||||
|
// if (error > 1.0f) { error = 1.0f; }
|
||||||
|
// else if (error < -1.0f) { error = -1.0f; }
|
||||||
|
|
||||||
|
// Integrate frequency and clamp it
|
||||||
|
vcoFrequency += _beta * error;
|
||||||
|
if (vcoFrequency > 1.0f) { vcoFrequency = 1.0f; }
|
||||||
|
else if (vcoFrequency < -1.0f) { vcoFrequency = -1.0f; }
|
||||||
|
|
||||||
|
// Calculate new phase and wrap it
|
||||||
|
vcoPhase += vcoFrequency + (_alpha * error);
|
||||||
|
while (vcoPhase > (2.0f * FL_M_PI)) { vcoPhase -= (2.0f * FL_M_PI); }
|
||||||
|
while (vcoPhase < (-2.0f * FL_M_PI)) { vcoPhase += (2.0f * FL_M_PI); }
|
||||||
|
|
||||||
|
// Calculate output
|
||||||
|
lastVCO.re = cosf(vcoPhase);
|
||||||
|
lastVCO.im = sinf(vcoPhase);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<T> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float _loopBandwidth = 1.0f;
|
||||||
|
|
||||||
|
float _alpha; // Integral coefficient
|
||||||
|
float _beta; // Proportional coefficient
|
||||||
|
float vcoFrequency = 0.0f;
|
||||||
|
float vcoPhase = 0.0f;
|
||||||
|
complex_t lastVCO;
|
||||||
|
|
||||||
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class PLL: public generic_block<PLL> {
|
||||||
|
public:
|
||||||
|
PLL() {}
|
||||||
|
|
||||||
|
PLL(stream<complex_t>* in, float loopBandwidth) { init(in, loopBandwidth); }
|
||||||
|
|
||||||
|
void init(stream<complex_t>* in, float loopBandwidth) {
|
||||||
|
_in = in;
|
||||||
|
lastVCO.re = 1.0f;
|
||||||
|
lastVCO.im = 0.0f;
|
||||||
|
_loopBandwidth = loopBandwidth;
|
||||||
|
|
||||||
|
float dampningFactor = sqrtf(2.0f) / 2.0f;
|
||||||
|
float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth);
|
||||||
|
_alpha = (4 * dampningFactor * _loopBandwidth) / denominator;
|
||||||
|
_beta = (4 * _loopBandwidth * _loopBandwidth) / denominator;
|
||||||
|
|
||||||
|
generic_block<PLL>::registerInput(_in);
|
||||||
|
generic_block<PLL>::registerOutput(&out);
|
||||||
|
generic_block<PLL>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<PLL>::_block_init);
|
||||||
|
generic_block<PLL>::tempStop();
|
||||||
|
generic_block<PLL>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<PLL>::registerInput(_in);
|
||||||
|
generic_block<PLL>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLoopBandwidth(float loopBandwidth) {
|
||||||
|
assert(generic_block<PLL>::_block_init);
|
||||||
|
generic_block<PLL>::tempStop();
|
||||||
|
_loopBandwidth = loopBandwidth;
|
||||||
|
float dampningFactor = sqrtf(2.0f) / 2.0f;
|
||||||
|
float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth);
|
||||||
|
_alpha = (4 * dampningFactor * _loopBandwidth) / denominator;
|
||||||
|
_beta = (4 * _loopBandwidth * _loopBandwidth) / denominator;
|
||||||
|
generic_block<PLL>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
complex_t outVal;
|
||||||
|
float error;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
out.writeBuf[i] = lastVCO;
|
||||||
|
|
||||||
|
// Calculate the phase error estimation
|
||||||
|
// TODO: Figure out why fastPhase doesn't work
|
||||||
|
error = _in->readBuf[i].phase() - vcoPhase;
|
||||||
|
if (error > 3.1415926535f) { error -= 2.0f * 3.1415926535f; }
|
||||||
|
else if (error <= -3.1415926535f) { error += 2.0f * 3.1415926535f; }
|
||||||
|
|
||||||
|
// Integrate frequency and clamp it
|
||||||
|
vcoFrequency += _beta * error;
|
||||||
|
if (vcoFrequency > 1.0f) { vcoFrequency = 1.0f; }
|
||||||
|
else if (vcoFrequency < -1.0f) { vcoFrequency = -1.0f; }
|
||||||
|
|
||||||
|
// Calculate new phase and wrap it
|
||||||
|
vcoPhase += vcoFrequency + (_alpha * error);
|
||||||
|
while (vcoPhase > (2.0f * FL_M_PI)) { vcoPhase -= (2.0f * FL_M_PI); }
|
||||||
|
while (vcoPhase < (-2.0f * FL_M_PI)) { vcoPhase += (2.0f * FL_M_PI); }
|
||||||
|
|
||||||
|
// Calculate output
|
||||||
|
lastVCO.re = cosf(vcoPhase);
|
||||||
|
lastVCO.im = sinf(vcoPhase);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<complex_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float _loopBandwidth = 1.0f;
|
||||||
|
|
||||||
|
float _alpha; // Integral coefficient
|
||||||
|
float _beta; // Proportional coefficient
|
||||||
|
float vcoFrequency = ((19000.0f / 250000.0f) * 2.0f * FL_M_PI);
|
||||||
|
float vcoPhase = 0.0f;
|
||||||
|
complex_t lastVCO;
|
||||||
|
|
||||||
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <dsp/block.h>
|
#include <dsp/block.h>
|
||||||
#include <fftw3.h>
|
|
||||||
#include <volk/volk.h>
|
#include <volk/volk.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <dsp/math.h>
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -13,10 +14,6 @@ namespace dsp {
|
|||||||
|
|
||||||
FrequencyXlator(stream<complex_t>* in, float sampleRate, float freq) { init(in, sampleRate, freq); }
|
FrequencyXlator(stream<complex_t>* in, float sampleRate, float freq) { init(in, sampleRate, freq); }
|
||||||
|
|
||||||
~FrequencyXlator() {
|
|
||||||
generic_block<FrequencyXlator<T>>::stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void init(stream<complex_t>* in, float sampleRate, float freq) {
|
void init(stream<complex_t>* in, float sampleRate, float freq) {
|
||||||
_in = in;
|
_in = in;
|
||||||
_sampleRate = sampleRate;
|
_sampleRate = sampleRate;
|
||||||
@@ -25,9 +22,11 @@ namespace dsp {
|
|||||||
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
||||||
generic_block<FrequencyXlator<T>>::registerInput(_in);
|
generic_block<FrequencyXlator<T>>::registerInput(_in);
|
||||||
generic_block<FrequencyXlator<T>>::registerOutput(&out);
|
generic_block<FrequencyXlator<T>>::registerOutput(&out);
|
||||||
|
generic_block<FrequencyXlator<T>>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInputSize(stream<complex_t>* in) {
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<FrequencyXlator<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<FrequencyXlator<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<FrequencyXlator<T>>::ctrlMtx);
|
||||||
generic_block<FrequencyXlator<T>>::tempStop();
|
generic_block<FrequencyXlator<T>>::tempStop();
|
||||||
generic_block<FrequencyXlator<T>>::unregisterInput(_in);
|
generic_block<FrequencyXlator<T>>::unregisterInput(_in);
|
||||||
@@ -37,48 +36,47 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setSampleRate(float sampleRate) {
|
void setSampleRate(float sampleRate) {
|
||||||
// No need to restart
|
assert(generic_block<FrequencyXlator<T>>::_block_init);
|
||||||
_sampleRate = sampleRate;
|
_sampleRate = sampleRate;
|
||||||
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
||||||
}
|
}
|
||||||
|
|
||||||
float getSampleRate() {
|
float getSampleRate() {
|
||||||
|
assert(generic_block<FrequencyXlator<T>>::_block_init);
|
||||||
return _sampleRate;
|
return _sampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setFrequency(float freq) {
|
void setFrequency(float freq) {
|
||||||
// No need to restart
|
assert(generic_block<FrequencyXlator<T>>::_block_init);
|
||||||
_freq = freq;
|
_freq = freq;
|
||||||
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
||||||
}
|
}
|
||||||
|
|
||||||
float getFrequency() {
|
float getFrequency() {
|
||||||
|
assert(generic_block<FrequencyXlator<T>>::_block_init);
|
||||||
return _freq;
|
return _freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
|
||||||
|
|
||||||
// TODO: Do float xlation
|
// TODO: Do float xlation
|
||||||
if constexpr (std::is_same_v<T, float>) {
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
spdlog::error("XLATOR NOT IMPLEMENTED FOR FLOAT");
|
spdlog::error("XLATOR NOT IMPLEMENTED FOR FLOAT");
|
||||||
}
|
}
|
||||||
if constexpr (std::is_same_v<T, complex_t>) {
|
if constexpr (std::is_same_v<T, complex_t>) {
|
||||||
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)out.data, (lv_32fc_t*)_in->data, phaseDelta, &phase, count);
|
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)out.writeBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<complex_t> out;
|
stream<complex_t> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
|
||||||
float _sampleRate;
|
float _sampleRate;
|
||||||
float _freq;
|
float _freq;
|
||||||
lv_32fc_t phaseDelta;
|
lv_32fc_t phaseDelta;
|
||||||
@@ -91,18 +89,20 @@ namespace dsp {
|
|||||||
public:
|
public:
|
||||||
AGC() {}
|
AGC() {}
|
||||||
|
|
||||||
AGC(stream<float>* in, float ratio) { init(in, ratio); }
|
AGC(stream<float>* in, float fallRate, float sampleRate) { init(in, fallRate, sampleRate); }
|
||||||
|
|
||||||
~AGC() { generic_block<AGC>::stop(); }
|
void init(stream<float>* in, float fallRate, float sampleRate) {
|
||||||
|
|
||||||
void init(stream<float>* in, float ratio) {
|
|
||||||
_in = in;
|
_in = in;
|
||||||
_ratio = ratio;
|
_sampleRate = sampleRate;
|
||||||
|
_fallRate = fallRate;
|
||||||
|
_CorrectedFallRate = _fallRate / _sampleRate;
|
||||||
generic_block<AGC>::registerInput(_in);
|
generic_block<AGC>::registerInput(_in);
|
||||||
generic_block<AGC>::registerOutput(&out);
|
generic_block<AGC>::registerOutput(&out);
|
||||||
|
generic_block<AGC>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<float>* in) {
|
void setInput(stream<float>* in) {
|
||||||
|
assert(generic_block<AGC>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<AGC>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<AGC>::ctrlMtx);
|
||||||
generic_block<AGC>::tempStop();
|
generic_block<AGC>::tempStop();
|
||||||
generic_block<AGC>::unregisterInput(_in);
|
generic_block<AGC>::unregisterInput(_in);
|
||||||
@@ -111,34 +111,173 @@ namespace dsp {
|
|||||||
generic_block<AGC>::tempStart();
|
generic_block<AGC>::tempStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setSampleRate(float sampleRate) {
|
||||||
|
assert(generic_block<AGC>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<AGC>::ctrlMtx);
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
_CorrectedFallRate = _fallRate / _sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFallRate(float fallRate) {
|
||||||
|
assert(generic_block<AGC>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<AGC>::ctrlMtx);
|
||||||
|
_fallRate = fallRate;
|
||||||
|
_CorrectedFallRate = _fallRate / _sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
level = pow(10, ((10.0f * log10f(level)) - (_CorrectedFallRate * count)) / 10.0f);
|
||||||
|
|
||||||
|
if (level < 10e-14) { level = 10e-14; }
|
||||||
|
|
||||||
|
float absVal;
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
level = (fabsf(_in->data[i]) * _ratio) + (level * (1.0f - _ratio));
|
absVal = fabsf(_in->readBuf[i]);
|
||||||
out.data[i] = _in->data[i] / level;
|
if (absVal > level) { level = absVal; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
volk_32f_s32f_multiply_32f(out.writeBuf, _in->readBuf, 1.0f / level, count);
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<float> out;
|
stream<float> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
float level = 0.0f;
|
||||||
float level = 1.0f;
|
float _fallRate;
|
||||||
float _ratio;
|
float _CorrectedFallRate;
|
||||||
|
float _sampleRate;
|
||||||
stream<float>* _in;
|
stream<float>* _in;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ComplexAGC : public generic_block<ComplexAGC> {
|
||||||
|
public:
|
||||||
|
ComplexAGC() {}
|
||||||
|
|
||||||
|
ComplexAGC(stream<complex_t>* in, float setPoint, float maxGain, float rate) { init(in, setPoint, maxGain, rate); }
|
||||||
|
|
||||||
|
void init(stream<complex_t>* in, float setPoint, float maxGain, float rate) {
|
||||||
|
_in = in;
|
||||||
|
_setPoint = setPoint;
|
||||||
|
_maxGain = maxGain;
|
||||||
|
_rate = rate;
|
||||||
|
generic_block<ComplexAGC>::registerInput(_in);
|
||||||
|
generic_block<ComplexAGC>::registerOutput(&out);
|
||||||
|
generic_block<ComplexAGC>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<ComplexAGC>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<ComplexAGC>::ctrlMtx);
|
||||||
|
generic_block<ComplexAGC>::tempStop();
|
||||||
|
generic_block<ComplexAGC>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<ComplexAGC>::registerInput(_in);
|
||||||
|
generic_block<ComplexAGC>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSetPoint(float setPoint) {
|
||||||
|
assert(generic_block<ComplexAGC>::_block_init);
|
||||||
|
_setPoint = setPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMaxGain(float maxGain) {
|
||||||
|
assert(generic_block<ComplexAGC>::_block_init);
|
||||||
|
_maxGain = maxGain;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRate(float rate) {
|
||||||
|
assert(generic_block<ComplexAGC>::_block_init);
|
||||||
|
_rate = rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
dsp::complex_t val;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
val = _in->readBuf[i] * _gain;
|
||||||
|
out.writeBuf[i] = val;
|
||||||
|
_gain += (_setPoint - val.amplitude()) * _rate;
|
||||||
|
if (_gain > _maxGain) { _gain = _maxGain; }
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<complex_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float _gain = 1.0f;
|
||||||
|
float _setPoint = 1.0f;
|
||||||
|
float _maxGain = 10e4;
|
||||||
|
float _rate = 10e-4;
|
||||||
|
|
||||||
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class DelayImag : public generic_block<DelayImag> {
|
||||||
|
public:
|
||||||
|
DelayImag() {}
|
||||||
|
|
||||||
|
DelayImag(stream<complex_t>* in) { init(in); }
|
||||||
|
|
||||||
|
void init(stream<complex_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
generic_block<DelayImag>::registerInput(_in);
|
||||||
|
generic_block<DelayImag>::registerOutput(&out);
|
||||||
|
generic_block<DelayImag>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<DelayImag>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<DelayImag>::ctrlMtx);
|
||||||
|
generic_block<DelayImag>::tempStop();
|
||||||
|
generic_block<DelayImag>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<DelayImag>::registerInput(_in);
|
||||||
|
generic_block<DelayImag>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
dsp::complex_t val;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
val = _in->readBuf[i];
|
||||||
|
out.writeBuf[i].re = val.re;
|
||||||
|
out.writeBuf[i].im = lastIm;
|
||||||
|
lastIm = val.im;
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<complex_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float lastIm = 0.0f;
|
||||||
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class Volume : public generic_block<Volume<T>> {
|
class Volume : public generic_block<Volume<T>> {
|
||||||
public:
|
public:
|
||||||
@@ -146,16 +285,16 @@ namespace dsp {
|
|||||||
|
|
||||||
Volume(stream<T>* in, float volume) { init(in, volume); }
|
Volume(stream<T>* in, float volume) { init(in, volume); }
|
||||||
|
|
||||||
~Volume() { generic_block<Volume<T>>::stop(); }
|
|
||||||
|
|
||||||
void init(stream<T>* in, float volume) {
|
void init(stream<T>* in, float volume) {
|
||||||
_in = in;
|
_in = in;
|
||||||
_volume = volume;
|
_volume = volume;
|
||||||
generic_block<Volume<T>>::registerInput(_in);
|
generic_block<Volume<T>>::registerInput(_in);
|
||||||
generic_block<Volume<T>>::registerOutput(&out);
|
generic_block<Volume<T>>::registerOutput(&out);
|
||||||
|
generic_block<Volume<T>>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInputSize(stream<T>* in) {
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<Volume<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<Volume<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<Volume<T>>::ctrlMtx);
|
||||||
generic_block<Volume<T>>::tempStop();
|
generic_block<Volume<T>>::tempStop();
|
||||||
generic_block<Volume<T>>::unregisterInput(_in);
|
generic_block<Volume<T>>::unregisterInput(_in);
|
||||||
@@ -165,54 +304,55 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setVolume(float volume) {
|
void setVolume(float volume) {
|
||||||
|
assert(generic_block<Volume<T>>::_block_init);
|
||||||
_volume = volume;
|
_volume = volume;
|
||||||
level = powf(_volume, 2);
|
level = powf(_volume, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
float getVolume() {
|
float getVolume() {
|
||||||
|
assert(generic_block<Volume<T>>::_block_init);
|
||||||
return _volume;
|
return _volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMuted(bool muted) {
|
void setMuted(bool muted) {
|
||||||
|
assert(generic_block<Volume<T>>::_block_init);
|
||||||
_muted = muted;
|
_muted = muted;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getMuted() {
|
bool getMuted() {
|
||||||
|
assert(generic_block<Volume<T>>::_block_init);
|
||||||
return _muted;
|
return _muted;
|
||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
|
||||||
|
|
||||||
if (_muted) {
|
if (_muted) {
|
||||||
if constexpr (std::is_same_v<T, stereo_t>) {
|
if constexpr (std::is_same_v<T, stereo_t>) {
|
||||||
memset(out.data, 0, sizeof(stereo_t) * count);
|
memset(out.writeBuf, 0, sizeof(stereo_t) * count);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
memset(out.data, 0, sizeof(float) * count);
|
memset(out.writeBuf, 0, sizeof(float) * count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if constexpr (std::is_same_v<T, stereo_t>) {
|
if constexpr (std::is_same_v<T, stereo_t>) {
|
||||||
volk_32f_s32f_multiply_32f((float*)out.data, (float*)_in->data, level, count * 2);
|
volk_32f_s32f_multiply_32f((float*)out.writeBuf, (float*)_in->readBuf, level, count * 2);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
volk_32f_s32f_multiply_32f((float*)out.data, (float*)_in->data, level, count);
|
volk_32f_s32f_multiply_32f((float*)out.writeBuf, (float*)_in->readBuf, level, count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream<T> out;
|
stream<T> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
|
||||||
float level = 1.0f;
|
float level = 1.0f;
|
||||||
float _volume = 1.0f;
|
float _volume = 1.0f;
|
||||||
bool _muted = false;
|
bool _muted = false;
|
||||||
@@ -227,8 +367,10 @@ namespace dsp {
|
|||||||
Squelch(stream<complex_t>* in, float level) { init(in, level); }
|
Squelch(stream<complex_t>* in, float level) { init(in, level); }
|
||||||
|
|
||||||
~Squelch() {
|
~Squelch() {
|
||||||
|
if (!generic_block<Squelch>::_block_init) { return; }
|
||||||
generic_block<Squelch>::stop();
|
generic_block<Squelch>::stop();
|
||||||
delete[] normBuffer;
|
delete[] normBuffer;
|
||||||
|
generic_block<Squelch>::_block_init = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(stream<complex_t>* in, float level) {
|
void init(stream<complex_t>* in, float level) {
|
||||||
@@ -237,9 +379,11 @@ namespace dsp {
|
|||||||
normBuffer = new float[STREAM_BUFFER_SIZE];
|
normBuffer = new float[STREAM_BUFFER_SIZE];
|
||||||
generic_block<Squelch>::registerInput(_in);
|
generic_block<Squelch>::registerInput(_in);
|
||||||
generic_block<Squelch>::registerOutput(&out);
|
generic_block<Squelch>::registerOutput(&out);
|
||||||
|
generic_block<Squelch>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<complex_t>* in) {
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<Squelch>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<Squelch>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<Squelch>::ctrlMtx);
|
||||||
generic_block<Squelch>::tempStop();
|
generic_block<Squelch>::tempStop();
|
||||||
generic_block<Squelch>::unregisterInput(_in);
|
generic_block<Squelch>::unregisterInput(_in);
|
||||||
@@ -249,32 +393,33 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setLevel(float level) {
|
void setLevel(float level) {
|
||||||
|
assert(generic_block<Squelch>::_block_init);
|
||||||
_level = level;
|
_level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
float getLevel() {
|
float getLevel() {
|
||||||
|
assert(generic_block<Squelch>::_block_init);
|
||||||
return _level;
|
return _level;
|
||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
if (out.aquire() < 0) { return -1; }
|
float sum;
|
||||||
float sum = 0.0f;
|
volk_32fc_magnitude_32f(normBuffer, (lv_32fc_t*)_in->readBuf, count);
|
||||||
volk_32fc_magnitude_32f(normBuffer, (lv_32fc_t*)_in->data, count);
|
|
||||||
volk_32f_accumulator_s32f(&sum, normBuffer, count);
|
volk_32f_accumulator_s32f(&sum, normBuffer, count);
|
||||||
sum /= (float)count;
|
sum /= (float)count;
|
||||||
|
|
||||||
if (10.0f * log10f(sum) >= _level) {
|
if (10.0f * log10f(sum) >= _level) {
|
||||||
memcpy(out.data, _in->data, count * sizeof(complex_t));
|
memcpy(out.writeBuf, _in->readBuf, count * sizeof(complex_t));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
memset(out.data, 0, count * sizeof(complex_t));
|
memset(out.writeBuf, 0, count * sizeof(complex_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
out.write(count);
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,10 +427,192 @@ namespace dsp {
|
|||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
|
||||||
float* normBuffer;
|
float* normBuffer;
|
||||||
float _level = -50.0f;
|
float _level = -50.0f;
|
||||||
stream<complex_t>* _in;
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class Packer : public generic_block<Packer<T>> {
|
||||||
|
public:
|
||||||
|
Packer() {}
|
||||||
|
|
||||||
|
Packer(stream<T>* in, int count) { init(in, count); }
|
||||||
|
|
||||||
|
void init(stream<T>* in, int count) {
|
||||||
|
_in = in;
|
||||||
|
samples = count;
|
||||||
|
generic_block<Packer<T>>::registerInput(_in);
|
||||||
|
generic_block<Packer<T>>::registerOutput(&out);
|
||||||
|
generic_block<Packer<T>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<Packer<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Packer<T>>::ctrlMtx);
|
||||||
|
generic_block<Packer<T>>::tempStop();
|
||||||
|
generic_block<Packer<T>>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<Packer<T>>::registerInput(_in);
|
||||||
|
generic_block<Packer<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSampleCount(int count) {
|
||||||
|
assert(generic_block<Packer<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Packer<T>>::ctrlMtx);
|
||||||
|
generic_block<Packer<T>>::tempStop();
|
||||||
|
samples = count;
|
||||||
|
generic_block<Packer<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) {
|
||||||
|
read = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
out.writeBuf[read++] = _in->readBuf[i];
|
||||||
|
if (read >= samples) {
|
||||||
|
read = 0;
|
||||||
|
if (!out.swap(samples)) {
|
||||||
|
_in->flush();
|
||||||
|
read = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<T> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int samples = 1;
|
||||||
|
int read = 0;
|
||||||
|
stream<T>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Threshold : public generic_block<Threshold> {
|
||||||
|
public:
|
||||||
|
Threshold() {}
|
||||||
|
|
||||||
|
Threshold(stream<float>* in) { init(in); }
|
||||||
|
|
||||||
|
~Threshold() {
|
||||||
|
if (!generic_block<Threshold>::_block_init) { return; }
|
||||||
|
generic_block<Threshold>::stop();
|
||||||
|
delete[] normBuffer;
|
||||||
|
generic_block<Threshold>::_block_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<float>* in) {
|
||||||
|
_in = in;
|
||||||
|
normBuffer = new float[STREAM_BUFFER_SIZE];
|
||||||
|
generic_block<Threshold>::registerInput(_in);
|
||||||
|
generic_block<Threshold>::registerOutput(&out);
|
||||||
|
generic_block<Threshold>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<float>* in) {
|
||||||
|
assert(generic_block<Threshold>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<Threshold>::ctrlMtx);
|
||||||
|
generic_block<Threshold>::tempStop();
|
||||||
|
generic_block<Threshold>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<Threshold>::registerInput(_in);
|
||||||
|
generic_block<Threshold>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLevel(float level) {
|
||||||
|
assert(generic_block<Threshold>::_block_init);
|
||||||
|
_level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getLevel() {
|
||||||
|
assert(generic_block<Threshold>::_block_init);
|
||||||
|
return _level;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
out.writeBuf[i] = (_in->readBuf[i] > 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<uint8_t> out;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
float* normBuffer;
|
||||||
|
float _level = -50.0f;
|
||||||
|
stream<float>* _in;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class BFMPilotToStereo : public generic_block<BFMPilotToStereo> {
|
||||||
|
public:
|
||||||
|
BFMPilotToStereo() {}
|
||||||
|
|
||||||
|
BFMPilotToStereo(stream<complex_t>* in) { init(in); }
|
||||||
|
|
||||||
|
~BFMPilotToStereo() {
|
||||||
|
generic_block<BFMPilotToStereo>::stop();
|
||||||
|
delete[] buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<complex_t>* in) {
|
||||||
|
_in = in;
|
||||||
|
|
||||||
|
buffer = new complex_t[STREAM_BUFFER_SIZE];
|
||||||
|
|
||||||
|
generic_block<BFMPilotToStereo>::registerInput(_in);
|
||||||
|
generic_block<BFMPilotToStereo>::registerOutput(&out);
|
||||||
|
generic_block<BFMPilotToStereo>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputs(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<BFMPilotToStereo>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<BFMPilotToStereo>::ctrlMtx);
|
||||||
|
generic_block<BFMPilotToStereo>::tempStop();
|
||||||
|
generic_block<BFMPilotToStereo>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<BFMPilotToStereo>::registerInput(_in);
|
||||||
|
generic_block<BFMPilotToStereo>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
volk_32fc_x2_multiply_32fc((lv_32fc_t*)buffer, (lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, count);
|
||||||
|
_in->flush();
|
||||||
|
|
||||||
|
volk_32fc_conjugate_32fc((lv_32fc_t*)out.writeBuf, (lv_32fc_t*)buffer, count);
|
||||||
|
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<complex_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
|
complex_t* buffer;
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <dsp/window.h>
|
#include <dsp/window.h>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <dsp/math.h>
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -13,9 +14,12 @@ namespace dsp {
|
|||||||
PolyphaseResampler(stream<T>* in, dsp::filter_window::generic_window* window, float inSampleRate, float outSampleRate) { init(in, window, inSampleRate, outSampleRate); }
|
PolyphaseResampler(stream<T>* in, dsp::filter_window::generic_window* window, float inSampleRate, float outSampleRate) { init(in, window, inSampleRate, outSampleRate); }
|
||||||
|
|
||||||
~PolyphaseResampler() {
|
~PolyphaseResampler() {
|
||||||
|
if (!generic_block<PolyphaseResampler<T>>::_block_init) { return; }
|
||||||
generic_block<PolyphaseResampler<T>>::stop();
|
generic_block<PolyphaseResampler<T>>::stop();
|
||||||
volk_free(buffer);
|
volk_free(buffer);
|
||||||
volk_free(taps);
|
volk_free(taps);
|
||||||
|
freeTapPhases();
|
||||||
|
generic_block<PolyphaseResampler<T>>::_block_init = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(stream<T>* in, dsp::filter_window::generic_window* window, float inSampleRate, float outSampleRate) {
|
void init(stream<T>* in, dsp::filter_window::generic_window* window, float inSampleRate, float outSampleRate) {
|
||||||
@@ -32,51 +36,69 @@ namespace dsp {
|
|||||||
taps = (float*)volk_malloc(tapCount * sizeof(float), volk_get_alignment());
|
taps = (float*)volk_malloc(tapCount * sizeof(float), volk_get_alignment());
|
||||||
_window->createTaps(taps, tapCount, _interp);
|
_window->createTaps(taps, tapCount, _interp);
|
||||||
|
|
||||||
|
buildTapPhases();
|
||||||
|
|
||||||
buffer = (T*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(T) * 2, volk_get_alignment());
|
buffer = (T*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(T) * 2, volk_get_alignment());
|
||||||
memset(buffer, 0, STREAM_BUFFER_SIZE * sizeof(T) * 2);
|
memset(buffer, 0, STREAM_BUFFER_SIZE * sizeof(T) * 2);
|
||||||
bufStart = &buffer[tapCount];
|
counter = 0;
|
||||||
|
offset = 0;
|
||||||
generic_block<PolyphaseResampler<T>>::registerInput(_in);
|
generic_block<PolyphaseResampler<T>>::registerInput(_in);
|
||||||
generic_block<PolyphaseResampler<T>>::registerOutput(&out);
|
generic_block<PolyphaseResampler<T>>::registerOutput(&out);
|
||||||
|
generic_block<PolyphaseResampler<T>>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<T>* in) {
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<PolyphaseResampler<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<PolyphaseResampler<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<PolyphaseResampler<T>>::ctrlMtx);
|
||||||
generic_block<PolyphaseResampler<T>>::tempStop();
|
generic_block<PolyphaseResampler<T>>::tempStop();
|
||||||
generic_block<PolyphaseResampler<T>>::unregisterInput(_in);
|
generic_block<PolyphaseResampler<T>>::unregisterInput(_in);
|
||||||
_in = in;
|
_in = in;
|
||||||
|
counter = 0;
|
||||||
|
offset = 0;
|
||||||
generic_block<PolyphaseResampler<T>>::registerInput(_in);
|
generic_block<PolyphaseResampler<T>>::registerInput(_in);
|
||||||
generic_block<PolyphaseResampler<T>>::tempStart();
|
generic_block<PolyphaseResampler<T>>::tempStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInSampleRate(float inSampleRate) {
|
void setInSampleRate(float inSampleRate) {
|
||||||
|
assert(generic_block<PolyphaseResampler<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<PolyphaseResampler<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<PolyphaseResampler<T>>::ctrlMtx);
|
||||||
generic_block<PolyphaseResampler<T>>::tempStop();
|
generic_block<PolyphaseResampler<T>>::tempStop();
|
||||||
_inSampleRate = inSampleRate;
|
_inSampleRate = inSampleRate;
|
||||||
int _gcd = std::gcd((int)_inSampleRate, (int)_outSampleRate);
|
int _gcd = std::gcd((int)_inSampleRate, (int)_outSampleRate);
|
||||||
_interp = _outSampleRate / _gcd;
|
_interp = _outSampleRate / _gcd;
|
||||||
_decim = _inSampleRate / _gcd;
|
_decim = _inSampleRate / _gcd;
|
||||||
|
buildTapPhases();
|
||||||
|
counter = 0;
|
||||||
|
offset = 0;
|
||||||
generic_block<PolyphaseResampler<T>>::tempStart();
|
generic_block<PolyphaseResampler<T>>::tempStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOutSampleRate(float outSampleRate) {
|
void setOutSampleRate(float outSampleRate) {
|
||||||
|
assert(generic_block<PolyphaseResampler<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<PolyphaseResampler<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<PolyphaseResampler<T>>::ctrlMtx);
|
||||||
generic_block<PolyphaseResampler<T>>::tempStop();
|
generic_block<PolyphaseResampler<T>>::tempStop();
|
||||||
_outSampleRate = outSampleRate;
|
_outSampleRate = outSampleRate;
|
||||||
int _gcd = std::gcd((int)_inSampleRate, (int)_outSampleRate);
|
int _gcd = std::gcd((int)_inSampleRate, (int)_outSampleRate);
|
||||||
_interp = _outSampleRate / _gcd;
|
_interp = _outSampleRate / _gcd;
|
||||||
_decim = _inSampleRate / _gcd;
|
_decim = _inSampleRate / _gcd;
|
||||||
|
buildTapPhases();
|
||||||
|
counter = 0;
|
||||||
|
offset = 0;
|
||||||
generic_block<PolyphaseResampler<T>>::tempStart();
|
generic_block<PolyphaseResampler<T>>::tempStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
int getInterpolation() {
|
int getInterpolation() {
|
||||||
|
assert(generic_block<PolyphaseResampler<T>>::_block_init);
|
||||||
return _interp;
|
return _interp;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getDecimation() {
|
int getDecimation() {
|
||||||
|
assert(generic_block<PolyphaseResampler<T>>::_block_init);
|
||||||
return _decim;
|
return _decim;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateWindow(dsp::filter_window::generic_window* window) {
|
void updateWindow(dsp::filter_window::generic_window* window) {
|
||||||
|
assert(generic_block<PolyphaseResampler<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<PolyphaseResampler<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<PolyphaseResampler<T>>::ctrlMtx);
|
||||||
generic_block<PolyphaseResampler<T>>::tempStop();
|
generic_block<PolyphaseResampler<T>>::tempStop();
|
||||||
_window = window;
|
_window = window;
|
||||||
@@ -84,53 +106,53 @@ namespace dsp {
|
|||||||
tapCount = window->getTapCount();
|
tapCount = window->getTapCount();
|
||||||
taps = (float*)volk_malloc(tapCount * sizeof(float), volk_get_alignment());
|
taps = (float*)volk_malloc(tapCount * sizeof(float), volk_get_alignment());
|
||||||
window->createTaps(taps, tapCount, _interp);
|
window->createTaps(taps, tapCount, _interp);
|
||||||
bufStart = &buffer[tapCount];
|
buildTapPhases();
|
||||||
|
counter = 0;
|
||||||
|
offset = 0;
|
||||||
generic_block<PolyphaseResampler<T>>::tempStart();
|
generic_block<PolyphaseResampler<T>>::tempStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
int calcOutSize(int in) {
|
int calcOutSize(int in) {
|
||||||
|
assert(generic_block<PolyphaseResampler<T>>::_block_init);
|
||||||
return (in * _interp) / _decim;
|
return (in * _interp) / _decim;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int run() override {
|
virtual int run() override {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int outCount = calcOutSize(count);
|
memcpy(&buffer[tapsPerPhase], _in->readBuf, count * sizeof(T));
|
||||||
|
|
||||||
memcpy(&buffer[tapCount], _in->data, count * sizeof(T));
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
|
|
||||||
// Write to output
|
// Write to output
|
||||||
if (out.aquire() < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int outIndex = 0;
|
int outIndex = 0;
|
||||||
|
int inOffset = offset;
|
||||||
|
int _counter = counter;
|
||||||
if constexpr (std::is_same_v<T, float>) {
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
for (int i = 0; outIndex < outCount; i += _decim) {
|
while (inOffset < count) {
|
||||||
out.data[outIndex] = 0;
|
volk_32f_x2_dot_prod_32f(&out.writeBuf[outIndex++], &buffer[inOffset], tapPhases[_counter], tapsPerPhase);
|
||||||
for (int j = i % _interp; j < tapCount; j += _interp) {
|
_counter += _decim;
|
||||||
out.data[outIndex] += buffer[((i - j) / _interp) + tapCount] * taps[j];
|
inOffset += (_counter / _interp);
|
||||||
}
|
_counter = (_counter % _interp);
|
||||||
outIndex++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if constexpr (std::is_same_v<T, complex_t>) {
|
if constexpr (std::is_same_v<T, complex_t> || std::is_same_v<T, stereo_t>) {
|
||||||
for (int i = 0; outIndex < outCount; i += _decim) {
|
while (inOffset < count) {
|
||||||
out.data[outIndex].i = 0;
|
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&out.writeBuf[outIndex++], (lv_32fc_t*)&buffer[inOffset], tapPhases[_counter], tapsPerPhase);
|
||||||
out.data[outIndex].q = 0;
|
_counter += _decim;
|
||||||
for (int j = i % _interp; j < tapCount; j += _interp) {
|
inOffset += (_counter / _interp);
|
||||||
out.data[outIndex].i += buffer[((i - j) / _interp) + tapCount].i * taps[j];
|
_counter = (_counter % _interp);
|
||||||
out.data[outIndex].q += buffer[((i - j) / _interp) + tapCount].q * taps[j];
|
|
||||||
}
|
|
||||||
outIndex++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.write(outCount);
|
|
||||||
|
|
||||||
memmove(buffer, &buffer[count], tapCount * sizeof(T));
|
if (!out.swap(outIndex)) { return -1; }
|
||||||
|
|
||||||
|
offset = inOffset - count;
|
||||||
|
counter = _counter;
|
||||||
|
|
||||||
|
memmove(buffer, &buffer[count], tapsPerPhase * sizeof(T));
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@@ -138,7 +160,44 @@ namespace dsp {
|
|||||||
stream<T> out;
|
stream<T> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
void buildTapPhases(){
|
||||||
|
if(!taps){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!tapPhases.empty()){
|
||||||
|
freeTapPhases();
|
||||||
|
}
|
||||||
|
|
||||||
|
int phases = _interp;
|
||||||
|
tapsPerPhase = (tapCount+phases-1)/phases; //Integer division ceiling
|
||||||
|
|
||||||
|
bufStart = &buffer[tapsPerPhase];
|
||||||
|
|
||||||
|
for(int i = 0; i < phases; i++){
|
||||||
|
tapPhases.push_back((float*)volk_malloc(tapsPerPhase * sizeof(float), volk_get_alignment()));
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentTap = 0;
|
||||||
|
for(int tap = 0; tap < tapsPerPhase; tap++) {
|
||||||
|
for (int phase = 0; phase < phases; phase++) {
|
||||||
|
if(currentTap < tapCount) {
|
||||||
|
tapPhases[(_interp - 1) - phase][tap] = taps[currentTap++];
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
tapPhases[(_interp - 1) - phase][tap] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeTapPhases(){
|
||||||
|
for(auto & tapPhase : tapPhases){
|
||||||
|
volk_free(tapPhase);
|
||||||
|
}
|
||||||
|
tapPhases.clear();
|
||||||
|
}
|
||||||
|
|
||||||
stream<T>* _in;
|
stream<T>* _in;
|
||||||
|
|
||||||
dsp::filter_window::generic_window* _window;
|
dsp::filter_window::generic_window* _window;
|
||||||
@@ -150,5 +209,11 @@ namespace dsp {
|
|||||||
float _inSampleRate, _outSampleRate;
|
float _inSampleRate, _outSampleRate;
|
||||||
float* taps;
|
float* taps;
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
int tapsPerPhase;
|
||||||
|
std::vector<float*> tapPhases;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -13,14 +13,14 @@ namespace dsp {
|
|||||||
|
|
||||||
Splitter(stream<T>* in) { init(in); }
|
Splitter(stream<T>* in) { init(in); }
|
||||||
|
|
||||||
~Splitter() { generic_block<Splitter>::stop(); }
|
|
||||||
|
|
||||||
void init(stream<T>* in) {
|
void init(stream<T>* in) {
|
||||||
_in = in;
|
_in = in;
|
||||||
generic_block<Splitter>::registerInput(_in);
|
generic_block<Splitter>::registerInput(_in);
|
||||||
|
generic_block<Splitter>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<T>* in) {
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<Splitter>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<Splitter>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<Splitter>::ctrlMtx);
|
||||||
generic_block<Splitter>::tempStop();
|
generic_block<Splitter>::tempStop();
|
||||||
generic_block<Splitter>::unregisterInput(_in);
|
generic_block<Splitter>::unregisterInput(_in);
|
||||||
@@ -30,6 +30,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void bindStream(stream<T>* stream) {
|
void bindStream(stream<T>* stream) {
|
||||||
|
assert(generic_block<Splitter>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<Splitter>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<Splitter>::ctrlMtx);
|
||||||
generic_block<Splitter>::tempStop();
|
generic_block<Splitter>::tempStop();
|
||||||
out.push_back(stream);
|
out.push_back(stream);
|
||||||
@@ -38,6 +39,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void unbindStream(stream<T>* stream) {
|
void unbindStream(stream<T>* stream) {
|
||||||
|
assert(generic_block<Splitter>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<Splitter>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<Splitter>::ctrlMtx);
|
||||||
generic_block<Splitter>::tempStop();
|
generic_block<Splitter>::tempStop();
|
||||||
generic_block<Splitter>::unregisterOutput(stream);
|
generic_block<Splitter>::unregisterOutput(stream);
|
||||||
@@ -51,9 +53,8 @@ namespace dsp {
|
|||||||
int count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
for (const auto& stream : out) {
|
for (const auto& stream : out) {
|
||||||
if (stream->aquire() < 0) { return -1; }
|
memcpy(stream->writeBuf, _in->readBuf, count * sizeof(T));
|
||||||
memcpy(stream->data, _in->data, count * sizeof(T));
|
if (!stream->swap(count)) { return -1; }
|
||||||
stream->write(count);
|
|
||||||
}
|
}
|
||||||
_in->flush();
|
_in->flush();
|
||||||
return count;
|
return count;
|
||||||
@@ -73,7 +74,12 @@ namespace dsp {
|
|||||||
|
|
||||||
Reshaper(stream<T>* in, int keep, int skip) { init(in, keep, skip); }
|
Reshaper(stream<T>* in, int keep, int skip) { init(in, keep, skip); }
|
||||||
|
|
||||||
~Reshaper() { generic_block<Reshaper<T>>::stop(); }
|
// NOTE: For some reason, the base class destrcutor doesn't get called.... this is a temporary fix I guess
|
||||||
|
// I also don't check for _block_init for the exact sample reason, something's weird
|
||||||
|
~Reshaper() {
|
||||||
|
if (!generic_block<Reshaper<T>>::_block_init) { return; }
|
||||||
|
generic_block<Reshaper<T>>::stop();
|
||||||
|
}
|
||||||
|
|
||||||
void init(stream<T>* in, int keep, int skip) {
|
void init(stream<T>* in, int keep, int skip) {
|
||||||
_in = in;
|
_in = in;
|
||||||
@@ -82,9 +88,11 @@ namespace dsp {
|
|||||||
ringBuf.init(keep * 2);
|
ringBuf.init(keep * 2);
|
||||||
generic_block<Reshaper<T>>::registerInput(_in);
|
generic_block<Reshaper<T>>::registerInput(_in);
|
||||||
generic_block<Reshaper<T>>::registerOutput(&out);
|
generic_block<Reshaper<T>>::registerOutput(&out);
|
||||||
|
generic_block<Reshaper<T>>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<T>* in) {
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<Reshaper<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<Reshaper<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<Reshaper<T>>::ctrlMtx);
|
||||||
generic_block<Reshaper<T>>::tempStop();
|
generic_block<Reshaper<T>>::tempStop();
|
||||||
generic_block<Reshaper<T>>::unregisterInput(_in);
|
generic_block<Reshaper<T>>::unregisterInput(_in);
|
||||||
@@ -94,28 +102,26 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setKeep(int keep) {
|
void setKeep(int keep) {
|
||||||
|
assert(generic_block<Reshaper<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<Reshaper<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<Reshaper<T>>::ctrlMtx);
|
||||||
generic_block<Reshaper<T>>::tempStop();
|
generic_block<Reshaper<T>>::tempStop();
|
||||||
generic_block<Reshaper<T>>::unregisterInput(_in);
|
|
||||||
_keep = keep;
|
_keep = keep;
|
||||||
ringBuf.setMaxLatency(keep * 2);
|
ringBuf.setMaxLatency(keep * 2);
|
||||||
generic_block<Reshaper<T>>::registerInput(_in);
|
|
||||||
generic_block<Reshaper<T>>::tempStart();
|
generic_block<Reshaper<T>>::tempStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSkip(int skip) {
|
void setSkip(int skip) {
|
||||||
|
assert(generic_block<Reshaper<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<Reshaper<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<Reshaper<T>>::ctrlMtx);
|
||||||
generic_block<Reshaper<T>>::tempStop();
|
generic_block<Reshaper<T>>::tempStop();
|
||||||
generic_block<Reshaper<T>>::unregisterInput(_in);
|
|
||||||
_skip = skip;
|
_skip = skip;
|
||||||
generic_block<Reshaper<T>>::registerInput(_in);
|
|
||||||
generic_block<Reshaper<T>>::tempStart();
|
generic_block<Reshaper<T>>::tempStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
int count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
ringBuf.write(_in->data, count);
|
ringBuf.write(_in->readBuf, count);
|
||||||
_in->flush();
|
_in->flush();
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@@ -123,7 +129,7 @@ namespace dsp {
|
|||||||
stream<T> out;
|
stream<T> out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doStart() {
|
void doStart() override {
|
||||||
workThread = std::thread(&Reshaper<T>::loop, this);
|
workThread = std::thread(&Reshaper<T>::loop, this);
|
||||||
bufferWorkerThread = std::thread(&Reshaper<T>::bufferWorker, this);
|
bufferWorkerThread = std::thread(&Reshaper<T>::bufferWorker, this);
|
||||||
}
|
}
|
||||||
@@ -132,7 +138,7 @@ namespace dsp {
|
|||||||
while (run() >= 0);
|
while (run() >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void doStop() {
|
void doStop() override {
|
||||||
_in->stopReader();
|
_in->stopReader();
|
||||||
ringBuf.stopReader();
|
ringBuf.stopReader();
|
||||||
out.stopWriter();
|
out.stopWriter();
|
||||||
@@ -152,29 +158,30 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void bufferWorker() {
|
void bufferWorker() {
|
||||||
complex_t* buf = new complex_t[_keep];
|
T* buf = new T[_keep];
|
||||||
bool delay = _skip < 0;
|
bool delay = _skip < 0;
|
||||||
|
|
||||||
int readCount = std::min<int>(_keep + _skip, _keep);
|
int readCount = std::min<int>(_keep + _skip, _keep);
|
||||||
int skip = std::max<int>(_skip, 0);
|
int skip = std::max<int>(_skip, 0);
|
||||||
int delaySize = (-_skip) * sizeof(complex_t);
|
int delaySize = (-_skip) * sizeof(T);
|
||||||
int delayCount = (-_skip);
|
int delayCount = (-_skip);
|
||||||
|
|
||||||
complex_t* start = &buf[std::max<int>(-_skip, 0)];
|
T* start = &buf[std::max<int>(-_skip, 0)];
|
||||||
complex_t* delayStart = &buf[_keep + _skip];
|
T* delayStart = &buf[_keep + _skip];
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (delay) {
|
if (delay) {
|
||||||
memmove(buf, delayStart, delaySize);
|
memmove(buf, delayStart, delaySize);
|
||||||
for (int i = 0; i < delayCount; i++) {
|
if constexpr (std::is_same_v<T, complex_t> || std::is_same_v<T, stereo_t>) {
|
||||||
buf[i].i /= 10.0f;
|
for (int i = 0; i < delayCount; i++) {
|
||||||
buf[i].q /= 10.0f;
|
buf[i].re /= 10.0f;
|
||||||
|
buf[i].im /= 10.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ringBuf.readAndSkip(start, readCount, skip) < 0) { break; };
|
if (ringBuf.readAndSkip(start, readCount, skip) < 0) { break; };
|
||||||
if (out.aquire() < 0) { break; }
|
memcpy(out.writeBuf, buf, _keep * sizeof(T));
|
||||||
memcpy(out.data, buf, _keep * sizeof(complex_t));
|
if (!out.swap(_keep)) { break; }
|
||||||
out.write(_keep);
|
|
||||||
}
|
}
|
||||||
delete[] buf;
|
delete[] buf;
|
||||||
}
|
}
|
||||||
@@ -186,6 +193,5 @@ namespace dsp {
|
|||||||
std::thread workThread;
|
std::thread workThread;
|
||||||
int _keep, _skip;
|
int _keep, _skip;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <dsp/block.h>
|
#include <dsp/block.h>
|
||||||
#include <dsp/buffer.h>
|
#include <dsp/buffer.h>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -10,16 +11,16 @@ namespace dsp {
|
|||||||
|
|
||||||
HandlerSink(stream<T>* in, void (*handler)(T* data, int count, void* ctx), void* ctx) { init(in, handler, ctx); }
|
HandlerSink(stream<T>* in, void (*handler)(T* data, int count, void* ctx), void* ctx) { init(in, handler, ctx); }
|
||||||
|
|
||||||
~HandlerSink() { generic_block<HandlerSink<T>>::stop(); }
|
|
||||||
|
|
||||||
void init(stream<T>* in, void (*handler)(T* data, int count, void* ctx), void* ctx) {
|
void init(stream<T>* in, void (*handler)(T* data, int count, void* ctx), void* ctx) {
|
||||||
_in = in;
|
_in = in;
|
||||||
_handler = handler;
|
_handler = handler;
|
||||||
_ctx = ctx;
|
_ctx = ctx;
|
||||||
generic_block<HandlerSink<T>>::registerInput(_in);
|
generic_block<HandlerSink<T>>::registerInput(_in);
|
||||||
|
generic_block<HandlerSink<T>>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<T>* in) {
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<HandlerSink<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<HandlerSink<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<HandlerSink<T>>::ctrlMtx);
|
||||||
generic_block<HandlerSink<T>>::tempStop();
|
generic_block<HandlerSink<T>>::tempStop();
|
||||||
generic_block<HandlerSink<T>>::unregisterInput(_in);
|
generic_block<HandlerSink<T>>::unregisterInput(_in);
|
||||||
@@ -29,6 +30,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setHandler(void (*handler)(T* data, int count, void* ctx), void* ctx) {
|
void setHandler(void (*handler)(T* data, int count, void* ctx), void* ctx) {
|
||||||
|
assert(generic_block<HandlerSink<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<HandlerSink<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<HandlerSink<T>>::ctrlMtx);
|
||||||
generic_block<HandlerSink<T>>::tempStop();
|
generic_block<HandlerSink<T>>::tempStop();
|
||||||
_handler = handler;
|
_handler = handler;
|
||||||
@@ -37,15 +39,14 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
_handler(_in->data, count, _ctx);
|
_handler(_in->readBuf, count, _ctx);
|
||||||
_in->flush();
|
_in->flush();
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
|
||||||
stream<T>* _in;
|
stream<T>* _in;
|
||||||
void (*_handler)(T* data, int count, void* ctx);
|
void (*_handler)(T* data, int count, void* ctx);
|
||||||
void* _ctx;
|
void* _ctx;
|
||||||
@@ -59,15 +60,15 @@ namespace dsp {
|
|||||||
|
|
||||||
RingBufferSink(stream<T>* in) { init(in); }
|
RingBufferSink(stream<T>* in) { init(in); }
|
||||||
|
|
||||||
~RingBufferSink() { generic_block<RingBufferSink<T>>::stop(); }
|
|
||||||
|
|
||||||
void init(stream<T>* in) {
|
void init(stream<T>* in) {
|
||||||
_in = in;
|
_in = in;
|
||||||
data.init(480); // TODO: Use an argument
|
data.init(480); // TODO: Use an argument
|
||||||
generic_block<RingBufferSink<T>>::registerInput(_in);
|
generic_block<RingBufferSink<T>>::registerInput(_in);
|
||||||
|
generic_block<RingBufferSink<T>>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<T>* in) {
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<RingBufferSink<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<RingBufferSink<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<RingBufferSink<T>>::ctrlMtx);
|
||||||
generic_block<RingBufferSink<T>>::tempStop();
|
generic_block<RingBufferSink<T>>::tempStop();
|
||||||
generic_block<RingBufferSink<T>>::unregisterInput(_in);
|
generic_block<RingBufferSink<T>>::unregisterInput(_in);
|
||||||
@@ -77,9 +78,9 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
if (data.write(_in->data, count) < 0) { return -1; }
|
if (data.write(_in->readBuf, count) < 0) { return -1; }
|
||||||
_in->flush();
|
_in->flush();
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@@ -97,7 +98,6 @@ namespace dsp {
|
|||||||
data.clearWriteStop();
|
data.clearWriteStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
int count;
|
|
||||||
stream<T>* _in;
|
stream<T>* _in;
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -109,14 +109,14 @@ namespace dsp {
|
|||||||
|
|
||||||
NullSink(stream<T>* in) { init(in); }
|
NullSink(stream<T>* in) { init(in); }
|
||||||
|
|
||||||
~NullSink() { generic_block<NullSink<T>>::stop(); }
|
|
||||||
|
|
||||||
void init(stream<T>* in) {
|
void init(stream<T>* in) {
|
||||||
_in = in;
|
_in = in;
|
||||||
generic_block<NullSink<T>>::registerInput(_in);
|
generic_block<NullSink<T>>::registerInput(_in);
|
||||||
|
generic_block<NullSink<T>>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<T>* in) {
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<NullSink<T>>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<NullSink<T>>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<NullSink<T>>::ctrlMtx);
|
||||||
generic_block<NullSink<T>>::tempStop();
|
generic_block<NullSink<T>>::tempStop();
|
||||||
generic_block<NullSink<T>>::unregisterInput(_in);
|
generic_block<NullSink<T>>::unregisterInput(_in);
|
||||||
@@ -126,15 +126,68 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
count = _in->read();
|
int count = _in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
_in->flush();
|
_in->flush();
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
|
||||||
stream<T>* _in;
|
stream<T>* _in;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class FileSink : public generic_block<FileSink<T>> {
|
||||||
|
public:
|
||||||
|
FileSink() {}
|
||||||
|
|
||||||
|
FileSink(stream<T>* in, std::string path) { init(in, path); }
|
||||||
|
|
||||||
|
~FileSink() {
|
||||||
|
if (!generic_block<FileSink<T>>::_block_init) { return; }
|
||||||
|
generic_block<FileSink<T>>::stop();
|
||||||
|
if (file.is_open()) { file.close(); }
|
||||||
|
generic_block<FileSink<T>>::_block_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<T>* in, std::string path) {
|
||||||
|
_in = in;
|
||||||
|
file = std::ofstream(path, std::ios::binary);
|
||||||
|
generic_block<FileSink<T>>::registerInput(_in);
|
||||||
|
generic_block<FileSink<T>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<FileSink<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<FileSink<T>>::ctrlMtx);
|
||||||
|
generic_block<FileSink<T>>::tempStop();
|
||||||
|
generic_block<FileSink<T>>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<FileSink<T>>::registerInput(_in);
|
||||||
|
generic_block<FileSink<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOpen() {
|
||||||
|
assert(generic_block<FileSink<T>>::_block_init);
|
||||||
|
return file.is_open();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
if (file.is_open()) {
|
||||||
|
file.write((char*)_in->readBuf, count * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<T>* _in;
|
||||||
|
std::ofstream file;
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <dsp/block.h>
|
#include <dsp/block.h>
|
||||||
|
#include <dsp/math.h>
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
class SineSource : public generic_block<SineSource> {
|
class SineSource : public generic_block<SineSource> {
|
||||||
@@ -8,8 +9,6 @@ namespace dsp {
|
|||||||
|
|
||||||
SineSource(int blockSize, float sampleRate, float freq) { init(blockSize, sampleRate, freq); }
|
SineSource(int blockSize, float sampleRate, float freq) { init(blockSize, sampleRate, freq); }
|
||||||
|
|
||||||
~SineSource() { generic_block<SineSource>::stop(); }
|
|
||||||
|
|
||||||
void init(int blockSize, float sampleRate, float freq) {
|
void init(int blockSize, float sampleRate, float freq) {
|
||||||
_blockSize = blockSize;
|
_blockSize = blockSize;
|
||||||
_sampleRate = sampleRate;
|
_sampleRate = sampleRate;
|
||||||
@@ -21,9 +20,11 @@ namespace dsp {
|
|||||||
phase = lv_cmake(1.0f, 0.0f);
|
phase = lv_cmake(1.0f, 0.0f);
|
||||||
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
||||||
generic_block<SineSource>::registerOutput(&out);
|
generic_block<SineSource>::registerOutput(&out);
|
||||||
|
generic_block<SineSource>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBlockSize(int blockSize) {
|
void setBlockSize(int blockSize) {
|
||||||
|
assert(generic_block<SineSource>::_block_init);
|
||||||
std::lock_guard<std::mutex> lck(generic_block<SineSource>::ctrlMtx);
|
std::lock_guard<std::mutex> lck(generic_block<SineSource>::ctrlMtx);
|
||||||
generic_block<SineSource>::tempStop();
|
generic_block<SineSource>::tempStop();
|
||||||
_blockSize = blockSize;
|
_blockSize = blockSize;
|
||||||
@@ -31,33 +32,35 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int getBlockSize() {
|
int getBlockSize() {
|
||||||
|
assert(generic_block<SineSource>::_block_init);
|
||||||
return _blockSize;
|
return _blockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSampleRate(float sampleRate) {
|
void setSampleRate(float sampleRate) {
|
||||||
// No need to restart
|
assert(generic_block<SineSource>::_block_init);
|
||||||
_sampleRate = sampleRate;
|
_sampleRate = sampleRate;
|
||||||
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
||||||
}
|
}
|
||||||
|
|
||||||
float getSampleRate() {
|
float getSampleRate() {
|
||||||
|
assert(generic_block<SineSource>::_block_init);
|
||||||
return _sampleRate;
|
return _sampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setFrequency(float freq) {
|
void setFrequency(float freq) {
|
||||||
// No need to restart
|
assert(generic_block<SineSource>::_block_init);
|
||||||
_freq = freq;
|
_freq = freq;
|
||||||
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
phaseDelta = lv_cmake(std::cos((_freq / _sampleRate) * 2.0f * FL_M_PI), std::sin((_freq / _sampleRate) * 2.0f * FL_M_PI));
|
||||||
}
|
}
|
||||||
|
|
||||||
float getFrequency() {
|
float getFrequency() {
|
||||||
|
assert(generic_block<SineSource>::_block_init);
|
||||||
return _freq;
|
return _freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
if (out.aquire() < 0) { return -1; }
|
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)out.writeBuf, zeroPhase, phaseDelta, &phase, _blockSize);
|
||||||
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)out.data, zeroPhase, phaseDelta, &phase, _blockSize);
|
if(!out.swap(_blockSize)) { return -1; }
|
||||||
out.write(_blockSize);
|
|
||||||
return _blockSize;
|
return _blockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,4 +75,42 @@ namespace dsp {
|
|||||||
lv_32fc_t* zeroPhase;
|
lv_32fc_t* zeroPhase;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class HandlerSource : public generic_block<HandlerSource<T>> {
|
||||||
|
public:
|
||||||
|
HandlerSource() {}
|
||||||
|
|
||||||
|
HandlerSource(int (*handler)(T* data, void* ctx), void* ctx) { init(handler, ctx); }
|
||||||
|
|
||||||
|
void init(int (*handler)(T* data, void* ctx), void* ctx) {
|
||||||
|
_handler = handler;
|
||||||
|
_ctx = ctx;
|
||||||
|
generic_block<HandlerSource<T>>::registerOutput(&out);
|
||||||
|
generic_block<HandlerSource<T>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHandler(int (*handler)(T* data, void* ctx), void* ctx) {
|
||||||
|
assert(generic_block<HandlerSource<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<HandlerSource<T>>::ctrlMtx);
|
||||||
|
generic_block<HandlerSource<T>>::tempStop();
|
||||||
|
_handler = handler;
|
||||||
|
_ctx = ctx;
|
||||||
|
generic_block<HandlerSource<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _handler(out.writeBuf, _ctx);
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
out.swap(count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<T> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int (*_handler)(T* data, void* ctx);
|
||||||
|
void* _ctx;
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
289
core/src/dsp/stereo_fm.h
Normal file
289
core/src/dsp/stereo_fm.h
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <dsp/stream.h>
|
||||||
|
#include <dsp/types.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
|
||||||
|
class FMStereoDemuxPilotFilter : public generic_block<FMStereoDemuxPilotFilter> {
|
||||||
|
public:
|
||||||
|
FMStereoDemuxPilotFilter() {}
|
||||||
|
|
||||||
|
FMStereoDemuxPilotFilter(stream<complex_t>* in, dsp::filter_window::generic_complex_window* window) { init(in, window); }
|
||||||
|
|
||||||
|
~FMStereoDemuxPilotFilter() {
|
||||||
|
if (!generic_block<FMStereoDemuxPilotFilter>::_block_init) { return; }
|
||||||
|
generic_block<FMStereoDemuxPilotFilter>::stop();
|
||||||
|
volk_free(buffer);
|
||||||
|
volk_free(taps);
|
||||||
|
generic_block<FMStereoDemuxPilotFilter>::_block_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<complex_t>* in, dsp::filter_window::generic_complex_window* window) {
|
||||||
|
_in = in;
|
||||||
|
|
||||||
|
tapCount = window->getTapCount();
|
||||||
|
taps = (complex_t*)volk_malloc(tapCount * sizeof(complex_t), volk_get_alignment());
|
||||||
|
window->createTaps(taps, tapCount);
|
||||||
|
|
||||||
|
buffer = (complex_t*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(complex_t) * 2, volk_get_alignment());
|
||||||
|
bufStart = &buffer[tapCount];
|
||||||
|
generic_block<FMStereoDemuxPilotFilter>::registerInput(_in);
|
||||||
|
generic_block<FMStereoDemuxPilotFilter>::registerOutput(&dataOut);
|
||||||
|
generic_block<FMStereoDemuxPilotFilter>::registerOutput(&pilotOut);
|
||||||
|
generic_block<FMStereoDemuxPilotFilter>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* in) {
|
||||||
|
assert(generic_block<FMStereoDemuxPilotFilter>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<FMStereoDemuxPilotFilter>::ctrlMtx);
|
||||||
|
generic_block<FMStereoDemuxPilotFilter>::tempStop();
|
||||||
|
generic_block<FMStereoDemuxPilotFilter>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<FMStereoDemuxPilotFilter>::registerInput(_in);
|
||||||
|
generic_block<FMStereoDemuxPilotFilter>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateWindow(dsp::filter_window::generic_complex_window* window) {
|
||||||
|
assert(generic_block<FMStereoDemuxPilotFilter>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<FMStereoDemuxPilotFilter>::ctrlMtx);
|
||||||
|
std::lock_guard<std::mutex> lck2(bufMtx);
|
||||||
|
_window = window;
|
||||||
|
volk_free(taps);
|
||||||
|
tapCount = window->getTapCount();
|
||||||
|
taps = (complex_t*)volk_malloc(tapCount * sizeof(complex_t), volk_get_alignment());
|
||||||
|
bufStart = &buffer[tapCount];
|
||||||
|
window->createTaps(taps, tapCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
bufMtx.lock();
|
||||||
|
|
||||||
|
memcpy(bufStart, _in->readBuf, count * sizeof(complex_t));
|
||||||
|
_in->flush();
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
volk_32fc_x2_dot_prod_32fc((lv_32fc_t*)&pilotOut.writeBuf[i], (lv_32fc_t*)&buffer[i+1], (lv_32fc_t*)taps, tapCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(dataOut.writeBuf, &buffer[tapCount - ((tapCount-1)/2)], count * sizeof(complex_t));
|
||||||
|
|
||||||
|
if (!pilotOut.swap(count) || !dataOut.swap(count)) {
|
||||||
|
bufMtx.unlock();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memmove(buffer, &buffer[count], tapCount * sizeof(complex_t));
|
||||||
|
|
||||||
|
bufMtx.unlock();
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<complex_t> dataOut;
|
||||||
|
stream<complex_t> pilotOut;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
|
dsp::filter_window::generic_complex_window* _window;
|
||||||
|
|
||||||
|
std::mutex bufMtx;
|
||||||
|
|
||||||
|
complex_t* bufStart;
|
||||||
|
complex_t* buffer;
|
||||||
|
int tapCount;
|
||||||
|
complex_t* taps;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class FMStereoDemux: public generic_block<FMStereoDemux> {
|
||||||
|
public:
|
||||||
|
FMStereoDemux() {}
|
||||||
|
|
||||||
|
FMStereoDemux(stream<complex_t>* data, stream<complex_t>* pilot, float loopBandwidth) { init(data, pilot, loopBandwidth); }
|
||||||
|
|
||||||
|
void init(stream<complex_t>* data, stream<complex_t>* pilot, float loopBandwidth) {
|
||||||
|
_data = data;
|
||||||
|
_pilot = pilot;
|
||||||
|
lastVCO.re = 1.0f;
|
||||||
|
lastVCO.im = 0.0f;
|
||||||
|
_loopBandwidth = loopBandwidth;
|
||||||
|
|
||||||
|
float dampningFactor = sqrtf(2.0f) / 2.0f;
|
||||||
|
float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth);
|
||||||
|
_alpha = (4 * dampningFactor * _loopBandwidth) / denominator;
|
||||||
|
_beta = (4 * _loopBandwidth * _loopBandwidth) / denominator;
|
||||||
|
|
||||||
|
generic_block<FMStereoDemux>::registerInput(_data);
|
||||||
|
generic_block<FMStereoDemux>::registerInput(_pilot);
|
||||||
|
generic_block<FMStereoDemux>::registerOutput(&AplusBOut);
|
||||||
|
generic_block<FMStereoDemux>::registerOutput(&AminusBOut);
|
||||||
|
generic_block<FMStereoDemux>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<complex_t>* data, stream<complex_t>* pilot) {
|
||||||
|
assert(generic_block<FMStereoDemux>::_block_init);
|
||||||
|
generic_block<FMStereoDemux>::tempStop();
|
||||||
|
generic_block<FMStereoDemux>::unregisterInput(_data);
|
||||||
|
generic_block<FMStereoDemux>::unregisterInput(_pilot);
|
||||||
|
_data = data;
|
||||||
|
_pilot = pilot;
|
||||||
|
generic_block<FMStereoDemux>::registerInput(_data);
|
||||||
|
generic_block<FMStereoDemux>::registerInput(_pilot);
|
||||||
|
generic_block<FMStereoDemux>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLoopBandwidth(float loopBandwidth) {
|
||||||
|
assert(generic_block<FMStereoDemux>::_block_init);
|
||||||
|
generic_block<FMStereoDemux>::tempStop();
|
||||||
|
_loopBandwidth = loopBandwidth;
|
||||||
|
float dampningFactor = sqrtf(2.0f) / 2.0f;
|
||||||
|
float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth);
|
||||||
|
_alpha = (4 * dampningFactor * _loopBandwidth) / denominator;
|
||||||
|
_beta = (4 * _loopBandwidth * _loopBandwidth) / denominator;
|
||||||
|
generic_block<FMStereoDemux>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _data->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
int pCount = _pilot->read();
|
||||||
|
if (pCount < 0) { return -1; }
|
||||||
|
|
||||||
|
complex_t doubledVCO;
|
||||||
|
float error;
|
||||||
|
|
||||||
|
volk_32fc_deinterleave_real_32f(AplusBOut.writeBuf, (lv_32fc_t*)_data->readBuf, count);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
// Double the VCO, then mix it with the input data.
|
||||||
|
// IMPORTANT: THERE SHOULDN'T BE A NEED FOR A GAIN HERE
|
||||||
|
doubledVCO = lastVCO*lastVCO;
|
||||||
|
AminusBOut.writeBuf[i] = (_data->readBuf[i].re * doubledVCO.re) * 2.0f;
|
||||||
|
|
||||||
|
// Calculate the phase error estimation
|
||||||
|
error = _pilot->readBuf[i].phase() - vcoPhase;
|
||||||
|
if (error > 3.1415926535f) { error -= 2.0f * 3.1415926535f; }
|
||||||
|
else if (error <= -3.1415926535f) { error += 2.0f * 3.1415926535f; }
|
||||||
|
|
||||||
|
// Integrate frequency and clamp it
|
||||||
|
vcoFrequency += _beta * error;
|
||||||
|
if (vcoFrequency > upperLimit) { vcoFrequency = upperLimit; }
|
||||||
|
else if (vcoFrequency < lowerLimit) { vcoFrequency = lowerLimit; }
|
||||||
|
|
||||||
|
// Calculate new phase and wrap it
|
||||||
|
vcoPhase += vcoFrequency + (_alpha * error);
|
||||||
|
while (vcoPhase > (2.0f * FL_M_PI)) { vcoPhase -= (2.0f * FL_M_PI); }
|
||||||
|
while (vcoPhase < (-2.0f * FL_M_PI)) { vcoPhase += (2.0f * FL_M_PI); }
|
||||||
|
|
||||||
|
// Calculate output
|
||||||
|
lastVCO.re = cosf(vcoPhase);
|
||||||
|
lastVCO.im = sinf(vcoPhase);
|
||||||
|
}
|
||||||
|
|
||||||
|
_data->flush();
|
||||||
|
_pilot->flush();
|
||||||
|
|
||||||
|
if (!AplusBOut.swap(count)) { return -1; }
|
||||||
|
if (!AminusBOut.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<float> AplusBOut;
|
||||||
|
stream<float> AminusBOut;
|
||||||
|
|
||||||
|
float gain = 2.0f;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float _loopBandwidth = 0.01f;
|
||||||
|
|
||||||
|
const float expectedFreq = ((19000.0f / 250000.0f) * 2.0f * FL_M_PI);
|
||||||
|
const float upperLimit = ((19200.0f / 250000.0f) * 2.0f * FL_M_PI);
|
||||||
|
const float lowerLimit = ((18800.0f / 250000.0f) * 2.0f * FL_M_PI);
|
||||||
|
|
||||||
|
float _alpha; // Integral coefficient
|
||||||
|
float _beta; // Proportional coefficient
|
||||||
|
float vcoFrequency = expectedFreq;
|
||||||
|
float vcoPhase = 0.0f;
|
||||||
|
complex_t lastVCO;
|
||||||
|
|
||||||
|
stream<complex_t>* _data;
|
||||||
|
stream<complex_t>* _pilot;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FMStereoReconstruct : public generic_block<FMStereoReconstruct> {
|
||||||
|
public:
|
||||||
|
FMStereoReconstruct() {}
|
||||||
|
|
||||||
|
FMStereoReconstruct(stream<float>* a, stream<float>* b) { init(a, b); }
|
||||||
|
|
||||||
|
~FMStereoReconstruct() {
|
||||||
|
generic_block<FMStereoReconstruct>::stop();
|
||||||
|
delete[] leftBuf;
|
||||||
|
delete[] rightBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<float>* aplusb, stream<float>* aminusb) {
|
||||||
|
_aplusb = aplusb;
|
||||||
|
_aminusb = aminusb;
|
||||||
|
|
||||||
|
leftBuf = new float[STREAM_BUFFER_SIZE];
|
||||||
|
rightBuf = new float[STREAM_BUFFER_SIZE];
|
||||||
|
|
||||||
|
generic_block<FMStereoReconstruct>::registerInput(aplusb);
|
||||||
|
generic_block<FMStereoReconstruct>::registerInput(aminusb);
|
||||||
|
generic_block<FMStereoReconstruct>::registerOutput(&out);
|
||||||
|
generic_block<FMStereoReconstruct>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputs(stream<float>* aplusb, stream<float>* aminusb) {
|
||||||
|
assert(generic_block<FMStereoReconstruct>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<FMStereoReconstruct>::ctrlMtx);
|
||||||
|
generic_block<FMStereoReconstruct>::tempStop();
|
||||||
|
generic_block<FMStereoReconstruct>::unregisterInput(_aplusb);
|
||||||
|
generic_block<FMStereoReconstruct>::unregisterInput(_aminusb);
|
||||||
|
_aplusb = aplusb;
|
||||||
|
_aminusb = aminusb;
|
||||||
|
generic_block<FMStereoReconstruct>::registerInput(_aplusb);
|
||||||
|
generic_block<FMStereoReconstruct>::registerInput(_aminusb);
|
||||||
|
generic_block<FMStereoReconstruct>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int a_count = _aplusb->read();
|
||||||
|
if (a_count < 0) { return -1; }
|
||||||
|
int b_count = _aminusb->read();
|
||||||
|
if (b_count < 0) { return -1; }
|
||||||
|
if (a_count != b_count) {
|
||||||
|
_aplusb->flush();
|
||||||
|
_aminusb->flush();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
volk_32f_x2_add_32f(rightBuf, _aplusb->readBuf, _aminusb->readBuf, a_count);
|
||||||
|
volk_32f_x2_subtract_32f(leftBuf, _aplusb->readBuf, _aminusb->readBuf, a_count);
|
||||||
|
_aplusb->flush();
|
||||||
|
_aminusb->flush();
|
||||||
|
|
||||||
|
volk_32f_x2_interleave_32fc((lv_32fc_t*)out.writeBuf, leftBuf, rightBuf, a_count);
|
||||||
|
|
||||||
|
if (!out.swap(a_count)) { return -1; }
|
||||||
|
return a_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<stereo_t> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<float>* _aplusb;
|
||||||
|
stream<float>* _aminusb;
|
||||||
|
|
||||||
|
float* leftBuf;
|
||||||
|
float* rightBuf;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -7,102 +7,120 @@
|
|||||||
#define STREAM_BUFFER_SIZE 1000000
|
#define STREAM_BUFFER_SIZE 1000000
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
class untyped_steam {
|
class untyped_stream {
|
||||||
public:
|
public:
|
||||||
virtual int aquire() { return -1; }
|
virtual bool swap(int size) { return false; }
|
||||||
virtual void write(int size) {}
|
|
||||||
virtual int read() { return -1; }
|
virtual int read() { return -1; }
|
||||||
virtual void flush() {}
|
virtual void flush() {}
|
||||||
virtual void stopReader() {}
|
|
||||||
virtual void clearReadStop() {}
|
|
||||||
virtual void stopWriter() {}
|
virtual void stopWriter() {}
|
||||||
virtual void clearWriteStop() {}
|
virtual void clearWriteStop() {}
|
||||||
|
virtual void stopReader() {}
|
||||||
|
virtual void clearReadStop() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class stream : public untyped_steam {
|
class stream : public untyped_stream {
|
||||||
public:
|
public:
|
||||||
stream() {
|
stream() {
|
||||||
data = (T*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(T), volk_get_alignment());
|
writeBuf = (T*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(T), volk_get_alignment());
|
||||||
|
readBuf = (T*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(T), volk_get_alignment());
|
||||||
}
|
}
|
||||||
|
|
||||||
int aquire() {
|
~stream() {
|
||||||
waitReady();
|
volk_free(writeBuf);
|
||||||
if (writerStop) {
|
volk_free(readBuf);
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(int size) {
|
bool swap(int size) {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lck(sigMtx);
|
// Wait to either swap or stop
|
||||||
contentSize = size;
|
std::unique_lock<std::mutex> lck(swapMtx);
|
||||||
|
swapCV.wait(lck, [this]{ return (canSwap || writerStop); });
|
||||||
|
|
||||||
|
// If writer was stopped, abandon operation
|
||||||
|
if (writerStop) { return false; }
|
||||||
|
|
||||||
|
// Swap buffers
|
||||||
|
dataSize = size;
|
||||||
|
T* temp = writeBuf;
|
||||||
|
writeBuf = readBuf;
|
||||||
|
readBuf = temp;
|
||||||
|
canSwap = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify reader that some data is ready
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lck(rdyMtx);
|
||||||
dataReady = true;
|
dataReady = true;
|
||||||
}
|
}
|
||||||
cv.notify_one();
|
rdyCV.notify_all();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int read() {
|
int read() {
|
||||||
waitData();
|
// Wait for data to be ready or to be stopped
|
||||||
if (readerStop) {
|
std::unique_lock<std::mutex> lck(rdyMtx);
|
||||||
return -1;
|
rdyCV.wait(lck, [this]{ return (dataReady || readerStop); });
|
||||||
}
|
|
||||||
return contentSize;
|
return (readerStop ? -1 : dataSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
|
// Clear data ready
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lck(sigMtx);
|
std::lock_guard<std::mutex> lck(rdyMtx);
|
||||||
dataReady = false;
|
dataReady = false;
|
||||||
}
|
}
|
||||||
cv.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
void stopReader() {
|
// Notify writer that buffers can be swapped
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lck(sigMtx);
|
std::lock_guard<std::mutex> lck(swapMtx);
|
||||||
readerStop = true;
|
canSwap = true;
|
||||||
}
|
}
|
||||||
cv.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearReadStop() {
|
swapCV.notify_all();
|
||||||
readerStop = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopWriter() {
|
void stopWriter() {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lck(sigMtx);
|
std::lock_guard<std::mutex> lck(swapMtx);
|
||||||
writerStop = true;
|
writerStop = true;
|
||||||
}
|
}
|
||||||
cv.notify_one();
|
swapCV.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearWriteStop() {
|
void clearWriteStop() {
|
||||||
writerStop = false;
|
writerStop = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
T* data;
|
void stopReader() {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lck(rdyMtx);
|
||||||
|
readerStop = true;
|
||||||
|
}
|
||||||
|
rdyCV.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearReadStop() {
|
||||||
|
readerStop = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* writeBuf;
|
||||||
|
T* readBuf;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void waitReady() {
|
std::mutex swapMtx;
|
||||||
std::unique_lock<std::mutex> lck(sigMtx);
|
std::condition_variable swapCV;
|
||||||
cv.wait(lck, [this]{ return (!dataReady || writerStop); });
|
bool canSwap = true;
|
||||||
}
|
|
||||||
|
|
||||||
void waitData() {
|
std::mutex rdyMtx;
|
||||||
std::unique_lock<std::mutex> lck(sigMtx);
|
std::condition_variable rdyCV;
|
||||||
cv.wait(lck, [this]{ return (dataReady || readerStop); });
|
|
||||||
}
|
|
||||||
|
|
||||||
std::mutex sigMtx;
|
|
||||||
std::condition_variable cv;
|
|
||||||
bool dataReady = false;
|
bool dataReady = false;
|
||||||
|
|
||||||
bool readerStop = false;
|
bool readerStop = false;
|
||||||
bool writerStop = false;
|
bool writerStop = false;
|
||||||
|
|
||||||
int contentSize = 0;
|
int dataSize = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,83 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <math.h>
|
||||||
|
#include <dsp/utils/math.h>
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
struct complex_t {
|
struct complex_t {
|
||||||
float q;
|
complex_t operator*(const float b) {
|
||||||
float i;
|
return complex_t{re*b, im*b};
|
||||||
|
}
|
||||||
|
|
||||||
|
complex_t operator/(const float b) {
|
||||||
|
return complex_t{re/b, im/b};
|
||||||
|
}
|
||||||
|
|
||||||
|
complex_t operator*(const complex_t& b) {
|
||||||
|
return complex_t{(re*b.re) - (im*b.im), (im*b.re) + (re*b.im)};
|
||||||
|
}
|
||||||
|
|
||||||
|
complex_t operator+(const complex_t& b) {
|
||||||
|
return complex_t{re+b.re, im+b.im};
|
||||||
|
}
|
||||||
|
|
||||||
|
complex_t operator-(const complex_t& b) {
|
||||||
|
return complex_t{re-b.re, im-b.im};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline complex_t conj() {
|
||||||
|
return complex_t{re, -im};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float phase() {
|
||||||
|
return atan2f(im, re);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float fastPhase() {
|
||||||
|
float abs_im = fabsf(im);
|
||||||
|
float r, angle;
|
||||||
|
if (re == 0.0f && im == 0.0f) { return 0.0f; }
|
||||||
|
if (re>=0.0f) {
|
||||||
|
r = (re - abs_im) / (re + abs_im);
|
||||||
|
angle = (FL_M_PI / 4.0f) - (FL_M_PI / 4.0f) * r;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
r = (re + abs_im) / (abs_im - re);
|
||||||
|
angle = (3.0f * (FL_M_PI / 4.0f)) - (FL_M_PI / 4.0f) * r;
|
||||||
|
}
|
||||||
|
if (im < 0.0f) {
|
||||||
|
return -angle;
|
||||||
|
}
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float amplitude() {
|
||||||
|
return sqrt((re*re) + (im*im));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float fastAmplitude() {
|
||||||
|
float re_abs = fabsf(re);
|
||||||
|
float im_abs = fabsf(re);
|
||||||
|
if (re_abs > im_abs) { return re_abs + 0.4f * im_abs; }
|
||||||
|
return im_abs + 0.4f * re_abs;
|
||||||
|
}
|
||||||
|
|
||||||
|
float re;
|
||||||
|
float im;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct stereo_t {
|
struct stereo_t {
|
||||||
|
stereo_t operator*(const float b) {
|
||||||
|
return stereo_t{l*b, r*b};
|
||||||
|
}
|
||||||
|
|
||||||
|
stereo_t operator+(const stereo_t& b) {
|
||||||
|
return stereo_t{l+b.l, r+b.r};
|
||||||
|
}
|
||||||
|
|
||||||
|
stereo_t operator-(const stereo_t& b) {
|
||||||
|
return stereo_t{l-b.l, r-b.r};
|
||||||
|
}
|
||||||
|
|
||||||
float l;
|
float l;
|
||||||
float r;
|
float r;
|
||||||
};
|
};
|
||||||
|
|||||||
43
core/src/dsp/utils/bitstream.h
Normal file
43
core/src/dsp/utils/bitstream.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
inline uint64_t readBits(int offset, int length, uint8_t* buffer) {
|
||||||
|
uint64_t outputValue = 0;
|
||||||
|
|
||||||
|
int lastBit = offset + (length - 1);
|
||||||
|
|
||||||
|
int firstWord = offset / 8;
|
||||||
|
int lastWord = lastBit / 8;
|
||||||
|
int firstOffset = offset - (firstWord * 8);
|
||||||
|
int lastOffset = lastBit - (lastWord * 8);
|
||||||
|
|
||||||
|
int wordCount = (lastWord - firstWord) + 1;
|
||||||
|
|
||||||
|
// If the data fits in a single byte, just get it
|
||||||
|
if (wordCount == 1) {
|
||||||
|
return (buffer[firstWord] & (0xFF >> firstOffset)) >> (7 - lastOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bitsRead = length;
|
||||||
|
for (int i = 0; i < wordCount; i++) {
|
||||||
|
// First word
|
||||||
|
if (i == 0) {
|
||||||
|
bitsRead -= 8 - firstOffset;
|
||||||
|
outputValue |= (uint64_t)(buffer[firstWord] & (0xFF >> firstOffset)) << bitsRead;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last word
|
||||||
|
if (i == (wordCount - 1)) {
|
||||||
|
outputValue |= (uint64_t)buffer[lastWord] >> (7 - lastOffset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just a normal byte
|
||||||
|
bitsRead -= 8;
|
||||||
|
outputValue |= (uint64_t)buffer[firstWord + i] << bitsRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
121
core/src/dsp/utils/ccsds.h
Normal file
121
core/src/dsp/utils/ccsds.h
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
namespace ccsds {
|
||||||
|
const uint8_t TO_DUAL_BASIS[256] = {
|
||||||
|
0x00, 0x7b, 0xaf, 0xd4, 0x99, 0xe2, 0x36, 0x4d, 0xfa, 0x81, 0x55, 0x2e, 0x63, 0x18, 0xcc, 0xb7,
|
||||||
|
0x86, 0xfd, 0x29, 0x52, 0x1f, 0x64, 0xb0, 0xcb, 0x7c, 0x07, 0xd3, 0xa8, 0xe5, 0x9e, 0x4a, 0x31,
|
||||||
|
0xec, 0x97, 0x43, 0x38, 0x75, 0x0e, 0xda, 0xa1, 0x16, 0x6d, 0xb9, 0xc2, 0x8f, 0xf4, 0x20, 0x5b,
|
||||||
|
0x6a, 0x11, 0xc5, 0xbe, 0xf3, 0x88, 0x5c, 0x27, 0x90, 0xeb, 0x3f, 0x44, 0x09, 0x72, 0xa6, 0xdd,
|
||||||
|
0xef, 0x94, 0x40, 0x3b, 0x76, 0x0d, 0xd9, 0xa2, 0x15, 0x6e, 0xba, 0xc1, 0x8c, 0xf7, 0x23, 0x58,
|
||||||
|
0x69, 0x12, 0xc6, 0xbd, 0xf0, 0x8b, 0x5f, 0x24, 0x93, 0xe8, 0x3c, 0x47, 0x0a, 0x71, 0xa5, 0xde,
|
||||||
|
0x03, 0x78, 0xac, 0xd7, 0x9a, 0xe1, 0x35, 0x4e, 0xf9, 0x82, 0x56, 0x2d, 0x60, 0x1b, 0xcf, 0xb4,
|
||||||
|
0x85, 0xfe, 0x2a, 0x51, 0x1c, 0x67, 0xb3, 0xc8, 0x7f, 0x04, 0xd0, 0xab, 0xe6, 0x9d, 0x49, 0x32,
|
||||||
|
0x8d, 0xf6, 0x22, 0x59, 0x14, 0x6f, 0xbb, 0xc0, 0x77, 0x0c, 0xd8, 0xa3, 0xee, 0x95, 0x41, 0x3a,
|
||||||
|
0x0b, 0x70, 0xa4, 0xdf, 0x92, 0xe9, 0x3d, 0x46, 0xf1, 0x8a, 0x5e, 0x25, 0x68, 0x13, 0xc7, 0xbc,
|
||||||
|
0x61, 0x1a, 0xce, 0xb5, 0xf8, 0x83, 0x57, 0x2c, 0x9b, 0xe0, 0x34, 0x4f, 0x02, 0x79, 0xad, 0xd6,
|
||||||
|
0xe7, 0x9c, 0x48, 0x33, 0x7e, 0x05, 0xd1, 0xaa, 0x1d, 0x66, 0xb2, 0xc9, 0x84, 0xff, 0x2b, 0x50,
|
||||||
|
0x62, 0x19, 0xcd, 0xb6, 0xfb, 0x80, 0x54, 0x2f, 0x98, 0xe3, 0x37, 0x4c, 0x01, 0x7a, 0xae, 0xd5,
|
||||||
|
0xe4, 0x9f, 0x4b, 0x30, 0x7d, 0x06, 0xd2, 0xa9, 0x1e, 0x65, 0xb1, 0xca, 0x87, 0xfc, 0x28, 0x53,
|
||||||
|
0x8e, 0xf5, 0x21, 0x5a, 0x17, 0x6c, 0xb8, 0xc3, 0x74, 0x0f, 0xdb, 0xa0, 0xed, 0x96, 0x42, 0x39,
|
||||||
|
0x08, 0x73, 0xa7, 0xdc, 0x91, 0xea, 0x3e, 0x45, 0xf2, 0x89, 0x5d, 0x26, 0x6b, 0x10, 0xc4, 0xbf
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t FROM_DUAL_BASIS[256] = {
|
||||||
|
0x00, 0xcc, 0xac, 0x60, 0x79, 0xb5, 0xd5, 0x19, 0xf0, 0x3c, 0x5c, 0x90, 0x89, 0x45, 0x25, 0xe9,
|
||||||
|
0xfd, 0x31, 0x51, 0x9d, 0x84, 0x48, 0x28, 0xe4, 0x0d, 0xc1, 0xa1, 0x6d, 0x74, 0xb8, 0xd8, 0x14,
|
||||||
|
0x2e, 0xe2, 0x82, 0x4e, 0x57, 0x9b, 0xfb, 0x37, 0xde, 0x12, 0x72, 0xbe, 0xa7, 0x6b, 0x0b, 0xc7,
|
||||||
|
0xd3, 0x1f, 0x7f, 0xb3, 0xaa, 0x66, 0x06, 0xca, 0x23, 0xef, 0x8f, 0x43, 0x5a, 0x96, 0xf6, 0x3a,
|
||||||
|
0x42, 0x8e, 0xee, 0x22, 0x3b, 0xf7, 0x97, 0x5b, 0xb2, 0x7e, 0x1e, 0xd2, 0xcb, 0x07, 0x67, 0xab,
|
||||||
|
0xbf, 0x73, 0x13, 0xdf, 0xc6, 0x0a, 0x6a, 0xa6, 0x4f, 0x83, 0xe3, 0x2f, 0x36, 0xfa, 0x9a, 0x56,
|
||||||
|
0x6c, 0xa0, 0xc0, 0x0c, 0x15, 0xd9, 0xb9, 0x75, 0x9c, 0x50, 0x30, 0xfc, 0xe5, 0x29, 0x49, 0x85,
|
||||||
|
0x91, 0x5d, 0x3d, 0xf1, 0xe8, 0x24, 0x44, 0x88, 0x61, 0xad, 0xcd, 0x01, 0x18, 0xd4, 0xb4, 0x78,
|
||||||
|
0xc5, 0x09, 0x69, 0xa5, 0xbc, 0x70, 0x10, 0xdc, 0x35, 0xf9, 0x99, 0x55, 0x4c, 0x80, 0xe0, 0x2c,
|
||||||
|
0x38, 0xf4, 0x94, 0x58, 0x41, 0x8d, 0xed, 0x21, 0xc8, 0x04, 0x64, 0xa8, 0xb1, 0x7d, 0x1d, 0xd1,
|
||||||
|
0xeb, 0x27, 0x47, 0x8b, 0x92, 0x5e, 0x3e, 0xf2, 0x1b, 0xd7, 0xb7, 0x7b, 0x62, 0xae, 0xce, 0x02,
|
||||||
|
0x16, 0xda, 0xba, 0x76, 0x6f, 0xa3, 0xc3, 0x0f, 0xe6, 0x2a, 0x4a, 0x86, 0x9f, 0x53, 0x33, 0xff,
|
||||||
|
0x87, 0x4b, 0x2b, 0xe7, 0xfe, 0x32, 0x52, 0x9e, 0x77, 0xbb, 0xdb, 0x17, 0x0e, 0xc2, 0xa2, 0x6e,
|
||||||
|
0x7a, 0xb6, 0xd6, 0x1a, 0x03, 0xcf, 0xaf, 0x63, 0x8a, 0x46, 0x26, 0xea, 0xf3, 0x3f, 0x5f, 0x93,
|
||||||
|
0xa9, 0x65, 0x05, 0xc9, 0xd0, 0x1c, 0x7c, 0xb0, 0x59, 0x95, 0xf5, 0x39, 0x20, 0xec, 0x8c, 0x40,
|
||||||
|
0x54, 0x98, 0xf8, 0x34, 0x2d, 0xe1, 0x81, 0x4d, 0xa4, 0x68, 0x08, 0xc4, 0xdd, 0x11, 0x71, 0xbd
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t SCRAMBLING_SEQUENCE[255] = {
|
||||||
|
0xFF, 0x48, 0x0E, 0xC0, 0x9A, 0x0D, 0x70, 0xBC, 0x8E, 0x2C, 0x93, 0xAD, 0xA7, 0xB7, 0x46, 0xCE,
|
||||||
|
0x5A, 0x97, 0x7D, 0xCC, 0x32, 0xA2, 0xBF, 0x3E, 0x0A, 0x10, 0xF1, 0x88, 0x94, 0xCD, 0xEA, 0xB1,
|
||||||
|
0xFE, 0x90, 0x1D, 0x81, 0x34, 0x1A, 0xE1, 0x79, 0x1C, 0x59, 0x27, 0x5B, 0x4F, 0x6E, 0x8D, 0x9C,
|
||||||
|
0xB5, 0x2E, 0xFB, 0x98, 0x65, 0x45, 0x7E, 0x7C, 0x14, 0x21, 0xE3, 0x11, 0x29, 0x9B, 0xD5, 0x63,
|
||||||
|
0xFD, 0x20, 0x3B, 0x02, 0x68, 0x35, 0xC2, 0xF2, 0x38, 0xB2, 0x4E, 0xB6, 0x9E, 0xDD, 0x1B, 0x39,
|
||||||
|
0x6A, 0x5D, 0xF7, 0x30, 0xCA, 0x8A, 0xFC, 0xF8, 0x28, 0x43, 0xC6, 0x22, 0x53, 0x37, 0xAA, 0xC7,
|
||||||
|
0xFA, 0x40, 0x76, 0x04, 0xD0, 0x6B, 0x85, 0xE4, 0x71, 0x64, 0x9D, 0x6D, 0x3D, 0xBA, 0x36, 0x72,
|
||||||
|
0xD4, 0xBB, 0xEE, 0x61, 0x95, 0x15, 0xF9, 0xF0, 0x50, 0x87, 0x8C, 0x44, 0xA6, 0x6F, 0x55, 0x8F,
|
||||||
|
0xF4, 0x80, 0xEC, 0x09, 0xA0, 0xD7, 0x0B, 0xC8, 0xE2, 0xC9, 0x3A, 0xDA, 0x7B, 0x74, 0x6C, 0xE5,
|
||||||
|
0xA9, 0x77, 0xDC, 0xC3, 0x2A, 0x2B, 0xF3, 0xE0, 0xA1, 0x0F, 0x18, 0x89, 0x4C, 0xDE, 0xAB, 0x1F,
|
||||||
|
0xE9, 0x01, 0xD8, 0x13, 0x41, 0xAE, 0x17, 0x91, 0xC5, 0x92, 0x75, 0xB4, 0xF6, 0xE8, 0xD9, 0xCB,
|
||||||
|
0x52, 0xEF, 0xB9, 0x86, 0x54, 0x57, 0xE7, 0xC1, 0x42, 0x1E, 0x31, 0x12, 0x99, 0xBD, 0x56, 0x3F,
|
||||||
|
0xD2, 0x03, 0xB0, 0x26, 0x83, 0x5C, 0x2F, 0x23, 0x8B, 0x24, 0xEB, 0x69, 0xED, 0xD1, 0xB3, 0x96,
|
||||||
|
0xA5, 0xDF, 0x73, 0x0C, 0xA8, 0xAF, 0xCF, 0x82, 0x84, 0x3C, 0x62, 0x25, 0x33, 0x7A, 0xAC, 0x7F,
|
||||||
|
0xA4, 0x07, 0x60, 0x4D, 0x06, 0xB8, 0x5E, 0x47, 0x16, 0x49, 0xD6, 0xD3, 0xDB, 0xA3, 0x67, 0x2D,
|
||||||
|
0x4B, 0xBE, 0xE6, 0x19, 0x51, 0x5F, 0x9F, 0x05, 0x08, 0x78, 0xC4, 0x4A, 0x66, 0xF5, 0x58
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32_t ASM_VALUE = 0x1ACFFC1D;
|
||||||
|
const uint8_t ASM_BYTES[4] = {0x1A, 0xCF, 0xFC, 0x1D};
|
||||||
|
const uint8_t ASM_SYMS[16] = {0b00, 0b01, 0b10, 0b10, 0b11, 0b00, 0b11, 0b11, 0b11, 0b11, 0b11, 0b00, 0b00, 0b01, 0b11, 0b01};
|
||||||
|
const uint8_t ASM_BITS[32] = {0,0,0,1,1,0,1,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,1};
|
||||||
|
|
||||||
|
class FrameDataDecoder {
|
||||||
|
public:
|
||||||
|
FrameDataDecoder(int interleaving, bool dualBasis, int rsBlockSize, int rsParitySize) {
|
||||||
|
_interleaving = interleaving;
|
||||||
|
_dualBasis = dualBasis;
|
||||||
|
_rsBlockSize = rsBlockSize;
|
||||||
|
_rsParitySize = rsParitySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void decode(uint8_t* in, uint8_t* out, int count) {
|
||||||
|
// Deinterleave
|
||||||
|
if (_dualBasis) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
workBuffer[i % _interleaving][i / _interleaving] = FROM_DUAL_BASIS[in[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
workBuffer[i % _interleaving][i / _interleaving] = in[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reed solomon
|
||||||
|
|
||||||
|
// Reinterleave and descramble if needed
|
||||||
|
if (_dualBasis) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
out[i] = TO_DUAL_BASIS[workOutputBuffer[i % _interleaving][i / _interleaving]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
out[i] = workOutputBuffer[i % _interleaving][i / _interleaving];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t workBuffer[5][255];
|
||||||
|
uint8_t workOutputBuffer[5][255];
|
||||||
|
int _interleaving;
|
||||||
|
bool _dualBasis;
|
||||||
|
int _rsBlockSize;
|
||||||
|
int _rsParitySize;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void descramble(uint8_t* in, uint8_t* out, int count) {
|
||||||
|
for (int i = 0; i < count; i++){
|
||||||
|
out[i] = in[i] ^ SCRAMBLING_SEQUENCE[i % 255];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
core/src/dsp/utils/macros.h
Normal file
6
core/src/dsp/utils/macros.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/types.h>
|
||||||
|
|
||||||
|
#define DSP_SIGN(n) ((n) >= 0)
|
||||||
|
#define DSP_STEP_CPLX(c) (complex_t{(c.re > 0.0f) ? 1.0f : -1.0f, (c.im > 0.0f) ? 1.0f : -1.0f})
|
||||||
|
#define DSP_STEP(n) (((n) > 0.0f) ? 1.0f : -1.0f)
|
||||||
12
core/src/dsp/utils/math.h
Normal file
12
core/src/dsp/utils/math.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#define FL_M_PI 3.1415926535f
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
namespace math {
|
||||||
|
inline double sinc(double omega, double x, double norm) {
|
||||||
|
return (x == 0.0f) ? 1.0f : (sin(omega*x)/(norm*x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
core/src/dsp/utils/window_functions.h
Normal file
28
core/src/dsp/utils/window_functions.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/types.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
namespace window_function {
|
||||||
|
inline double blackman(double n, double N, double alpha = 0.16f) {
|
||||||
|
double a0 = (1.0f-alpha) / 2.0f;
|
||||||
|
double a2 = alpha / 2.0f;
|
||||||
|
return a0 - (0.5f*cos(2.0f*FL_M_PI*(n/N))) + (a2*cos(4.0f*FL_M_PI*(n/N)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double blackmanThirdOrder(double n, double N, double a0, double a1, double a2, double a3) {
|
||||||
|
return a0 - (a1*cos(2.0f*FL_M_PI*(n/N))) + (a2*cos(4.0f*FL_M_PI*(n/N))) - (a3*cos(6.0f*FL_M_PI*(n/N)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double nuttall(double n, double N) {
|
||||||
|
return blackmanThirdOrder(n, N, 0.3635819f, 0.4891775f, 0.1365995f, 0.0106411f);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double blackmanNuttall(double n, double N) {
|
||||||
|
return blackmanThirdOrder(n, N, 0.3635819f, 0.4891775f, 0.1365995f, 0.0106411f);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double blackmanHarris(double n, double N) {
|
||||||
|
return blackmanThirdOrder(n, N, 0.35875f, 0.48829f, 0.14128f, 0.01168f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,11 @@ namespace dsp {
|
|||||||
public:
|
public:
|
||||||
VFO() {}
|
VFO() {}
|
||||||
|
|
||||||
~VFO() { stop(); }
|
~VFO() {
|
||||||
|
if (!_init) { return; }
|
||||||
|
stop();
|
||||||
|
_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
VFO(stream<complex_t>* in, float offset, float inSampleRate, float outSampleRate, float bandWidth) {
|
VFO(stream<complex_t>* in, float offset, float inSampleRate, float outSampleRate, float bandWidth) {
|
||||||
init(in, offset, inSampleRate, outSampleRate, bandWidth);
|
init(in, offset, inSampleRate, outSampleRate, bandWidth);
|
||||||
@@ -33,21 +37,26 @@ namespace dsp {
|
|||||||
resamp.updateWindow(&win);
|
resamp.updateWindow(&win);
|
||||||
|
|
||||||
out = &resamp.out;
|
out = &resamp.out;
|
||||||
|
|
||||||
|
_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void start() {
|
void start() {
|
||||||
|
assert(_init);
|
||||||
if (running) { return; }
|
if (running) { return; }
|
||||||
xlator.start();
|
xlator.start();
|
||||||
resamp.start();
|
resamp.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop() {
|
void stop() {
|
||||||
|
assert(_init);
|
||||||
if (!running) { return; }
|
if (!running) { return; }
|
||||||
xlator.stop();
|
xlator.stop();
|
||||||
resamp.stop();
|
resamp.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInSampleRate(float inSampleRate) {
|
void setInSampleRate(float inSampleRate) {
|
||||||
|
assert(_init);
|
||||||
_inSampleRate = inSampleRate;
|
_inSampleRate = inSampleRate;
|
||||||
if (running) { xlator.stop(); resamp.stop(); }
|
if (running) { xlator.stop(); resamp.stop(); }
|
||||||
xlator.setSampleRate(_inSampleRate);
|
xlator.setSampleRate(_inSampleRate);
|
||||||
@@ -61,6 +70,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setOutSampleRate(float outSampleRate) {
|
void setOutSampleRate(float outSampleRate) {
|
||||||
|
assert(_init);
|
||||||
_outSampleRate = outSampleRate;
|
_outSampleRate = outSampleRate;
|
||||||
if (running) { resamp.stop(); }
|
if (running) { resamp.stop(); }
|
||||||
resamp.setOutSampleRate(_outSampleRate);
|
resamp.setOutSampleRate(_outSampleRate);
|
||||||
@@ -73,6 +83,7 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setOutSampleRate(float outSampleRate, float bandWidth) {
|
void setOutSampleRate(float outSampleRate, float bandWidth) {
|
||||||
|
assert(_init);
|
||||||
_outSampleRate = outSampleRate;
|
_outSampleRate = outSampleRate;
|
||||||
_bandWidth = bandWidth;
|
_bandWidth = bandWidth;
|
||||||
if (running) { resamp.stop(); }
|
if (running) { resamp.stop(); }
|
||||||
@@ -86,11 +97,13 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setOffset(float offset) {
|
void setOffset(float offset) {
|
||||||
|
assert(_init);
|
||||||
_offset = offset;
|
_offset = offset;
|
||||||
xlator.setFrequency(-_offset);
|
xlator.setFrequency(-_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBandwidth(float bandWidth) {
|
void setBandwidth(float bandWidth) {
|
||||||
|
assert(_init);
|
||||||
_bandWidth = bandWidth;
|
_bandWidth = bandWidth;
|
||||||
float realCutoff = std::min<float>(_bandWidth, std::min<float>(_inSampleRate, _outSampleRate)) / 2.0f;
|
float realCutoff = std::min<float>(_bandWidth, std::min<float>(_inSampleRate, _outSampleRate)) / 2.0f;
|
||||||
win.setCutoff(realCutoff);
|
win.setCutoff(realCutoff);
|
||||||
@@ -101,6 +114,7 @@ namespace dsp {
|
|||||||
stream<complex_t>* out;
|
stream<complex_t>* out;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool _init = false;
|
||||||
bool running = false;
|
bool running = false;
|
||||||
float _offset, _inSampleRate, _outSampleRate, _bandWidth;
|
float _offset, _inSampleRate, _outSampleRate, _bandWidth;
|
||||||
filter_window::BlackmanWindow win;
|
filter_window::BlackmanWindow win;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <dsp/block.h>
|
#include <dsp/block.h>
|
||||||
|
#include <dsp/types.h>
|
||||||
|
#include <dsp/utils/window_functions.h>
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
namespace filter_window {
|
namespace filter_window {
|
||||||
@@ -9,6 +11,12 @@ namespace dsp {
|
|||||||
virtual void createTaps(float* taps, int tapCount, float factor = 1.0f) {}
|
virtual void createTaps(float* taps, int tapCount, float factor = 1.0f) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class generic_complex_window {
|
||||||
|
public:
|
||||||
|
virtual int getTapCount() { return -1; }
|
||||||
|
virtual void createTaps(dsp::complex_t* taps, int tapCount, float factor = 1.0f) {}
|
||||||
|
};
|
||||||
|
|
||||||
class BlackmanWindow : public filter_window::generic_window {
|
class BlackmanWindow : public filter_window::generic_window {
|
||||||
public:
|
public:
|
||||||
BlackmanWindow() {}
|
BlackmanWindow() {}
|
||||||
@@ -49,19 +57,21 @@ namespace dsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void createTaps(float* taps, int tapCount, float factor = 1.0f) {
|
void createTaps(float* taps, int tapCount, float factor = 1.0f) {
|
||||||
float fc = _cutoff / _sampleRate;
|
// Calculate cuttoff frequency
|
||||||
if (fc > 1.0f) {
|
float omega = 2.0f * FL_M_PI * (_cutoff / _sampleRate);
|
||||||
fc = 1.0f;
|
if (omega > FL_M_PI) { omega = FL_M_PI; }
|
||||||
}
|
|
||||||
float tc = tapCount;
|
// Generate taps
|
||||||
float sum = 0.0f;
|
|
||||||
float val;
|
float val;
|
||||||
|
float sum = 0.0f;
|
||||||
|
float tc = tapCount;
|
||||||
for (int i = 0; i < tapCount; i++) {
|
for (int i = 0; i < tapCount; i++) {
|
||||||
val = (sin(2.0f * FL_M_PI * fc * ((float)i - (tc / 2))) / ((float)i - (tc / 2))) *
|
val = math::sinc(omega, (float)i - (tc/2), FL_M_PI) * window_function::blackman(i, tc - 1);
|
||||||
(0.42f - (0.5f * cos(2.0f * FL_M_PI / tc)) + (0.8f * cos(4.0f * FL_M_PI / tc)));
|
taps[i] = val;
|
||||||
taps[i] = val; // tapCount - i - 1
|
|
||||||
sum += val;
|
sum += val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalize taps and multiply by supplied factor
|
||||||
for (int i = 0; i < tapCount; i++) {
|
for (int i = 0; i < tapCount; i++) {
|
||||||
taps[i] *= factor;
|
taps[i] *= factor;
|
||||||
taps[i] /= sum;
|
taps[i] /= sum;
|
||||||
@@ -72,5 +82,191 @@ namespace dsp {
|
|||||||
float _cutoff, _transWidth, _sampleRate;
|
float _cutoff, _transWidth, _sampleRate;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BandPassBlackmanWindow : public filter_window::generic_complex_window {
|
||||||
|
public:
|
||||||
|
BandPassBlackmanWindow() {}
|
||||||
|
BandPassBlackmanWindow(float lowCutoff, float highCutoff, float transWidth, float sampleRate) { init(lowCutoff, highCutoff, transWidth, sampleRate); }
|
||||||
|
|
||||||
|
void init(float lowCutoff, float highCutoff, float transWidth, float sampleRate) {
|
||||||
|
assert(lowCutoff <= highCutoff);
|
||||||
|
_lowCutoff = lowCutoff;
|
||||||
|
_highCutoff = highCutoff;
|
||||||
|
_transWidth = transWidth;
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
|
||||||
|
// Calculate other values
|
||||||
|
_offset = (_lowCutoff + _highCutoff) / 2.0f;
|
||||||
|
_cutoff = fabs((_highCutoff - _lowCutoff) / 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSampleRate(float sampleRate) {
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCutoffs(float lowCutoff, float highCutoff) {
|
||||||
|
assert(lowCutoff <= highCutoff);
|
||||||
|
_lowCutoff = lowCutoff;
|
||||||
|
_highCutoff = highCutoff;
|
||||||
|
|
||||||
|
// Calculate other values
|
||||||
|
_offset = (_lowCutoff + _highCutoff) / 2.0f;
|
||||||
|
_cutoff = fabs((_highCutoff - _lowCutoff) / 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLowCutoff(float lowCutoff) {
|
||||||
|
assert(lowCutoff <= _highCutoff);
|
||||||
|
_lowCutoff = lowCutoff;
|
||||||
|
|
||||||
|
// Calculate other values
|
||||||
|
_offset = (_lowCutoff + _highCutoff) / 2.0f;
|
||||||
|
_cutoff = fabs((_highCutoff - _lowCutoff) / 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHighCutoff(float highCutoff) {
|
||||||
|
assert(_lowCutoff <= highCutoff);
|
||||||
|
_highCutoff = highCutoff;
|
||||||
|
|
||||||
|
// Calculate other values
|
||||||
|
_offset = (_lowCutoff + _highCutoff) / 2.0f;
|
||||||
|
_cutoff = fabs((_highCutoff - _lowCutoff) / 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTransWidth(float transWidth) {
|
||||||
|
_transWidth = transWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getTapCount() {
|
||||||
|
float fc = _cutoff / _sampleRate;
|
||||||
|
if (fc > 1.0f) {
|
||||||
|
fc = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _M = 4.0f / (_transWidth / _sampleRate);
|
||||||
|
if (_M < 4) {
|
||||||
|
_M = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_M % 2 == 0) { _M++; }
|
||||||
|
|
||||||
|
return _M;
|
||||||
|
}
|
||||||
|
|
||||||
|
void createTaps(dsp::complex_t* taps, int tapCount, float factor = 1.0f) {
|
||||||
|
// Calculate cuttoff frequency
|
||||||
|
float omega = 2.0f * FL_M_PI * (_cutoff / _sampleRate);
|
||||||
|
if (omega > FL_M_PI) { omega = FL_M_PI; }
|
||||||
|
|
||||||
|
// Generate taps
|
||||||
|
float val;
|
||||||
|
float sum = 0.0f;
|
||||||
|
float tc = tapCount;
|
||||||
|
for (int i = 0; i < tapCount; i++) {
|
||||||
|
val = math::sinc(omega, (float)i - (tc/2), FL_M_PI) * window_function::blackman(i, tc - 1);
|
||||||
|
taps[i].re = val;
|
||||||
|
taps[i].im = 0;
|
||||||
|
sum += val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize taps and multiply by supplied factor
|
||||||
|
for (int i = 0; i < tapCount; i++) {
|
||||||
|
taps[i] = taps[i] * factor;
|
||||||
|
taps[i] = taps[i] / sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add offset
|
||||||
|
lv_32fc_t phase = lv_cmake(1.0f, 0.0f);
|
||||||
|
lv_32fc_t phaseDelta = lv_cmake(std::cos((-_offset / _sampleRate) * 2.0f * FL_M_PI), std::sin((-_offset / _sampleRate) * 2.0f * FL_M_PI));
|
||||||
|
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)taps, (lv_32fc_t*)taps, phaseDelta, &phase, tapCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
float _lowCutoff, _highCutoff;
|
||||||
|
float _cutoff, _transWidth, _sampleRate, _offset;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RRCTaps : public filter_window::generic_window {
|
||||||
|
public:
|
||||||
|
RRCTaps() {}
|
||||||
|
RRCTaps(int tapCount, float sampleRate, float baudRate, float alpha) { init(tapCount, sampleRate, baudRate, alpha); }
|
||||||
|
|
||||||
|
void init(int tapCount, float sampleRate, float baudRate, float alpha) {
|
||||||
|
_tapCount = tapCount;
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
_baudRate = baudRate;
|
||||||
|
_alpha = alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getTapCount() {
|
||||||
|
return _tapCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSampleRate(float sampleRate) {
|
||||||
|
_sampleRate = sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBaudRate(float baudRate) {
|
||||||
|
_baudRate = baudRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTapCount(int count) {
|
||||||
|
_tapCount = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAlpha(float alpha) {
|
||||||
|
_alpha = alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
void createTaps(float* taps, int tapCount, float factor = 1.0f) {
|
||||||
|
// ======== CREDIT: GNU Radio =========
|
||||||
|
tapCount |= 1; // ensure that tapCount is odd
|
||||||
|
|
||||||
|
double spb = _sampleRate / _baudRate; // samples per bit/symbol
|
||||||
|
double scale = 0;
|
||||||
|
for (int i = 0; i < tapCount; i++)
|
||||||
|
{
|
||||||
|
double x1, x2, x3, num, den;
|
||||||
|
double xindx = i - tapCount / 2;
|
||||||
|
x1 = FL_M_PI * xindx / spb;
|
||||||
|
x2 = 4 * _alpha * xindx / spb;
|
||||||
|
x3 = x2 * x2 - 1;
|
||||||
|
|
||||||
|
// Avoid Rounding errors...
|
||||||
|
if (fabs(x3) >= 0.000001) {
|
||||||
|
if (i != tapCount / 2)
|
||||||
|
num = cos((1 + _alpha) * x1) +
|
||||||
|
sin((1 - _alpha) * x1) / (4 * _alpha * xindx / spb);
|
||||||
|
else
|
||||||
|
num = cos((1 + _alpha) * x1) + (1 - _alpha) * FL_M_PI / (4 * _alpha);
|
||||||
|
den = x3 * FL_M_PI;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (_alpha == 1)
|
||||||
|
{
|
||||||
|
taps[i] = -1;
|
||||||
|
scale += taps[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
x3 = (1 - _alpha) * x1;
|
||||||
|
x2 = (1 + _alpha) * x1;
|
||||||
|
num = (sin(x2) * (1 + _alpha) * FL_M_PI -
|
||||||
|
cos(x3) * ((1 - _alpha) * FL_M_PI * spb) / (4 * _alpha * xindx) +
|
||||||
|
sin(x3) * spb * spb / (4 * _alpha * xindx * xindx));
|
||||||
|
den = -32 * FL_M_PI * _alpha * _alpha * xindx / spb;
|
||||||
|
}
|
||||||
|
taps[i] = 4 * _alpha * num / den;
|
||||||
|
scale += taps[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tapCount; i++) {
|
||||||
|
taps[i] = taps[i] / scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _tapCount;
|
||||||
|
float _sampleRate, _baudRate, _alpha;
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,185 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minimal 'console' binding.
|
|
||||||
*
|
|
||||||
* https://github.com/DeveloperToolsWG/console-object/blob/master/api.md
|
|
||||||
* https://developers.google.com/web/tools/chrome-devtools/debug/console/console-reference
|
|
||||||
* https://developer.mozilla.org/en/docs/Web/API/console
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include "duktape.h"
|
|
||||||
#include "duk_console.h"
|
|
||||||
|
|
||||||
/* XXX: Add some form of log level filtering. */
|
|
||||||
|
|
||||||
/* XXX: Should all output be written via e.g. console.write(formattedMsg)?
|
|
||||||
* This would make it easier for user code to redirect all console output
|
|
||||||
* to a custom backend.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* XXX: Init console object using duk_def_prop() when that call is available. */
|
|
||||||
|
|
||||||
static duk_ret_t duk__console_log_helper(duk_context *ctx, const char *error_name) {
|
|
||||||
duk_uint_t flags = (duk_uint_t) duk_get_current_magic(ctx);
|
|
||||||
FILE *output = (flags & DUK_CONSOLE_STDOUT_ONLY) ? stdout : stderr;
|
|
||||||
duk_idx_t n = duk_get_top(ctx);
|
|
||||||
duk_idx_t i;
|
|
||||||
|
|
||||||
duk_get_global_string(ctx, "console");
|
|
||||||
duk_get_prop_string(ctx, -1, "format");
|
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
if (duk_check_type_mask(ctx, i, DUK_TYPE_MASK_OBJECT)) {
|
|
||||||
/* Slow path formatting. */
|
|
||||||
duk_dup(ctx, -1); /* console.format */
|
|
||||||
duk_dup(ctx, i);
|
|
||||||
duk_call(ctx, 1);
|
|
||||||
duk_replace(ctx, i); /* arg[i] = console.format(arg[i]); */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
duk_pop_2(ctx);
|
|
||||||
|
|
||||||
duk_push_string(ctx, " ");
|
|
||||||
duk_insert(ctx, 0);
|
|
||||||
duk_join(ctx, n);
|
|
||||||
|
|
||||||
if (error_name) {
|
|
||||||
duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_require_string(ctx, -1));
|
|
||||||
duk_push_string(ctx, "name");
|
|
||||||
duk_push_string(ctx, error_name);
|
|
||||||
duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE); /* to get e.g. 'Trace: 1 2 3' */
|
|
||||||
duk_get_prop_string(ctx, -1, "stack");
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(output, "%s\n", duk_to_string(ctx, -1));
|
|
||||||
if (flags & DUK_CONSOLE_FLUSH) {
|
|
||||||
fflush(output);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static duk_ret_t duk__console_assert(duk_context *ctx) {
|
|
||||||
if (duk_to_boolean(ctx, 0)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
duk_remove(ctx, 0);
|
|
||||||
|
|
||||||
return duk__console_log_helper(ctx, "AssertionError");
|
|
||||||
}
|
|
||||||
|
|
||||||
static duk_ret_t duk__console_log(duk_context *ctx) {
|
|
||||||
return duk__console_log_helper(ctx, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static duk_ret_t duk__console_trace(duk_context *ctx) {
|
|
||||||
return duk__console_log_helper(ctx, "Trace");
|
|
||||||
}
|
|
||||||
|
|
||||||
static duk_ret_t duk__console_info(duk_context *ctx) {
|
|
||||||
return duk__console_log_helper(ctx, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static duk_ret_t duk__console_warn(duk_context *ctx) {
|
|
||||||
return duk__console_log_helper(ctx, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static duk_ret_t duk__console_error(duk_context *ctx) {
|
|
||||||
return duk__console_log_helper(ctx, "Error");
|
|
||||||
}
|
|
||||||
|
|
||||||
static duk_ret_t duk__console_dir(duk_context *ctx) {
|
|
||||||
/* For now, just share the formatting of .log() */
|
|
||||||
return duk__console_log_helper(ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void duk__console_reg_vararg_func(duk_context *ctx, duk_c_function func, const char *name, duk_uint_t flags) {
|
|
||||||
duk_push_c_function(ctx, func, DUK_VARARGS);
|
|
||||||
duk_push_string(ctx, "name");
|
|
||||||
duk_push_string(ctx, name);
|
|
||||||
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); /* Improve stacktraces by displaying function name */
|
|
||||||
duk_set_magic(ctx, -1, (duk_int_t) flags);
|
|
||||||
duk_put_prop_string(ctx, -2, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void duk_console_init(duk_context *ctx, duk_uint_t flags) {
|
|
||||||
duk_uint_t flags_orig;
|
|
||||||
|
|
||||||
/* If both DUK_CONSOLE_STDOUT_ONLY and DUK_CONSOLE_STDERR_ONLY where specified,
|
|
||||||
* just turn off DUK_CONSOLE_STDOUT_ONLY and keep DUK_CONSOLE_STDERR_ONLY.
|
|
||||||
*/
|
|
||||||
if ((flags & DUK_CONSOLE_STDOUT_ONLY) && (flags & DUK_CONSOLE_STDERR_ONLY)) {
|
|
||||||
flags &= ~DUK_CONSOLE_STDOUT_ONLY;
|
|
||||||
}
|
|
||||||
/* Remember the (possibly corrected) flags we received. */
|
|
||||||
flags_orig = flags;
|
|
||||||
|
|
||||||
duk_push_object(ctx);
|
|
||||||
|
|
||||||
/* Custom function to format objects; user can replace.
|
|
||||||
* For now, try JX-formatting and if that fails, fall back
|
|
||||||
* to ToString(v).
|
|
||||||
*/
|
|
||||||
duk_eval_string(ctx,
|
|
||||||
"(function (E) {"
|
|
||||||
"return function format(v){"
|
|
||||||
"try{"
|
|
||||||
"return E('jx',v);"
|
|
||||||
"}catch(e){"
|
|
||||||
"return String(v);" /* String() allows symbols, ToString() internal algorithm doesn't. */
|
|
||||||
"}"
|
|
||||||
"};"
|
|
||||||
"})(Duktape.enc)");
|
|
||||||
duk_put_prop_string(ctx, -2, "format");
|
|
||||||
|
|
||||||
flags = flags_orig;
|
|
||||||
if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) {
|
|
||||||
/* No output indicators were specified; these levels go to stdout. */
|
|
||||||
flags |= DUK_CONSOLE_STDOUT_ONLY;
|
|
||||||
}
|
|
||||||
duk__console_reg_vararg_func(ctx, duk__console_assert, "assert", flags);
|
|
||||||
duk__console_reg_vararg_func(ctx, duk__console_log, "log", flags);
|
|
||||||
duk__console_reg_vararg_func(ctx, duk__console_log, "debug", flags); /* alias to console.log */
|
|
||||||
duk__console_reg_vararg_func(ctx, duk__console_trace, "trace", flags);
|
|
||||||
duk__console_reg_vararg_func(ctx, duk__console_info, "info", flags);
|
|
||||||
|
|
||||||
flags = flags_orig;
|
|
||||||
if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) {
|
|
||||||
/* No output indicators were specified; these levels go to stderr. */
|
|
||||||
flags |= DUK_CONSOLE_STDERR_ONLY;
|
|
||||||
}
|
|
||||||
duk__console_reg_vararg_func(ctx, duk__console_warn, "warn", flags);
|
|
||||||
duk__console_reg_vararg_func(ctx, duk__console_error, "error", flags);
|
|
||||||
duk__console_reg_vararg_func(ctx, duk__console_error, "exception", flags); /* alias to console.error */
|
|
||||||
duk__console_reg_vararg_func(ctx, duk__console_dir, "dir", flags);
|
|
||||||
|
|
||||||
duk_put_global_string(ctx, "console");
|
|
||||||
|
|
||||||
/* Proxy wrapping: ensures any undefined console method calls are
|
|
||||||
* ignored silently. This was required specifically by the
|
|
||||||
* DeveloperToolsWG proposal (and was implemented also by Firefox:
|
|
||||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=629607). This is
|
|
||||||
* apparently no longer the preferred way of implementing console.
|
|
||||||
* When Proxy is enabled, whitelist at least .toJSON() to avoid
|
|
||||||
* confusing JX serialization of the console object.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (flags & DUK_CONSOLE_PROXY_WRAPPER) {
|
|
||||||
/* Tolerate failure to initialize Proxy wrapper in case
|
|
||||||
* Proxy support is disabled.
|
|
||||||
*/
|
|
||||||
(void) duk_peval_string_noresult(ctx,
|
|
||||||
"(function(){"
|
|
||||||
"var D=function(){};"
|
|
||||||
"var W={toJSON:true};" /* whitelisted */
|
|
||||||
"console=new Proxy(console,{"
|
|
||||||
"get:function(t,k){"
|
|
||||||
"var v=t[k];"
|
|
||||||
"return typeof v==='function'||W[k]?v:D;"
|
|
||||||
"}"
|
|
||||||
"});"
|
|
||||||
"})();"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#if !defined(DUK_CONSOLE_H_INCLUDED)
|
|
||||||
#define DUK_CONSOLE_H_INCLUDED
|
|
||||||
|
|
||||||
#include "duktape.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Use a proxy wrapper to make undefined methods (console.foo()) no-ops. */
|
|
||||||
#define DUK_CONSOLE_PROXY_WRAPPER (1U << 0)
|
|
||||||
|
|
||||||
/* Flush output after every call. */
|
|
||||||
#define DUK_CONSOLE_FLUSH (1U << 1)
|
|
||||||
|
|
||||||
/* Send output to stdout only (default is mixed stdout/stderr). */
|
|
||||||
#define DUK_CONSOLE_STDOUT_ONLY (1U << 2)
|
|
||||||
|
|
||||||
/* Send output to stderr only (default is mixed stdout/stderr). */
|
|
||||||
#define DUK_CONSOLE_STDERR_ONLY (1U << 3)
|
|
||||||
|
|
||||||
/* Initialize the console system */
|
|
||||||
extern void duk_console_init(duk_context *ctx, duk_uint_t flags);
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
}
|
|
||||||
#endif /* end 'extern "C"' wrapper */
|
|
||||||
|
|
||||||
#endif /* DUK_CONSOLE_H_INCLUDED */
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,43 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <vector>
|
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
class Event {
|
|
||||||
public:
|
|
||||||
Event() {}
|
|
||||||
~Event() {}
|
|
||||||
|
|
||||||
struct EventHandler {
|
|
||||||
EventHandler() {}
|
|
||||||
EventHandler(void (*handler)(T, void*), void* ctx) {
|
|
||||||
this->handler = handler;
|
|
||||||
this->ctx = ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
void (*handler)(T, void*);
|
|
||||||
void* ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
void emit(T value) {
|
|
||||||
for (auto const& handler : handlers) {
|
|
||||||
handler.handler(value, handler.ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bindHandler(const EventHandler& handler) {
|
|
||||||
handlers.push_back(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
void unbindHandler(const EventHandler& handler) {
|
|
||||||
if (handlers.find(handler) == handlers.end()) {
|
|
||||||
spdlog::error("Tried to remove a non-existant event handler");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handlers.erase(std::remove(handlers.begin(), handlers.end(), handler), handlers.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<EventHandler> handlers;
|
|
||||||
|
|
||||||
};
|
|
||||||
6
core/src/glfw_window.h
Normal file
6
core/src/glfw_window.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <module.h>
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
SDRPP_EXPORT GLFWwindow* window;
|
||||||
|
};
|
||||||
49
core/src/gui/colormaps.cpp
Normal file
49
core/src/gui/colormaps.cpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#include <gui/colormaps.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <json.hpp>
|
||||||
|
|
||||||
|
using nlohmann::json;
|
||||||
|
|
||||||
|
namespace colormaps {
|
||||||
|
std::map<std::string, Map> maps;
|
||||||
|
|
||||||
|
void loadMap(std::string path) {
|
||||||
|
if (!std::filesystem::is_regular_file(path)) {
|
||||||
|
spdlog::error("Could not load {0}, file doesn't exist", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream file(path.c_str());
|
||||||
|
json data;
|
||||||
|
file >> data;
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
Map map;
|
||||||
|
std::vector<std::string> mapTxt;
|
||||||
|
|
||||||
|
try {
|
||||||
|
map.name = data["name"];
|
||||||
|
map.author = data["author"];
|
||||||
|
mapTxt = data["map"].get<std::vector<std::string>>();
|
||||||
|
}
|
||||||
|
catch (const std::exception&) {
|
||||||
|
spdlog::error("Could not load {0}", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
map.entryCount = mapTxt.size();
|
||||||
|
map.map = new float[mapTxt.size() * 3];
|
||||||
|
int i = 0;
|
||||||
|
for(auto const& col : mapTxt) {
|
||||||
|
uint8_t r, g, b, a;
|
||||||
|
map.map[i * 3] = std::stoi(col.substr(1, 2), NULL, 16);
|
||||||
|
map.map[(i * 3) + 1] = std::stoi(col.substr(3, 2), NULL, 16);
|
||||||
|
map.map[(i * 3) + 2] = std::stoi(col.substr(5, 2), NULL, 16);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
maps[map.name] = map;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
core/src/gui/colormaps.h
Normal file
18
core/src/gui/colormaps.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <module.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace colormaps {
|
||||||
|
struct Map {
|
||||||
|
std::string name;
|
||||||
|
std::string author;
|
||||||
|
float* map;
|
||||||
|
int entryCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
void loadMap(std::string path);
|
||||||
|
|
||||||
|
SDRPP_EXPORT std::map<std::string, Map> maps;
|
||||||
|
}
|
||||||
@@ -15,6 +15,10 @@ namespace credits {
|
|||||||
|
|
||||||
void show() {
|
void show() {
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f, 20.0f));
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f, 20.0f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0,0,0,0));
|
||||||
|
ImVec2 dispSize = ImGui::GetIO().DisplaySize;
|
||||||
|
ImVec2 center = ImVec2(dispSize.x/2.0f, dispSize.y/2.0f);
|
||||||
|
ImGui::SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
||||||
ImGui::OpenPopup("Credits");
|
ImGui::OpenPopup("Credits");
|
||||||
ImGui::BeginPopupModal("Credits", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove);
|
ImGui::BeginPopupModal("Credits", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove);
|
||||||
|
|
||||||
@@ -27,25 +31,25 @@ namespace credits {
|
|||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::Text("This software is brought to you by\n\n");
|
ImGui::Text("This software is brought to you by Alexandre Rouma with the help of\n\n");
|
||||||
|
|
||||||
ImGui::Columns(3, "CreditColumns", true);
|
ImGui::Columns(3, "CreditColumns", true);
|
||||||
|
|
||||||
ImGui::Text("Contributors");
|
ImGui::Text("Contributors");
|
||||||
for (int i = 0; i < sdrpp_credits::contributorCount; i++) {
|
for (int i = 0; i < sdrpp_credits::contributorCount; i++) {
|
||||||
ImGui::BulletText(sdrpp_credits::contributors[i]);
|
ImGui::BulletText("%s", sdrpp_credits::contributors[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("Libraries");
|
ImGui::Text("Libraries");
|
||||||
for (int i = 0; i < sdrpp_credits::libraryCount; i++) {
|
for (int i = 0; i < sdrpp_credits::libraryCount; i++) {
|
||||||
ImGui::BulletText(sdrpp_credits::libraries[i]);
|
ImGui::BulletText("%s", sdrpp_credits::libraries[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::Text("Patrons");
|
ImGui::Text("Patrons");
|
||||||
for (int i = 0; i < sdrpp_credits::patronCount; i++) {
|
for (int i = 0; i < sdrpp_credits::patronCount; i++) {
|
||||||
ImGui::BulletText(sdrpp_credits::patrons[i]);
|
ImGui::BulletText("%s", sdrpp_credits::patrons[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Columns(1, "CreditColumnsEnd", true);
|
ImGui::Columns(1, "CreditColumnsEnd", true);
|
||||||
@@ -53,9 +57,10 @@ namespace credits {
|
|||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Text("SDR++ v" VERSION_STR);
|
ImGui::Text("SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")");
|
||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
ImGui::PopStyleVar(1);
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <gui/icons.h>
|
#include <gui/icons.h>
|
||||||
#include <gui/style.h>
|
#include <gui/style.h>
|
||||||
#include <credits.h>
|
#include <credits.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
|
||||||
namespace LoadingScreen {
|
namespace LoadingScreen {
|
||||||
GLFWwindow* _win;
|
GLFWwindow* _win;
|
||||||
@@ -26,7 +27,7 @@ namespace LoadingScreen {
|
|||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f, 20.0f));
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f, 20.0f));
|
||||||
ImGui::OpenPopup("Credits");
|
ImGui::OpenPopup("Credits");
|
||||||
ImGui::PushStyleColor(ImGuiCol_ModalWindowDarkening, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
ImGui::PushStyleColor(ImGuiCol_ModalWindowDimBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||||
ImGui::BeginPopupModal("Credits", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBackground);
|
ImGui::BeginPopupModal("Credits", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBackground);
|
||||||
|
|
||||||
ImGui::PushFont(style::hugeFont);
|
ImGui::PushFont(style::hugeFont);
|
||||||
@@ -34,37 +35,40 @@ namespace LoadingScreen {
|
|||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Image(icons::LOGO, ImVec2(128, 128));
|
ImGui::Image(icons::LOGO, ImVec2(128, 128));
|
||||||
ImGui::Spacing();
|
// ImGui::Spacing();
|
||||||
ImGui::Spacing();
|
// ImGui::Spacing();
|
||||||
ImGui::Spacing();
|
// ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::Text("This software is brought to you by\n\n");
|
// ImGui::Text("This software is brought to you by\n\n");
|
||||||
|
|
||||||
ImGui::Columns(3, "CreditColumns", true);
|
// ImGui::Columns(3, "CreditColumns", true);
|
||||||
|
|
||||||
ImGui::Text("Contributors");
|
// ImGui::Text("Contributors");
|
||||||
for (int i = 0; i < sdrpp_credits::contributorCount; i++) {
|
// for (int i = 0; i < sdrpp_credits::contributorCount; i++) {
|
||||||
ImGui::BulletText(sdrpp_credits::contributors[i]);
|
// ImGui::BulletText("%s", sdrpp_credits::contributors[i]);
|
||||||
}
|
// }
|
||||||
|
|
||||||
ImGui::NextColumn();
|
// ImGui::NextColumn();
|
||||||
ImGui::Text("Libraries");
|
// ImGui::Text("Libraries");
|
||||||
for (int i = 0; i < sdrpp_credits::libraryCount; i++) {
|
// for (int i = 0; i < sdrpp_credits::libraryCount; i++) {
|
||||||
ImGui::BulletText(sdrpp_credits::libraries[i]);
|
// ImGui::BulletText("%s", sdrpp_credits::libraries[i]);
|
||||||
}
|
// }
|
||||||
|
|
||||||
ImGui::NextColumn();
|
// ImGui::NextColumn();
|
||||||
ImGui::Text("Patrons");
|
// ImGui::Text("Patrons");
|
||||||
for (int i = 0; i < sdrpp_credits::patronCount; i++) {
|
// for (int i = 0; i < sdrpp_credits::patronCount; i++) {
|
||||||
ImGui::BulletText(sdrpp_credits::patrons[i]);
|
// ImGui::BulletText("%s", sdrpp_credits::patrons[i]);
|
||||||
}
|
// }
|
||||||
|
|
||||||
ImGui::Columns(1, "CreditColumnsEnd", true);
|
// ImGui::Columns(1, "CreditColumnsEnd", true);
|
||||||
|
|
||||||
ImGui::Spacing();
|
// ImGui::Spacing();
|
||||||
ImGui::Spacing();
|
// ImGui::Spacing();
|
||||||
ImGui::Spacing();
|
// ImGui::Spacing();
|
||||||
ImGui::Text(msg.c_str());
|
ImVec2 origPos = ImGui::GetCursorPos();
|
||||||
|
ImGui::SetCursorPosY(origPos.y + 50);
|
||||||
|
ImGui::Text("%s", msg.c_str());
|
||||||
|
ImGui::SetCursorPos(origPos);
|
||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
ImGui::PopStyleVar(1);
|
ImGui::PopStyleVar(1);
|
||||||
@@ -76,7 +80,7 @@ namespace LoadingScreen {
|
|||||||
int display_w, display_h;
|
int display_w, display_h;
|
||||||
glfwGetFramebufferSize(_win, &display_w, &display_h);
|
glfwGetFramebufferSize(_win, &display_w, &display_h);
|
||||||
glViewport(0, 0, display_w, display_h);
|
glViewport(0, 0, display_w, display_h);
|
||||||
glClearColor(0.0666f, 0.0666f, 0.0666f, 1.0f);
|
glClearColor(gui::themeManager.clearColor.x, gui::themeManager.clearColor.y, gui::themeManager.clearColor.z, gui::themeManager.clearColor.w);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
|
|||||||
1709
core/src/gui/file_dialogs.h
Normal file
1709
core/src/gui/file_dialogs.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,9 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
|
MainWindow mainWindow;
|
||||||
ImGui::WaterFall waterfall;
|
ImGui::WaterFall waterfall;
|
||||||
FrequencySelect freqSelect;
|
FrequencySelect freqSelect;
|
||||||
|
ThemeManager themeManager;
|
||||||
Menu menu;
|
Menu menu;
|
||||||
};
|
};
|
||||||
@@ -3,12 +3,16 @@
|
|||||||
#include <gui/widgets/frequency_select.h>
|
#include <gui/widgets/frequency_select.h>
|
||||||
#include <gui/widgets/menu.h>
|
#include <gui/widgets/menu.h>
|
||||||
#include <gui/dialogs/loading_screen.h>
|
#include <gui/dialogs/loading_screen.h>
|
||||||
#include <new_module.h>
|
#include <module.h>
|
||||||
|
#include <gui/main_window.h>
|
||||||
|
#include <gui/theme_manager.h>
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
SDRPP_EXPORT ImGui::WaterFall waterfall;
|
SDRPP_EXPORT ImGui::WaterFall waterfall;
|
||||||
SDRPP_EXPORT FrequencySelect freqSelect;
|
SDRPP_EXPORT FrequencySelect freqSelect;
|
||||||
SDRPP_EXPORT Menu menu;
|
SDRPP_EXPORT Menu menu;
|
||||||
|
SDRPP_EXPORT ThemeManager themeManager;
|
||||||
|
SDRPP_EXPORT MainWindow mainWindow;
|
||||||
|
|
||||||
void selectSource(std::string name);
|
void selectSource(std::string name);
|
||||||
};
|
};
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include <imgui/stb_image.h>
|
#include <imgui/stb_image.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
namespace icons {
|
namespace icons {
|
||||||
ImTextureID LOGO;
|
ImTextureID LOGO;
|
||||||
@@ -31,14 +33,21 @@ namespace icons {
|
|||||||
return texId;
|
return texId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load() {
|
bool load(std::string resDir) {
|
||||||
LOGO = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/sdrpp.png");
|
if (!std::filesystem::is_directory(resDir)) {
|
||||||
PLAY = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/play.png");
|
spdlog::error("Inavlid resource directory: {0}", resDir);
|
||||||
STOP = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/stop.png");
|
return false;
|
||||||
MENU = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/menu.png");
|
}
|
||||||
MUTED = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/muted.png");
|
|
||||||
UNMUTED = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/unmuted.png");
|
LOGO = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/sdrpp.png");
|
||||||
NORMAL_TUNING = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/normal_tuning.png");
|
PLAY = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/play.png");
|
||||||
CENTER_TUNING = (ImTextureID)(uintptr_t)loadTexture(options::opts.root + "/res/icons/center_tuning.png");
|
STOP = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/stop.png");
|
||||||
|
MENU = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/menu.png");
|
||||||
|
MUTED = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/muted.png");
|
||||||
|
UNMUTED = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/unmuted.png");
|
||||||
|
NORMAL_TUNING = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/normal_tuning.png");
|
||||||
|
CENTER_TUNING = (ImTextureID)(uintptr_t)loadTexture(resDir + "/icons/center_tuning.png");
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,5 +14,5 @@ namespace icons {
|
|||||||
extern ImTextureID CENTER_TUNING;
|
extern ImTextureID CENTER_TUNING;
|
||||||
|
|
||||||
GLuint loadTexture(std::string path);
|
GLuint loadTexture(std::string path);
|
||||||
void load();
|
bool load(std::string resDir);
|
||||||
}
|
}
|
||||||
@@ -6,17 +6,13 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
#include <imgui_plot.h>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
#include <gui/widgets/waterfall.h>
|
#include <gui/widgets/waterfall.h>
|
||||||
#include <gui/widgets/frequency_select.h>
|
#include <gui/widgets/frequency_select.h>
|
||||||
#include <fftw3.h>
|
|
||||||
#include <signal_path/dsp.h>
|
#include <signal_path/dsp.h>
|
||||||
#include <gui/icons.h>
|
#include <gui/icons.h>
|
||||||
#include <gui/widgets/bandplan.h>
|
#include <gui/widgets/bandplan.h>
|
||||||
#include <watcher.h>
|
|
||||||
#include <signal_path/vfo_manager.h>
|
|
||||||
#include <gui/style.h>
|
#include <gui/style.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <signal_path/signal_path.h>
|
#include <signal_path/signal_path.h>
|
||||||
@@ -25,108 +21,51 @@
|
|||||||
#include <gui/menus/display.h>
|
#include <gui/menus/display.h>
|
||||||
#include <gui/menus/bandplan.h>
|
#include <gui/menus/bandplan.h>
|
||||||
#include <gui/menus/sink.h>
|
#include <gui/menus/sink.h>
|
||||||
#include <gui/menus/scripting.h>
|
#include <gui/menus/vfo_color.h>
|
||||||
|
#include <gui/menus/module_manager.h>
|
||||||
|
#include <gui/menus/theme.h>
|
||||||
#include <gui/dialogs/credits.h>
|
#include <gui/dialogs/credits.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <signal_path/source.h>
|
#include <signal_path/source.h>
|
||||||
#include <gui/dialogs/loading_screen.h>
|
#include <gui/dialogs/loading_screen.h>
|
||||||
#include <options.h>
|
#include <options.h>
|
||||||
|
#include <gui/colormaps.h>
|
||||||
|
#include <gui/widgets/snr_meter.h>
|
||||||
|
#include <gui/tuner.h>
|
||||||
|
|
||||||
// const int FFTSizes[] = {
|
void MainWindow::init() {
|
||||||
// 65536,
|
|
||||||
// 32768,
|
|
||||||
// 16384,
|
|
||||||
// 8192,
|
|
||||||
// 4096,
|
|
||||||
// 2048
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const char* FFTSizesStr[] = {
|
|
||||||
// "65536",
|
|
||||||
// "32768",
|
|
||||||
// "16384",
|
|
||||||
// "8192",
|
|
||||||
// "4096",
|
|
||||||
// "2048"
|
|
||||||
// };
|
|
||||||
|
|
||||||
// int fftSizeId = 0;
|
|
||||||
int fftSize = 8192 * 8;
|
|
||||||
|
|
||||||
std::thread worker;
|
|
||||||
std::mutex fft_mtx;
|
|
||||||
fftwf_complex *fft_in, *fft_out;
|
|
||||||
fftwf_plan p;
|
|
||||||
float* tempFFT;
|
|
||||||
float* FFTdata;
|
|
||||||
char buf[1024];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void fftHandler(dsp::complex_t* samples, int count, void* ctx) {
|
|
||||||
memcpy(fft_in, samples, count * sizeof(dsp::complex_t));
|
|
||||||
fftwf_execute(p);
|
|
||||||
int half = count / 2;
|
|
||||||
|
|
||||||
volk_32fc_s32f_power_spectrum_32f(tempFFT, (lv_32fc_t*)fft_out, count, count);
|
|
||||||
volk_32f_s32f_multiply_32f(FFTdata, tempFFT, 0.5f, count);
|
|
||||||
|
|
||||||
memcpy(tempFFT, &FFTdata[half], half * sizeof(float));
|
|
||||||
memmove(&FFTdata[half], FFTdata, half * sizeof(float));
|
|
||||||
memcpy(FFTdata, tempFFT, half * sizeof(float));
|
|
||||||
|
|
||||||
float* fftBuf = gui::waterfall.getFFTBuffer();
|
|
||||||
if (fftBuf == NULL) {
|
|
||||||
gui::waterfall.pushFFT();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
float last = FFTdata[0];
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
last = (FFTdata[i] * 0.1f) + (last * 0.9f);
|
|
||||||
fftBuf[i] = last;
|
|
||||||
}
|
|
||||||
gui::waterfall.pushFFT();
|
|
||||||
}
|
|
||||||
|
|
||||||
watcher<uint64_t> freq((uint64_t)90500000);
|
|
||||||
watcher<double> vfoFreq(92000000.0);
|
|
||||||
float fftMin = -70.0;
|
|
||||||
float fftMax = 0.0;
|
|
||||||
watcher<double> offset(0.0, true);
|
|
||||||
float bw = 8000000;
|
|
||||||
bool playing = false;
|
|
||||||
watcher<bool> dcbias(false, false);
|
|
||||||
bool showCredits = false;
|
|
||||||
std::string audioStreamName = "";
|
|
||||||
std::string sourceName = "";
|
|
||||||
int menuWidth = 300;
|
|
||||||
bool grabbingMenu = false;
|
|
||||||
int newWidth = 300;
|
|
||||||
int fftHeight = 300;
|
|
||||||
bool showMenu = true;
|
|
||||||
bool centerTuning = false;
|
|
||||||
dsp::stream<dsp::complex_t> dummyStream;
|
|
||||||
bool demoWindow = false;
|
|
||||||
|
|
||||||
void windowInit() {
|
|
||||||
LoadingScreen::show("Initializing UI");
|
LoadingScreen::show("Initializing UI");
|
||||||
gui::waterfall.init();
|
gui::waterfall.init();
|
||||||
gui::waterfall.setRawFFTSize(fftSize);
|
gui::waterfall.setRawFFTSize(fftSize);
|
||||||
|
|
||||||
tempFFT = new float[fftSize];
|
|
||||||
FFTdata = new float[fftSize];
|
|
||||||
|
|
||||||
credits::init();
|
credits::init();
|
||||||
|
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
gui::menu.order = core::configManager.conf["menuOrder"].get<std::vector<std::string>>();
|
json menuElements = core::configManager.conf["menuElements"];
|
||||||
|
std::string modulesDir = core::configManager.conf["modulesDirectory"];
|
||||||
|
std::string resourcesDir = core::configManager.conf["resourcesDirectory"];
|
||||||
core::configManager.release();
|
core::configManager.release();
|
||||||
|
|
||||||
|
// Load menu elements
|
||||||
|
gui::menu.order.clear();
|
||||||
|
for (auto& elem : menuElements) {
|
||||||
|
if (!elem.contains("name")) { spdlog::error("Menu element is missing name key"); continue; }
|
||||||
|
if (!elem["name"].is_string()) { spdlog::error("Menu element name isn't a string"); continue; }
|
||||||
|
if (!elem.contains("open")) { spdlog::error("Menu element is missing open key"); continue; }
|
||||||
|
if (!elem["open"].is_boolean()) { spdlog::error("Menu element name isn't a string"); continue; }
|
||||||
|
Menu::MenuOption_t opt;
|
||||||
|
opt.name = elem["name"];
|
||||||
|
opt.open = elem["open"];
|
||||||
|
gui::menu.order.push_back(opt);
|
||||||
|
}
|
||||||
|
|
||||||
gui::menu.registerEntry("Source", sourecmenu::draw, NULL);
|
gui::menu.registerEntry("Source", sourecmenu::draw, NULL);
|
||||||
gui::menu.registerEntry("Sinks", sinkmenu::draw, NULL);
|
gui::menu.registerEntry("Sinks", sinkmenu::draw, NULL);
|
||||||
gui::menu.registerEntry("Scripting", scriptingmenu::draw, NULL);
|
|
||||||
gui::menu.registerEntry("Band Plan", bandplanmenu::draw, NULL);
|
gui::menu.registerEntry("Band Plan", bandplanmenu::draw, NULL);
|
||||||
gui::menu.registerEntry("Display", displaymenu::draw, NULL);
|
gui::menu.registerEntry("Display", displaymenu::draw, NULL);
|
||||||
|
gui::menu.registerEntry("Theme", thememenu::draw, NULL);
|
||||||
|
gui::menu.registerEntry("VFO Color", vfo_color_menu::draw, NULL);
|
||||||
|
gui::menu.registerEntry("Module Manager", module_manager_menu::draw, NULL);
|
||||||
|
|
||||||
gui::freqSelect.init();
|
gui::freqSelect.init();
|
||||||
|
|
||||||
@@ -136,16 +75,20 @@ void windowInit() {
|
|||||||
|
|
||||||
fft_in = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSize);
|
fft_in = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSize);
|
||||||
fft_out = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSize);
|
fft_out = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSize);
|
||||||
p = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE);
|
fftwPlan = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
|
|
||||||
sigpath::signalPath.init(8000000, 20, fftSize, &dummyStream, (dsp::complex_t*)fft_in, fftHandler);
|
sigpath::signalPath.init(8000000, 20, fftSize, &dummyStream, (dsp::complex_t*)fft_in, fftHandler, this);
|
||||||
sigpath::signalPath.start();
|
sigpath::signalPath.start();
|
||||||
|
|
||||||
|
vfoCreatedHandler.handler = vfoAddedHandler;
|
||||||
|
vfoCreatedHandler.ctx = this;
|
||||||
|
sigpath::vfoManager.onVfoCreated.bindHandler(&vfoCreatedHandler);
|
||||||
|
|
||||||
spdlog::info("Loading modules");
|
spdlog::info("Loading modules");
|
||||||
|
|
||||||
// Load modules from /module directory
|
// Load modules from /module directory
|
||||||
if (std::filesystem::is_directory(options::opts.root + "/modules")) {
|
if (std::filesystem::is_directory(modulesDir)) {
|
||||||
for (const auto & file : std::filesystem::directory_iterator(options::opts.root + "/modules")) {
|
for (const auto & file : std::filesystem::directory_iterator(modulesDir)) {
|
||||||
std::string path = file.path().generic_string();
|
std::string path = file.path().generic_string();
|
||||||
if (file.path().extension().generic_string() != SDRPP_MOD_EXTENTSION) {
|
if (file.path().extension().generic_string() != SDRPP_MOD_EXTENTSION) {
|
||||||
continue;
|
continue;
|
||||||
@@ -157,13 +100,13 @@ void windowInit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
spdlog::warn("Module directory {0} does not exist, not loading modules from directory");
|
spdlog::warn("Module directory {0} does not exist, not loading modules from directory", modulesDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read module config
|
// Read module config
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
std::vector<std::string> modules = core::configManager.conf["modules"];
|
std::vector<std::string> modules = core::configManager.conf["modules"];
|
||||||
std::map<std::string, std::string> modList = core::configManager.conf["moduleInstances"];
|
auto modList = core::configManager.conf["moduleInstances"].items();
|
||||||
core::configManager.release();
|
core::configManager.release();
|
||||||
|
|
||||||
// Load additional modules specified through config
|
// Load additional modules specified through config
|
||||||
@@ -174,30 +117,49 @@ void windowInit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create module instances
|
// Create module instances
|
||||||
for (auto const& [name, module] : modList) {
|
for (auto const& [name, _module] : modList) {
|
||||||
spdlog::info("Initializing {0} ({1})", name, module);
|
std::string mod = _module["module"];
|
||||||
LoadingScreen::show("Initializing " + name + " (" + module + ")");
|
bool enabled = _module["enabled"];
|
||||||
core::moduleManager.createInstance(name, module);
|
spdlog::info("Initializing {0} ({1})", name, mod);
|
||||||
|
LoadingScreen::show("Initializing " + name + " (" + mod + ")");
|
||||||
|
core::moduleManager.createInstance(name, mod);
|
||||||
|
if (!enabled) { core::moduleManager.disableInstance(name); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load color maps
|
||||||
|
LoadingScreen::show("Loading color maps");
|
||||||
|
spdlog::info("Loading color maps");
|
||||||
|
if (std::filesystem::is_directory(resourcesDir + "/colormaps")) {
|
||||||
|
for (const auto & file : std::filesystem::directory_iterator(resourcesDir + "/colormaps")) {
|
||||||
|
std::string path = file.path().generic_string();
|
||||||
|
LoadingScreen::show("Loading " + path);
|
||||||
|
spdlog::info("Loading {0}", path);
|
||||||
|
if (file.path().extension().generic_string() != ".json") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!file.is_regular_file()) { continue; }
|
||||||
|
colormaps::loadMap(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
spdlog::warn("Color map directory {0} does not exist, not loading modules from directory", modulesDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
gui::waterfall.updatePalletteFromArray(colormaps::maps["Turbo"].map, colormaps::maps["Turbo"].entryCount);
|
||||||
|
|
||||||
sourecmenu::init();
|
sourecmenu::init();
|
||||||
sinkmenu::init();
|
sinkmenu::init();
|
||||||
scriptingmenu::init();
|
|
||||||
bandplanmenu::init();
|
bandplanmenu::init();
|
||||||
displaymenu::init();
|
displaymenu::init();
|
||||||
|
vfo_color_menu::init();
|
||||||
|
module_manager_menu::init();
|
||||||
|
|
||||||
// TODO for 0.2.5
|
// TODO for 0.2.5
|
||||||
// Add "select file" option for the file source
|
|
||||||
// Have a good directory system on both linux and windows
|
|
||||||
// Switch to double buffering (should fix occassional underruns)
|
|
||||||
// Fix gain not updated on startup, soapysdr
|
// Fix gain not updated on startup, soapysdr
|
||||||
|
|
||||||
// TODO for 0.2.6
|
|
||||||
// Add a module add/remove/change order menu
|
|
||||||
|
|
||||||
// Update UI settings
|
// Update UI settings
|
||||||
LoadingScreen::show("Loading configuration");
|
LoadingScreen::show("Loading configuration");
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
fftMin = core::configManager.conf["min"];
|
fftMin = core::configManager.conf["min"];
|
||||||
fftMax = core::configManager.conf["max"];
|
fftMax = core::configManager.conf["max"];
|
||||||
gui::waterfall.setFFTMin(fftMin);
|
gui::waterfall.setFFTMin(fftMin);
|
||||||
@@ -207,6 +169,9 @@ void windowInit() {
|
|||||||
|
|
||||||
double frequency = core::configManager.conf["frequency"];
|
double frequency = core::configManager.conf["frequency"];
|
||||||
|
|
||||||
|
showMenu = core::configManager.conf["showMenu"];
|
||||||
|
startedWithMenuClosed = !showMenu;
|
||||||
|
|
||||||
gui::freqSelect.setFrequency(frequency);
|
gui::freqSelect.setFrequency(frequency);
|
||||||
gui::freqSelect.frequencyChanged = false;
|
gui::freqSelect.frequencyChanged = false;
|
||||||
sigpath::sourceManager.tune(frequency);
|
sigpath::sourceManager.tune(frequency);
|
||||||
@@ -222,113 +187,82 @@ void windowInit() {
|
|||||||
fftHeight = core::configManager.conf["fftHeight"];
|
fftHeight = core::configManager.conf["fftHeight"];
|
||||||
gui::waterfall.setFFTHeight(fftHeight);
|
gui::waterfall.setFFTHeight(fftHeight);
|
||||||
|
|
||||||
centerTuning = core::configManager.conf["centerTuning"];
|
tuningMode = core::configManager.conf["centerTuning"] ? tuner::TUNER_MODE_CENTER : tuner::TUNER_MODE_NORMAL;
|
||||||
|
|
||||||
core::configManager.release();
|
core::configManager.release();
|
||||||
|
|
||||||
|
// Correct the offset of all VFOs so that they fit on the screen
|
||||||
|
float finalBwHalf = gui::waterfall.getBandwidth() / 2.0;
|
||||||
|
for (auto& [_name, _vfo] : gui::waterfall.vfos) {
|
||||||
|
if (_vfo->lowerOffset < -finalBwHalf) {
|
||||||
|
sigpath::vfoManager.setCenterOffset(_name, (_vfo->bandwidth/2)-finalBwHalf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (_vfo->upperOffset > finalBwHalf) {
|
||||||
|
sigpath::vfoManager.setCenterOffset(_name, finalBwHalf-(_vfo->bandwidth/2));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initComplete = true;
|
||||||
|
|
||||||
|
core::moduleManager.doPostInitAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setVFO(double freq) {
|
void MainWindow::fftHandler(dsp::complex_t* samples, int count, void* ctx) {
|
||||||
|
MainWindow* _this = (MainWindow*)ctx;
|
||||||
|
std::lock_guard<std::mutex> lck(_this->fft_mtx);
|
||||||
|
|
||||||
|
// Apply window
|
||||||
|
volk_32fc_32f_multiply_32fc((lv_32fc_t*)_this->fft_in, (lv_32fc_t*)samples, sigpath::signalPath.fftTaps, count);
|
||||||
|
|
||||||
|
// Zero out the rest of the samples
|
||||||
|
if (count < _this->fftSize) {
|
||||||
|
memset(&_this->fft_in[count], 0, (_this->fftSize-count) * sizeof(dsp::complex_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute FFT
|
||||||
|
fftwf_execute(_this->fftwPlan);
|
||||||
|
|
||||||
|
// Get the FFT buffer
|
||||||
|
float* fftBuf = gui::waterfall.getFFTBuffer();
|
||||||
|
if (fftBuf == NULL) {
|
||||||
|
gui::waterfall.pushFFT();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take power of spectrum
|
||||||
|
volk_32fc_s32f_power_spectrum_32f(fftBuf, (lv_32fc_t*)_this->fft_out, _this->fftSize, _this->fftSize);
|
||||||
|
|
||||||
|
// Push back data
|
||||||
|
gui::waterfall.pushFFT();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::vfoAddedHandler(VFOManager::VFO* vfo, void* ctx) {
|
||||||
|
MainWindow* _this = (MainWindow*)ctx;
|
||||||
|
std::string name = vfo->getName();
|
||||||
|
core::configManager.acquire();
|
||||||
|
if (!core::configManager.conf["vfoOffsets"].contains(name)) {
|
||||||
|
core::configManager.release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double offset = core::configManager.conf["vfoOffsets"][name];
|
||||||
|
core::configManager.release();
|
||||||
|
|
||||||
double viewBW = gui::waterfall.getViewBandwidth();
|
double viewBW = gui::waterfall.getViewBandwidth();
|
||||||
double BW = gui::waterfall.getBandwidth();
|
double viewOffset = gui::waterfall.getViewOffset();
|
||||||
if (gui::waterfall.selectedVFO == "") {
|
|
||||||
gui::waterfall.setViewOffset((BW / 2.0) - (viewBW / 2.0));
|
|
||||||
gui::waterfall.setCenterFrequency(freq);
|
|
||||||
sigpath::sourceManager.tune(freq);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::WaterfallVFO* vfo = gui::waterfall.vfos[gui::waterfall.selectedVFO];
|
double viewLower = viewOffset - (viewBW/2.0);
|
||||||
|
double viewUpper = viewOffset + (viewBW/2.0);
|
||||||
|
|
||||||
double currentOff = vfo->centerOffset;
|
double newOffset = std::clamp<double>(offset, viewLower, viewUpper);
|
||||||
double currentTune = gui::waterfall.getCenterFrequency() + vfo->generalOffset;
|
|
||||||
double delta = freq - currentTune;
|
|
||||||
|
|
||||||
double newVFO = currentOff + delta;
|
sigpath::vfoManager.setCenterOffset(name, _this->initComplete ? newOffset : offset);
|
||||||
double vfoBW = vfo->bandwidth;
|
|
||||||
double vfoBottom = newVFO - (vfoBW / 2.0);
|
|
||||||
double vfoTop = newVFO + (vfoBW / 2.0);
|
|
||||||
|
|
||||||
double view = gui::waterfall.getViewOffset();
|
|
||||||
double viewBottom = view - (viewBW / 2.0);
|
|
||||||
double viewTop = view + (viewBW / 2.0);
|
|
||||||
|
|
||||||
double wholeFreq = gui::waterfall.getCenterFrequency();
|
|
||||||
double bottom = -(BW / 2.0);
|
|
||||||
double top = (BW / 2.0);
|
|
||||||
|
|
||||||
if (centerTuning) {
|
|
||||||
gui::waterfall.setViewOffset((BW / 2.0) - (viewBW / 2.0));
|
|
||||||
gui::waterfall.setCenterFrequency(freq);
|
|
||||||
gui::waterfall.setViewOffset(0);
|
|
||||||
sigpath::vfoManager.setOffset(gui::waterfall.selectedVFO, 0);
|
|
||||||
sigpath::sourceManager.tune(freq);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// VFO still fints in the view
|
|
||||||
if (vfoBottom > viewBottom && vfoTop < viewTop) {
|
|
||||||
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFO);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// VFO too low for current SDR tuning
|
|
||||||
if (vfoBottom < bottom) {
|
|
||||||
gui::waterfall.setViewOffset((BW / 2.0) - (viewBW / 2.0));
|
|
||||||
double newVFOOffset = (BW / 2.0) - (vfoBW / 2.0) - (viewBW / 10.0);
|
|
||||||
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset);
|
|
||||||
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
|
||||||
sigpath::sourceManager.tune(freq - newVFOOffset);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// VFO too high for current SDR tuning
|
|
||||||
if (vfoTop > top) {
|
|
||||||
gui::waterfall.setViewOffset((viewBW / 2.0) - (BW / 2.0));
|
|
||||||
double newVFOOffset = (vfoBW / 2.0) - (BW / 2.0) + (viewBW / 10.0);
|
|
||||||
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset);
|
|
||||||
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
|
||||||
sigpath::sourceManager.tune(freq - newVFOOffset);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// VFO is still without the SDR's bandwidth
|
|
||||||
if (delta < 0) {
|
|
||||||
double newViewOff = vfoTop - (viewBW / 2.0) + (viewBW / 10.0);
|
|
||||||
double newViewBottom = newViewOff - (viewBW / 2.0);
|
|
||||||
double newViewTop = newViewOff + (viewBW / 2.0);
|
|
||||||
|
|
||||||
if (newViewBottom > bottom) {
|
|
||||||
gui::waterfall.setViewOffset(newViewOff);
|
|
||||||
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFO);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gui::waterfall.setViewOffset((BW / 2.0) - (viewBW / 2.0));
|
|
||||||
double newVFOOffset = (BW / 2.0) - (vfoBW / 2.0) - (viewBW / 10.0);
|
|
||||||
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset);
|
|
||||||
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
|
||||||
sigpath::sourceManager.tune(freq - newVFOOffset);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
double newViewOff = vfoBottom + (viewBW / 2.0) - (viewBW / 10.0);
|
|
||||||
double newViewBottom = newViewOff - (viewBW / 2.0);
|
|
||||||
double newViewTop = newViewOff + (viewBW / 2.0);
|
|
||||||
|
|
||||||
if (newViewTop < top) {
|
|
||||||
gui::waterfall.setViewOffset(newViewOff);
|
|
||||||
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFO);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gui::waterfall.setViewOffset((viewBW / 2.0) - (BW / 2.0));
|
|
||||||
double newVFOOffset = (vfoBW / 2.0) - (BW / 2.0) + (viewBW / 10.0);
|
|
||||||
sigpath::vfoManager.setCenterOffset(gui::waterfall.selectedVFO, newVFOOffset);
|
|
||||||
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
|
||||||
sigpath::sourceManager.tune(freq - newVFOOffset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawWindow() {
|
void MainWindow::draw() {
|
||||||
ImGui::Begin("Main", NULL, WINDOW_FLAGS);
|
ImGui::Begin("Main", NULL, WINDOW_FLAGS);
|
||||||
|
|
||||||
ImGui::WaterfallVFO* vfo = NULL;
|
ImGui::WaterfallVFO* vfo = NULL;
|
||||||
@@ -336,43 +270,47 @@ void drawWindow() {
|
|||||||
vfo = gui::waterfall.vfos[gui::waterfall.selectedVFO];
|
vfo = gui::waterfall.vfos[gui::waterfall.selectedVFO];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle VFO movement
|
||||||
if (vfo != NULL) {
|
if (vfo != NULL) {
|
||||||
if (vfo->centerOffsetChanged) {
|
if (vfo->centerOffsetChanged) {
|
||||||
if (centerTuning) {
|
if (tuningMode == tuner::TUNER_MODE_CENTER) {
|
||||||
setVFO(gui::waterfall.getCenterFrequency() + vfo->generalOffset);
|
tuner::tune(tuner::TUNER_MODE_CENTER, gui::waterfall.selectedVFO, gui::waterfall.getCenterFrequency() + vfo->generalOffset);
|
||||||
}
|
}
|
||||||
gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset);
|
gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency() + vfo->generalOffset);
|
||||||
gui::freqSelect.frequencyChanged = false;
|
gui::freqSelect.frequencyChanged = false;
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["frequency"] = gui::freqSelect.frequency;
|
core::configManager.conf["vfoOffsets"][gui::waterfall.selectedVFO] = vfo->generalOffset;
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sigpath::vfoManager.updateFromWaterfall(&gui::waterfall);
|
sigpath::vfoManager.updateFromWaterfall(&gui::waterfall);
|
||||||
|
|
||||||
if (gui::waterfall.selectedVFOChanged && vfo != NULL) {
|
// Handle selection of another VFO
|
||||||
|
if (gui::waterfall.selectedVFOChanged) {
|
||||||
|
gui::freqSelect.setFrequency((vfo != NULL) ? (vfo->generalOffset + gui::waterfall.getCenterFrequency()) : gui::waterfall.getCenterFrequency());
|
||||||
gui::waterfall.selectedVFOChanged = false;
|
gui::waterfall.selectedVFOChanged = false;
|
||||||
gui::freqSelect.setFrequency(vfo->generalOffset + gui::waterfall.getCenterFrequency());
|
|
||||||
gui::freqSelect.frequencyChanged = false;
|
gui::freqSelect.frequencyChanged = false;
|
||||||
core::configManager.aquire();
|
|
||||||
core::configManager.conf["frequency"] = gui::freqSelect.frequency;
|
|
||||||
core::configManager.release(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle change in selected frequency
|
||||||
if (gui::freqSelect.frequencyChanged) {
|
if (gui::freqSelect.frequencyChanged) {
|
||||||
gui::freqSelect.frequencyChanged = false;
|
gui::freqSelect.frequencyChanged = false;
|
||||||
setVFO(gui::freqSelect.frequency);
|
tuner::tune(tuningMode, gui::waterfall.selectedVFO, gui::freqSelect.frequency);
|
||||||
if (vfo != NULL) {
|
if (vfo != NULL) {
|
||||||
vfo->centerOffsetChanged = false;
|
vfo->centerOffsetChanged = false;
|
||||||
vfo->lowerOffsetChanged = false;
|
vfo->lowerOffsetChanged = false;
|
||||||
vfo->upperOffsetChanged = false;
|
vfo->upperOffsetChanged = false;
|
||||||
}
|
}
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["frequency"] = gui::freqSelect.frequency;
|
core::configManager.conf["frequency"] = gui::waterfall.getCenterFrequency();
|
||||||
|
if (vfo != NULL) {
|
||||||
|
core::configManager.conf["vfoOffsets"][gui::waterfall.selectedVFO] = vfo->generalOffset;
|
||||||
|
}
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle dragging the frequency scale
|
||||||
if (gui::waterfall.centerFreqMoved) {
|
if (gui::waterfall.centerFreqMoved) {
|
||||||
gui::waterfall.centerFreqMoved = false;
|
gui::waterfall.centerFreqMoved = false;
|
||||||
sigpath::sourceManager.tune(gui::waterfall.getCenterFrequency());
|
sigpath::sourceManager.tune(gui::waterfall.getCenterFrequency());
|
||||||
@@ -382,15 +320,15 @@ void drawWindow() {
|
|||||||
else {
|
else {
|
||||||
gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency());
|
gui::freqSelect.setFrequency(gui::waterfall.getCenterFrequency());
|
||||||
}
|
}
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["frequency"] = gui::freqSelect.frequency;
|
core::configManager.conf["frequency"] = gui::waterfall.getCenterFrequency();
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _fftHeight = gui::waterfall.getFFTHeight();
|
int _fftHeight = gui::waterfall.getFFTHeight();
|
||||||
if (fftHeight != _fftHeight) {
|
if (fftHeight != _fftHeight) {
|
||||||
fftHeight = _fftHeight;
|
fftHeight = _fftHeight;
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["fftHeight"] = fftHeight;
|
core::configManager.conf["fftHeight"] = fftHeight;
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
@@ -403,31 +341,40 @@ void drawWindow() {
|
|||||||
|
|
||||||
// To Bar
|
// To Bar
|
||||||
ImGui::PushID(ImGui::GetID("sdrpp_menu_btn"));
|
ImGui::PushID(ImGui::GetID("sdrpp_menu_btn"));
|
||||||
if (ImGui::ImageButton(icons::MENU, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) {
|
if (ImGui::ImageButton(icons::MENU, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(GLFW_KEY_MENU, false)) {
|
||||||
showMenu = !showMenu;
|
showMenu = !showMenu;
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["showMenu"] = showMenu;
|
||||||
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
bool tmpPlaySate = playing;
|
||||||
|
if (playButtonLocked && !tmpPlaySate) { style::beginDisabled(); }
|
||||||
if (playing) {
|
if (playing) {
|
||||||
ImGui::PushID(ImGui::GetID("sdrpp_stop_btn"));
|
ImGui::PushID(ImGui::GetID("sdrpp_stop_btn"));
|
||||||
if (ImGui::ImageButton(icons::STOP, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) {
|
if (ImGui::ImageButton(icons::STOP, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(GLFW_KEY_END, false)) {
|
||||||
sigpath::sourceManager.stop();
|
|
||||||
playing = false;
|
playing = false;
|
||||||
|
onPlayStateChange.emit(false);
|
||||||
|
sigpath::sourceManager.stop();
|
||||||
|
sigpath::signalPath.inputBuffer.flush();
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
else { // TODO: Might need to check if there even is a device
|
else { // TODO: Might need to check if there even is a device
|
||||||
ImGui::PushID(ImGui::GetID("sdrpp_play_btn"));
|
ImGui::PushID(ImGui::GetID("sdrpp_play_btn"));
|
||||||
if (ImGui::ImageButton(icons::PLAY, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) {
|
if (ImGui::ImageButton(icons::PLAY, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(GLFW_KEY_END, false)) {
|
||||||
|
sigpath::signalPath.inputBuffer.flush();
|
||||||
sigpath::sourceManager.start();
|
sigpath::sourceManager.start();
|
||||||
// TODO: tune in module instead
|
|
||||||
sigpath::sourceManager.tune(gui::waterfall.getCenterFrequency());
|
sigpath::sourceManager.tune(gui::waterfall.getCenterFrequency());
|
||||||
playing = true;
|
playing = true;
|
||||||
|
onPlayStateChange.emit(true);
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
if (playButtonLocked && !tmpPlaySate) { style::endDisabled(); }
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
@@ -438,15 +385,15 @@ void drawWindow() {
|
|||||||
|
|
||||||
gui::freqSelect.draw();
|
gui::freqSelect.draw();
|
||||||
|
|
||||||
//ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 9);
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 9);
|
||||||
if (centerTuning) {
|
if (tuningMode == tuner::TUNER_MODE_CENTER) {
|
||||||
ImGui::PushID(ImGui::GetID("sdrpp_ena_st_btn"));
|
ImGui::PushID(ImGui::GetID("sdrpp_ena_st_btn"));
|
||||||
if (ImGui::ImageButton(icons::CENTER_TUNING, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) {
|
if (ImGui::ImageButton(icons::CENTER_TUNING, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) {
|
||||||
centerTuning = false;
|
tuningMode = tuner::TUNER_MODE_NORMAL;
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["centerTuning"] = centerTuning;
|
core::configManager.conf["centerTuning"] = false;
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
@@ -454,10 +401,10 @@ void drawWindow() {
|
|||||||
else { // TODO: Might need to check if there even is a device
|
else { // TODO: Might need to check if there even is a device
|
||||||
ImGui::PushID(ImGui::GetID("sdrpp_dis_st_btn"));
|
ImGui::PushID(ImGui::GetID("sdrpp_dis_st_btn"));
|
||||||
if (ImGui::ImageButton(icons::NORMAL_TUNING, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) {
|
if (ImGui::ImageButton(icons::NORMAL_TUNING, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) {
|
||||||
centerTuning = true;
|
tuningMode = tuner::TUNER_MODE_CENTER;
|
||||||
setVFO(gui::freqSelect.frequency);
|
tuner::tune(tuner::TUNER_MODE_CENTER, gui::waterfall.selectedVFO, gui::freqSelect.frequency);
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["centerTuning"] = centerTuning;
|
core::configManager.conf["centerTuning"] = true;
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
@@ -465,6 +412,15 @@ void drawWindow() {
|
|||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
int snrWidth = std::min<int>(300, ImGui::GetWindowSize().x - ImGui::GetCursorPosX() - 87);
|
||||||
|
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - (snrWidth+87));
|
||||||
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5);
|
||||||
|
ImGui::SetNextItemWidth(snrWidth);
|
||||||
|
ImGui::SNRMeter((vfo != NULL) ? gui::waterfall.selectedVFOSNR : 0);
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
// Logo button
|
// Logo button
|
||||||
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 48);
|
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 48);
|
||||||
ImGui::SetCursorPosY(10);
|
ImGui::SetCursorPosY(10);
|
||||||
@@ -479,35 +435,38 @@ void drawWindow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle menu resize
|
// Handle menu resize
|
||||||
float curY = ImGui::GetCursorPosY();
|
|
||||||
ImVec2 winSize = ImGui::GetWindowSize();
|
ImVec2 winSize = ImGui::GetWindowSize();
|
||||||
ImVec2 mousePos = ImGui::GetMousePos();
|
ImVec2 mousePos = ImGui::GetMousePos();
|
||||||
bool click = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
if (!lockWaterfallControls) {
|
||||||
bool down = ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
float curY = ImGui::GetCursorPosY();
|
||||||
if (grabbingMenu) {
|
bool click = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||||
newWidth = mousePos.x;
|
bool down = ImGui::IsMouseDown(ImGuiMouseButton_Left);
|
||||||
newWidth = std::clamp<float>(newWidth, 250, winSize.x - 250);
|
if (grabbingMenu) {
|
||||||
ImGui::GetForegroundDrawList()->AddLine(ImVec2(newWidth, curY), ImVec2(newWidth, winSize.y - 10), ImGui::GetColorU32(ImGuiCol_SeparatorActive));
|
newWidth = mousePos.x;
|
||||||
}
|
newWidth = std::clamp<float>(newWidth, 250, winSize.x - 250);
|
||||||
if (mousePos.x >= newWidth - 2 && mousePos.x <= newWidth + 2 && mousePos.y > curY) {
|
ImGui::GetForegroundDrawList()->AddLine(ImVec2(newWidth, curY), ImVec2(newWidth, winSize.y - 10), ImGui::GetColorU32(ImGuiCol_SeparatorActive));
|
||||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
}
|
||||||
if (click) {
|
if (mousePos.x >= newWidth - 2 && mousePos.x <= newWidth + 2 && mousePos.y > curY) {
|
||||||
grabbingMenu = true;
|
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||||
|
if (click) {
|
||||||
|
grabbingMenu = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow);
|
||||||
|
}
|
||||||
|
if(!down && grabbingMenu) {
|
||||||
|
grabbingMenu = false;
|
||||||
|
menuWidth = newWidth;
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["menuWidth"] = menuWidth;
|
||||||
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow);
|
|
||||||
}
|
|
||||||
if(!down && grabbingMenu) {
|
|
||||||
grabbingMenu = false;
|
|
||||||
menuWidth = newWidth;
|
|
||||||
core::configManager.aquire();
|
|
||||||
core::configManager.conf["menuWidth"] = menuWidth;
|
|
||||||
core::configManager.release(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Left Column
|
// Left Column
|
||||||
|
lockWaterfallControls = false;
|
||||||
if (showMenu) {
|
if (showMenu) {
|
||||||
ImGui::Columns(3, "WindowColumns", false);
|
ImGui::Columns(3, "WindowColumns", false);
|
||||||
ImGui::SetColumnWidth(0, menuWidth);
|
ImGui::SetColumnWidth(0, menuWidth);
|
||||||
@@ -516,17 +475,51 @@ void drawWindow() {
|
|||||||
ImGui::BeginChild("Left Column");
|
ImGui::BeginChild("Left Column");
|
||||||
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
|
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
|
||||||
|
|
||||||
gui::menu.draw();
|
if (gui::menu.draw(firstMenuRender)) {
|
||||||
|
core::configManager.acquire();
|
||||||
|
json arr = json::array();
|
||||||
|
for (int i = 0; i < gui::menu.order.size(); i++) {
|
||||||
|
arr[i]["name"] = gui::menu.order[i].name;
|
||||||
|
arr[i]["open"] = gui::menu.order[i].open;
|
||||||
|
}
|
||||||
|
core::configManager.conf["menuElements"] = arr;
|
||||||
|
|
||||||
|
// Update enabled and disabled modules
|
||||||
|
for (auto [_name, inst] : core::moduleManager.instances) {
|
||||||
|
if (!core::configManager.conf["moduleInstances"].contains(_name)) { continue; }
|
||||||
|
core::configManager.conf["moduleInstances"][_name]["enabled"] = inst.instance->isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
if (startedWithMenuClosed) {
|
||||||
|
startedWithMenuClosed = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
firstMenuRender = false;
|
||||||
|
}
|
||||||
|
|
||||||
if(ImGui::CollapsingHeader("Debug")) {
|
if(ImGui::CollapsingHeader("Debug")) {
|
||||||
ImGui::Text("Frame time: %.3f ms/frame", 1000.0 / ImGui::GetIO().Framerate);
|
ImGui::Text("Frame time: %.3f ms/frame", 1000.0 / ImGui::GetIO().Framerate);
|
||||||
ImGui::Text("Framerate: %.1f FPS", ImGui::GetIO().Framerate);
|
ImGui::Text("Framerate: %.1f FPS", ImGui::GetIO().Framerate);
|
||||||
ImGui::Text("Center Frequency: %.0f Hz", gui::waterfall.getCenterFrequency());
|
ImGui::Text("Center Frequency: %.0f Hz", gui::waterfall.getCenterFrequency());
|
||||||
ImGui::Text("Source name: %s", sourceName.c_str());
|
ImGui::Text("Source name: %s", sourceName.c_str());
|
||||||
if (ImGui::Checkbox("Test technique", &dcbias.val)) {
|
|
||||||
//sigpath::signalPath.setDCBiasCorrection(dcbias.val);
|
|
||||||
}
|
|
||||||
ImGui::Checkbox("Show demo window", &demoWindow);
|
ImGui::Checkbox("Show demo window", &demoWindow);
|
||||||
|
ImGui::Text("ImGui version: %s", ImGui::GetVersion());
|
||||||
|
|
||||||
|
ImGui::Checkbox("Bypass buffering", &sigpath::signalPath.inputBuffer.bypass);
|
||||||
|
|
||||||
|
ImGui::Text("Buffering: %d", (sigpath::signalPath.inputBuffer.writeCur - sigpath::signalPath.inputBuffer.readCur + 32) % 32);
|
||||||
|
|
||||||
|
if (ImGui::Button("Test Bug")) {
|
||||||
|
spdlog::error("Will this make the software crash?");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Testing something")) {
|
||||||
|
gui::menu.order[0].open = true;
|
||||||
|
firstMenuRender = true;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,6 +544,48 @@ void drawWindow() {
|
|||||||
|
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
if (!lockWaterfallControls) {
|
||||||
|
// Handle arrow keys
|
||||||
|
if (vfo != NULL && (gui::waterfall.mouseInFFT || gui::waterfall.mouseInWaterfall)) {
|
||||||
|
if (ImGui::IsKeyPressed(GLFW_KEY_LEFT) && !gui::freqSelect.digitHovered) {
|
||||||
|
double nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset - vfo->snapInterval;
|
||||||
|
nfreq = roundl(nfreq / vfo->snapInterval) * vfo->snapInterval;
|
||||||
|
tuner::tune(tuningMode, gui::waterfall.selectedVFO, nfreq);
|
||||||
|
}
|
||||||
|
if (ImGui::IsKeyPressed(GLFW_KEY_RIGHT) && !gui::freqSelect.digitHovered) {
|
||||||
|
double nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset + vfo->snapInterval;
|
||||||
|
nfreq = roundl(nfreq / vfo->snapInterval) * vfo->snapInterval;
|
||||||
|
tuner::tune(tuningMode, gui::waterfall.selectedVFO, nfreq);
|
||||||
|
}
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["frequency"] = gui::waterfall.getCenterFrequency();
|
||||||
|
if (vfo != NULL) {
|
||||||
|
core::configManager.conf["vfoOffsets"][gui::waterfall.selectedVFO] = vfo->generalOffset;
|
||||||
|
}
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle scrollwheel
|
||||||
|
int wheel = ImGui::GetIO().MouseWheel;
|
||||||
|
if (wheel != 0 && (gui::waterfall.mouseInFFT || gui::waterfall.mouseInWaterfall)) {
|
||||||
|
double nfreq;
|
||||||
|
if (vfo != NULL) {
|
||||||
|
nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset + (vfo->snapInterval * wheel);
|
||||||
|
nfreq = roundl(nfreq / vfo->snapInterval) * vfo->snapInterval;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nfreq = gui::waterfall.getCenterFrequency() - (gui::waterfall.getViewBandwidth() * wheel / 20.0);
|
||||||
|
}
|
||||||
|
tuner::tune(tuningMode, gui::waterfall.selectedVFO, nfreq);
|
||||||
|
gui::freqSelect.setFrequency(nfreq);
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["frequency"] = gui::waterfall.getCenterFrequency();
|
||||||
|
if (vfo != NULL) {
|
||||||
|
core::configManager.conf["vfoOffsets"][gui::waterfall.selectedVFO] = vfo->generalOffset;
|
||||||
|
}
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::BeginChild("WaterfallControls");
|
ImGui::BeginChild("WaterfallControls");
|
||||||
@@ -570,9 +605,9 @@ void drawWindow() {
|
|||||||
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Max").x / 2.0));
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Max").x / 2.0));
|
||||||
ImGui::Text("Max");
|
ImGui::Text("Max");
|
||||||
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10);
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10);
|
||||||
if (ImGui::VSliderFloat("##_8_", ImVec2(20.0, 150.0), &fftMax, 0.0, -100.0, "")) {
|
if (ImGui::VSliderFloat("##_8_", ImVec2(20.0, 150.0), &fftMax, 0.0, -160.0f, "")) {
|
||||||
fftMax = std::max<float>(fftMax, fftMin + 10);
|
fftMax = std::max<float>(fftMax, fftMin + 10);
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["max"] = fftMax;
|
core::configManager.conf["max"] = fftMax;
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
@@ -582,9 +617,9 @@ void drawWindow() {
|
|||||||
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Min").x / 2.0));
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Min").x / 2.0));
|
||||||
ImGui::Text("Min");
|
ImGui::Text("Min");
|
||||||
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10);
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10);
|
||||||
if (ImGui::VSliderFloat("##_9_", ImVec2(20.0, 150.0), &fftMin, 0.0, -100.0, "")) {
|
if (ImGui::VSliderFloat("##_9_", ImVec2(20.0, 150.0), &fftMin, 0.0, -160.0f, "")) {
|
||||||
fftMin = std::min<float>(fftMax - 10, fftMin);
|
fftMin = std::min<float>(fftMax - 10, fftMin);
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["min"] = fftMin;
|
core::configManager.conf["min"] = fftMin;
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
@@ -607,6 +642,35 @@ void drawWindow() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setViewBandwidthSlider(float bandwidth) {
|
void MainWindow::setViewBandwidthSlider(float bandwidth) {
|
||||||
bw = bandwidth;
|
bw = bandwidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MainWindow::sdrIsRunning() {
|
||||||
|
return playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setFFTSize(int size) {
|
||||||
|
std::lock_guard<std::mutex> lck(fft_mtx);
|
||||||
|
fftSize = size;
|
||||||
|
|
||||||
|
gui::waterfall.setRawFFTSize(fftSize);
|
||||||
|
sigpath::signalPath.setFFTSize(fftSize);
|
||||||
|
|
||||||
|
fftwf_free(fft_in);
|
||||||
|
fftwf_free(fft_out);
|
||||||
|
fftwf_destroy_plan(fftwPlan);
|
||||||
|
|
||||||
|
fft_in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize);
|
||||||
|
fft_out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize);
|
||||||
|
fftwPlan = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setFFTWindow(int win) {
|
||||||
|
std::lock_guard<std::mutex> lck(fft_mtx);
|
||||||
|
sigpath::signalPath.setFFTWindow(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainWindow::isPlaying() {
|
||||||
|
return playing;
|
||||||
|
}
|
||||||
@@ -1,8 +1,67 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "imgui.h"
|
#include <imgui/imgui.h>
|
||||||
|
#include <fftw3.h>
|
||||||
|
#include <dsp/types.h>
|
||||||
|
#include <dsp/stream.h>
|
||||||
|
#include <signal_path/vfo_manager.h>
|
||||||
|
#include <string>
|
||||||
|
#include <utils/event.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <gui/tuner.h>
|
||||||
|
|
||||||
#define WINDOW_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground
|
#define WINDOW_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground
|
||||||
|
|
||||||
void windowInit();
|
class MainWindow {
|
||||||
void drawWindow();
|
public:
|
||||||
void setViewBandwidthSlider(float bandwidth);
|
void init();
|
||||||
|
void draw();
|
||||||
|
void setViewBandwidthSlider(float bandwidth);
|
||||||
|
bool sdrIsRunning();
|
||||||
|
void setFFTSize(int size);
|
||||||
|
void setFFTWindow(int win);
|
||||||
|
|
||||||
|
// TODO: Replace with it's own class
|
||||||
|
void setVFO(double freq);
|
||||||
|
|
||||||
|
bool isPlaying();
|
||||||
|
|
||||||
|
bool lockWaterfallControls = false;
|
||||||
|
bool playButtonLocked = false;
|
||||||
|
|
||||||
|
Event<bool> onPlayStateChange;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void fftHandler(dsp::complex_t* samples, int count, void* ctx);
|
||||||
|
static void vfoAddedHandler(VFOManager::VFO* vfo, void* ctx);
|
||||||
|
|
||||||
|
// FFT Variables
|
||||||
|
int fftSize = 8192 * 8;
|
||||||
|
std::mutex fft_mtx;
|
||||||
|
fftwf_complex *fft_in, *fft_out;
|
||||||
|
fftwf_plan fftwPlan;
|
||||||
|
|
||||||
|
// GUI Variables
|
||||||
|
bool firstMenuRender = true;
|
||||||
|
bool startedWithMenuClosed = false;
|
||||||
|
float fftMin = -70.0;
|
||||||
|
float fftMax = 0.0;
|
||||||
|
float bw = 8000000;
|
||||||
|
bool playing = false;
|
||||||
|
bool showCredits = false;
|
||||||
|
std::string audioStreamName = "";
|
||||||
|
std::string sourceName = "";
|
||||||
|
int menuWidth = 300;
|
||||||
|
bool grabbingMenu = false;
|
||||||
|
int newWidth = 300;
|
||||||
|
int fftHeight = 300;
|
||||||
|
bool showMenu = true;
|
||||||
|
int tuningMode = tuner::TUNER_MODE_NORMAL;
|
||||||
|
dsp::stream<dsp::complex_t> dummyStream;
|
||||||
|
bool demoWindow = false;
|
||||||
|
int selectedWindow = 0;
|
||||||
|
|
||||||
|
bool initComplete = false;
|
||||||
|
|
||||||
|
EventHandler<VFOManager::VFO*> vfoCreatedHandler;
|
||||||
|
|
||||||
|
};
|
||||||
@@ -6,6 +6,9 @@
|
|||||||
namespace bandplanmenu {
|
namespace bandplanmenu {
|
||||||
int bandplanId;
|
int bandplanId;
|
||||||
bool bandPlanEnabled;
|
bool bandPlanEnabled;
|
||||||
|
int bandPlanPos = 0;
|
||||||
|
|
||||||
|
const char* bandPlanPosTxt = "Bottom\0Top\0";
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
// todo: check if the bandplan wasn't removed
|
// todo: check if the bandplan wasn't removed
|
||||||
@@ -26,21 +29,34 @@ namespace bandplanmenu {
|
|||||||
|
|
||||||
bandPlanEnabled = core::configManager.conf["bandPlanEnabled"];
|
bandPlanEnabled = core::configManager.conf["bandPlanEnabled"];
|
||||||
bandPlanEnabled ? gui::waterfall.showBandplan() : gui::waterfall.hideBandplan();
|
bandPlanEnabled ? gui::waterfall.showBandplan() : gui::waterfall.hideBandplan();
|
||||||
|
bandPlanPos = core::configManager.conf["bandPlanPos"];
|
||||||
|
gui::waterfall.setBandPlanPos(bandPlanPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(void* ctx) {
|
void draw(void* ctx) {
|
||||||
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
|
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
|
||||||
ImGui::PushItemWidth(menuColumnWidth);
|
ImGui::PushItemWidth(menuColumnWidth);
|
||||||
if (ImGui::Combo("##_4_", &bandplanId, bandplan::bandplanNameTxt.c_str())) {
|
if (ImGui::Combo("##_bandplan_name_", &bandplanId, bandplan::bandplanNameTxt.c_str())) {
|
||||||
gui::waterfall.bandplan = &bandplan::bandplans[bandplan::bandplanNames[bandplanId]];
|
gui::waterfall.bandplan = &bandplan::bandplans[bandplan::bandplanNames[bandplanId]];
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["bandPlan"] = bandplan::bandplanNames[bandplanId];
|
core::configManager.conf["bandPlan"] = bandplan::bandplanNames[bandplanId];
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
|
|
||||||
|
ImGui::Text("Position");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuColumnWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::Combo("##_bandplan_pos_", &bandPlanPos, bandPlanPosTxt)) {
|
||||||
|
gui::waterfall.setBandPlanPos(bandPlanPos);
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["bandPlanPos"] = bandPlanPos;
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui::Checkbox("Enabled", &bandPlanEnabled)) {
|
if (ImGui::Checkbox("Enabled", &bandPlanEnabled)) {
|
||||||
bandPlanEnabled ? gui::waterfall.showBandplan() : gui::waterfall.hideBandplan();
|
bandPlanEnabled ? gui::waterfall.showBandplan() : gui::waterfall.hideBandplan();
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["bandPlanEnabled"] = bandPlanEnabled;
|
core::configManager.conf["bandPlanEnabled"] = bandPlanEnabled;
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,155 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <core.h>
|
#include <core.h>
|
||||||
|
#include <gui/colormaps.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/main_window.h>
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
|
||||||
namespace displaymenu {
|
namespace displaymenu {
|
||||||
bool showWaterfall;
|
bool showWaterfall;
|
||||||
|
bool fastFFT = true;
|
||||||
|
bool fullWaterfallUpdate = true;
|
||||||
|
int colorMapId = 0;
|
||||||
|
std::vector<std::string> colorMapNames;
|
||||||
|
std::string colorMapNamesTxt = "";
|
||||||
|
std::string colorMapAuthor = "";
|
||||||
|
int selectedWindow = 0;
|
||||||
|
int fftRate = 20;
|
||||||
|
|
||||||
|
const int FFTSizes[] = {
|
||||||
|
65536,
|
||||||
|
32768,
|
||||||
|
16384,
|
||||||
|
8192,
|
||||||
|
4096,
|
||||||
|
2048,
|
||||||
|
1024
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* FFTSizesStr = "65536\0"
|
||||||
|
"32768\0"
|
||||||
|
"16384\0"
|
||||||
|
"8192\0"
|
||||||
|
"4096\0"
|
||||||
|
"2048\0"
|
||||||
|
"1024\0";
|
||||||
|
|
||||||
|
int fftSizeId = 0;
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
showWaterfall = core::configManager.conf["showWaterfall"];
|
showWaterfall = core::configManager.conf["showWaterfall"];
|
||||||
showWaterfall ? gui::waterfall.showWaterfall() : gui::waterfall.hideWaterfall();
|
showWaterfall ? gui::waterfall.showWaterfall() : gui::waterfall.hideWaterfall();
|
||||||
|
std::string colormapName = core::configManager.conf["colorMap"];
|
||||||
|
if (colormaps::maps.find(colormapName) != colormaps::maps.end()) {
|
||||||
|
colormaps::Map map = colormaps::maps[colormapName];
|
||||||
|
gui::waterfall.updatePalletteFromArray(map.map, map.entryCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& [name, map] : colormaps::maps) {
|
||||||
|
colorMapNames.push_back(name);
|
||||||
|
colorMapNamesTxt += name;
|
||||||
|
colorMapNamesTxt += '\0';
|
||||||
|
if (name == colormapName) {
|
||||||
|
colorMapId = (colorMapNames.size() - 1);
|
||||||
|
colorMapAuthor = map.author;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fastFFT = core::configManager.conf["fastFFT"];
|
||||||
|
gui::waterfall.setFastFFT(fastFFT);
|
||||||
|
|
||||||
|
fullWaterfallUpdate = core::configManager.conf["fullWaterfallUpdate"];
|
||||||
|
gui::waterfall.setFullWaterfallUpdate(fullWaterfallUpdate);
|
||||||
|
|
||||||
|
fftSizeId = 0;
|
||||||
|
int fftSize = core::configManager.conf["fftSize"];
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
if (fftSize == FFTSizes[i]) {
|
||||||
|
fftSizeId = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gui::mainWindow.setFFTSize(FFTSizes[fftSizeId]);
|
||||||
|
|
||||||
|
fftRate = core::configManager.conf["fftRate"];
|
||||||
|
sigpath::signalPath.setFFTRate(fftRate);
|
||||||
|
|
||||||
|
selectedWindow = std::clamp<int>((int)core::configManager.conf["fftWindow"], 0, _FFT_WINDOW_COUNT-1);
|
||||||
|
gui::mainWindow.setFFTWindow(selectedWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(void* ctx) {
|
void draw(void* ctx) {
|
||||||
if (ImGui::Checkbox("Show Waterfall", &showWaterfall)) {
|
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||||
|
bool homePressed = ImGui::IsKeyPressed(GLFW_KEY_HOME, false);
|
||||||
|
if (ImGui::Checkbox("Show Waterfall##_sdrpp", &showWaterfall) || homePressed) {
|
||||||
|
if (homePressed) { showWaterfall = !showWaterfall; }
|
||||||
showWaterfall ? gui::waterfall.showWaterfall() : gui::waterfall.hideWaterfall();
|
showWaterfall ? gui::waterfall.showWaterfall() : gui::waterfall.hideWaterfall();
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["showWaterfall"] = showWaterfall;
|
core::configManager.conf["showWaterfall"] = showWaterfall;
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::Checkbox("Fast FFT##_sdrpp", &fastFFT)) {
|
||||||
|
gui::waterfall.setFastFFT(fastFFT);
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["fastFFT"] = fastFFT;
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Checkbox("Full Waterfall Update##_sdrpp", &fullWaterfallUpdate)) {
|
||||||
|
gui::waterfall.setFullWaterfallUpdate(fullWaterfallUpdate);
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["fullWaterfallUpdate"] = fullWaterfallUpdate;
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("FFT Framerate");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::InputInt("##sdrpp_fft_rate", &fftRate, 1, 10)) {
|
||||||
|
fftRate = std::max<int>(1, fftRate);
|
||||||
|
sigpath::signalPath.setFFTRate(fftRate);
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["fftRate"] = fftRate;
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("FFT Size");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::Combo("##sdrpp_fft_size", &fftSizeId, FFTSizesStr)) {
|
||||||
|
gui::mainWindow.setFFTSize(FFTSizes[fftSizeId]);
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["fftSize"] = FFTSizes[fftSizeId];
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("FFT Window");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::Combo("##sdrpp_fft_window", &selectedWindow, "Rectangular\0Blackman\0")) {
|
||||||
|
gui::mainWindow.setFFTWindow(selectedWindow);
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["fftWindow"] = selectedWindow;
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorMapNames.size() > 0) {
|
||||||
|
ImGui::Text("Color Map");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::Combo("##_sdrpp_color_map_sel", &colorMapId, colorMapNamesTxt.c_str())) {
|
||||||
|
colormaps::Map map = colormaps::maps[colorMapNames[colorMapId]];
|
||||||
|
gui::waterfall.updatePalletteFromArray(map.map, map.entryCount);
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["colorMap"] = colorMapNames[colorMapId];
|
||||||
|
core::configManager.release(true);
|
||||||
|
colorMapAuthor = map.author;
|
||||||
|
}
|
||||||
|
ImGui::Text("Color map Author: %s", colorMapAuthor.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
104
core/src/gui/menus/module_manager.cpp
Normal file
104
core/src/gui/menus/module_manager.cpp
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#include <gui/menus/module_manager.h>
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <core.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <gui/style.h>
|
||||||
|
|
||||||
|
namespace module_manager_menu {
|
||||||
|
char modName[1024];
|
||||||
|
std::vector<std::string> modTypes;
|
||||||
|
std::vector<std::string> toBeRemoved;
|
||||||
|
std::string modTypesTxt;
|
||||||
|
int modTypeId;
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
modName[0] = 0;
|
||||||
|
|
||||||
|
modTypes.clear();
|
||||||
|
modTypesTxt = "";
|
||||||
|
for (auto& [name, mod] : core::moduleManager.modules) {
|
||||||
|
modTypes.push_back(name);
|
||||||
|
modTypesTxt += name;
|
||||||
|
modTypesTxt += '\0';
|
||||||
|
}
|
||||||
|
modTypeId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(void* ctx) {
|
||||||
|
bool modified = false;
|
||||||
|
if (ImGui::BeginTable("Module Manager Table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, ImVec2(0, 200))) {
|
||||||
|
ImGui::TableSetupColumn("Name");
|
||||||
|
ImGui::TableSetupColumn("Type");
|
||||||
|
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 10);
|
||||||
|
ImGui::TableSetupScrollFreeze(3, 1);
|
||||||
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
|
float height = ImGui::CalcTextSize("-").y;
|
||||||
|
|
||||||
|
toBeRemoved.clear();
|
||||||
|
|
||||||
|
for (auto& [name, inst] : core::moduleManager.instances) {
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
ImGui::Text(name.c_str());
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(1);
|
||||||
|
ImGui::Text(inst.module.info->name);
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(2);
|
||||||
|
ImVec2 origPos = ImGui::GetCursorPos();
|
||||||
|
ImGui::SetCursorPos(ImVec2(origPos.x - 3, origPos.y));
|
||||||
|
if (ImGui::Button(("##module_mgr_"+name).c_str(), ImVec2(height,height))) {
|
||||||
|
toBeRemoved.push_back(name);
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
ImGui::SetCursorPos(ImVec2(origPos.x + 2, origPos.y - 5));
|
||||||
|
ImGui::Text("_");
|
||||||
|
}
|
||||||
|
ImGui::EndTable();
|
||||||
|
|
||||||
|
for (auto& rem : toBeRemoved) {
|
||||||
|
core::moduleManager.deleteInstance(rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Add module row with slightly different settings
|
||||||
|
ImGui::BeginTable("Module Manager Add Table", 3);
|
||||||
|
ImGui::TableSetupColumn("Name");
|
||||||
|
ImGui::TableSetupColumn("Type");
|
||||||
|
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 16);
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
|
||||||
|
ImGui::InputText("##module_mod_name", modName, 1000);
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(1);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
|
||||||
|
ImGui::Combo("##module_mgr_type", &modTypeId, modTypesTxt.c_str());
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(2);
|
||||||
|
if (strlen(modName) == 0) { style::beginDisabled(); }
|
||||||
|
if (ImGui::Button("+##module_mgr_add_btn", ImVec2(16,0))) {
|
||||||
|
core::moduleManager.createInstance(modName, modTypes[modTypeId]);
|
||||||
|
core::moduleManager.postInit(modName);
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
if (strlen(modName) == 0) { style::endDisabled(); }
|
||||||
|
ImGui::EndTable();
|
||||||
|
|
||||||
|
if (modified) {
|
||||||
|
// Update enabled and disabled modules
|
||||||
|
core::configManager.acquire();
|
||||||
|
json instances;
|
||||||
|
for (auto [_name, inst] : core::moduleManager.instances) {
|
||||||
|
instances[_name]["module"] = inst.module.info->name;
|
||||||
|
instances[_name]["enabled"] = inst.instance->isEnabled();
|
||||||
|
}
|
||||||
|
core::configManager.conf["moduleInstances"] = instances;
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
core/src/gui/menus/module_manager.h
Normal file
6
core/src/gui/menus/module_manager.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace module_manager_menu {
|
||||||
|
void init();
|
||||||
|
void draw(void* ctx);
|
||||||
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#include <gui/menus/scripting.h>
|
|
||||||
#include <core.h>
|
|
||||||
#include <gui/style.h>
|
|
||||||
#include <imgui/imgui.h>
|
|
||||||
|
|
||||||
namespace scriptingmenu {
|
|
||||||
void init() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(void* ctx) {
|
|
||||||
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
|
||||||
for (const auto& [name, script] : core::scriptManager.scripts) {
|
|
||||||
bool running = script->running;
|
|
||||||
if (running) { style::beginDisabled(); }
|
|
||||||
if (ImGui::Button(name.c_str(), ImVec2(menuWidth, 0))) {
|
|
||||||
script->run();
|
|
||||||
}
|
|
||||||
if (running) { style::endDisabled(); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
namespace sinkmenu {
|
namespace sinkmenu {
|
||||||
void init() {
|
void init() {
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
sigpath::sinkManager.loadSinksFromConfig();
|
sigpath::sinkManager.loadSinksFromConfig();
|
||||||
core::configManager.release();
|
core::configManager.release();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,53 +2,209 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <core.h>
|
#include <core.h>
|
||||||
|
#include <gui/main_window.h>
|
||||||
|
#include <gui/style.h>
|
||||||
#include <signal_path/signal_path.h>
|
#include <signal_path/signal_path.h>
|
||||||
|
|
||||||
namespace sourecmenu {
|
namespace sourecmenu {
|
||||||
|
int offsetMode = 0;
|
||||||
int sourceId = 0;
|
int sourceId = 0;
|
||||||
double freqOffset = 0.0;
|
double customOffset = 0.0;
|
||||||
|
double effectiveOffset = 0.0;
|
||||||
|
int decimationPower = 0;
|
||||||
|
bool iqCorrection = false;
|
||||||
|
|
||||||
|
EventHandler<std::string> sourceRegisteredHandler;
|
||||||
|
EventHandler<std::string> sourceUnregisterHandler;
|
||||||
|
EventHandler<std::string> sourceUnregisteredHandler;
|
||||||
|
|
||||||
|
std::vector<std::string> sourceNames;
|
||||||
|
std::string sourceNamesTxt;
|
||||||
|
std::string selectedSource;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
OFFSET_MODE_NONE,
|
||||||
|
OFFSET_MODE_CUSTOM,
|
||||||
|
OFFSET_MODE_SPYVERTER,
|
||||||
|
OFFSET_MODE_HAM_IT_UP,
|
||||||
|
OFFSET_MODE_DK5AV_XB,
|
||||||
|
OFFSET_MODE_KU_LNB_9750,
|
||||||
|
OFFSET_MODE_KU_LNB_10700,
|
||||||
|
_OFFSET_MODE_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* offsetModesTxt = "None\0"
|
||||||
|
"Custom\0"
|
||||||
|
"SpyVerter\0"
|
||||||
|
"Ham-It-Up\0"
|
||||||
|
"DK5AV X-Band\0"
|
||||||
|
"Ku LNB (9750MHz)\0"
|
||||||
|
"Ku LNB (10700MHz)\0";
|
||||||
|
|
||||||
|
const char* decimationStages = "None\0"
|
||||||
|
"2\0"
|
||||||
|
"4\0"
|
||||||
|
"8\0"
|
||||||
|
"16\0"
|
||||||
|
"32\0"
|
||||||
|
"64\0";
|
||||||
|
|
||||||
|
void updateOffset() {
|
||||||
|
if (offsetMode == OFFSET_MODE_CUSTOM) { effectiveOffset = customOffset; }
|
||||||
|
else if (offsetMode == OFFSET_MODE_SPYVERTER) { effectiveOffset = 120000000; } // 120MHz Up-conversion
|
||||||
|
else if (offsetMode == OFFSET_MODE_HAM_IT_UP) { effectiveOffset = 125000000; } // 125MHz Up-conversion
|
||||||
|
else if (offsetMode == OFFSET_MODE_DK5AV_XB) { effectiveOffset = -6800000000; } // 6.8GHz Down-conversion
|
||||||
|
else if (offsetMode == OFFSET_MODE_KU_LNB_9750) { effectiveOffset = -9750000000; } // 9.750GHz Down-conversion
|
||||||
|
else if (offsetMode == OFFSET_MODE_KU_LNB_10700) { effectiveOffset = -10700000000; } // 10.7GHz Down-conversion
|
||||||
|
else { effectiveOffset = 0; }
|
||||||
|
sigpath::sourceManager.setTuningOffset(effectiveOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void refreshSources() {
|
||||||
|
sourceNames = sigpath::sourceManager.getSourceNames();
|
||||||
|
sourceNamesTxt.clear();
|
||||||
|
for (auto name : sourceNames) {
|
||||||
|
sourceNamesTxt += name;
|
||||||
|
sourceNamesTxt += '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectSource(std::string name) {
|
||||||
|
if (sourceNames.empty()) {
|
||||||
|
selectedSource.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto it = std::find(sourceNames.begin(), sourceNames.end(), name);
|
||||||
|
if (it == sourceNames.end()) {
|
||||||
|
selectSource(sourceNames[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sourceId = std::distance(sourceNames.begin(), it);
|
||||||
|
selectedSource = sourceNames[sourceId];
|
||||||
|
sigpath::sourceManager.selectSource(sourceNames[sourceId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onSourceRegistered(std::string name, void* ctx) {
|
||||||
|
refreshSources();
|
||||||
|
|
||||||
|
if (selectedSource.empty()) {
|
||||||
|
sourceId = 0;
|
||||||
|
selectSource(sourceNames[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
void onSourceUnregister(std::string name, void* ctx) {
|
||||||
|
if (name != selectedSource) { return; }
|
||||||
|
|
||||||
|
// TODO: Stop everything
|
||||||
|
}
|
||||||
|
|
||||||
|
void onSourceUnregistered(std::string name, void* ctx) {
|
||||||
|
refreshSources();
|
||||||
|
|
||||||
|
if (sourceNames.empty()) {
|
||||||
|
selectedSource = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name == selectedSource) {
|
||||||
|
sourceId = std::clamp<int>(sourceId, 0, sourceNames.size() - 1);
|
||||||
|
selectSource(sourceNames[sourceId]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
|
||||||
|
}
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
std::string name = core::configManager.conf["source"];
|
std::string selected = core::configManager.conf["source"];
|
||||||
auto it = std::find(sigpath::sourceManager.sourceNames.begin(), sigpath::sourceManager.sourceNames.end(), name);
|
customOffset = core::configManager.conf["offset"];
|
||||||
if (it != sigpath::sourceManager.sourceNames.end()) {
|
offsetMode = core::configManager.conf["offsetMode"];
|
||||||
sigpath::sourceManager.selectSource(name);
|
decimationPower = core::configManager.conf["decimationPower"];
|
||||||
sourceId = std::distance(sigpath::sourceManager.sourceNames.begin(), it);
|
iqCorrection = core::configManager.conf["iqCorrection"];
|
||||||
}
|
sigpath::signalPath.setIQCorrection(iqCorrection);
|
||||||
else if (sigpath::sourceManager.sourceNames.size() > 0) {
|
updateOffset();
|
||||||
sigpath::sourceManager.selectSource(sigpath::sourceManager.sourceNames[0]);
|
|
||||||
}
|
refreshSources();
|
||||||
else {
|
selectSource(selected);
|
||||||
spdlog::warn("No source available...");
|
sigpath::signalPath.setDecimation(decimationPower);
|
||||||
}
|
|
||||||
sigpath::sourceManager.setTuningOffset(core::configManager.conf["offset"]);
|
sourceRegisteredHandler.handler = onSourceRegistered;
|
||||||
|
sourceUnregisterHandler.handler = onSourceUnregister;
|
||||||
|
sourceUnregisteredHandler.handler = onSourceUnregistered;
|
||||||
|
sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler);
|
||||||
|
sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler);
|
||||||
|
sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler);
|
||||||
|
|
||||||
core::configManager.release();
|
core::configManager.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(void* ctx) {
|
void draw(void* ctx) {
|
||||||
std::string items = "";
|
|
||||||
for (std::string name : sigpath::sourceManager.sourceNames) {
|
|
||||||
items += name;
|
|
||||||
items += '\0';
|
|
||||||
}
|
|
||||||
float itemWidth = ImGui::GetContentRegionAvailWidth();
|
float itemWidth = ImGui::GetContentRegionAvailWidth();
|
||||||
|
bool running = gui::mainWindow.sdrIsRunning();
|
||||||
|
|
||||||
|
if (running) { style::beginDisabled(); }
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(itemWidth);
|
ImGui::SetNextItemWidth(itemWidth);
|
||||||
if (ImGui::Combo("##source", &sourceId, items.c_str())) {
|
if (ImGui::Combo("##source", &sourceId, sourceNamesTxt.c_str())) {
|
||||||
sigpath::sourceManager.selectSource(sigpath::sourceManager.sourceNames[sourceId]);
|
selectSource(sourceNames[sourceId]);
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["source"] = sigpath::sourceManager.sourceNames[sourceId];
|
core::configManager.conf["source"] = sourceNames[sourceId];
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (running) { style::endDisabled(); }
|
||||||
|
|
||||||
sigpath::sourceManager.showSelectedMenu();
|
sigpath::sourceManager.showSelectedMenu();
|
||||||
ImGui::SetNextItemWidth(itemWidth - ImGui::CalcTextSize("Offset (Hz)").x - 10);
|
|
||||||
if (ImGui::InputDouble("Offset (Hz)##freq_offset", &freqOffset, 1.0, 100.0)) {
|
if (ImGui::Checkbox("IQ Correction##_sdrpp_iq_corr", &iqCorrection)) {
|
||||||
sigpath::sourceManager.setTuningOffset(freqOffset);
|
sigpath::signalPath.setIQCorrection(iqCorrection);
|
||||||
core::configManager.aquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["offset"] = freqOffset;
|
core::configManager.conf["iqCorrection"] = iqCorrection;
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Offset mode");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::Combo("##_sdrpp_offset_mode", &offsetMode, offsetModesTxt)) {
|
||||||
|
updateOffset();
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["offsetMode"] = offsetMode;
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Offset");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
|
||||||
|
if (offsetMode == OFFSET_MODE_CUSTOM) {
|
||||||
|
if (ImGui::InputDouble("##freq_offset", &customOffset, 1.0, 100.0)) {
|
||||||
|
updateOffset();
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["offset"] = customOffset;
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
style::beginDisabled();
|
||||||
|
ImGui::InputDouble("##freq_offset", &effectiveOffset, 1.0, 100.0);
|
||||||
|
style::endDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (running) { style::beginDisabled(); }
|
||||||
|
ImGui::Text("Decimation");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::Combo("##source_decim", &decimationPower, decimationStages)) {
|
||||||
|
sigpath::signalPath.setDecimation(decimationPower);
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["decimationPower"] = decimationPower;
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
if (running) { style::endDisabled(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
47
core/src/gui/menus/theme.cpp
Normal file
47
core/src/gui/menus/theme.cpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#include <gui/menus/theme.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <options.h>
|
||||||
|
#include <core.h>
|
||||||
|
|
||||||
|
namespace thememenu {
|
||||||
|
int themeId;
|
||||||
|
std::vector<std::string> themeNames;
|
||||||
|
std::string themeNamesTxt;
|
||||||
|
|
||||||
|
void init(std::string resDir) {
|
||||||
|
// TODO: Not hardcode theme directory
|
||||||
|
gui::themeManager.loadThemesFromDir(resDir + "/themes/");
|
||||||
|
core::configManager.acquire();
|
||||||
|
std::string selectedThemeName = core::configManager.conf["theme"];
|
||||||
|
core::configManager.release();
|
||||||
|
|
||||||
|
// Select theme by name, if not available, apply Dark theme
|
||||||
|
themeNames = gui::themeManager.getThemeNames();
|
||||||
|
auto it = std::find(themeNames.begin(), themeNames.end(), selectedThemeName);
|
||||||
|
if (it == themeNames.end()) {
|
||||||
|
it = std::find(themeNames.begin(), themeNames.end(), "Dark");
|
||||||
|
selectedThemeName = "Dark";
|
||||||
|
}
|
||||||
|
gui::themeManager.applyTheme(selectedThemeName);
|
||||||
|
themeId = std::distance(themeNames.begin(), it);
|
||||||
|
|
||||||
|
themeNamesTxt = "";
|
||||||
|
for (auto name : themeNames) {
|
||||||
|
themeNamesTxt += name;
|
||||||
|
themeNamesTxt += '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(void* ctx) {
|
||||||
|
float menuWidth = ImGui::GetContentRegionAvailWidth();
|
||||||
|
ImGui::Text("Theme");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::Combo("##theme_select_combo", &themeId, themeNamesTxt.c_str())) {
|
||||||
|
gui::themeManager.applyTheme(themeNames[themeId]);
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["theme"] = themeNames[themeId];
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
core/src/gui/menus/theme.h
Normal file
7
core/src/gui/menus/theme.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace thememenu {
|
||||||
|
void init(std::string resDir);
|
||||||
|
void draw(void* ctx);
|
||||||
|
}
|
||||||
137
core/src/gui/menus/vfo_color.cpp
Normal file
137
core/src/gui/menus/vfo_color.cpp
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
#include <gui/menus/vfo_color.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/widgets/waterfall.h>
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <string>
|
||||||
|
#include <core.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace vfo_color_menu {
|
||||||
|
std::map<std::string, ImVec4> vfoColors;
|
||||||
|
std::string openName = "";
|
||||||
|
EventHandler<VFOManager::VFO*> vfoAddHndl;
|
||||||
|
|
||||||
|
void vfoAddHandler(VFOManager::VFO* vfo, void* ctx) {
|
||||||
|
std::string name = vfo->getName();
|
||||||
|
if (vfoColors.find(name) != vfoColors.end()) {
|
||||||
|
ImVec4 col = vfoColors[name];
|
||||||
|
vfo->setColor(IM_COL32((int)roundf(col.x * 255), (int)roundf(col.y * 255), (int)roundf(col.z * 255), 50));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vfo->setColor(IM_COL32(255, 255, 255, 50));
|
||||||
|
vfoColors[name] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
// Load colors from config
|
||||||
|
bool modified = false;
|
||||||
|
core::configManager.acquire();
|
||||||
|
json conf = core::configManager.conf["vfoColors"];
|
||||||
|
for (auto& [name, val] : conf.items()) {
|
||||||
|
// If not a string, repair with default
|
||||||
|
if (!val.is_string()) {
|
||||||
|
core::configManager.conf["vfoColors"][name] = "#FFFFFF";
|
||||||
|
vfoColors[name] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
modified = true;
|
||||||
|
if (sigpath::vfoManager.vfoExists(name)) {
|
||||||
|
sigpath::vfoManager.setColor(name, IM_COL32(255, 255, 255, 50));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not a valid hex color, repair with default
|
||||||
|
std::string col = val;
|
||||||
|
if (col[0] != '#' || !std::all_of(col.begin() + 1, col.end(), ::isxdigit)) {
|
||||||
|
core::configManager.conf["vfoColors"][name] = "#FFFFFF";
|
||||||
|
vfoColors[name] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
modified = true;
|
||||||
|
if (sigpath::vfoManager.vfoExists(name)) {
|
||||||
|
sigpath::vfoManager.setColor(name, IM_COL32(255, 255, 255, 50));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the color is valid, decode it and set the vfo's color
|
||||||
|
float r, g, b;
|
||||||
|
r = std::stoi(col.substr(1, 2), NULL, 16);
|
||||||
|
g = std::stoi(col.substr(3, 2), NULL, 16);
|
||||||
|
b = std::stoi(col.substr(5, 2), NULL, 16);
|
||||||
|
vfoColors[name] = ImVec4(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f);
|
||||||
|
if (sigpath::vfoManager.vfoExists(name)) {
|
||||||
|
sigpath::vfoManager.setColor(name, IM_COL32((int)roundf(r), (int)roundf(g), (int)roundf(b), 50));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate existing VFOs and set their color if in the config, if not set to default
|
||||||
|
for (auto& [name, vfo] : gui::waterfall.vfos) {
|
||||||
|
if (vfoColors.find(name) == vfoColors.end()) {
|
||||||
|
vfoColors[name] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
vfo->color = IM_COL32(255, 255, 255, 50);
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vfoAddHndl.handler = vfoAddHandler;
|
||||||
|
sigpath::vfoManager.onVfoCreated.bindHandler(&vfoAddHndl);
|
||||||
|
core::configManager.release(modified);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(void* ctx) {
|
||||||
|
ImGui::BeginTable("VFO Color Buttons Table", 2);
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
if (ImGui::Button("Auto Color##vfo_color", ImVec2(ImGui::GetContentRegionAvailWidth(), 0))) {
|
||||||
|
float delta = 1.0f / (float)gui::waterfall.vfos.size();
|
||||||
|
float hue = 0;
|
||||||
|
for (auto& [name, vfo] : gui::waterfall.vfos) {
|
||||||
|
float r, g, b;
|
||||||
|
ImGui::ColorConvertHSVtoRGB(hue, 0.5f, 1.0f, r, g, b);
|
||||||
|
vfoColors[name] = ImVec4(r, g, b, 1.0f);
|
||||||
|
vfo->color = IM_COL32((int)roundf(r * 255), (int)roundf(g * 255), (int)roundf(b * 255), 50);
|
||||||
|
hue += delta;
|
||||||
|
core::configManager.acquire();
|
||||||
|
char buf[16];
|
||||||
|
sprintf(buf, "#%02X%02X%02X", (int)roundf(r * 255), (int)roundf(g * 255), (int)roundf(b * 255));
|
||||||
|
core::configManager.conf["vfoColors"][name] = buf;
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TableSetColumnIndex(1);
|
||||||
|
if (ImGui::Button("Clear All##vfo_color", ImVec2(ImGui::GetContentRegionAvailWidth(), 0))) {
|
||||||
|
for (auto& [name, vfo] : gui::waterfall.vfos) {
|
||||||
|
vfoColors[name] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
vfo->color = IM_COL32(255, 255, 255, 50);
|
||||||
|
core::configManager.acquire();
|
||||||
|
char buf[16];
|
||||||
|
core::configManager.conf["vfoColors"][name] = "#FFFFFF";
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
|
||||||
|
ImGui::BeginTable("VFO Color table", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders);
|
||||||
|
for (auto& [name, vfo] : gui::waterfall.vfos) {
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
ImVec4 col(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
if (vfoColors.find(name) != vfoColors.end()) {
|
||||||
|
col = vfoColors[name];
|
||||||
|
}
|
||||||
|
if (ImGui::ColorEdit3(("##vfo_color_"+name).c_str(), (float*)&col, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) {
|
||||||
|
vfoColors[name] = col;
|
||||||
|
vfo->color = IM_COL32((int)roundf(col.x * 255), (int)roundf(col.y * 255), (int)roundf(col.z * 255), 50);
|
||||||
|
core::configManager.acquire();
|
||||||
|
char buf[16];
|
||||||
|
sprintf(buf, "#%02X%02X%02X", (int)roundf(col.x * 255), (int)roundf(col.y * 255), (int)roundf(col.z * 255));
|
||||||
|
core::configManager.conf["vfoColors"][name] = buf;
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text(name.c_str());
|
||||||
|
}
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace scriptingmenu {
|
namespace vfo_color_menu {
|
||||||
void init();
|
void init();
|
||||||
void draw(void* ctx);
|
void draw(void* ctx);
|
||||||
}
|
}
|
||||||
@@ -3,99 +3,40 @@
|
|||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <options.h>
|
#include <options.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
namespace style {
|
namespace style {
|
||||||
ImFont* baseFont;
|
ImFont* baseFont;
|
||||||
ImFont* bigFont;
|
ImFont* bigFont;
|
||||||
ImFont* hugeFont;
|
ImFont* hugeFont;
|
||||||
|
|
||||||
void setDefaultStyle() {
|
bool loadFonts(std::string resDir) {
|
||||||
ImGui::GetStyle().WindowRounding = 0.0f;
|
if (!std::filesystem::is_directory(resDir)) {
|
||||||
ImGui::GetStyle().ChildRounding = 0.0f;
|
spdlog::error("Inavlid resource directory: {0}", resDir);
|
||||||
ImGui::GetStyle().FrameRounding = 0.0f;
|
return false;
|
||||||
ImGui::GetStyle().GrabRounding = 0.0f;
|
}
|
||||||
ImGui::GetStyle().PopupRounding = 0.0f;
|
|
||||||
ImGui::GetStyle().ScrollbarRounding = 0.0f;
|
|
||||||
|
|
||||||
baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 16.0f);
|
baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 16.0f);
|
||||||
bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 42.0f);
|
bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 45.0f);
|
||||||
hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 128.0f);
|
hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 128.0f);
|
||||||
|
|
||||||
ImGui::StyleColorsDark();
|
return true;
|
||||||
//ImGui::StyleColorsLight();
|
|
||||||
}
|
|
||||||
|
|
||||||
void testtt() {
|
|
||||||
ImGui::StyleColorsLight();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDarkStyle() {
|
|
||||||
ImGui::GetStyle().WindowRounding = 0.0f;
|
|
||||||
ImGui::GetStyle().ChildRounding = 0.0f;
|
|
||||||
ImGui::GetStyle().FrameRounding = 0.0f;
|
|
||||||
ImGui::GetStyle().GrabRounding = 0.0f;
|
|
||||||
ImGui::GetStyle().PopupRounding = 0.0f;
|
|
||||||
ImGui::GetStyle().ScrollbarRounding = 0.0f;
|
|
||||||
|
|
||||||
baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 16.0f);
|
|
||||||
bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 42.0f);
|
|
||||||
hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(options::opts.root + "/res/fonts/Roboto-Medium.ttf")).c_str(), 128.0f);
|
|
||||||
|
|
||||||
ImGui::StyleColorsDark();
|
|
||||||
|
|
||||||
auto& style = ImGui::GetStyle();
|
|
||||||
|
|
||||||
ImVec4* colors = style.Colors;
|
|
||||||
|
|
||||||
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
|
||||||
colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
|
|
||||||
colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.94f);
|
|
||||||
colors[ImGuiCol_ChildBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.00f);
|
|
||||||
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
|
|
||||||
colors[ImGuiCol_Border] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
|
|
||||||
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
|
||||||
colors[ImGuiCol_FrameBg] = ImVec4(0.20f, 0.21f, 0.22f, 0.54f);
|
|
||||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.20f, 0.21f, 0.22f, 0.54f);
|
|
||||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.20f, 0.21f, 0.22f, 0.54f);
|
|
||||||
colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
|
|
||||||
colors[ImGuiCol_TitleBgActive] = ImVec4(0.29f, 0.29f, 0.29f, 1.00f);
|
|
||||||
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
|
|
||||||
colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
|
|
||||||
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
|
|
||||||
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
|
|
||||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
|
|
||||||
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
|
|
||||||
colors[ImGuiCol_CheckMark] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f);
|
|
||||||
colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f);
|
|
||||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
|
||||||
colors[ImGuiCol_Button] = ImVec4(0.44f, 0.44f, 0.44f, 0.40f);
|
|
||||||
colors[ImGuiCol_ButtonHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.45f);
|
|
||||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.44f, 0.44f, 0.44f, 0.40f);
|
|
||||||
colors[ImGuiCol_Header] = ImVec4(0.63f, 0.63f, 0.70f, 0.31f);
|
|
||||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.63f, 0.63f, 0.70f, 0.40f);
|
|
||||||
colors[ImGuiCol_HeaderActive] = ImVec4(0.63f, 0.63f, 0.70f, 0.31f);
|
|
||||||
colors[ImGuiCol_Separator] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
|
|
||||||
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.72f, 0.72f, 0.72f, 0.78f);
|
|
||||||
colors[ImGuiCol_SeparatorActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
|
|
||||||
colors[ImGuiCol_ResizeGrip] = ImVec4(0.91f, 0.91f, 0.91f, 0.25f);
|
|
||||||
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.81f, 0.81f, 0.81f, 0.67f);
|
|
||||||
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.46f, 0.46f, 0.46f, 0.95f);
|
|
||||||
colors[ImGuiCol_PlotLines] = ImVec4(0.4f, 0.9f, 1.0f, 1.00f);
|
|
||||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
|
||||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.73f, 0.60f, 0.15f, 1.00f);
|
|
||||||
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
|
||||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.87f, 0.87f, 0.87f, 0.35f);
|
|
||||||
colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
|
||||||
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
|
||||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
|
|
||||||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void beginDisabled() {
|
void beginDisabled() {
|
||||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.44f, 0.44f, 0.44f, 0.15f));
|
auto& style = ImGui::GetStyle();
|
||||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.20f, 0.21f, 0.22f, 0.30f));
|
ImVec4* colors = style.Colors;
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 0.65f));
|
ImVec4 btnCol = colors[ImGuiCol_Button];
|
||||||
|
ImVec4 frameCol = colors[ImGuiCol_FrameBg];
|
||||||
|
ImVec4 textCol = colors[ImGuiCol_Text];
|
||||||
|
btnCol.w = 0.15f;
|
||||||
|
frameCol.w = 0.30f;
|
||||||
|
textCol.w = 0.65f;
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, btnCol);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, frameCol);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, textCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
void endDisabled() {
|
void endDisabled() {
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace style {
|
namespace style {
|
||||||
extern ImFont* baseFont;
|
extern ImFont* baseFont;
|
||||||
extern ImFont* bigFont;
|
extern ImFont* bigFont;
|
||||||
extern ImFont* hugeFont;
|
extern ImFont* hugeFont;
|
||||||
|
|
||||||
void setDefaultStyle();
|
bool setDefaultStyle(std::string resDir);
|
||||||
void setDarkStyle();
|
bool loadFonts(std::string resDir);
|
||||||
void beginDisabled();
|
void beginDisabled();
|
||||||
void endDisabled();
|
void endDisabled();
|
||||||
void testtt();
|
void testtt();
|
||||||
|
|||||||
237
core/src/gui/theme_manager.cpp
Normal file
237
core/src/gui/theme_manager.cpp
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
#include <json.hpp>
|
||||||
|
#include <gui/theme_manager.h>
|
||||||
|
#include <imgui_internal.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
bool ThemeManager::loadThemesFromDir(std::string path) {
|
||||||
|
// // TEST JUST TO DUMP THE ORIGINAL THEME
|
||||||
|
// auto& style = ImGui::GetStyle();
|
||||||
|
// ImVec4* colors = style.Colors;
|
||||||
|
|
||||||
|
// printf("\n\n");
|
||||||
|
// for (auto [name, id] : IMGUI_COL_IDS) {
|
||||||
|
// ImVec4 col = colors[id];
|
||||||
|
// uint8_t r = 255 - (col.x * 255.0f);
|
||||||
|
// uint8_t g = 255 - (col.y * 255.0f);
|
||||||
|
// uint8_t b = 255 - (col.z * 255.0f);
|
||||||
|
// uint8_t a = col.w * 255.0f;
|
||||||
|
// printf("\"%s\": \"#%02X%02X%02X%02X\",\n", name.c_str(), r, g, b, a);
|
||||||
|
// }
|
||||||
|
// printf("\n\n");
|
||||||
|
|
||||||
|
|
||||||
|
if (!std::filesystem::is_directory(path)) {
|
||||||
|
spdlog::error("Theme directory doesn't exist: {0}", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
themes.clear();
|
||||||
|
for (const auto & file : std::filesystem::directory_iterator(path)) {
|
||||||
|
std::string _path = file.path().generic_string();
|
||||||
|
if (file.path().extension().generic_string() != ".json") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
loadTheme(_path);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ThemeManager::loadTheme(std::string path) {
|
||||||
|
if (!std::filesystem::is_regular_file(path)) {
|
||||||
|
spdlog::error("Theme file doesn't exist: {0}", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load defaults in theme
|
||||||
|
Theme thm;
|
||||||
|
thm.author = "--";
|
||||||
|
|
||||||
|
// Load JSON
|
||||||
|
std::ifstream file(path.c_str());
|
||||||
|
json data;
|
||||||
|
file >> data;
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// Load theme name
|
||||||
|
if (!data.contains("name")) {
|
||||||
|
spdlog::error("Theme {0} is missing the name parameter", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!data["name"].is_string()) {
|
||||||
|
spdlog::error("Theme {0} contains invalid name field. Expected string", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string name = data["name"];
|
||||||
|
|
||||||
|
if (themes.find(name) != themes.end()) {
|
||||||
|
spdlog::error("A theme named '{0}' already exists", name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load theme author if available
|
||||||
|
if (data.contains("author")) {
|
||||||
|
if (!data["author"].is_string()) {
|
||||||
|
spdlog::error("Theme {0} contains invalid author field. Expected string", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
thm.author = data["author"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through all parameters and check their contents
|
||||||
|
std::map<std::string, std::string> params = data;
|
||||||
|
for (auto const& [param, val] : params) {
|
||||||
|
if (param == "name" || param == "author") { continue; }
|
||||||
|
|
||||||
|
// Exception for non-imgu colors
|
||||||
|
if (param == "WaterfallBackground" || param == "ClearColor") {
|
||||||
|
if (val[0] != '#' || !std::all_of(val.begin() + 1, val.end(), ::isxdigit) || val.length() != 9) {
|
||||||
|
spdlog::error("Theme {0} contains invalid {1} field. Expected hex RGBA color", path, param);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValid = false;
|
||||||
|
|
||||||
|
// If param is a color, check that it's a valid RGBA hex value
|
||||||
|
if (IMGUI_COL_IDS.find(param) != IMGUI_COL_IDS.end()) {
|
||||||
|
if (val[0] != '#' || !std::all_of(val.begin() + 1, val.end(), ::isxdigit) || val.length() != 9) {
|
||||||
|
spdlog::error("Theme {0} contains invalid {1} field. Expected hex RGBA color", path, param);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
isValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
spdlog::error("Theme {0} contains unknown {1} field.", path, param);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thm.data = data;
|
||||||
|
themes[name] = thm;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ThemeManager::applyTheme(std::string name) {
|
||||||
|
if (themes.find(name) == themes.end()) {
|
||||||
|
spdlog::error("Unknown theme: {0}", name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::StyleColorsDark();
|
||||||
|
|
||||||
|
auto& style = ImGui::GetStyle();
|
||||||
|
|
||||||
|
style.WindowRounding = 0.0f;
|
||||||
|
style.ChildRounding = 0.0f;
|
||||||
|
style.FrameRounding = 0.0f;
|
||||||
|
style.GrabRounding = 0.0f;
|
||||||
|
style.PopupRounding = 0.0f;
|
||||||
|
style.ScrollbarRounding = 0.0f;
|
||||||
|
|
||||||
|
ImVec4* colors = style.Colors;
|
||||||
|
Theme thm = themes[name];
|
||||||
|
|
||||||
|
uint8_t ret[4];
|
||||||
|
std::map<std::string, std::string> params = thm.data;
|
||||||
|
for (auto const& [param, val] : params) {
|
||||||
|
if (param == "name" || param == "author") { continue; }
|
||||||
|
|
||||||
|
if (param == "WaterfallBackground") {
|
||||||
|
decodeRGBA(val, ret);
|
||||||
|
waterfallBg = ImVec4((float)ret[0]/255.0f, (float)ret[1]/255.0f, (float)ret[2]/255.0f, (float)ret[3]/255.0f);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param == "ClearColor") {
|
||||||
|
decodeRGBA(val, ret);
|
||||||
|
clearColor = ImVec4((float)ret[0]/255.0f, (float)ret[1]/255.0f, (float)ret[2]/255.0f, (float)ret[3]/255.0f);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If param is a color, check that it's a valid RGBA hex value
|
||||||
|
if (IMGUI_COL_IDS.find(param) != IMGUI_COL_IDS.end()) {
|
||||||
|
decodeRGBA(val, ret);
|
||||||
|
colors[IMGUI_COL_IDS[param]] = ImVec4((float)ret[0]/255.0f, (float)ret[1]/255.0f, (float)ret[2]/255.0f, (float)ret[3]/255.0f);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ThemeManager::decodeRGBA(std::string str, uint8_t out[4]) {
|
||||||
|
if (str[0] != '#' || !std::all_of(str.begin() + 1, str.end(), ::isxdigit) || str.length() != 9) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out[0] = std::stoi(str.substr(1, 2), NULL, 16);
|
||||||
|
out[1] = std::stoi(str.substr(3, 2), NULL, 16);
|
||||||
|
out[2] = std::stoi(str.substr(5, 2), NULL, 16);
|
||||||
|
out[3] = std::stoi(str.substr(7, 2), NULL, 16);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ThemeManager::getThemeNames() {
|
||||||
|
std::vector<std::string> names;
|
||||||
|
for (auto [name, theme] : themes) { names.push_back(name); }
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, int> ThemeManager::IMGUI_COL_IDS = {
|
||||||
|
{"Text", ImGuiCol_Text},
|
||||||
|
{"TextDisabled", ImGuiCol_TextDisabled},
|
||||||
|
{"WindowBg", ImGuiCol_WindowBg},
|
||||||
|
{"ChildBg", ImGuiCol_ChildBg},
|
||||||
|
{"PopupBg", ImGuiCol_PopupBg},
|
||||||
|
{"Border", ImGuiCol_Border},
|
||||||
|
{"BorderShadow", ImGuiCol_BorderShadow},
|
||||||
|
{"FrameBg", ImGuiCol_FrameBg},
|
||||||
|
{"FrameBgHovered", ImGuiCol_FrameBgHovered},
|
||||||
|
{"FrameBgActive", ImGuiCol_FrameBgActive},
|
||||||
|
{"TitleBg", ImGuiCol_TitleBg},
|
||||||
|
{"TitleBgActive", ImGuiCol_TitleBgActive},
|
||||||
|
{"TitleBgCollapsed", ImGuiCol_TitleBgCollapsed},
|
||||||
|
{"MenuBarBg", ImGuiCol_MenuBarBg},
|
||||||
|
{"ScrollbarBg", ImGuiCol_ScrollbarBg},
|
||||||
|
{"ScrollbarGrab", ImGuiCol_ScrollbarGrab},
|
||||||
|
{"ScrollbarGrabHovered", ImGuiCol_ScrollbarGrabHovered},
|
||||||
|
{"ScrollbarGrabActive", ImGuiCol_ScrollbarGrabActive},
|
||||||
|
{"CheckMark", ImGuiCol_CheckMark},
|
||||||
|
{"SliderGrab", ImGuiCol_SliderGrab},
|
||||||
|
{"SliderGrabActive", ImGuiCol_SliderGrabActive},
|
||||||
|
{"Button", ImGuiCol_Button},
|
||||||
|
{"ButtonHovered", ImGuiCol_ButtonHovered},
|
||||||
|
{"ButtonActive", ImGuiCol_ButtonActive},
|
||||||
|
{"Header", ImGuiCol_Header},
|
||||||
|
{"HeaderHovered", ImGuiCol_HeaderHovered},
|
||||||
|
{"HeaderActive", ImGuiCol_HeaderActive},
|
||||||
|
{"Separator", ImGuiCol_Separator},
|
||||||
|
{"SeparatorHovered", ImGuiCol_SeparatorHovered},
|
||||||
|
{"SeparatorActive", ImGuiCol_SeparatorActive},
|
||||||
|
{"ResizeGrip", ImGuiCol_ResizeGrip},
|
||||||
|
{"ResizeGripHovered", ImGuiCol_ResizeGripHovered},
|
||||||
|
{"ResizeGripActive", ImGuiCol_ResizeGripActive},
|
||||||
|
{"Tab", ImGuiCol_Tab},
|
||||||
|
{"TabHovered", ImGuiCol_TabHovered},
|
||||||
|
{"TabActive", ImGuiCol_TabActive},
|
||||||
|
{"TabUnfocused", ImGuiCol_TabUnfocused},
|
||||||
|
{"TabUnfocusedActive", ImGuiCol_TabUnfocusedActive},
|
||||||
|
{"PlotLines", ImGuiCol_PlotLines},
|
||||||
|
{"PlotLinesHovered", ImGuiCol_PlotLinesHovered},
|
||||||
|
{"PlotHistogram", ImGuiCol_PlotHistogram},
|
||||||
|
{"PlotHistogramHovered", ImGuiCol_PlotHistogramHovered},
|
||||||
|
{"TableHeaderBg", ImGuiCol_TableHeaderBg},
|
||||||
|
{"TableBorderStrong", ImGuiCol_TableBorderStrong},
|
||||||
|
{"TableBorderLight", ImGuiCol_TableBorderLight},
|
||||||
|
{"TableRowBg", ImGuiCol_TableRowBg},
|
||||||
|
{"TableRowBgAlt", ImGuiCol_TableRowBgAlt},
|
||||||
|
{"TextSelectedBg", ImGuiCol_TextSelectedBg},
|
||||||
|
{"DragDropTarget", ImGuiCol_DragDropTarget},
|
||||||
|
{"NavHighlight", ImGuiCol_NavHighlight},
|
||||||
|
{"NavWindowingHighlight", ImGuiCol_NavWindowingHighlight},
|
||||||
|
{"NavWindowingDimBg", ImGuiCol_NavWindowingDimBg},
|
||||||
|
{"ModalWindowDimBg", ImGuiCol_ModalWindowDimBg}
|
||||||
|
};
|
||||||
33
core/src/gui/theme_manager.h
Normal file
33
core/src/gui/theme_manager.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <json.hpp>
|
||||||
|
|
||||||
|
using nlohmann::json;
|
||||||
|
|
||||||
|
struct Theme {
|
||||||
|
std::string author;
|
||||||
|
json data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ThemeManager {
|
||||||
|
public:
|
||||||
|
bool loadThemesFromDir(std::string path);
|
||||||
|
bool loadTheme(std::string path);
|
||||||
|
bool applyTheme(std::string name);
|
||||||
|
|
||||||
|
std::vector<std::string> getThemeNames();
|
||||||
|
|
||||||
|
ImVec4 waterfallBg = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);;
|
||||||
|
ImVec4 clearColor = ImVec4(0.0666f, 0.0666f, 0.0666f, 1.0f);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool decodeRGBA(std::string str, uint8_t out[4]);
|
||||||
|
|
||||||
|
static std::map<std::string, int> IMGUI_COL_IDS;
|
||||||
|
|
||||||
|
std::map<std::string, Theme> themes;
|
||||||
|
|
||||||
|
};
|
||||||
139
core/src/gui/tuner.cpp
Normal file
139
core/src/gui/tuner.cpp
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/tuner.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace tuner {
|
||||||
|
|
||||||
|
void centerTuning(std::string vfoName, double freq) {
|
||||||
|
if (vfoName != "") {
|
||||||
|
if (gui::waterfall.vfos.find(vfoName) == gui::waterfall.vfos.end()) { return; }
|
||||||
|
sigpath::vfoManager.setOffset(vfoName, 0);
|
||||||
|
}
|
||||||
|
double BW = gui::waterfall.getBandwidth();
|
||||||
|
double viewBW = gui::waterfall.getViewBandwidth();
|
||||||
|
gui::waterfall.setViewOffset((BW / 2.0) - (viewBW / 2.0));
|
||||||
|
gui::waterfall.setCenterFrequency(freq);
|
||||||
|
gui::waterfall.setViewOffset(0);
|
||||||
|
gui::freqSelect.setFrequency(freq);
|
||||||
|
sigpath::sourceManager.tune(freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void normalTuning(std::string vfoName, double freq) {
|
||||||
|
if (vfoName == "") {
|
||||||
|
centerTuning(vfoName, freq);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gui::waterfall.vfos.find(vfoName) == gui::waterfall.vfos.end()) { return; }
|
||||||
|
|
||||||
|
double viewBW = gui::waterfall.getViewBandwidth();
|
||||||
|
double BW = gui::waterfall.getBandwidth();
|
||||||
|
|
||||||
|
ImGui::WaterfallVFO* vfo = gui::waterfall.vfos[vfoName];
|
||||||
|
|
||||||
|
double currentOff = vfo->centerOffset;
|
||||||
|
double currentTune = gui::waterfall.getCenterFrequency() + vfo->generalOffset;
|
||||||
|
double delta = freq - currentTune;
|
||||||
|
|
||||||
|
double newVFO = currentOff + delta;
|
||||||
|
double vfoBW = vfo->bandwidth;
|
||||||
|
double vfoBottom = newVFO - (vfoBW / 2.0);
|
||||||
|
double vfoTop = newVFO + (vfoBW / 2.0);
|
||||||
|
|
||||||
|
double view = gui::waterfall.getViewOffset();
|
||||||
|
double viewBottom = view - (viewBW / 2.0);
|
||||||
|
double viewTop = view + (viewBW / 2.0);
|
||||||
|
|
||||||
|
double wholeFreq = gui::waterfall.getCenterFrequency();
|
||||||
|
double bottom = -(BW / 2.0);
|
||||||
|
double top = (BW / 2.0);
|
||||||
|
|
||||||
|
// VFO still fints in the view
|
||||||
|
if (vfoBottom > viewBottom && vfoTop < viewTop) {
|
||||||
|
sigpath::vfoManager.setCenterOffset(vfoName, newVFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VFO too low for current SDR tuning
|
||||||
|
if (vfoBottom < bottom) {
|
||||||
|
gui::waterfall.setViewOffset((BW / 2.0) - (viewBW / 2.0));
|
||||||
|
double newVFOOffset = (BW / 2.0) - (vfoBW / 2.0) - (viewBW / 10.0);
|
||||||
|
sigpath::vfoManager.setOffset(vfoName, newVFOOffset);
|
||||||
|
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
||||||
|
sigpath::sourceManager.tune(freq - newVFOOffset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VFO too high for current SDR tuning
|
||||||
|
if (vfoTop > top) {
|
||||||
|
gui::waterfall.setViewOffset((viewBW / 2.0) - (BW / 2.0));
|
||||||
|
double newVFOOffset = (vfoBW / 2.0) - (BW / 2.0) + (viewBW / 10.0);
|
||||||
|
sigpath::vfoManager.setOffset(vfoName, newVFOOffset);
|
||||||
|
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
||||||
|
sigpath::sourceManager.tune(freq - newVFOOffset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VFO is still without the SDR's bandwidth
|
||||||
|
if (delta < 0) {
|
||||||
|
double newViewOff = vfoTop - (viewBW / 2.0) + (viewBW / 10.0);
|
||||||
|
double newViewBottom = newViewOff - (viewBW / 2.0);
|
||||||
|
double newViewTop = newViewOff + (viewBW / 2.0);
|
||||||
|
|
||||||
|
if (newViewBottom > bottom) {
|
||||||
|
gui::waterfall.setViewOffset(newViewOff);
|
||||||
|
sigpath::vfoManager.setCenterOffset(vfoName, newVFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gui::waterfall.setViewOffset((BW / 2.0) - (viewBW / 2.0));
|
||||||
|
double newVFOOffset = (BW / 2.0) - (vfoBW / 2.0) - (viewBW / 10.0);
|
||||||
|
sigpath::vfoManager.setCenterOffset(vfoName, newVFOOffset);
|
||||||
|
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
||||||
|
sigpath::sourceManager.tune(freq - newVFOOffset);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
double newViewOff = vfoBottom + (viewBW / 2.0) - (viewBW / 10.0);
|
||||||
|
double newViewBottom = newViewOff - (viewBW / 2.0);
|
||||||
|
double newViewTop = newViewOff + (viewBW / 2.0);
|
||||||
|
|
||||||
|
if (newViewTop < top) {
|
||||||
|
gui::waterfall.setViewOffset(newViewOff);
|
||||||
|
sigpath::vfoManager.setCenterOffset(vfoName, newVFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gui::waterfall.setViewOffset((viewBW / 2.0) - (BW / 2.0));
|
||||||
|
double newVFOOffset = (vfoBW / 2.0) - (BW / 2.0) + (viewBW / 10.0);
|
||||||
|
sigpath::vfoManager.setCenterOffset(vfoName, newVFOOffset);
|
||||||
|
gui::waterfall.setCenterFrequency(freq - newVFOOffset);
|
||||||
|
sigpath::sourceManager.tune(freq - newVFOOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void iqTuning(double freq) {
|
||||||
|
gui::waterfall.setCenterFrequency(freq);
|
||||||
|
gui::waterfall.centerFreqMoved = true;
|
||||||
|
sigpath::sourceManager.tune(freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tune(int mode, std::string vfoName, double freq) {
|
||||||
|
switch (mode) {
|
||||||
|
case TUNER_MODE_CENTER:
|
||||||
|
centerTuning(vfoName, freq);
|
||||||
|
break;
|
||||||
|
case TUNER_MODE_NORMAL:
|
||||||
|
normalTuning(vfoName, freq);
|
||||||
|
break;
|
||||||
|
case TUNER_MODE_LOWER_HALF:
|
||||||
|
normalTuning(vfoName, freq);
|
||||||
|
break;
|
||||||
|
case TUNER_MODE_UPPER_HALF:
|
||||||
|
normalTuning(vfoName, freq);
|
||||||
|
break;
|
||||||
|
case TUNER_MODE_IQ_ONLY:
|
||||||
|
iqTuning(freq);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
core/src/gui/tuner.h
Normal file
20
core/src/gui/tuner.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <module.h>
|
||||||
|
|
||||||
|
namespace tuner {
|
||||||
|
void centerTuning(std::string vfoName, double freq);
|
||||||
|
void normalTuning(std::string vfoName, double freq);
|
||||||
|
void iqTuning(double freq);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TUNER_MODE_CENTER,
|
||||||
|
TUNER_MODE_NORMAL,
|
||||||
|
TUNER_MODE_LOWER_HALF,
|
||||||
|
TUNER_MODE_UPPER_HALF,
|
||||||
|
TUNER_MODE_IQ_ONLY,
|
||||||
|
_TUNER_MODE_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
void tune(int mode, std::string vfoName, double freq);
|
||||||
|
}
|
||||||
@@ -108,20 +108,7 @@ namespace bandplan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadColorTable(std::string path) {
|
void loadColorTable(json table) {
|
||||||
if (!std::filesystem::exists(path)) {
|
colorTable = table.get<std::map<std::string, BandPlanColor_t>>();
|
||||||
spdlog::error("Band Plan Color Table file does not exist");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!std::filesystem::is_regular_file(path)) {
|
|
||||||
spdlog::error("Band Plan Color Table file isn't a file...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::ifstream file(path.c_str());
|
|
||||||
json data;
|
|
||||||
file >> data;
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
colorTable = data.get<std::map<std::string, BandPlanColor_t>>();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -38,7 +38,7 @@ namespace bandplan {
|
|||||||
|
|
||||||
void loadBandPlan(std::string path);
|
void loadBandPlan(std::string path);
|
||||||
void loadFromDir(std::string path);
|
void loadFromDir(std::string path);
|
||||||
void loadColorTable(std::string path);
|
void loadColorTable(json table);
|
||||||
|
|
||||||
extern std::map<std::string, BandPlan_t> bandplans;
|
extern std::map<std::string, BandPlan_t> bandplans;
|
||||||
extern std::vector<std::string> bandplanNames;
|
extern std::vector<std::string> bandplanNames;
|
||||||
|
|||||||
42
core/src/gui/widgets/constellation_diagram.cpp
Normal file
42
core/src/gui/widgets/constellation_diagram.cpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#include <gui/widgets/constellation_diagram.h>
|
||||||
|
|
||||||
|
namespace ImGui {
|
||||||
|
ConstellationDiagram::ConstellationDiagram() {
|
||||||
|
memset(buffer, 0, 1024 * sizeof(dsp::complex_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConstellationDiagram::draw(const ImVec2& size_arg) {
|
||||||
|
std::lock_guard<std::mutex> lck(bufferMtx);
|
||||||
|
ImGuiWindow* window = GetCurrentWindow();
|
||||||
|
ImGuiStyle& style = GetStyle();
|
||||||
|
float pad = style.FramePadding.y;
|
||||||
|
ImVec2 min = window->DC.CursorPos;
|
||||||
|
ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), CalcItemWidth());
|
||||||
|
ImRect bb(min, ImVec2(min.x+size.x, min.y+size.y));
|
||||||
|
float lineHeight = size.y;
|
||||||
|
|
||||||
|
ItemSize(size, style.FramePadding.y);
|
||||||
|
if (!ItemAdd(bb, 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window->DrawList->AddRectFilled(min, ImVec2(min.x+size.x, min.y+size.y), IM_COL32(0,0,0,255));
|
||||||
|
ImU32 col = ImGui::GetColorU32(ImGuiCol_CheckMark, 0.7f);
|
||||||
|
float increment = size.x / 1024.0f;
|
||||||
|
for (int i = 0; i < 1024; i++) {
|
||||||
|
if (buffer[i].re > 1.5f || buffer[i].re < -1.5f) { continue; }
|
||||||
|
if (buffer[i].im > 1.5f || buffer[i].im < -1.5f) { continue; }
|
||||||
|
window->DrawList->AddCircleFilled(ImVec2((((buffer[i].re / 1.5f) + 1) * (size.x*0.5f)) + min.x, (((buffer[i].im / 1.5f) + 1) * (size.y*0.5f)) + min.y), 2, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp::complex_t* ConstellationDiagram::acquireBuffer() {
|
||||||
|
bufferMtx.lock();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConstellationDiagram::releaseBuffer() {
|
||||||
|
bufferMtx.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
core/src/gui/widgets/constellation_diagram.h
Normal file
25
core/src/gui/widgets/constellation_diagram.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <imgui_internal.h>
|
||||||
|
#include <dsp/stream.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <dsp/types.h>
|
||||||
|
|
||||||
|
namespace ImGui {
|
||||||
|
class ConstellationDiagram {
|
||||||
|
public:
|
||||||
|
ConstellationDiagram();
|
||||||
|
|
||||||
|
void draw(const ImVec2& size_arg = ImVec2(0, 0));
|
||||||
|
|
||||||
|
dsp::complex_t* acquireBuffer();
|
||||||
|
|
||||||
|
void releaseBuffer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex bufferMtx;
|
||||||
|
dsp::complex_t buffer[1024];
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
77
core/src/gui/widgets/file_select.cpp
Normal file
77
core/src/gui/widgets/file_select.cpp
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include <gui/widgets/file_select.h>
|
||||||
|
#include <regex>
|
||||||
|
#include <options.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <gui/file_dialogs.h>
|
||||||
|
|
||||||
|
FileSelect::FileSelect(std::string defaultPath, std::vector<std::string> filter) {
|
||||||
|
_filter = filter;
|
||||||
|
setPath(defaultPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSelect::render(std::string id) {
|
||||||
|
bool _pathChanged = false;
|
||||||
|
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
|
||||||
|
|
||||||
|
float buttonWidth = ImGui::CalcTextSize("...").x + 20.0f;
|
||||||
|
bool lastPathValid = pathValid;
|
||||||
|
if (!lastPathValid) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
|
||||||
|
}
|
||||||
|
ImGui::SetNextItemWidth(menuColumnWidth - buttonWidth);
|
||||||
|
if (ImGui::InputText(id.c_str(), strPath, 2047)) {
|
||||||
|
path = std::string(strPath);
|
||||||
|
std::string expandedPath = expandString(strPath);
|
||||||
|
if (!std::filesystem::is_regular_file(expandedPath)) {
|
||||||
|
pathValid = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pathValid = true;
|
||||||
|
_pathChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!lastPathValid) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(("..." + id + "_winselect").c_str(), ImVec2(buttonWidth - 8.0f, 0)) && !dialogOpen) {
|
||||||
|
dialogOpen = true;
|
||||||
|
if (workerThread.joinable()) { workerThread.join(); }
|
||||||
|
workerThread = std::thread(&FileSelect::worker, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pathChanged |= pathChanged;
|
||||||
|
pathChanged = false;
|
||||||
|
return _pathChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSelect::setPath(std::string path, bool markChanged) {
|
||||||
|
this->path = path;
|
||||||
|
std::string expandedPath = expandString(path);
|
||||||
|
pathValid = std::filesystem::is_regular_file(expandedPath);
|
||||||
|
if (markChanged) { pathChanged = true; }
|
||||||
|
strcpy(strPath, path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FileSelect::expandString(std::string input) {
|
||||||
|
input = std::regex_replace(input, std::regex("%ROOT%"), options::opts.root);
|
||||||
|
return std::regex_replace(input, std::regex("//"), "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSelect::pathIsValid() {
|
||||||
|
return pathValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSelect::worker() {
|
||||||
|
auto file = pfd::open_file("Open File", pathValid ? std::filesystem::path(expandString(path)).parent_path().string() : "", _filter);
|
||||||
|
std::vector<std::string> res = file.result();
|
||||||
|
|
||||||
|
if (res.size() > 0) {
|
||||||
|
path = res[0];
|
||||||
|
strcpy(strPath, path.c_str());
|
||||||
|
pathChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pathValid = std::filesystem::is_regular_file(expandString(path));
|
||||||
|
dialogOpen = false;
|
||||||
|
}
|
||||||
30
core/src/gui/widgets/file_select.h
Normal file
30
core/src/gui/widgets/file_select.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <imgui_internal.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class FileSelect {
|
||||||
|
public:
|
||||||
|
FileSelect(std::string defaultPath, std::vector<std::string> filter = {"All Files", "*"});
|
||||||
|
bool render(std::string id);
|
||||||
|
void setPath(std::string path, bool markChanged = false);
|
||||||
|
bool pathIsValid();
|
||||||
|
|
||||||
|
std::string expandString(std::string input);
|
||||||
|
|
||||||
|
std::string path = "";
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void worker();
|
||||||
|
std::thread workerThread;
|
||||||
|
std::vector<std::string> _filter;
|
||||||
|
|
||||||
|
bool pathValid = false;
|
||||||
|
bool dialogOpen = false;
|
||||||
|
char strPath[2048];
|
||||||
|
bool pathChanged = false;
|
||||||
|
};
|
||||||
76
core/src/gui/widgets/folder_select.cpp
Normal file
76
core/src/gui/widgets/folder_select.cpp
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#include <gui/widgets/folder_select.h>
|
||||||
|
#include <regex>
|
||||||
|
#include <options.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <gui/file_dialogs.h>
|
||||||
|
|
||||||
|
FolderSelect::FolderSelect(std::string defaultPath) {
|
||||||
|
setPath(defaultPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FolderSelect::render(std::string id) {
|
||||||
|
bool _pathChanged = false;
|
||||||
|
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
|
||||||
|
|
||||||
|
float buttonWidth = ImGui::CalcTextSize("...").x + 20.0f;
|
||||||
|
bool lastPathValid = pathValid;
|
||||||
|
if (!lastPathValid) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
|
||||||
|
}
|
||||||
|
ImGui::SetNextItemWidth(menuColumnWidth - buttonWidth);
|
||||||
|
if (ImGui::InputText(id.c_str(), strPath, 2047)) {
|
||||||
|
path = std::string(strPath);
|
||||||
|
std::string expandedPath = expandString(strPath);
|
||||||
|
if (!std::filesystem::is_directory(expandedPath)) {
|
||||||
|
pathValid = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pathValid = true;
|
||||||
|
_pathChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!lastPathValid) {
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(("..." + id + "_winselect").c_str(), ImVec2(buttonWidth - 8.0f, 0)) && !dialogOpen) {
|
||||||
|
dialogOpen = true;
|
||||||
|
if (workerThread.joinable()) { workerThread.join(); }
|
||||||
|
workerThread = std::thread(&FolderSelect::worker, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pathChanged |= pathChanged;
|
||||||
|
pathChanged = false;
|
||||||
|
return _pathChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderSelect::setPath(std::string path, bool markChanged) {
|
||||||
|
this->path = path;
|
||||||
|
std::string expandedPath = expandString(path);
|
||||||
|
pathValid = std::filesystem::is_directory(expandedPath);
|
||||||
|
if (markChanged) { pathChanged = true; }
|
||||||
|
strcpy(strPath, path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FolderSelect::expandString(std::string input) {
|
||||||
|
input = std::regex_replace(input, std::regex("%ROOT%"), options::opts.root);
|
||||||
|
return std::regex_replace(input, std::regex("//"), "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FolderSelect::pathIsValid() {
|
||||||
|
return pathValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderSelect::worker() {
|
||||||
|
auto fold = pfd::select_folder("Select Folder", pathValid ? std::filesystem::path(expandString(path)).parent_path().string() : "");
|
||||||
|
std::string res = fold.result();
|
||||||
|
|
||||||
|
if (res != "") {
|
||||||
|
path = res;
|
||||||
|
strcpy(strPath, path.c_str());
|
||||||
|
pathChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pathValid = std::filesystem::is_directory(expandString(path));
|
||||||
|
dialogOpen = false;
|
||||||
|
}
|
||||||
28
core/src/gui/widgets/folder_select.h
Normal file
28
core/src/gui/widgets/folder_select.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <imgui_internal.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class FolderSelect {
|
||||||
|
public:
|
||||||
|
FolderSelect(std::string defaultPath);
|
||||||
|
bool render(std::string id);
|
||||||
|
void setPath(std::string path, bool markChanged = false);
|
||||||
|
bool pathIsValid();
|
||||||
|
|
||||||
|
std::string expandString(std::string input);
|
||||||
|
|
||||||
|
std::string path = "";
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void worker();
|
||||||
|
std::thread workerThread;
|
||||||
|
|
||||||
|
bool pathValid = false;
|
||||||
|
bool dialogOpen = false;
|
||||||
|
char strPath[2048];
|
||||||
|
bool pathChanged = false;
|
||||||
|
};
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
#include <gui/widgets/frequency_select.h>
|
#include <gui/widgets/frequency_select.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <gui/style.h>
|
#include <gui/style.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <glfw_window.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||||
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
|
#endif
|
||||||
|
#include <imgui/imgui_internal.h>
|
||||||
|
|
||||||
bool isInArea(ImVec2 val, ImVec2 min, ImVec2 max) {
|
bool isInArea(ImVec2 val, ImVec2 min, ImVec2 max) {
|
||||||
return val.x >= min.x && val.x < max.x && val.y >= min.y && val.y < max.y;
|
return val.x >= min.x && val.x < max.x && val.y >= min.y && val.y < max.y;
|
||||||
@@ -18,17 +26,20 @@ void FrequencySelect::init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FrequencySelect::onPosChange() {
|
void FrequencySelect::onPosChange() {
|
||||||
int digitHeight = ImGui::CalcTextSize("0").y;
|
ImVec2 digitSz = ImGui::CalcTextSize("0");
|
||||||
|
ImVec2 commaSz = ImGui::CalcTextSize(".");
|
||||||
|
int digitHeight = digitSz.y;
|
||||||
|
int digitWidth = digitSz.x;
|
||||||
int commaOffset = 0;
|
int commaOffset = 0;
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 12; i++) {
|
||||||
digitTopMins[i] = ImVec2(widgetPos.x + (i * 22) + commaOffset, widgetPos.y);
|
digitTopMins[i] = ImVec2(widgetPos.x + (i * digitWidth) + commaOffset, widgetPos.y);
|
||||||
digitBottomMins[i] = ImVec2(widgetPos.x + (i * 22) + commaOffset, widgetPos.y + (digitHeight / 2));
|
digitBottomMins[i] = ImVec2(widgetPos.x + (i * digitWidth) + commaOffset, widgetPos.y + (digitHeight / 2));
|
||||||
|
|
||||||
digitTopMaxs[i] = ImVec2(widgetPos.x + (i * 22) + commaOffset + 22, widgetPos.y + (digitHeight / 2));
|
digitTopMaxs[i] = ImVec2(widgetPos.x + (i * digitWidth) + commaOffset + digitWidth, widgetPos.y + (digitHeight / 2));
|
||||||
digitBottomMaxs[i] = ImVec2(widgetPos.x + (i * 22) + commaOffset + 22, widgetPos.y + digitHeight);
|
digitBottomMaxs[i] = ImVec2(widgetPos.x + (i * digitWidth) + commaOffset + digitWidth, widgetPos.y + digitHeight);
|
||||||
|
|
||||||
if ((i + 1) % 3 == 0 && i < 11) {
|
if ((i + 1) % 3 == 0 && i < 11) {
|
||||||
commaOffset += 12;
|
commaOffset += commaSz.x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,12 +70,31 @@ void FrequencySelect::decrementDigit(int i) {
|
|||||||
digits[i]--;
|
digits[i]--;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (i == 0) { return; }
|
||||||
|
|
||||||
|
// Check if there are non zero digits afterwards
|
||||||
|
bool otherNoneZero = false;
|
||||||
|
for (int j = i - 1; j >= 0; j--) {
|
||||||
|
if (digits[j] > 0) {
|
||||||
|
otherNoneZero = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!otherNoneZero) { return; }
|
||||||
|
|
||||||
digits[i] = 9;
|
digits[i] = 9;
|
||||||
decrementDigit(i - 1);
|
decrementDigit(i - 1);
|
||||||
}
|
}
|
||||||
frequencyChanged = true;
|
frequencyChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FrequencySelect::moveCursorToDigit(int i) {
|
||||||
|
double xpos, ypos;
|
||||||
|
glfwGetCursorPos(core::window, &xpos, &ypos);
|
||||||
|
float nxpos = (digitTopMaxs[i].x + digitTopMins[i].x) / 2.0f;
|
||||||
|
glfwSetCursorPos(core::window, nxpos, ypos);
|
||||||
|
}
|
||||||
|
|
||||||
void FrequencySelect::draw() {
|
void FrequencySelect::draw() {
|
||||||
window = ImGui::GetCurrentWindow();
|
window = ImGui::GetCurrentWindow();
|
||||||
widgetPos = ImGui::GetWindowContentRegionMin();
|
widgetPos = ImGui::GetWindowContentRegionMin();
|
||||||
@@ -90,65 +120,110 @@ void FrequencySelect::draw() {
|
|||||||
ImU32 disabledColor = ImGui::GetColorU32(ImGuiCol_Text, 0.3f);
|
ImU32 disabledColor = ImGui::GetColorU32(ImGuiCol_Text, 0.3f);
|
||||||
ImU32 textColor = ImGui::GetColorU32(ImGuiCol_Text);
|
ImU32 textColor = ImGui::GetColorU32(ImGuiCol_Text);
|
||||||
|
|
||||||
|
ImVec2 digitSz = ImGui::CalcTextSize("0");
|
||||||
|
ImVec2 commaSz = ImGui::CalcTextSize(".");
|
||||||
|
int digitHeight = digitSz.y;
|
||||||
|
int digitWidth = digitSz.x;
|
||||||
int commaOffset = 0;
|
int commaOffset = 0;
|
||||||
bool zeros = true;
|
bool zeros = true;
|
||||||
|
|
||||||
|
ImGui::ItemSize(ImRect(digitTopMins[0], ImVec2(digitBottomMaxs[11].x + 15, digitBottomMaxs[11].y)));
|
||||||
|
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 12; i++) {
|
||||||
if (digits[i] != 0) {
|
if (digits[i] != 0) {
|
||||||
zeros = false;
|
zeros = false;
|
||||||
}
|
}
|
||||||
sprintf(buf, "%d", digits[i]);
|
sprintf(buf, "%d", digits[i]);
|
||||||
window->DrawList->AddText(ImVec2(widgetPos.x + (i * 22) + commaOffset, widgetPos.y),
|
window->DrawList->AddText(ImVec2(widgetPos.x + (i * digitWidth) + commaOffset, widgetPos.y),
|
||||||
zeros ? disabledColor : textColor, buf);
|
zeros ? disabledColor : textColor, buf);
|
||||||
if ((i + 1) % 3 == 0 && i < 11) {
|
if ((i + 1) % 3 == 0 && i < 11) {
|
||||||
commaOffset += 12;
|
commaOffset += commaSz.x;
|
||||||
window->DrawList->AddText(ImVec2(widgetPos.x + (i * 22) + commaOffset + 10, widgetPos.y),
|
window->DrawList->AddText(ImVec2(widgetPos.x + (i * digitWidth) + commaOffset + 11, widgetPos.y),
|
||||||
zeros ? disabledColor : textColor, ".");
|
zeros ? disabledColor : textColor, ".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImVec2 mousePos = ImGui::GetMousePos();
|
if (!gui::mainWindow.lockWaterfallControls) {
|
||||||
bool leftClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
ImVec2 mousePos = ImGui::GetMousePos();
|
||||||
bool rightClick = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
|
bool leftClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||||
int mw = ImGui::GetIO().MouseWheel;
|
bool rightClick = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
|
||||||
bool onDigit = false;
|
int mw = ImGui::GetIO().MouseWheel;
|
||||||
|
bool onDigit = false;
|
||||||
|
bool hovered = false;
|
||||||
|
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 12; i++) {
|
||||||
onDigit = false;
|
onDigit = false;
|
||||||
if (isInArea(mousePos, digitTopMins[i], digitTopMaxs[i])) {
|
if (isInArea(mousePos, digitTopMins[i], digitTopMaxs[i])) {
|
||||||
window->DrawList->AddRectFilled(digitTopMins[i], digitTopMaxs[i], IM_COL32(255, 0, 0, 75));
|
window->DrawList->AddRectFilled(digitTopMins[i], digitTopMaxs[i], IM_COL32(255, 0, 0, 75));
|
||||||
if (leftClick) {
|
if (leftClick) {
|
||||||
incrementDigit(i);
|
incrementDigit(i);
|
||||||
}
|
|
||||||
onDigit = true;
|
|
||||||
}
|
|
||||||
if (isInArea(mousePos, digitBottomMins[i], digitBottomMaxs[i])) {
|
|
||||||
window->DrawList->AddRectFilled(digitBottomMins[i], digitBottomMaxs[i], IM_COL32(0, 0, 255, 75));
|
|
||||||
if (leftClick) {
|
|
||||||
decrementDigit(i);
|
|
||||||
}
|
|
||||||
onDigit = true;
|
|
||||||
}
|
|
||||||
if (onDigit) {
|
|
||||||
if (rightClick) {
|
|
||||||
for (int j = i; j < 12; j++) {
|
|
||||||
digits[j] = 0;
|
|
||||||
}
|
}
|
||||||
frequencyChanged = true;
|
onDigit = true;
|
||||||
}
|
}
|
||||||
if (mw != 0) {
|
if (isInArea(mousePos, digitBottomMins[i], digitBottomMaxs[i])) {
|
||||||
int count = abs(mw);
|
window->DrawList->AddRectFilled(digitBottomMins[i], digitBottomMaxs[i], IM_COL32(0, 0, 255, 75));
|
||||||
for (int j = 0; j < count; j++) {
|
if (leftClick) {
|
||||||
mw > 0 ? incrementDigit(i) : decrementDigit(i);
|
decrementDigit(i);
|
||||||
|
}
|
||||||
|
onDigit = true;
|
||||||
|
}
|
||||||
|
if (onDigit) {
|
||||||
|
hovered = true;
|
||||||
|
if (rightClick || (ImGui::IsKeyPressed(GLFW_KEY_DELETE) || ImGui::IsKeyPressed(GLFW_KEY_ENTER) || ImGui::IsKeyPressed(GLFW_KEY_KP_ENTER))) {
|
||||||
|
for (int j = i; j < 12; j++) {
|
||||||
|
digits[j] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
frequencyChanged = true;
|
||||||
|
}
|
||||||
|
if (ImGui::IsKeyPressed(GLFW_KEY_UP)) {
|
||||||
|
incrementDigit(i);
|
||||||
|
}
|
||||||
|
if (ImGui::IsKeyPressed(GLFW_KEY_DOWN)) {
|
||||||
|
decrementDigit(i);
|
||||||
|
}
|
||||||
|
if ((ImGui::IsKeyPressed(GLFW_KEY_LEFT) || ImGui::IsKeyPressed(GLFW_KEY_BACKSPACE)) && i > 0) {
|
||||||
|
moveCursorToDigit(i - 1);
|
||||||
|
}
|
||||||
|
if (ImGui::IsKeyPressed(GLFW_KEY_RIGHT) && i < 11) {
|
||||||
|
moveCursorToDigit(i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto chars = ImGui::GetIO().InputQueueCharacters;
|
||||||
|
|
||||||
|
// For each keyboard characters, type it
|
||||||
|
for (int j = 0; j < chars.Size; j++) {
|
||||||
|
if (chars[j] >= '0' && chars[j] <= '9') {
|
||||||
|
digits[i + j] = chars[j] - '0';
|
||||||
|
if ((i + j) < 11) { moveCursorToDigit(i + j + 1); }
|
||||||
|
frequencyChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mw != 0) {
|
||||||
|
int count = abs(mw);
|
||||||
|
for (int j = 0; j < count; j++) {
|
||||||
|
mw > 0 ? incrementDigit(i) : decrementDigit(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
digitHovered = hovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t freq = 0;
|
uint64_t freq = 0;
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 12; i++) {
|
||||||
freq += digits[i] * pow(10, 11 - i);
|
freq += digits[i] * pow(10, 11 - i);
|
||||||
}
|
}
|
||||||
frequency = freq;
|
|
||||||
|
uint64_t orig = freq;
|
||||||
|
freq = std::clamp<uint64_t>(freq, minFreq, maxFreq);
|
||||||
|
if (freq != orig && limitFreq) {
|
||||||
|
setFrequency(freq);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
frequency = orig;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|
||||||
@@ -157,7 +232,8 @@ void FrequencySelect::draw() {
|
|||||||
//ImGui::NewLine();
|
//ImGui::NewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrequencySelect::setFrequency(uint64_t freq) {
|
void FrequencySelect::setFrequency(int64_t freq) {
|
||||||
|
freq = std::max<int64_t>(0, freq);
|
||||||
int i = 11;
|
int i = 11;
|
||||||
for (uint64_t f = freq; i >= 0; i--) {
|
for (uint64_t f = freq; i >= 0; i--) {
|
||||||
digits[i] = f % 10;
|
digits[i] = f % 10;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user