128 Commits

Author SHA1 Message Date
a94e2d6712 Show RTL-SDR dongle manufacturer and model name instead of generic name
Some checks failed
Build Binaries / build_debian_buster_amd64 (push) Failing after 5s
Build Binaries / build_debian_bullseye_amd64 (push) Failing after 4s
Build Binaries / build_debian_bookworm_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_jammy_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_oracular_amd64 (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 3s
Build Binaries / build_debian_sid_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_focal_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_noble_amd64 (push) Failing after 3s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_debian_buster_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bullseye_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bookworm_aarch64 (push) Has been cancelled
Build Binaries / build_debian_sid_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_focal_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_jammy_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_noble_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_oracular_aarch64 (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2025-06-25 00:35:18 +02:00
981bd1695a Added beginning of HydraSDR source
Some checks failed
Build Binaries / build_debian_buster_amd64 (push) Failing after 18s
Build Binaries / build_debian_bullseye_amd64 (push) Failing after 4s
Build Binaries / build_debian_bookworm_amd64 (push) Failing after 4s
Build Binaries / build_debian_sid_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_focal_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_jammy_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_noble_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_oracular_amd64 (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 3s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_debian_buster_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bullseye_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bookworm_aarch64 (push) Has been cancelled
Build Binaries / build_debian_sid_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_focal_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_jammy_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_noble_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_oracular_aarch64 (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2025-06-19 18:45:42 +02:00
dd9b8db6c9 Add new patreon
Some checks failed
Build Binaries / build_debian_buster_amd64 (push) Failing after 4s
Build Binaries / build_debian_bullseye_amd64 (push) Failing after 3s
Build Binaries / build_debian_bookworm_amd64 (push) Failing after 3s
Build Binaries / build_debian_sid_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_focal_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_jammy_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_noble_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_oracular_amd64 (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 3s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_debian_buster_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bullseye_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bookworm_aarch64 (push) Has been cancelled
Build Binaries / build_debian_sid_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_focal_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_jammy_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_noble_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_oracular_aarch64 (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2025-05-01 01:43:13 +02:00
543c60ccbc fix (#1594) duplicate plutosdr crash when shitty internet protocol IPv6 is enabled 2025-05-01 01:39:08 +02:00
2dd8c6cea4 maybe final missing include?
Some checks failed
Build Binaries / build_debian_buster_amd64 (push) Failing after 18s
Build Binaries / build_debian_bullseye_amd64 (push) Failing after 4s
Build Binaries / build_debian_bookworm_amd64 (push) Failing after 3s
Build Binaries / build_debian_sid_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_focal_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_jammy_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_noble_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_oracular_amd64 (push) Failing after 4s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 3s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_debian_buster_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bullseye_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bookworm_aarch64 (push) Has been cancelled
Build Binaries / build_debian_sid_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_focal_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_jammy_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_noble_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_oracular_aarch64 (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2025-04-23 06:06:47 +02:00
9457ad9369 fixed missing include 2025-04-23 05:35:13 +02:00
fccd72b5f8 switch to official codec2 repo for windows build 2025-04-23 05:14:40 +02:00
e75cc7be6f add sddc source prototype + add new ATV decoder + fix windows builds 2025-04-23 04:49:45 +02:00
aa2b4b1c58 attempt to fix plutosdr macos issues
Some checks failed
Build Binaries / build_debian_sid_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_jammy_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_noble_amd64 (push) Failing after 4s
Build Binaries / build_debian_buster_amd64 (push) Failing after 3s
Build Binaries / build_debian_bullseye_amd64 (push) Failing after 3s
Build Binaries / build_debian_bookworm_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_focal_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_oracular_amd64 (push) Failing after 3s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 3s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_debian_buster_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bullseye_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bookworm_aarch64 (push) Has been cancelled
Build Binaries / build_debian_sid_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_focal_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_jammy_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_noble_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_oracular_aarch64 (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2025-03-12 16:50:58 +01:00
64315ebc61 work on ATV demod
Some checks failed
Build Binaries / build_debian_buster_amd64 (push) Failing after 5s
Build Binaries / build_debian_bullseye_amd64 (push) Failing after 3s
Build Binaries / build_debian_bookworm_amd64 (push) Failing after 4s
Build Binaries / build_debian_sid_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_focal_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_jammy_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_noble_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_oracular_amd64 (push) Failing after 3s
Build Binaries / build_android (push) Failing after 3s
Build Binaries / check_spelling (push) Failing after 2s
Build Binaries / check_formatting (push) Successful in 3s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_debian_buster_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bullseye_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bookworm_aarch64 (push) Has been cancelled
Build Binaries / build_debian_sid_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_focal_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_jammy_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_noble_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_oracular_aarch64 (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2025-03-10 17:47:56 +01:00
553204b801 add missing include
Some checks failed
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_ubuntu_oracular_aarch64 (push) Has been cancelled
Build Binaries / build_debian_buster_amd64 (push) Failing after 15s
Build Binaries / build_debian_bullseye_amd64 (push) Failing after 3s
Build Binaries / build_debian_bookworm_amd64 (push) Failing after 3s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
Build Binaries / build_debian_sid_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_focal_amd64 (push) Failing after 3s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / build_ubuntu_jammy_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_noble_amd64 (push) Failing after 4s
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_debian_buster_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bullseye_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bookworm_aarch64 (push) Has been cancelled
Build Binaries / build_debian_sid_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_focal_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_jammy_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_noble_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_oracular_amd64 (push) Failing after 4s
2025-03-03 15:31:09 +01:00
2a84ed202c Move gain clamping in rtl-sdr init 2025-03-03 14:52:29 +01:00
f90e2d53a7 fixed audio source crashing if no device is available
Some checks failed
Build Binaries / build_debian_buster_amd64 (push) Failing after 17s
Build Binaries / build_debian_bullseye_amd64 (push) Failing after 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_ubuntu_focal_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_jammy_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_noble_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bookworm_amd64 (push) Failing after 4s
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_debian_buster_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bullseye_aarch64 (push) Has been cancelled
Build Binaries / build_debian_bookworm_aarch64 (push) Has been cancelled
Build Binaries / build_debian_sid_aarch64 (push) Has been cancelled
Build Binaries / build_ubuntu_oracular_aarch64 (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
Build Binaries / build_debian_sid_amd64 (push) Failing after 4s
Build Binaries / build_ubuntu_focal_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_jammy_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_noble_amd64 (push) Failing after 3s
Build Binaries / build_ubuntu_oracular_amd64 (push) Failing after 4s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 3s
Build Binaries / check_spelling (push) Failing after 3s
2025-02-14 00:43:32 +01:00
993bf9128c add aarch64 builds for all other debian-based distros and copy all aarch64 builds to nightly release 2025-02-14 00:34:47 +01:00
d9ff7eaa12 fix SDRplay API version 2025-02-13 23:41:52 +01:00
32b289953d Fix SDRplay CI build on all linux distros 2025-02-13 23:38:26 +01:00
5178de0849 add experimental aarch64 build to CI 2025-02-13 23:25:05 +01:00
5c355e21f5 Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 3s
Build Binaries / build_ubuntu_focal (push) Failing after 3s
Build Binaries / build_ubuntu_jammy (push) Failing after 3s
Build Binaries / build_ubuntu_noble (push) Failing after 3s
Build Binaries / build_ubuntu_oracular (push) Failing after 3s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 5s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2025-02-06 15:46:55 +01:00
d020640b7c add new patreon to credits + fix USRP samplerate formatting 2025-02-06 15:46:46 +01:00
e0f9053229 Merge pull request #1559 from SrS2225a/wayland_icon
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 3s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_oracular (push) Failing after 3s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
added window icon support for wayland
2025-02-02 07:10:28 +01:00
395186ffb0 another format fix 2025-02-02 07:10:04 +01:00
0e77a9f4ab disable VOR receiver module by default
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 17s
Build Binaries / build_debian_bullseye (push) Failing after 3s
Build Binaries / build_debian_bookworm (push) Failing after 3s
Build Binaries / build_debian_sid (push) Failing after 3s
Build Binaries / build_ubuntu_focal (push) Failing after 5s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_ubuntu_oracular (push) Failing after 4s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2025-01-28 05:07:54 +01:00
4799d0e3a8 add VOR receiver module 2025-01-28 05:07:23 +01:00
6c6f4264b2 fix formatting 2025-01-07 21:41:22 +01:00
ea3675da47 add new patreons to the credit
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 3s
Build Binaries / build_debian_sid (push) Failing after 3s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 3s
Build Binaries / build_ubuntu_oracular (push) Failing after 4s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2025-01-05 21:20:43 +01:00
afef9f57ab add check for correct glfw for macro 2025-01-04 22:58:53 -08:00
e1de2daca8 Merge pull request #1556 from Oskar-Dudek/patch-4
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 6s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 5s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_ubuntu_oracular (push) Failing after 3s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
Update ireland.json
2025-01-04 00:06:44 +01:00
69bd6b0f3a revert changes not meant for this branch
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 3s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 5s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_ubuntu_oracular (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2025-01-02 00:44:19 +01:00
fea3fc2563 fix tuning code to allow using high-side injection downconverters 2025-01-01 23:02:16 +01:00
7bccc67311 changed GLFW_WAYLAND_APP_ID, to the correct string name 2024-12-31 19:56:42 -08:00
4310bbb1ea added window icon support for wayland 2024-12-29 21:17:32 -08:00
d811a839ff Update ireland.json
sorry for not updating for this long. I added CB, Air band VOR/ILS, Air band Voice, Polar orbiting satellites, and ADS-B. i validated this and jsonlint says its valid
2024-12-24 23:05:06 +00:00
46bcba7594 fix some settings not applied on start for RSP1B and RSPdxR2
Some checks failed
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 3s
Build Binaries / build_ubuntu_oracular (push) Failing after 4s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 3s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-12-19 04:26:30 +01:00
45e4286f38 Merge pull request #1550 from AlexandreRouma/new_sdrplay_source
New sdrplay source
2024-12-18 23:32:34 +01:00
c266a37a6b fix sdrplay configuration bug
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 6s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 3s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
2024-12-18 23:19:40 +01:00
895199ae94 fix RSPdx bug
Some checks failed
Build Binaries / build_windows (push) Waiting to run
Build Binaries / build_macos_intel (push) Waiting to run
Build Binaries / build_macos_arm (push) Waiting to run
Build Binaries / build_raspios_bullseye_armhf (push) Waiting to run
Build Binaries / create_full_archive (push) Blocked by required conditions
Build Binaries / update_nightly_release (push) Blocked by required conditions
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
2024-12-18 23:02:49 +01:00
d62426364a finish implementing support for RSP1B and RSPdx R2 + Improve samplerate and bandwidth code 2024-12-18 22:59:57 +01:00
5ded73ce71 fix deprecated macos CI runner version
Some checks failed
Build Binaries / build_windows (push) Waiting to run
Build Binaries / build_macos_intel (push) Waiting to run
Build Binaries / build_macos_arm (push) Waiting to run
Build Binaries / build_raspios_bullseye_armhf (push) Waiting to run
Build Binaries / create_full_archive (push) Blocked by required conditions
Build Binaries / update_nightly_release (push) Blocked by required conditions
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 5s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_ubuntu_oracular (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / build_debian_buster (push) Failing after 16s
Build Binaries / build_android (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
2024-12-18 21:18:13 +01:00
cf3e15d285 fix workflow file 2024-12-18 20:51:02 +01:00
59ca2cf1c0 deprecate ubuntu noble build and add ubuntu oracular build 2024-12-18 20:21:48 +01:00
9bb4aeda14 fix package version 2024-12-18 19:47:11 +01:00
304d5c42cc some fucking changes too lazy to name this shit 2024-11-27 21:49:46 +01:00
b914587228 Revert "add support for new models (in a bad way) and start the rewrite process"
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 17s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 3s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
This reverts commit 3c1d0c7422.
2024-11-27 21:42:59 +01:00
3c1d0c7422 add support for new models (in a bad way) and start the rewrite process 2024-11-27 21:41:46 +01:00
11f87e0fe2 fix menu order bug
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 5s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 5s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-11-09 19:28:34 +01:00
e192cb963b fix MacOS ARM CI
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 5s
Build Binaries / build_ubuntu_focal (push) Failing after 5s
Build Binaries / build_ubuntu_jammy (push) Failing after 5s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 5s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-11-08 00:29:46 +01:00
fe407a2f27 Merge pull request #1521 from AlexandreRouma/new_source_menu
New source menu
2024-11-08 00:20:26 +01:00
6891d0bb0f final bugfixes to the new source menu
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 3s
Build Binaries / build_debian_bookworm (push) Failing after 6s
Build Binaries / build_debian_sid (push) Failing after 5s
Build Binaries / build_ubuntu_focal (push) Failing after 5s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 5s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-11-07 23:49:15 +01:00
b835d07573 finish custom offset definitions and fix bug in source selection 2024-11-07 17:39:52 +01:00
f205d97b52 Merge pull request #1518 from Oskar-Dudek/patch-3
Some checks failed
Build Binaries / build_windows (push) Waiting to run
Build Binaries / build_macos_intel (push) Waiting to run
Build Binaries / build_macos_arm (push) Waiting to run
Build Binaries / build_raspios_bullseye_armhf (push) Waiting to run
Build Binaries / create_full_archive (push) Blocked by required conditions
Build Binaries / update_nightly_release (push) Blocked by required conditions
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 5s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 8s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
Fix ireland.json
2024-11-07 17:36:13 +01:00
628dcfcce0 Fix ireland.json
I used jsonlint.com and i edited it in some places and it says json valid, please can you try this version
2024-11-07 16:26:37 +00:00
d1e7cc56b4 re-disable the M17 decoder module in MacOS ARM nightly builds due to codec2 package bug 2024-11-07 15:02:16 +01:00
334860c963 attempt to enable M17 Decoder on MacOS ARM 2024-11-07 14:53:27 +01:00
69161253e8 source menu upgrade 2024-11-07 14:03:32 +01:00
5ab3428b90 update readme
Some checks failed
Build Binaries / build_windows (push) Waiting to run
Build Binaries / build_macos_intel (push) Waiting to run
Build Binaries / build_macos_arm (push) Waiting to run
Build Binaries / create_full_archive (push) Blocked by required conditions
Build Binaries / build_raspios_bullseye_armhf (push) Waiting to run
Build Binaries / update_nightly_release (push) Blocked by required conditions
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 5s
Build Binaries / build_debian_sid (push) Failing after 5s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 6s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
2024-11-06 21:27:45 +01:00
7f002f6276 add lower limit to network source samplerate 2024-11-06 20:54:01 +01:00
a728403a3f Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus 2024-11-06 20:29:58 +01:00
0f1d2da3b7 finish and enable the network source 2024-11-06 20:29:50 +01:00
6d0b65c27f Merge pull request #1515 from Oskar-Dudek/patch-1
Some checks failed
Build Binaries / create_full_archive (push) Blocked by required conditions
Build Binaries / update_nightly_release (push) Blocked by required conditions
Build Binaries / build_windows (push) Waiting to run
Build Binaries / build_macos_intel (push) Waiting to run
Build Binaries / build_macos_arm (push) Waiting to run
Build Binaries / build_raspios_bullseye_armhf (push) Waiting to run
Build Binaries / build_debian_buster (push) Failing after 6s
Build Binaries / build_debian_bullseye (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 5s
Build Binaries / build_debian_sid (push) Failing after 5s
Build Binaries / build_ubuntu_focal (push) Failing after 5s
Build Binaries / build_ubuntu_jammy (push) Failing after 5s
Build Binaries / build_ubuntu_mantic (push) Failing after 5s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
Create ireland.json
2024-11-06 17:51:31 +01:00
f640cdcb6a fix formatting 2024-11-06 17:47:14 +01:00
80a90e13d9 Create ireland.json 2024-11-06 16:37:49 +00:00
3982db73d3 Merge pull request #1501 from bsy0317/master
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 3s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 6s
Build Binaries / build_ubuntu_mantic (push) Failing after 8s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 9s
Build Binaries / check_formatting (push) Successful in 5s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
Add Republic of Korea BandPlan
2024-10-22 11:03:52 +02:00
bd64f07a20 Update republic-of-korea.json 2024-10-22 17:57:18 +09:00
c9950d9331 fix band plan name 2024-10-22 10:48:39 +02:00
9bc609f4e4 Update republic-of-korea.json
Modify indentation
2024-10-22 17:39:15 +09:00
bcc8e20e66 Update republic-of-korea.json
Corrected typos
2024-10-22 17:34:02 +09:00
b07e828fed Update republic-of-korea.json
Correcting typos
2024-10-22 12:55:10 +09:00
bd24a4a5eb Add Republic of Korea.json
Add Republic of Korea BandPlan.
It's not the Democratic People's Republic of Korea!
2024-10-22 12:39:06 +09:00
fe4a7b32a7 Merge pull request #1496 from AlexandreRouma/bladerf_clock_sel
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 3s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
add clock selection got bladerf devices
2024-10-17 20:35:52 +02:00
0e1ab29b5d ingore commas in pasted frequencies
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 5s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-10-16 22:14:12 +02:00
fbbafddd3d fix frequency formatting when copying from frequency selector 2024-10-16 20:48:35 +02:00
1cbc8ec6f5 add copy/paste support to the frequency selector 2024-10-16 18:31:14 +02:00
9f65e3ec71 add clock selection got bladerf devices 2024-10-12 01:48:39 +02:00
08f3a7d201 make the modulation field of a baseband recording 'IQ' instead of 'Unknown'
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 6s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
2024-10-07 14:39:45 +02:00
9ce62f8885 add whitelist for plutosdr-like devices 2024-10-07 14:10:23 +02:00
caeaa2d46c add kcsdr_source to the readme
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 8s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 6s
Build Binaries / build_android (push) Failing after 7s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-10-02 19:15:13 +02:00
7ae030a3a6 fix fobossdr_source module missing on MacOS as described in #1485 2024-10-02 18:58:48 +02:00
1b27379a3d add beginning of kcsdr source 2024-10-02 18:57:05 +02:00
e52123038e Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 5s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-09-28 03:32:14 +02:00
ec8c60111d uncomment lines that copy the fobossdr source module for windows packaging 2024-09-28 03:32:07 +02:00
f61799cf5f Merge pull request #1482 from AlexandreRouma/fobos_test
Enable the FobosSDR source on windows and linux
2024-09-28 02:44:04 +02:00
17eccf5156 enabled fobos source on windows 2024-09-14 15:05:12 +02:00
e835c8dd9a disable fobos source on windows 2024-09-14 15:04:07 +02:00
acb1be121c enable fobossdr source 2024-09-14 14:48:37 +02:00
0fa89614bb disabled problematic fobossdr source 2024-09-14 14:47:10 +02:00
256affd918 fixed fobossdr CI 2024-09-14 02:06:34 +02:00
e80cdbf248 fix CI + fix fobossdr_source cmakelist + prepare for fobos source on windows 2024-09-14 00:18:31 +02:00
75e66226c3 finish fobossdr source + add rigexpert to hardware donor list + fix source module default instantiation 2024-09-13 23:02:30 +02:00
c2f0e756a5 add fobossdr_source module and fix network sink crash when the given hostname is invalid 2024-09-11 21:57:12 +02:00
79dd5bdcbb add beginning of DAB decoder and add missing hardware donors to the credits 2024-09-10 15:33:22 +02:00
6dce28345c add missing stddef includes 2024-08-26 22:37:18 +02:00
fe9ac6c9a1 implemented all user options for harogic devices 2024-08-22 04:36:36 +02:00
bfdfa2b30b add debug logging to the harogic source enumeration function 2024-08-22 02:20:51 +02:00
46e98b9b03 add harogic_source module 2024-08-22 01:52:27 +02:00
e674a73771 removed useless include 2024-08-20 00:32:16 +02:00
118e1fbff0 Add badgesdr source and add logging to spectran source 2024-08-05 22:32:39 +02:00
bcadb36232 fix include issues in dsp library 2024-07-26 23:55:26 +02:00
554ba2f596 add ryfi decoder module 2024-07-24 16:31:29 +02:00
949fde022d fix recorder options not disabled during recording, increase rfnm source dsp frame size and fix long pause when stopping rfnm 2024-07-18 18:10:33 +02:00
123e34d250 fix appliesCh of preferred path in rfnm_source 2024-07-18 16:53:02 +02:00
f1c7010437 add rfnm_source path logging 2024-07-18 16:40:21 +02:00
33a7795de1 fix RFNM source on MacOS 2024-07-18 03:12:38 +02:00
fe7299c18a lots of work on the RFNM source 2024-07-18 02:15:20 +02:00
8a9e0abcc2 fix RFNM source not enabled 2024-07-16 21:01:44 +02:00
13abe4860b fix missing permissions 2024-07-16 20:59:32 +02:00
981592fa19 how many times can someone fuck up a build script 2024-07-16 20:58:25 +02:00
582750f79b fix CI script typo 2024-07-16 20:55:19 +02:00
36f2a083ce More work towards working RFNM CI 2024-07-16 20:53:32 +02:00
d753135a61 fix ARM macos CI build not having RFNM source 2024-07-16 20:45:27 +02:00
07744e5bae add rfnm_source module to linux and MacOS CI 2024-07-16 20:43:53 +02:00
9ec78da7ac rfnm source cleanup 2024-07-16 00:23:06 +02:00
0066994899 add missing files for rfnm source and add it to the default instantiation config 2024-07-15 22:48:27 +02:00
93ab51bf2f fix ci 2024-07-15 20:36:08 +02:00
f9d7d20073 add rfnm_source to windows nightlies 2024-07-15 20:25:53 +02:00
e81db5d85c make text bold so people won't miss it 2024-07-07 00:00:23 +02:00
5c3a66642b Added an idiot-proof warning that the drivers for the SDR you want to use must be installed. I know... shocker... 2024-07-06 23:59:33 +02:00
36492e799a Merge pull request #1418 from Armand31/patch-1
Update france.json
2024-06-26 17:41:53 +02:00
0110dfbef6 Another attempt 2024-06-26 17:19:06 +02:00
0b5a2ff786 Update librtlsdr for windows CI 2024-06-26 17:11:23 +02:00
ce0f1f05ae Attempt to fix the Windows CI builds (fuck you Microsoft) 2024-06-26 16:49:05 +02:00
46a5ff8ac5 Update france.json
Correcting typos
2024-06-10 00:57:42 +02:00
03559b928b Update france.json
Adding the 2 DAB bands and Police band (tetrapol protocol)
2024-06-10 00:26:48 +02:00
206ce6e8c3 fix formatting mistake in pull request... for fucks sake... 2024-06-08 05:33:57 +02:00
d7a1f46af0 Merge pull request #1416 from Armand31/patch-1
Update france.json
2024-06-07 22:37:40 +02:00
89e6e4f7ad Update france.json 2024-06-07 22:13:55 +02:00
6ced9b15c3 Update france.json
Add DVB-T (TNT) band, from 470 to 694 MHz
2024-06-07 21:30:13 +02:00
0de189a7b7 Merge pull request #1413 from ycanerol/master
Add Turkish Bandplan
2024-06-06 19:35:30 +02:00
309717b5f8 Add Turkish Bandplan 2024-05-31 01:59:31 +03:00
112 changed files with 14307 additions and 1004 deletions

View File

@ -36,6 +36,13 @@ jobs:
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/" ; rm "C:/Program Files/PothosSDR/lib/libusb-1.0.lib" ; cp "libusb_old/MS64/dll/libusb-1.0.lib" "C:/Program Files/PothosSDR/lib/"
- name: Download librtlsdr
run: Invoke-WebRequest -Uri "https://ftp.osmocom.org/binaries/windows/rtl-sdr/rtl-sdr-64bit-20240623.zip" -OutFile ${{runner.workspace}}/rtl-sdr.zip
- name: Patch Pothos with newer librtlsdr version
working-directory: ${{runner.workspace}}
run: 7z x rtl-sdr.zip ; rm "C:/Program Files/PothosSDR/bin/rtlsdr.dll" ; cp "rtl-sdr-64bit-20240623/librtlsdr.dll" "C:/Program Files/PothosSDR/bin/rtlsdr.dll"
- name: Download SDRPlay API
run: Invoke-WebRequest -Uri "https://www.sdrpp.org/SDRplay.zip" -OutFile ${{runner.workspace}}/SDRplay.zip
@ -43,7 +50,7 @@ jobs:
run: 7z x ${{runner.workspace}}/SDRplay.zip -o"C:/Program Files/"
- name: Download codec2
run: git clone https://github.com/AlexandreRouma/codec2
run: git clone https://github.com/drowe67/codec2
- name: Prepare MinGW
run: C:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 -c "pacman --noconfirm -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja"
@ -58,17 +65,23 @@ jobs:
run: mkdir "C:/Program Files/codec2" ; mkdir "C:/Program Files/codec2/include" ; mkdir "C:/Program Files/codec2/include/codec2" ; mkdir "C:/Program Files/codec2/lib" ; cd "codec2" ; xcopy "src" "C:/Program Files/codec2/include" ; cd "build" ; xcopy "src" "C:/Program Files/codec2/lib" ; xcopy "codec2" "C:/Program Files/codec2/include/codec2"
- name: Install vcpkg dependencies
run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows libusb:x64-windows
run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows libusb:x64-windows spdlog:x64-windows
- name: Install rtaudio
run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; git checkout 2f2fca4502d506abc50f6d4473b2836d24cfb1e3 ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install .
- name: Install libperseus-sdr
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake -DCMAKE_BUILD_TYPE=Release "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
- name: Install librfnm
run: git clone https://github.com/AlexandreRouma/librfnm ; cd librfnm ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install .
- name: Install libfobos
run: git clone https://github.com/AlexandreRouma/libfobos ; cd libfobos ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.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_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
run: cmake -DCOPY_MSVC_REDISTRIBUTABLES=ON "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
- name: Build
working-directory: ${{runner.workspace}}/build
@ -85,7 +98,7 @@ jobs:
path: ${{runner.workspace}}/sdrpp_windows_x64.zip
build_macos_intel:
runs-on: macos-12
runs-on: macos-13
steps:
- uses: actions/checkout@v4
@ -94,7 +107,7 @@ jobs:
run: cmake -E make_directory ${{runner.workspace}}/build
- name: Install dependencies
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool spdlog && pip3 install mako
- name: Install volk
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
@ -112,14 +125,20 @@ jobs:
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
- name: Install libperseus
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd ..
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && sudo make install && cd ..
- name: Install librfnm
run: git clone https://github.com/AlexandreRouma/librfnm && cd librfnm && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
- name: Install libfobos
run: git clone https://github.com/AlexandreRouma/libfobos && cd libfobos && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
- name: Install more recent librtlsdr
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
- name: Prepare CMake
working-directory: ${{runner.workspace}}/build
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
- name: Build
working-directory: ${{runner.workspace}}/build
@ -145,7 +164,7 @@ jobs:
run: cmake -E make_directory ${{runner.workspace}}/build
- name: Install dependencies
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako --break-system-packages
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool spdlog && pip3 install mako --break-system-packages
- name: Install volk
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
@ -165,12 +184,18 @@ jobs:
# - name: Install libperseus
# run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd ..
- name: Install librfnm
run: git clone https://github.com/AlexandreRouma/librfnm && cd librfnm && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
- name: Install libfobos
run: git clone https://github.com/AlexandreRouma/libfobos && cd libfobos && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
- name: Install more recent librtlsdr
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
- name: Prepare CMake
working-directory: ${{runner.workspace}}/build
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
- name: Build
working-directory: ${{runner.workspace}}/build
@ -186,7 +211,7 @@ jobs:
name: sdrpp_macos_arm
path: ${{runner.workspace}}/sdrpp_macos_arm.zip
build_debian_buster:
build_debian_buster_amd64:
runs-on: ubuntu-latest
steps:
@ -208,7 +233,29 @@ jobs:
name: sdrpp_debian_buster_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_bullseye:
build_debian_buster_aarch64:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- 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@v4
with:
name: sdrpp_debian_buster_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_bullseye_amd64:
runs-on: ubuntu-latest
steps:
@ -230,7 +277,29 @@ jobs:
name: sdrpp_debian_bullseye_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_bookworm:
build_debian_bullseye_aarch64:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- 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@v4
with:
name: sdrpp_debian_bullseye_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_bookworm_amd64:
runs-on: ubuntu-latest
steps:
@ -252,7 +321,29 @@ jobs:
name: sdrpp_debian_bookworm_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_sid:
build_debian_bookworm_aarch64:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_bookworm && 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@v4
with:
name: sdrpp_debian_bookworm_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_sid_amd64:
runs-on: ubuntu-latest
steps:
@ -274,7 +365,29 @@ jobs:
name: sdrpp_debian_sid_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_focal:
build_debian_sid_aarch64:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- 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@v4
with:
name: sdrpp_debian_sid_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_focal_amd64:
runs-on: ubuntu-latest
steps:
@ -296,7 +409,29 @@ jobs:
name: sdrpp_ubuntu_focal_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_jammy:
build_ubuntu_focal_aarch64:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- 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@v4
with:
name: sdrpp_ubuntu_focal_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_jammy_amd64:
runs-on: ubuntu-latest
steps:
@ -318,14 +453,14 @@ jobs:
name: sdrpp_ubuntu_jammy_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_mantic:
runs-on: ubuntu-latest
build_ubuntu_jammy_aarch64:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_mantic && docker build . --tag sdrpp_build
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_jammy && 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
@ -337,10 +472,10 @@ jobs:
- name: Save Deb Archive
uses: actions/upload-artifact@v4
with:
name: sdrpp_ubuntu_mantic_amd64
name: sdrpp_ubuntu_jammy_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_noble:
build_ubuntu_noble_amd64:
runs-on: ubuntu-latest
steps:
@ -362,6 +497,72 @@ jobs:
name: sdrpp_ubuntu_noble_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_noble_aarch64:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_noble && 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@v4
with:
name: sdrpp_ubuntu_noble_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_oracular_amd64:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_oracular && 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@v4
with:
name: sdrpp_ubuntu_oracular_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_oracular_aarch64:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_oracular && 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@v4
with:
name: sdrpp_ubuntu_oracular_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_raspios_bullseye_armhf:
runs-on: ARM
@ -417,7 +618,29 @@ jobs:
path: ${{runner.workspace}}/sdrpp.apk
create_full_archive:
needs: ['build_windows', 'build_macos_intel', 'build_macos_arm', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_bookworm', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_ubuntu_mantic', 'build_ubuntu_noble', 'build_raspios_bullseye_armhf', 'build_android']
needs: [
'build_windows',
'build_macos_intel',
'build_macos_arm',
'build_debian_buster_amd64',
'build_debian_buster_aarch64',
'build_debian_bullseye_amd64',
'build_debian_bullseye_aarch64',
'build_debian_bookworm_amd64',
'build_debian_bookworm_aarch64',
'build_debian_sid_amd64',
'build_debian_sid_aarch64',
'build_ubuntu_focal_amd64',
'build_ubuntu_focal_aarch64',
'build_ubuntu_jammy_amd64',
'build_ubuntu_jammy_aarch64',
'build_ubuntu_noble_amd64',
'build_ubuntu_noble_aarch64',
'build_ubuntu_oracular_amd64',
'build_ubuntu_oracular_aarch64',
'build_raspios_bullseye_armhf',
'build_android'
]
runs-on: ubuntu-latest
steps:
@ -431,13 +654,21 @@ jobs:
mv sdrpp_macos_intel/sdrpp_macos_intel.zip sdrpp_all/ &&
mv sdrpp_macos_arm/sdrpp_macos_arm.zip sdrpp_all/ &&
mv sdrpp_debian_buster_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_buster_amd64.deb &&
mv sdrpp_debian_buster_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_buster_aarch64.deb &&
mv sdrpp_debian_bullseye_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bullseye_amd64.deb &&
mv sdrpp_debian_bullseye_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bullseye_aarch64.deb &&
mv sdrpp_debian_bookworm_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bookworm_amd64.deb &&
mv sdrpp_debian_bookworm_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bookworm_aarch64.deb &&
mv sdrpp_debian_sid_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_amd64.deb &&
mv sdrpp_debian_sid_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_aarch64.deb &&
mv sdrpp_ubuntu_focal_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_amd64.deb &&
mv sdrpp_ubuntu_focal_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_aarch64.deb &&
mv sdrpp_ubuntu_jammy_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_amd64.deb &&
mv sdrpp_ubuntu_mantic_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_mantic_amd64.deb &&
mv sdrpp_ubuntu_jammy_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_aarch64.deb &&
mv sdrpp_ubuntu_noble_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_noble_amd64.deb &&
mv sdrpp_ubuntu_noble_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_noble_aarch64.deb &&
mv sdrpp_ubuntu_oracular_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_oracular_amd64.deb &&
mv sdrpp_ubuntu_oracular_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_oracular_aarch64.deb &&
mv sdrpp_raspios_bullseye_armhf/sdrpp_debian_armhf.deb sdrpp_all/sdrpp_raspios_bullseye_armhf.deb &&
mv sdrpp_android/sdrpp.apk sdrpp_all/sdrpp.apk

4
.gitignore vendored
View File

@ -17,3 +17,7 @@ m17_decoder/libcorrect
SDR++.app
android/deps
android/app/assets
source_modules/dragonlabs_source
source_modules/badgesdr_source
decoder_modules/mystery_decoder
decoder_modules/tetra_decoder

View File

@ -15,8 +15,12 @@ option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)
option(OPT_BUILD_BADGESDR_SOURCE "Build BadgeSDR Source Module (Dependencies: libusb)" OFF)
option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" OFF)
option(OPT_BUILD_FILE_SOURCE "Wav file source" ON)
option(OPT_BUILD_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: libfobos)" OFF)
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
option(OPT_BUILD_HAROGIC_SOURCE "Build Harogic Source Module (Dependencies: htra_api)" OFF)
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
option(OPT_BUILD_HYDRASDR_SOURCE "Build HydraSDR Source Module (Dependencies: libhydrasdr)" OFF)
option(OPT_BUILD_KCSDR_SOURCE "Build KCSDR Source Module (Dependencies: libkcsdr)" OFF)
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON)
option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF)
@ -42,12 +46,15 @@ option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: port
# Decoders
option(OPT_BUILD_ATV_DECODER "Build ATV decoder (no dependencies required)" OFF)
option(OPT_BUILD_DAB_DECODER "Build the DAB/DAB+ decoder (no dependencies required)" OFF)
option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF)
option(OPT_BUILD_KG_SSTV_DECODER "Build the KG SSTV (KG-STV) decoder module (no dependencies required)" OFF)
option(OPT_BUILD_M17_DECODER "Build the M17 decoder module (Dependencies: codec2)" OFF)
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
option(OPT_BUILD_PAGER_DECODER "Build the pager decoder module (no dependencies required)" ON)
option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
option(OPT_BUILD_RYFI_DECODER "RyFi data link decoder" OFF)
option(OPT_BUILD_VOR_RECEIVER "VOR beacon receiver" OFF)
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF)
# Misc
@ -63,6 +70,7 @@ option(OPT_BUILD_SCHEDULER "Build the scheduler" OFF)
# Other options
option(USE_INTERNAL_LIBCORRECT "Use an internal version of libcorrect" ON)
option(USE_BUNDLE_DEFAULTS "Set the default resource and module directories to the right ones for a MacOS .app" OFF)
option(COPY_MSVC_REDISTRIBUTABLES "Copy over the Visual C++ Redistributable" OFF)
# Module cmake path
set(SDRPP_MODULE_CMAKE "${CMAKE_SOURCE_DIR}/sdrpp_module.cmake")
@ -139,14 +147,30 @@ if (OPT_BUILD_FILE_SOURCE)
add_subdirectory("source_modules/file_source")
endif (OPT_BUILD_FILE_SOURCE)
if (OPT_BUILD_FOBOSSDR_SOURCE)
add_subdirectory("source_modules/fobossdr_source")
endif (OPT_BUILD_FOBOSSDR_SOURCE)
if (OPT_BUILD_HACKRF_SOURCE)
add_subdirectory("source_modules/hackrf_source")
endif (OPT_BUILD_HACKRF_SOURCE)
if (OPT_BUILD_HAROGIC_SOURCE)
add_subdirectory("source_modules/harogic_source")
endif (OPT_BUILD_HAROGIC_SOURCE)
if (OPT_BUILD_HERMES_SOURCE)
add_subdirectory("source_modules/hermes_source")
endif (OPT_BUILD_HERMES_SOURCE)
if (OPT_BUILD_HYDRASDR_SOURCE)
add_subdirectory("source_modules/hydrasdr_source")
endif (OPT_BUILD_HYDRASDR_SOURCE)
if (OPT_BUILD_KCSDR_SOURCE)
add_subdirectory("source_modules/kcsdr_source")
endif (OPT_BUILD_KCSDR_SOURCE)
if (OPT_BUILD_LIMESDR_SOURCE)
add_subdirectory("source_modules/limesdr_source")
endif (OPT_BUILD_LIMESDR_SOURCE)
@ -179,6 +203,10 @@ if (OPT_BUILD_RTL_TCP_SOURCE)
add_subdirectory("source_modules/rtl_tcp_source")
endif (OPT_BUILD_RTL_TCP_SOURCE)
if (OPT_BUILD_SDDC_SOURCE)
add_subdirectory("source_modules/sddc_source")
endif (OPT_BUILD_SDDC_SOURCE)
if (OPT_BUILD_SDRPP_SERVER_SOURCE)
add_subdirectory("source_modules/sdrpp_server_source")
endif (OPT_BUILD_SDRPP_SERVER_SOURCE)
@ -235,6 +263,10 @@ if (OPT_BUILD_ATV_DECODER)
add_subdirectory("decoder_modules/atv_decoder")
endif (OPT_BUILD_ATV_DECODER)
if (OPT_BUILD_DAB_DECODER)
add_subdirectory("decoder_modules/dab_decoder")
endif (OPT_BUILD_DAB_DECODER)
if (OPT_BUILD_FALCON9_DECODER)
add_subdirectory("decoder_modules/falcon9_decoder")
endif (OPT_BUILD_FALCON9_DECODER)
@ -259,6 +291,14 @@ if (OPT_BUILD_RADIO)
add_subdirectory("decoder_modules/radio")
endif (OPT_BUILD_RADIO)
if (OPT_BUILD_RYFI_DECODER)
add_subdirectory("decoder_modules/ryfi_decoder")
endif (OPT_BUILD_RYFI_DECODER)
if (OPT_BUILD_VOR_RECEIVER)
add_subdirectory("decoder_modules/vor_receiver")
endif (OPT_BUILD_VOR_RECEIVER)
if (OPT_BUILD_WEATHER_SAT_DECODER)
add_subdirectory("decoder_modules/weather_sat_decoder")
endif (OPT_BUILD_WEATHER_SAT_DECODER)
@ -312,6 +352,21 @@ target_compile_options(sdrpp PRIVATE ${SDRPP_COMPILER_FLAGS})
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_volk ALL xcopy /s \"C:/Program Files/PothosSDR/bin\\volk.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
if (COPY_MSVC_REDISTRIBUTABLES)
# Get the list of Visual C++ runtime DLLs
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP True)
include(InstallRequiredSystemLibraries)
# Create a space sperated list
set(REDIST_DLLS_STR "")
foreach(DLL IN LISTS CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
set(REDIST_DLLS_STR COMMAND xcopy /F \"${DLL}\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y ${REDIST_DLLS_STR})
endforeach()
# Create target
add_custom_target(do_always_msvc ALL ${REDIST_DLLS_STR})
endif ()
endif ()

View File

@ -10,7 +10,7 @@ android {
minSdkVersion 28
targetSdkVersion 28
versionCode 1
versionName "1.2.0"
versionName "1.2.1"
externalNativeBuild {
cmake {

View File

@ -99,6 +99,9 @@ namespace backend {
glfwWindowHint(GLFW_CLIENT_API, OPENGL_VERSIONS_IS_ES[i] ? GLFW_OPENGL_ES_API : GLFW_OPENGL_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_VERSIONS_MAJOR[i]);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_VERSIONS_MINOR[i]);
#if GLFW_VERSION_MAJOR > 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 4)
glfwWindowHintString(GLFW_WAYLAND_APP_ID, "sdrpp");
#endif
// Create window with graphics context
monitor = glfwGetPrimaryMonitor();

View File

@ -147,12 +147,12 @@ int sdrpp_main(int argc, char* argv[]) {
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]["name"] = "Frequency Manager";
defConfig["menuElements"][4]["open"] = true;
defConfig["menuElements"][5]["name"] = "VFO Color";
defConfig["menuElements"][5]["open"] = true;
defConfig["menuElements"][6]["name"] = "Band Plan";
defConfig["menuElements"][6]["open"] = true;
@ -173,16 +173,26 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["moduleInstances"]["BladeRF Source"]["enabled"] = true;
defConfig["moduleInstances"]["File Source"]["module"] = "file_source";
defConfig["moduleInstances"]["File Source"]["enabled"] = true;
defConfig["moduleInstances"]["FobosSDR Source"]["module"] = "fobossdr_source";
defConfig["moduleInstances"]["FobosSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["HackRF Source"]["module"] = "hackrf_source";
defConfig["moduleInstances"]["HackRF Source"]["enabled"] = true;
defConfig["moduleInstances"]["Harogic Source"]["module"] = "harogic_source";
defConfig["moduleInstances"]["Harogic Source"]["enabled"] = true;
defConfig["moduleInstances"]["Hermes Source"]["module"] = "hermes_source";
defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true;
defConfig["moduleInstances"]["HydraSDR Source"]["module"] = "hydrasdr_source";
defConfig["moduleInstances"]["HydraSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source";
defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["Network Source"]["module"] = "network_source";
defConfig["moduleInstances"]["Network Source"]["enabled"] = true;
defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["RFNM Source"]["module"] = "rfnm_source";
defConfig["moduleInstances"]["RFNM Source"]["enabled"] = true;
defConfig["moduleInstances"]["RFspace Source"]["module"] = "rfspace_source";
defConfig["moduleInstances"]["RFspace Source"]["enabled"] = true;
defConfig["moduleInstances"]["RTL-SDR Source"]["module"] = "rtl_sdr_source";
@ -193,8 +203,12 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["moduleInstances"]["SDRplay Source"]["enabled"] = true;
defConfig["moduleInstances"]["SDR++ Server Source"]["module"] = "sdrpp_server_source";
defConfig["moduleInstances"]["SDR++ Server Source"]["enabled"] = true;
defConfig["moduleInstances"]["Spectran HTTP Source"]["module"] = "spectran_http_source";
defConfig["moduleInstances"]["Spectran HTTP Source"]["enabled"] = true;
defConfig["moduleInstances"]["SpyServer Source"]["module"] = "spyserver_source";
defConfig["moduleInstances"]["SpyServer Source"]["enabled"] = true;
defConfig["moduleInstances"]["USRP Source"]["module"] = "usrp_source";
defConfig["moduleInstances"]["USRP Source"]["enabled"] = true;
defConfig["moduleInstances"]["Audio Sink"] = "audio_sink";
defConfig["moduleInstances"]["Network Sink"] = "network_sink";
@ -220,12 +234,19 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["modules"] = json::array();
defConfig["offsetMode"] = (int)0; // Off
defConfig["offset"] = 0.0;
defConfig["offsets"]["SpyVerter"] = 120000000.0;
defConfig["offsets"]["Ham-It-Up"] = 125000000.0;
defConfig["offsets"]["MMDS S-band (1998MHz)"] = -1998000000.0;
defConfig["offsets"]["DK5AV X-Band"] = -6800000000.0;
defConfig["offsets"]["Ku LNB (9750MHz)"] = -9750000000.0;
defConfig["offsets"]["Ku LNB (10700MHz)"] = -10700000000.0;
defConfig["selectedOffset"] = "None";
defConfig["manualOffset"] = 0.0;
defConfig["showMenu"] = true;
defConfig["showWaterfall"] = true;
defConfig["source"] = "";
defConfig["decimationPower"] = 0;
defConfig["decimation"] = 1;
defConfig["iqCorrection"] = false;
defConfig["invertIQ"] = false;
@ -306,12 +327,18 @@ int sdrpp_main(int argc, char* argv[]) {
// Remove unused elements
auto items = core::configManager.conf.items();
auto newConf = core::configManager.conf;
bool configCorrected = false;
for (auto const& item : items) {
if (!defConfig.contains(item.key())) {
flog::info("Unused key in config {0}, repairing", item.key());
core::configManager.conf.erase(item.key());
newConf.erase(item.key());
configCorrected = true;
}
}
if (configCorrected) {
core::configManager.conf = newConf;
}
// Update to new module representation in config if needed
for (auto [_name, inst] : core::configManager.conf["moduleInstances"].items()) {

View File

@ -37,9 +37,12 @@ namespace sdrpp_credits {
const char* hardwareDonators[] = {
"Aaronia AG",
"Airspy",
"Alex 4Z5LV",
"Analog Devices",
"CaribouLabs",
"Deepace",
"Ettus Research",
"Harogic",
"Howard Su",
"MicroPhase",
"Microtelecom",
@ -47,6 +50,7 @@ namespace sdrpp_credits {
"Nuand",
"RFNM",
"RFspace",
"RigExpert",
"RTL-SDRblog",
"SDRplay"
};
@ -66,15 +70,18 @@ namespace sdrpp_credits {
"Flinger Films",
"Frank Werner (HB9FXQ)",
"gringogrigio",
"Jandro",
"Jeff Moe",
"Joe Cupano",
"KD1SQ",
"Kezza",
"Krys Kamieniecki",
"Lee Donaghy",
"Lee KD1SQ",
"Lee (KD1SQ)",
".lozenge. (Hank Hill)",
"Martin Herren (HB9FXX)",
"NeoVilsonWong",
"Nitin (VU2JEK)",
"ON4MU",
"Passion-Radio.com",
"Paul Maine",

View File

@ -1,5 +1,6 @@
#pragma once
#include <volk/volk.h>
#include <string.h>
namespace dsp::buffer {
template<class T>

View File

@ -1,5 +1,6 @@
#pragma once
#include <volk/volk.h>
#include "../buffer/buffer.h"
namespace dsp {
template<class T>

View File

@ -5,113 +5,120 @@
#include <gui/main_window.h>
#include <gui/style.h>
#include <signal_path/signal_path.h>
#include <utils/optionlist.h>
#include <gui/dialogs/dialog_box.h>
namespace sourcemenu {
int offsetMode = 0;
int sourceId = 0;
double customOffset = 0.0;
double effectiveOffset = 0.0;
int decimationPower = 0;
EventHandler<std::string> sourcesChangedHandler;
EventHandler<std::string> sourceUnregisterHandler;
OptionList<std::string, std::string> sources;
std::string selectedSource;
int decimId = 0;
OptionList<int, int> decimations;
bool iqCorrection = false;
bool invertIQ = false;
EventHandler<std::string> sourceRegisteredHandler;
EventHandler<std::string> sourceUnregisterHandler;
EventHandler<std::string> sourceUnregisteredHandler;
int offsetId = 0;
double manualOffset = 0.0;
std::string selectedOffset;
double effectiveOffset = 0.0;
OptionList<std::string, double> offsets;
std::map<std::string, double> namedOffsets;
std::vector<std::string> sourceNames;
std::string sourceNamesTxt;
std::string selectedSource;
bool showAddOffsetDialog = false;
char newOffsetName[1024];
double newOffset = 0.0;
bool showDelOffsetDialog = false;
std::string delOffsetName = "";
// Offset IDs
enum {
OFFSET_MODE_NONE,
OFFSET_MODE_CUSTOM,
OFFSET_MODE_SPYVERTER,
OFFSET_MODE_HAM_IT_UP,
OFFSET_MODE_MMDS_SB_1998,
OFFSET_MODE_DK5AV_XB,
OFFSET_MODE_KU_LNB_9750,
OFFSET_MODE_KU_LNB_10700,
_OFFSET_MODE_COUNT
OFFSET_ID_NONE,
OFFSET_ID_MANUAL,
OFFSET_ID_CUSTOM_BASE
};
const char* offsetModesTxt = "None\0"
"Custom\0"
"SpyVerter\0"
"Ham-It-Up\0"
"MMDS S-band (1998MHz)\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_MMDS_SB_1998) {
effectiveOffset = -1998000000;
} // 1.998GHz Down-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 {
// Compute the effective offset
switch (offsetId) {
case OFFSET_ID_NONE:
effectiveOffset = 0;
break;
case OFFSET_ID_MANUAL:
effectiveOffset = manualOffset;
break;
default:
effectiveOffset = namedOffsets[offsets.name(offsetId)];
break;
}
// Apply it
sigpath::sourceManager.setTuningOffset(effectiveOffset);
}
void selectOffsetById(int id) {
// Update the offset mode
offsetId = id;
selectedOffset = offsets.name(id);
// Update the offset
updateOffset();
}
void selectOffsetByName(const std::string& name) {
// If the name doesn't exist, select 'None'
if (!offsets.nameExists(name)) {
selectOffsetById(OFFSET_ID_NONE);
return;
}
// Select using the ID associated with the name
selectOffsetById(offsets.nameId(name));
}
void refreshSources() {
sourceNames = sigpath::sourceManager.getSourceNames();
sourceNamesTxt.clear();
// Get sources
auto sourceNames = sigpath::sourceManager.getSourceNames();
// Define source options
sources.clear();
for (auto name : sourceNames) {
sourceNamesTxt += name;
sourceNamesTxt += '\0';
sources.define(name, name, name);
}
}
void selectSource(std::string name) {
if (sourceNames.empty()) {
// If there is no source, give up
if (sources.empty()) {
sourceId = 0;
selectedSource.clear();
return;
}
auto it = std::find(sourceNames.begin(), sourceNames.end(), name);
if (it == sourceNames.end()) {
selectSource(sourceNames[0]);
// If a source with the given name doesn't exist, select the first source instead
if (!sources.valueExists(name)) {
selectSource(sources.value(0));
return;
}
sourceId = std::distance(sourceNames.begin(), it);
selectedSource = sourceNames[sourceId];
sigpath::sourceManager.selectSource(sourceNames[sourceId]);
// Update the GUI variables
sourceId = sources.valueId(name);
selectedSource = name;
// Select the source module
sigpath::sourceManager.selectSource(name);
}
void onSourceRegistered(std::string name, void* ctx) {
void onSourcesChanged(std::string name, void* ctx) {
// Update the source list
refreshSources();
if (selectedSource.empty()) {
sourceId = 0;
selectSource(sourceNames[0]);
return;
}
sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
// Reselect the current source
selectSource(selectedSource);
}
void onSourceUnregister(std::string name, void* ctx) {
@ -120,60 +127,173 @@ namespace sourcemenu {
// TODO: Stop everything
}
void onSourceUnregistered(std::string name, void* ctx) {
refreshSources();
void reloadOffsets() {
// Clear list
offsets.clear();
namedOffsets.clear();
if (sourceNames.empty()) {
selectedSource = "";
return;
// Define special offset modes
offsets.define("None", OFFSET_ID_NONE);
offsets.define("Manual", OFFSET_ID_MANUAL);
// Acquire the config file
core::configManager.acquire();
// Load custom offsets
auto ofs = core::configManager.conf["offsets"].items();
for (auto& o : ofs) {
namedOffsets[o.key()] = (double)o.value();
}
if (name == selectedSource) {
sourceId = std::clamp<int>(sourceId, 0, sourceNames.size() - 1);
selectSource(sourceNames[sourceId]);
return;
// Define custom offsets
for (auto& [name, offset] : namedOffsets) {
offsets.define(name, offsets.size());
}
sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
// Release the config file
core::configManager.release();
}
void init() {
// Load offset modes
reloadOffsets();
// Define decimation values
decimations.define(1, "None", 1);
decimations.define(2, "2x", 2);
decimations.define(4, "4x", 4);
decimations.define(8, "8x", 8);
decimations.define(16, "16x", 16);
decimations.define(32, "32x", 32);
decimations.define(64, "64x", 64);
// Acquire the config file
core::configManager.acquire();
std::string selected = core::configManager.conf["source"];
customOffset = core::configManager.conf["offset"];
offsetMode = core::configManager.conf["offsetMode"];
decimationPower = core::configManager.conf["decimationPower"];
// Load other settings
std::string selectedSource = core::configManager.conf["source"];
manualOffset = core::configManager.conf["manualOffset"];
std::string selectedOffset = core::configManager.conf["selectedOffset"];
iqCorrection = core::configManager.conf["iqCorrection"];
invertIQ = core::configManager.conf["invertIQ"];
int decimation = core::configManager.conf["decimation"];
if (decimations.keyExists(decimation)) {
decimId = decimations.keyId(decimation);
}
// Release the config file
core::configManager.release();
// Select the source module
refreshSources();
selectSource(selectedSource);
// Update frontend settings
sigpath::iqFrontEnd.setDCBlocking(iqCorrection);
sigpath::iqFrontEnd.setInvertIQ(invertIQ);
updateOffset();
sigpath::iqFrontEnd.setDecimation(decimations.value(decimId));
selectOffsetByName(selectedOffset);
refreshSources();
selectSource(selected);
sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
sourceRegisteredHandler.handler = onSourceRegistered;
// Register handlers
sourcesChangedHandler.handler = onSourcesChanged;
sourceUnregisterHandler.handler = onSourceUnregister;
sourceUnregisteredHandler.handler = onSourceUnregistered;
sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler);
sigpath::sourceManager.onSourceRegistered.bindHandler(&sourcesChangedHandler);
sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler);
sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler);
sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourcesChangedHandler);
}
core::configManager.release();
void addOffset(const std::string& name, double offset) {
// Acquire the config file
core::configManager.acquire();
// Define a new offset
core::configManager.conf["offsets"][name] = offset;
// Acquire the config file
core::configManager.release(true);
// Reload the offsets
reloadOffsets();
// Attempt to re-select the same one
selectOffsetByName(selectedOffset);
}
void delOffset(const std::string& name) {
// Acquire the config file
core::configManager.acquire();
// Define a new offset
core::configManager.conf["offsets"].erase(name);
// Acquire the config file
core::configManager.release(true);
// Reload the offsets
reloadOffsets();
// Attempt to re-select the same one
selectOffsetByName(selectedOffset);
}
bool addOffsetDialog() {
bool open = true;
gui::mainWindow.lockWaterfallControls = true;
float menuWidth = ImGui::GetContentRegionAvail().x;
const char* id = "Add offset##sdrpp_add_offset_dialog_";
ImGui::OpenPopup(id);
if (ImGui::BeginPopup(id, ImGuiWindowFlags_NoResize)) {
ImGui::LeftLabel("Name");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
ImGui::InputText("##sdrpp_add_offset_name", newOffsetName, 1023);
ImGui::LeftLabel("Offset");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
ImGui::InputDouble("##sdrpp_add_offset_offset", &newOffset);
bool nameExists = offsets.nameExists(newOffsetName);
bool reservedName = !strcmp(newOffsetName, "None") || !strcmp(newOffsetName, "Manual");
bool denyApply = !newOffsetName[0] || nameExists || reservedName;
if (nameExists) {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "An offset with the given name already exists.");
}
else if (reservedName) {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "The given name is reserved.");
}
if (denyApply) { style::beginDisabled(); }
if (ImGui::Button("Apply")) {
addOffset(newOffsetName, newOffset);
open = false;
}
if (denyApply) { style::endDisabled(); }
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
open = false;
}
ImGui::EndPopup();
}
return open;
}
void draw(void* ctx) {
float itemWidth = ImGui::GetContentRegionAvail().x;
float lineHeight = ImGui::GetTextLineHeightWithSpacing();
float spacing = lineHeight - ImGui::GetTextLineHeight();
bool running = gui::mainWindow.sdrIsRunning();
if (running) { style::beginDisabled(); }
ImGui::SetNextItemWidth(itemWidth);
if (ImGui::Combo("##source", &sourceId, sourceNamesTxt.c_str())) {
selectSource(sourceNames[sourceId]);
if (ImGui::Combo("##source", &sourceId, sources.txt)) {
std::string newSource = sources.value(sourceId);
selectSource(newSource);
core::configManager.acquire();
core::configManager.conf["source"] = sourceNames[sourceId];
core::configManager.conf["source"] = newSource;
core::configManager.release(true);
}
@ -196,21 +316,45 @@ namespace sourcemenu {
}
ImGui::LeftLabel("Offset mode");
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
if (ImGui::Combo("##_sdrpp_offset_mode", &offsetMode, offsetModesTxt)) {
updateOffset();
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX() - 2.0f*(lineHeight + 1.5f*spacing));
if (ImGui::Combo("##_sdrpp_offset", &offsetId, offsets.txt)) {
selectOffsetById(offsetId);
core::configManager.acquire();
core::configManager.conf["offsetMode"] = offsetMode;
core::configManager.conf["selectedOffset"] = offsets.key(offsetId);
core::configManager.release(true);
}
ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing);
if (offsetId < OFFSET_ID_CUSTOM_BASE) { ImGui::BeginDisabled(); }
if (ImGui::Button("-##_sdrpp_offset_del_", ImVec2(lineHeight + 0.5f*spacing, 0))) {
delOffsetName = selectedOffset;
showDelOffsetDialog = true;
}
if (offsetId < OFFSET_ID_CUSTOM_BASE) { ImGui::EndDisabled(); }
ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing);
if (ImGui::Button("+##_sdrpp_offset_add_", ImVec2(lineHeight + 0.5f*spacing, 0))) {
strcpy(newOffsetName, "New Offset");
showAddOffsetDialog = true;
}
// Offset delete confirmation
if (ImGui::GenericDialog("sdrpp_del_offset_confirm", showDelOffsetDialog, GENERIC_DIALOG_BUTTONS_YES_NO, []() {
ImGui::Text("Deleting offset named \"%s\". Are you sure?", delOffsetName.c_str());
}) == GENERIC_DIALOG_BUTTON_YES) {
delOffset(delOffsetName);
}
// Offset add diaglog
if (showAddOffsetDialog) { showAddOffsetDialog = addOffsetDialog(); }
ImGui::LeftLabel("Offset");
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
if (offsetMode == OFFSET_MODE_CUSTOM) {
if (ImGui::InputDouble("##freq_offset", &customOffset, 1.0, 100.0)) {
ImGui::FillWidth();
if (offsetId == OFFSET_ID_MANUAL) {
if (ImGui::InputDouble("##freq_offset", &manualOffset, 1.0, 100.0)) {
updateOffset();
core::configManager.acquire();
core::configManager.conf["offset"] = customOffset;
core::configManager.conf["manualOffset"] = manualOffset;
core::configManager.release(true);
}
}
@ -222,11 +366,11 @@ namespace sourcemenu {
if (running) { style::beginDisabled(); }
ImGui::LeftLabel("Decimation");
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
if (ImGui::Combo("##source_decim", &decimationPower, decimationStages)) {
sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
ImGui::FillWidth();
if (ImGui::Combo("##source_decim", &decimId, decimations.txt)) {
sigpath::iqFrontEnd.setDecimation(decimations.value(decimId));
core::configManager.acquire();
core::configManager.conf["decimationPower"] = decimationPower;
core::configManager.conf["decimation"] = decimations.key(decimId);
core::configManager.release(true);
}
if (running) { style::endDisabled(); }

View File

@ -3,6 +3,7 @@
#include <gui/style.h>
#include <gui/gui.h>
#include <backend.h>
#include <utils/hrfreq.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
@ -90,6 +91,7 @@ void FrequencySelect::moveCursorToDigit(int i) {
void FrequencySelect::draw() {
auto window = ImGui::GetCurrentWindow();
auto io = ImGui::GetIO();
widgetPos = ImGui::GetWindowContentRegionMin();
ImVec2 cursorPos = ImGui::GetCursorPos();
widgetPos.x += window->Pos.x + cursorPos.x;
@ -132,7 +134,7 @@ void FrequencySelect::draw() {
ImVec2 mousePos = ImGui::GetMousePos();
bool leftClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
bool rightClick = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
int mw = ImGui::GetIO().MouseWheel;
int mw = io.MouseWheel;
bool onDigit = false;
bool hovered = false;
@ -174,7 +176,7 @@ void FrequencySelect::draw() {
moveCursorToDigit(i + 1);
}
auto chars = ImGui::GetIO().InputQueueCharacters;
auto chars = io.InputQueueCharacters;
// For each keyboard characters, type it
for (int j = 0; j < chars.Size; j++) {
@ -194,6 +196,34 @@ void FrequencySelect::draw() {
}
}
digitHovered = hovered;
if (isInArea(mousePos, digitTopMins[0], digitBottomMaxs[11])) {
bool shortcutKey = io.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl);
bool ctrlOnly = (io.KeyMods == ImGuiKeyModFlags_Ctrl);
bool shiftOnly = (io.KeyMods == ImGuiKeyModFlags_Shift);
bool copy = ((shortcutKey && ImGui::IsKeyPressed(ImGuiKey_C)) || (ctrlOnly && ImGui::IsKeyPressed(ImGuiKey_Insert)));
bool paste = ((shortcutKey && ImGui::IsKeyPressed(ImGuiKey_V)) || (shiftOnly && ImGui::IsKeyPressed(ImGuiKey_Insert)));
if (copy) {
// Convert the freqency to a string
std::string freqStr = hrfreq::toString(frequency);
// Write it to the clipboard
ImGui::SetClipboardText(freqStr.c_str());
}
if (paste) {
// Attempt to parse the clipboard as a number
const char* clip = ImGui::GetClipboardText();
// If the clipboard is not empty, attempt to parse it
if (clip) {
double newFreq;
if (hrfreq::fromString(clip, newFreq)) {
setFrequency(abs(newFreq));
frequencyChanged = true;
}
}
}
}
}
uint64_t freq = 0;

View File

@ -85,7 +85,7 @@ void SourceManager::tune(double freq) {
return;
}
// TODO: No need to always retune the hardware in Panadapter mode
selectedHandler->tuneHandler(((tuneMode == TuningMode::NORMAL) ? freq : ifFreq) + tuneOffset, selectedHandler->ctx);
selectedHandler->tuneHandler(abs(((tuneMode == TuningMode::NORMAL) ? freq : ifFreq) + tuneOffset), selectedHandler->ctx);
onRetune.emit(freq);
currentFreq = freq;
}

120
core/src/utils/hrfreq.cpp Normal file
View File

@ -0,0 +1,120 @@
#include "hrfreq.h"
#include <utils/flog.h>
namespace hrfreq {
std::string toString(double freq) {
// Determine the scale
int maxDecimals = 0;
const char* suffix = "Hz";
if (freq >= 1e9) {
freq /= 1e9;
maxDecimals = 9;
suffix = "GHz";
}
else if (freq >= 1e6) {
freq /= 1e6;
maxDecimals = 6;
suffix = "MHz";
}
else if (freq >= 1e3) {
freq /= 1e3;
maxDecimals = 3;
suffix = "KHz";
}
// Convert to string (TODO: Not sure if limiting the decimals rounds)
char numBuf[128];
int numLen = sprintf(numBuf, "%0.*lf", maxDecimals, freq);
// If there is a decimal point, remove the useless zeros
if (maxDecimals) {
for (int i = numLen-1; i >= 0; i--) {
bool dot = (numBuf[i] == '.');
if (numBuf[i] != '0' && !dot) { break; }
numBuf[i] = 0;
if (dot) { break; }
}
}
// Concat the suffix
char finalBuf[128];
sprintf(finalBuf, "%s%s", numBuf, suffix);
// Return the final string
return finalBuf;
}
bool isNumeric(char c) {
return std::isdigit(c) || c == '+' || c == '-' || c == '.' || c == ',';
}
bool fromString(const std::string& str, double& freq) {
// Skip non-numeric characters
int i = 0;
char c;
for (; i < str.size(); i++) {
if (isNumeric(str[i])) { break; }
}
// Extract the numeric part
std::string numeric;
for (; i < str.size(); i++) {
// Get the character
c = str[i];
// If it's a letter, stop
if (std::isalpha(c)) { break; }
// If isn't numeric, skip it
if (!isNumeric(c)) { continue; }
// If it's a comma, skip it for now. This enforces a dot as a decimal point
if (c == ',') { continue; }
// Add the character to the numeric string
numeric += c;
}
// Attempt to parse the numeric part
double num;
try {
num = std::stod(numeric);
}
catch (const std::exception& e) {
flog::error("Failed to parse numeric part: '{}'", numeric);
return false;
}
// If no more text is available, assume the numeric part gives a frequency in Hz
if (i == str.size()) {
flog::warn("No unit given, assuming it's Hz");
freq = num;
return true;
}
// Scale the numeric value depending on the first scale character
char scale = std::toupper(str[i]);
switch (scale) {
case 'G':
num *= 1e9;
break;
case 'M':
num *= 1e6;
break;
case 'K':
num *= 1e3;
break;
case 'H':
break;
default:
flog::warn("Unknown frequency scale: '{}'", scale);
break;
}
// Return the frequency
freq = num;
return true; // TODO
}
}

19
core/src/utils/hrfreq.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <string>
namespace hrfreq {
/**
* Convert a frequency to a human-readable string.
* @param freq Frequency in Hz.
* @return Human-readable representation of the frequency.
*/
std::string toString(double freq);
/**
* Convert a human-readable representation of a frequency to a frequency value.
* @param str String containing the human-readable frequency.
* @param freq Value to write the decoded frequency to.
* @return True on success, false otherwise.
*/
bool fromString(const std::string& str, double& freq);
}

View File

@ -1,3 +1,3 @@
#pragma once
#define VERSION_STR "1.2.0"
#define VERSION_STR "1.2.1"

View File

@ -0,0 +1,65 @@
#pragma once
#include <dsp/processor.h>
#include <dsp/math/fast_atan2.h>
#include <dsp/math/hz_to_rads.h>
#include <dsp/math/normalize_phase.h>
namespace dsp::demod {
class Amplitude : public Processor<complex_t, float> {
using base_type = Processor<complex_t, float>;
public:
Amplitude() {}
Amplitude(stream<complex_t>* in, double deviation) { init(in, deviation); }
Amplitude(stream<complex_t>* in, double deviation, double samplerate) { init(in, deviation, samplerate); }
virtual void init(stream<complex_t>* in, double deviation) {
_invDeviation = 1.0 / deviation;
base_type::init(in);
}
virtual void init(stream<complex_t>* in, double deviation, double samplerate) {
init(in, math::hzToRads(deviation, samplerate));
}
void setDeviation(double deviation) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
_invDeviation = 1.0 / deviation;
}
void setDeviation(double deviation, double samplerate) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
_invDeviation = 1.0 / math::hzToRads(deviation, samplerate);
}
inline int process(int count, complex_t* in, float* out) {
volk_32fc_magnitude_32f(out, (lv_32fc_t*)in, count);
return count;
}
void reset() {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
phase = 0.0f;
}
int run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
process(count, base_type::_in->readBuf, base_type::out.writeBuf);
base_type::_in->flush();
if (!base_type::out.swap(count)) { return -1; }
return count;
}
protected:
float _invDeviation;
float phase = 0.0f;
};
}

View File

@ -0,0 +1,176 @@
617 + 6 -> I
304 + 6 -> II
616 + 6 -> III
305 + 6 -> IV
304
305
306
307
308
309
310
311
312
616
617
618
619
620
621
622
623
624
305
306
307
308
309
310
311
312
313
617
618
619
620
621
622
623
624
0
304
305
306
307
308
309
310
311
312
616
617
618
619
620
621
622
623
624
305
306
307
308
309
310
311
312
313
617
618
619
620
621
622
623
624
0
304
305
306
307
308
309
310
311
312
616
617
618
619
620
621
622
623
624
305
306
307
308
309
310
311
312
313
617
618
619
620
621
622
623
624
0
304
305
306
307
308
309
310
311
312
616
617
618
619
620
621
622
623
624
305
306
307
308
309
310
311
312
313
617
618
619
620
621
622
623
624
0
304
305
306
307
308
309
310
311
312
616
617
618
619
620
621
622
623
624

View File

@ -1,63 +0,0 @@
#pragma once
#include <dsp/loop/pll.h>
#include "chrominance_filter.h"
// TODO: Should be 60 but had to try something
#define BURST_START (63+CHROMA_FIR_DELAY)
#define BURST_END (BURST_START+28)
#define A_PHASE ((135.0/180.0)*FL_M_PI)
#define B_PHASE ((-135.0/180.0)*FL_M_PI)
namespace dsp::loop {
class ChromaPLL : public PLL {
using base_type = PLL;
public:
ChromaPLL() {}
ChromaPLL(stream<complex_t>* in, double bandwidth, double initPhase = 0.0, double initFreq = 0.0, double minFreq = -FL_M_PI, double maxFreq = FL_M_PI) {
base_type::init(in, bandwidth, initFreq, initPhase, minFreq, maxFreq);
}
inline int process(int count, complex_t* in, complex_t* out, bool aphase = false) {
// Process the pre-burst section
for (int i = 0; i < BURST_START; i++) {
out[i] = in[i] * math::phasor(-pcl.phase);
pcl.advancePhase();
}
// Process the burst itself
if (aphase) {
for (int i = BURST_START; i < BURST_END; i++) {
complex_t outVal = in[i] * math::phasor(-pcl.phase);
out[i] = outVal;
pcl.advance(math::normalizePhase(outVal.phase() - A_PHASE));
}
}
else {
for (int i = BURST_START; i < BURST_END; i++) {
complex_t outVal = in[i] * math::phasor(-pcl.phase);
out[i] = outVal;
pcl.advance(math::normalizePhase(outVal.phase() - B_PHASE));
}
}
// Process the post-burst section
for (int i = BURST_END; i < count; i++) {
out[i] = in[i] * math::phasor(-pcl.phase);
pcl.advancePhase();
}
return count;
}
inline int processBlank(int count, complex_t* in, complex_t* out) {
for (int i = 0; i < count; i++) {
out[i] = in[i] * math::phasor(-pcl.phase);
pcl.advancePhase();
}
return count;
}
};
}

View File

@ -1,239 +0,0 @@
#pragma once
#include <dsp/types.h>
inline const dsp::complex_t CHROMA_FIR[] = {
{-0.000005461290583903, -0.000011336784355655},
{ 0.000020060944485414, 0.000009851315045203},
{-0.000034177222729438, 0.000007245841504981},
{ 0.000027694034878705, -0.000033114740542635},
{-0.000001217597841648, 0.000039141482370942},
{-0.000008324593371228, -0.000011315001355976},
{-0.000038085228233509, -0.000010585909953738},
{ 0.000114833396071141, -0.000047778708840608},
{-0.000115428390169113, 0.000205816198882814},
{-0.000055467806072871, -0.000356692479491626},
{ 0.000349316846854190, 0.000326162940234916},
{-0.000558465829929114, -0.000048001521408724},
{ 0.000488176200631416, -0.000319593757302922},
{-0.000169437838021935, 0.000501610900725908},
{-0.000131793335799502, -0.000373003580727547},
{ 0.000166817395492786, 0.000105930895534474},
{ 0.000030499908326112, -0.000003048682668943},
{-0.000174999505027919, 0.000168008090089458},
{ 0.000054431163395030, -0.000385174790951272},
{ 0.000215876012859739, 0.000372695852521209},
{-0.000325534912280750, -0.000130173041693966},
{ 0.000154951430569290, -0.000045395998708328},
{ 0.000054324657659002, -0.000076028700470037},
{ 0.000015664427565764, 0.000348002612845696},
{-0.000345943017888332, -0.000402175417043307},
{ 0.000568731727879741, 0.000112347863435682},
{-0.000416485880859085, 0.000211750352828909},
{ 0.000087462353623011, -0.000188197153014309},
{-0.000032082305030264, -0.000136804226080664},
{ 0.000379089999045955, 0.000303466839685362},
{-0.000726760198519770, -0.000007022279302816},
{ 0.000619888661818195, -0.000476871323359809},
{-0.000151885493742993, 0.000595641190573181},
{-0.000100626407015494, -0.000227947144491108},
{-0.000201935458823941, -0.000107628631934340},
{ 0.000680260922139900, -0.000120771182888852},
{-0.000666108629277491, 0.000744775901128973},
{ 0.000067236591919755, -0.001044125966364420},
{ 0.000447037274751822, 0.000651912509450913},
{-0.000262675893448686, -0.000082499729563337},
{-0.000349821460486320, 0.000132102793530818},
{ 0.000507024815168287, -0.000837598610490618},
{ 0.000163814255478652, 0.001346530693477834},
{-0.000970457632383793, -0.000968411010101160},
{ 0.000974834882891140, 0.000116507082762032},
{-0.000225464280571542, 0.000137131865995708},
{-0.000211542240694642, 0.000563783548428947},
{-0.000414412310798766, -0.001309793399193736},
{ 0.001497010004594478, 0.001021907858926259},
{-0.001752019159639658, 0.000116536066154131},
{ 0.000872822027879430, -0.000783952720205569},
{-0.000032439446797970, 0.000184988059956734},
{ 0.000446259382722895, 0.000833040920509238},
{-0.001741577737284306, -0.000764423771425237},
{ 0.002306569133792772, -0.000593352416441601},
{-0.001336084746214192, 0.001744394557524181},
{-0.000015810020735495, -0.001342809547658260},
{ 0.000007636494885364, 0.000009498318627546},
{ 0.001403876768349702, 0.000326101441888391},
{-0.002351020828600226, 0.001098649819278302},
{ 0.001389314639579544, -0.002746943712072884},
{ 0.000526319899588909, 0.002635084366837732},
{-0.001109526585744687, -0.000950323796527721},
{-0.000307792427984886, -0.000013203419520794},
{ 0.001737955094951111, -0.001247368808692850},
{-0.000974502437588420, 0.003352512117661680},
{-0.001462571137390936, -0.003635296917435679},
{ 0.002783459090201693, 0.001604420226187745},
{-0.001471518558760170, 0.000211117948702137},
{-0.000575340825070194, 0.000601820846100026},
{ 0.000302090333345692, -0.003088058972305493},
{ 0.002496092353182990, 0.003912508340989065},
{-0.004645661091012423, -0.001630427298020200},
{ 0.003556824805628799, -0.001209822327859352},
{-0.000744999556260706, 0.001143238699138109},
{ 0.000144278726929409, 0.001638049051599065},
{-0.003025291044450178, -0.003226370992887968},
{ 0.006047866290490120, 0.000927406808799887},
{-0.005338456415106141, 0.003008811999350399},
{ 0.001642959659014839, -0.003972384205231079},
{ 0.000273874932822212, 0.000977326273749033},
{ 0.002315022846573390, 0.001695671268241410},
{-0.006240953957978884, 0.000207330368698293},
{ 0.006164252120861735, -0.005177351717451013},
{-0.001560310257561104, 0.007437030759707700},
{-0.002131333814462852, -0.004317129694157112},
{ 0.000280518918541908, 0.000134405998842553},
{ 0.004612116481180659, -0.001024468120657814},
{-0.005599300279638699, 0.006828277067771868},
{ 0.000228879728552504, -0.010675998154712657},
{ 0.005692081512980654, 0.007582243186569848},
{-0.005100500569859509, -0.001364751685737153},
{-0.000902490398043454, 0.000385770160220703},
{ 0.003673858819546609, -0.006701685283451640},
{ 0.002079056046131593, 0.012568579063417429},
{-0.010730008156911677, -0.009826454574016218},
{ 0.012092401380903161, 0.000921764172237851},
{-0.004714530989129091, 0.003151948807627123},
{-0.001055930168838909, 0.003228576712467020},
{-0.004343270165991213, -0.011924332879354394},
{ 0.016499994418955999, 0.010255324919126899},
{-0.021047239750251585, 0.002309419513135448},
{ 0.011855513874047341, -0.011604071033866310},
{-0.000777842281358575, 0.005916341648175263},
{ 0.004380939277688377, 0.007397670455730446},
{-0.021891594662401131, -0.008509480947490166},
{ 0.032787638290674201, -0.009950745850861956},
{-0.021022579272463194, 0.030030850567389102},
{-0.001508145650189953, -0.027571914870304640},
{ 0.004056649693022923, 0.004624901687718579},
{ 0.025728742586666287, 0.004824671348397606},
{-0.058002700931665603, 0.030198618296813803},
{ 0.043631619628438784, -0.096308304333327280},
{ 0.033451363423624300, 0.136687079396426990},
{-0.129387018420204200, -0.101540513046619400},
{ 0.172881344826560730, -0.000000000000005297},
{-0.129387018420198010, 0.101540513046627330},
{ 0.033451363423615862, -0.136687079396429050},
{ 0.043631619628444723, 0.096308304333324601},
{-0.058002700931667456, -0.030198618296810247},
{ 0.025728742586665992, -0.004824671348399184},
{ 0.004056649693022639, -0.004624901687718827},
{-0.001508145650188251, 0.027571914870304734},
{-0.021022579272465047, -0.030030850567387805},
{ 0.032787638290674812, 0.009950745850859947},
{-0.021891594662400610, 0.008509480947491507},
{ 0.004380939277687923, -0.007397670455730714},
{-0.000777842281358940, -0.005916341648175215},
{ 0.011855513874048058, 0.011604071033865578},
{-0.021047239750251731, -0.002309419513134139},
{ 0.016499994418955360, -0.010255324919127926},
{-0.004343270165990471, 0.011924332879354665},
{-0.001055930168839110, -0.003228576712466955},
{-0.004714530989129287, -0.003151948807626830},
{ 0.012092401380903103, -0.000921764172238603},
{-0.010730008156911072, 0.009826454574016881},
{ 0.002079056046130817, -0.012568579063417559},
{ 0.003673858819547020, 0.006701685283451416},
{-0.000902490398043478, -0.000385770160220647},
{-0.005100500569859424, 0.001364751685737466},
{ 0.005692081512980187, -0.007582243186570198},
{ 0.000228879728553163, 0.010675998154712643},
{-0.005599300279639117, -0.006828277067771524},
{ 0.004612116481180722, 0.001024468120657532},
{ 0.000280518918541900, -0.000134405998842571},
{-0.002131333814462586, 0.004317129694157243},
{-0.001560310257561563, -0.007437030759707604},
{ 0.006164252120862052, 0.005177351717450635},
{-0.006240953957978898, -0.000207330368697911},
{ 0.002315022846573286, -0.001695671268241552},
{ 0.000273874932822152, -0.000977326273749050},
{ 0.001642959659015084, 0.003972384205230976},
{-0.005338456415106324, -0.003008811999350072},
{ 0.006047866290490063, -0.000927406808800258},
{-0.003025291044449980, 0.003226370992888153},
{ 0.000144278726929308, -0.001638049051599074},
{-0.000744999556260777, -0.001143238699138063},
{ 0.003556824805628873, 0.001209822327859134},
{-0.004645661091012323, 0.001630427298020484},
{ 0.002496092353182751, -0.003912508340989219},
{ 0.000302090333345882, 0.003088058972305475},
{-0.000575340825070231, -0.000601820846099991},
{-0.001471518558760183, -0.000211117948702046},
{ 0.002783459090201593, -0.001604420226187919},
{-0.001462571137390710, 0.003635296917435769},
{-0.000974502437588628, -0.003352512117661619},
{ 0.001737955094951189, 0.001247368808692742},
{-0.000307792427984885, 0.000013203419520814},
{-0.001109526585744628, 0.000950323796527789},
{ 0.000526319899588746, -0.002635084366837765},
{ 0.001389314639579712, 0.002746943712072799},
{-0.002351020828600294, -0.001098649819278158},
{ 0.001403876768349682, -0.000326101441888477},
{ 0.000007636494885364, -0.000009498318627546},
{-0.000015810020735412, 0.001342809547658261},
{-0.001336084746214299, -0.001744394557524099},
{ 0.002306569133792808, 0.000593352416441460},
{-0.001741577737284259, 0.000764423771425344},
{ 0.000446259382722843, -0.000833040920509266},
{-0.000032439446797982, -0.000184988059956732},
{ 0.000872822027879478, 0.000783952720205515},
{-0.001752019159639665, -0.000116536066154024},
{ 0.001497010004594416, -0.001021907858926351},
{-0.000414412310798685, 0.001309793399193761},
{-0.000211542240694677, -0.000563783548428934},
{-0.000225464280571550, -0.000137131865995694},
{ 0.000974834882891133, -0.000116507082762092},
{-0.000970457632383734, 0.000968411010101219},
{ 0.000163814255478569, -0.001346530693477844},
{ 0.000507024815168339, 0.000837598610490586},
{-0.000349821460486328, -0.000132102793530797},
{-0.000262675893448681, 0.000082499729563353},
{ 0.000447037274751782, -0.000651912509450940},
{ 0.000067236591919819, 0.001044125966364416},
{-0.000666108629277537, -0.000744775901128932},
{ 0.000680260922139908, 0.000120771182888810},
{-0.000201935458823935, 0.000107628631934352},
{-0.000100626407015480, 0.000227947144491114},
{-0.000151885493743030, -0.000595641190573172},
{ 0.000619888661818225, 0.000476871323359771},
{-0.000726760198519770, 0.000007022279302861},
{ 0.000379089999045936, -0.000303466839685386},
{-0.000032082305030256, 0.000136804226080666},
{ 0.000087462353623023, 0.000188197153014303},
{-0.000416485880859098, -0.000211750352828883},
{ 0.000568731727879734, -0.000112347863435717},
{-0.000345943017888307, 0.000402175417043329},
{ 0.000015664427565742, -0.000348002612845697},
{ 0.000054324657659007, 0.000076028700470034},
{ 0.000154951430569292, 0.000045395998708319},
{-0.000325534912280742, 0.000130173041693986},
{ 0.000215876012859716, -0.000372695852521222},
{ 0.000054431163395054, 0.000385174790951269},
{-0.000174999505027930, -0.000168008090089447},
{ 0.000030499908326113, 0.000003048682668941},
{ 0.000166817395492779, -0.000105930895534485},
{-0.000131793335799479, 0.000373003580727555},
{-0.000169437838021966, -0.000501610900725898},
{ 0.000488176200631435, 0.000319593757302892},
{-0.000558465829929111, 0.000048001521408758},
{ 0.000349316846854170, -0.000326162940234938},
{-0.000055467806072849, 0.000356692479491629},
{-0.000115428390169126, -0.000205816198882806},
{ 0.000114833396071144, 0.000047778708840601},
{-0.000038085228233508, 0.000010585909953741},
{-0.000008324593371228, 0.000011315001355977},
{-0.000001217597841650, -0.000039141482370942},
{ 0.000027694034878707, 0.000033114740542633},
{-0.000034177222729439, -0.000007245841504979},
{ 0.000020060944485413, -0.000009851315045204},
{-0.000005461290583903, 0.000011336784355656},
};
#define CHROMA_FIR_SIZE (sizeof(CHROMA_FIR)/sizeof(dsp::complex_t))
#define CHROMA_FIR_DELAY ((CHROMA_FIR_SIZE-1)/2)

View File

@ -0,0 +1,131 @@
#pragma once
#include <dsp/types.h>
const dsp::complex_t CHROMA_BANDPASS[123] = {
{ -0.000007675039564594f, -0.000017362992335168f },
{ 0.000050180791439308f, -0.000005054021864311f },
{ -0.000022529111707761f, 0.000102942513429095f },
{ -0.000157609487484146f, -0.000092618697641464f },
{ 0.000205649042029007f, -0.000181710515677257f },
{ 0.000143445458895462f, 0.000331994546004200f },
{ -0.000414693079508517f, 0.000038265188132615f },
{ 0.000090081630021837f, -0.000395731646002122f },
{ 0.000257705918065856f, 0.000154354504676150f },
{ -0.000064051192147575f, 0.000055648228186439f },
{ 0.000089938060647145f, 0.000213032074676941f },
{ -0.000604775098099200f, 0.000050706635726124f },
{ 0.000223309865890358f, -0.000944433958755193f },
{ 0.001049943574694384f, 0.000640863688898729f },
{ -0.000983491651119595f, 0.000840133365053179f },
{ -0.000417178588714773f, -0.001011686459999295f },
{ 0.000616677332283103f, -0.000046513429902547f },
{ 0.000018549463752019f, -0.000075619948809012f },
{ 0.000734408386201158f, 0.000456742966201638f },
{ -0.001192460562555901f, 0.001001510577200253f },
{ -0.000729137747758392f, -0.001811046261815935f },
{ 0.001878272869910273f, -0.000125879189667096f },
{ -0.000312873903977849f, 0.001230889889574772f },
{ -0.000142534831707354f, -0.000090307321579771f },
{ -0.000942796972567241f, 0.000778470227412111f },
{ -0.000945381510920278f, -0.002406055808135091f },
{ 0.003537159230775561f, -0.000207350791625892f },
{ -0.000956199555190230f, 0.003634225577771235f },
{ -0.002543835202533561f, -0.001641705037372486f },
{ 0.001064108471592447f, -0.000863770138941644f },
{ -0.000335799601479829f, -0.000876091753216939f },
{ 0.003390761989356699f, -0.000170321604912419f },
{ -0.001408130728751909f, 0.005175554625981795f },
{ -0.005203055300834108f, -0.003419861284250694f },
{ 0.004342719678657084f, -0.003465264906764298f },
{ 0.001143432997855297f, 0.003059520699490539f },
{ 0.000304096484476364f, -0.000012725974706621f },
{ -0.001193870642975282f, 0.004247469277548632f },
{ -0.006681021498855877f, -0.004471771356204969f },
{ 0.007965721969864534f, -0.006247895626072559f },
{ 0.003365883969059717f, 0.009241201835481184f },
{ -0.006835562188141396f, 0.000228798228738161f },
{ 0.000409900284971528f, -0.001412838961851673f },
{ -0.004331406608345981f, -0.002951876085350234f },
{ 0.009290089917766562f, -0.007161958719089258f },
{ 0.005418326020709935f, 0.015272361365960607f },
{ -0.017077565432843410f, 0.000428641984774326f },
{ 0.003850771342644978f, -0.012869517593577566f },
{ 0.004380859690202961f, 0.003039552423897447f },
{ 0.004761181766399753f, -0.003607421240356480f },
{ 0.005926935731028822f, 0.017160134858844222f },
{ -0.028153584885925551f, 0.000471042980325370f },
{ 0.009655944938035437f, -0.031314555422639050f },
{ 0.023930146568136038f, 0.016901617811072800f },
{ -0.012998853255109976f, 0.009678807314399702f },
{ 0.002043176559434885f, 0.006079907699564680f },
{ -0.036686455817128191f, 0.000306882557812233f },
{ 0.021529138474771701f, -0.067800343150283604f },
{ 0.085421344938160879f, 0.061409588050754214f },
{ -0.108166660998898100f, 0.079141989828113088f },
{ -0.047617308971534079f, -0.145721049254261960f },
{ 0.160079041453427080f, -0.000000000000000427f },
{ -0.047617308971533295f, 0.145721049254262240f },
{ -0.108166660998898530f, -0.079141989828112505f },
{ 0.085421344938160546f, -0.061409588050754672f },
{ 0.021529138474772065f, 0.067800343150283493f },
{ -0.036686455817128191f, -0.000306882557812037f },
{ 0.002043176559434853f, -0.006079907699564691f },
{ -0.012998853255110026f, -0.009678807314399631f },
{ 0.023930146568135951f, -0.016901617811072928f },
{ 0.009655944938035604f, 0.031314555422638994f },
{ -0.028153584885925554f, -0.000471042980325220f },
{ 0.005926935731028730f, -0.017160134858844253f },
{ 0.004761181766399772f, 0.003607421240356455f },
{ 0.004380859690202943f, -0.003039552423897470f },
{ 0.003850771342645046f, 0.012869517593577545f },
{ -0.017077565432843413f, -0.000428641984774235f },
{ 0.005418326020709854f, -0.015272361365960637f },
{ 0.009290089917766600f, 0.007161958719089209f },
{ -0.004331406608345964f, 0.002951876085350257f },
{ 0.000409900284971536f, 0.001412838961851670f },
{ -0.006835562188141398f, -0.000228798228738125f },
{ 0.003365883969059667f, -0.009241201835481201f },
{ 0.007965721969864567f, 0.006247895626072517f },
{ -0.006681021498855855f, 0.004471771356205005f },
{ -0.001193870642975304f, -0.004247469277548626f },
{ 0.000304096484476364f, 0.000012725974706619f },
{ 0.001143432997855281f, -0.003059520699490545f },
{ 0.004342719678657102f, 0.003465264906764274f },
{ -0.005203055300834089f, 0.003419861284250722f },
{ -0.001408130728751936f, -0.005175554625981787f },
{ 0.003390761989356700f, 0.000170321604912401f },
{ -0.000335799601479825f, 0.000876091753216940f },
{ 0.001064108471592452f, 0.000863770138941638f },
{ -0.002543835202533552f, 0.001641705037372499f },
{ -0.000956199555190250f, -0.003634225577771230f },
{ 0.003537159230775563f, 0.000207350791625874f },
{ -0.000945381510920265f, 0.002406055808135096f },
{ -0.000942796972567245f, -0.000778470227412106f },
{ -0.000142534831707354f, 0.000090307321579771f },
{ -0.000312873903977856f, -0.001230889889574770f },
{ 0.001878272869910274f, 0.000125879189667086f },
{ -0.000729137747758382f, 0.001811046261815939f },
{ -0.001192460562555906f, -0.001001510577200246f },
{ 0.000734408386201156f, -0.000456742966201642f },
{ 0.000018549463752019f, 0.000075619948809012f },
{ 0.000616677332283103f, 0.000046513429902543f },
{ -0.000417178588714767f, 0.001011686459999298f },
{ -0.000983491651119600f, -0.000840133365053174f },
{ 0.001049943574694380f, -0.000640863688898734f },
{ 0.000223309865890363f, 0.000944433958755192f },
{ -0.000604775098099200f, -0.000050706635726121f },
{ 0.000089938060647144f, -0.000213032074676941f },
{ -0.000064051192147576f, -0.000055648228186438f },
{ 0.000257705918065856f, -0.000154354504676151f },
{ 0.000090081630021839f, 0.000395731646002121f },
{ -0.000414693079508517f, -0.000038265188132613f },
{ 0.000143445458895461f, -0.000331994546004200f },
{ 0.000205649042029008f, 0.000181710515677256f },
{ -0.000157609487484145f, 0.000092618697641465f },
{ -0.000022529111707761f, -0.000102942513429094f },
{ 0.000050180791439308f, 0.000005054021864311f },
{ -0.000007675039564594f, 0.000017362992335168f }
};
#define CHROMA_BANDPASS_SIZE (sizeof(CHROMA_BANDPASS)/sizeof(dsp::complex_t))
#define CHROMA_BANDPASS_DELAY (CHROMA_BANDPASS_SIZE/2)

View File

@ -5,6 +5,33 @@
#include <dsp/multirate/polyphase_bank.h>
#include <dsp/math/step.h>
#define LINE_SIZE 945
#define SYNC_LEN 70
#define SYNC_SIDE_LEN 17
#define SYNC_L_START (LINE_SIZE - SYNC_SIDE_LEN)
#define SYNC_R_START (SYNC_LEN/2)
#define SYNC_R_END (SYNC_R_START + (SYNC_LEN/2) + SYNC_SIDE_LEN)
#define SYNC_HALF_LEN ((SYNC_LEN/2) + SYNC_SIDE_LEN)
#define EQUAL_LEN 35
#define HBLANK_START SYNC_LEN
#define HBLANK_END 155
#define HBLANK_LEN (HBLANK_END - HBLANK_START + 1)
#define SYNC_LEVEL (-0.428)
#define COLORBURST_START 84
#define COLORBURST_LEN 33
#define MAX_LOCK 1000
dsp::complex_t PHASE_REF[2] = {
{ -0.707106781186547f, 0.707106781186547f },
{ -0.707106781186547f, -0.707106781186547f }
};
class LineSync : public dsp::Processor<float, float> {
using base_type = dsp::Processor<float, float>;
public:
@ -27,41 +54,17 @@ public:
_interpPhaseCount = interpPhaseCount;
_interpTapCount = interpTapCount;
pcl.init(_muGain, _omegaGain, 0.0, 0.0, 1.0, _omega, _omega * (1.0 - omegaRelLimit), _omega * (1.0 + omegaRelLimit));
generateInterpTaps();
buffer = dsp::buffer::alloc<float>(STREAM_BUFFER_SIZE + _interpTapCount);
bufStart = &buffer[_interpTapCount - 1];
// TODO: Needs tuning, so do the gains
maxPeriod = (int32_t)(1.0001 * (float)(1 << 30));
minPeriod = (int32_t)(0.9999 * (float)(1 << 30));
base_type::init(in);
}
void setOmegaGain(double omegaGain) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
_omegaGain = omegaGain;
pcl.setCoefficients(_muGain, _omegaGain);
}
void setMuGain(double muGain) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
_muGain = muGain;
pcl.setCoefficients(_muGain, _omegaGain);
}
void setOmegaRelLimit(double omegaRelLimit) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
_omegaRelLimit = omegaRelLimit;
pcl.setFreqLimits(_omega * (1.0 - _omegaRelLimit), _omega * (1.0 + _omegaRelLimit));
}
void setSyncLevel(float level) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
syncLevel = level;
}
void setInterpParams(int interpPhaseCount, int interpTapCount) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
@ -81,8 +84,7 @@ public:
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop();
offset = 0;
pcl.phase = 0.0f;
pcl.freq = _omega;
phase = 0;
base_type::tempStart();
}
@ -93,61 +95,120 @@ public:
// Copy data to work buffer
memcpy(bufStart, base_type::_in->readBuf, count * sizeof(float));
if (test2) {
test2 = false;
offset += 5;
}
// Process all samples
// Process samples while they are available
while (offset < count) {
// Calculate new output value
int phase = std::clamp<int>(floorf(pcl.phase * (float)_interpPhaseCount), 0, _interpPhaseCount - 1);
float outVal;
volk_32f_x2_dot_prod_32f(&outVal, &buffer[offset], interpBank.phases[phase], _interpTapCount);
base_type::out.writeBuf[outCount++] = outVal;
// While the offset is negative, out put zeros
while (offset < 0 && pixel < LINE_SIZE) {
// Output a zero
base_type::out.writeBuf[pixel++] = 0.0f;
// If the end of the line is reached, process it and determin error
float error = 0;
if (outCount >= 720) {
// Compute averages.
// Increment the phase
phase += period;
offset += (phase >> 30);
phase &= 0x3FFFFFFF;
}
// Process as much of a line as possible
while (offset < count && pixel < LINE_SIZE) {
// Compute the output sample
volk_32f_x2_dot_prod_32f(&base_type::out.writeBuf[pixel++], &buffer[offset], interpBank.phases[(phase >> 23) & 0x7F], _interpTapCount);
// Increment the phase
phase += period;
offset += (phase >> 30);
phase &= 0x3FFFFFFF;
}
// If the line is done, process it
if (pixel == LINE_SIZE) {
// Compute averages. (TODO: Try faster method)
float left = 0.0f, right = 0.0f;
for (int i = (720-17); i < 720; i++) {
int lc = 0, rc = 0;
for (int i = SYNC_L_START; i < LINE_SIZE; i++) {
left += base_type::out.writeBuf[i];
lc++;
}
for (int i = 0; i < 27; i++) {
for (int i = 0; i < SYNC_R_START; i++) {
left += base_type::out.writeBuf[i];
lc++;
}
for (int i = 27; i < (54+17); i++) {
for (int i = SYNC_R_START; i < SYNC_R_END; i++) {
right += base_type::out.writeBuf[i];
}
left *= (1.0f/44.0f);
right *= (1.0f/44.0f);
// If the sync is present, compute error
if ((left < syncLevel && right < syncLevel) && !forceLock) {
error = (left + syncBias - right);
locked = true;
}
else {
locked = false;
rc++;
}
if (++counter >= 100) {
counter = 0;
//flog::warn("Left: {}, Right: {}, Error: {}, Freq: {}, Phase: {}", left, right, error, pcl.freq, pcl.phase);
// Compute the error
float error = (left - right) * (1.0f/((float)SYNC_HALF_LEN));
// Compute the change in phase and frequency due to the error
float periodDelta = error * _omegaGain;
float phaseDelta = error * _muGain;
// Normalize the phase delta (TODO: Make faster)
while (phaseDelta <= -1.0f) {
phaseDelta += 1.0f;
offset--;
}
while (phaseDelta >= 1.0f) {
phaseDelta -= 1.0f;
offset++;
}
// Update the period (TODO: Clamp error*omegaGain to prevent weird shit with corrupt samples)
period += (int32_t)(periodDelta * (float)(1 << 30));
period = std::clamp<uint32_t>(period, minPeriod, maxPeriod);
// Update the phase
phase += (int32_t)(phaseDelta * (float)(1 << 30));
// Normalize the phase
uint32_t overflow = phase >> 30;
if (overflow) {
if (error < 0) {
offset -= 4 - overflow;
}
else {
offset += overflow;
}
}
phase &= 0x3FFFFFFF;
// Find the lowest value
float lowest = INFINITY;
int lowestId = -1;
for (int i = 0; i < LINE_SIZE; i++) {
float val = base_type::out.writeBuf[i];
if (val < lowest) {
lowest = val;
lowestId = i;
}
}
// Check the the line is in lock
bool lineLocked = (lowestId < SYNC_R_END || lowestId >= SYNC_L_START);
// Update the lock status based on the line lock
if (!lineLocked && locked) {
locked--;
}
else if (lineLocked && locked < MAX_LOCK) {
locked++;
}
// If not locked, attempt to lock by forcing the sync to happen at the right spot
// TODO: This triggers waaaay too easily at low SNR
if (!locked && fastLock) {
offset += lowestId - SYNC_R_START;
locked = MAX_LOCK / 2;
}
// Output line
if (!base_type::out.swap(outCount)) { break; }
outCount = 0;
if (!base_type::out.swap(LINE_SIZE)) { break; }
pixel = 0;
}
// Advance symbol offset and phase
pcl.advance(error);
float delta = floorf(pcl.phase);
offset += delta;
pcl.phase -= delta;
}
// Get the offset ready for the next buffer
offset -= count;
// Update delay buffer
@ -155,16 +216,15 @@ public:
// Swap if some data was generated
base_type::_in->flush();
return outCount;
return 0;
}
bool locked = false;
bool test2 = false;
float syncBias = 0;
float syncBias = 0.0f;
bool forceLock = false;
uint32_t period = (0x800072F3 >> 1);//(1 << 31) + 1;
int counter = 0;
int locked = 0;
bool fastLock = true;
protected:
void generateInterpTaps() {
@ -175,7 +235,6 @@ protected:
}
dsp::multirate::PolyphaseBank<float> interpBank;
dsp::loop::PhaseControlLoop<double, false> pcl;
double _omega;
double _omegaGain;
@ -183,11 +242,14 @@ protected:
double _omegaRelLimit;
int _interpPhaseCount;
int _interpTapCount;
int offset = 0;
int outCount = 0;
float* buffer;
float* bufStart;
uint32_t phase = 0;
uint32_t maxPeriod;
uint32_t minPeriod;
float syncLevel = -0.03f;
int offset = 0;
int pixel = 0;
};

View File

@ -16,9 +16,13 @@
#include <dsp/filter/fir.h>
#include <dsp/taps/from_array.h>
#include "chrominance_filter.h"
#include "amplitude.h"
#include <dsp/demod/am.h>
#include <dsp/loop/fast_agc.h>
#include "chroma_pll.h"
#include "filters.h"
#include <dsp/math/normalize_phase.h>
#include <fstream>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -29,24 +33,26 @@ SDRPP_MOD_INFO{/* Name: */ "atv_decoder",
/* Max instances */ -1
};
#define SAMPLE_RATE (625.0f * 720.0f * 25.0f)
#define SAMPLE_RATE (625.0f * (float)LINE_SIZE * 25.0f)
class ATVDecoderModule : public ModuleManager::Instance {
public:
ATVDecoderModule(std::string name) : img(720, 625) {
ATVDecoderModule(std::string name) : img(768, 576) {
this->name = name;
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 8000000.0f, SAMPLE_RATE, SAMPLE_RATE, SAMPLE_RATE, true);
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 7000000.0f, SAMPLE_RATE, SAMPLE_RATE, SAMPLE_RATE, true);
demod.init(vfo->output, SAMPLE_RATE, SAMPLE_RATE / 2.0f);
agc.init(vfo->output, 1.0f, 1e6, 0.001f, 1.0f);
demod.init(&agc.out, SAMPLE_RATE, SAMPLE_RATE / 2.0f);
//demod.init(vfo->output, dsp::demod::AM<float>::CARRIER, 8000000.0f, 50.0 / SAMPLE_RATE, 50.0 / SAMPLE_RATE, 0.0f, SAMPLE_RATE);
sync.init(&demod.out, 1.0f, 1e-6, 1.0, 0.05);
sink.init(&sync.out, handler, this);
r2c.init(NULL);
chromaTaps = dsp::taps::fromArray(CHROMA_FIR_SIZE, CHROMA_FIR);
fir.init(NULL, chromaTaps);
pll.init(NULL, 0.01, 0.0, dsp::math::hzToRads(4433618.75, SAMPLE_RATE), dsp::math::hzToRads(4433618.75*0.90, SAMPLE_RATE), dsp::math::hzToRads(4433618.75*1.1, SAMPLE_RATE));
file = std::ofstream("chromasub_diff.bin", std::ios::binary | std::ios::out);
agc.start();
demod.start();
sync.start();
sink.start();
@ -58,7 +64,10 @@ class ATVDecoderModule : public ModuleManager::Instance {
if (vfo) {
sigpath::vfoManager.deleteVFO(vfo);
}
agc.stop();
demod.stop();
sync.stop();
sink.stop();
gui::menu.removeEntry(name);
}
@ -74,6 +83,8 @@ class ATVDecoderModule : public ModuleManager::Instance {
bool isEnabled() { return enabled; }
std::ofstream file;
private:
static void menuHandler(void *ctx) {
ATVDecoderModule *_this = (ATVDecoderModule *)ctx;
@ -82,142 +93,215 @@ class ATVDecoderModule : public ModuleManager::Instance {
style::beginDisabled();
}
// Ideal width for testing: 750pixels
ImGui::FillWidth();
_this->img.draw();
ImGui::LeftLabel("Sync");
ImGui::FillWidth();
ImGui::SliderFloat("##syncLvl", &_this->sync_level, -2, 2);
ImGui::LeftLabel("Min");
ImGui::FillWidth();
ImGui::SliderFloat("##minLvl", &_this->minLvl, -1.0, 1.0);
ImGui::LeftLabel("Span");
ImGui::FillWidth();
ImGui::SliderFloat("##spanLvl", &_this->spanLvl, 0, 1.0);
ImGui::LeftLabel("Sync Bias");
ImGui::FillWidth();
ImGui::SliderFloat("##syncBias", &_this->sync.syncBias,-0.1, 0.1);
if (ImGui::Button("Test2")) {
_this->sync.test2 = true;
}
if (ImGui::Button("Switch frame")) {
std::lock_guard<std::mutex> lck(_this->evenFrameMtx);
_this->evenFrame = !_this->evenFrame;
}
if (_this->sync.locked) {
ImGui::TextUnformatted("Horizontal Sync:");
ImGui::SameLine();
if (_this->sync.locked > 750) {
ImGui::TextColored(ImVec4(0, 1, 0, 1), "Locked");
}
else {
ImGui::TextUnformatted("Not locked");
}
ImGui::Checkbox("Force Lock", &_this->sync.forceLock);
ImGui::TextUnformatted("Vertical Sync:");
ImGui::SameLine();
if (_this->vlock > 15) {
ImGui::TextColored(ImVec4(0, 1, 0, 1), "Locked");
}
else {
ImGui::TextUnformatted("Not locked");
}
ImGui::Checkbox("Fast Lock", &_this->sync.fastLock);
ImGui::Checkbox("Color Mode", &_this->colorMode);
if (!_this->enabled) {
style::endDisabled();
}
if (ImGui::Button("Close Debug")) {
_this->file.close();
}
ImGui::Text("Gain: %f", _this->gain);
ImGui::Text("Offset: %f", _this->offset);
ImGui::Text("Subcarrier: %f", _this->subcarrierFreq);
}
uint32_t pp = 0;
static void handler(float *data, int count, void *ctx) {
ATVDecoderModule *_this = (ATVDecoderModule *)ctx;
// Convert line to complex
_this->r2c.process(720, data, _this->r2c.out.writeBuf);
// Correct the offset
volk_32f_s32f_add_32f(data, data, _this->offset, count);
// Isolate the chroma subcarrier
_this->fir.process(720, _this->r2c.out.writeBuf, _this->fir.out.writeBuf);
// Correct the gain
volk_32f_s32f_multiply_32f(data, data, _this->gain, count);
// Run chroma carrier through the PLL
_this->pll.process(720, _this->fir.out.writeBuf, _this->pll.out.writeBuf, ((_this->ypos%2)==1) ^ _this->evenFrame);
// Compute the sync levels
float syncLLevel = 0.0f;
float syncRLevel = 0.0f;
volk_32f_accumulator_s32f(&syncLLevel, data, EQUAL_LEN);
volk_32f_accumulator_s32f(&syncRLevel, &data[EQUAL_LEN], SYNC_LEN - EQUAL_LEN);
syncLLevel *= 1.0f / EQUAL_LEN;
syncRLevel *= 1.0f / (SYNC_LEN - EQUAL_LEN);
float syncLevel = (syncLLevel + syncRLevel) * 0.5f; // TODO: It's technically correct but if the sizes were different it wouldn't be
// Render line to the image without color
int lypos = _this->ypos - 1;
if (lypos < 0) { lypos = 624; }
uint32_t* lastLine = &((uint32_t *)_this->img.buffer)[(lypos < 313) ? (lypos*720*2) : ((((lypos - 313)*2)+1)*720) ];
uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[(_this->ypos < 313) ? (_this->ypos*720*2) : ((((_this->ypos - 313)*2)+1)*720) ];
// Compute the blanking level
float blankLevel = 0.0f;
volk_32f_accumulator_s32f(&blankLevel, &data[HBLANK_START], HBLANK_LEN);
blankLevel /= (float)HBLANK_LEN;
//uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[_this->ypos*720];
// Run the offset control loop
_this->offset -= (blankLevel / _this->gain)*0.001;
_this->offset = std::clamp<float>(_this->offset, -1.0f, 1.0f);
_this->gain -= (blankLevel - syncLevel + SYNC_LEVEL)*0.01f;
_this->gain = std::clamp<float>(_this->gain, 0.1f, 10.0f);
for (int i = 0; i < count; i++) {
int imval = std::clamp<float>((data[i] - _this->minLvl) * 255.0 / _this->spanLvl, 0, 255);
// uint32_t re = std::clamp<float>((_this->pll.out.writeBuf[i].re - _this->minLvl) * 255.0 / _this->spanLvl, 0, 255);
// uint32_t im = std::clamp<float>((_this->pll.out.writeBuf[i].im - _this->minLvl) * 255.0 / _this->spanLvl, 0, 255);
// currentLine[i] = 0xFF000000 | (im << 8) | re;
currentLine[i] = 0xFF000000 | (imval << 16) | (imval << 8) | imval;
// Detect the sync type
uint16_t shortSync = (syncLLevel < 0.5f*SYNC_LEVEL) && (syncRLevel > 0.5f*SYNC_LEVEL) && (blankLevel > 0.5f*SYNC_LEVEL);
uint16_t longSync = (syncLLevel < 0.5f*SYNC_LEVEL) && (syncRLevel < 0.5f*SYNC_LEVEL) && (blankLevel < 0.5f*SYNC_LEVEL);
// Save sync type to history
_this->syncHistory = (_this->syncHistory << 2) | (longSync << 1) | shortSync;
// If the line has a colorburst, decode it
dsp::complex_t* buf1 = _this->r2c.out.readBuf;
dsp::complex_t* buf2 = _this->r2c.out.writeBuf;
if (true) {
// Convert the line into complex
_this->r2c.process(count, data, buf1);
// Extract the chroma subcarrier (TODO: Optimise by running only where needed)
for (int i = COLORBURST_START; i < count-(CHROMA_BANDPASS_DELAY+1); i++) {
volk_32fc_x2_dot_prod_32fc((lv_32fc_t*)&buf2[i], (lv_32fc_t*)&buf1[i - CHROMA_BANDPASS_DELAY], (lv_32fc_t*)CHROMA_BANDPASS, CHROMA_BANDPASS_SIZE);
}
// Down convert the chroma subcarrier (TODO: Optimise by running only where needed)
lv_32fc_t startPhase = { 1.0f, 0.0f };
lv_32fc_t phaseDelta = { sinf(_this->subcarrierFreq), cosf(_this->subcarrierFreq) };
#if VOLK_VERSION >= 030100
volk_32fc_s32fc_x2_rotator2_32fc((lv_32fc_t*)&buf2[COLORBURST_START], (lv_32fc_t*)&buf2[COLORBURST_START], &phaseDelta, &startPhase, count - COLORBURST_START);
#else
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)&buf2[COLORBURST_START], (lv_32fc_t*)&buf2[COLORBURST_START], phaseDelta, &startPhase, count - COLORBURST_START);
#endif
// Compute the phase of the burst
dsp::complex_t burstAvg = { 0.0f, 0.0f };
volk_32fc_accumulator_s32fc((lv_32fc_t*)&burstAvg, (lv_32fc_t*)&buf2[COLORBURST_START], COLORBURST_LEN);
float burstAmp = burstAvg.amplitude();
if (burstAmp*(1.0f/(float)COLORBURST_LEN) < 0.02f) {
printf("%d\n", _this->line);
}
burstAvg *= (1.0f / (burstAmp*burstAmp));
burstAvg = burstAvg.conj();
// Normalize the chroma data (TODO: Optimise by running only where needed)
volk_32fc_s32fc_multiply_32fc((lv_32fc_t*)&buf2[COLORBURST_START], (lv_32fc_t*)&buf2[COLORBURST_START], *((lv_32fc_t*)&burstAvg), count - COLORBURST_START);
// Compute the frequency error of the burst
float phase = buf2[COLORBURST_START].phase();
float error = 0.0f;
for (int i = COLORBURST_START+1; i < COLORBURST_START+COLORBURST_LEN; i++) {
float cphase = buf2[i].phase();
error += dsp::math::normalizePhase(cphase - phase);
phase = cphase;
}
error *= (1.0f / (float)(COLORBURST_LEN-1));
// Update the subcarrier freq
_this->subcarrierFreq += error*0.0001f;
}
// Vertical scan logic
_this->ypos++;
bool rollover = _this->ypos >= 625;
if (rollover) {
{
std::lock_guard<std::mutex> lck(_this->evenFrameMtx);
_this->evenFrame = !_this->evenFrame;
// Render the line if it's visible
if (_this->ypos >= 34 && _this->ypos <= 34+576-1) {
uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[(_this->ypos - 34)*768];
if (_this->colorMode) {
for (int i = 155; i < (155+768); i++) {
int imval1 = std::clamp<float>(fabsf(buf2[i-155+COLORBURST_START].re*5.0f) * 255.0f, 0, 255);
int imval2 = std::clamp<float>(fabsf(buf2[i-155+COLORBURST_START].im*5.0f) * 255.0f, 0, 255);
currentLine[i-155] = 0xFF000000 | (imval2 << 8) | imval1;
}
}
else {
for (int i = 155; i < (155+768); i++) {
int imval = std::clamp<float>(data[i-155+COLORBURST_START] * 255.0f, 0, 255);
currentLine[i-155] = 0xFF000000 | (imval << 16) | (imval << 8) | imval;
}
}
}
// Compute whether to rollover
bool rollToOdd = (_this->ypos == 624);
bool rollToEven = (_this->ypos == 623);
// Compute the field sync
bool syncToOdd = (_this->syncHistory == 0b0101011010010101);
bool syncToEven = (_this->syncHistory == 0b0001011010100101);
// Process the sync (NOTE: should start with 0b01, but for some reason I don't see a sync?)
if (rollToOdd || syncToOdd) {
// Update the vertical lock state
bool disagree = (rollToOdd ^ syncToOdd);
if (disagree && _this->vlock > 0) {
_this->vlock--;
}
else if (!disagree && _this->vlock < 20) {
_this->vlock++;
}
// Start the odd field
_this->ypos = 1;
_this->line++;
}
else if (rollToEven || syncToEven) {
// Update the vertical lock state
bool disagree = (rollToEven ^ syncToEven);
if (disagree && _this->vlock > 0) {
_this->vlock--;
}
else if (!disagree && _this->vlock < 20) {
_this->vlock++;
}
// Start the even field
_this->ypos = 0;
_this->line = 0;
// Swap the video buffer
_this->img.swap();
}
// Measure vsync levels
float sync0 = 0.0f, sync1 = 0.0f;
for (int i = 0; i < 306; i++) {
sync0 += data[i];
}
for (int i = (720/2); i < ((720/2)+306); i++) {
sync1 += data[i];
}
sync0 *= (1.0f/305.0f);
sync1 *= (1.0f/305.0f);
// Save sync detection to history
_this->syncHistory >>= 2;
_this->syncHistory |= (((uint16_t)(sync1 < _this->sync_level)) << 9) | (((uint16_t)(sync0 < _this->sync_level)) << 8);
// Trigger vsync in case one is detected
// TODO: Also sync with odd field
if (!rollover && _this->syncHistory == 0b0000011111) {
{
std::lock_guard<std::mutex> lck(_this->evenFrameMtx);
_this->evenFrame = !_this->evenFrame;
}
_this->ypos = 0;
_this->img.swap();
else {
_this->ypos += 2;
_this->line++;
}
}
// NEW SYNC:
float offset = 0.0f;
float gain = 1.0f;
uint16_t syncHistory = 0;
int line = 0;
int ypos = 0;
int vlock = 0;
float subcarrierFreq = 0.0f;
std::string name;
bool enabled = true;
VFOManager::VFO *vfo = NULL;
dsp::demod::Quadrature demod;
//dsp::demod::Quadrature demod;
dsp::loop::FastAGC<dsp::complex_t> agc;
dsp::demod::Amplitude demod;
//dsp::demod::AM<float> demod;
LineSync sync;
dsp::sink::Handler<float> sink;
dsp::convert::RealToComplex r2c;
dsp::tap<dsp::complex_t> chromaTaps;
dsp::filter::FIR<dsp::complex_t, dsp::complex_t> fir;
dsp::loop::ChromaPLL pll;
int ypos = 0;
bool evenFrame = false;
std::mutex evenFrameMtx;
float sync_level = -0.06f;
int sync_count = 0;
int short_sync = 0;
float minLvl = 0.0f;
float spanLvl = 1.0f;
bool lockedLines = 0;
uint16_t syncHistory = 0;
bool colorMode = false;
ImGui::ImageDisplay img;
};

View File

@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 3.13)
project(dab_decoder)
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
include(${SDRPP_MODULE_CMAKE})
target_include_directories(dab_decoder PRIVATE "src/")
if (MSVC)
# Lib path
target_include_directories(dab_decoder PRIVATE "C:/Program Files/codec2/include/")
target_link_directories(dab_decoder PRIVATE "C:/Program Files/codec2/lib")
target_link_libraries(dab_decoder PRIVATE libcodec2)
elseif (ANDROID)
target_include_directories(dab_decoder PUBLIC
/sdr-kit/${ANDROID_ABI}/include/codec2
)
target_link_libraries(dab_decoder PUBLIC
/sdr-kit/${ANDROID_ABI}/lib/libcodec2.so
)
else ()
find_package(PkgConfig)
pkg_check_modules(LIBCODEC2 REQUIRED codec2)
target_include_directories(dab_decoder PRIVATE ${LIBCODEC2_INCLUDE_DIRS})
target_link_directories(dab_decoder PRIVATE ${LIBCODEC2_LIBRARY_DIRS})
target_link_libraries(dab_decoder PRIVATE ${LIBCODEC2_LIBRARIES})
# Include it because for some reason pkgconfig doesn't look here?
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
target_include_directories(dab_decoder PRIVATE "/usr/local/include")
endif()
endif ()

View File

@ -0,0 +1,280 @@
#pragma once
#include <dsp/processor.h>
#include <utils/flog.h>
#include <fftw3.h>
#include "dab_phase_sym.h"
namespace dab {
class CyclicSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
public:
CyclicSync() {}
// TODO: The default AGC rate is probably way too fast, plot out the avgCorr to see how much it moves
CyclicSync(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) { init(in, symbolLength, cyclicPrefixLength, samplerate, agcRate); }
void init(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) {
// Computer the number of samples for the symbol and its cyclic prefix
symbolSamps = round(samplerate * symbolLength);
prefixSamps = round(samplerate * cyclicPrefixLength);
// Allocate and clear the delay buffer
delayBuf = dsp::buffer::alloc<dsp::complex_t>(STREAM_BUFFER_SIZE + 64000);
dsp::buffer::clear(delayBuf, symbolSamps);
// Allocate and clear the history buffer
histBuf = dsp::buffer::alloc<dsp::complex_t>(prefixSamps);
dsp::buffer::clear(histBuf, prefixSamps);
// Compute the delay input addresses
delayBufInput = &delayBuf[symbolSamps];
// Compute the correlation AGC configuration
this->agcRate = agcRate;
agcRateInv = 1.0f - agcRate;
base_type::init(in);
}
void reset() {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop();
base_type::tempStart();
}
int run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
// Copy the data into the normal delay buffer
memcpy(delayBufInput, base_type::_in->readBuf, count * sizeof(dsp::complex_t));
// Flush the input stream
base_type::_in->flush();
// Do cross-correlation
for (int i = 0; i < count; i++) {
// Get the current history slot
dsp::complex_t* slot = &histBuf[histId++];
// Wrap around the history slot index (TODO: Check that the history buffer's length is correct)
histId %= prefixSamps;
// Kick out last value from the correlation
corr -= *slot;
// Save input value and compute the new prodct
dsp::complex_t val = delayBuf[i];
dsp::complex_t prod = val.conj()*delayBuf[i+symbolSamps];
// Add the new value to the correlation
*slot = prod;
// Add the new value to the history buffer
corr += prod;
// Compute sample amplitude
float rcorr = corr.amplitude();
// If a high enough peak is reached, reset the symbol counter
if (rcorr > avgCorr && rcorr > peakCorr) { // Note keeping an average level might not be needed
peakCorr = rcorr;
peakLCorr = lastCorr;
samplesSincePeak = 0;
}
// If this is the sample right after the peak, save it
if (samplesSincePeak == 1) {
peakRCorr = rcorr;
}
// Write the sample to the output
out.writeBuf[samplesSincePeak++] = val;
// If the end of the symbol is reached, send it off
if (samplesSincePeak >= symbolSamps) {
if (!out.swap(symbolSamps)) {
return -1;
}
samplesSincePeak = 0;
peakCorr = 0;
}
// Update the average correlation
lastCorr = rcorr;
// Update the average correlation value
avgCorr = agcRate*rcorr + agcRateInv*avgCorr;
}
// Move unused data
memmove(delayBuf, &delayBuf[count], symbolSamps * sizeof(dsp::complex_t));
return count;
}
protected:
int symbolSamps;
int prefixSamps;
int histId = 0;
dsp::complex_t* histBuf;
dsp::complex_t* delayBuf;
dsp::complex_t* delayBufInput;
dsp::complex_t corr = { 0.0f, 0.0f };
int samplesSincePeak = 0;
float lastCorr = 0.0f;
float peakCorr = 0.0f;
float peakLCorr = 0.0f;
float peakRCorr = 0.0f;
// Note only required for DAB
float avgCorr = 0.0f;
float agcRate;
float agcRateInv;
};
class FrameFreqSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
public:
FrameFreqSync() {}
FrameFreqSync(dsp::stream<dsp::complex_t>* in, float agcRate = 0.01f) { init(in, agcRate); }
void init(dsp::stream<dsp::complex_t>* in, float agcRate = 0.01f) {
// Allocate buffers
amps = dsp::buffer::alloc<float>(2048);
conjRef = dsp::buffer::alloc<dsp::complex_t>(2048);
corrIn = (dsp::complex_t*)fftwf_alloc_complex(2048);
corrOut = (dsp::complex_t*)fftwf_alloc_complex(2048);
// Copy the phase reference
memcpy(conjRef, DAB_PHASE_SYM_CONJ, 2048 * sizeof(dsp::complex_t));
// Plan the FFT computation
plan = fftwf_plan_dft_1d(2048, (fftwf_complex*)corrIn, (fftwf_complex*)corrOut, FFTW_FORWARD, FFTW_ESTIMATE);
// Compute the correlation AGC configuration
this->agcRate = agcRate;
agcRateInv = 1.0f - agcRate;
base_type::init(in);
}
void reset() {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop();
base_type::tempStart();
}
int run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
// Apply frequency shift
lv_32fc_t phase = lv_cmake(1.0f, 0.0f);
lv_32fc_t phaseDelta = lv_cmake(cos(offset), sin(offset));
#if VOLK_VERSION >= 030100
volk_32fc_s32fc_x2_rotator2_32fc((lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
#else
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
#endif
// Compute the amplitude amplitude of all samples
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)_in->readBuf, 2048);
// Compute the average signal level by adding up all values
float level = 0.0f;
volk_32f_accumulator_s32f(&level, amps, 2048);
// Detect a frame sync condition
if (level < avgLvl * 0.5f) {
// Reset symbol counter
sym = 1;
// Update the average level
avgLvl = agcRate*level + agcRateInv*avgLvl;
// Flush the input stream and return
base_type::_in->flush();
return count;
}
// Update the average level
avgLvl = agcRate*level + agcRateInv*avgLvl;
// Handle phase reference
if (sym == 1) {
// Output the symbols (DEBUG ONLY)
memcpy(corrIn, _in->readBuf, 2048 * sizeof(dsp::complex_t));
fftwf_execute(plan);
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)corrOut, 2048);
int outCount = 0;
dsp::complex_t pi4 = { cos(3.1415926535*0.25), sin(3.1415926535*0.25) };
for (int i = -767; i < 768; i++) {
if (!i) { continue; }
int cid0 = ((i-1) >= 0) ? (i-1) : 2048+(i-1);
int cid1 = (i >= 0) ? i : 2048+i;;
out.writeBuf[outCount++] = pi4 * (corrOut[cid1] * corrOut[cid0].conj()) * (1.0f/(amps[cid0]*amps[cid0]));
}
out.swap(outCount);
// Multiply the samples with the conjugated phase reference signal
volk_32fc_x2_multiply_32fc((lv_32fc_t*)corrIn, (lv_32fc_t*)_in->readBuf, (lv_32fc_t*)conjRef, 2048);
// Compute the FFT of the product
fftwf_execute(plan);
// Compute the amplitude of the bins
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)corrOut, 2048);
// Locate highest power bin
uint32_t peakId;
volk_32f_index_max_32u(&peakId, amps, 2048);
// Obtain the value of the bins next to the peak
float peakL = amps[(peakId + 2047) % 2048];
float peakR = amps[(peakId + 1) % 2048];
// Compute the integer frequency offset
float offInt = (peakId < 1024) ? (float)peakId : ((float)peakId - 2048.0f);
// Compute the frequency offset in rad/samp
float off = 3.1415926535f * (offInt + ((peakR - peakL) / (peakR + peakL))) * (1.0f / 1024.0f);
// Run control loop
offset -= 0.1f*off;
flog::debug("Offset: {} Hz, Error: {} Hz, Avg Level: {}", offset * (0.5f/3.1415926535f)*2.048e6, off * (0.5f/3.1415926535f)*2.048e6, avgLvl);
}
// Increment the symbol counter
sym++;
// Flush the input stream and return
base_type::_in->flush();
return count;
}
protected:
fftwf_plan plan;
float* amps;
dsp::complex_t* conjRef;
dsp::complex_t* corrIn;
dsp::complex_t* corrOut;
int sym;
float offset = 0.0f;
float avgLvl = 0.0f;
float agcRate;
float agcRateInv;
};
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,163 @@
#include <imgui.h>
#include <config.h>
#include <core.h>
#include <gui/style.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <module.h>
#include <filesystem>
#include <dsp/stream.h>
#include <dsp/buffer/reshaper.h>
#include <dsp/multirate/rational_resampler.h>
#include <dsp/sink/handler_sink.h>
#include <fstream>
#include <chrono>
#include "dab_dsp.h"
#include <gui/widgets/constellation_diagram.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{
/* Name: */ "dab_decoder",
/* Description: */ "DAB/DAB+ Decoder for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
ConfigManager config;
#define INPUT_SAMPLE_RATE 2.048e6
#define VFO_BANDWIDTH 1.6e6
class M17DecoderModule : public ModuleManager::Instance {
public:
M17DecoderModule(std::string name) {
this->name = name;
file = std::ofstream("sync4.f32", std::ios::out | std::ios::binary);
// Load config
config.acquire();
config.release(true);
// Initialize VFO
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, VFO_BANDWIDTH, INPUT_SAMPLE_RATE, VFO_BANDWIDTH, VFO_BANDWIDTH, true);
vfo->setSnapInterval(250);
// Initialize DSP here
csync.init(vfo->output, 1e-3, 246e-6, INPUT_SAMPLE_RATE);
ffsync.init(&csync.out);
ns.init(&ffsync.out, handler, this);
// Start DSO Here
csync.start();
ffsync.start();
ns.start();
gui::menu.registerEntry(name, menuHandler, this, this);
}
~M17DecoderModule() {
gui::menu.removeEntry(name);
// Stop DSP Here
if (enabled) {
csync.stop();
ffsync.stop();
ns.stop();
sigpath::vfoManager.deleteVFO(vfo);
}
sigpath::sinkManager.unregisterStream(name);
}
void postInit() {}
void enable() {
double bw = gui::waterfall.getBandwidth();
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), VFO_BANDWIDTH, INPUT_SAMPLE_RATE, VFO_BANDWIDTH, VFO_BANDWIDTH, true);
vfo->setSnapInterval(250);
// Set Input of demod here
csync.setInput(vfo->output);
// Start DSP here
csync.start();
ffsync.start();
ns.start();
enabled = true;
}
void disable() {
// Stop DSP here
csync.stop();
ffsync.stop();
ns.stop();
sigpath::vfoManager.deleteVFO(vfo);
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
static void menuHandler(void* ctx) {
M17DecoderModule* _this = (M17DecoderModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvail().x;
if (!_this->enabled) { style::beginDisabled(); }
_this->constDiagram.draw();
if (!_this->enabled) { style::endDisabled(); }
}
std::ofstream file;
static void handler(dsp::complex_t* data, int count, void* ctx) {
M17DecoderModule* _this = (M17DecoderModule*)ctx;
//_this->file.write((char*)data, count * sizeof(dsp::complex_t));
dsp::complex_t* buf = _this->constDiagram.acquireBuffer();
memcpy(buf, data, 1024 * sizeof(dsp::complex_t));
_this->constDiagram.releaseBuffer();
}
std::string name;
bool enabled = true;
dab::CyclicSync csync;
dab::FrameFreqSync ffsync;
dsp::sink::Handler<dsp::complex_t> ns;
ImGui::ConstellationDiagram constDiagram;
// DSP Chain
VFOManager::VFO* vfo;
};
MOD_EXPORT void _INIT_() {
// Create default recording directory
json def = json({});
config.setPath(core::args["root"].s() + "/dab_decoder_config.json");
config.load(def);
config.enableAutoSave();
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new M17DecoderModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (M17DecoderModule*)instance;
}
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}

View File

@ -0,0 +1,34 @@
0123456789
--- ---
0*4
1*5
2*6
1*5
2*6 = L + 3*7 - 0*4
3*7
2*6
3*7 = L + 4*8 - 1*5
4*8
3*7
4*8 = L + 5*9 - 2*6
5*9
0*5
1*6
2*7
1*6
2*7
3*8
2*7
3*8
4*9
=> Use same technique to cache the interpolation results

View File

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.13)
project(ryfi_decoder)
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
include(${SDRPP_MODULE_CMAKE})
target_include_directories(ryfi_decoder PRIVATE "src/")

View File

@ -0,0 +1,139 @@
#include <imgui.h>
#include <config.h>
#include <core.h>
#include <gui/style.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <module.h>
#include <filesystem>
#include <dsp/routing/splitter.h>
#include <dsp/buffer/reshaper.h>
#include <dsp/sink/handler_sink.h>
#include <gui/widgets/folder_select.h>
#include <gui/widgets/constellation_diagram.h>
#include "ryfi/receiver.h"
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{
/* Name: */ "ryfi_decoder",
/* Description: */ "RyFi decoder for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
#define INPUT_BANDWIDTH 600e3
#define INPUT_SAMPLE_RATE 1000e3
#define INPUT_BAUDRATE 500e3
#define SYMBOL_DIAG_RATE 30
#define SYMBOL_DIAG_COUNT 1024
class RyFiDecoderModule : public ModuleManager::Instance {
public:
RyFiDecoderModule(std::string name) {
this->name = name;
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_BANDWIDTH, INPUT_SAMPLE_RATE, INPUT_BANDWIDTH, INPUT_BANDWIDTH, true);
rx.init(vfo->output, INPUT_BAUDRATE, INPUT_SAMPLE_RATE);
reshape.init(rx.softOut, SYMBOL_DIAG_COUNT, (INPUT_BAUDRATE / SYMBOL_DIAG_RATE) - SYMBOL_DIAG_COUNT);
symSink.init(&reshape.out, symSinkHandler, this);
rx.onPacket.bind(&RyFiDecoderModule::packetHandler, this);
rx.start();
reshape.start();
symSink.start();
gui::menu.registerEntry(name, menuHandler, this, this);
}
~RyFiDecoderModule() {
rx.stop();
reshape.stop();
symSink.stop();
sigpath::vfoManager.deleteVFO(vfo);
gui::menu.removeEntry(name);
}
void postInit() {}
void enable() {
double bw = gui::waterfall.getBandwidth();
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), INPUT_BANDWIDTH, INPUT_SAMPLE_RATE, INPUT_BANDWIDTH, INPUT_BANDWIDTH, true);
rx.setInput(vfo->output);
rx.start();
reshape.start();
symSink.start();
enabled = true;
}
void disable() {
rx.stop();
reshape.stop();
symSink.stop();
sigpath::vfoManager.deleteVFO(vfo);
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
void packetHandler(ryfi::Packet pkt) {
flog::debug("Got a {} byte packet!", pkt.size());
}
static void menuHandler(void* ctx) {
RyFiDecoderModule* _this = (RyFiDecoderModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvail().x;
if (!_this->enabled) { style::beginDisabled(); }
ImGui::SetNextItemWidth(menuWidth);
_this->constDiagram.draw();
if (!_this->enabled) { style::endDisabled(); }
}
static void symSinkHandler(dsp::complex_t* data, int count, void* ctx) {
RyFiDecoderModule* _this = (RyFiDecoderModule*)ctx;
dsp::complex_t* buf = _this->constDiagram.acquireBuffer();
memcpy(buf, data, 1024 * sizeof(dsp::complex_t));
_this->constDiagram.releaseBuffer();
}
std::string name;
bool enabled = true;
// DSP Chain
VFOManager::VFO* vfo;
ryfi::Receiver rx;
dsp::buffer::Reshaper<dsp::complex_t> reshape;
dsp::sink::Handler<dsp::complex_t> symSink;
ImGui::ConstellationDiagram constDiagram;
};
MOD_EXPORT void _INIT_() {
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new RyFiDecoderModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (RyFiDecoderModule*)instance;
}
MOD_EXPORT void _END_() {
}

View File

@ -0,0 +1,74 @@
#include "conv_codec.h"
namespace ryfi {
ConvEncoder::ConvEncoder(dsp::stream<uint8_t>* in) {
// Create the convolutional encoder instance
conv = correct_convolutional_create(2, 7, correct_conv_r12_7_polynomial);
// Init the base class
base_type::init(in);
}
ConvEncoder::~ConvEncoder() {
// Destroy the convolutional encoder instance
correct_convolutional_destroy(conv);
}
int ConvEncoder::encode(const uint8_t* in, uint8_t* out, int count) {
// Run convolutional encoder on the data
return correct_convolutional_encode(conv, in, count, out);
}
int ConvEncoder::run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count);
base_type::_in->flush();
if (!out.swap(count)) { return -1; }
return count;
}
ConvDecoder::ConvDecoder(dsp::stream<dsp::complex_t>* in) {
// Create the convolutional encoder instance
conv = correct_convolutional_create(2, 7, correct_conv_r12_7_polynomial);
// Allocate the soft symbol buffer
soft = dsp::buffer::alloc<uint8_t>(STREAM_BUFFER_SIZE);
// Init the base class
base_type::init(in);
}
ConvDecoder::~ConvDecoder() {
// Destroy the convolutional encoder instance
correct_convolutional_destroy(conv);
// Free the soft symbol buffer
dsp::buffer::free(soft);
}
int ConvDecoder::decode(const dsp::complex_t* in, uint8_t* out, int count) {
// Convert to uint8
const float* _in = (const float*)in;
count *= 2;
for (int i = 0; i < count; i++) {
soft[i] = std::clamp<int>((_in[i] * 127.0f) + 128.0f, 0, 255);
}
// Run convolutional decoder on the data
return correct_convolutional_decode_soft(conv, soft, count, out);
}
int ConvDecoder::run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
count = decode(base_type::_in->readBuf, base_type::out.writeBuf, count);
base_type::_in->flush();
if (!out.swap(count)) { return -1; }
return count;
}
}

View File

@ -0,0 +1,71 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include "dsp/processor.h"
extern "C" {
#include "correct.h"
}
namespace ryfi {
/**
* RyFi Convolutional Encoder.
*/
class ConvEncoder : public dsp::Processor<uint8_t, uint8_t> {
using base_type = dsp::Processor<uint8_t, uint8_t>;
public:
/**
* Create a convolutional encoder specifying an input stream.
* @param in Input stream.
*/
ConvEncoder(dsp::stream<uint8_t>* in = NULL);
// Destructor
~ConvEncoder();
/**
* Encode data.
* @param in Input bytes.
* @param out Output bits.
* @param count Number of input bytes.
* @return Number of output bits.
*/
int encode(const uint8_t* in, uint8_t* out, int count);
private:
int run();
correct_convolutional* conv;
};
/**
* RyFi Convolutional Decoder.
*/
class ConvDecoder : public dsp::Processor<dsp::complex_t, uint8_t> {
using base_type = dsp::Processor<dsp::complex_t, uint8_t>;
public:
/**
* Create a convolutional encoder specifying an input stream.
* @param in Input stream.
*/
ConvDecoder(dsp::stream<dsp::complex_t>* in = NULL);
// Destructor
~ConvDecoder();
/**
* Decode soft symbols.
* @param in Input soft symbols.
* @param out Output bytes.
* @param count Number of input bytes.
* @return Number of output bits.
*/
int decode(const dsp::complex_t* in, uint8_t* out, int count);
private:
int run();
correct_convolutional* conv;
uint8_t* soft = NULL;
};
}

View File

@ -0,0 +1,37 @@
#include "frame.h"
namespace ryfi {
int Frame::serialize(uint8_t* bytes) const {
// Write the counter
bytes[0] = (counter >> 8) & 0xFF;
bytes[1] = counter & 0xFF;
// Write the first packet pointer
bytes[2] = (firstPacket >> 8) & 0xFF;
bytes[3] = firstPacket & 0xFF;
// Write the last packet pointer
bytes[4] = (lastPacket >> 8) & 0xFF;
bytes[5] = lastPacket & 0xFF;
// Write the data
memcpy(&bytes[6], content, FRAME_DATA_SIZE);
// Return the length of a serialized frame
return FRAME_SIZE;
}
void Frame::deserialize(const uint8_t* bytes, Frame& frame) {
// Read the counter
frame.counter = (((uint16_t)bytes[0]) << 8) | ((uint16_t)bytes[1]);
// Read the first packet pointer
frame.firstPacket = (((uint16_t)bytes[2]) << 8) | ((uint16_t)bytes[3]);
// Read the last packet pointer
frame.lastPacket = (((uint16_t)bytes[4]) << 8) | ((uint16_t)bytes[5]);
// Read the data
memcpy(frame.content, &bytes[6], FRAME_DATA_SIZE);
}
}

View File

@ -0,0 +1,42 @@
#pragma once
#include <stdint.h>
#include "rs_codec.h"
namespace ryfi {
enum PacketOffset {
PKT_OFFS_NONE = 0xFFFF
};
struct Frame {
/**
* Serialize the frame to bytes.
* @param bytes Buffer to write the serialized frame to.
*/
int serialize(uint8_t* bytes) const;
/**
* Deserialize a frame from bytes.
* @param bytes Buffer to deserialize the frame from.
* @param frame Object that will contain the deserialize frame.
*/
static void deserialize(const uint8_t* bytes, Frame& frame);
// Size of a serialized frame
static inline const int FRAME_SIZE = RS_BLOCK_DEC_SIZE*RS_BLOCK_COUNT;
// Size of the data area of the frame
static inline const int FRAME_DATA_SIZE = FRAME_SIZE - 6;
// Steadily increasing counter.
uint16_t counter = 0;
// Byte offset of the first packet in the frame.
uint16_t firstPacket = 0;
// Byte offset of the last packet in the frame.
uint16_t lastPacket = 0;
// Data area of the frame.
uint8_t content[FRAME_DATA_SIZE];
};
}

View File

@ -0,0 +1,137 @@
#include "framing.h"
namespace ryfi {
dsp::complex_t QPSK_SYMBOLS[4] = {
{ -0.070710678118f, -0.070710678118f },
{ -0.070710678118f, 0.070710678118f },
{ 0.070710678118f, -0.070710678118f },
{ 0.070710678118f, 0.070710678118f },
};
Framer::Framer(dsp::stream<uint8_t>* in) {
// Generate the sync symbols
int k = 0;
for (int i = 62; i >= 0; i -= 2) {
syncSyms[k++] = QPSK_SYMBOLS[(SYNC_WORD >> i) & 0b11];
}
// Initialize base class
base_type::init(in);
}
int Framer::encode(const uint8_t* in, dsp::complex_t* out, int count) {
// Copy sync symbols
memcpy(out, syncSyms, SYNC_SYMS*sizeof(dsp::complex_t));
// Modulate the rest of the bits
dsp::complex_t* dataOut = &out[SYNC_SYMS];
int dataSyms = count / 2;
for (int i = 0; i < dataSyms; i++) {
uint8_t bits = (in[i >> 2] >> (6 - 2*(i & 0b11))) & 0b11;
dataOut[i] = QPSK_SYMBOLS[bits];
}
// Compute and return the total number of symbols
return SYNC_SYMS + dataSyms;
}
int Framer::run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count);
base_type::_in->flush();
if (!out.swap(count)) { return -1; }
return count;
}
Deframer::Deframer(dsp::stream<dsp::complex_t> *in) {
// Compute sync word rotations
// 0: 00 01 11 10
// 90: 10 00 01 11
// 180: 11 10 00 01
// 270: 01 11 10 00
// For 0 and 180 it's the sync and its complement
syncRots[ROT_0_DEG] = SYNC_WORD;
syncRots[ROT_180_DEG] = ~SYNC_WORD;
// For 90 and 270 its the quadrature and its complement
uint64_t quad;
for (int i = 62; i >= 0; i -= 2) {
// Get the symbol
uint8_t sym = (SYNC_WORD >> i) & 0b11;
// Rotate it 90 degrees
uint8_t rsym;
switch (sym) {
case 0b00: rsym = 0b10; break;
case 0b01: rsym = 0b00; break;
case 0b11: rsym = 0b01; break;
case 0b10: rsym = 0b11; break;
}
// Push it into the quadrature
quad = (quad << 2) | rsym;
}
syncRots[ROT_90_DEG] = quad;
syncRots[ROT_270_DEG] = ~quad;
base_type::init(in);
}
int Deframer::run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
dsp::complex_t* in = base_type::_in->readBuf;
for (int i = 0; i < count; i++) {
if (recv) {
// Copy the symbol to the output and rotate it approprieate
base_type::out.writeBuf[outCount++] = in[i] * symRot;
// Check if we're done receiving the frame, send it out
if (!(--recv)) {
if (!base_type::out.swap(outCount)) {
base_type::_in->flush();
return -1;
}
}
}
else {
// Get the raw symbol
dsp::complex_t fsym = in[i];
// Decode the symbol
uint8_t sym = ((fsym.re > 0) ? 0b10 : 0b00) | ((fsym.im > 0) ? 0b01 : 0b00);
// Push it to the shift register
shift = (shift << 2) | sym;
// Find the rotation starting with the last known one
for (int i = 0; i < 4; i++) {
// Get the test rotation
int testRot = (knownRot+i) & 0b11;
// Check if the hamming distance is close enough
int dist;
if (distance(shift, syncRots[testRot]) < 6) {
// Save the new rotation
knownRot = testRot;
// Start reading in symbols for the frame
symRot = symRots[knownRot];
recv = 8168; // TODO: Don't hardcode!
outCount = 0;
}
}
}
}
base_type::_in->flush();
return count;
}
}

View File

@ -0,0 +1,87 @@
#pragma once
#include "dsp/processor.h"
#include <stdint.h>
#include <stddef.h>
namespace ryfi {
// Synchronization word.
inline const uint64_t SYNC_WORD = 0x341CC540819D8963;
// Number of synchronization bits.
inline const int SYNC_BITS = 64;
// Number of synchronization symbols.
inline const int SYNC_SYMS = SYNC_BITS / 2;
// Possible constellation rotations
enum {
ROT_0_DEG = 0,
ROT_90_DEG = 1,
ROT_180_DEG = 2,
ROT_270_DEG = 3
};
/**
* RyFi Framer.
*/
class Framer : public dsp::Processor<uint8_t, dsp::complex_t> {
using base_type = dsp::Processor<uint8_t, dsp::complex_t>;
public:
/**
* Create a framer specifying an input stream.
* @param in Input stream.
*/
Framer(dsp::stream<uint8_t>* in = NULL);
/**
* Encode a frame to symbols adding a sync word.
*/
int encode(const uint8_t* in, dsp::complex_t* out, int count);
private:
int run();
dsp::complex_t syncSyms[SYNC_SYMS];
};
class Deframer : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
public:
/**
* Create a deframer specifying an input stream.
* @param in Input stream.
*/
Deframer(dsp::stream<dsp::complex_t> *in = NULL);
private:
int run();
inline static constexpr int distance(uint64_t a, uint64_t b) {
int dist = 0;
for (int i = 0; i < 64; i++) {
dist += ((a & 1) != (b & 1));
a >>= 1;
b >>= 1;
}
return dist;
}
// Frame reading counters
int recv = 0;
int outCount = 0;
// Rotation handling
int knownRot = 0;
uint64_t syncRots[4];
dsp::complex_t symRot;
const dsp::complex_t symRots[4] = {
{ 1.0f, 0.0f }, // 0 deg
{ 0.0f, -1.0f }, // 90 deg
{ -1.0f, 0.0f }, // 180 deg
{ 0.0f, 1.0f }, // 270 deg
};
// Shift register
uint64_t shift;
};
}

View File

@ -0,0 +1,126 @@
#include "packet.h"
#include "string.h"
#include <stdexcept>
namespace ryfi {
Packet::Packet() {}
Packet::Packet(uint8_t* content, int size) {
// Check that the size isn't too large
if (size > MAX_CONTENT_SIZE) {
throw std::runtime_error("Content size is too large to fit in a packet");
}
// Allocate the buffer
allocate(size);
// Copy over the content
memcpy(_content, content, size);
}
Packet::Packet(const Packet& b) {
// Reallocate the buffer
allocate(b._size);
// Copy over the content
memcpy(_content, b._content, b._size);
}
Packet::Packet(Packet&& b) {
// Move members
_content = b._content;
_size = b._size;
// Destroy old object
b._content = NULL;
b._size = 0;
}
Packet::~Packet() {
// Delete the content
if (_content) { delete[] _content; }
}
Packet& Packet::operator=(const Packet& b) {
// Reallocate the buffer
allocate(b._size);
// Copy over the content
memcpy(_content, b._content, b._size);
// Return self
return *this;
}
Packet& Packet::operator=(Packet&& b) {
// Move members
_content = b._content;
_size = b._size;
// Destroy old object
b._content = NULL;
b._size = 0;
// Return self
return *this;
}
Packet::operator bool() const {
return _size > 0;
}
int Packet::size() const {
// Return the size
return _size;
}
const uint8_t* Packet::data() const {
// Return the size
return _content;
}
void Packet::setContent(uint8_t* content, int size) {
// Check that the size isn't too large
if (size > MAX_CONTENT_SIZE) {
throw std::runtime_error("Content size is too large to fit in a packet");
}
// Reallocate the buffer
allocate(size);
// Copy over the content
memcpy(_content, content, size);
}
int Packet::serializedSize() const {
// Two size bytes + Size of the content
return _size + 2;
}
int Packet::serialize(uint8_t* bytes) const {
// Write the size in big-endian
bytes[0] = (_size >> 8) & 0xFF;
bytes[1] = _size & 0xFF;
// Copy the content of the packet
memcpy(&bytes[2], _content, _size);
// Return the serialized size
return serializedSize();
}
void Packet::allocate(int newSize) {
// If the size hasn't changed, do nothing
if (newSize == _size) { return; }
// Free the old buffer
if (_content) { delete[] _content; };
// Update the size
_size = newSize;
// Allocate the buffer
_content = new uint8_t[newSize];
}
}

View File

@ -0,0 +1,89 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
namespace ryfi {
/**
* RyFi Protocol Packet.
*/
class Packet {
public:
// Default constructor
Packet();
/**
* Create a packet from its content.
* @param content Content of the packet.
* @param size Number of bytes of content.
*/
Packet(uint8_t* content, int size);
// Copy constructor
Packet(const Packet& b);
// Move constructor
Packet(Packet&& b);
// Destructor
~Packet();
// Copy assignment operator
Packet& operator=(const Packet& b);
// Move assignment operator
Packet& operator=(Packet&& b);
// Cast to bool operator
operator bool() const;
/**
* Get the size of the content of the packet.
* @return Size of the content of the packet.
*/
int size() const;
/**
* Get the content of the packet. The pointer is only valid until reallocation or deletion.
* @return Content of the packet.
*/
const uint8_t* data() const;
/**
* Set the content of the packet.
* @param content Content of the packet.
* @param size Number of bytes of content.
*/
void setContent(uint8_t* content, int size);
/**
* Get the size of the serialized packet.
* @return Size of the serialized packet.
*/
int serializedSize() const;
/**
* Serialize the packet to bytes.
* @param bytes Buffer to which to write the serialized packet.
* @return Size of the serialized packet.
*/
int serialize(uint8_t* bytes) const;
/**
* Deserialize a packet from bytes.
* TODO
*/
static bool deserialize(uint8_t* bytes, int size, Packet& pkt);
// Maximum size of the content of the packet.
static inline const int MAX_CONTENT_SIZE = 0xFFFF;
// Maximum size of the serialized packet.
static inline const int MAX_SERIALIZED_SIZE = MAX_CONTENT_SIZE + 2;
private:
void allocate(int newSize);
uint8_t* _content = NULL;
int _size = 0;
};
}

View File

@ -0,0 +1,194 @@
#include "receiver.h"
#include "utils/flog.h"
namespace ryfi {
Receiver::Receiver() {}
Receiver::Receiver(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate) {
init(in, baudrate, samplerate);
}
Receiver::~Receiver() {
// Stop everything
stop();
}
void Receiver::init(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate) {
// Initialize the DSP
demod.init(in, baudrate, samplerate, 31, 0.6, 0.1f, 0.005f, 1e-6, 0.01);
doubler.init(&demod.out);
softOut = &doubler.outA;
deframer.setInput(&doubler.outB);
conv.setInput(&deframer.out);
rs.setInput(&conv.out);
}
void Receiver::setInput(dsp::stream<dsp::complex_t>* in) {
demod.setInput(in);
}
void Receiver::start() {
// Do nothing if already running
if (running) { return; }
// Start the worker thread
workerThread = std::thread(&Receiver::worker, this);
// Start the DSP
demod.start();
doubler.start();
deframer.start();
conv.start();
rs.start();
// Update the running state
running = true;
}
void Receiver::stop() {
// Do nothing if not running
if (!running) { return; }
// Stop the worker thread
rs.out.stopReader();
if (workerThread.joinable()) { workerThread.join(); }
rs.out.clearReadStop();
// Stop the DSP
demod.stop();
doubler.stop();
deframer.stop();
conv.stop();
rs.stop();
// Update the running state
running = false;
}
void Receiver::worker() {
Frame frame;
uint16_t lastCounter = 0;
uint8_t* pktBuffer = new uint8_t[Packet::MAX_CONTENT_SIZE];
int pktExpected = 0;
int pktRead = 0;
int valid = 0;
while (true) {
// Read a frame
int count = rs.out.read();
if (count <= 0) { break; }
// Deserialize the frame
Frame::deserialize(rs.out.readBuf, frame);
valid++;
// Flush the stream
rs.out.flush();
//flog::info("Frame[{}]: FirstPacket={}, LastPacket={}", frame.counter, frame.firstPacket, frame.lastPacket);
// Compute the expected frame counter
uint16_t expectedCounter = lastCounter + 1;
lastCounter = frame.counter;
// If the frames aren't consecutive
int frameRead = 0;
if (frame.counter != expectedCounter) {
flog::warn("Lost at least {} frames after {} valid frames", ((int)frame.counter - (int)expectedCounter + 0x10000) % 0x10000, valid);
// Cancel the partial packet if there was one
pktExpected = 0;
pktRead = 0;
valid = 1;
// If this frame is not an idle frame or continuation frame
if (frame.firstPacket != PKT_OFFS_NONE) {
// If the offset of the first packet is not plausible
if (frame.firstPacket > Frame::FRAME_DATA_SIZE-2) {
flog::warn("Packet had non-plausible offset: {}", frameRead);
// Skip the frame
continue;
}
// Skip to the end of the packet
frameRead = frame.firstPacket;
}
}
// If there is no partial packet and the frame doesn't contain a packet start, skip it
if (!pktExpected && frame.firstPacket == PKT_OFFS_NONE) { continue; }
// Extract packets from the frame
bool firstPacket = true;
bool lastPacket = false;
while (frameRead < Frame::FRAME_DATA_SIZE) {
// If there is a partial packet read as much as possible from it
if (pktExpected) {
// Compute how many bytes of the packet are available in the frame
int readable = std::min<int>(pktExpected - pktRead, Frame::FRAME_DATA_SIZE - frameRead);
//flog::debug("Reading {} bytes", readable);
// Write them to the packet
memcpy(&pktBuffer[pktRead], &frame.content[frameRead], readable);
pktRead += readable;
frameRead += readable;
// If the packet is read entirely
if (pktRead >= pktExpected) {
// Create the packet object
Packet pkt(pktBuffer, pktExpected);
// Send off the packet
onPacket(pkt);
// Prepare for the next packet
pktRead = 0;
pktExpected = 0;
// If this was the last packet of the frame
if (lastPacket || frame.firstPacket == PKT_OFFS_NONE) {
// Skip the rest of the frame
frameRead = Frame::FRAME_DATA_SIZE;
continue;
}
}
// Go to next packet
continue;
}
// If the packet offset is not plausible
if (Frame::FRAME_DATA_SIZE - frameRead < 2) {
flog::warn("Packet had non-plausible offset: {}", frameRead);
// Skip the rest of the frame and the packet
frameRead = Frame::FRAME_DATA_SIZE;
pktExpected = 0;
pktRead = 0;
continue;
}
// If this is the first packet, use the frame info to skip possible left over data
if (firstPacket) {
frameRead = frame.firstPacket;
firstPacket = false;
}
// Check if this is the last packet
lastPacket = (frameRead == frame.lastPacket);
// Parse the packet size
pktExpected = ((uint16_t)frame.content[frameRead]) << 8;
pktExpected |= (uint16_t)frame.content[frameRead+1];
//flog::debug("Starting to read a {} byte packet at offset {}", pktExpected, frameRead);
// Skip to the packet content
frameRead += 2;
}
}
delete[] pktBuffer;
}
}

View File

@ -0,0 +1,69 @@
#pragma once
#include "utils/new_event.h"
#include "dsp/demod/psk.h"
#include "dsp/routing/doubler.h"
#include "packet.h"
#include "frame.h"
#include "rs_codec.h"
#include "conv_codec.h"
#include "framing.h"
#include <mutex>
namespace ryfi {
class Receiver {
public:
Receiver();
/**
* Create a transmitter.
* @param in Baseband input.
* @param baudrate Baudrate to use over the air.
* @param samplerate Samplerate of the baseband.
*/
Receiver(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate);
/**
* Create a transmitter.
* @param in Baseband input.
* @param baudrate Baudrate to use over the air.
* @param samplerate Samplerate of the baseband.
*/
void init(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate);
/**
* Set the input stream.
* @param in Baseband input.
*/
void setInput(dsp::stream<dsp::complex_t>* in);
// Destructor
~Receiver();
/**
* Start the transmitter's DSP.
*/
void start();
/**
* Stop the transmitter's DSP.
*/
void stop();
dsp::stream<dsp::complex_t>* softOut;
NewEvent<Packet> onPacket;
private:
void worker();
// DSP
dsp::demod::PSK<4> demod;
dsp::routing::Doubler<dsp::complex_t> doubler;
Deframer deframer;
ConvDecoder conv;
RSDecoder rs;
bool running = false;
std::thread workerThread;
};
}

View File

@ -0,0 +1,169 @@
#include "rs_codec.h"
namespace ryfi {
RSEncoder::RSEncoder(dsp::stream<uint8_t>* in) {
// Create the convolutional encoder instance
rs = correct_reed_solomon_create(correct_rs_primitive_polynomial_ccsds, 1, 1, 32);
// Init the base class
base_type::init(in);
}
RSEncoder::~RSEncoder() {
// Destroy the convolutional encoder instance
correct_reed_solomon_destroy(rs);
}
int RSEncoder::encode(const uint8_t* in, uint8_t* out, int count) {
// Check the size
assert(count == RS_BLOCK_COUNT*RS_BLOCK_DEC_SIZE);
// Go through each block
uint8_t block[RS_BLOCK_ENC_SIZE];
for (int i = 0; i < RS_BLOCK_COUNT; i++) {
// Encode block
correct_reed_solomon_encode(rs, &in[i*RS_BLOCK_DEC_SIZE], RS_BLOCK_DEC_SIZE, block);
// Interleave into the frame
int k = 0;
for (int j = i; j < RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT; j += RS_BLOCK_COUNT) {
out[j] = block[k++];
}
}
// Scramble
for (int i = 0; i < RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE; i++) {
out[i] ^= RS_SCRAMBLER_SEQ[i];
}
return RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE;
}
int RSEncoder::run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count);
base_type::_in->flush();
if (!out.swap(count)) { return -1; }
return count;
}
RSDecoder::RSDecoder(dsp::stream<uint8_t>* in) {
// Create the convolutional encoder instance
rs = correct_reed_solomon_create(correct_rs_primitive_polynomial_ccsds, 1, 1, 32);
// Init the base class
base_type::init(in);
}
RSDecoder::~RSDecoder() {
// Destroy the convolutional encoder instance
correct_reed_solomon_destroy(rs);
}
int RSDecoder::decode(uint8_t* in, uint8_t* out, int count) {
// Check the size
assert(count == RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE);
// Descramble (TODO: Don't do it in-place)
for (int i = 0; i < RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE; i++) {
in[i] ^= RS_SCRAMBLER_SEQ[i];
}
// Go through each block
uint8_t block[RS_BLOCK_ENC_SIZE];
for (int i = 0; i < RS_BLOCK_COUNT; i++) {
// Deinterleave out of the frame
int k = 0;
for (int j = i; j < count; j += RS_BLOCK_COUNT) {
block[k++] = in[j];
}
// Decode block and return if decoding fails
int res = correct_reed_solomon_decode(rs, block, RS_BLOCK_ENC_SIZE, &out[i*RS_BLOCK_DEC_SIZE]);
if (res < 0) { return 0; }
}
return RS_BLOCK_COUNT*RS_BLOCK_DEC_SIZE;
}
int RSDecoder::run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
count = decode(base_type::_in->readBuf, base_type::out.writeBuf, count);
base_type::_in->flush();
if (count && !out.swap(count)) { return -1; }
return count;
}
const uint8_t RS_SCRAMBLER_SEQ[RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT] = {
0x75, 0x05, 0x7C, 0xCE, 0xF1, 0xD0, 0x6C, 0xF6, 0xFA, 0x65, 0xF6, 0xFC, 0xE0, 0x0A, 0x82, 0x17,
0x6C, 0xBE, 0x76, 0xA0, 0xD6, 0x46, 0x12, 0x2E, 0xDE, 0xB5, 0xF7, 0xAD, 0xCB, 0x51, 0x63, 0x47,
0x27, 0x30, 0x7E, 0x43, 0xD1, 0xA1, 0xCB, 0x10, 0x08, 0x49, 0xDF, 0x86, 0xD4, 0xC4, 0xD7, 0x3C,
0x6D, 0x03, 0x07, 0x37, 0x5B, 0xB3, 0xCD, 0x79, 0x6F, 0x1E, 0xBA, 0xC5, 0x6E, 0xC3, 0x8C, 0x7A,
0x25, 0x99, 0x61, 0x54, 0x5A, 0x96, 0x57, 0x9B, 0xE0, 0x60, 0x5B, 0x09, 0x6D, 0x8B, 0x2D, 0x9D,
0x15, 0x9D, 0x0E, 0xBF, 0x57, 0xFB, 0x9C, 0x49, 0x82, 0x2C, 0x48, 0x59, 0x92, 0x47, 0x79, 0x17,
0x16, 0x74, 0xEA, 0xEA, 0xBB, 0xC5, 0x72, 0x32, 0x17, 0xD1, 0xB3, 0xDE, 0xEB, 0x15, 0xC7, 0x55,
0x8A, 0xF2, 0x88, 0xC2, 0x33, 0xA6, 0x17, 0x8B, 0xD4, 0x77, 0x22, 0x00, 0x63, 0x47, 0x45, 0x5F,
0x36, 0x35, 0x58, 0x8B, 0x88, 0xEC, 0xCA, 0xC4, 0x60, 0x53, 0x9E, 0xBD, 0xB2, 0xF5, 0x51, 0x46,
0x34, 0x9A, 0x07, 0x25, 0x3F, 0xF5, 0x65, 0x63, 0x77, 0x3C, 0x5A, 0xFA, 0x4E, 0x0C, 0xF7, 0x1B,
0x82, 0xAB, 0x73, 0x06, 0x7F, 0xB7, 0xC6, 0x6B, 0xBF, 0xB1, 0x46, 0xF3, 0x01, 0x91, 0xB1, 0xFF,
0x5C, 0x6F, 0xF9, 0x43, 0x0E, 0x6A, 0x70, 0x89, 0x0B, 0xEA, 0x8C, 0xD4, 0x1B, 0x51, 0x01, 0x31,
0x71, 0x2E, 0xDF, 0x24, 0xC1, 0xD5, 0xDB, 0x0E, 0xF5, 0xEB, 0x78, 0x79, 0x39, 0x5B, 0xAD, 0xC3,
0xA9, 0xA6, 0x60, 0x30, 0xA2, 0x9A, 0x7B, 0xA0, 0xF4, 0xAA, 0xC5, 0x57, 0xB3, 0x16, 0xF9, 0xB5,
0x79, 0x20, 0xC1, 0x88, 0x9A, 0x00, 0x43, 0xB2, 0xC6, 0x84, 0x8D, 0x03, 0xF2, 0xD8, 0x90, 0x7A,
0x21, 0x37, 0x7E, 0xF7, 0x75, 0xE5, 0xFB, 0xC9, 0xDC, 0xAB, 0x4B, 0xBC, 0x35, 0x38, 0xB9, 0x3A,
0x53, 0x89, 0x7E, 0xD5, 0x94, 0x12, 0x2D, 0x9B, 0x91, 0x90, 0x1D, 0x4D, 0x0E, 0xE0, 0x93, 0xF3,
0xC1, 0xA1, 0x9B, 0x73, 0x27, 0x22, 0x41, 0x27, 0xEE, 0x2A, 0xD7, 0x45, 0xBC, 0x8F, 0x9B, 0xA2,
0x36, 0x11, 0x16, 0x37, 0x1A, 0xF1, 0x2E, 0x71, 0xCF, 0x86, 0x89, 0x83, 0x5A, 0xF1, 0x24, 0x6C,
0x56, 0x71, 0x53, 0xE4, 0xD2, 0xCB, 0xCA, 0x86, 0x1E, 0xA0, 0xD5, 0x83, 0x3B, 0xEF, 0x09, 0x09,
0xC2, 0x07, 0x53, 0x86, 0xE6, 0x8A, 0xC6, 0x70, 0xFB, 0x91, 0x43, 0xCB, 0x91, 0x6E, 0xA9, 0xBC,
0x31, 0x42, 0x61, 0x0C, 0x88, 0xB8, 0x2C, 0xED, 0xD8, 0xE6, 0xA3, 0xEC, 0xAC, 0xB9, 0x45, 0x5E,
0x2C, 0x73, 0x3F, 0x2E, 0x06, 0xE0, 0xBF, 0x73, 0xDD, 0x2E, 0x45, 0x50, 0x6C, 0x53, 0x55, 0xF0,
0x7F, 0x6E, 0x61, 0xFA, 0xA0, 0x7A, 0x1C, 0xF0, 0xBD, 0xAC, 0x48, 0x61, 0x03, 0x6B, 0xED, 0x54,
0x2A, 0x27, 0x94, 0xF6, 0xF9, 0x6A, 0x04, 0x08, 0x0B, 0x3C, 0xC3, 0x30, 0x66, 0x01, 0xFB, 0xDC,
0xC9, 0x65, 0x03, 0x83, 0x7D, 0x0A, 0xDF, 0xA5, 0x04, 0x14, 0xE4, 0xF2, 0x4C, 0x01, 0xDF, 0x04,
0xD2, 0x80, 0xB9, 0x9B, 0xD9, 0x5E, 0xF8, 0x2A, 0x93, 0x8D, 0x8C, 0x09, 0x9B, 0x38, 0xEC, 0x3B,
0xC4, 0x29, 0x90, 0x7C, 0x65, 0x3A, 0xF2, 0x4B, 0x69, 0xD3, 0x63, 0x9B, 0x40, 0x95, 0xC3, 0xFB,
0x67, 0x54, 0x40, 0x9B, 0x26, 0x9F, 0x52, 0xFE, 0xD8, 0xD0, 0x24, 0x9C, 0x5C, 0xD4, 0xEF, 0xDE,
0x28, 0x66, 0x75, 0x04, 0xCB, 0xA4, 0xC0, 0xB9, 0x4B, 0xC9, 0x20, 0x4B, 0x56, 0xC7, 0x86, 0xC5,
0x39, 0x45, 0x18, 0xA7, 0x48, 0x14, 0x1A, 0x51, 0xCA, 0xD0, 0xC0, 0x15, 0xDD, 0xC1, 0x28, 0x4A,
0x7A, 0xD2, 0x10, 0xEA, 0x83, 0xD3, 0x3A, 0xEF, 0x48, 0x29, 0x41, 0xA4, 0xD4, 0x57, 0xA6, 0x1D,
0x76, 0x24, 0x93, 0x58, 0x7E, 0xB7, 0xDD, 0x0B, 0xF2, 0xCE, 0x71, 0x55, 0xF5, 0xAB, 0x8C, 0xC8,
0x70, 0x59, 0x73, 0x69, 0x9D, 0x29, 0x5E, 0x59, 0xF4, 0xB2, 0xC4, 0x97, 0x75, 0xF0, 0x65, 0x1B,
0x66, 0x5F, 0xA4, 0x33, 0x5C, 0xC7, 0xBF, 0x45, 0xE6, 0x20, 0xC0, 0xBD, 0xAD, 0xAE, 0x9F, 0x97,
0x05, 0xD8, 0x04, 0x2B, 0x0A, 0x46, 0xE8, 0xB8, 0xCB, 0x00, 0xE2, 0x7C, 0x70, 0x1B, 0x49, 0xDE,
0x81, 0xEB, 0x24, 0xAC, 0x1B, 0x3E, 0x09, 0xFB, 0xAC, 0xB7, 0xF2, 0xD1, 0xB2, 0x78, 0xF3, 0xAC,
0xC7, 0x6A, 0xA2, 0x07, 0x4C, 0xED, 0x61, 0xAD, 0x04, 0x7F, 0x45, 0x83, 0x59, 0x31, 0x27, 0xF0,
0x16, 0x6B, 0x0C, 0xAA, 0xD4, 0xD1, 0xCB, 0x1C, 0x51, 0x41, 0x0D, 0x2F, 0x8F, 0xF9, 0xF9, 0x7F,
0x22, 0x89, 0x46, 0xF4, 0xB8, 0x93, 0x98, 0x9E, 0x3E, 0x23, 0xF1, 0x6E, 0x64, 0x08, 0xB6, 0xC9,
0x6E, 0x53, 0x53, 0xED, 0xAD, 0x21, 0xCD, 0x1A, 0xF0, 0x45, 0xFC, 0x14, 0x00, 0xEA, 0xF7, 0x42,
0xEE, 0xDA, 0x58, 0x0D, 0x85, 0xBC, 0x74, 0xFB, 0x73, 0x78, 0xB5, 0x5E, 0x5E, 0x6F, 0x6F, 0x7E,
0x39, 0xC2, 0x05, 0x50, 0xDB, 0x3D, 0xB8, 0xF3, 0x8F, 0x80, 0xEC, 0x46, 0x29, 0x39, 0x89, 0xF3,
0x55, 0x9C, 0x6A, 0x5F, 0x7C, 0xD9, 0x7C, 0x13, 0xE4, 0x56, 0x5E, 0xE9, 0x60, 0x19, 0xE2, 0x7D,
0xC4, 0x41, 0x92, 0x8D, 0xDA, 0x21, 0x58, 0x20, 0xE9, 0xA8, 0x4C, 0x16, 0x34, 0x99, 0xAC, 0xB7,
0x30, 0xBD, 0x39, 0x19, 0xAC, 0x9B, 0x4B, 0x27, 0xFA, 0x32, 0xC1, 0x48, 0xA1, 0x80, 0x34, 0x36,
0x1E, 0xFB, 0x92, 0x43, 0x35, 0x72, 0x2D, 0xEF, 0xD2, 0xF2, 0xFC, 0xC2, 0x85, 0xAB, 0x59, 0x40,
0x8D, 0x9D, 0x1A, 0x1F, 0xE2, 0x92, 0x87, 0xA2, 0xF9, 0x2C, 0x78, 0xE4, 0xC3, 0x26, 0x56, 0x07,
0xB3, 0x78, 0xAF, 0x79, 0x3D, 0x88, 0xF4, 0xAD, 0x66, 0x7C, 0x07, 0x58, 0x98, 0x82, 0x1A, 0x26,
0xF7, 0xFD, 0xCE, 0xFF, 0x75, 0xED, 0xAB, 0xBD, 0xAE, 0x6D, 0x5C, 0x28, 0x91, 0xF3, 0xB7, 0x5C,
0x27, 0x05, 0xEC, 0x3B, 0xE3, 0xDD, 0x93, 0x24, 0x7F, 0xAD, 0x14, 0xAA, 0x49, 0x61, 0x8F, 0x96,
0x1F, 0xAA, 0xB2, 0xEE, 0xA8, 0x24, 0x41, 0x7C, 0xDC, 0xF1, 0x28, 0x26, 0xE6, 0x7F, 0x98, 0x20,
0x50, 0x5F, 0x90, 0x21, 0x8A, 0x09, 0x26, 0x59, 0xD0, 0x07, 0x2F, 0xE1, 0x35, 0x4D, 0x0B, 0x20,
0xB2, 0xD5, 0xDD, 0xB5, 0xAC, 0x1B, 0xFE, 0xD9, 0xE3, 0x35, 0xF1, 0xB8, 0x3F, 0x3D, 0xFC, 0x0B,
0x5A, 0x57, 0xA9, 0x92, 0x2B, 0xC8, 0x3E, 0xC2, 0xAA, 0xEF, 0xB9, 0x98, 0x2C, 0xA8, 0xAB, 0xF6,
0xA1, 0xBF, 0xBC, 0x8D, 0x97, 0xA2, 0x74, 0xD9, 0xE5, 0x99, 0x85, 0x81, 0x15, 0xB0, 0xE7, 0x8B,
0x48, 0x86, 0xF4, 0x94, 0x9C, 0x62, 0x82, 0xD1, 0x2C, 0x24, 0x4B, 0xAC, 0x7A, 0xB8, 0x4E, 0x4A,
0xD2, 0xF6, 0xAA, 0xED, 0xE0, 0x9C, 0x98, 0xD2, 0xDF, 0xC1, 0xBC, 0xBF, 0x55, 0x7D, 0x40, 0xB5,
0xDE, 0xD4, 0x25, 0xBB, 0x81, 0xF4, 0x07, 0x1D, 0xE7, 0x3C, 0xB4, 0x62, 0xC9, 0x55, 0x0A, 0x3A,
0xD5, 0xCE, 0x97, 0xED, 0x30, 0x76, 0x76, 0x51, 0xBC, 0x8C, 0xE4, 0x54, 0xBE, 0xB7, 0xB5, 0xCD,
0xF8, 0x76, 0x37, 0x53, 0x2C, 0x9F, 0xE4, 0xC7, 0xEB, 0xF5, 0x8D, 0x23, 0x8A, 0xDA, 0xD1, 0xA9,
0xD8, 0x4C, 0x53, 0xF3, 0x49, 0xA7, 0x1A, 0x5D, 0xE5, 0x03, 0x49, 0x52, 0xD3, 0xE2, 0x1F, 0xA5,
0x35, 0x9C, 0xBB, 0x0B, 0xC7, 0x0D, 0xA4, 0x65, 0x54, 0x8B, 0x39, 0xF1, 0x3B, 0x67, 0x21, 0x71,
0x10, 0xE7, 0x76, 0xC4, 0xA8, 0xC2, 0x9D, 0x93, 0xC6, 0x51, 0xBA, 0x23
};
}

View File

@ -0,0 +1,82 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include "dsp/processor.h"
extern "C" {
#include "correct.h"
}
namespace ryfi {
// Size of an encoded reed-solomon block.
inline const int RS_BLOCK_ENC_SIZE = 255;
// Size of a decoded reed-solomon block.
inline const int RS_BLOCK_DEC_SIZE = 223;
// Number of reed-solomon blocks.
inline const int RS_BLOCK_COUNT = 4;
// Scrambler sequence
extern const uint8_t RS_SCRAMBLER_SEQ[RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT];
/**
* RyFi Reed-Solomon Encoder.
*/
class RSEncoder : public dsp::Processor<uint8_t, uint8_t> {
using base_type = dsp::Processor<uint8_t, uint8_t>;
public:
/**
* Create a reed-solomon encoder specifying an input stream.
* @param in Input stream
*/
RSEncoder(dsp::stream<uint8_t>* in = NULL);
// Destructor
~RSEncoder();
/**
* Encode data.
* @param in Input bytes.
* @param out Output bytes.
* @param count Number of input bytes.
* @return Number of output bytes.
*/
int encode(const uint8_t* in, uint8_t* out, int count);
private:
int run();
correct_reed_solomon* rs;
};
/**
* RyFi Reed-Solomon Decoder.
*/
class RSDecoder : public dsp::Processor<uint8_t, uint8_t> {
using base_type = dsp::Processor<uint8_t, uint8_t>;
public:
/**
* Create a reed-solomon decoder specifying an input stream.
* @param in Input stream
*/
RSDecoder(dsp::stream<uint8_t>* in = NULL);
// Destructor
~RSDecoder();
/**
* Decode data.
* @param in Input bytes.
* @param out Output bytes.
* @param count Number of input bytes.
* @return Number of output bytes.
*/
int decode(uint8_t* in, uint8_t* out, int count);
private:
int run();
correct_reed_solomon* rs;
};
}

View File

@ -0,0 +1,177 @@
#include "transmitter.h"
namespace ryfi {
Transmitter::Transmitter(double baudrate, double samplerate) {
// Initialize the DSP
rs.setInput(&in);
conv.setInput(&rs.out);
framer.setInput(&conv.out);
resamp.init(&framer.out, baudrate, samplerate);
rrcTaps = dsp::taps::rootRaisedCosine<float>(511, 0.6, baudrate, samplerate);
// Normalize the taps
float tot = 0.0f;
for (int i = 0; i < rrcTaps.size; i++) {
tot += rrcTaps.taps[i];
}
for (int i = 0; i < rrcTaps.size; i++) {
rrcTaps.taps[i] /= tot;
}
rrc.init(&resamp.out, rrcTaps);
out = &rrc.out;
}
Transmitter::~Transmitter() {
// Stop everything
stop();
}
void Transmitter::start() {
// Do nothing if already running
if (running) { return; }
// Start the worker thread
workerThread = std::thread(&Transmitter::worker, this);
// Start the DSP
rs.start();
conv.start();
framer.start();
resamp.start();
rrc.start();
// Update the running state
running = true;
}
void Transmitter::stop() {
// Do nothing if not running
if (!running) { return; }
// Stop the worker thread
in.stopWriter();
if (workerThread.joinable()) { workerThread.join(); }
in.clearWriteStop();
// Stop the DSP
rs.stop();
conv.stop();
framer.stop();
resamp.stop();
rrc.stop();
// Update the running state
running = false;
}
bool Transmitter::send(const Packet& pkt) {
// Acquire the packet queue
std::lock_guard<std::mutex> lck(packetsMtx);
// If there are too many packets queued up, drop the packet
if (packets.size() >= MAX_QUEUE_SIZE) { return false; }
// Push the packet onto the queue
packets.push(pkt);
}
bool Transmitter::txFrame(const Frame& frame) {
// Serialize the frame
int count = frame.serialize(in.writeBuf);
// Send it off
return in.swap(count);
}
Packet Transmitter::popPacket() {
// Acquire the packet queue
std::unique_lock<std::mutex> lck(packetsMtx);
// If no packets are available, return empty packet
if (!packets.size()) { return Packet(); }
// Pop the front packet and return it
Packet pkt = packets.front();
packets.pop();
return pkt;
}
void Transmitter::worker() {
Frame frame;
Packet pkt;
uint16_t counter = 0;
int pktToWrite = 0;
int pktWritten = 0;
uint8_t* pktBuffer = new uint8_t[Packet::MAX_SERIALIZED_SIZE];
while (true) {
// Initialize the frame
frame.counter = counter++;
frame.firstPacket = PKT_OFFS_NONE;
frame.lastPacket = PKT_OFFS_NONE;
int frameOffset = 0;
// Fill the frame with as much packet data as possible
while (frameOffset < sizeof(Frame::content)) {
// If there is no packet in the process of being sent
if (!pktWritten) {
// If there is not enough space for the size of the packet
if ((sizeof(Frame::content) - frameOffset) < 2) {
// Fill the rest of the frame with noise and send it
for (int i = frameOffset; i < sizeof(Frame::content); i++) { frame.content[i] = rand(); }
break;
}
// Get the next packet
pkt = popPacket();
// If there was an available packet
if (pkt) {
// Serialize the packet
pktToWrite = pkt.serializedSize();
pkt.serialize(pktBuffer);
}
}
// If none was available
if (!pkt) {
// Fill the rest of the frame with noise and send it
for (int i = frameOffset; i < sizeof(Frame::content); i++) { frame.content[i] = rand(); }
break;
}
// If this is the beginning of the packet
if (!pktWritten) {
//flog::debug("Starting to write a {} byte packet at offset {}", pktToWrite-2, frameOffset);
// If this is the first packet of the frame, update its offset
if (frame.firstPacket == PKT_OFFS_NONE) { frame.firstPacket = frameOffset; }
// Update the last packet pointer
frame.lastPacket = frameOffset;
}
// Compute the amount of data writeable to the frame
int writeable = std::min<int>(pktToWrite - pktWritten, sizeof(Frame::content) - frameOffset);
// Copy the data to the frame
memcpy(&frame.content[frameOffset], &pktBuffer[pktWritten], writeable);
pktWritten += writeable;
frameOffset += writeable;
// If the packet is done being sent
if (pktWritten >= pktToWrite) {
// Prepare for a new packet
pktToWrite = 0;
pktWritten = 0;
}
}
// Send the frame
if (!txFrame(frame)) { break; }
}
delete[] pktBuffer;
}
}

View File

@ -0,0 +1,69 @@
#pragma once
#include "dsp/multirate/rational_resampler.h"
#include "dsp/taps/root_raised_cosine.h"
#include "dsp/filter/fir.h"
#include "packet.h"
#include "frame.h"
#include "rs_codec.h"
#include "conv_codec.h"
#include "framing.h"
#include <queue>
#include <mutex>
namespace ryfi {
class Transmitter {
public:
/**
* Create a transmitter.
* @param baudrate Baudrate to use over the air.
* @param samplerate Samplerate of the baseband.
*/
Transmitter(double baudrate, double samplerate);
// Destructor
~Transmitter();
/**
* Start the transmitter's DSP.
*/
void start();
/**
* Stop the transmitter's DSP.
*/
void stop();
/**
* Send a packet.
* @param pkg Packet to send.
* @return True if the packet was send, false if it was dropped.
*/
bool send(const Packet& pkt);
// Baseband output
dsp::stream<dsp::complex_t>* out;
static inline const int MAX_QUEUE_SIZE = 32;
private:
bool txFrame(const Frame& frame);
Packet popPacket();
void worker();
// Packet queue
std::mutex packetsMtx;
std::queue<Packet> packets;
// DSP
dsp::stream<uint8_t> in;
RSEncoder rs;
ConvEncoder conv;
Framer framer;
dsp::multirate::RationalResampler<dsp::complex_t> resamp;
dsp::tap<float> rrcTaps;
dsp::filter::FIR<dsp::complex_t, float> rrc;
bool running = false;
std::thread workerThread;
};
}

View File

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.13)
project(vor_receiver)
file(GLOB_RECURSE SRC "src/*.cpp")
include(${SDRPP_MODULE_CMAKE})
target_include_directories(vor_receiver PRIVATE "src/")

View File

@ -0,0 +1,128 @@
#include <imgui.h>
#include <config.h>
#include <core.h>
#include <gui/style.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <module.h>
#include <filesystem>
#include <dsp/buffer/reshaper.h>
#include <dsp/sink/handler_sink.h>
#include <gui/widgets/constellation_diagram.h>
#include "vor_decoder.h"
#include <fstream>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{
/* Name: */ "vor_receiver",
/* Description: */ "VOR Receiver for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
ConfigManager config;
#define INPUT_SAMPLE_RATE VOR_IN_SR
class VORReceiverModule : public ModuleManager::Instance {
public:
VORReceiverModule(std::string name) {
this->name = name;
// Load config
config.acquire();
// TODO: Load config
config.release();
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, true);
decoder = new vor::Decoder(vfo->output, 1);
decoder->onBearing.bind(&VORReceiverModule::onBearing, this);
decoder->start();
gui::menu.registerEntry(name, menuHandler, this, this);
}
~VORReceiverModule() {
decoder->stop();
sigpath::vfoManager.deleteVFO(vfo);
gui::menu.removeEntry(name);
delete decoder;
}
void postInit() {}
void enable() {
double bw = gui::waterfall.getBandwidth();
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, true);
decoder->setInput(vfo->output);
decoder->start();
enabled = true;
}
void disable() {
decoder->stop();
sigpath::vfoManager.deleteVFO(vfo);
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
static void menuHandler(void* ctx) {
VORReceiverModule* _this = (VORReceiverModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvail().x;
if (!_this->enabled) { style::beginDisabled(); }
ImGui::Text("Bearing: %f°", _this->bearing);
ImGui::Text("Quality: %0.1f%%", _this->quality);
if (!_this->enabled) { style::endDisabled(); }
}
void onBearing(float nbearing, float nquality) {
bearing = (180.0f * nbearing / FL_M_PI);
quality = nquality * 100.0f;
}
std::string name;
bool enabled = true;
// DSP Chain
VFOManager::VFO* vfo;
vor::Decoder* decoder;
float bearing = 0.0f, quality = 0.0f;
};
MOD_EXPORT void _INIT_() {
// Create default recording directory
std::string root = (std::string)core::args["root"];
json def = json({});
config.setPath(root + "/vor_receiver_config.json");
config.load(def);
config.enableAutoSave();
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new VORReceiverModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (VORReceiverModule*)instance;
}
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}

View File

@ -0,0 +1,50 @@
#include "vor_decoder.h"
#define STDDEV_NORM_FACTOR 1.813799364234218f // 2.0f * FL_M_PI / sqrt(12)
namespace vor {
Decoder::Decoder(dsp::stream<dsp::complex_t>* in, double integrationTime) {
rx.init(in);
reshape.init(&rx.out, round(1000.0 * integrationTime), 0);
symSink.init(&reshape.out, dataHandler, this);
}
Decoder::~Decoder() {
// TODO
}
void Decoder::setInput(dsp::stream<dsp::complex_t>* in) {
rx.setInput(in);
}
void Decoder::start() {
rx.start();
reshape.start();
symSink.start();
}
void Decoder::stop() {
rx.stop();
reshape.stop();
symSink.stop();
}
void Decoder::dataHandler(float* data, int count, void* ctx) {
// Get the instance from context
Decoder* _this = (Decoder*)ctx;
// Compute the mean and standard deviation of the
float mean, stddev;
volk_32f_stddev_and_mean_32f_x2(&stddev, &mean, data, count);
// Compute the signal quality
float quality = std::max<float>(1.0f - (stddev / STDDEV_NORM_FACTOR), 0.0f);
// Convert the phase difference to a compass heading
mean = -mean;
if (mean < 0) { mean = 2.0f*FL_M_PI + mean; }
// Call the handler
_this->onBearing(mean, quality);
}
}

View File

@ -0,0 +1,49 @@
#include "vor_receiver.h"
#include <dsp/buffer/reshaper.h>
#include <dsp/sink/handler_sink.h>
#include <utils/new_event.h>
namespace vor {
// Note: hard coded to 22KHz samplerate
class Decoder {
public:
/**
* Create an instance of a VOR decoder.
* @param in Input IQ stream at 22 KHz sampling rate.
* @param integrationTime Integration time of the bearing data in seconds.
*/
Decoder(dsp::stream<dsp::complex_t>* in, double integrationTime);
// Destructor
~Decoder();
/**
* Set the input stream.
* @param in Input IQ stream at 22 KHz sampling rate.
*/
void setInput(dsp::stream<dsp::complex_t>* in);
/**
* Start the decoder.
*/
void start();
/**
* Stop the decoder.
*/
void stop();
/**
* handler(bearing, signalQuality);
*/
NewEvent<float, float> onBearing;
private:
static void dataHandler(float* data, int count, void* ctx);
// DSP
Receiver rx;
dsp::buffer::Reshaper<float> reshape;
dsp::sink::Handler<float> symSink;
};
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,106 @@
#include <dsp/sink.h>
#include <dsp/types.h>
#include <dsp/demod/am.h>
#include <dsp/demod/quadrature.h>
#include <dsp/convert/real_to_complex.h>
#include <dsp/channel/frequency_xlator.h>
#include <dsp/filter/fir.h>
#include <dsp/math/delay.h>
#include <dsp/math/conjugate.h>
#include <dsp/channel/rx_vfo.h>
#include "vor_fm_filter.h"
#include <utils/wav.h>
#define VOR_IN_SR 25e3
namespace vor {
class Receiver : public dsp::Processor<dsp::complex_t, float> {
using base_type = dsp::Processor<dsp::complex_t, float>;
public:
Receiver() {}
Receiver(dsp::stream<dsp::complex_t>* in) { init(in); }
~Receiver() {
if (!base_type::_block_init) { return; }
base_type::stop();
dsp::taps::free(fmfTaps);
}
void init(dsp::stream<dsp::complex_t>* in) {
amd.init(NULL, dsp::demod::AM<float>::CARRIER, VOR_IN_SR, 50.0f / VOR_IN_SR, 5.0f / VOR_IN_SR, 100.0f / VOR_IN_SR, VOR_IN_SR);
amr2c.init(NULL);
fmr2c.init(NULL);
fmx.init(NULL, -9960, VOR_IN_SR);
fmfTaps = dsp::taps::fromArray(FM_TAPS_COUNT, fm_taps);
fmf.init(NULL, fmfTaps);
fmd.init(NULL, 600, VOR_IN_SR);
amde.init(NULL, FM_TAPS_COUNT / 2);
amv.init(NULL, VOR_IN_SR, 1000, 30, 30);
fmv.init(NULL, VOR_IN_SR, 1000, 30, 30);
base_type::init(in);
}
int process(dsp::complex_t* in, float* out, int count) {
// Demodulate the AM outer modulation
volk_32fc_magnitude_32f(amd.out.writeBuf, (lv_32fc_t*)in, count);
amr2c.process(count, amd.out.writeBuf, amr2c.out.writeBuf);
// Isolate the FM subcarrier
fmx.process(count, amr2c.out.writeBuf, fmx.out.writeBuf);
fmf.process(count, fmx.out.writeBuf, fmx.out.writeBuf);
// Demodulate the FM subcarrier
fmd.process(count, fmx.out.writeBuf, fmd.out.writeBuf);
fmr2c.process(count, fmd.out.writeBuf, fmr2c.out.writeBuf);
// Delay the AM signal by the same amount as the FM one
amde.process(count, amr2c.out.writeBuf, amr2c.out.writeBuf);
// Isolate the 30Hz component on both the AM and FM channels
int rcount = amv.process(count, amr2c.out.writeBuf, amv.out.writeBuf);
fmv.process(count, fmr2c.out.writeBuf, fmv.out.writeBuf);
// If no data was returned, we're done for this round
if (!rcount) { return 0; }
// Conjugate FM reference
volk_32fc_conjugate_32fc((lv_32fc_t*)fmv.out.writeBuf, (lv_32fc_t*)fmv.out.writeBuf, rcount);
// Multiply both together
volk_32fc_x2_multiply_32fc((lv_32fc_t*)amv.out.writeBuf, (lv_32fc_t*)amv.out.writeBuf, (lv_32fc_t*)fmv.out.writeBuf, rcount);
// Compute angle
volk_32fc_s32f_atan2_32f(out, (lv_32fc_t*)amv.out.writeBuf, 1.0f, rcount);
return rcount;
}
int run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
int outCount = process(base_type::_in->readBuf, base_type::out.writeBuf, count);
// Swap if some data was generated
base_type::_in->flush();
if (outCount) {
if (!base_type::out.swap(outCount)) { return -1; }
}
return outCount;
}
private:
dsp::demod::AM<float> amd;
dsp::convert::RealToComplex amr2c;
dsp::convert::RealToComplex fmr2c;
dsp::channel::FrequencyXlator fmx;
dsp::tap<float> fmfTaps;
dsp::filter::FIR<dsp::complex_t, float> fmf;
dsp::demod::Quadrature fmd;
dsp::math::Delay<dsp::complex_t> amde;
dsp::channel::RxVFO amv;
dsp::channel::RxVFO fmv;
};
}

View File

@ -6,13 +6,14 @@ cd /root
apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev autoconf libtool xxd
libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
SDRPLAY_ARCH=$(dpkg --print-architecture)
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus
@ -25,10 +26,30 @@ make install
ldconfig
cd ..
# Install librfnm
git clone https://github.com/AlexandreRouma/librfnm
cd librfnm
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -6,13 +6,14 @@ cd /root
apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev autoconf libtool xxd
libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
SDRPLAY_ARCH=$(dpkg --print-architecture)
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus
@ -25,10 +26,30 @@ make install
ldconfig
cd ..
# Install librfnm
git clone https://github.com/AlexandreRouma/librfnm
cd librfnm
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -6,13 +6,14 @@ cd /root
apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev autoconf libtool xxd
libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
SDRPLAY_ARCH=$(dpkg --print-architecture)
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus
@ -25,10 +26,30 @@ make install
ldconfig
cd ..
# Install librfnm
git clone https://github.com/AlexandreRouma/librfnm
cd librfnm
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -6,13 +6,14 @@ cd /root
apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev autoconf libtool xxd
libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
SDRPLAY_ARCH=$(dpkg --print-architecture)
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus
@ -25,10 +26,30 @@ make install
ldconfig
cd ..
# Install librfnm
git clone https://github.com/AlexandreRouma/librfnm
cd librfnm
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -12,13 +12,14 @@ apt update
# Install dependencies and tools
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev libudev-dev autoconf libtool xxd
libcodec2-dev libudev-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
SDRPLAY_ARCH=$(dpkg --print-architecture)
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install a more recent libusb version
@ -51,6 +52,26 @@ make install
ldconfig
cd ..
# Install librfnm
git clone https://github.com/AlexandreRouma/librfnm
cd librfnm
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
# Fix missing .pc file for codec2
echo 'prefix=/usr/' >> /usr/share/pkgconfig/codec2.pc
echo 'libdir=/usr/include/x86_64-linux-gnu/' >> /usr/share/pkgconfig/codec2.pc
@ -66,7 +87,7 @@ echo 'Cflags: -I/usr/include/codec2' >> /usr/share/pkgconfig/codec2.pc
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
# Generate package

View File

@ -6,13 +6,14 @@ cd /root
apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev autoconf libtool xxd
libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
SDRPLAY_ARCH=$(dpkg --print-architecture)
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus
@ -25,10 +26,30 @@ make install
ldconfig
cd ..
# Install librfnm
git clone https://github.com/AlexandreRouma/librfnm
cd librfnm
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -6,13 +6,14 @@ cd /root
apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev autoconf libtool xxd
libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
SDRPLAY_ARCH=$(dpkg --print-architecture)
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus
@ -25,10 +26,30 @@ make install
ldconfig
cd ..
# Install librfnm
git clone https://github.com/AlexandreRouma/librfnm
cd librfnm
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -6,13 +6,14 @@ cd /root
apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev autoconf libtool xxd
libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
SDRPLAY_ARCH=$(dpkg --print-architecture)
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus
@ -25,10 +26,30 @@ make install
ldconfig
cd ..
# Install librfnm
git clone https://github.com/AlexandreRouma/librfnm
cd librfnm
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -1,4 +1,4 @@
FROM ubuntu:mantic
FROM ubuntu:oracular
ENV DEBIAN_FRONTEND=noninteractive
COPY do_build.sh /root
RUN chmod +x /root/do_build.sh

View File

@ -6,13 +6,14 @@ cd /root
apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev autoconf libtool xxd
libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.1
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
SDRPLAY_ARCH=$(dpkg --print-architecture)
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
7z x ./SDRplay_RSP_API-Linux-3.15.2
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus
@ -25,10 +26,30 @@ make install
ldconfig
cd ..
# Install librfnm
git clone https://github.com/AlexandreRouma/librfnm
cd librfnm
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
# Install libfobos
git clone https://github.com/AlexandreRouma/libfobos
cd libfobos
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2
cd ..

View File

@ -28,6 +28,8 @@ bundle_is_not_to_be_installed() {
if [ "$1" = "Security" ]; then echo 1; fi
if [ "$1" = "AppleFSCompression" ]; then echo 1; fi
if [ "$1" = "libsdrplay_api.so.3.14" ]; then echo 1; fi
if [ "$1" = "libsdrplay_api.so.3.15" ]; then echo 1; fi
if [ "$1" = "libxml2.2.dylib" ]; then echo 1; fi
}
# ========================= FOR INTERNAL USE ONLY =========================

View File

@ -8,7 +8,7 @@ mkdir sdrpp_debian_amd64/DEBIAN
# Create package info
echo Create package info
echo Package: sdrpp >> sdrpp_debian_amd64/DEBIAN/control
echo Version: 1.2.0$BUILD_NO >> sdrpp_debian_amd64/DEBIAN/control
echo Version: 1.2.1$BUILD_NO >> sdrpp_debian_amd64/DEBIAN/control
echo Maintainer: Ryzerth >> sdrpp_debian_amd64/DEBIAN/control
echo Architecture: all >> sdrpp_debian_amd64/DEBIAN/control
echo Description: Bloat-free SDR receiver software >> sdrpp_debian_amd64/DEBIAN/control

View File

@ -22,7 +22,7 @@ cp -R root/res/* $BUNDLE/Contents/Resources/
bundle_create_icns root/res/icons/sdrpp.macos.png $BUNDLE/Contents/Resources/sdrpp
# Create the property list
bundle_create_plist sdrpp SDR++ org.sdrpp.sdrpp 1.2.0 sdrp sdrpp sdrpp $BUNDLE/Contents/Info.plist
bundle_create_plist sdrpp SDR++ org.sdrpp.sdrpp 1.2.1 sdrp sdrpp sdrpp $BUNDLE/Contents/Info.plist
# ========================= Install binaries =========================
@ -35,11 +35,14 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/airspyhf_source/airspyhf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/bladerf_source/bladerf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/file_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/fobossdr_source/fobossdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hackrf_source/hackrf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hermes_source/hermes_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/limesdr_source/limesdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/network_source/network_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/perseus_source/perseus_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/plutosdr_source/plutosdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfnm_source/rfnm_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfspace_source/rfspace_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_sdr_source/rtl_sdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_tcp_source/rtl_tcp_source.dylib

View File

@ -24,6 +24,9 @@ cp 'C:/Program Files/PothosSDR/bin/bladeRF.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/file_source/Release/file_source.dll sdrpp_windows_x64/modules/
cp $build_dir/source_modules/fobossdr_source/Release/fobossdr_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/RigExpert/Fobos/bin/fobos.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/hackrf_source/Release/hackrf_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/PothosSDR/bin/hackrf.dll' sdrpp_windows_x64/
@ -32,6 +35,8 @@ cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windo
cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/network_source/Release/network_source.dll sdrpp_windows_x64/modules/
cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/
@ -39,6 +44,11 @@ cp $build_dir/source_modules/plutosdr_source/Release/plutosdr_source.dll sdrpp_w
cp 'C:/Program Files/PothosSDR/bin/libiio.dll' sdrpp_windows_x64/
cp 'C:/Program Files/PothosSDR/bin/libad9361.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/rfnm_source/Release/rfnm_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/RFNM/bin/rfnm.dll' sdrpp_windows_x64/
cp 'C:/Program Files/RFNM/bin/spdlog.dll' sdrpp_windows_x64/
cp 'C:/Program Files/RFNM/bin/fmt.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/rfspace_source/Release/rfspace_source.dll sdrpp_windows_x64/modules/
cp $build_dir/source_modules/rtl_sdr_source/Release/rtl_sdr_source.dll sdrpp_windows_x64/modules/

View File

@ -168,10 +168,9 @@ public:
writer.setSamplerate(samplerate);
// Open file
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
std::string extension = ".wav";
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, type, vfoName) + extension);
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, recMode, vfoName) + extension);
if (!writer.open(expandedPath)) {
flog::error("Failed to open file for recording: {0}", expandedPath);
return;
@ -249,7 +248,6 @@ private:
}
ImGui::Columns(1, CONCAT("EndRecorderModeColumns##_", _this->name), false);
ImGui::EndGroup();
if (_this->recording) { style::endDisabled(); }
// Recording path
if (_this->folderSelect.render("##_recorder_fold_" + _this->name)) {
@ -284,8 +282,11 @@ private:
config.release(true);
}
if (_this->recording) { style::endDisabled(); }
// Show additional audio options
if (_this->recMode == RECORDER_MODE_AUDIO) {
if (_this->recording) { style::beginDisabled(); }
ImGui::LeftLabel("Stream");
ImGui::FillWidth();
if (ImGui::Combo(CONCAT("##_recorder_stream_", _this->name), &_this->streamId, _this->audioStreams.txt)) {
@ -294,6 +295,7 @@ private:
config.conf[_this->name]["audioStream"] = _this->audioStreams.key(_this->streamId);
config.release(true);
}
if (_this->recording) { style::endDisabled(); }
_this->updateAudioMeter(_this->audioLvl);
ImGui::FillWidth();
@ -449,7 +451,7 @@ private:
{ RADIO_IFACE_MODE_RAW, "RAW" }
};
std::string genFileName(std::string templ, std::string type, std::string name) {
std::string genFileName(std::string templ, int mode, std::string name) {
// Get data
time_t now = time(0);
tm* ltm = localtime(&now);
@ -459,6 +461,9 @@ private:
freq += gui::waterfall.vfos[name]->generalOffset;
}
// Select the recording type string
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
// Format to string
char freqStr[128];
char hourStr[128];
@ -467,7 +472,7 @@ private:
char dayStr[128];
char monStr[128];
char yearStr[128];
const char* modeStr = "Unknown";
const char* modeStr = (recMode == RECORDER_MODE_AUDIO) ? "Unknown" : "IQ";
sprintf(freqStr, "%.0lfHz", freq);
sprintf(hourStr, "%02d", ltm->tm_hour);
sprintf(minStr, "%02d", ltm->tm_min);

View File

@ -3,6 +3,7 @@
#include <gui/gui.h>
#include <gui/style.h>
#include <signal_path/signal_path.h>
#include <chrono>
SDRPP_MOD_INFO{
/* Name: */ "scanner",

View File

@ -41,14 +41,13 @@ To create a desktop shortcut, rightclick the exe and select `Send to -> Desktop
Download the latest release from [the Releases page](https://github.com/AlexandreRouma/SDRPlusPlus/releases) and extract to the directory of your choice.
Then, run:
Then, use apt to install it:
```sh
sudo apt install libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev
sudo dpkg -i sdrpp_debian_amd64.deb
sudo apt install path/to/the/sdrpp_debian_amd64.deb
```
If `libvolk2-dev` is not available, use `libvolk1-dev`.
**IMPORTANT: You must install the drivers for your SDR. Follow instructions from your manufacturer as to how to do this on your particular distro.**
### Arch-based
@ -325,13 +324,16 @@ Modules in beta are still included in releases for the most part but not enabled
| audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ |
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ |
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
| fobossdr_source | Working | libfobos | OPT_BUILD_FOBOSSDR_SOURCE | ✅ | ✅ | ✅ |
| hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ |
| harogic_source | Beta | htra_api | OPT_BUILD_HAROGIC_SOURCE | ⛔ | ⛔ | ✅ |
| hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ✅ |
| kcsdr_source | Unfinished | libkcsdr | OPT_BUILD_KCSDR_SOURCE | ⛔ | ⛔ | ⛔ |
| limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ |
| network_source | Unfinished | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | |
| network_source | Beta | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | |
| perseus_source | Beta | libperseus-sdr | OPT_BUILD_PERSEUS_SOURCE | ⛔ | ✅ | ✅ |
| plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE | ✅ | ✅ | ✅ |
| rfnm_source | Beta | librfnm | OPT_BUILD_RFNM_SOURCE | ⛔ | | |
| rfnm_source | Beta | librfnm | OPT_BUILD_RFNM_SOURCE | ⛔ | | |
| rfspace_source | Working | - | OPT_BUILD_RFSPACE_SOURCE | ✅ | ✅ | ✅ |
| rtl_sdr_source | Working | librtlsdr | OPT_BUILD_RTL_SDR_SOURCE | ✅ | ✅ | ✅ |
| rtl_tcp_source | Working | - | OPT_BUILD_RTL_TCP_SOURCE | ✅ | ✅ | ✅ |
@ -339,9 +341,9 @@ Modules in beta are still included in releases for the most part but not enabled
| sdrpp_server_source | Working | - | OPT_BUILD_SDRPP_SERVER_SOURCE | ✅ | ✅ | ✅ |
| soapy_source | Deprecated | soapysdr | OPT_BUILD_SOAPY_SOURCE | ⛔ | ⛔ | ⛔ |
| spectran_source | Unfinished | RTSA Suite | OPT_BUILD_SPECTRAN_SOURCE | ⛔ | ⛔ | ⛔ |
| spectran_http_source | Beta | - | OPT_BUILD_SPECTRAN_HTTP_SOURCE | ✅ | ✅ | |
| spectran_http_source | Beta | - | OPT_BUILD_SPECTRAN_HTTP_SOURCE | ✅ | ✅ | |
| spyserver_source | Working | - | OPT_BUILD_SPYSERVER_SOURCE | ✅ | ✅ | ✅ |
| usrp_source | Beta | libuhd | OPT_BUILD_USRP_SOURCE | ⛔ | ⛔ | |
| usrp_source | Beta | libuhd | OPT_BUILD_USRP_SOURCE | ⛔ | ⛔ | |
## Sinks
@ -350,20 +352,22 @@ Modules in beta are still included in releases for the most part but not enabled
| android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | ✅ (Android only) |
| audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ |
| network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ |
| new_portaudio_sink | Beta | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
| portaudio_sink | Beta | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
| new_portaudio_sink | Working | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
| portaudio_sink | Working | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
## Decoders
| Name | Stage | Dependencies | Option | Built by default| Built in Release | Enabled in SDR++ by default |
|---------------------|------------|--------------|-------------------------------|:---------------:|:----------------:|:---------------------------:|
| atv_decoder | Unfinished | - | OPT_BUILD_ATV_DECODER | ⛔ | ⛔ | ⛔ |
| dab_decoder | Unfinished | - | OPT_BUILD_DAB_DECODER | ⛔ | ⛔ | ⛔ |
| falcon9_decoder | Unfinished | ffplay | OPT_BUILD_FALCON9_DECODER | ⛔ | ⛔ | ⛔ |
| kgsstv_decoder | Unfinished | - | OPT_BUILD_KGSSTV_DECODER | ⛔ | ⛔ | ⛔ |
| m17_decoder | Working | - | OPT_BUILD_M17_DECODER | ⛔ | ✅ | ⛔ |
| meteor_demodulator | Working | - | OPT_BUILD_METEOR_DEMODULATOR | ✅ | ✅ | ⛔ |
| pager_decoder | Unfinished | - | OPT_BUILD_PAGER_DECODER | ⛔ | ⛔ | ⛔ |
| radio | Working | - | OPT_BUILD_RADIO | ✅ | ✅ | ✅ |
| radio | Unfinished | - | OPT_BUILD_VOR_RECEIVER | ⛔ | ⛔ | ⛔ |
| weather_sat_decoder | Unfinished | - | OPT_BUILD_WEATHER_SAT_DECODER | ⛔ | ⛔ | ⛔ |
## Misc
@ -416,8 +420,8 @@ If you still have an issue, please open an issue about it or ask on the discord.
# Contributing
Feel free to submit pull requests and report bugs via the GitHub issue tracker.
I will soon publish a contributing.md listing the code style to use.
Feel free to submit band plans via the GitHub issue tracker.
For code changes, please create a feature request instead.
# Credits
@ -437,15 +441,18 @@ I will soon publish a contributing.md listing the code style to use.
* Flinger Films
* [Frank Werner (HB9FXQ)](https://twitter.com/HB9FXQ)
* gringogrigio
* Jandro
* Jeff Moe
* Joe Cupano
* KD1SQ
* Kezza
* Krys Kamieniecki
* Lee Donaghy
* Lee KD1SQ
* Lee (KD1SQ)
* .lozenge. (Hank Hill)
* Martin Herren (HB9FXX)
* NeoVilsonWong
* Nitin (VU2JEK)
* ON4MU
* [Passion-Radio.com](https://passion-radio.com/)
* Paul Maine

View File

@ -2,8 +2,8 @@
"name": "France",
"country_name": "France",
"country_code": "FR",
"author_name": "Fred F4EED",
"author_url": "http://f4eed.wordpress.com",
"author_name": "Fred F4EED, Armand31",
"author_url": "http://f4eed.wordpress.com, https://github.com/Armand31",
"bands": [
{
"name": "137KHz - Radioamateur",
@ -355,7 +355,7 @@
"end": 54000000
},
{
"name": "Bande FM - Radiodif.",
"name": "Radiodiffusion - Bande FM",
"type": "broadcast",
"start": 80000000,
"end": 108000000
@ -396,6 +396,12 @@
"start": 162362500,
"end": 162587500
},
{
"name": "Radiodiffusion - Bande DAB",
"type": "broadcast",
"start": 174000000,
"end": 223000000
},
{
"name": "Military Aviation",
"type": "military",
@ -408,6 +414,12 @@
"start": 240000000,
"end": 270000000
},
{
"name": "Police (TETRAPOL)",
"type": "military",
"start": 380000000,
"end": 400000000
},
{
"name": "70cm - Radioamateur",
"type": "amateur",
@ -420,12 +432,24 @@
"start": 446000000,
"end": 446200000
},
{
"name": "TNT (DVB-T)",
"type": "broadcast",
"start": 470000000,
"end": 694000000
},
{
"name": "23cm - Radioamateur",
"type": "amateur",
"start": 1240000000,
"end": 1300000000
},
{
"name": "Radiodiffusion - Bande DAB",
"type": "broadcast",
"start": 1452000000,
"end": 1492000000
},
{
"name": "13cm - Radioamateur",
"type": "amateur",

View File

@ -0,0 +1,231 @@
{
"name": "Ireland",
"country_name": "Republic Of Ireland",
"country_code": "IE",
"author_name": "Oskar Dudek",
"author_url": "",
"bands": [
{
"name": "2200m Ham Band",
"type": "amateur",
"start": 135700,
"end": 137800
},
{
"name": "Long wave",
"type": "broadcast",
"start": 148500,
"end": 282500
},
{
"name": "AM broadcast",
"type": "broadcast",
"start": 531000,
"end": 1602000
},
{
"name": "160m ham band",
"type": "amateur",
"start": 1810000,
"end": 2000000
},
{
"name": "120m SW broadcast",
"type": "broadcast",
"start": 2300000,
"end": 2495000
},
{
"name": "90m SW Broadcast",
"type": "broadcast",
"start": 3200000,
"end": 3400000
},
{
"name": "80m ham band",
"type": "amateur",
"start": 3500000,
"end": 3800000
},
{
"name": "75m SW Broadcast",
"type": "broadcast",
"start": 3900000,
"end": 4000000
},
{
"name": "60m SW Broadcast",
"type": "broadcast",
"start": 4750000,
"end": 5060000
},
{
"name": "60m ham band",
"type": "amateur",
"start": 5351500,
"end": 5366500
},
{
"name": "49m SW Broadcast",
"type": "broadcast",
"start": 5900000,
"end": 6200000
},
{
"name": "40m ham band",
"type": "amateur",
"start": 7000000,
"end": 7200000
},
{
"name": "40m SW Broadcast",
"type": "broadcast",
"start": 7200000,
"end": 7450000
},
{
"name": "31m SW Broadcast",
"type": "broadcast",
"start": 9400000,
"end": 9900000
},
{
"name": "30m ham band",
"type": "amateur",
"start": 10100000,
"end": 10150000
},
{
"name": "25m SW Broadcast",
"type": "broadcast",
"start": 11600000,
"end": 12100000
},
{
"name": "22m SW Broadcast",
"type": "broadcast",
"start": 13570000,
"end": 13870000
},
{
"name": "20m ham band",
"type": "amateur",
"start": 14000000,
"end": 14350000
},
{
"name": "19m SW Broadcast",
"type": "broadcast",
"start": 15100000,
"end": 15800000
},
{
"name": "17m ham band",
"type": "amateur",
"start": 18068000,
"end": 18168000
},
{
"name": "16m SW Broadcast",
"type": "broadcast",
"start": 17480000,
"end": 17900000
},
{
"name": "15m SW Broadcast",
"type": "broadcast",
"start": 18900000,
"end": 19020000
},
{
"name": "15m ham band",
"type": "amateur",
"start": 21000000,
"end": 21450000
},
{
"name": "13m SW Broadcast",
"type": "broadcast",
"start": 21450000,
"end": 21850000
},
{
"name": "12m ham band",
"type": "amateur",
"start": 24890000,
"end": 24990000
},
{
"name": "11m SW Broadcast",
"type": "broadcast",
"start": 25670000,
"end": 26100000
},
{
"name": "CB",
"type": "amateur",
"start": 26965000,
"end": 27405000
},
{
"name": "10m ham band",
"type": "amateur",
"start": 28000000,
"end": 29700000
},
{
"name": "FM Broadcast",
"type": "broadcast",
"start": 87500000,
"end": 108000000
},
{
"name": "Airband VOR/ILS",
"type": "aviation",
"start": 108000000,
"end": 117900000
},
{
"name": "Airband Voice",
"type": "aviation",
"start": 118000000,
"end": 137000000
},
{
"name": "Polar orbiting satellites",
"type": "satellite",
"start": 137000000,
"end": 138000000
},
{
"name": "6m ham band",
"type": "amateur",
"start": 50000000,
"end": 52000000
},
{
"name": "4m ham band",
"type": "amateur",
"start": 70000000,
"end": 70500000
},
{
"name": "2m ham band",
"type": "amateur",
"start": 144000000,
"end": 146000000
},
{
"name": "70cm ham band",
"type": "amateur",
"start": 430000000,
"end": 440000000
},
{
"name": "ADS-B",
"type": "aviation",
"start": 1089000000,
"end": 1091000000
}
]
}

View File

@ -0,0 +1,549 @@
{
"name": "Republic of Korea",
"country_name": "Republic of Korea",
"country_code": "KR",
"author_name": "SeoyeonBae",
"author_url": "https://github.com/bsy0317",
"bands": [
{
"name": "Radio Navigation",
"type": "aviation",
"start": 8300,
"end": 14000
},
{
"name": "Coastal Telegraph",
"type": "marine",
"start": 14000,
"end": 19950
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 19950,
"end": 20250
},
{
"name": "Coastal Telegraph",
"type": "marine",
"start": 20250,
"end": 70000
},
{
"name": "Radio Navigation",
"type": "navigation",
"start": 70000,
"end": 160000
},
{
"name": "Aviation Radio Navigation",
"type": "aviation",
"start": 160000,
"end": 285000
},
{
"name": "Aviation Maritime Radiobeacon",
"type": "aviation",
"start": 285000,
"end": 325000
},
{
"name": "Aviation Radio Navigation",
"type": "aviation",
"start": 325000,
"end": 472000
},
{
"name": "Amateur",
"type": "amateur",
"start": 472000,
"end": 479000
},
{
"name": "International Distress Safety Call",
"type": "marine",
"start": 479000,
"end": 505000
},
{
"name": "Maritime Telegraph",
"type": "marine",
"start": 505000,
"end": 526500
},
{
"name": "Standard Broadcast",
"type": "broadcast",
"start": 526500,
"end": 1606500
},
{
"name": "Radiobuoy",
"type": "navigation",
"start": 1606500,
"end": 1800000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 1800000,
"end": 1825000
},
{
"name": "Radiobuoy Control LORAN",
"type": "radiolocation",
"start": 1825000,
"end": 2000000
},
{
"name": "Radiobuoy",
"type": "fixed",
"start": 2000000,
"end": 2065000
},
{
"name": "Distress Call",
"type": "marine",
"start": 2065000,
"end": 2107000
},
{
"name": "International Distress Search and Rescue",
"type": "mobile",
"start": 2173500,
"end": 2190500
},
{
"name": "Road Management",
"type": "fixed",
"start": 2194000,
"end": 2495000
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 2495000,
"end": 2505000
},
{
"name": "Ship Station Telephone",
"type": "fixed",
"start": 2505000,
"end": 2850000
},
{
"name": "Aviation Mobile R",
"type": "aviation",
"start": 2850000,
"end": 3025000
},
{
"name": "Aviation Mobile OR",
"type": "aviation",
"start": 3025000,
"end": 3155000
},
{
"name": "Aviation Mobile R",
"type": "aviation",
"start": 3400000,
"end": 3500000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 3500000,
"end": 3550000
},
{
"name": "Experimental Station",
"type": "fixed",
"start": 3550000,
"end": 3790000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 3790000,
"end": 3800000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 3900000,
"end": 3950000
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 3995000,
"end": 4005000
},
{
"name": "Ship Station Telephone",
"type": "marine",
"start": 4005000,
"end": 4063000
},
{
"name": "Oceanographic Data",
"type": "marine",
"start": 4063000,
"end": 4065000
},
{
"name": "Ship Station Duplex Telephone",
"type": "marine",
"start": 4065000,
"end": 4146000
},
{
"name": "Ship Station Simplex Telephone",
"type": "marine",
"start": 4146000,
"end": 4152000
},
{
"name": "Ship Station Wideband Telegraph Fax",
"type": "marine",
"start": 4152000,
"end": 4172000
},
{
"name": "Ship Station Narrowband",
"type": "marine",
"start": 4172000,
"end": 4181750
},
{
"name": "Ship Station A1A Morse Code Communication",
"type": "marine",
"start": 4186750,
"end": 4202250
},
{
"name": "Radiolocation",
"type": "radiolocation",
"start": 4438000,
"end": 4488000
},
{
"name": "Calling Response",
"type": "fixed",
"start": 4488000,
"end": 4650000
},
{
"name": "Aviation Mobile R",
"type": "aviation",
"start": 4650000,
"end": 4850000
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 4995000,
"end": 5005000
},
{
"name": "Search Rescue",
"type": "aviation",
"start": 5480000,
"end": 5730000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 5900000,
"end": 5950000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 5950000,
"end": 6200000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 7000000,
"end": 7100000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 7100000,
"end": 7200000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 7200000,
"end": 7450000
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 7995000,
"end": 8005000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 9400000,
"end": 9500000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 9500000,
"end": 9900000
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 9995000,
"end": 10005000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 10100000,
"end": 10150000
},
{
"name": "Aviation Mobile",
"type": "aviation",
"start": 10150000,
"end": 11600000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 11600000,
"end": 11650000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 11650000,
"end": 12050000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 12050000,
"end": 12100000
},
{
"name": "Aviation Mobile",
"type": "aviation",
"start": 13260000,
"end": 13360000
},
{
"name": "Radio Astronomy",
"type": "astronomy",
"start": 13360000,
"end": 13410000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 13570000,
"end": 13600000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 13600000,
"end": 13800000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 13800000,
"end": 13870000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 14000000,
"end": 14350000
},
{
"name": "Aviation Mobile",
"type": "aviation",
"start": 15010000,
"end": 15100000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 15100000,
"end": 15600000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 15600000,
"end": 15800000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 15800000,
"end": 15995000
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 15995000,
"end": 16005000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 18900000,
"end": 19020000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 21000000,
"end": 21450000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 21450000,
"end": 21850000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 24890000,
"end": 24990000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 25670000,
"end": 26100000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 28000000,
"end": 29700000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 50000000,
"end": 54000000
},
{
"name": "TV Broadcast",
"type": "broadcast",
"start": 54000000,
"end": 72000000
},
{
"name": "Flood Warning",
"type": "broadcast",
"start": 72000000,
"end": 74800000
},
{
"name": "TV Broadcast",
"type": "broadcast",
"start": 76000000,
"end": 88000000
},
{
"name": "FM Broadcast",
"type": "broadcast",
"start": 88000000,
"end": 100000000
},
{
"name": "FM Broadcast",
"type": "broadcast",
"start": 100000000,
"end": 108000000
},
{
"name": "ILS Localizer VOR",
"type": "fixed",
"start": 108000000,
"end": 117975000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 144000000,
"end": 146000000
},
{
"name": "General Communication",
"type": "fixed",
"start": 146000000,
"end": 148000000
},
{
"name": "Low Power Device",
"type": "fixed",
"start": 162037500,
"end": 174000000
},
{
"name": "TV Broadcast",
"type": "broadcast",
"start": 174000000,
"end": 216000000
},
{
"name": "Low Power Device",
"type": "fixed",
"start": 216000000,
"end": 230000000
},
{
"name": "Low Power Device",
"type": "fixed",
"start": 273000000,
"end": 322000000
},
{
"name": "Personal Radio",
"type": "fixed",
"start": 420000000,
"end": 470000000
},
{
"name": "Public Network",
"type": "broadcast",
"start": 698000000,
"end": 806000000
},
{
"name": "Low Power Device",
"type": "fixed",
"start": 942000000,
"end": 960000000
},
{
"name": "Satellite Mobile Communication",
"type": "fixed",
"start": 15250000000,
"end": 16605000000
},
{
"name": "Mobile Communication",
"type": "mobile",
"start": 25000000000,
"end": 37000000000
}
]
}

View File

@ -0,0 +1,285 @@
{
"name": "Turkey",
"country_name": "Turkey",
"country_code": "TR",
"author_name": "Yunus TA2PEA",
"author_url": "https://github.com/ycanerol",
"bands": [
{
"name": "LW",
"type": "amateur",
"start": 135700,
"end": 137800
},
{
"name": "630m",
"type": "amateur",
"start": 472000,
"end": 479000
},
{
"name": "160m",
"type": "amateur",
"start": 1810000,
"end": 1850000
},
{
"name": "80m",
"type": "amateur",
"start": 3500000,
"end": 3800000
},
{
"name": "60m",
"type": "amateur",
"start": 5351500,
"end": 5366500
},
{
"name": "40m",
"type": "amateur",
"start": 7000000,
"end": 7200000
},
{
"name": "30m",
"type": "amateur",
"start": 10100000,
"end": 10150000
},
{
"name": "20m",
"type": "amateur",
"start": 14000000,
"end": 14350000
},
{
"name": "17m",
"type": "amateur",
"start": 18068000,
"end": 18168000
},
{
"name": "15m",
"type": "amateur",
"start": 21000000,
"end": 21450000
},
{
"name": "12m",
"type": "amateur",
"start": 24890000,
"end": 24990000
},
{
"name": "CB",
"type": "other",
"start": 26565000,
"end": 27405000
},
{
"name": "Pagers",
"type": "amateur",
"start": 27750000,
"end": 28000000
},
{
"name": "10m",
"type": "amateur",
"start": 28000000,
"end": 29700000
},
{
"name": "6m",
"type": "amateur",
"start": 50030000,
"end": 51000000
},
{
"name": "FM",
"type": "broadcast",
"start": 87500000,
"end": 108000000
},
{
"name": "Airband VOR/ILS",
"type": "aviation",
"start": 108000000,
"end": 117975000
},
{
"name": "Airband Voice",
"type": "aviation",
"start": 117975000,
"end": 137000000
},
{
"name": "2m",
"type": "amateur",
"start": 144000000,
"end": 146000000
},
{
"name": "Sayac Okuma",
"type": "other",
"start": 169400000,
"end": 169475000
},
{
"name": "Pagers",
"type": "other",
"start": 167000000,
"end": 167100000
},
{
"name": "Public announcement systems",
"type": "other",
"start": 173882500,
"end": 174000000
},
{
"name": "DVB-T",
"type": "broadcast",
"start": 174000000,
"end": 216000000
},
{
"name": "T-DAB",
"type": "broadcast",
"start": 216000000,
"end": 233000000
},
{
"name": "ILS-Glide Path",
"type": "aviation",
"start": 328600000,
"end": 335400000
},
{
"name": "Public Safety/Emergency",
"type": "other",
"start": 380000000,
"end": 385000000
},
{
"name": "Public Safety/Emergency",
"type": "other",
"start": 390000000,
"end": 395000000
},
{
"name": "70cm",
"type": "amateur",
"start": 430200000,
"end": 430700000
},
{
"name": "70cm-RepeaterRX",
"type": "amateur",
"start": 431550000,
"end": 431825000
},
{
"name": "70cm",
"type": "amateur",
"start": 432000000,
"end": 432975000
},
{
"name": "70cm",
"type": "amateur",
"start": 433400000,
"end": 434000000
},
{
"name": "70cm",
"type": "amateur",
"start": 435000000,
"end": 438000000
},
{
"name": "70cm-RepeaterTX",
"type": "amateur",
"start": 439150000,
"end": 439425000
},
{
"name": "Public announcement systems",
"type": "other",
"start": 445250000,
"end": 445462500
},
{
"name": "PMR446",
"type": "other",
"start": 446006250,
"end": 446196875
},
{
"name": "RFID",
"type": "other",
"start": 865000000,
"end": 868000000
},
{
"name": "RFID",
"type": "other",
"start": 916100000,
"end": 918900000
},
{
"name": "23cm",
"type": "amateur",
"start": 1240000000,
"end": 1300000000
},
{
"name": "DECT",
"type": "other",
"start": 1880000000,
"end": 1900000000
},
{
"name": "5GHz",
"type": "amateur",
"start": 5650000000,
"end": 5670000000
},
{
"name": "5GHz",
"type": "amateur",
"start": 5820000000,
"end": 5850000000
},
{
"name": "3cm",
"type": "amateur",
"start": 104500000000,
"end": 104520000000
},
{
"name": "24GHz",
"type": "amateur",
"start": 24000000000,
"end": 24050000000
},
{
"name": "47GHz",
"type": "amateur",
"start": 47000000000,
"end": 47200000000
},
{
"name": "75GHz",
"type": "amateur",
"start": 75500000000,
"end": 7600000000
},
{
"name": "134GHz",
"type": "amateur",
"start": 134000000000,
"end": 142000000000
}
]
}

View File

@ -217,14 +217,19 @@ private:
}
void startServer() {
if (modeId == SINK_MODE_TCP) {
listener = net::listen(hostname, port);
if (listener) {
listener->acceptAsync(clientHandler, this);
try {
if (modeId == SINK_MODE_TCP) {
listener = net::listen(hostname, port);
if (listener) {
listener->acceptAsync(clientHandler, this);
}
}
else {
conn = net::openUDP("0.0.0.0", port, hostname, port, false);
}
}
else {
conn = net::openUDP("0.0.0.0", port, hostname, port, false);
catch (const std::exception& e) {
flog::error("Failed to open socket: {}", e.what());
}
}

View File

@ -184,6 +184,9 @@ private:
AudioSourceModule* _this = (AudioSourceModule*)ctx;
if (_this->running) { return; }
// If no device is selected, give up
if (_this->selectedDevice.empty()) { return; }
// Stream options
RtAudio::StreamParameters parameters;
parameters.deviceId = _this->devices[_this->devId].id;

View File

@ -10,6 +10,7 @@
#include <libbladeRF.h>
#include <gui/smgui.h>
#include <algorithm>
#include <utils/optionlist.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -37,6 +38,10 @@ public:
BladeRFSourceModule(std::string name) {
this->name = name;
// Define clocks
clocks.define("onboard", "On-Board", CLOCK_SELECT_ONBOARD);
clocks.define("external", "External", CLOCK_SELECT_EXTERNAL);
sampleRate = 1000000.0;
handler.ctx = this;
@ -267,6 +272,15 @@ public:
}
config.release(true);
// Load clock source
clkId = clocks.keyId("onboard");
if (config.conf["devices"][selectedSerial].contains("clock")) {
std::string clkStr = config.conf["devices"][selectedSerial]["clock"];
if (clocks.keyExists(clkStr)) {
clkId = clocks.keyId(clkStr);
}
}
// Load gain mode
if (config.conf["devices"][selectedSerial].contains("gainMode")) {
std::string gm = config.conf["devices"][selectedSerial]["gainMode"];
@ -364,6 +378,7 @@ private:
if (_this->bufferSize < 1024) { _this->bufferSize = 1024; }
// Setup device parameters
_this->setClockSource(_this->clocks[_this->clkId]);
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);
@ -486,6 +501,19 @@ private:
}
}
SmGui::LeftLabel("Clock Source");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_balderf_clk_sel_", _this->name), &_this->clkId, _this->clocks.txt)) {
if (_this->running) {
_this->setClockSource(_this->clocks[_this->clkId]);
}
if (_this->selectedSerial != "") {
config.acquire();
config.conf["devices"][_this->selectedSerial]["clock"] = _this->clocks.key(_this->clkId);
config.release(true);
}
}
// General config BS
SmGui::LeftLabel("Gain control mode");
SmGui::FillWidth();
@ -537,6 +565,15 @@ private:
}
}
void setClockSource(bladerf_clock_select clk) {
if (selectedBladeType == BLADERF_TYPE_V1) {
bladerf_set_smb_mode(openDev, (clk == CLOCK_SELECT_EXTERNAL) ? BLADERF_SMB_MODE_INPUT : BLADERF_SMB_MODE_DISABLED);
}
else {
bladerf_set_clock_select(openDev, clk);
}
}
void worker() {
int16_t* buffer = new int16_t[bufferSize * 2];
bladerf_metadata meta;
@ -565,6 +602,7 @@ private:
int devId = 0;
int srId = 0;
int bwId = 0;
int clkId = 0;
int chanId = 0;
int gainMode = 0;
bool streamingEnabled = false;
@ -580,8 +618,8 @@ private:
std::string sampleRatesTxt;
std::vector<uint64_t> bandwidths;
std::string bandwidthsTxt;
std::string channelNamesTxt;
OptionList<std::string, bladerf_clock_select> clocks;
int bufferSize;
struct bladerf_stream* rxStream;

View File

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.13)
project(fobossdr_source)
file(GLOB SRC "src/*.cpp")
include(${SDRPP_MODULE_CMAKE})
if (MSVC)
# Lib path
target_link_directories(fobossdr_source PRIVATE "C:/Program Files/RigExpert/Fobos/lib/")
target_include_directories(fobossdr_source PRIVATE "C:/Program Files/RigExpert/Fobos/include/")
target_link_libraries(fobossdr_source PRIVATE fobos)
else (MSVC)
find_package(PkgConfig)
pkg_check_modules(LIBFOBOS REQUIRED libfobos)
target_include_directories(fobossdr_source PRIVATE ${LIBFOBOS_INCLUDE_DIRS})
target_link_directories(fobossdr_source PRIVATE ${LIBFOBOS_LIBRARY_DIRS})
target_link_libraries(fobossdr_source PRIVATE ${LIBFOBOS_LIBRARIES})
endif ()

View File

@ -0,0 +1,560 @@
#include <imgui.h>
#include <module.h>
#include <gui/gui.h>
#include <gui/smgui.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <utils/optionlist.h>
#include <atomic>
#include <fobos.h>
SDRPP_MOD_INFO{
/* Name: */ "fobossdr_source",
/* Description: */ "FobosSDR Source Module",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
ConfigManager config;
#define CONCAT(a, b) ((std::string(a) + b).c_str())
// Work around for the fobos API not including
#define FOBOS_LNA_GAIN_MIN 1
#define FOBOS_LNA_GAIN_MAX 3
#define FOBOS_VGA_GAIN_MIN 0
#define FOBOS_VGA_GAIN_MAX 31
class FobosSDRSourceModule : public ModuleManager::Instance {
public:
FobosSDRSourceModule(std::string name) {
this->name = name;
sampleRate = 50000000.0;
// Initialize the DDC
ddc.init(&ddcIn, 50e6, 50e6, 50e6, 0.0);
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
handler.menuHandler = menuHandler;
handler.startHandler = start;
handler.stopHandler = stop;
handler.tuneHandler = tune;
handler.stream = &ddc.out;
// Refresh devices
refresh();
// Select device from config
config.acquire();
std::string devSerial = config.conf["device"];
config.release();
select(devSerial);
sigpath::sourceManager.registerSource("FobosSDR", &handler);
}
~FobosSDRSourceModule() {
// Nothing to do
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
enum Port {
PORT_RF,
PORT_HF1,
PORT_HF2
};
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);
}
void refresh() {
devices.clear();
// Get device list
char serials[1024];
memset(serials, 0, sizeof(serials));
int devCount = fobos_rx_list_devices(serials);
if (devCount < 0) {
flog::error("Failed to get device list: {}", devCount);
return;
}
// If no device, give up
if (!devCount) { return; }
// Generate device entries
const char* _serials = serials;
int index = 0;
while (*_serials) {
// Read serial until space
std::string serial = "";
while (*_serials) {
// Get a character
char c = *(_serials++);
// If it's a space, we're done
if (c == ' ') { break; }
// Otherwise, add it to the string
serial += c;
}
// Create entry
devices.define(serial, serial, index++);
}
}
void select(const std::string& serial) {
// If there are no devices, give up
if (devices.empty()) {
selectedSerial.clear();
return;
}
// If the serial was not found, select the first available serial
if (!devices.keyExists(serial)) {
select(devices.key(0));
return;
}
// Get the ID in the list
int id = devices.keyId(serial);
selectedDevId = devices[id];
// Open the device
fobos_dev_t* dev;
int err = fobos_rx_open(&dev, selectedDevId);
if (err) {
flog::error("Failed to open device: {}", err);
return;
}
// Get a list of supported samplerates
double srList[128];
unsigned int srCount;
err = fobos_rx_get_samplerates(dev, srList, &srCount);
if (err) {
flog::error("Failed to get samplerate list: {}", err);
return;
}
// Generate samplerate list
samplerates.clear();
for (int i = 0; i < srCount; i++) {
std::string str = getBandwdithScaled(srList[i]);
samplerates.define(srList[i], str, srList[i]);
}
// Add some custom samplerates
samplerates.define(5e6, "5.0MHz", 5e6);
samplerates.define(2.5e6, "2.5MHz", 2.5e6);
samplerates.define(1.25e6, "1.25MHz", 1.25e6);
// Define the ports
ports.clear();
ports.define("rf", "RF", PORT_RF);
ports.define("hf1", "HF1", PORT_HF1);
ports.define("hf2", "HF2", PORT_HF2);
// Define clock sources
clockSources.clear();
clockSources.define("internal", "Internal", 0);
clockSources.define("external", "External", 1);
// Close the device
fobos_rx_close(dev);
// Save serial number
selectedSerial = serial;
devId = id;
// Load default options
sampleRate = 50e6;
srId = samplerates.valueId(sampleRate);
port = PORT_RF;
portId = ports.valueId(port);
clkSrcId = clockSources.nameId("Internal");
lnaGain = 0;
vgaGain = 0;
// Load config
config.acquire();
if (config.conf["devices"][selectedSerial].contains("samplerate")) {
int desiredSr = config.conf["devices"][selectedSerial]["samplerate"];
if (samplerates.keyExists(desiredSr)) {
srId = samplerates.keyId(desiredSr);
sampleRate = samplerates[srId];
}
}
if (config.conf["devices"][selectedSerial].contains("port")) {
std::string desiredPort = config.conf["devices"][selectedSerial]["port"];
if (ports.keyExists(desiredPort)) {
portId = ports.keyId(desiredPort);
port = ports[portId];
}
}
if (config.conf["devices"][selectedSerial].contains("clkSrc")) {
std::string desiredClkSrc = config.conf["devices"][selectedSerial]["clkSrc"];
if (clockSources.keyExists(desiredClkSrc)) {
clkSrcId = clockSources.keyId(desiredClkSrc);
}
}
if (config.conf["devices"][selectedSerial].contains("lnaGain")) {
lnaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["lnaGain"], FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX);
}
if (config.conf["devices"][selectedSerial].contains("vgaGain")) {
vgaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["vgaGain"], FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX);
}
config.release();
// Update the samplerate
core::setInputSampleRate(sampleRate);
}
static void menuSelected(void* ctx) {
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("FobosSDRSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
flog::info("FobosSDRSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
if (_this->running) { return; }
// Open the device
int err = fobos_rx_open(&_this->openDev, _this->selectedDevId);
if (err) {
flog::error("Failed to open device: {}", err);
return;
}
// Get the selected port
_this->port = _this->ports[_this->portId];
// Configure the device
double actualSr, actualFreq;
fobos_rx_set_samplerate(_this->openDev, (_this->sampleRate >= 50e6) ? _this->sampleRate : 50e6, &actualSr);
fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
fobos_rx_set_direct_sampling(_this->openDev, _this->port != PORT_RF);
fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]);
fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain);
fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain);
// Configure the DDC
if (_this->port == PORT_RF && _this->sampleRate >= 50e6) {
// Set the frequency
fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
}
else if (_this->port == PORT_RF) {
// Set the frequency
fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
// Configure and start the DDC for decimation only
_this->ddc.setInSamplerate(actualSr);
_this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate);
_this->ddc.setOffset(0.0);
_this->ddc.start();
}
else {
// Configure and start the DDC
_this->ddc.setInSamplerate(actualSr);
_this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate);
_this->ddc.setOffset(_this->freq);
_this->ddc.start();
}
// Compute buffer size (Lower than usual, but it's a workaround for their API having broken streaming)
_this->bufferSize = _this->sampleRate / 400.0;
// Start streaming
err = fobos_rx_start_sync(_this->openDev, _this->bufferSize);
if (err) {
flog::error("Failed to start stream: {}", err);
return;
}
// Start worker
_this->run = true;
_this->workerThread = std::thread(&FobosSDRSourceModule::worker, _this);
_this->running = true;
flog::info("FobosSDRSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
// Stop worker
_this->run = false;
if (_this->port == PORT_RF && _this->sampleRate >= 50e6) {
_this->ddc.out.stopWriter();
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
_this->ddc.out.clearWriteStop();
}
else {
_this->ddcIn.stopWriter();
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
_this->ddcIn.clearWriteStop();
}
// Stop streaming
fobos_rx_stop_sync(_this->openDev);
// Stop the DDC
_this->ddc.stop();
// Close the device
fobos_rx_close(_this->openDev);
flog::info("FobosSDRSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
if (_this->running) {
if (_this->port == PORT_RF) {
double actual; // Dummy, don't care
fobos_rx_set_frequency(_this->openDev, freq, &actual);
}
else {
_this->ddc.setOffset(freq);
}
}
_this->freq = freq;
flog::info("FobosSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_fobossdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
_this->select(_this->devices.key(_this->devId));
core::setInputSampleRate(_this->sampleRate);
config.acquire();
config.conf["device"] = _this->selectedSerial;
config.release(true);
}
if (SmGui::Combo(CONCAT("##_fobossdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
_this->sampleRate = _this->samplerates.value(_this->srId);
core::setInputSampleRate(_this->sampleRate);
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["samplerate"] = _this->samplerates.key(_this->srId);
config.release(true);
}
}
SmGui::SameLine();
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_fobossdr_refr_", _this->name))) {
_this->refresh();
_this->select(_this->selectedSerial);
core::setInputSampleRate(_this->sampleRate);
}
SmGui::LeftLabel("Antenna Port");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_fobossdr_port_", _this->name), &_this->portId, _this->ports.txt)) {
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["port"] = _this->ports.key(_this->portId);
config.release(true);
}
}
if (_this->running) { SmGui::EndDisabled(); }
SmGui::LeftLabel("Clock Source");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_fobossdr_clk_", _this->name), &_this->clkSrcId, _this->clockSources.txt)) {
if (_this->running) {
fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]);
}
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["clkSrc"] = _this->clockSources.key(_this->clkSrcId);
config.release(true);
}
}
if (_this->port == PORT_RF) {
SmGui::LeftLabel("LNA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_fobossdr_lna_gain_", _this->name), &_this->lnaGain, FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX)) {
if (_this->running) {
fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain);
}
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["lnaGain"] = _this->lnaGain;
config.release(true);
}
}
SmGui::LeftLabel("VGA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_fobossdr_vga_gain_", _this->name), &_this->vgaGain, FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX)) {
if (_this->running) {
fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain);
}
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["vgaGain"] = _this->vgaGain;
config.release(true);
}
}
}
}
void worker() {
// Select different processing depending on the mode
if (port == PORT_RF && sampleRate >= 50e6) {
while (run) {
// Read samples
unsigned int sampCount = 0;
int err = fobos_rx_read_sync(openDev, (float*)ddc.out.writeBuf, &sampCount);
if (err) { break; }
// Send out samples to the core
if (!ddc.out.swap(sampCount)) { break; }
}
}
else if (port == PORT_RF) {
while (run) {
// Read samples
unsigned int sampCount = 0;
int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
if (err) { break; }
// Send samples to the DDC
if (!ddcIn.swap(sampCount)) { break; }
}
}
else if (port == PORT_HF1) {
while (run) {
// Read samples
unsigned int sampCount = 0;
int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
if (err) { break; }
// Null out the HF2 samples
for (int i = 0; i < sampCount; i++) {
ddcIn.writeBuf[i].im = 0.0f;
}
// Send samples to the DDC
if (!ddcIn.swap(sampCount)) { break; }
}
}
else if (port == PORT_HF2) {
while (run) {
// Read samples
unsigned int sampCount = 0;
int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
if (err) { break; }
// Null out the HF2 samples
for (int i = 0; i < sampCount; i++) {
ddcIn.writeBuf[i].re = 0.0f;
}
// Send samples to the DDC
if (!ddcIn.swap(sampCount)) { break; }
}
}
}
std::string name;
bool enabled = true;
double sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq;
OptionList<std::string, int> devices;
OptionList<int, double> samplerates;
OptionList<std::string, Port> ports;
OptionList<std::string, int> clockSources;
int devId = 0;
int srId = 0;
int portId = 0;
int clkSrcId = 0;
Port port;
int lnaGain = 0;
int vgaGain = 0;
std::string selectedSerial;
int selectedDevId;
fobos_dev_t* openDev;
int bufferSize;
std::thread workerThread;
std::atomic<bool> run = false;
dsp::stream<dsp::complex_t> ddcIn;
dsp::channel::RxVFO ddc;
};
MOD_EXPORT void _INIT_() {
json def = json({});
def["devices"] = json({});
def["device"] = "";
config.setPath(core::args["root"].s() + "/fobossdr_config.json");
config.load(def);
config.enableAutoSave();
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new FobosSDRSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (FobosSDRSourceModule*)instance;
}
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}

View File

@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.13)
project(harogic_source)
file(GLOB SRC "src/*.cpp")
include(${SDRPP_MODULE_CMAKE})
if (MSVC)
# Lib path
target_link_directories(harogic_source PRIVATE "C:/radio/HTRA_API/x64/htra_api/")
target_include_directories(harogic_source PRIVATE "C:/radio/HTRA_API/x64/htra_api/")
target_link_libraries(harogic_source PRIVATE htra_api)
else (MSVC)
target_link_directories(harogic_source PRIVATE "/opt/htraapi/lib/${CMAKE_SYSTEM_PROCESSOR}/")
target_include_directories(harogic_source PRIVATE "/opt/htraapi/inc/")
target_link_libraries(harogic_source PRIVATE htraapi)
endif ()

View File

@ -0,0 +1,510 @@
#include <imgui.h>
#include <module.h>
#include <gui/gui.h>
#include <gui/smgui.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <utils/optionlist.h>
#include <htra_api.h>
#include <atomic>
SDRPP_MOD_INFO{
/* Name: */ "harogic_source",
/* Description: */ "harogic Source Module",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
#define CONCAT(a, b) ((std::string(a) + b).c_str())
class HarogicSourceModule : public ModuleManager::Instance {
public:
HarogicSourceModule(std::string name) {
this->name = name;
sampleRate = 61440000.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 devices
refresh();
// Select first (TODO: Select from config)
select("");
sigpath::sourceManager.registerSource("Harogic", &handler);
}
~HarogicSourceModule() {
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
void refresh() {
devices.clear();
// Set up the device parameters
BootProfile_TypeDef profile = {};
profile.PhysicalInterface = PhysicalInterface_TypeDef::USB;
profile.DevicePowerSupply = DevicePowerSupply_TypeDef::USBPortOnly;
// Working variables
void* dev;
BootInfo_TypeDef binfo;
for (int i = 0; i < 128; i++) {
// Attempt to open the device with the given ID
int ret = Device_Open(&dev, i, &profile, &binfo);
if (ret < 0) { break; }
// Create serial string
char serial[64];
sprintf(serial, "%" PRIX64, binfo.DeviceInfo.DeviceUID);
// Add the device to the list
devices.define(serial, serial, i);
// Close the device
Device_Close(&dev);
}
}
void select(const std::string& serial) {
// If there are no devices, give up
if (devices.empty()) {
selectedSerial.clear();
return;
}
// If the serial was not found, select the first available serial
if (!devices.keyExists(serial)) {
select(devices.key(0));
return;
}
// Get the menu ID
devId = devices.keyId(serial);
selectedDevIndex = devices.value(devId);
// Set up the device parameters
BootProfile_TypeDef bprofile = {};
bprofile.PhysicalInterface = PhysicalInterface_TypeDef::USB;
bprofile.DevicePowerSupply = DevicePowerSupply_TypeDef::USBPortOnly;
// Working variables
BootInfo_TypeDef binfo;
// Attempt to open the device by ID
void* dev;
int ret = Device_Open(&dev, selectedDevIndex, &bprofile, &binfo);
if (ret < 0) {
flog::error("Could not open device: {}", ret);
return;
}
// Get the default streaming parameters to query some info
IQS_Profile_TypeDef profile;
IQS_ProfileDeInit(&dev, &profile);
// Compute all available samplerates
samplerates.clear();
for (int i = 0; i < 8; i++) {
double sr = profile.NativeIQSampleRate_SPS / (double)(1 << i);
char buf[128];
sprintf(buf, "%.02fMHz", sr / 1e6);
samplerates.define(1 << i, buf, sr);
}
// Define RX ports
rxPorts.clear();
rxPorts.define("external", "External", ExternalPort);
rxPorts.define("internal", "Internal", InternalPort);
rxPorts.define("ant", "ANT", ANT_Port);
rxPorts.define("tr", "T/R", TR_Port);
rxPorts.define("swr", "SWR", SWR_Port);
rxPorts.define("int", "INT", INT_Port);
// Define gain strategies
gainStategies.clear();
gainStategies.define("lowNoise", "Low Noise", LowNoisePreferred);
gainStategies.define("highLinearity", "High Linearity", HighLinearityPreferred);
// Define preamplifier modes
preampModes.clear();
preampModes.define("auto", "Auto", AutoOn);
preampModes.define("off", "Off", ForcedOff);
// Define LO modes
loModes.clear();
loModes.define("auto", "Auto", LOOpt_Auto);
loModes.define("speed", "Speed", LOOpt_Speed);
loModes.define("spurs", "Spurs", LOOpt_Spur);
loModes.define("phaseNoise", "Phase Noise", LOOpt_PhaseNoise);
// Close the device
Device_Close(&dev);
// TODO: Load configuration
sampleRate = samplerates.value(0);
refLvl = 0;
portId = rxPorts.valueId(ExternalPort);
gainStratId = gainStategies.valueId(LowNoisePreferred);
preampModeId = preampModes.valueId(AutoOn);
ifAgc = false;
loModeId = loModes.valueId(LOOpt_Auto);
// Update the samplerate
core::setInputSampleRate(sampleRate);
// Save serial number
selectedSerial = serial;
}
static void menuSelected(void* ctx) {
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("HarogicSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
flog::info("HarogicSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
if (_this->running) { return; }
// Set up the device parameters
BootProfile_TypeDef bprofile = {};
bprofile.PhysicalInterface = PhysicalInterface_TypeDef::USB;
bprofile.DevicePowerSupply = DevicePowerSupply_TypeDef::USBPortAndPowerPort;
// Working variables
BootInfo_TypeDef binfo;
// Attempt to open the device by ID
int ret = Device_Open(&_this->openDev, _this->selectedDevIndex, &bprofile, &binfo);
if (ret < 0) {
flog::error("Could not open device: {}", ret);
return;
}
// Get the decimation amount
int dec = _this->samplerates.key(_this->samplerates.valueId(_this->sampleRate));
flog::debug("Using decimation factor: {}", dec);
// Decide to use either 8 or 16bit samples
_this->sampsInt8 = (_this->sampleRate > 64e6);
// Get the default configuration
IQS_ProfileDeInit(&_this->openDev, &_this->profile);
// Automatic configuration
_this->profile.Atten = -1;
_this->profile.BusTimeout_ms = 100;
_this->profile.TriggerSource = Bus;
_this->profile.TriggerMode = Adaptive;
_this->profile.DataFormat = _this->sampsInt8 ? Complex8bit : Complex16bit;
// User selectable config
_this->profile.CenterFreq_Hz = _this->freq;
_this->profile.RefLevel_dBm = _this->refLvl;
_this->profile.DecimateFactor = dec;
_this->profile.RxPort = _this->rxPorts.value(_this->portId);
_this->profile.GainStrategy = _this->gainStategies.value(_this->gainStratId);
_this->profile.Preamplifier = _this->preampModes.value(_this->preampModeId);
_this->profile.EnableIFAGC = _this->ifAgc;
_this->profile.LOOptimization = _this->loModes.value(_this->loModeId);
// Apply the configuration
IQS_StreamInfo_TypeDef info;
ret = IQS_Configuration(&_this->openDev, &_this->profile, &_this->profile, &info);
if (ret < 0) {
flog::error("Could not configure device: {}", ret);
Device_Close(&_this->openDev);
return;
}
// Save the stream configuration
_this->bufferSize = info.PacketSamples;
flog::debug("Got buffer size: {}", _this->bufferSize);
// Start the stream
ret = IQS_BusTriggerStart(&_this->openDev);
if (ret < 0) {
flog::error("Could not start stream: {}", ret);
Device_Close(&_this->openDev);
return;
}
// Start worker
_this->run = true;
_this->workerThread = std::thread(&HarogicSourceModule::worker, _this);
_this->running = true;
flog::info("HarogicSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
// Stop worker
_this->run = false;
_this->stream.stopWriter();
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
_this->stream.clearWriteStop();
// Stop the stream
IQS_BusTriggerStop(&_this->openDev);
// Close the device
Device_Close(&_this->openDev);
flog::info("HarogicSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
if (_this->running) {
// Update the frequency in the configuration
_this->profile.CenterFreq_Hz = freq;
_this->applyProfile();
}
_this->freq = freq;
flog::info("HarogicSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_harogic_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
_this->select(_this->devices.key(_this->devId));
core::setInputSampleRate(_this->sampleRate);
// TODO: Save
}
if (SmGui::Combo(CONCAT("##_harogic_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
_this->sampleRate = _this->samplerates.value(_this->srId);
core::setInputSampleRate(_this->sampleRate);
// TODO: Save
}
SmGui::SameLine();
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_harogic_refr_", _this->name))) {
_this->refresh();
_this->select(_this->selectedSerial);
core::setInputSampleRate(_this->sampleRate);
}
if (_this->running) { SmGui::EndDisabled(); }
SmGui::LeftLabel("RX Port");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_harogic_port_", _this->name), &_this->portId, _this->rxPorts.txt)) {
if (_this->running) {
_this->profile.RxPort = _this->rxPorts.value(_this->portId);
_this->applyProfile();
}
// TODO: Save
}
SmGui::LeftLabel("LO Mode");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_lo_mode_", _this->name), &_this->loModeId, _this->loModes.txt)) {
if (_this->running) {
_this->profile.LOOptimization = _this->loModes.value(_this->loModeId);
_this->applyProfile();
}
// TODO: Save
}
SmGui::LeftLabel("Gain Mode");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_harogic_gain_mode_", _this->name), &_this->gainStratId, _this->gainStategies.txt)) {
if (_this->running) {
_this->profile.GainStrategy = _this->gainStategies.value(_this->gainStratId);
_this->applyProfile();
}
// TODO: Save
}
SmGui::LeftLabel("Preamp Mode");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_harogic_preamp_mode_", _this->name), &_this->preampModeId, _this->preampModes.txt)) {
if (_this->running) {
_this->profile.Preamplifier = _this->preampModes.value(_this->preampModeId);
_this->applyProfile();
}
// TODO: Save
}
SmGui::LeftLabel("Reference");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_harogic_ref_", _this->name), &_this->refLvl, _this->minRef, _this->maxRef)) {
if (_this->running) {
_this->profile.RefLevel_dBm = _this->refLvl;
_this->applyProfile();
}
// TODO: Save
}
if (SmGui::Checkbox(CONCAT("IF AGC##_harogic_if_agc_", _this->name), &_this->ifAgc)) {
if (_this->running) {
_this->profile.EnableIFAGC = _this->ifAgc;
_this->applyProfile();
}
// TODO: Save
}
}
void applyProfile() {
// Acquire device
std::lock_guard<std::mutex> lck(devMtx);
// Configure the device
IQS_StreamInfo_TypeDef info;
int ret = IQS_Configuration(&openDev, &profile, &profile, &info);
if (ret < 0) {
flog::error("Failed to apply tuning config: {}", ret);
}
// Re-trigger the stream
ret = IQS_BusTriggerStart(&openDev);
if (ret < 0) {
flog::error("Could not start stream: {}", ret);
}
}
void worker() {
// Allocate sample buffer
int realSamps = bufferSize*2;
IQStream_TypeDef iqs;
// Define number of buffers per swap to maintain 200 fps
int maxBufCount = STREAM_BUFFER_SIZE / bufferSize;
int bufCount = (sampleRate / bufferSize) / 200;
if (bufCount <= 0) { bufCount = 1; }
if (bufCount > maxBufCount) { bufCount = maxBufCount; }
int count = 0;
flog::debug("Swapping will be done {} buffers at a time", bufCount);
// Worker loop
while (run) {
// Read samples
devMtx.lock();
int ret = IQS_GetIQStream_PM1(&openDev, &iqs);
devMtx.unlock();
if (ret < 0) {
if (ret == APIRETVAL_WARNING_BusTimeOut) {
flog::warn("Stream timed out");
continue;
}
else if (ret <= APIRETVAL_WARNING_IFOverflow && ret >= APIRETVAL_WARNING_ADCConfigError) {
// Just warnings, do nothing
}
else {
flog::error("Streaming error: {}", ret);
break;
}
}
// Convert them to floating point
if (sampsInt8) {
volk_8i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*bufferSize], (int8_t*)iqs.AlternIQStream, 128.0f, realSamps);
}
else {
volk_16i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*bufferSize], (int16_t*)iqs.AlternIQStream, 32768.0f, realSamps);
}
// Send them off if we have enough
if (count >= bufCount) {
count = 0;
if (!stream.swap(bufferSize*bufCount)) { break; }
}
}
}
std::string name;
bool enabled = true;
dsp::stream<dsp::complex_t> stream;
double sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq;
OptionList<std::string, int> devices;
OptionList<int, double> samplerates;
OptionList<std::string, RxPort_TypeDef> rxPorts;
OptionList<std::string, GainStrategy_TypeDef> gainStategies;
OptionList<std::string, PreamplifierState_TypeDef> preampModes;
OptionList<std::string, LOOptimization_TypeDef> loModes;
int devId = 0;
int srId = 0;
int refLvl = -30;
int minRef = -100;
int maxRef = 7;
int portId = 0;
int gainStratId = 0;
int preampModeId = 0;
int loModeId = 0;
bool ifAgc = false;
std::string selectedSerial;
int selectedDevIndex;
void* openDev;
IQS_Profile_TypeDef profile;
int bufferSize;
std::thread workerThread;
std::atomic<bool> run = false;
std::mutex devMtx;
bool sampsInt8;
};
MOD_EXPORT void _INIT_() {
// Nothing here
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new HarogicSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (HarogicSourceModule*)instance;
}
MOD_EXPORT void _END_() {
// Nothing here
}

View File

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.13)
project(hydrasdr_source)
file(GLOB SRC "src/*.cpp")
include(${SDRPP_MODULE_CMAKE})
if (MSVC)
target_link_directories(hydrasdr_source PRIVATE "C:/Program Files/HydraSDR/")
target_include_directories(hydrasdr_source PUBLIC "C:/Program Files/HydraSDR/include/")
target_link_libraries(hydrasdr_source PRIVATE hydrasdr)
elseif (ANDROID)
# TODO
else (MSVC)
# TODO
endif ()

View File

@ -0,0 +1,635 @@
#include <imgui.h>
#include <utils/flog.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 <gui/smgui.h>
#include <hydrasdr.h>
#include <utils/optionlist.h>
#ifdef __ANDROID__
#include <android_backend.h>
#endif
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{
/* Name: */ "hydrasdr_source",
/* Description: */ "HydraSDR source module for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ 1
};
ConfigManager config;
class HydraSDRSourceModule : public ModuleManager::Instance {
public:
HydraSDRSourceModule(std::string name) {
this->name = name;
// Define the ports
ports.define("rx0", "RX0", RF_PORT_RX0);
ports.define("rx1", "RX1", RF_PORT_RX1);
ports.define("rx2", "RX2", RF_PORT_RX2);
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();
// Select device from config
config.acquire();
std::string devSerial = config.conf["device"];
config.release();
selectByString(devSerial);
sigpath::sourceManager.registerSource("HydraSDR", &handler);
}
~HydraSDRSourceModule() {
stop(this);
sigpath::sourceManager.unregisterSource("HydraSDR");;
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
void refresh() {
#ifndef __ANDROID__
devices.clear();
uint64_t serials[256];
int n = hydrasdr_list_devices(serials, 256);
char buf[1024];
for (int i = 0; i < n; i++) {
sprintf(buf, "%016" PRIX64, serials[i]);
devices.define(buf, buf, serials[i]);
}
#else
// Check for device presence
int vid, pid;
devFd = backend::getDeviceFD(vid, pid, backend::AIRSPY_VIDPIDS);
if (devFd < 0) { return; }
// Get device info
std::string fakeName = "HydraSDR USB";
devList.push_back(0xDEADBEEF);
devListTxt += fakeName;
devListTxt += '\0';
#endif
}
void selectFirst() {
if (!devices.empty()) {
selectBySerial(devices.value(0));
}
}
void selectByString(std::string serial) {
if (devices.keyExists(serial)) {
selectBySerial(devices.value(devices.keyId(serial)));
return;
}
selectFirst();
}
void selectBySerial(uint64_t serial) {
hydrasdr_device* dev;
try {
#ifndef __ANDROID__
int err = hydrasdr_open_sn(&dev, serial);
#else
int err = hydrasdr_open_fd(&dev, devFd);
#endif
if (err != 0) {
char buf[1024];
sprintf(buf, "%016" PRIX64, serial);
flog::error("Could not open HydraSDR {0}", buf);
selectedSerial = 0;
return;
}
}
catch (const std::exception& e) {
char buf[1024];
sprintf(buf, "%016" PRIX64, serial);
flog::error("Could not open HydraSDR {}", buf);
}
devId = devices.valueId(serial);
selectedSerial = serial;
selectedSerStr = devices.key(devId);
uint32_t sampleRates[256];
hydrasdr_get_samplerates(dev, sampleRates, 0);
int n = sampleRates[0];
hydrasdr_get_samplerates(dev, sampleRates, n);
samplerates.clear();
for (int i = 0; i < n; i++) {
samplerates.define(sampleRates[i], getBandwdithScaled(sampleRates[i]), sampleRates[i]);
}
// 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 = samplerates.value(0);
if (config.conf["devices"][selectedSerStr].contains("sampleRate")) {
int selectedSr = config.conf["devices"][selectedSerStr]["sampleRate"];
if (samplerates.keyExists(selectedSr)) {
srId = samplerates.keyId(selectedSr);
sampleRate = samplerates[srId];
}
}
// Load port
if (config.conf["devices"][selectedSerStr].contains("port")) {
std::string portStr = config.conf["devices"][selectedSerStr]["port"];
if (ports.keyExists(portStr)) {
portId = ports.keyId(portStr);
}
}
// 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);
hydrasdr_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) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("HydraSDRSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
flog::info("HydraSDRSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
if (_this->running) { return; }
if (_this->selectedSerial == 0) {
flog::error("Tried to start HydraSDR source with null serial");
return;
}
#ifndef __ANDROID__
int err = hydrasdr_open_sn(&_this->openDev, _this->selectedSerial);
#else
int err = hydrasdr_open_fd(&_this->openDev, _this->devFd);
#endif
if (err != 0) {
char buf[1024];
sprintf(buf, "%016" PRIX64, _this->selectedSerial);
flog::error("Could not open HydraSDR {0}", buf);
return;
}
hydrasdr_set_samplerate(_this->openDev, _this->samplerates[_this->srId]);
hydrasdr_set_freq(_this->openDev, _this->freq);
hydrasdr_set_rf_port(_this->openDev, _this->ports[_this->portId]);
if (_this->gainMode == 0) {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
}
else if (_this->gainMode == 1) {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_linearity_gain(_this->openDev, _this->linearGain);
}
else if (_this->gainMode == 2) {
if (_this->lnaAgc) {
hydrasdr_set_lna_agc(_this->openDev, 1);
}
else {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain);
}
if (_this->mixerAgc) {
hydrasdr_set_mixer_agc(_this->openDev, 1);
}
else {
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain);
}
hydrasdr_set_vga_gain(_this->openDev, _this->vgaGain);
}
hydrasdr_set_rf_bias(_this->openDev, _this->biasT);
hydrasdr_start_rx(_this->openDev, callback, _this);
_this->running = true;
flog::info("HydraSDRSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
_this->stream.stopWriter();
hydrasdr_close(_this->openDev);
_this->stream.clearWriteStop();
flog::info("HydraSDRSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
if (_this->running) {
hydrasdr_set_freq(_this->openDev, freq);
}
_this->freq = freq;
flog::info("HydraSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_hydrasdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
_this->selectBySerial(_this->devices[_this->devId]);
core::setInputSampleRate(_this->sampleRate);
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["device"] = _this->selectedSerStr;
config.release(true);
}
}
if (SmGui::Combo(CONCAT("##_hydrasdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
_this->sampleRate = _this->samplerates[_this->srId];
core::setInputSampleRate(_this->sampleRate);
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["sampleRate"] = _this->samplerates.key(_this->srId);
config.release(true);
}
}
SmGui::SameLine();
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_hydrasdr_refr_", _this->name))) {
_this->refresh();
config.acquire();
std::string devSerial = config.conf["device"];
config.release();
_this->selectByString(devSerial);
core::setInputSampleRate(_this->sampleRate);
}
if (_this->running) { SmGui::EndDisabled(); }
SmGui::LeftLabel("Antenna Port");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_hydrasdr_port_", _this->name), &_this->portId, _this->ports.txt)) {
if (_this->running) {
hydrasdr_set_rf_port(_this->openDev, _this->ports[_this->portId]);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["port"] = _this->ports.key(_this->portId);
config.release(true);
}
}
SmGui::BeginGroup();
SmGui::Columns(3, CONCAT("HydraSDRGainModeColumns##_", _this->name), false);
SmGui::ForceSync();
if (SmGui::RadioButton(CONCAT("Sensitive##_hydrasdr_gm_", _this->name), _this->gainMode == 0)) {
_this->gainMode = 0;
if (_this->running) {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 0;
config.release(true);
}
}
SmGui::NextColumn();
SmGui::ForceSync();
if (SmGui::RadioButton(CONCAT("Linear##_hydrasdr_gm_", _this->name), _this->gainMode == 1)) {
_this->gainMode = 1;
if (_this->running) {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_linearity_gain(_this->openDev, _this->linearGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 1;
config.release(true);
}
}
SmGui::NextColumn();
SmGui::ForceSync();
if (SmGui::RadioButton(CONCAT("Free##_hydrasdr_gm_", _this->name), _this->gainMode == 2)) {
_this->gainMode = 2;
if (_this->running) {
if (_this->lnaAgc) {
hydrasdr_set_lna_agc(_this->openDev, 1);
}
else {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain);
}
if (_this->mixerAgc) {
hydrasdr_set_mixer_agc(_this->openDev, 1);
}
else {
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain);
}
hydrasdr_set_vga_gain(_this->openDev, _this->vgaGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 2;
config.release(true);
}
}
SmGui::Columns(1, CONCAT("EndHydraSDRGainModeColumns##_", _this->name), false);
SmGui::EndGroup();
// Gain menus
if (_this->gainMode == 0) {
SmGui::LeftLabel("Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_hydrasdr_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) {
if (_this->running) {
hydrasdr_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) {
SmGui::LeftLabel("Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_hydrasdr_lin_gain_", _this->name), &_this->linearGain, 0, 21)) {
if (_this->running) {
hydrasdr_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) {
// TODO: Switch to a table for alignment
if (_this->lnaAgc) { SmGui::BeginDisabled(); }
SmGui::LeftLabel("LNA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_hydrasdr_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) {
if (_this->running) {
hydrasdr_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) { SmGui::EndDisabled(); }
if (_this->mixerAgc) { SmGui::BeginDisabled(); }
SmGui::LeftLabel("Mixer Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_hydrasdr_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) {
if (_this->running) {
hydrasdr_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) { SmGui::EndDisabled(); }
SmGui::LeftLabel("VGA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_hydrasdr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
if (_this->running) {
hydrasdr_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
SmGui::ForceSync();
if (SmGui::Checkbox(CONCAT("LNA AGC##_hydrasdr_", _this->name), &_this->lnaAgc)) {
if (_this->running) {
if (_this->lnaAgc) {
hydrasdr_set_lna_agc(_this->openDev, 1);
}
else {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain);
}
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["lnaAgc"] = _this->lnaAgc;
config.release(true);
}
}
SmGui::ForceSync();
if (SmGui::Checkbox(CONCAT("Mixer AGC##_hydrasdr_", _this->name), &_this->mixerAgc)) {
if (_this->running) {
if (_this->mixerAgc) {
hydrasdr_set_mixer_agc(_this->openDev, 1);
}
else {
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_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 (SmGui::Checkbox(CONCAT("Bias T##_hydrasdr_", _this->name), &_this->biasT)) {
if (_this->running) {
hydrasdr_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(hydrasdr_transfer_t* transfer) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)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;
hydrasdr_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;
int portId = 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;
#ifdef __ANDROID__
int devFd = 0;
#endif
OptionList<std::string, uint64_t> devices;
OptionList<uint32_t, uint32_t> samplerates;
OptionList<std::string, hydrasdr_rf_port_t> ports;
};
MOD_EXPORT void _INIT_() {
json def = json({});
def["devices"] = json({});
def["device"] = "";
config.setPath(core::args["root"].s() + "/hydrasdr_config.json");
config.load(def);
config.enableAutoSave();
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new HydraSDRSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
delete (HydraSDRSourceModule*)instance;
}
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}

View File

@ -0,0 +1 @@
vendor/*

View File

@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.13)
project(kcsdr_source)
file(GLOB SRC "src/*.cpp" "src/*.c")
include(${SDRPP_MODULE_CMAKE})
target_link_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10/x64/DLL")
target_include_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10")
target_link_libraries(kcsdr_source PRIVATE FTD3XX)

View File

@ -0,0 +1,209 @@
#include "kcsdr.h"
#include <string.h>
#include "../vendor/FTD3XXLibrary_1.3.0.10/FTD3XX.h"
#include <stdio.h>
#include <stddef.h>
#define KCSDR_PKT_EMPTY_LEN 0x0C
#define KCSDR_COMMAND_PIPE 0x02
#define KCSDR_RX_DATA_PIPE 0x83
#define KCSDR_TX_DATA_PIPE 0x03
struct kcsdr {
FT_HANDLE ft;
};
#pragma pack(push, 1)
struct kcsdr_packet {
uint8_t zeros0[4];
uint8_t length;
uint8_t zeros1[2];
uint8_t hex_eighty;
uint32_t command;
uint8_t data[188];
};
typedef struct kcsdr_packet kcsdr_packet_t;
#pragma pack(pop)
enum kcsdr_command {
CMD_NOT_USED_0x00 = 0x00,
CMD_SET_PORT = 0x01,
CMD_SET_FREQUENCY = 0x02,
CMD_SET_ATTENUATION = 0x03,
CMD_SET_AMPLIFIER = 0x04,
CMD_SET_BANDWIDTH = 0x05,
CMD_START = 0x06,
CMD_STOP = 0x07,
CMD_SET_EXT_AMP = 0x08,
CMD_START_REMOTE = 0x09,
CMD_STOP_REMOTE = 0x0A
};
typedef enum kcsdr_command kcsdr_command_t;
int kcsdr_send_command(kcsdr_t* dev, kcsdr_direction_t dir, kcsdr_command_t cmd, const uint8_t* data, int len) {
Sleep(50);
// Create an empty packet
kcsdr_packet_t pkt;
memset(&pkt, 0, sizeof(kcsdr_packet_t));
// Fill out the packet info
pkt.length = len + KCSDR_PKT_EMPTY_LEN;
pkt.hex_eighty = 0x80; // Whatever the fuck that is
pkt.command = (uint32_t)cmd | (uint32_t)dir;
// Copy the data if there is some
if (len) { memcpy(pkt.data, data, len); }
// Dump the bytes
uint8_t* dump = (uint8_t*)&pkt;
printf("Sending:");
for (int i = 0; i < pkt.length; i++) {
printf(" %02X", dump[i]);
}
printf("\n");
// Send the command to endpoint 0
int sent;
FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_COMMAND_PIPE, (uint8_t*)&pkt, sizeof(kcsdr_packet_t), &sent, NULL);
if (err != FT_OK) {
return -err;
}
printf("Sent %d bytes (%d)\n", sent, err);
Sleep(50);
// Flush existing commands
FT_FlushPipe(dev->ft, KCSDR_COMMAND_PIPE);
return -(int)err;
}
int kcsdr_list_devices(kcsdr_info_t** devices) {
// Generate a list of FTDI devices
int ftdiDevCount = 0;
FT_STATUS err = FT_CreateDeviceInfoList(&ftdiDevCount);
if (err != FT_OK) {
return -1;
}
// If no device was found, return nothing
if (!ftdiDevCount) {
*devices = NULL;
return 0;
}
// Get said device list
FT_DEVICE_LIST_INFO_NODE* list = malloc(ftdiDevCount * sizeof(FT_DEVICE_LIST_INFO_NODE));
err = FT_GetDeviceInfoList(list, &ftdiDevCount);
if (err != FT_OK) {
return -1;
}
// Allocate the device info list
*devices = malloc(ftdiDevCount * sizeof(kcsdr_info_t));
// Find all KC908s
int kcCount = 0;
for (int i = 0; i < ftdiDevCount; i++) {
strcpy((*devices)[kcCount++].serial, list[i].SerialNumber);
}
// Free the FTDI list
free(list);
return kcCount;
}
void kcsdr_free_device_list(kcsdr_info_t* devices) {
// Free the list
if (devices) { free(devices); }
}
int kcsdr_open(kcsdr_t** dev, const char* serial) {
// Attempt to open the device using the serial number
FT_HANDLE ft;
FT_STATUS err = FT_Create(serial, FT_OPEN_BY_SERIAL_NUMBER, &ft);
if (err != FT_OK) {
return -1;
}
// Set the timeouts for the data pipes
FT_SetPipeTimeout(ft, KCSDR_RX_DATA_PIPE, 1000);
FT_SetPipeTimeout(ft, KCSDR_TX_DATA_PIPE, 1000);
// Allocate the device object
*dev = malloc(sizeof(kcsdr_t));
// Fill out the device object
(*dev)->ft = ft;
// Put device into remote control mode
return kcsdr_send_command(*dev, KCSDR_DIR_RX, CMD_START_REMOTE, NULL, 0);
}
void kcsdr_close(kcsdr_t* dev) {
// Put device back in normal mode
kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_STOP_REMOTE, NULL, 0);
// Close the device
FT_Close(dev->ft);
// Free the device object
free(dev);
}
int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port) {
// Send SET_PORT command
return kcsdr_send_command(dev, dir, CMD_SET_PORT, &port, 1);
}
int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq) {
// Send SET_FREQUENCY command
return kcsdr_send_command(dev, dir, CMD_SET_FREQUENCY, (uint8_t*)&freq, 8);
}
int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att) {
// Send SET_ATTENUATION command
return kcsdr_send_command(dev, dir, CMD_SET_ATTENUATION, &att, 1);
}
int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain) {
// Send SET_AMPLIFIER command
return kcsdr_send_command(dev, dir, CMD_SET_AMPLIFIER, &gain, 1);
}
int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain) {
// Send CMD_SET_EXT_AMP command
return kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_SET_EXT_AMP, &gain, 1);
}
int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate) {
// Set SET_BANDWIDTH command
return kcsdr_send_command(dev, dir, CMD_SET_BANDWIDTH, (uint8_t*)&samplerate, 4);
}
int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir) {
// Send START command
return kcsdr_send_command(dev, dir, CMD_START, NULL, 0);
}
int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir) {
// Send STOP command
return kcsdr_send_command(dev, dir, CMD_STOP, NULL, 0);
}
int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count) {
// Receive samples (TODO: Endpoint might be 0x81)
int received;
FT_STATUS err = FT_ReadPipeEx(dev->ft, KCSDR_RX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &received, NULL);
return (err == FT_OK) ? received / (2*sizeof(uint16_t)) : -(int)err;
}
int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count) {
// Transmit samples
int sent;
FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_TX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &sent, NULL);
return (err == FT_OK) ? sent / (2*sizeof(uint16_t)) : -(int)err;
}

View File

@ -0,0 +1,150 @@
#pragma once
#include <stdint.h>
#define KCSDR_SERIAL_LEN 16
#define KCSDR_MAX_PORTS 6
// Detect C++
#ifdef __cplusplus
extern "C" {
#endif
/**
* KCSDR Device.
*/
struct kcsdr;
typedef struct kcsdr kcsdr_t;
/**
* Device Information
*/
struct kcsdr_info {
char serial[KCSDR_SERIAL_LEN+1];
};
typedef struct kcsdr_info kcsdr_info_t;
/**
* RF Direction.
*/
enum kcsdr_direction {
KCSDR_DIR_RX = 0x00,
KCSDR_DIR_TX = 0x80
};
typedef enum kcsdr_direction kcsdr_direction_t;
/**
* Get a list of KCSDR devices on the system.
* @param devices Pointer to an array of device info.
* @return Number of devices found or error code.
*/
int kcsdr_list_devices(kcsdr_info_t** devices);
/**
* Free a device list returned by `kcsdr_list_devices()`.
* @param devices Device list to free.
*/
void kcsdr_free_device_list(kcsdr_info_t* devices);
/**
* Open a KCSDR device.
* @param dev Newly open device.
* @param serial Serial number of the device to open as returned in the device list.
* @return 0 on success, error code otherwise.
*/
int kcsdr_open(kcsdr_t** dev, const char* serial);
/**
* Close a KCSDR device.
* @param dev Device to be closed.
*/
void kcsdr_close(kcsdr_t* dev);
/**
* Select the RF port.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
* @param port RF port number to select.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port);
/**
* Set the center frequency.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
* @param freq Frequency in Hz.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq);
/**
* Set the attenuation.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
* @param samplerate Attenuation in dB.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att);
/**
* Set the internal amplifier gain.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
* @param gain Gain in dB.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain);
/**
* Set the external amplifier gain.
* @param dev Device to control.
* @param gain Gain in dB.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain);
/**
* Set the samplerate.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
* @param samplerate Samplerate in Hz.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate);
/**
* Start streaming samples.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
* @return 0 on success, error code otherwise.
*/
int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir);
/**
* Stop streaming samples.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
* @return 0 on success, error code otherwise.
*/
int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir);
/**
* Receive a buffer of samples.
* @param samples Sample buffer.
* @param count Number of complex samples.
* @return Number of samples received.
*/
int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count);
/**
* Transmit a buffer of samples.
* @param samples Sample buffer.
* @param count Number of complex samples.
* @return Number of samples transmitted.
*/
int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count);
// Detect C++
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,324 @@
#include <imgui.h>
#include <module.h>
#include <gui/gui.h>
#include <gui/smgui.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <utils/optionlist.h>
#include "kcsdr.h"
#include <atomic>
SDRPP_MOD_INFO{
/* Name: */ "kcsdr_source",
/* Description: */ "KCSDR Source Module",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
#define CONCAT(a, b) ((std::string(a) + b).c_str())
class KCSDRSourceModule : public ModuleManager::Instance {
public:
KCSDRSourceModule(std::string name) {
this->name = name;
sampleRate = 2000000.0;
samplerates.define(40e6, "40MHz", 40e6);
samplerates.define(35e6, "35MHz", 35e6);
samplerates.define(30e6, "30MHz", 30e6);
samplerates.define(25e6, "25MHz", 25e6);
samplerates.define(20e6, "20MHz", 20e6);
samplerates.define(15e6, "15MHz", 15e6);
samplerates.define(10e6, "10MHz", 10e6);
samplerates.define(5e6, "5MHz", 5e6);
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 devices
refresh();
// Select first (TODO: Select from config)
select("");
sigpath::sourceManager.registerSource("KCSDR", &handler);
}
~KCSDRSourceModule() {
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
void refresh() {
devices.clear();
// Get device list
kcsdr_info_t* list;
int count = kcsdr_list_devices(&list);
if (count < 0) {
flog::error("Failed to list devices: {}", count);
return;
}
// Create list
for (int i = 0; i < count; i++) {
devices.define(list[i].serial, list[i].serial, list[i].serial);
}
// Free the device list
kcsdr_free_device_list(list);
}
void select(const std::string& serial) {
// If there are no devices, give up
if (devices.empty()) {
selectedSerial.clear();
return;
}
// If the serial was not found, select the first available serial
if (!devices.keyExists(serial)) {
select(devices.key(0));
return;
}
// Get the menu ID
devId = devices.keyId(serial);
// TODO
// Update the samplerate
core::setInputSampleRate(sampleRate);
// Save serial number
selectedSerial = serial;
}
static void menuSelected(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("KCSDRSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
flog::info("KCSDRSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
if (_this->running) { return; }
// If no serial is given, do nothing
if (_this->selectedSerial.empty()) { return; }
// Open the device
int err = kcsdr_open(&_this->openDev, _this->selectedSerial.c_str());
if (err) {
flog::error("Failed to open device: {}", err);
return;
}
// Configure the device
kcsdr_set_port(_this->openDev, KCSDR_DIR_RX, 0);
kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, _this->freq);
kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att);
kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain);
kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain);
kcsdr_set_samplerate(_this->openDev, KCSDR_DIR_RX, _this->sampleRate);
// Start the stream
kcsdr_start(_this->openDev, KCSDR_DIR_RX);
// Start worker
_this->run = true;
_this->workerThread = std::thread(&KCSDRSourceModule::worker, _this);
_this->running = true;
flog::info("KCSDRSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
// Stop worker
_this->run = false;
_this->stream.stopWriter();
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
_this->stream.clearWriteStop();
// Stop streaming
kcsdr_stop(_this->openDev, KCSDR_DIR_RX);
// Close the device
kcsdr_close(_this->openDev);
flog::info("KCSDRSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
if (_this->running) {
kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, freq);
}
_this->freq = freq;
flog::info("KCSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_kcsdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
_this->select(_this->devices.key(_this->devId));
core::setInputSampleRate(_this->sampleRate);
// TODO: Save
}
if (SmGui::Combo(CONCAT("##_kcsdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
_this->sampleRate = _this->samplerates.value(_this->srId);
core::setInputSampleRate(_this->sampleRate);
// TODO: Save
}
SmGui::SameLine();
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_kcsdr_refr_", _this->name))) {
_this->refresh();
_this->select(_this->selectedSerial);
core::setInputSampleRate(_this->sampleRate);
}
if (_this->running) { SmGui::EndDisabled(); }
// SmGui::LeftLabel("RX Port");
// SmGui::FillWidth();
// if (SmGui::Combo(CONCAT("##_kcsdr_port_", _this->name), &_this->portId, _this->rxPorts.txt)) {
// if (_this->running) {
// // TODO
// }
// // TODO: Save
// }
SmGui::LeftLabel("Attenuation");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_kcsdr_att_", _this->name), &_this->att, 0, 31)) {
if (_this->running) {
kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att);
}
// TODO: Save
}
SmGui::LeftLabel("Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_kcsdr_gain_", _this->name), &_this->gain, 0, 31)) {
if (_this->running) {
kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain);
}
// TODO: Save
}
SmGui::LeftLabel("External Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_kcsdr_ext_gain_", _this->name), &_this->extGain, 0, 31)) {
if (_this->running) {
kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain);
}
// TODO: Save
}
}
void worker() {
// Compute the buffer size
int bufferSize = 0x4000/4;//sampleRate / 200;
// Allocate the sample buffer
int16_t* samps = dsp::buffer::alloc<int16_t>(bufferSize*2);
// Loop
while (run) {
// Read samples
int count = kcsdr_rx(openDev, samps, bufferSize);
if (!count) { continue; }
if (count < 0) {
flog::debug("Failed to read samples: {}", count);
break;
}
// Convert the samples to float
volk_16i_s32f_convert_32f((float*)stream.writeBuf, samps, 8192.0f, count*2);
// Send out the samples
if (!stream.swap(count)) { break; }
}
// Free the sample buffer
dsp::buffer::free(samps);
}
std::string name;
bool enabled = true;
dsp::stream<dsp::complex_t> stream;
double sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq;
OptionList<std::string, std::string> devices;
OptionList<int, double> samplerates;
int devId = 0;
int srId = 0;
int att = 0;
int gain = 30;
int extGain = 1;
int portId = 0;
std::string selectedSerial;
kcsdr_t* openDev;
std::thread workerThread;
std::atomic<bool> run = false;
};
MOD_EXPORT void _INIT_() {
// Nothing here
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new KCSDRSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (KCSDRSourceModule*)instance;
}
MOD_EXPORT void _END_() {
// Nothing here
}

View File

@ -36,10 +36,10 @@ enum SampleType {
};
const size_t SAMPLE_TYPE_SIZE[] {
sizeof(int8_t)*2,
sizeof(int16_t)*2,
sizeof(int32_t)*2,
sizeof(float)*2,
2*sizeof(int8_t),
2*sizeof(int16_t),
2*sizeof(int32_t),
2*sizeof(float),
};
class NetworkSourceModule : public ModuleManager::Instance {
@ -58,20 +58,6 @@ public:
handler.tuneHandler = tune;
handler.stream = &stream;
// Define samplerates
for (int i = 3000; i <= 192000; i <<= 1) {
samplerates.define(i, getSrScaled(i), i);
}
for (int i = 250000; i < 1000000; i += 250000) {
samplerates.define(i, getSrScaled(i), i);
}
for (int i = 1000000; i < 10000000; i += 500000) {
samplerates.define(i, getSrScaled(i), i);
}
for (int i = 10000000; i <= 100000000; i += 5000000) {
samplerates.define(i, getSrScaled(i), i);
}
// Define protocols
// protocols.define("TCP (Server)", PROTOCOL_TCP_SERVER);
protocols.define("TCP (Client)", PROTOCOL_TCP_CLIENT);
@ -86,8 +72,8 @@ public:
// Load config
config.acquire();
if (config.conf[name].contains("samplerate")) {
int sr = config.conf[name]["samplerate"];
if (samplerates.keyExists(sr)) { samplerate = samplerates.value(samplerates.keyId(sr)); }
samplerate = config.conf[name]["samplerate"];
tempSamplerate = samplerate;
}
if (config.conf[name].contains("protocol")) {
std::string protoStr = config.conf[name]["protocol"];
@ -108,7 +94,6 @@ public:
config.release();
// Set menu IDs
srId = samplerates.valueId(samplerate);
protoId = protocols.valueId(proto);
sampTypeId = sampleTypes.valueId(sampType);
@ -228,35 +213,24 @@ private:
if (_this->running) { SmGui::BeginDisabled(); }
// Hostname and port field
if (ImGui::InputText(("##iq_exporter_host_" + _this->name).c_str(), _this->hostname, sizeof(_this->hostname))) {
if (SmGui::InputText(("##network_source_host_" + _this->name).c_str(), _this->hostname, sizeof(_this->hostname))) {
config.acquire();
config.conf[_this->name]["host"] = _this->hostname;
config.release(true);
}
ImGui::SameLine();
ImGui::FillWidth();
if (ImGui::InputInt(("##iq_exporter_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
SmGui::SameLine();
SmGui::FillWidth();
if (SmGui::InputInt(("##network_source_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
_this->port = std::clamp<int>(_this->port, 1, 65535);
config.acquire();
config.conf[_this->name]["port"] = _this->port;
config.release(true);
}
// Samplerate selector
ImGui::LeftLabel("Samplerate");
ImGui::FillWidth();
if (ImGui::Combo(("##iq_exporter_sr_" + _this->name).c_str(), &_this->srId, _this->samplerates.txt)) {
_this->samplerate = _this->samplerates.value(_this->srId);
core::setInputSampleRate(_this->samplerate);
config.acquire();
config.conf[_this->name]["samplerate"] = _this->samplerates.key(_this->srId);
config.release(true);
}
// Mode protocol selector
ImGui::LeftLabel("Protocol");
ImGui::FillWidth();
if (ImGui::Combo(("##iq_exporter_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
SmGui::LeftLabel("Protocol");
SmGui::FillWidth();
if (SmGui::Combo(("##network_source_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
_this->proto = _this->protocols.value(_this->protoId);
config.acquire();
config.conf[_this->name]["protocol"] = _this->protocols.key(_this->protoId);
@ -264,15 +238,38 @@ private:
}
// Sample type selector
ImGui::LeftLabel("Sample type");
ImGui::FillWidth();
if (ImGui::Combo(("##iq_exporter_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
SmGui::LeftLabel("Sample type");
SmGui::FillWidth();
if (SmGui::Combo(("##network_source_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
_this->sampType = _this->sampleTypes.value(_this->sampTypeId);
config.acquire();
config.conf[_this->name]["sampleType"] = _this->sampleTypes.key(_this->sampTypeId);
config.release(true);
}
// Samplerate selector
SmGui::LeftLabel("Samplerate");
SmGui::FillWidth();
if (SmGui::InputInt(("##network_source_sr_" + _this->name).c_str(), &_this->tempSamplerate)) {
// Prevent silly values from silly users
_this->tempSamplerate = std::max<int>(_this->tempSamplerate, 1000);
}
bool applyEn = (!_this->running && _this->tempSamplerate != _this->samplerate);
if (!applyEn) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
if (SmGui::Button(("Apply##network_source_apply_" + _this->name).c_str())) {
_this->samplerate = _this->tempSamplerate;
core::setInputSampleRate(_this->samplerate);
config.acquire();
config.conf[_this->name]["samplerate"] = _this->samplerate;
config.release(true);
}
if (!applyEn) { SmGui::EndDisabled(); }
if (_this->tempSamplerate != _this->samplerate) {
SmGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Warning: Samplerate not applied yet");
}
if (_this->running) { SmGui::EndDisabled(); }
}
@ -280,14 +277,17 @@ private:
// Compute sizes
int blockSize = samplerate / 200;
int sampleSize = SAMPLE_TYPE_SIZE[sampType];
int frameSize = blockSize*sampleSize;
// Chose amount of bytes to attempt to read
bool forceSize = (proto != PROTOCOL_UDP);
int frameSize = sampleSize * (forceSize ? blockSize : STREAM_BUFFER_SIZE);
// Allocate receive buffer
uint8_t* buffer = dsp::buffer::alloc<uint8_t>(frameSize);
while (true) {
// Read samples from socket
int bytes = sock->recv(buffer, frameSize, true);
int bytes = sock->recv(buffer, frameSize, forceSize);
if (bytes <= 0) { break; }
// Convert to CF32 (note: problem if partial sample)
@ -325,7 +325,7 @@ private:
double freq;
int samplerate = 1000000;
int srId;
int tempSamplerate = 1000000;
Protocol proto = PROTOCOL_UDP;
int protoId;
SampleType sampType = SAMPLE_TYPE_INT16;
@ -333,7 +333,6 @@ private:
char hostname[1024] = "localhost";
int port = 1234;
OptionList<int, int> samplerates;
OptionList<std::string, Protocol> protocols;
OptionList<std::string, SampleType> sampleTypes;

View File

@ -23,6 +23,12 @@ SDRPP_MOD_INFO{
ConfigManager config;
const std::vector<const char*> deviceWhiteList = {
"PlutoSDR",
"ANTSDR",
"LibreSDR"
};
class PlutoSDRSourceModule : public ModuleManager::Instance {
public:
PlutoSDRSourceModule(std::string name) {
@ -130,7 +136,14 @@ private:
std::string duri = iio_context_info_get_uri(info);
// If the device is not a plutosdr, don't include it
if (desc.find("PlutoSDR") == std::string::npos) {
bool isPluto = false;
for (const auto type : deviceWhiteList) {
if (desc.find(type) != std::string::npos) {
isPluto = true;
break;
}
}
if (!isPluto) {
flog::warn("Ignored IIO device: [{}] {}", duri, desc);
continue;
}
@ -163,6 +176,9 @@ private:
// Construct the device name
std::string devName = '(' + backend + ") " + model + " [" + serial + ']';
// Skip duplicate devices
if (devices.keyExists(desc) || devices.nameExists(devName) || devices.valueExists(duri)) { continue; }
// Save device
devices.define(desc, devName, duri);
}

View File

@ -5,6 +5,17 @@ file(GLOB SRC "src/*.cpp")
include(${SDRPP_MODULE_CMAKE})
target_include_directories(rfnm_source PRIVATE "C:/Users/ryzerth/Documents/Code/librfnm/include/librfnm")
target_link_directories(rfnm_source PRIVATE "C:/Users/ryzerth/Documents/Code/librfnm/build/Release")
target_link_libraries(rfnm_source PRIVATE librfnm)
if (MSVC)
# Lib path
target_link_directories(rfnm_source PRIVATE "C:/Program Files/RFNM/lib/")
target_include_directories(rfnm_source PUBLIC "C:/Program Files/RFNM/include/")
target_link_libraries(rfnm_source PRIVATE rfnm)
else (MSVC)
find_package(PkgConfig)
pkg_check_modules(LIBRFNM REQUIRED librfnm)
target_include_directories(rfnm_source PRIVATE ${LIBRFNM_INCLUDE_DIRS})
target_link_directories(rfnm_source PRIVATE ${LIBRFNM_LIBRARY_DIRS})
target_link_libraries(rfnm_source PRIVATE ${LIBRFNM_LIBRARIES})
endif ()

View File

@ -3,9 +3,10 @@
#include <gui/gui.h>
#include <gui/smgui.h>
#include <signal_path/signal_path.h>
#include <librfnm.h>
#include <librfnm/librfnm.h>
#include <core.h>
#include <utils/optionlist.h>
#include <atomic>
SDRPP_MOD_INFO{
/* Name: */ "rfnm_source",
@ -90,8 +91,131 @@ private:
return;
}
// // Open the device
// librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, serial);
// Open the device
librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, serial);
// Define samplerates
samplerates.clear();
samplerates.define(61440000, "61.44 MHz", 2);
samplerates.define(122880000, "122.88 MHz", 1);
// Define daughterboards
daughterboards.clear();
for (int i = 0; i < 2; i++) {
// If not present, skip
if (!dev->s->hwinfo.daughterboard[i].board_id) { continue; }
// Format the daughterboard name
std::string name = (i ? "[SEC] " : "[PRI] ") + std::string(dev->s->hwinfo.daughterboard[i].user_readable_name);
// Add the daughterboard to the list
daughterboards.define(name, name, i);
}
// Load options (TODO)
srId = samplerates.keyId(61440000);
dgbId = 0;
// Select the daughterboard
selectDaughterboard(dev, 0);
// Update samplerate
sampleRate = samplerates.key(srId);
// Close device
delete dev;
// Save serial number
selectedSerial = serial;
}
struct PathConfig {
rfnm_rf_path path;
int chId;
uint16_t appliesCh;
bool operator==(const PathConfig& b) const {
return b.path == path;
}
};
void selectDaughterboard(librfnm* dev, int id) {
// If no daugherboard is populated, give up
if (!dev->s->hwinfo.daughterboard[0].board_id && !dev->s->hwinfo.daughterboard[1].board_id) {
flog::error("The selected device has no daughterboards");
return;
}
// If the ID is not populated, select the other one
if (id >= 2 || !dev->s->hwinfo.daughterboard[id].board_id) {
selectDaughterboard(dev, 1 - id);
}
// Compute the channel offset
int offset = 0;
for (int i = 0; i < id; i++) {
offset += dev->s->hwinfo.daughterboard[i].rx_ch_cnt;
}
// Define antenna paths by going through all channels
paths.clear();
int count = dev->s->hwinfo.daughterboard[id].rx_ch_cnt;
for (int i = 0; i < count; i++) {
// Go through each possible path
for (int j = 0; j < 10; j++) {
// If it's the null path, stop searching
rfnm_rf_path path = dev->s->rx.ch[offset + i].path_possible[j];
if (path == RFNM_PATH_NULL) { continue; }
// Get the path
PathConfig pc = { path, offset + i, (uint16_t)(1 << (offset + i + 8))};
// If it's not in the list, add it
if (!paths.valueExists(pc)) {
std::string name = librfnm::rf_path_to_string(pc.path);
std::string capName = name;
if (std::islower(capName[0])) { capName[0] = std::toupper(capName[0]); }
paths.define(name, capName, pc);
}
}
// Get the preferred path
PathConfig preferred_pc = { dev->s->rx.ch[offset + i].path_preferred, 0, 0 };
// Make sure the path is accessible or give up
if (!paths.valueExists(preferred_pc)) { continue; }
// Set this channel as the channel of its prefered path (cursed af but lazy)
const PathConfig& pc = paths.value(paths.valueId(preferred_pc));
((PathConfig*)&pc)->chId = offset + i;
((PathConfig*)&pc)->appliesCh = (uint16_t)(1 << (offset + i + 8));
}
// Dump antenna paths
for (int i = 0; i < paths.size(); i++) {
flog::debug("PATH[{}]: Name={}, Ch={}, Path={}", i, paths.name(i), paths.value(i).chId, (int)paths.value(i).path);
}
// Load configuration (TODO)
selectedPath = paths.key(0);
// Select antenna path
selectPath(dev, id, selectedPath);
// Save selected daughterboard
dgbId = id;
}
void selectPath(librfnm* dev, int dgbId, const std::string& path) {
// If the path doesn't exist, select the first path
if (!paths.keyExists(path)) {
selectPath(dev, dgbId, paths.key(0));
}
// Save selected path
selectedPath = path;
pathId = paths.keyId(path);
currentPath = paths.value(pathId);
// Define bandwidths
bandwidths.clear();
@ -103,27 +227,8 @@ private:
}
// Get gain range
gainMin = -30;//dev->librfnm_s->rx.ch[0].gain_range.min;
gainMax = 60;//dev->librfnm_s->rx.ch[0].gain_range.max;
// // Close device
// delete dev;
// Define samplerates
samplerates.clear();
samplerates.define(61440000, "61.44 MHz", 2);
samplerates.define(122880000, "122.88 MHz", 1);
// TODO: Load options
srId = samplerates.keyId(61440000);
bwId = bandwidths.nameId("Auto");
gain = 0;
// Update samplerate
sampleRate = samplerates.key(srId);
// Save serial number
selectedSerial = serial;
gainMin = dev->s->rx.ch[currentPath.chId].gain_range.min;
gainMax = dev->s->rx.ch[currentPath.chId].gain_range.max;
}
static void menuSelected(void* ctx) {
@ -144,19 +249,6 @@ private:
// Open the device
_this->openDev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial);
// Configure the device
_this->openDev->librfnm_s->rx.ch[0].enable = RFNM_CH_ON;
_this->openDev->librfnm_s->rx.ch[0].samp_freq_div_n = _this->samplerates[_this->srId];
_this->openDev->librfnm_s->rx.ch[0].freq = _this->freq;
_this->openDev->librfnm_s->rx.ch[0].gain = _this->gain;
_this->openDev->librfnm_s->rx.ch[0].rfic_lpf_bw = 100;
_this->openDev->librfnm_s->rx.ch[0].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
_this->openDev->librfnm_s->rx.ch[0].path = _this->openDev->librfnm_s->rx.ch[0].path_preferred;
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
if (fail != rfnm_api_failcode::RFNM_API_OK) {
flog::error("Failed to configure device: {}", (int)fail);
}
// Configure the stream
_this->bufferSize = -1;
_this->openDev->rx_stream(librfnm_stream_format::LIBRFNM_STREAM_FORMAT_CS16, &_this->bufferSize);
@ -171,7 +263,24 @@ private:
_this->openDev->rx_qbuf(&_this->rxBuf[i]);
}
// Flush buffers
_this->openDev->rx_flush();
// Configure the device
_this->openDev->s->rx.ch[_this->currentPath.chId].enable = RFNM_CH_ON;
_this->openDev->s->rx.ch[_this->currentPath.chId].samp_freq_div_n = _this->samplerates[_this->srId];
_this->openDev->s->rx.ch[_this->currentPath.chId].freq = _this->freq;
_this->openDev->s->rx.ch[_this->currentPath.chId].gain = _this->gain;
_this->openDev->s->rx.ch[_this->currentPath.chId].rfic_lpf_bw = 100;
_this->openDev->s->rx.ch[_this->currentPath.chId].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
_this->openDev->s->rx.ch[_this->currentPath.chId].path = _this->currentPath.path;
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
if (fail != rfnm_api_failcode::RFNM_API_OK) {
flog::error("Failed to configure device: {}", (int)fail);
}
// Start worker
_this->run = true;
_this->workerThread = std::thread(&RFNMSourceModule::worker, _this);
_this->running = true;
@ -184,13 +293,20 @@ private:
_this->running = false;
// Stop worker
_this->run = false;
_this->stream.stopWriter();
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
_this->stream.clearWriteStop();
// Stop the RX streaming
_this->openDev->rx_stream_stop();
// Disable channel
_this->openDev->librfnm_s->rx.ch[0].enable = RFNM_CH_ON;
_this->openDev->set(LIBRFNM_APPLY_CH0_RX);
_this->openDev->s->rx.ch[_this->currentPath.chId].enable = RFNM_CH_OFF;
_this->openDev->set(_this->currentPath.appliesCh);
// Flush buffers
_this->openDev->rx_flush();
// Close device
delete _this->openDev;
@ -206,8 +322,8 @@ private:
static void tune(double freq, void* ctx) {
RFNMSourceModule* _this = (RFNMSourceModule*)ctx;
if (_this->running) {
_this->openDev->librfnm_s->rx.ch[0].freq = freq;
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
_this->openDev->s->rx.ch[_this->currentPath.chId].freq = freq;
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
if (fail != rfnm_api_failcode::RFNM_API_OK) {
flog::error("Failed to tune: {}", (int)fail);
}
@ -244,11 +360,48 @@ private:
core::setInputSampleRate(_this->sampleRate);
}
if (_this->daughterboards.size() > 1) {
SmGui::LeftLabel("Daughterboard");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_rfnm_dgb_sel_", _this->name), &_this->dgbId, _this->daughterboards.txt)) {
// Open the device
librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial);
// Select the daughterboard
_this->selectDaughterboard(dev, _this->dgbId);
// Close device
delete dev;
// TODO: Save
}
}
if (_this->paths.size() > 1) {
SmGui::LeftLabel("Antenna Path");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_rfnm_path_sel_", _this->name), &_this->pathId, _this->paths.txt)) {
// Open the device
librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial);
// Select the atennna path
_this->selectPath(dev, _this->dgbId, _this->paths.key(_this->pathId));
// Close device
delete dev;
// TODO: Save
}
}
if (_this->running) { SmGui::EndDisabled(); }
SmGui::LeftLabel("Bandwidth");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_rfnm_bw_sel_", _this->name), &_this->bwId, _this->bandwidths.txt)) {
if (_this->running) {
// TODO: Set
}
// TODO: Save
}
@ -256,16 +409,16 @@ private:
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_rfnm_gain_", _this->name), &_this->gain, _this->gainMin, _this->gainMax)) {
if (_this->running) {
_this->openDev->librfnm_s->rx.ch[0].gain = _this->gain;
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
_this->openDev->s->rx.ch[_this->currentPath.chId].gain = _this->gain;
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
}
// TODO: Save
}
if (SmGui::Checkbox(CONCAT("FM Notch##_rfnm_", _this->name), &_this->fmNotch)) {
if (_this->running) {
_this->openDev->librfnm_s->rx.ch[0].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
_this->openDev->s->rx.ch[_this->currentPath.chId].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
}
// TODO: Save
}
@ -274,12 +427,18 @@ private:
void worker() {
librfnm_rx_buf* lrxbuf;
int sampCount = bufferSize/4;
uint8_t ch = (1 << currentPath.chId);
// TODO: Define number of buffers per swap to maintain 200 fps
// Define number of buffers per swap to maintain 200 fps
int maxBufCount = STREAM_BUFFER_SIZE / sampCount;
int bufCount = (sampleRate / sampCount) / 200;
if (bufCount <= 0) { bufCount = 1; }
if (bufCount > maxBufCount) { bufCount = maxBufCount; }
while (true) {
int count = 0;
while (run) {
// Receive a buffer
auto fail = openDev->rx_dqbuf(&lrxbuf, LIBRFNM_CH0, 1000);
auto fail = openDev->rx_dqbuf(&lrxbuf, ch, 1000);
if (fail == rfnm_api_failcode::RFNM_API_DQBUF_NO_DATA) {
flog::error("Dequeue buffer didn't have any data");
continue;
@ -287,13 +446,17 @@ private:
else if (fail) { break; }
// Convert buffer to CF32
volk_16i_s32f_convert_32f((float*)stream.writeBuf, (int16_t*)lrxbuf->buf, 32768.0f, sampCount * 2);
volk_16i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*sampCount], (int16_t*)lrxbuf->buf, 32768.0f, sampCount * 2);
// Reque buffer
openDev->rx_qbuf(lrxbuf);
// Swap data
if (!stream.swap(sampCount)) { break; }
if (count >= bufCount) {
if (!stream.swap(count*sampCount)) { break; }
count = 0;
}
}
flog::debug("Worker exiting");
@ -308,12 +471,15 @@ private:
double freq;
OptionList<std::string, std::string> devices;
OptionList<std::string, int> daughterboards;
OptionList<std::string, PathConfig> paths;
OptionList<int, int> bandwidths;
OptionList<int, int> samplerates;
int gainMin = 0;
int gainMax = 0;
int devId = 0;
int dgbId = 0;
int pathId = 0;
int srId = 0;
int bwId = 0;
int gain = 0;
@ -321,8 +487,11 @@ private:
std::string selectedSerial;
librfnm* openDev;
int bufferSize = -1;
std::string selectedPath;
PathConfig currentPath;
librfnm_rx_buf rxBuf[LIBRFNM_MIN_RX_BUFCNT];
std::atomic<bool> run = false;
std::thread workerThread;
};

View File

@ -5,6 +5,7 @@
#include <thread>
#include <vector>
#include <mutex>
#include <chrono>
#define RFSPACE_MAX_SIZE 8192
#define RFSPACE_HEARTBEAT_INTERVAL_MS 1000

View File

@ -121,14 +121,23 @@ public:
#ifndef __ANDROID__
devCount = rtlsdr_get_device_count();
char buf[1024];
char snBuf[1024];
char venBuf[256];
char prodBuf[256];
char snBuf[256];
for (int i = 0; i < devCount; i++) {
// Gather device info
const char* devName = rtlsdr_get_device_name(i);
int snErr = rtlsdr_get_device_usb_strings(i, NULL, NULL, snBuf);
int snErr = rtlsdr_get_device_usb_strings(i, venBuf, prodBuf, snBuf);
// Build name
sprintf(buf, "[%s] %s##%d", (!snErr && snBuf[0]) ? snBuf : "No Serial", devName, i);
if (venBuf[0] && prodBuf[0]) {
sprintf(buf, "%s %s [%s]##%d", venBuf, prodBuf, (!snErr && snBuf[0]) ? snBuf : "No Serial", i);
}
else {
sprintf(buf, "%s [%s]##%d", devName, (!snErr && snBuf[0]) ? snBuf : "No Serial", i);
}
// Add device to list
devNames.push_back(buf);
devListTxt += buf;
devListTxt += '\0';
@ -199,8 +208,6 @@ public:
config.conf["devices"][selectedDevName]["tunerAgc"] = tunerAgc;
config.conf["devices"][selectedDevName]["gain"] = gainId;
}
if (gainId >= gainList.size()) { gainId = gainList.size() - 1; }
updateGainTxt();
// Load config
if (config.conf["devices"][selectedDevName].contains("sampleRate")) {
@ -240,9 +247,11 @@ public:
if (config.conf["devices"][selectedDevName].contains("gain")) {
gainId = config.conf["devices"][selectedDevName]["gain"];
updateGainTxt();
}
if (gainId >= gainList.size()) { gainId = gainList.size() - 1; }
updateGainTxt();
config.release(created);
rtlsdr_close(openDev);

View File

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.13)
project(sddc_source)
file(GLOB SRC "src/*.cpp")
include(${SDRPP_MODULE_CMAKE})
add_subdirectory("./libsddc")
target_link_libraries(sddc_source PRIVATE sddc)

View File

@ -0,0 +1,2 @@
build/
.vscode/

View File

@ -0,0 +1,87 @@
cmake_minimum_required(VERSION 3.13)
project(libsddc VERSION 0.2.0)
# Options
option(BUILD_SDDC_UTILS "Build SDDC utilities such as sddc_info" ON)
option(INSTALL_UDEV_RULES "Install UDEV rules (Linux only)" ON)
# List all source files
file(GLOB_RECURSE SRC "src/*.c")
# On windows, all symbols must be exported
if (MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif ()
# Create dynamic libs
add_library(sddc SHARED ${SRC})
# # Set optimisation flags
# if (${CMAKE_BUILD_TYPE} MATCHES "Debug")
# # Debug Flags
# if (MSVC)
# target_compile_options(sddc PRIVATE /EHsc)
# else ()
# target_compile_options(sddc PRIVATE -g -Og)
# endif ()
# else()
# # Normal Flags
# if (MSVC)
# target_compile_options(sddc PRIVATE /O2 /Ob2 /EHsc)
# else ()
# target_compile_options(sddc PRIVATE -O3)
# endif ()
# endif()
# Include the include folder
target_include_directories(sddc PUBLIC "include/")
# Find libusb
find_package(PkgConfig REQUIRED)
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
# Link to libusb
target_link_libraries(sddc PRIVATE PkgConfig::libusb)
# TODO: Have it default instead of override
if (MSVC)
set(CMAKE_INSTALL_PREFIX "C:/Program Files/SDDC/")
set(CMAKE_INSTALL_BINDIR "bin")
set(CMAKE_INSTALL_LIBDIR "lib")
set(CMAKE_INSTALL_INCLUDEDIR "include")
else ()
include(GNUInstallDirs)
endif ()
if (NOT MSVC)
# Configure pkgconfig file
configure_file(${CMAKE_SOURCE_DIR}/libsddc.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libsddc.pc @ONLY)
# Install pkgconfig file
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libsddc.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
endif ()
# Install the library
if (MSVC)
install(TARGETS sddc)
else ()
install(TARGETS sddc DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif ()
# Install the headers
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# On Windows, install dependencies
if (MSVC)
install(FILES $<TARGET_FILE_DIR:sddc>/libusb-1.0.dll DESTINATION ${CMAKE_INSTALL_BINDIR})
endif ()
# Build utils if enabled
if (BUILD_SDDC_UTILS)
add_subdirectory("utils/sddc_info")
add_subdirectory("utils/sddc_rx")
endif ()
# # Create uninstall target
# configure_file(${CMAKE_SOURCE_DIR}/cmake/uninstall.cmake ${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake @ONLY)
# add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake)

View 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)

View File

@ -0,0 +1,177 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
// Handle inclusion from C++ code
#ifdef __cplusplus
extern "C" {
#endif
#define SDDC_SERIAL_MAX_LEN 256
enum sddc_model {
SDDC_MODEL_UNKNOWN = 0x00,
SDDC_MODEL_BBRF103 = 0x01,
SDDC_MODEL_HF103 = 0x02,
SDDC_MODEL_RX888 = 0x03,
SDDC_MODEL_RX888_MK2 = 0x04,
SDDC_MODEL_RX999 = 0x05,
SDDC_MODEL_RXLUCY = 0x06,
SDDC_MODEL_RX888_MK3 = 0x07
};
typedef enum sddc_model sddc_model_t;
enum sddc_error {
SDDC_ERROR_UNKNOWN = -99,
SDDC_ERROR_NOT_IMPLEMENTED = -98,
SDDC_ERROR_FIRMWARE_UPLOAD_FAILED = -4,
SDDC_ERROR_NOT_FOUND = -3,
SDDC_ERROR_USB_ERROR = -2,
SDDC_ERROR_TIMEOUT = -1,
SDDC_SUCCESS = 0
};
typedef enum sddc_error sddc_error_t;
/**
* Device instance.
*/
struct sddc_dev;
typedef struct sddc_dev sddc_dev_t;
/**
* Device information.
*/
struct sddc_devinfo {
const char serial[SDDC_SERIAL_MAX_LEN];
sddc_model_t model;
int firmwareMajor;
int firmwareMinor;
};
typedef struct sddc_devinfo sddc_devinfo_t;
/**
* Parameter range. A step size of zero means infinitely variable.
*/
struct sddc_range {
double start;
double end;
double step;
};
typedef struct sddc_range sddc_range_t;
/**
* Get the string representation of a device model.
* @param model Model to get the string representation of.
* @return String representation of the model.
*/
const char* sddc_model_to_string(sddc_model_t model);
/**
* Get the string representation of an error.
* @param model Error to get the string representation of.
* @return String representation of the error.
*/
const char* sddc_error_to_string(sddc_error_t error);
/**
* Set the path to the firmware image.
* @param path Path to the firmware image.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_firmware_path(const char* path);
/**
* Get a list of connected devices. The returned list has to be freed using `sddc_free_device_list()` if it isn't empty.
* @param dev_list Pointer to a list of devices.
* @return Number of devices in the list or an error code if an error occured.
*/
int sddc_get_device_list(sddc_devinfo_t** dev_list);
/**
* Free a device list returned by `sddc_get_device_list()`. Attempting to free a list returned empty has no effect.
* @param dev_list Device list to free.
*/
void sddc_free_device_list(sddc_devinfo_t* dev_list);
/**
* Open a device by its serial number.
* @param serial Serial number of the device to open.
* @param dev Pointer to a SDDC device pointer to populate once open.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_open(const char* serial, sddc_dev_t** dev);
/**
* Close an opened SDDC device.
* @param dev SDDC Device to close.
*/
void sddc_close(sddc_dev_t* dev);
/**
* Get the range of samplerate supported by a device.
* @param dev SDDC device.
* @return Range of supported samplerates.
*/
sddc_range_t sddc_get_samplerate_range(sddc_dev_t* dev);
/**
* Set the device's sampling rate.
* @param dev SDDC device.
* @param samplerate Sampling rate.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_samplerate(sddc_dev_t* dev, uint32_t samplerate);
/**
* Enable the ADC's dithering feature.
* @param dev SDDC device.
* @param enabled True to enable dithering, false to disable it.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_dithering(sddc_dev_t* dev, bool enabled);
/**
* Enable the ADC's randomizer feature.
* @param dev SDDC device.
* @param enabled True to enable randomization, false to disable it.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_randomizer(sddc_dev_t* dev, bool enabled);
/**
* Set the LO of the tuner.
* @param dev SDDC device.
* @param frequency Frequency of the LO.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_tuner_frequency(sddc_dev_t* dev, uint64_t frequency);
/**
* Start the device.
* @param dev SDDC device.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_start(sddc_dev_t* dev);
/**
* Stop the device.
* @param dev SDDC device.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_stop(sddc_dev_t* dev);
/**
* Receive samples.
* @param dev SDDC device.
* @param samples Buffer to write the samples to.
* @param count Number of samples to read.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_rx(sddc_dev_t* dev, int16_t* samples, int count);
// Handle inclusion from C++ code
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@ -0,0 +1,116 @@
#include "fx3_boot.h"
#include <stdio.h>
#define FX3_TIMEOUT 1000
#define FX3_VENDOR_REQUEST 0xA0
#define FX3_MAX_BLOCK_SIZE 0x1000
int sddc_fx3_boot_mem_read(libusb_device_handle* dev, uint32_t addr, uint8_t* data, uint16_t len) {
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, FX3_VENDOR_REQUEST, addr & 0xFFFF, addr >> 16, data, len, FX3_TIMEOUT);
}
int sddc_fx3_boot_mem_write(libusb_device_handle* dev, uint32_t addr, const uint8_t* data, uint16_t len) {
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, FX3_VENDOR_REQUEST, addr & 0xFFFF, addr >> 16, data, len, FX3_TIMEOUT);
}
int sddc_fx3_boot_run(libusb_device_handle* dev, uint32_t entry) {
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, FX3_VENDOR_REQUEST, entry & 0xFFFF, entry >> 16, NULL, 0, FX3_TIMEOUT);
}
int sddc_fx3_boot_upload_firmware(libusb_device_handle* dev, const char* path) {
// Open the firmware image
FILE* fw = fopen(path, "rb");
if (!fw) {
fprintf(stderr, "Failed to open firmware image\n");
return LIBUSB_ERROR_OTHER;
}
// Read the signature
char sign[2];
int read = fread(sign, 2, 1, fw);
if (read != 1) {
fprintf(stderr, "Failed to read firmware image signature: %d\n", read);
fclose(fw);
return LIBUSB_ERROR_OTHER;
}
// Check the signature
if (sign[0] != 'C' || sign[1] != 'Y') {
fprintf(stderr, "Firmware image has invalid signature\n");
fclose(fw);
return LIBUSB_ERROR_OTHER;
}
// Skip useless metadata
int err = fseek(fw, 2, SEEK_CUR);
if (err) {
fprintf(stderr, "Invalid firmware image: %d\n", err);
fclose(fw);
return LIBUSB_ERROR_OTHER;
}
// Preallocate data buffer
int bufferSize = 0x10000;
uint8_t* buffer = malloc(bufferSize);
// Read every section
while (1) {
// Read the section size
uint32_t sizeWords;
read = fread(&sizeWords, sizeof(uint32_t), 1, fw);
if (read != 1) {
fprintf(stderr, "Invalid firmware image section size\n");
fclose(fw);
return LIBUSB_ERROR_OTHER;
}
uint32_t size = sizeWords << 2;
// Read the section address
uint32_t addr;
read = fread(&addr, sizeof(uint32_t), 1, fw);
if (read != 1) {
fprintf(stderr, "Invalid firmware image section address\n");
fclose(fw);
return LIBUSB_ERROR_OTHER;
}
// If the section is a termination section, run the code at the given address
if (!size) {
sddc_fx3_boot_run(dev, addr);
break;
}
// Re-allocate buffer if needed
if (size > bufferSize) {
bufferSize = size;
realloc(buffer, bufferSize);
}
// Read the section data
read = fread(buffer, 1, size, fw);
if (read != size) {
fprintf(stderr, "Failed to read section data: %d\n", read);
fclose(fw);
return LIBUSB_ERROR_OTHER;
}
// Upload it to the chip
for (int i = 0; i < size; i += FX3_MAX_BLOCK_SIZE) {
int left = size - i;
err = sddc_fx3_boot_mem_write(dev, addr + i, &buffer[i], (left > FX3_MAX_BLOCK_SIZE) ? FX3_MAX_BLOCK_SIZE : left);
if (err < LIBUSB_SUCCESS) {
fprintf(stderr, "Failed to write to device memory: %d\n", err);
fclose(fw);
return err;
}
}
}
// TODO: Checksum stuff and verification ideally
// Close the firmware image
fclose(fw);
// Return successfully
return LIBUSB_SUCCESS;
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <libusb.h>
#include <stdint.h>
/**
* Read data from the device's memory.
* @param dev Device to read data from.
* @param addr Start address of the data in the device's memory.
* @param data Buffer to write the data into.
* @param len Number of bytes to read.
* @return libusb error code.
*/
int sddc_fx3_boot_mem_read(libusb_device_handle* dev, uint32_t addr, uint8_t* data, uint16_t len);
/**
* Write data to the device's memory.
* @param dev Device to write data to.
* @param addr Start address of the data in the device's memory.
* @param data Buffer to write the data into.
* @param len Number of bytes to read.
* @return libusb error code.
*/
int sddc_fx3_boot_mem_write(libusb_device_handle* dev, uint32_t addr, const uint8_t* data, uint16_t len);
/**
* Execute code on the device.
* @param dev Device to execute code on.
* @param entry Entry point of the code.
* @return libusb error code.
*/
int sddc_fx3_boot_run(libusb_device_handle* dev, uint32_t entry);
/**
* Parse, upload and execute a firmware image.
* @param dev Device to upload the firmware to.
* @param path Path to the firmware image.
* @return libusb error code.
*/
int sddc_fx3_boot_upload_firmware(libusb_device_handle* dev, const char* path);

View File

@ -0,0 +1,451 @@
#include <sddc.h>
#include <stdlib.h>
#include <stdint.h>
#include <libusb.h>
#include <stdio.h>
#include "usb_interface.h"
struct sddc_dev {
// USB handles
struct libusb_device_handle* openDev;
// Device info
sddc_devinfo_t info;
// Device state
bool running;
uint32_t samplerate;
uint64_t tunerFreq;
sddc_gpio_t gpioState;
};
struct libusb_context* ctx = NULL;
char* sddc_firmware_path = NULL;
bool sddc_is_init = false;
void sddc_init() {
// If already initialized, do nothing
if (sddc_is_init) { return; }
// If the firmware isn't already found, find it
if (!sddc_firmware_path) {
// TODO: Find the firmware
}
// Init libusb
libusb_init(&ctx);
}
const char* sddc_model_to_string(sddc_model_t model) {
switch (model) {
case SDDC_MODEL_BBRF103: return "BBRF103";
case SDDC_MODEL_HF103: return "HF103";
case SDDC_MODEL_RX888: return "RX888";
case SDDC_MODEL_RX888_MK2: return "RX888 MK2";
case SDDC_MODEL_RX999: return "RX999";
case SDDC_MODEL_RXLUCY: return "RXLUCY";
case SDDC_MODEL_RX888_MK3: return "RX888 MK3";
default: return "Unknown";
}
}
const char* sddc_error_to_string(sddc_error_t error) {
switch (error) {
case SDDC_ERROR_NOT_IMPLEMENTED: return "Not Implemented";
case SDDC_ERROR_FIRMWARE_UPLOAD_FAILED: return "Firmware Upload Failed";
case SDDC_ERROR_NOT_FOUND: return "Not Found";
case SDDC_ERROR_USB_ERROR: return "USB Error";
case SDDC_ERROR_TIMEOUT: return "Timeout";
case SDDC_SUCCESS: return "Success";
default: return "Unknown";
}
}
sddc_error_t sddc_set_firmware_path(const char* path) {
// Free the old path if it exists
if (sddc_firmware_path) { free(sddc_firmware_path); }
// Allocate the new path
sddc_firmware_path = malloc(strlen(path) + 1);
// Copy the new path
strcpy(sddc_firmware_path, path);
// TODO: Check if the file path exists
return SDDC_SUCCESS;
}
int sddc_get_device_list(sddc_devinfo_t** dev_list) {
// Initialize libsddc in case it isn't already
sddc_init();
// Get a list of USB devices
libusb_device** devices;
int devCount = libusb_get_device_list(ctx, &devices);
if (devCount < 0 || !devices) { return SDDC_ERROR_USB_ERROR; }
// Initialize all uninitialized devices
bool uninit = false;
for (int i = 0; i < devCount; i++) {
// Get the device from the list
libusb_device* dev = devices[i];
// Get the device descriptor. Fail silently on error as it might not even be a SDDC device.
struct libusb_device_descriptor desc;
int err = libusb_get_device_descriptor(dev, &desc);
if (err != LIBUSB_SUCCESS) { continue; }
// If it's not an uninitialized device, go to next device
if (desc.idVendor != SDDC_UNINIT_VID || desc.idProduct != SDDC_UNINIT_PID) { continue; }
// Initialize the device
printf("Found uninitialized device, initializing...\n");
// TODO: Check that the firmware path is valid
sddc_error_t serr = sddc_init_device(dev, sddc_firmware_path);
if (serr != SDDC_SUCCESS) { continue; }
// Set the flag to wait the devices to start up
uninit = true;
}
// If some uninitialized devices were found
if (uninit) {
// Free the device list
libusb_free_device_list(devices, 1);
// Wait for the devices to show back up
#ifdef _WIN32
Sleep(SDDC_INIT_SEARCH_DELAY_MS);
#else
usleep(SDDC_INIT_SEARCH_DELAY_MS * 1000);
#endif
// Attempt to list devices again
devCount = libusb_get_device_list(ctx, &devices);
if (devCount < 0 || !devices) { return SDDC_ERROR_USB_ERROR; }
}
// Allocate the device list
*dev_list = malloc(devCount * sizeof(sddc_devinfo_t));
// Check each device
int found = 0;
libusb_device_handle* openDev;
for (int i = 0; i < devCount; i++) {
// Get the device from the list
libusb_device* dev = devices[i];
// Get the device descriptor. Fail silently on error as it might not even be a SDDC device.
struct libusb_device_descriptor desc;
int err = libusb_get_device_descriptor(dev, &desc);
if (err != LIBUSB_SUCCESS) { continue; }
// If the device is not an SDDC device, go to next device
if (desc.idVendor != SDDC_VID || desc.idProduct != SDDC_PID) { continue; }
// Open the device
err = libusb_open(dev, &openDev);
if (err != LIBUSB_SUCCESS) {
fprintf(stderr, "Failed to open device: %d\n", err);
continue;
}
// Create entry
sddc_devinfo_t* info = &((*dev_list)[found]);
// Get the serial number
err = libusb_get_string_descriptor_ascii(openDev, desc.iSerialNumber, info->serial, SDDC_SERIAL_MAX_LEN-1);
if (err < LIBUSB_SUCCESS) {
printf("Failed to get descriptor: %d\n", err);
libusb_close(openDev);
continue;
}
// Get the hardware info
sddc_hwinfo_t hwinfo;
err = sddc_fx3_get_info(openDev, &hwinfo, 0);
if (err < LIBUSB_SUCCESS) {
printf("Failed to get device info: %d\n", err);
libusb_close(openDev);
continue;
}
// Save the hardware info
info->model = (sddc_model_t)hwinfo.model;
info->firmwareMajor = hwinfo.firmwareConfigH;
info->firmwareMinor = hwinfo.firmwareConfigL;
// Close the device
libusb_close(openDev);
// Increment device counter
found++;
}
// Free the libusb device list
libusb_free_device_list(devices, 1);
// Return the number of devices found
return found;
}
void sddc_free_device_list(sddc_devinfo_t* dev_list) {
// Free the device list if it exists
if (dev_list) { free(dev_list); };
}
sddc_error_t sddc_open(const char* serial, sddc_dev_t** dev) {
// Initialize libsddc in case it isn't already
sddc_init();
// Get a list of USB devices
libusb_device** devices;
int devCount = libusb_get_device_list(ctx, &devices);
if (devCount < 0 || !devices) { return SDDC_ERROR_USB_ERROR; }
// Initialize all uninitialized devices
bool uninit = false;
for (int i = 0; i < devCount; i++) {
// Get the device from the list
libusb_device* dev = devices[i];
// Get the device descriptor. Fail silently on error as it might not even be a SDDC device.
struct libusb_device_descriptor desc;
int err = libusb_get_device_descriptor(dev, &desc);
if (err != LIBUSB_SUCCESS) { continue; }
// If it's not an uninitialized device, go to next device
if (desc.idVendor != SDDC_UNINIT_VID || desc.idProduct != SDDC_UNINIT_PID) { continue; }
// Initialize the device
printf("Found uninitialized device, initializing...\n");
// TODO: Check that the firmware path is valid
sddc_error_t serr = sddc_init_device(dev, sddc_firmware_path);
if (serr != SDDC_SUCCESS) { continue; }
// Set the flag to wait the devices to start up
uninit = true;
}
// If some uninitialized devices were found
if (uninit) {
// Free the device list
libusb_free_device_list(devices, 1);
// Wait for the devices to show back up
#ifdef _WIN32
Sleep(SDDC_INIT_SEARCH_DELAY_MS);
#else
usleep(SDDC_INIT_SEARCH_DELAY_MS * 1000);
#endif
// Attempt to list devices again
devCount = libusb_get_device_list(ctx, &devices);
if (devCount < 0 || !devices) { return SDDC_ERROR_USB_ERROR; }
}
// Search through all USB device
bool found = false;
libusb_device_handle* openDev;
for (int i = 0; i < devCount; i++) {
// Get the device from the list
libusb_device* dev = devices[i];
// Get the device descriptor. Fail silently on error as it might not even be a SDDC device.
struct libusb_device_descriptor desc;
int err = libusb_get_device_descriptor(dev, &desc);
if (err != LIBUSB_SUCCESS) { continue; }
// If the device is not an SDDC device, go to next device
if (desc.idVendor != SDDC_VID || desc.idProduct != SDDC_PID) { continue; }
// Open the device
err = libusb_open(dev, &openDev);
if (err != LIBUSB_SUCCESS) {
fprintf(stderr, "Failed to open device: %d\n", err);
continue;
}
// Get the serial number
char dserial[SDDC_SERIAL_MAX_LEN];
err = libusb_get_string_descriptor_ascii(openDev, desc.iSerialNumber, dserial, SDDC_SERIAL_MAX_LEN-1);
if (err < LIBUSB_SUCCESS) {
printf("Failed to get descriptor: %d\n", err);
libusb_close(openDev);
continue;
}
// Compare the serial number and give up if not a match
if (strcmp(dserial, serial)) { continue; }
// Get the device info
// TODO
// Set the found flag and stop searching
found = true;
break;
}
// Free the libusb device list
libusb_free_device_list(devices, true);
// If the device was not found, give up
if (!found) { return SDDC_ERROR_NOT_FOUND; }
// Claim the interface
libusb_claim_interface(openDev, 0);
// Allocate the device object
*dev = malloc(sizeof(sddc_dev_t));
// Initialize the device object
(*dev)->openDev = openDev;
//(*dev)->info = ; //TODO
(*dev)->running = false;
(*dev)->samplerate = 128e6;
(*dev)->tunerFreq = 100e6;
(*dev)->gpioState = SDDC_GPIO_SHUTDOWN | SDDC_GPIO_SEL0; // ADC shutdown and HF port selected
// Stop everything in case the device is partially started
printf("Stopping...\n");
sddc_stop(*dev);
// TODO: Setup all of the other state
sddc_gpio_put(*dev, SDDC_GPIO_SEL0, false);
sddc_gpio_put(*dev, SDDC_GPIO_SEL1, true);
sddc_gpio_put(*dev, SDDC_GPIO_VHF_EN, true);
sddc_tuner_start((*dev)->openDev, 16e6);
sddc_tuner_tune((*dev)->openDev, 100e6);
sddc_fx3_set_param((*dev)->openDev, SDDC_PARAM_R82XX_ATT, 15);
sddc_fx3_set_param((*dev)->openDev, SDDC_PARAM_R83XX_VGA, 9);
sddc_fx3_set_param((*dev)->openDev, SDDC_PARAM_AD8340_VGA, 5);
return SDDC_SUCCESS;
}
void sddc_close(sddc_dev_t* dev) {
// Stop everything
sddc_stop(dev);
// Release the interface
libusb_release_interface(dev->openDev, 0);
// Close the USB device
libusb_close(dev->openDev);
// Free the device struct
free(dev);
}
sddc_range_t sddc_get_samplerate_range(sddc_dev_t* dev) {
// All devices have the same samplerate range
sddc_range_t range = { 8e6, 128e6, 0 };
return range;
}
int sddc_gpio_set(sddc_dev_t* dev, sddc_gpio_t gpios) {
// Update the state
dev->gpioState = gpios;
// Push to the device
sddc_fx3_gpio(dev->openDev, gpios);
}
int sddc_gpio_put(sddc_dev_t* dev, sddc_gpio_t gpios, bool value) {
// Update the state of the given GPIOs only
return sddc_gpio_set(dev, (dev->gpioState & (~gpios)) | (value ? gpios : 0));
}
sddc_error_t sddc_set_samplerate(sddc_dev_t* dev, uint32_t samplerate) {
// Update the state
dev->samplerate = samplerate;
// If running, send the new sampling rate to the device
if (dev->running) {
int err = sddc_adc_set_samplerate(dev->openDev, samplerate);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
}
// Return successfully
return SDDC_SUCCESS;
}
sddc_error_t sddc_set_dithering(sddc_dev_t* dev, bool enabled) {
// Update the GPIOs according to the desired state
int err = sddc_gpio_put(dev, SDDC_GPIO_DITHER, enabled);
return (err < LIBUSB_SUCCESS) ? SDDC_ERROR_USB_ERROR : SDDC_SUCCESS;
}
sddc_error_t sddc_set_randomizer(sddc_dev_t* dev, bool enabled) {
// Update the GPIOs according to the desired state
int err = sddc_gpio_put(dev, SDDC_GPIO_RANDOM, enabled);
return (err < LIBUSB_SUCCESS) ? SDDC_ERROR_USB_ERROR : SDDC_SUCCESS;
}
sddc_error_t sddc_set_tuner_frequency(sddc_dev_t* dev, uint64_t frequency) {
// Update the state
dev->tunerFreq = frequency;
// If running, send the new frequency to the device
if (dev->running) {
int err = sddc_tuner_tune(dev->openDev, frequency);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
}
// Return successfully
return SDDC_SUCCESS;
}
sddc_error_t sddc_start(sddc_dev_t* dev) {
// De-assert the shutdown pin
int err = sddc_gpio_put(dev, SDDC_GPIO_SHUTDOWN, false);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Start the tuner (TODO: Check if in VHF mode)
// Start the ADC
err = sddc_adc_set_samplerate(dev->openDev, dev->samplerate);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Start the FX3
err = sddc_fx3_start(dev->openDev);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Update the state
dev->running = true;
// Return successfully
return SDDC_SUCCESS;
}
sddc_error_t sddc_stop(sddc_dev_t* dev) {
// Stop the FX3
int err = sddc_fx3_stop(dev->openDev);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Stop the tuner
err = sddc_tuner_stop(dev->openDev);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Stop the ADC
err = sddc_adc_set_samplerate(dev->openDev, 0);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Set the GPIOs for standby mode
err = sddc_gpio_put(dev, SDDC_GPIO_SHUTDOWN, true);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Update the state
dev->running = false;
// Return successfully
return SDDC_SUCCESS;
}
sddc_error_t sddc_rx(sddc_dev_t* dev, int16_t* samples, int count) {
// Read samples from the device
int bytesRead = 0;
int err = libusb_bulk_transfer(dev->openDev, LIBUSB_ENDPOINT_IN | 1, samples, count * sizeof(int16_t), &bytesRead, SDDC_TIMEOUT_MS);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
return SDDC_SUCCESS;
}

View File

@ -0,0 +1,92 @@
#include "usb_interface.h"
#include "fx3_boot.h"
#include <stdio.h>
sddc_error_t sddc_init_device(libusb_device* dev, const char* firmware) {
// Open the device
libusb_device_handle* openDev;
int err = libusb_open(dev, &openDev);
if (err != LIBUSB_SUCCESS) {
printf("Failed to open device: %d\n", err);
return SDDC_ERROR_USB_ERROR;
}
// Attempt to upload the firmware
err = sddc_fx3_boot_upload_firmware(openDev, firmware);
if (err) {
fprintf(stderr, "Failed to upload firmware to uninitialized device\n");
return SDDC_ERROR_FIRMWARE_UPLOAD_FAILED;
}
// Close the device
libusb_close(openDev);
// Return successfully
return SDDC_SUCCESS;
}
int sddc_fx3_start(libusb_device_handle* dev) {
// Send the start command
uint32_t dummy;
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_START, 0, 0, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_fx3_stop(libusb_device_handle* dev) {
// Send the stop command
uint32_t dummy;
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_STOP, 0, 0, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_fx3_get_info(libusb_device_handle* dev, sddc_hwinfo_t* info, char enableDebug) {
// Fetch the info data
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_GET_INFO, enableDebug, 0, info, sizeof(sddc_hwinfo_t), SDDC_TIMEOUT_MS);
}
int sddc_fx3_gpio(libusb_device_handle* dev, sddc_gpio_t gpio) {
// Send the GPIO state
uint32_t dword = gpio;
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_GPIO, 0, 0, &dword, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_fx3_i2c_write(libusb_device_handle* dev, uint8_t addr, uint16_t reg, const uint8_t* data, uint16_t len) {
// Send the I2C data
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_I2C_W, addr, reg, data, len, SDDC_TIMEOUT_MS);
}
int sddc_fx3_i2c_read(libusb_device_handle* dev, uint8_t addr, uint16_t reg, const uint8_t* data, uint16_t len) {
// Fetch the I2C data
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_I2C_R, addr, reg, data, len, SDDC_TIMEOUT_MS);
}
int sddc_fx3_reset(libusb_device_handle* dev) {
// Send the reset command
uint32_t dummy;
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_RESET, 0, 0, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_fx3_set_param(libusb_device_handle* dev, sddc_param_t param, uint16_t value) {
// Send the parameter
uint32_t dummy;
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_SET_PARAM, value, param, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_adc_set_samplerate(libusb_device_handle* dev, uint32_t samplerate) {
// Send the samplerate
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_ADC_SET_RATE, 0, 0, &samplerate, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_tuner_start(libusb_device_handle* dev, uint32_t refFreq) {
// Send the reset command
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_TUNER_START, 0, 0, &refFreq, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_tuner_tune(libusb_device_handle* dev, uint64_t frequency) {
// Send the reset command
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_TUNER_TUNE, 0, 0, &frequency, sizeof(uint64_t), SDDC_TIMEOUT_MS);
}
int sddc_tuner_stop(libusb_device_handle* dev) {
// Send the reset command
uint32_t dummy; // Because the shitty firmware absolute wants data to stop the tuner, this is dumb...
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_TUNER_STOP, 0, 0, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}

Some files were not shown because too many files have changed in this diff Show More